diff --git a/.clang-tidy b/.clang-tidy index db499035..f0dfdf1b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,111 +1,4 @@ -WarningsAsErrors: > - -*, - bugprone-*, - -bugprone-multi-level-implicit-pointer-conversion, - -bugprone-empty-catch, - -bugprone-unused-return-value, - -bugprone-reserved-identifier, - -bugprone-switch-missing-default-case, - -bugprone-unused-local-non-trivial-variable, - -bugprone-easily-swappable-parameters, - -bugprone-forward-declararion-namespace, - -bugprone-forward-declararion-namespace, - -bugprone-macro-parentheses, - -bugprone-narrowing-conversions, - -bugprone-branch-clone, - -bugprone-assignment-in-if-condition, - concurrency-*, - -concurrency-mt-unsafe, - cppcoreguidelines-*, - -cppcoreguidelines-pro-type-const-cast, - -cppcoreguidelines-owning-memory, - -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-pro-bounds-constant-array-index, - -cppcoreguidelines-avoid-const-or-ref-data-members, - -cppcoreguidelines-non-private-member-variables-in-classes, - -cppcoreguidelines-avoid-goto, - -cppcoreguidelines-pro-bounds-array-to-pointer-decay, - -cppcoreguidelines-avoid-do-while, - -cppcoreguidelines-avoid-non-const-global-variables, - -cppcoreguidelines-special-member-functions, - -cppcoreguidelines-explicit-virtual-functions, - -cppcoreguidelines-avoid-c-arrays, - -cppcoreguidelines-pro-bounds-pointer-arithmetic, - -cppcoreguidelines-narrowing-conversions, - -cppcoreguidelines-pro-type-union-access, - -cppcoreguidelines-pro-type-member-init, - -cppcoreguidelines-macro-usage, - -cppcoreguidelines-macro-to-enum, - -cppcoreguidelines-init-variables, - -cppcoreguidelines-pro-type-cstyle-cast, - -cppcoreguidelines-pro-type-vararg, - -cppcoreguidelines-pro-type-reinterpret-cast, - -google-global-names-in-headers, - -google-readability-casting, - google-runtime-operator, - misc-*, - -misc-use-internal-linkage, - -misc-unused-parameters, - -misc-no-recursion, - -misc-non-private-member-variables-in-classes, - -misc-include-cleaner, - -misc-use-anonymous-namespace, - -misc-const-correctness, - modernize-*, - -modernize-use-emplace, - -modernize-redundant-void-arg, - -modernize-use-starts-ends-with, - -modernize-use-designated-initializers, - -modernize-use-std-numbers, - -modernize-return-braced-init-list, - -modernize-use-trailing-return-type, - -modernize-use-using, - -modernize-use-override, - -modernize-avoid-c-arrays, - -modernize-macro-to-enum, - -modernize-loop-convert, - -modernize-use-nodiscard, - -modernize-pass-by-value, - -modernize-use-auto, - performance-*, - -performance-inefficient-vector-operation, - -performance-inefficient-string-concatenation, - -performance-enum-size, - -performance-move-const-arg, - -performance-avoid-endl, - -performance-unnecessary-value-param, - portability-std-allocator-const, - readability-*, - -readability-identifier-naming, - -readability-use-std-min-max, - -readability-math-missing-parentheses, - -readability-simplify-boolean-expr, - -readability-static-accessed-through-instance, - -readability-use-anyofallof, - -readability-enum-initial-value, - -readability-redundant-inline-specifier, - -readability-function-cognitive-complexity, - -readability-function-size, - -readability-identifier-length, - -readability-magic-numbers, - -readability-uppercase-literal-suffix, - -readability-braces-around-statements, - -readability-redundant-access-specifiers, - -readability-else-after-return, - -readability-container-data-pointer, - -readability-implicit-bool-conversion, - -readability-avoid-nested-conditional-operator, - -readability-redundant-member-init, - -readability-redundant-string-init, - -readability-avoid-const-params-in-decls, - -readability-named-parameter, - -readability-convert-member-functions-to-static, - -readability-qualified-auto, - -readability-make-member-function-const, - -readability-isolate-declaration, - -readability-inconsistent-declaration-parameter-name, - -clang-diagnostic-error, - +WarningsAsErrors: '*' HeaderFilterRegex: '.*\.hpp' FormatStyle: file Checks: > diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index fd5797d5..5a20b890 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,15 +1,117 @@ -name: Do not open issues, go to discussions please! -description: Do not open an issue +name: Bug Report +description: Something is not working right +labels: ["bug"] body: - type: checkboxes attributes: - label: Please close this issue. - description: Users cannot open issues. I want my issue to be closed. + label: Already reported ? * + description: Before opening a new bug report, please take a moment to search through the current open issues. If the same bug is already reported, don't open new issue - instead go upvote/comment on an existing one. options: - - label: Yes, I want this issue to be closed. + - label: I have searched the existing open and closed issues. required: true - - type: textarea - id: body + - type: dropdown + id: type attributes: - label: Issue body + label: Regression? + description: | + Regression means that something used to work but no longer does. + **BEFORE CONTINUING**, please check if this bug is a regression or not, and if it is, we need you to bisect with the help of the wiki: https://wiki.hyprland.org/Crashes-and-Bugs/#bisecting-an-issue + multiple: true + options: + - "Definitely a regression - something broke after update (requires bisect)" + - "Probably not a regression / I don't remember it happening before" + - "Not a regression - it's bug regarding new feature" + - "Not a regression - it's an old bug" + - "I don't know, I started using Hyprland only recently" + validations: + required: true + + - type: textarea + id: ver + attributes: + label: System Info and Hyprland Version + description: | + Paste the output of `hyprctl systeminfo` here. If you can't + launch Hyprland, paste the output of `Hyprland --systeminfo`. + value: "
+ System/Version info + + + ``` + + + + ``` + + +
" + validations: + required: true + + - type: textarea + id: desc + attributes: + label: Description + description: "What went wrong?" + validations: + required: true + + - type: textarea + id: repro + attributes: + label: How to reproduce + description: "How can someone else reproduce the issue?" + placeholder: | + 1. ... + 2. ... + 3. ... + + validations: + required: true + + - type: markdown + attributes: + value: | + ## Additional info section + + In the section below you will be asked to upload some files. + + When including text files (such as logs or config), please **always ATTACH** them, and not paste them directly. + + This is important to avoid clutter, spam, and make the issues more readable. + Thanks for your understanding. + + # The main reason to disallow pasting directly or in a dropdown, is to not clutter + # the issue with unnecessary keywords, making the github issue search useless. + - type: checkboxes + attributes: + label: Attach not paste + options: + - label: I understand that all text files must be *attached*, and not pasted directly. If not respected, this issue will likely get closed as spam + required: true + + - type: markdown + attributes: + value: >- + Please be sure to upload the following files below if they are relevant to the issue: + + - Logs can be found in $XDG_RUNTIME_DIR/hypr (sort by date to grab the latest) + - Crash reports are stored in ~/.cache/hyprland or $XDG_CACHE_HOME/hyprland + - Hyprland config files - `hyprctl systeminfo -c > /tmp/hyprland_config_dump.txt` use this command to dump full configuration to a single file. + + - type: checkboxes + attributes: + label: Checklist of files to include below + options: + - label: Hyprland config - `hyprctl systeminfo -c` (always include) + - label: Crash report (always include in case of crash) + - label: Video (always include in case of a visual bug) + - label: Logs (might contain useful info such as errors) + + - type: textarea + id: logs + attributes: + label: Additional info & File uploads + description: | + Tip: You can attach files by clicking this area to highlight it and then dragging files in. diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 00000000..b34786a0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,19 @@ +name: Feature Request +description: I'd like to request additional functionality +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Before opening a new issue, take a moment to search through the current open ones. + + --- + + - type: textarea + id: desc + attributes: + label: Description + description: "Describe your idea" + validations: + required: true + diff --git a/.github/actions/setup_base/action.yml b/.github/actions/setup_base/action.yml index 9fcaabfb..b4c9cf78 100644 --- a/.github/actions/setup_base/action.yml +++ b/.github/actions/setup_base/action.yml @@ -20,11 +20,9 @@ runs: clang \ cmake \ git \ - glaze \ glm \ glslang \ go \ - gtest \ hyprlang \ hyprcursor \ jq \ @@ -46,7 +44,6 @@ runs: libxkbfile \ lld \ meson \ - muparser \ ninja \ pango \ pixman \ @@ -67,6 +64,15 @@ runs: librsvg \ re2 + - name: Get glaze + shell: bash + run: | + git clone https://github.com/stephenberry/glaze.git + cd glaze + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build + cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` + cmake --install build + - name: Get hyprwayland-scanner-git shell: bash run: | @@ -76,25 +82,16 @@ runs: cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` cmake --install build - - name: Get hyprwire-git + - name: Get hyprgraphics-git shell: bash run: | - git clone https://github.com/hyprwm/hyprwire --recursive - cd hyprwire - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build - cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` - cmake --install build + git clone https://github.com/hyprwm/hyprgraphics && cd hyprgraphics && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprgraphics && cmake --install build - name: Get hyprutils-git shell: bash run: | git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build - - name: Get hyprgraphics-git - shell: bash - run: | - git clone https://github.com/hyprwm/hyprgraphics && cd hyprgraphics && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprgraphics && cmake --install build - - name: Get aquamarine-git shell: bash run: | diff --git a/.github/labeler.yml b/.github/labeler.yml index 6b89255a..a0685fcf 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -22,10 +22,6 @@ protocols: - changed-files: - any-glob-to-any-file: ["protocols/**", "src/protocols/**"] -start: - - changed-files: - - any-glob-to-any-file: "start/**" - core: - changed-files: - any-glob-to-any-file: "src/**" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 75b4b7c5..fda97f57 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,8 +1,6 @@ diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2ec558ed..06c1e197 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -21,19 +21,22 @@ jobs: - name: Build Hyprland run: | - CFLAGS=-Werror CXXFLAGS=-Werror make nopch + CFLAGS=-Werror CXXFLAGS=-Werror make all - name: Compress and package artifacts run: | mkdir x86_64-pc-linux-gnu mkdir hyprland + mkdir hyprland/example + mkdir hyprland/assets cp ./LICENSE hyprland/ cp build/Hyprland hyprland/ cp build/hyprctl/hyprctl hyprland/ cp build/hyprpm/hyprpm hyprland/ + cp build/Hyprland hyprland/ cp -r example/ hyprland/ cp -r assets/ hyprland/ - tar -cvJf Hyprland.tar.xz hyprland + tar -cvf Hyprland.tar.xz hyprland - name: Release uses: actions/upload-artifact@v4 @@ -41,43 +44,86 @@ jobs: name: Build archive path: Hyprland.tar.xz - clang-format: - permissions: read-all + meson: if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork - name: "Code Style" + name: "Build Hyprland with Meson (Arch)" runs-on: ubuntu-latest container: image: archlinux steps: - - name: Checkout repository + - name: Checkout repository actions uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions - # - name: clang-format check - # uses: jidicula/clang-format-action@v4.16.0 - # with: - # exclude-regex: ^subprojects$ + - name: Setup base + uses: ./.github/actions/setup_base - - name: Install clang-format - run: | - pacman --noconfirm --noprogressbar -Syyu - pacman --noconfirm --noprogressbar -Sy clang + - name: Configure + run: meson setup build -Ddefault_library=static + + - name: Compile + run: ninja -C build + + no-pch: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork + name: "Build Hyprland without precompiled headers (Arch)" + runs-on: ubuntu-latest + container: + image: archlinux + steps: + - name: Checkout repository actions + uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions + + - name: Setup base + uses: ./.github/actions/setup_base + with: + INSTALL_XORG_PKGS: true + + - name: Compile + run: make nopch + + noxwayland: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork + name: "Build Hyprland in pure Wayland (Arch)" + runs-on: ubuntu-latest + container: + image: archlinux + steps: + - name: Checkout repository actions + uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions + + - name: Setup base + uses: ./.github/actions/setup_base + + - name: Configure + run: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DNO_XWAYLAND:STRING=true -H./ -B./build -G Ninja + + - name: Compile + run: make release + + clang-format: + permissions: read-all + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork + name: "Code Style (Arch)" + runs-on: ubuntu-latest + container: + image: archlinux + steps: + - name: Checkout repository actions + uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions + + - name: Setup base + uses: ./.github/actions/setup_base + + - name: Configure + run: meson setup build -Ddefault_library=static - name: clang-format check - run: .github/workflows/clang-format-check.sh "." "llvm" "^subprojects$" "" - - - name: Save PR head commit SHA - if: failure() && github.event_name == 'pull_request' - shell: bash - run: | - SHA="${{ github.event.pull_request.head.sha }}" - echo "SHA=$SHA" >> $GITHUB_ENV - - name: Save latest commit SHA if not PR - if: failure() && github.event_name != 'pull_request' - shell: bash - run: echo "SHA=${{ github.sha }}" >> $GITHUB_ENV - - - name: Report failure in job summary - if: failure() - run: | - DEEPLINK="${{ github.server_url }}/${{ github.repository }}/commit/${{ env.SHA }}" - echo -e "Format check failed on commit [${GITHUB_SHA:0:8}]($DEEPLINK) with files:\n$(<$GITHUB_WORKSPACE/failing-files.txt)" >> $GITHUB_STEP_SUMMARY + run: ninja -C build clang-format-check diff --git a/.github/workflows/clang-format-check.sh b/.github/workflows/clang-format-check.sh deleted file mode 100755 index 41237aa7..00000000 --- a/.github/workflows/clang-format-check.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env bash -# -# Adapted from https://github.com/jidicula/clang-format-action - -############################################################################### -# check.sh # -############################################################################### -# USAGE: ./entrypoint.sh [] [] -# -# Checks all C/C++/Protobuf/CUDA files (.h, .H, .hpp, .hh, .h++, .hxx and .c, -# .C, .cpp, .cc, .c++, .cxx, .proto, .cu) in the provided GitHub repository path -# (arg1) for conforming to clang-format. If no path is provided or provided path -# is not a directory, all C/C++/Protobuf/CUDA files are checked. If any files -# are incorrectly formatted, the script lists them and exits with 1. -# -# Define your own formatting rules in a .clang-format file at your repository -# root. Otherwise, the provided style guide (arg2) is used as a fallback. - -# format_diff function -# Accepts a filepath argument. The filepath passed to this function must point -# to a C/C++/Protobuf/CUDA file. -format_diff() { - local filepath="$1" - - # Invoke clang-format with dry run and formatting error output - local_format="$(clang-format \ - --dry-run \ - --Werror \ - --style=file \ - --fallback-style="$FALLBACK_STYLE" \ - "${filepath}")" - - local format_status="$?" - if [[ ${format_status} -ne 0 ]]; then - # Append Markdown-bulleted monospaced filepath of failing file to - # summary file. - echo "* \`$filepath\`" >>failing-files.txt - - echo "Failed on file: $filepath" >&2 - echo "$local_format" >&2 - exit_code=1 # flip the global exit code - return "${format_status}" - fi - return 0 -} - -CHECK_PATH="$1" -FALLBACK_STYLE="$2" -EXCLUDE_REGEX="$3" -INCLUDE_REGEX="$4" - -# Set the regex to an empty string regex if nothing was provided -if [[ -z $EXCLUDE_REGEX ]]; then - EXCLUDE_REGEX="^$" -fi - -# Set the filetype regex if nothing was provided. -# Find all C/C++/Protobuf/CUDA files: -# h, H, hpp, hh, h++, hxx -# c, C, cpp, cc, c++, cxx -# ino, pde -# proto -# cu -if [[ -z $INCLUDE_REGEX ]]; then - INCLUDE_REGEX='^.*\.((((c|C)(c|pp|xx|\+\+)?$)|((h|H)h?(pp|xx|\+\+)?$))|(ino|pde|proto|cu))$' -fi - -cd "$GITHUB_WORKSPACE" || exit 2 - -if [[ ! -d $CHECK_PATH ]]; then - echo "Not a directory in the workspace, fallback to all files." >&2 - CHECK_PATH="." -fi - -# initialize exit code -exit_code=0 - -# All files improperly formatted will be printed to the output. -src_files=$(find "$CHECK_PATH" -name .git -prune -o -regextype posix-egrep -regex "$INCLUDE_REGEX" -print) - -# check formatting in each source file -IFS=$'\n' # Loop below should separate on new lines, not spaces. -for file in $src_files; do - # Only check formatting if the path doesn't match the regex - if ! [[ ${file} =~ $EXCLUDE_REGEX ]]; then - format_diff "${file}" - fi -done - -# global exit code is flipped to nonzero if any invocation of `format_diff` has -# a formatting difference. -exit "$exit_code" diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 505829e3..e935a605 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -4,23 +4,43 @@ jobs: clang-format: permissions: write-all if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork - name: "Code Style" + name: "Code Style (Arch)" runs-on: ubuntu-latest + container: + image: archlinux steps: - - name: Checkout repository + - name: Checkout repository actions uses: actions/checkout@v4 + with: + sparse-checkout: .github/actions + + - name: Setup base + uses: ./.github/actions/setup_base + + - name: Configure + run: meson setup build -Ddefault_library=static - name: clang-format check - uses: jidicula/clang-format-action@v4.16.0 - with: - exclude-regex: ^subprojects$ + run: ninja -C build clang-format-check - - name: Create comment + - name: clang-format apply + if: ${{ failure() && github.event_name == 'pull_request' }} + run: ninja -C build clang-format + + - name: Create patch if: ${{ failure() && github.event_name == 'pull_request' }} run: | - echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style).' > clang-format.patch + echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style), or directly apply this patch:' > clang-format.patch + echo '
' >> clang-format.patch + echo 'clang-format.patch' >> clang-format.patch + echo >> clang-format.patch + echo '```diff' >> clang-format.patch + git diff >> clang-format.patch + echo '```' >> clang-format.patch + echo >> clang-format.patch + echo '
' >> clang-format.patch - - name: Post comment + - name: Comment patch if: ${{ failure() && github.event_name == 'pull_request' }} uses: mshick/add-pr-comment@v2 with: diff --git a/.github/workflows/close-issues.yml b/.github/workflows/close-issues.yml deleted file mode 100644 index 55f4e126..00000000 --- a/.github/workflows/close-issues.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: Close Unauthorized Issues - -on: - workflow_dispatch: - issues: - types: [opened] - -jobs: - close-unauthorized-issues: - runs-on: ubuntu-latest - permissions: - issues: write - steps: - # XXX: This *could* be done in Bash by abusing GitHub's own tool to interact with its API - # but that's too much of a hack, and we'll be adding a layer of abstraction. github-script - # is a workflow that eases interaction with GitHub API in the workflow run context. - - name: "Close 'unauthorized' issues" - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const ALLOWED_USERS = ['vaxerski', 'fufexan', 'NotAShelf']; - const CLOSING_COMMENT = 'Users are no longer allowed to open issues themselves, please open a discussion instead.\n\nPlease see the [wiki](https://wiki.hyprland.org/Contributing-and-Debugging/Issue-Guidelines/) on why this is the case.\n\nWe are volunteers, and we need your cooperation to make the best software we can. Thank you for understanding! ❤️\n\n[Open a discussion here](https://github.com/hyprwm/Hyprland/discussions)'; - - async function closeUnauthorizedIssue(issueNumber, userName) { - if (ALLOWED_USERS.includes(userName)) { - console.log(`Issue #${issueNumber} - Created by authorized user ${userName}`); - return; - } - - console.log(`Issue #${issueNumber} - Unauthorized, closing`); - - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issueNumber, - state: 'closed', - state_reason: 'not_planned' - }); - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issueNumber, - body: CLOSING_COMMENT - }); - } - - if (context.eventName === 'issues' && context.payload.action === 'opened') { - // Direct access to the issue that triggered the workflow - const issue = context.payload.issue; - - // Skip if this is a PR - if (issue.pull_request) { - console.log(`Issue #${issue.number} - Skipping, this is a pull request`); - return; - } - - // Process the single issue that triggered the workflow - await closeUnauthorizedIssue(issue.number, issue.user.login); - } else { - // For manual runs, we need to handle pagination - async function* fetchAllOpenIssues() { - let page = 1; - let hasNextPage = true; - - while (hasNextPage) { - const response = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - per_page: 100, - page: page - }); - - if (response.data.length === 0) { - hasNextPage = false; - } else { - for (const issue of response.data) { - yield issue; - } - page++; - } - } - } - - // Process issues one by one - for await (const issue of fetchAllOpenIssues()) { - try { - // Skip pull requests - if (issue.pull_request) { - console.log(`Issue #${issue.number} - Skipping, this is a pull request`); - continue; - } - - await closeUnauthorizedIssue(issue.number, issue.user.login); - } catch (error) { - console.error(`Error processing issue #${issue.number}: ${error.message}`); - } - } - } diff --git a/.github/workflows/new-pr-comment.yml b/.github/workflows/new-pr-comment.yml deleted file mode 100644 index 36ea1909..00000000 --- a/.github/workflows/new-pr-comment.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: "New MR welcome comment" - -on: - pull_request_target: - types: - - opened - -jobs: - comment: - if: > - github.event.pull_request.user.login != 'vaxerski' && - github.event.pull_request.user.login != 'fufexan' && - github.event.pull_request.user.login != 'gulafaran' && - github.event.pull_request.user.login != 'ujint34' && - github.event.pull_request.user.login != 'paideiadilemma' && - github.event.pull_request.user.login != 'notashelf' - runs-on: ubuntu-latest - permissions: - pull-requests: write - - env: - PR_COMMENT: | - Hello and thank you for making a PR to Hyprland! - - Please check the [PR Guidelines](https://wiki.hypr.land/Contributing-and-Debugging/PR-Guidelines/) and make sure your PR follows them. - It will make the entire review process faster. :) - - If your code can be tested, please always add tests. See more [here](https://wiki.hypr.land/Contributing-and-Debugging/Tests/). - - _beep boop, I'm just a bot. A real human will review your PR soon._ - - steps: - - name: Add comment to PR - uses: actions/github-script@v7 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const pr = context.payload.pull_request; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: process.env.PR_COMMENT, - }); diff --git a/.github/workflows/nix-build.yml b/.github/workflows/nix-build.yml new file mode 100644 index 00000000..5b2e81cf --- /dev/null +++ b/.github/workflows/nix-build.yml @@ -0,0 +1,28 @@ +name: Nix (Build) + +on: + workflow_call: + secrets: + CACHIX_AUTH_TOKEN: + required: false + +jobs: + build: + strategy: + matrix: + package: + - hyprland + # - hyprland-cross # cross compiling fails due to qt + # failure chain: hyprland-qtutils -> qt6.qtsvg -> qt6.qtbase -> psqlodbc & qt6.qttranslations + - xdg-desktop-portal-hyprland + + runs-on: ubuntu-latest + steps: + - uses: DeterminateSystems/nix-installer-action@main + + - uses: cachix/cachix-action@v15 + with: + name: hyprland + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + - run: nix build 'github:hyprwm/Hyprland?ref=${{ github.ref }}#${{ matrix.package }}' -L --extra-substituters "https://hyprland.cachix.org" diff --git a/.github/workflows/nix-ci.yml b/.github/workflows/nix-ci.yml index 5b22e992..75c19790 100644 --- a/.github/workflows/nix-ci.yml +++ b/.github/workflows/nix-ci.yml @@ -1,4 +1,4 @@ -name: Nix +name: Nix (CI) on: [push, pull_request, workflow_dispatch] @@ -8,22 +8,7 @@ jobs: uses: ./.github/workflows/nix-update-inputs.yml secrets: inherit - hyprland: + build: if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork) - uses: ./.github/workflows/nix.yml - secrets: inherit - with: - command: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}' -L --extra-substituters "https://hyprland.cachix.org" - - xdph: - if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork) - needs: hyprland - uses: ./.github/workflows/nix.yml - secrets: inherit - with: - command: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}#xdg-desktop-portal-hyprland' -L --extra-substituters "https://hyprland.cachix.org" - - test: - if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork) - uses: ./.github/workflows/nix-test.yml + uses: ./.github/workflows/nix-build.yml secrets: inherit diff --git a/.github/workflows/nix-test.yml b/.github/workflows/nix-test.yml deleted file mode 100644 index 68357093..00000000 --- a/.github/workflows/nix-test.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Nix (Test) - -on: - workflow_call: - secrets: - CACHIX_AUTH_TOKEN: - required: false - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Install Nix - uses: nixbuild/nix-quick-install-action@v31 - with: - nix_conf: | - keep-env-derivations = true - keep-outputs = true - - - name: Restore and save Nix store - uses: nix-community/cache-nix-action@v6 - with: - # restore and save a cache using this key (per job) - primary-key: nix-${{ runner.os }}-${{ github.job }} - # if there's no cache hit, restore a cache by this prefix - restore-prefixes-first-match: nix-${{ runner.os }} - # collect garbage until the Nix store size (in bytes) is at most this number - # before trying to save a new cache - gc-max-store-size-linux: 5G - - - uses: cachix/cachix-action@v15 - with: - name: hyprland - authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - - - name: Run test VM - run: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}#checks.x86_64-linux.tests' -L --extra-substituters "https://hyprland.cachix.org" - - - name: Check exit status - run: grep 0 result/exit_status - - - name: Upload artifacts - if: always() - uses: actions/upload-artifact@v4 - with: - name: logs - path: result diff --git a/.github/workflows/nix-update-inputs.yml b/.github/workflows/nix-update-inputs.yml index a3084b27..c9a094a2 100644 --- a/.github/workflows/nix-update-inputs.yml +++ b/.github/workflows/nix-update-inputs.yml @@ -17,24 +17,7 @@ jobs: with: token: ${{ secrets.PAT }} - - name: Install Nix - uses: nixbuild/nix-quick-install-action@v31 - with: - nix_conf: | - keep-env-derivations = true - keep-outputs = true - - - name: Restore and save Nix store - uses: nix-community/cache-nix-action@v6 - with: - # restore and save a cache using this key (per job) - primary-key: nix-${{ runner.os }}-${{ github.job }} - # if there's no cache hit, restore a cache by this prefix - restore-prefixes-first-match: nix-${{ runner.os }} - # collect garbage until the Nix store size (in bytes) is at most this number - # before trying to save a new cache - gc-max-store-size-linux: 5G - + - uses: DeterminateSystems/nix-installer-action@main - name: Update inputs run: nix/update-inputs.sh diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml deleted file mode 100644 index b46b3795..00000000 --- a/.github/workflows/nix.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Build - -on: - workflow_call: - inputs: - command: - required: true - type: string - description: Command to run - secrets: - CACHIX_AUTH_TOKEN: - required: false - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Install Nix - uses: nixbuild/nix-quick-install-action@v31 - with: - nix_conf: | - keep-env-derivations = true - keep-outputs = true - - - name: Restore and save Nix store - uses: nix-community/cache-nix-action@v6 - with: - # restore and save a cache using this key (per job) - primary-key: nix-${{ runner.os }}-${{ github.job }} - # if there's no cache hit, restore a cache by this prefix - restore-prefixes-first-match: nix-${{ runner.os }} - # collect garbage until the Nix store size (in bytes) is at most this number - # before trying to save a new cache - gc-max-store-size-linux: 5G - - - uses: cachix/cachix-action@v15 - with: - name: hyprland - authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - - - run: ${{ inputs.command }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 09aae111..44baff97 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,36 +9,17 @@ jobs: source-tarball: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v5 + - name: Checkout Hyprland + id: checkout + uses: actions/checkout@v4 with: - fetch-depth: 0 submodules: recursive - - name: Populate git info in version.h.in + - name: Generate version + id: genversion run: | - git fetch --tags --unshallow || true - - COMMIT_HASH=$(git rev-parse HEAD) - BRANCH="${GITHUB_REF_NAME:-$(git rev-parse --abbrev-ref HEAD)}" - COMMIT_MSG=$(git show -s --format=%s | sed 's/[&/]/\\&/g') - COMMIT_DATE=$(git show -s --format=%cd --date=local) - GIT_DIRTY=$(git diff-index --quiet HEAD -- && echo "clean" || echo "dirty") - GIT_TAG=$(git describe --tags --always || echo "unknown") - GIT_COMMITS=$(git rev-list --count HEAD) - - echo "Branch: $BRANCH" - echo "Tag: $GIT_TAG" - - sed -i \ - -e "s|@GIT_COMMIT_HASH@|$COMMIT_HASH|" \ - -e "s|@GIT_BRANCH@|$BRANCH|" \ - -e "s|@GIT_COMMIT_MESSAGE@|$COMMIT_MSG|" \ - -e "s|@GIT_COMMIT_DATE@|$COMMIT_DATE|" \ - -e "s|@GIT_DIRTY@|$GIT_DIRTY|" \ - -e "s|@GIT_TAG@|$GIT_TAG|" \ - -e "s|@GIT_COMMITS@|$GIT_COMMITS|" \ - src/version.h.in + git fetch --unshallow || echo "failed unshallowing" + bash -c scripts/generateVersion.sh - name: Create tarball with submodules id: tar diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..9d2eed80 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,28 @@ +# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. +# +# You can adjust the behavior by modifying this file. +# For more information, see: +# https://github.com/actions/stale +name: Mark stale issues and pull requests + +on: + schedule: + - cron: "7 */4 * * *" + workflow_dispatch: + +jobs: + stale: + if: github.repository == 'hyprwm/Hyprland' + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - uses: actions/stale@v9 + with: + repo-token: ${{ secrets.STALEBOT_PAT }} + stale-issue-label: "stale" + stale-pr-label: "stale" + operations-per-run: 40 + days-before-close: -1 diff --git a/.github/workflows/translation-ai-check.yml b/.github/workflows/translation-ai-check.yml deleted file mode 100644 index d6a62a60..00000000 --- a/.github/workflows/translation-ai-check.yml +++ /dev/null @@ -1,139 +0,0 @@ -name: AI Translation Check - -on: - # pull_request_target: - # types: - # - opened - issue_comment: - types: - - created - -permissions: - contents: read - pull-requests: write - issues: write - -jobs: - review: - name: Review Translation - if: ${{ github.event_name == 'pull_request_target' || (github.event_name == 'issue_comment' && github.event.action == 'created' && github.event.issue.pull_request != null && github.event.comment.user.login == 'vaxerski' && github.event.comment.body == 'ai, please recheck' ) }} - runs-on: ubuntu-latest - env: - OPENAI_MODEL: gpt-5-mini - SYSTEM_PROMPT: | - You are a programmer and a translator. Your job is to review the attached patch for adding translation to a piece of software and make sure the submitted translation is not malicious, and that it makes sense. If the translation is not malicious, and doesn't contain obvious grammatical mistakes, say "Translation check OK". Otherwise, say "Translation check not ok" and list bad entries. - Examples of bad translations include obvious trolling (slurs, etc) or nonsense sentences. Meaningful improvements may be suggested, but if there are only minor improvements, just reply with "Translation check OK". Do not provide anything but the result and (if applicable) the bad entries or improvements. - - AI_PROMPT: Translation patch below. - - steps: - - name: Checkout source code - uses: actions/checkout@v5 - - - uses: dorny/paths-filter@v3 - id: changes - with: - filters: | - i18n: - - 'src/i18n/**' - - - name: Stop if i18n not changed - if: steps.changes.outputs.i18n != 'true' - run: echo "No i18n changes in this PR; skipping." && exit 0 - - - name: Determine PR number - id: pr - run: | - if [ "${{ github.event_name }}" = "pull_request_target" ]; then - echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT" - else - echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" - fi - - - name: Download combined PR diff - id: get_diff - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ steps.pr.outputs.number }} - run: | - # Get the combined diff for the entire PR - curl -sSL \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Accept: application/vnd.github.v3.diff" \ - "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" \ - -o pr.diff - - # Compute character length - LEN=$(wc -c < pr.diff | tr -d ' ') - echo "len=$LEN" >> "$GITHUB_OUTPUT" - if [ "$LEN" -gt 25000 ]; then - echo "too_long=true" >> "$GITHUB_OUTPUT" - else - echo "too_long=false" >> "$GITHUB_OUTPUT" - fi - - echo "got diff:" - cat pr.diff - - - name: Comment when diff length exceeded - if: steps.get_diff.outputs.too_long == 'true' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ steps.pr.outputs.number }} - run: | - jq -n --arg body "Diff length exceeded, can't query API" '{body: ("AI translation check result:\n\n" + $body)}' > body.json - curl -sS -X POST \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \ - --data @body.json - - - name: Query OpenAI and post review - if: steps.get_diff.outputs.too_long == 'false' - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - OPENAI_MODEL: ${{ env.OPENAI_MODEL }} - SYSTEM_PROMPT: ${{ env.SYSTEM_PROMPT }} - AI_PROMPT: ${{ env.AI_PROMPT }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ steps.pr.outputs.number }} - run: | - # Prepare OpenAI chat request payload (embed diff safely) - jq -n \ - --arg model "$OPENAI_MODEL" \ - --arg sys "$SYSTEM_PROMPT" \ - --arg prompt "$AI_PROMPT" \ - --rawfile diff pr.diff \ - '{model:$model, - messages:[ - {role:"system", content:$sys}, - {role:"user", content: ($prompt + "\n\n```diff\n" + $diff + "\n```")} - ] - }' > payload.json - - # Call OpenAI - curl -sS https://api.openai.com/v1/chat/completions \ - -H "Authorization: Bearer $OPENAI_API_KEY" \ - -H "Content-Type: application/json" \ - -d @payload.json > response.json - - # Extract response text - COMMENT=$(jq -r '.choices[0].message.content // empty' response.json) - if [ -z "$COMMENT" ]; then - COMMENT="AI did not return a response." - fi - - # If failed, add a note - ADDITIONAL_NOTE="" - if [[ "$COMMENT" == *"not ok"* ]]; then - ADDITIONAL_NOTE=$(echo -ne "\n\nPlease note this check is a guideline, not a hard requirement. It is here to help you translate. If you disagree with some points, just state that. Any typos should be fixed.") - fi - - # Post the review as a PR comment - jq -n --arg body "$COMMENT" --arg note "$ADDITIONAL_NOTE" '{body: ("AI translation check result:\n\n" + $body + $note)}' > body.json - echo "CURLing https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" - curl -sS -X POST \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \ - --data @body.json diff --git a/.gitignore b/.gitignore index 4e5c2323..a7934790 100644 --- a/.gitignore +++ b/.gitignore @@ -28,12 +28,8 @@ protocols/*.c* protocols/*.h* .ccls-cache *.so -src/render/shaders/*.inc -src/render/shaders/Shaders.hpp hyprctl/hyprctl -hyprctl/hw-protocols/*.c* -hyprctl/hw-protocols/*.h* gmon.out *.out @@ -44,7 +40,6 @@ PKGBUILD src/version.h hyprpm/Makefile hyprctl/Makefile -example/hyprland.desktop **/.#*.* **/#*.*# diff --git a/CMakeLists.txt b/CMakeLists.txt index 87574b82..c4ad4ca0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,6 @@ project( DESCRIPTION "A Modern C++ Wayland Compositor" VERSION ${VER}) -include(CTest) include(CheckIncludeFile) include(GNUInstallDirs) @@ -17,21 +16,15 @@ set(HYPRLAND_VERSION ${VER}) set(PREFIX ${CMAKE_INSTALL_PREFIX}) set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}) set(BINDIR ${CMAKE_INSTALL_BINDIR}) +configure_file(hyprland.pc.in hyprland.pc @ONLY) set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") message(STATUS "Gathering git info") -# Make shader files includable -execute_process(COMMAND ./scripts/generateShaderIncludes.sh - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - RESULT_VARIABLE HYPR_SHADER_GEN_RESULT) -if(NOT HYPR_SHADER_GEN_RESULT EQUAL 0) - message( - FATAL_ERROR - "Failed to generate shader includes (scripts/generateShaderIncludes.sh), exit code: ${HYPR_SHADER_GEN_RESULT}" - ) -endif() +# Get git info hash and branch +execute_process(COMMAND ./scripts/generateVersion.sh + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) find_package(PkgConfig REQUIRED) @@ -39,23 +32,11 @@ find_package(PkgConfig REQUIRED) # provide a .pc file and won't be detected this way pkg_check_modules(udis_dep IMPORTED_TARGET udis86>=1.7.2) -# Find non-pkgconfig udis86, otherwise fallback to subproject +# Fallback to subproject if(NOT udis_dep_FOUND) - find_library(udis_nopc udis86) - if(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND")) - message(STATUS "Found udis86 at ${udis_nopc}") - else() - add_subdirectory("subprojects/udis86") - include_directories("subprojects/udis86") - message(STATUS "udis86 dependency not found, falling back to subproject") - endif() -endif() - -find_library(librt rt) -if("${librt}" MATCHES "librt-NOTFOUND") - unset(LIBRT) -else() - set(LIBRT rt) + add_subdirectory("subprojects/udis86") + include_directories("subprojects/udis86") + message(STATUS "udis86 dependency not found, falling back to subproject") endif() if(CMAKE_BUILD_TYPE) @@ -86,11 +67,9 @@ message( if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Configuring Hyprland in Debug with CMake") add_compile_definitions(HYPRLAND_DEBUG) - set(BUILD_TESTING ON) else() add_compile_options(-O3) message(STATUS "Configuring Hyprland in Release with CMake") - set(BUILD_TESTING OFF) endif() add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}") @@ -102,20 +81,13 @@ set(CXX_STANDARD_REQUIRED ON) add_compile_options( -Wall -Wextra - -Wpedantic -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers - -Wno-gnu-zero-variadic-macro-arguments -Wno-narrowing -Wno-pointer-arith - -Wno-clobbered - -frtti -fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=) -# disable lto as it may break plugins -add_compile_options(-fno-lto) - set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE) set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) @@ -123,158 +95,55 @@ message(STATUS "Checking deps...") find_package(Threads REQUIRED) -set(GLES_VERSION "GLES3") +if(LEGACY_RENDERER) + set(GLES_VERSION "GLES2") +else() + set(GLES_VERSION "GLES3") +endif() find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) -find_package(glslang CONFIG REQUIRED) -set(AQUAMARINE_MINIMUM_VERSION 0.9.3) -set(HYPRLANG_MINIMUM_VERSION 0.6.7) -set(HYPRCURSOR_MINIMUM_VERSION 0.1.7) -set(HYPRUTILS_MINIMUM_VERSION 0.11.0) -set(HYPRGRAPHICS_MINIMUM_VERSION 0.1.6) - -pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=${AQUAMARINE_MINIMUM_VERSION}) -pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=${HYPRLANG_MINIMUM_VERSION}) -pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=${HYPRCURSOR_MINIMUM_VERSION}) -pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=${HYPRUTILS_MINIMUM_VERSION}) -pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=${HYPRGRAPHICS_MINIMUM_VERSION}) +pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.8.0) +pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2) +pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7) +pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.5.1) +pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.1) string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION}) list(GET AQ_VERSION_LIST 0 AQ_VERSION_MAJOR) list(GET AQ_VERSION_LIST 1 AQ_VERSION_MINOR) list(GET AQ_VERSION_LIST 2 AQ_VERSION_PATCH) -set(AQUAMARINE_VERSION "${aquamarine_dep_VERSION}") -set(AQUAMARINE_VERSION_MAJOR "${AQ_VERSION_MAJOR}") -set(AQUAMARINE_VERSION_MINOR "${AQ_VERSION_MINOR}") -set(AQUAMARINE_VERSION_PATCH "${AQ_VERSION_PATCH}") -set(HYPRLANG_VERSION "${hyprlang_dep_VERSION}") -set(HYPRUTILS_VERSION "${hyprutils_dep_VERSION}") -set(HYPRCURSOR_VERSION "${hyprcursor_dep_VERSION}") -set(HYPRGRAPHICS_VERSION "${hyprgraphics_dep_VERSION}") - - -find_package(Git QUIET) - -# Populate variables with env vars if present -set(GIT_COMMIT_HASH "$ENV{GIT_COMMIT_HASH}") -if(NOT GIT_COMMIT_HASH) - set(GIT_COMMIT_HASH "unknown") -endif() - -set(GIT_BRANCH "$ENV{GIT_BRANCH}") -if(NOT GIT_BRANCH) - set(GIT_BRANCH "unknown") -endif() - -set(GIT_COMMIT_MESSAGE "$ENV{GIT_COMMIT_MESSAGE}") -if(NOT GIT_COMMIT_MESSAGE) - set(GIT_COMMIT_MESSAGE "unknown") -endif() - -set(GIT_COMMIT_DATE "$ENV{GIT_COMMIT_DATE}") -if(NOT GIT_COMMIT_DATE) - set(GIT_COMMIT_DATE "unknown") -endif() - -set(GIT_DIRTY "$ENV{GIT_DIRTY}") -if(NOT GIT_DIRTY) - set(GIT_DIRTY "unknown") -endif() - -set(GIT_TAG "$ENV{GIT_TAG}") -if(NOT GIT_TAG) - set(GIT_TAG "unknown") -endif() - -set(GIT_COMMITS "$ENV{GIT_COMMITS}") -if(NOT GIT_COMMITS) - set(GIT_COMMITS "0") -endif() - -if(Git_FOUND) - execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse --show-toplevel - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE GIT_TOPLEVEL - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET - RESULT_VARIABLE GIT_TOPLEVEL_RESULT - ) - - if(GIT_TOPLEVEL_RESULT EQUAL 0) - message(STATUS "Detected git repository root: ${GIT_TOPLEVEL}") - - execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD - WORKING_DIRECTORY ${GIT_TOPLEVEL} - OUTPUT_VARIABLE GIT_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current - WORKING_DIRECTORY ${GIT_TOPLEVEL} - OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND sh "-c" "${GIT_EXECUTABLE} show -s --format=%s --no-show-signature | sed \"s/\\\"/\'/g\"" - WORKING_DIRECTORY ${GIT_TOPLEVEL} - OUTPUT_VARIABLE GIT_COMMIT_MESSAGE OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format=%cd --date=local --no-show-signature - WORKING_DIRECTORY ${GIT_TOPLEVEL} - OUTPUT_VARIABLE GIT_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --quiet HEAD -- - WORKING_DIRECTORY ${GIT_TOPLEVEL} - RESULT_VARIABLE GIT_DIRTY_RESULT) - if(NOT GIT_DIRTY_RESULT EQUAL 0) - set(GIT_DIRTY "dirty") - else() - set(GIT_DIRTY "clean") - endif() - execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags - WORKING_DIRECTORY ${GIT_TOPLEVEL} - OUTPUT_VARIABLE GIT_TAG OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD - WORKING_DIRECTORY ${GIT_TOPLEVEL} - OUTPUT_VARIABLE GIT_COMMITS OUTPUT_STRIP_TRAILING_WHITESPACE) - else() - message(WARNING "No Git repository detected in ${CMAKE_SOURCE_DIR}") - endif() -endif() - -configure_file( - ${CMAKE_SOURCE_DIR}/src/version.h.in - ${CMAKE_SOURCE_DIR}/src/version.h - @ONLY -) - -set_source_files_properties(${CMAKE_SOURCE_DIR}/src/version.h PROPERTIES GENERATED TRUE) - -set(XKBCOMMON_MINIMUM_VERSION 1.11.0) -set(WAYLAND_SERVER_MINIMUM_VERSION 1.22.91) -set(WAYLAND_SERVER_PROTOCOLS_MINIMUM_VERSION 1.45) -set(LIBINPUT_MINIMUM_VERSION 1.28) +add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}") +add_compile_definitions(AQUAMARINE_VERSION_MAJOR=${AQ_VERSION_MAJOR}) +add_compile_definitions(AQUAMARINE_VERSION_MINOR=${AQ_VERSION_MINOR}) +add_compile_definitions(AQUAMARINE_VERSION_PATCH=${AQ_VERSION_PATCH}) +add_compile_definitions(HYPRLANG_VERSION="${hyprlang_dep_VERSION}") +add_compile_definitions(HYPRUTILS_VERSION="${hyprutils_dep_VERSION}") +add_compile_definitions(HYPRCURSOR_VERSION="${hyprcursor_dep_VERSION}") +add_compile_definitions(HYPRGRAPHICS_VERSION="${hyprgraphics_dep_VERSION}") pkg_check_modules( deps REQUIRED - IMPORTED_TARGET GLOBAL - xkbcommon>=${XKBCOMMON_MINIMUM_VERSION} + IMPORTED_TARGET + xkbcommon uuid - wayland-server>=${WAYLAND_SERVER_MINIMUM_VERSION} - wayland-protocols>=${WAYLAND_SERVER_PROTOCOLS_MINIMUM_VERSION} + wayland-server>=1.22.90 + wayland-protocols>=1.41 cairo pango pangocairo pixman-1 xcursor libdrm - libinput>=${LIBINPUT_MINIMUM_VERSION} + libinput gbm gio-2.0 - re2 - muparser - lcms2) + re2) find_package(hyprwayland-scanner 0.3.10 REQUIRED) file(GLOB_RECURSE SRCFILES "src/*.cpp") -get_filename_component(FULL_MAIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp ABSOLUTE) -list(REMOVE_ITEM SRCFILES "${FULL_MAIN_PATH}") set(TRACY_CPP_FILES "") if(USE_TRACY) @@ -282,14 +151,9 @@ if(USE_TRACY) message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES}) endif() -add_library(hyprland_lib STATIC ${SRCFILES}) -add_executable(Hyprland src/main.cpp ${TRACY_CPP_FILES}) -target_link_libraries(Hyprland hyprland_lib) +add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES}) -target_include_directories(hyprland_lib PUBLIC ${deps_INCLUDE_DIRS}) -target_include_directories(Hyprland PUBLIC ${deps_INCLUDE_DIRS}) - -set(USE_GPROF OFF) +set(USE_GPROF ON) if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) message(STATUS "Setting debug flags") @@ -297,8 +161,23 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) if(WITH_ASAN) message(STATUS "Enabling ASan") - target_link_libraries(hyprland_lib PUBLIC asan) - target_compile_options(hyprland_lib PUBLIC -fsanitize=address) + target_link_libraries(Hyprland asan) + target_compile_options(Hyprland PUBLIC -fsanitize=address) + endif() + + if(USE_TRACY) + message(STATUS "Tracy is turned on") + + option(TRACY_ENABLE "" ON) + option(TRACY_ON_DEMAND "" ON) + add_subdirectory(subprojects/tracy) + + target_link_libraries(Hyprland Tracy::TracyClient) + + if(USE_TRACY_GPU) + message(STATUS "Tracy GPU Profiling is turned on") + add_compile_definitions(USE_TRACY_GPU) + endif() endif() add_compile_options(-fno-pie -fno-builtin) @@ -309,27 +188,6 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) endif() endif() -if(USE_TRACY) - message(STATUS "Tracy is turned on") - - option(TRACY_ENABLE "" ON) - option(TRACY_ON_DEMAND "" ON) - add_subdirectory(subprojects/tracy) - - add_compile_options(-fno-omit-frame-pointer) - - target_link_libraries(hyprland_lib PUBLIC Tracy::TracyClient) - - if(USE_TRACY_GPU) - message(STATUS "Tracy GPU Profiling is turned on") - add_compile_definitions(USE_TRACY_GPU) - endif() -endif() - -if(BUILT_WITH_NIX) - add_compile_definitions(BUILT_WITH_NIX) -endif() - check_include_file("execinfo.h" EXECINFOH) if(EXECINFOH) message(STATUS "Configuration supports execinfo") @@ -339,19 +197,24 @@ endif() include(CheckLibraryExists) check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO) if(HAVE_LIBEXECINFO) - target_link_libraries(hyprland_lib PUBLIC execinfo) + target_link_libraries(Hyprland execinfo) endif() check_include_file("sys/timerfd.h" HAS_TIMERFD) pkg_check_modules(epoll IMPORTED_TARGET epoll-shim) if(NOT HAS_TIMERFD AND epoll_FOUND) - target_link_libraries(hyprland_lib PUBLIC PkgConfig::epoll) + target_link_libraries(Hyprland PkgConfig::epoll) endif() check_include_file("sys/inotify.h" HAS_INOTIFY) pkg_check_modules(inotify IMPORTED_TARGET libinotify) if(NOT HAS_INOTIFY AND inotify_FOUND) - target_link_libraries(hyprland_lib PUBLIC PkgConfig::inotify) + target_link_libraries(Hyprland PkgConfig::inotify) +endif() + +if(LEGACY_RENDERER) + message(STATUS "Using the legacy GLES2 renderer!") + add_compile_definitions(LEGACY_RENDERER) endif() if(NO_XWAYLAND) @@ -359,7 +222,10 @@ if(NO_XWAYLAND) add_compile_definitions(NO_XWAYLAND) else() message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...") - set(XWAYLAND_DEPENDENCIES + pkg_check_modules( + xdeps + REQUIRED + IMPORTED_TARGET xcb xcb-render xcb-xfixes @@ -367,21 +233,9 @@ else() xcb-composite xcb-res xcb-errors) - - pkg_check_modules( - xdeps - REQUIRED - IMPORTED_TARGET - ${XWAYLAND_DEPENDENCIES}) - - string(JOIN ", " PKGCONFIG_XWAYLAND_DEPENDENCIES ${XWAYLAND_DEPENDENCIES}) - string(PREPEND PKGCONFIG_XWAYLAND_DEPENDENCIES ", ") - - target_link_libraries(hyprland_lib PUBLIC PkgConfig::xdeps) + target_link_libraries(Hyprland PkgConfig::xdeps) endif() -configure_file(hyprland.pc.in hyprland.pc @ONLY) - if(NO_SYSTEMD) message(STATUS "SYSTEMD support is disabled...") else() @@ -402,42 +256,30 @@ set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) -if(CMAKE_DISABLE_PRECOMPILE_HEADERS) - message(STATUS "Not using precompiled headers") -else() - message(STATUS "Setting precompiled headers") - target_precompile_headers(hyprland_lib PRIVATE - $<$:src/pch/pch.hpp>) -endif() +message(STATUS "Setting precompiled headers") + +target_precompile_headers(Hyprland PRIVATE + $<$:src/pch/pch.hpp>) message(STATUS "Setting link libraries") target_link_libraries( - hyprland_lib - PUBLIC + Hyprland + rt PkgConfig::aquamarine_dep PkgConfig::hyprlang_dep PkgConfig::hyprutils_dep PkgConfig::hyprcursor_dep PkgConfig::hyprgraphics_dep - PkgConfig::deps -) - -target_link_libraries( - Hyprland - ${LIBRT} - hyprland_lib) + PkgConfig::deps) if(udis_dep_FOUND) - target_link_libraries(hyprland_lib PUBLIC PkgConfig::udis_dep) -elseif(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND")) - target_link_libraries(hyprland_lib PUBLIC ${udis_nopc}) + target_link_libraries(Hyprland PkgConfig::udis_dep) else() - target_link_libraries(hyprland_lib PUBLIC libudis86) + target_link_libraries(Hyprland libudis86) endif() # used by `make installheaders`, to ensure the headers are generated add_custom_target(generate-protocol-headers) -set(PROTOCOL_SOURCES "") function(protocolnew protoPath protoName external) if(external) @@ -451,15 +293,10 @@ function(protocolnew protoPath protoName external) COMMAND hyprwayland-scanner ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(hyprland_lib PRIVATE protocols/${protoName}.cpp + target_sources(Hyprland PRIVATE protocols/${protoName}.cpp protocols/${protoName}.hpp) target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp) - - list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp") - set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE) - list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp") - set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE) endfunction() function(protocolWayland) add_custom_command( @@ -469,23 +306,14 @@ function(protocolWayland) hyprwayland-scanner --wayland-enums ${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(hyprland_lib PRIVATE protocols/wayland.cpp protocols/wayland.hpp) + target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp) target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp) - - list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.hpp") - set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE) - list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.cpp") - set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE) endfunction() -if(TARGET OpenGL::GL) - target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads) -else() - target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads) -endif() +target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads) -pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4) +pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.2) if(hyprland_protocols_dep_FOUND) pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir) message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}") @@ -511,12 +339,12 @@ protocolnew("protocols" "kde-server-decoration" true) protocolnew("protocols" "wlr-data-control-unstable-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true) protocolnew("protocols" "wlr-layer-shell-unstable-v1" true) +protocolnew("protocols" "xx-color-management-v4" true) +protocolnew("protocols" "frog-color-management-v1" true) protocolnew("protocols" "wayland-drm" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true) -protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-mapping-v1" - true) protocolnew("staging/tearing-control" "tearing-control-v1" false) protocolnew("staging/fractional-scale" "fractional-scale-v1" false) @@ -551,21 +379,11 @@ protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false) protocolnew("staging/security-context" "security-context-v1" false) protocolnew("staging/content-type" "content-type-v1" false) protocolnew("staging/color-management" "color-management-v1" false) -protocolnew("staging/xdg-toplevel-tag" "xdg-toplevel-tag-v1" false) -protocolnew("staging/xdg-system-bell" "xdg-system-bell-v1" false) -protocolnew("staging/ext-workspace" "ext-workspace-v1" false) -protocolnew("staging/ext-data-control" "ext-data-control-v1" false) -protocolnew("staging/pointer-warp" "pointer-warp-v1" false) -protocolnew("staging/fifo" "fifo-v1" false) -protocolnew("staging/commit-timing" "commit-timing-v1" false) -protocolnew("staging/ext-image-capture-source" "ext-image-capture-source-v1" false) -protocolnew("staging/ext-image-copy-capture" "ext-image-copy-capture-v1" false) protocolwayland() # tools add_subdirectory(hyprctl) -add_subdirectory(start) if(NO_HYPRPM) message(STATUS "hyprpm is disabled") @@ -584,11 +402,6 @@ install( \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \ )") # session file -configure_file( - ${CMAKE_SOURCE_DIR}/example/hyprland.desktop.in - ${CMAKE_SOURCE_DIR}/example/hyprland.desktop - @ONLY -) install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions) @@ -597,6 +410,7 @@ add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}") # installable assets file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*") +list(FILTER INSTALLABLE_ASSETS EXCLUDE REGEX "meson.build") install(FILES ${INSTALLABLE_ASSETS} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr) @@ -630,51 +444,5 @@ install( DIRECTORY ${HEADERS_SRC} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland FILES_MATCHING - PATTERN "*.h" - PATTERN "*.hpp" - PATTERN "*.inc") - -if(BUILD_TESTING OR WITH_TESTS) - message(STATUS "Building tests") - - # hyprtester - add_subdirectory(hyprtester) - - # GTest - find_package(GTest CONFIG REQUIRED) - include(GoogleTest) - file(GLOB_RECURSE TESTFILES "tests/*.cpp") - add_executable(hyprland_gtests ${TESTFILES}) - target_compile_options(hyprland_gtests PRIVATE --coverage) - target_link_options(hyprland_gtests PRIVATE --coverage) - target_include_directories( - hyprland_gtests - PUBLIC "./include" - PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}") - - target_link_libraries(hyprland_gtests hyprland_lib GTest::gtest_main) - - gtest_discover_tests(hyprland_gtests) - - # Enable coverage in main hyprland lib - target_compile_options(hyprland_lib PRIVATE --coverage) - target_link_options(hyprland_lib PRIVATE --coverage) - target_link_libraries(hyprland_lib PUBLIC gcov) - - # Enable coverage in hyprland exe - target_compile_options(Hyprland PRIVATE --coverage) - target_link_options(Hyprland PRIVATE --coverage) - target_link_libraries(Hyprland gcov) -endif() - -if(BUILD_TESTING) - message(STATUS "Testing is enabled") - - enable_testing() - add_custom_target(tests) - - add_dependencies(tests hyprland_gtests) - -else() - message(STATUS "Testing is disabled") -endif() + PATTERN "*.h*" + PATTERN "*.frag") diff --git a/LICENSE b/LICENSE index efdec21a..a34afebb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2022-2026, vaxerski +Copyright (c) 2022-2024, vaxerski All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Makefile b/Makefile index 852fcddf..6c79b8db 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,20 @@ PREFIX = /usr/local stub: @echo "Do not run $(MAKE) directly without any arguments. Please refer to the wiki on how to compile Hyprland." +legacyrenderer: + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build + cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` + +legacyrendererdebug: + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build + cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` + release: cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` debug: - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DTESTS=true -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` nopch: @@ -18,7 +26,6 @@ nopch: clear: rm -rf build rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp - rm -f ./hyprctl/hw-protocols/*.cpp ./hyprctl/hw-protocols/*.hpp all: $(MAKE) clear @@ -45,7 +52,7 @@ installheaders: cmake --build ./build --config Release --target generate-protocol-headers - find src -type f \( -name '*.hpp' -o -name '*.h' -o -name '*.inc' \) -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland + find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland cp ./protocols/*.h* ${PREFIX}/include/hyprland/protocols cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi @@ -88,12 +95,8 @@ asan: @echo "Wayland done" patch -p1 < ./scripts/hyprlandStaticAsan.diff - cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DWITH_ASAN:STRING=True -DUSE_TRACY:STRING=False -DUSE_TRACY_GPU:STRING=False -S . -B ./build + cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DWITH_ASAN:STRING=True -DUSE_TRACY:STRING=False -DUSE_TRACY_GPU:STRING=False -S . -B ./build -G Ninja cmake --build ./build --config Debug --target all @echo "Hyprland done" ASAN_OPTIONS="detect_odr_violation=0,log_path=asan.log" HYPRLAND_NO_CRASHREPORTER=1 ./build/Hyprland -c ~/.config/hypr/hyprland.conf - -test: - $(MAKE) debug - ./build/hyprtester/hyprtester -c hyprtester/test.conf -b ./build/Hyprland -p hyprtester/plugin/hyprtestplugin.so diff --git a/README.md b/README.md index bb74c4e4..0970819c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@
-[![Badge Workflow]][Workflow] +![Badge Workflow] [![Badge License]][License] ![Badge Language] [![Badge Pull Requests]][Pull Requests] @@ -100,7 +100,7 @@ easy IPC, much more QoL stuff than other compositors and more... -[Configure]: https://wiki.hypr.land/Configuring/ +[Configure]: https://wiki.hyprland.org/Configuring/ [Stars]: https://starchart.cc/hyprwm/Hyprland [Hypr]: https://github.com/hyprwm/Hypr @@ -108,10 +108,9 @@ easy IPC, much more QoL stuff than other compositors and more... [Issues]: https://github.com/hyprwm/Hyprland/issues [Todo]: https://github.com/hyprwm/Hyprland/projects?type=beta -[Contribute]: https://wiki.hypr.land/Contributing-and-Debugging/ -[Install]: https://wiki.hypr.land/Getting-Started/Installation/ -[Quick Start]: https://wiki.hypr.land/Getting-Started/Master-Tutorial/ -[Workflow]: https://github.com/hyprwm/Hyprland/actions/workflows/ci.yaml +[Contribute]: https://wiki.hyprland.org/Contributing-and-Debugging/ +[Install]: https://wiki.hyprland.org/Getting-Started/Installation/ +[Quick Start]: https://wiki.hyprland.org/Getting-Started/Master-Tutorial/ [License]: LICENSE @@ -126,9 +125,9 @@ easy IPC, much more QoL stuff than other compositors and more... -[Preview A]: https://i.ibb.co/XxFY75Mk/greerggergerhtrytghjnyhjn.png -[Preview B]: https://i.ibb.co/C1yTb0r/falf.png -[Preview C]: https://i.ibb.co/2Yc4q835/hyprland-preview-b.png +[Preview A]: https://i.ibb.co/C1yTb0r/falf.png +[Preview B]: https://linfindel.github.io/cdn/hyprland-preview-b.png +[Preview C]: https://i.ibb.co/B3GJg28/20221126-20h53m26s-grim.png diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 187165ce..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,32 +0,0 @@ -# Hyprland Development Security Policy - -If you have a bug that affects the security of your system, you may -want to privately disclose it instead of making it immediately public. - -## Supported versions - -_Only_ the most recent release on Github is supported. There are no LTS releases. - -## What is not a security issue - -Some examples of issues that should not be reported as security issues: - -- An app can execute a command when ran outside of a sandbox -- An app can write / read hyprland sockets when ran outside of a sandbox -- Crashes -- Things that are protected via permissions when the permission system is disabled - -## What is a security issue - -Some examples of issues that should be reported as security issues: - -- Sandboxed application executing arbitrary code via Hyprland -- Application being able to modify Hyprland's code on the fly -- Application being able to keylog / track user's activity beyond what the wayland protocols allow - -## How to report security issues - -Please report your security issues via either of these channels: -- Mail: `vaxry [at] vaxry [dot] net` -- Matrix: `@vaxry:matrix.vaxry.net` -- Discord: `@vaxry` diff --git a/VERSION b/VERSION index 524456c7..a758a09a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.54.0 +0.48.0 diff --git a/assets/header.svg b/assets/header.svg index a2b32551..c8cf8222 100644 --- a/assets/header.svg +++ b/assets/header.svg @@ -1,64 +1,72 @@ - - -
# Reporting suggestions Suggestions are welcome. -Many features can be implemented using bash scripts and Hyprland sockets, read up on those [Here](https://wiki.hypr.land/IPC). Please do not suggest features that can be implemented as such. +Many features can be implemented using bash scripts and Hyprland sockets, read up on those [Here](https://wiki.hyprland.org/IPC). Please do not suggest features that can be implemented as such.
@@ -70,7 +70,7 @@ A debug coredump provides more information for debugging and may speed up the pr Make sure you're on latest git. Run `git pull --recurse-submodules` to sync everything. -1. [Compile Hyprland with debug mode](http://wiki.hypr.land/Contributing-and-Debugging/#build-in-debug-mode) +1. [Compile Hyprland with debug mode](http://wiki.hyprland.org/Contributing-and-Debugging/#build-in-debug-mode) > Note: The config file used will be `hyprlandd.conf` instead of `hyprland.conf` 2. `cd ~` diff --git a/docs/hyprctl.1 b/docs/hyprctl.1 index 61cb84fd..8c9f3a23 100644 --- a/docs/hyprctl.1 +++ b/docs/hyprctl.1 @@ -1,19 +1,5 @@ -.\" Automatically generated by Pandoc 3.1.3 +.\" Automatically generated by Pandoc 2.9.2.1 .\" -.\" Define V font for inline verbatim, using C font in formats -.\" that render this, and otherwise B font. -.ie "\f[CB]x\f[]"x" \{\ -. ftr V B -. ftr VI BI -. ftr VB B -. ftr VBI BI -.\} -.el \{\ -. ftr V CR -. ftr VI CI -. ftr VB CB -. ftr VBI CBI -.\} .TH "hyprctl" "1" "" "" "hyprctl User Manual" .hy .SH NAME diff --git a/docs/meson.build b/docs/meson.build new file mode 100644 index 00000000..6ff51d1a --- /dev/null +++ b/docs/meson.build @@ -0,0 +1,2 @@ +install_man('Hyprland.1') +install_man('hyprctl.1') diff --git a/example/hyprland.conf b/example/hyprland.conf index 15b0e4f7..cfe5040b 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -1,6 +1,6 @@ # This is an example Hyprland config file. # Refer to the wiki for more information. -# https://wiki.hypr.land/Configuring/ +# https://wiki.hyprland.org/Configuring/ # Please note not all available settings / options are set here. # For a full list, see the wiki @@ -14,7 +14,7 @@ ### MONITORS ### ################ -# See https://wiki.hypr.land/Configuring/Monitors/ +# See https://wiki.hyprland.org/Configuring/Monitors/ monitor=,preferred,auto,auto @@ -22,12 +22,12 @@ monitor=,preferred,auto,auto ### MY PROGRAMS ### ################### -# See https://wiki.hypr.land/Configuring/Keywords/ +# See https://wiki.hyprland.org/Configuring/Keywords/ # Set programs that you use $terminal = kitty $fileManager = dolphin -$menu = hyprlauncher +$menu = wofi --show drun ################# @@ -46,56 +46,39 @@ $menu = hyprlauncher ### ENVIRONMENT VARIABLES ### ############################# -# See https://wiki.hypr.land/Configuring/Environment-variables/ +# See https://wiki.hyprland.org/Configuring/Environment-variables/ env = XCURSOR_SIZE,24 env = HYPRCURSOR_SIZE,24 -################### -### PERMISSIONS ### -################### - -# See https://wiki.hypr.land/Configuring/Permissions/ -# Please note permission changes here require a Hyprland restart and are not applied on-the-fly -# for security reasons - -# ecosystem { -# enforce_permissions = 1 -# } - -# permission = /usr/(bin|local/bin)/grim, screencopy, allow -# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow -# permission = /usr/(bin|local/bin)/hyprpm, plugin, allow - - ##################### ### LOOK AND FEEL ### ##################### -# Refer to https://wiki.hypr.land/Configuring/Variables/ +# Refer to https://wiki.hyprland.org/Configuring/Variables/ -# https://wiki.hypr.land/Configuring/Variables/#general +# https://wiki.hyprland.org/Configuring/Variables/#general general { gaps_in = 5 gaps_out = 20 border_size = 2 - # https://wiki.hypr.land/Configuring/Variables/#variable-types for info about colors + # https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg col.inactive_border = rgba(595959aa) # Set to true enable resizing windows by clicking and dragging on borders and gaps resize_on_border = false - # Please see https://wiki.hypr.land/Configuring/Tearing/ before you turn this on + # Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on allow_tearing = false layout = dwindle } -# https://wiki.hypr.land/Configuring/Variables/#decoration +# https://wiki.hyprland.org/Configuring/Variables/#decoration decoration { rounding = 10 rounding_power = 2 @@ -111,7 +94,7 @@ decoration { color = rgba(1a1a1aee) } - # https://wiki.hypr.land/Configuring/Variables/#blur + # https://wiki.hyprland.org/Configuring/Variables/#blur blur { enabled = true size = 3 @@ -121,74 +104,58 @@ decoration { } } -# https://wiki.hypr.land/Configuring/Variables/#animations +# https://wiki.hyprland.org/Configuring/Variables/#animations animations { enabled = yes, please :) - # Default curves, see https://wiki.hypr.land/Configuring/Animations/#curves - # NAME, X0, Y0, X1, Y1 - bezier = easeOutQuint, 0.23, 1, 0.32, 1 - bezier = easeInOutCubic, 0.65, 0.05, 0.36, 1 - bezier = linear, 0, 0, 1, 1 - bezier = almostLinear, 0.5, 0.5, 0.75, 1 - bezier = quick, 0.15, 0, 0.1, 1 + # Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more - # Default animations, see https://wiki.hypr.land/Configuring/Animations/ - # NAME, ONOFF, SPEED, CURVE, [STYLE] - animation = global, 1, 10, default - animation = border, 1, 5.39, easeOutQuint - animation = windows, 1, 4.79, easeOutQuint - animation = windowsIn, 1, 4.1, easeOutQuint, popin 87% - animation = windowsOut, 1, 1.49, linear, popin 87% - animation = fadeIn, 1, 1.73, almostLinear - animation = fadeOut, 1, 1.46, almostLinear - animation = fade, 1, 3.03, quick - animation = layers, 1, 3.81, easeOutQuint - animation = layersIn, 1, 4, easeOutQuint, fade - animation = layersOut, 1, 1.5, linear, fade - animation = fadeLayersIn, 1, 1.79, almostLinear - animation = fadeLayersOut, 1, 1.39, almostLinear - animation = workspaces, 1, 1.94, almostLinear, fade - animation = workspacesIn, 1, 1.21, almostLinear, fade - animation = workspacesOut, 1, 1.94, almostLinear, fade - animation = zoomFactor, 1, 7, quick + bezier = easeOutQuint,0.23,1,0.32,1 + bezier = easeInOutCubic,0.65,0.05,0.36,1 + bezier = linear,0,0,1,1 + bezier = almostLinear,0.5,0.5,0.75,1.0 + bezier = quick,0.15,0,0.1,1 + + animation = global, 1, 10, default + animation = border, 1, 5.39, easeOutQuint + animation = windows, 1, 4.79, easeOutQuint + animation = windowsIn, 1, 4.1, easeOutQuint, popin 87% + animation = windowsOut, 1, 1.49, linear, popin 87% + animation = fadeIn, 1, 1.73, almostLinear + animation = fadeOut, 1, 1.46, almostLinear + animation = fade, 1, 3.03, quick + animation = layers, 1, 3.81, easeOutQuint + animation = layersIn, 1, 4, easeOutQuint, fade + animation = layersOut, 1, 1.5, linear, fade + animation = fadeLayersIn, 1, 1.79, almostLinear + animation = fadeLayersOut, 1, 1.39, almostLinear + animation = workspaces, 1, 1.94, almostLinear, fade + animation = workspacesIn, 1, 1.21, almostLinear, fade + animation = workspacesOut, 1, 1.94, almostLinear, fade } -# Ref https://wiki.hypr.land/Configuring/Workspace-Rules/ +# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/ # "Smart gaps" / "No gaps when only" # uncomment all if you wish to use that. # workspace = w[tv1], gapsout:0, gapsin:0 # workspace = f[1], gapsout:0, gapsin:0 -# windowrule { -# name = no-gaps-wtv1 -# match:float = false -# match:workspace = w[tv1] -# -# border_size = 0 -# rounding = 0 -# } -# -# windowrule { -# name = no-gaps-f1 -# match:float = false -# match:workspace = f[1] -# -# border_size = 0 -# rounding = 0 -# } +# windowrule = bordersize 0, floating:0, onworkspace:w[tv1] +# windowrule = rounding 0, floating:0, onworkspace:w[tv1] +# windowrule = bordersize 0, floating:0, onworkspace:f[1] +# windowrule = rounding 0, floating:0, onworkspace:f[1] -# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more +# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more dwindle { pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below preserve_split = true # You probably want this } -# See https://wiki.hypr.land/Configuring/Master-Layout/ for more +# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more master { new_status = master } -# https://wiki.hypr.land/Configuring/Variables/#misc +# https://wiki.hyprland.org/Configuring/Variables/#misc misc { force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :( @@ -199,7 +166,7 @@ misc { ### INPUT ### ############# -# https://wiki.hypr.land/Configuring/Variables/#input +# https://wiki.hyprland.org/Configuring/Variables/#input input { kb_layout = us kb_variant = @@ -216,11 +183,13 @@ input { } } -# See https://wiki.hypr.land/Configuring/Gestures -gesture = 3, horizontal, workspace +# https://wiki.hyprland.org/Configuring/Variables/#gestures +gestures { + workspace_swipe = false +} # Example per-device config -# See https://wiki.hypr.land/Configuring/Keywords/#per-device-input-configs for more +# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more device { name = epic-mouse-v1 sensitivity = -0.5 @@ -231,18 +200,18 @@ device { ### KEYBINDINGS ### ################### -# See https://wiki.hypr.land/Configuring/Keywords/ +# See https://wiki.hyprland.org/Configuring/Keywords/ $mainMod = SUPER # Sets "Windows" key as main modifier -# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more +# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more bind = $mainMod, Q, exec, $terminal bind = $mainMod, C, killactive, -bind = $mainMod, M, exec, command -v hyprshutdown >/dev/null 2>&1 && hyprshutdown || hyprctl dispatch exit +bind = $mainMod, M, exit, bind = $mainMod, E, exec, $fileManager bind = $mainMod, V, togglefloating, bind = $mainMod, R, exec, $menu bind = $mainMod, P, pseudo, # dwindle -bind = $mainMod, J, layoutmsg, togglesplit # dwindle +bind = $mainMod, J, togglesplit, # dwindle # Move focus with mainMod + arrow keys bind = $mainMod, left, movefocus, l @@ -291,8 +260,8 @@ bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle -bindel = ,XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+ -bindel = ,XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%- +bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+ +bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%- # Requires playerctl bindl = , XF86AudioNext, exec, playerctl next @@ -304,38 +273,14 @@ bindl = , XF86AudioPrev, exec, playerctl previous ### WINDOWS AND WORKSPACES ### ############################## -# See https://wiki.hypr.land/Configuring/Window-Rules/ for more -# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules +# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more +# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules -# Example windowrules that are useful +# Example windowrule +# windowrule = float,class:^(kitty)$,title:^(kitty)$ -windowrule { - # Ignore maximize requests from all apps. You'll probably like this. - name = suppress-maximize-events - match:class = .* +# Ignore maximize requests from apps. You'll probably like this. +windowrule = suppressevent maximize, class:.* - suppress_event = maximize -} - -windowrule { - # Fix some dragging issues with XWayland - name = fix-xwayland-drags - match:class = ^$ - match:title = ^$ - match:xwayland = true - match:float = true - match:fullscreen = false - match:pin = false - - no_focus = true -} - -# Hyprland-run windowrule -windowrule { - name = move-hyprland-run - - match:class = hyprland-run - - move = 20 monitor_h-120 - float = yes -} +# Fix some dragging issues with XWayland +windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0 diff --git a/example/hyprland.desktop.in b/example/hyprland.desktop similarity index 75% rename from example/hyprland.desktop.in rename to example/hyprland.desktop index d8e87d60..bb2801a9 100644 --- a/example/hyprland.desktop.in +++ b/example/hyprland.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Name=Hyprland Comment=An intelligent dynamic tiling Wayland compositor -Exec=@PREFIX@/@CMAKE_INSTALL_BINDIR@/start-hyprland +Exec=Hyprland Type=Application DesktopNames=Hyprland Keywords=tiling;wayland;compositor; diff --git a/example/meson.build b/example/meson.build new file mode 100644 index 00000000..a338644e --- /dev/null +++ b/example/meson.build @@ -0,0 +1,10 @@ +install_data( + 'hyprland.conf', + install_dir: join_paths(get_option('datadir'), 'hypr'), + install_tag: 'runtime', +) +install_data( + 'hyprland.desktop', + install_dir: join_paths(get_option('datadir'), 'wayland-sessions'), + install_tag: 'runtime', +) diff --git a/example/screenShader.frag b/example/screenShader.frag index 71598e51..5eeac17a 100644 --- a/example/screenShader.frag +++ b/example/screenShader.frag @@ -1,19 +1,16 @@ // // Example blue light filter shader. -// - -#version 300 es +// precision mediump float; -in vec2 v_texcoord; -layout(location = 0) out vec4 fragColor; +varying vec2 v_texcoord; uniform sampler2D tex; void main() { - vec4 pixColor = texture(tex, v_texcoord); + vec4 pixColor = texture2D(tex, v_texcoord); pixColor[2] *= 0.8; - fragColor = pixColor; + gl_FragColor = pixColor; } diff --git a/flake.lock b/flake.lock index 4a89c0fc..9dbd3bc8 100644 --- a/flake.lock +++ b/flake.lock @@ -16,11 +16,11 @@ ] }, "locked": { - "lastModified": 1772292445, - "narHash": "sha256-4F1Q7U313TKUDDovCC96m/Za4wZcJ3yqtu4eSrj8lk8=", + "lastModified": 1742213273, + "narHash": "sha256-0l0vDb4anfsBu1rOs94bC73Hub+xEivgBAo6QXl2MmU=", "owner": "hyprwm", "repo": "aquamarine", - "rev": "1dbbba659c1cef0b0202ce92cadfe13bae550e8f", + "rev": "484b732195cc53f4536ce4bd59a5c6402b1e7ccf", "type": "github" }, "original": { @@ -32,15 +32,15 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1767039857, - "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", - "owner": "NixOS", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", "repo": "flake-compat", - "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { - "owner": "NixOS", + "owner": "edolstra", "repo": "flake-compat", "type": "github" } @@ -79,11 +79,11 @@ ] }, "locked": { - "lastModified": 1753964049, - "narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=", + "lastModified": 1742215578, + "narHash": "sha256-zfs71PXVVPEe56WEyNi2TJQPs0wabU4WAlq0XV7GcdE=", "owner": "hyprwm", "repo": "hyprcursor", - "rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5", + "rev": "2fd36421c21aa87e2fe3bee11067540ae612f719", "type": "github" }, "original": { @@ -105,11 +105,11 @@ ] }, "locked": { - "lastModified": 1770511807, - "narHash": "sha256-suKmSbSk34uPOJDTg/GbPrKEJutzK08vj0VoTvAFBCA=", + "lastModified": 1739049071, + "narHash": "sha256-3+7TpXMrbsUXSwgr5VAKAnmkzMb6JO+Rvc9XRb5NMg4=", "owner": "hyprwm", "repo": "hyprgraphics", - "rev": "7c75487edd43a71b61adb01cae8326d277aab683", + "rev": "175c6b29b6ff82100539e7c4363a35a02c74dd73", "type": "github" }, "original": { @@ -118,45 +118,6 @@ "type": "github" } }, - "hyprland-guiutils": { - "inputs": { - "aquamarine": [ - "aquamarine" - ], - "hyprgraphics": [ - "hyprgraphics" - ], - "hyprlang": [ - "hyprlang" - ], - "hyprtoolkit": "hyprtoolkit", - "hyprutils": [ - "hyprutils" - ], - "hyprwayland-scanner": [ - "hyprwayland-scanner" - ], - "nixpkgs": [ - "nixpkgs" - ], - "systems": [ - "systems" - ] - }, - "locked": { - "lastModified": 1767023960, - "narHash": "sha256-R2HgtVS1G3KSIKAQ77aOZ+Q0HituOmPgXW9nBNkpp3Q=", - "owner": "hyprwm", - "repo": "hyprland-guiutils", - "rev": "c2e906261142f5dd1ee0bfc44abba23e2754c660", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprland-guiutils", - "type": "github" - } - }, "hyprland-protocols": { "inputs": { "nixpkgs": [ @@ -167,11 +128,11 @@ ] }, "locked": { - "lastModified": 1765214753, - "narHash": "sha256-P9zdGXOzToJJgu5sVjv7oeOGPIIwrd9hAUAP3PsmBBs=", + "lastModified": 1738422629, + "narHash": "sha256-5v+bv75wJWvahyM2xcMTSNNxmV8a7hb01Eey5zYnBJw=", "owner": "hyprwm", "repo": "hyprland-protocols", - "rev": "3f3860b869014c00e8b9e0528c7b4ddc335c21ab", + "rev": "755aef8dab49d0fc4663c715fa4ad221b2aedaed", "type": "github" }, "original": { @@ -180,6 +141,67 @@ "type": "github" } }, + "hyprland-qt-support": { + "inputs": { + "hyprlang": [ + "hyprland-qtutils", + "hyprlang" + ], + "nixpkgs": [ + "hyprland-qtutils", + "nixpkgs" + ], + "systems": [ + "hyprland-qtutils", + "systems" + ] + }, + "locked": { + "lastModified": 1737634706, + "narHash": "sha256-nGCibkfsXz7ARx5R+SnisRtMq21IQIhazp6viBU8I/A=", + "owner": "hyprwm", + "repo": "hyprland-qt-support", + "rev": "8810df502cdee755993cb803eba7b23f189db795", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-qt-support", + "type": "github" + } + }, + "hyprland-qtutils": { + "inputs": { + "hyprland-qt-support": "hyprland-qt-support", + "hyprlang": [ + "hyprlang" + ], + "hyprutils": [ + "hyprland-qtutils", + "hyprlang", + "hyprutils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1739048983, + "narHash": "sha256-REhTcXq4qs3B3cCDtLlYDz0GZvmsBSh947Ub6pQWGTQ=", + "owner": "hyprwm", + "repo": "hyprland-qtutils", + "rev": "3504a293c8f8db4127cb0f7cfc1a318ffb4316f8", + "type": "github" + }, + "original": { + "owner": "hyprwm", + "repo": "hyprland-qtutils", + "type": "github" + } + }, "hyprlang": { "inputs": { "hyprutils": [ @@ -193,11 +215,11 @@ ] }, "locked": { - "lastModified": 1771866172, - "narHash": "sha256-fYFoXhQLrm1rD8vSFKQBOEX4OGCuJdLt1amKfHd5GAw=", + "lastModified": 1741191527, + "narHash": "sha256-kM+11Nch47Xwfgtw2EpRitJuORy4miwoMuRi5tyMBDY=", "owner": "hyprwm", "repo": "hyprlang", - "rev": "0b219224910e7642eb0ed49f0db5ec3d008e3e41", + "rev": "72df3861f1197e41b078faa3e38eedd60e00018d", "type": "github" }, "original": { @@ -206,51 +228,6 @@ "type": "github" } }, - "hyprtoolkit": { - "inputs": { - "aquamarine": [ - "hyprland-guiutils", - "aquamarine" - ], - "hyprgraphics": [ - "hyprland-guiutils", - "hyprgraphics" - ], - "hyprlang": [ - "hyprland-guiutils", - "hyprlang" - ], - "hyprutils": [ - "hyprland-guiutils", - "hyprutils" - ], - "hyprwayland-scanner": [ - "hyprland-guiutils", - "hyprwayland-scanner" - ], - "nixpkgs": [ - "hyprland-guiutils", - "nixpkgs" - ], - "systems": [ - "hyprland-guiutils", - "systems" - ] - }, - "locked": { - "lastModified": 1764592794, - "narHash": "sha256-7CcO+wbTJ1L1NBQHierHzheQGPWwkIQug/w+fhTAVuU=", - "owner": "hyprwm", - "repo": "hyprtoolkit", - "rev": "5cfe0743f0e608e1462972303778d8a0859ee63e", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprtoolkit", - "type": "github" - } - }, "hyprutils": { "inputs": { "nixpkgs": [ @@ -261,11 +238,11 @@ ] }, "locked": { - "lastModified": 1771271487, - "narHash": "sha256-41gEiUS0Pyw3L/ge1l8MXn61cK14VAhgWB/JV8s/oNI=", + "lastModified": 1741534688, + "narHash": "sha256-EV3945SnjOCuRVbGRghsWx/9D89FyshnSO1Q6/TuQ14=", "owner": "hyprwm", "repo": "hyprutils", - "rev": "340a792e3b3d482c4ae5f66d27a9096bdee6d76d", + "rev": "dd1f720cbc2dbb3c71167c9598045dd3261d27b3", "type": "github" }, "original": { @@ -284,11 +261,11 @@ ] }, "locked": { - "lastModified": 1770501770, - "narHash": "sha256-NWRM6+YxTRv+bT9yvlhhJ2iLae1B1pNH3mAL5wi2rlQ=", + "lastModified": 1739870480, + "narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=", "owner": "hyprwm", "repo": "hyprwayland-scanner", - "rev": "0bd8b6cde9ec27d48aad9e5b4deefb3746909d40", + "rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b", "type": "github" }, "original": { @@ -297,39 +274,13 @@ "type": "github" } }, - "hyprwire": { - "inputs": { - "hyprutils": [ - "hyprutils" - ], - "nixpkgs": [ - "nixpkgs" - ], - "systems": [ - "systems" - ] - }, - "locked": { - "lastModified": 1771606233, - "narHash": "sha256-F3PLUqQ/TwgR70U+UeOqJnihJZ2EuunzojYC4g5xHr0=", - "owner": "hyprwm", - "repo": "hyprwire", - "rev": "06c7f1f8c4194786c8400653c4efc49dc14c0f3a", - "type": "github" - }, - "original": { - "owner": "hyprwm", - "repo": "hyprwire", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1772198003, - "narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=", + "lastModified": 1742069588, + "narHash": "sha256-C7jVfohcGzdZRF6DO+ybyG/sqpo1h6bZi9T56sxLy+k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61", + "rev": "c80f6a7e10b39afcc1894e02ef785b1ad0b0d7e5", "type": "github" }, "original": { @@ -348,11 +299,11 @@ ] }, "locked": { - "lastModified": 1772024342, - "narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=", + "lastModified": 1742058297, + "narHash": "sha256-b4SZc6TkKw8WQQssbN5O2DaCEzmFfvSTPYHlx/SFW9Y=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476", + "rev": "59f17850021620cd348ad2e9c0c64f4e6325ce2a", "type": "github" }, "original": { @@ -366,12 +317,11 @@ "aquamarine": "aquamarine", "hyprcursor": "hyprcursor", "hyprgraphics": "hyprgraphics", - "hyprland-guiutils": "hyprland-guiutils", "hyprland-protocols": "hyprland-protocols", + "hyprland-qtutils": "hyprland-qtutils", "hyprlang": "hyprlang", "hyprutils": "hyprutils", "hyprwayland-scanner": "hyprwayland-scanner", - "hyprwire": "hyprwire", "nixpkgs": "nixpkgs", "pre-commit-hooks": "pre-commit-hooks", "systems": "systems", @@ -415,11 +365,11 @@ ] }, "locked": { - "lastModified": 1761431178, - "narHash": "sha256-xzjC1CV3+wpUQKNF+GnadnkeGUCJX+vgaWIZsnz9tzI=", + "lastModified": 1741934139, + "narHash": "sha256-ZhTcTH9FoeAtbPfWGrhkH7RjLJZ7GeF18nygLAMR+WE=", "owner": "hyprwm", "repo": "xdg-desktop-portal-hyprland", - "rev": "4b8801228ff958d028f588f0c2b911dbf32297f9", + "rev": "150b0b6f52bb422a1b232a53698606fe0320dde0", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 6d695bfb..801d96ec 100644 --- a/flake.nix +++ b/flake.nix @@ -35,15 +35,11 @@ inputs.systems.follows = "systems"; }; - hyprland-guiutils = { - url = "github:hyprwm/hyprland-guiutils"; + hyprland-qtutils = { + url = "github:hyprwm/hyprland-qtutils"; inputs.nixpkgs.follows = "nixpkgs"; inputs.systems.follows = "systems"; - inputs.aquamarine.follows = "aquamarine"; - inputs.hyprgraphics.follows = "hyprgraphics"; - inputs.hyprutils.follows = "hyprutils"; inputs.hyprlang.follows = "hyprlang"; - inputs.hyprwayland-scanner.follows = "hyprwayland-scanner"; }; hyprlang = { @@ -65,13 +61,6 @@ inputs.systems.follows = "systems"; }; - hyprwire = { - url = "github:hyprwm/hyprwire"; - inputs.nixpkgs.follows = "nixpkgs"; - inputs.systems.follows = "systems"; - inputs.hyprutils.follows = "hyprutils"; - }; - xdph = { url = "github:hyprwm/xdg-desktop-portal-hyprland"; inputs.nixpkgs.follows = "nixpkgs"; @@ -88,122 +77,91 @@ }; }; - outputs = - inputs@{ - self, - nixpkgs, - systems, - ... - }: - let - inherit (nixpkgs) lib; - eachSystem = lib.genAttrs (import systems); - pkgsFor = eachSystem ( - system: - import nixpkgs { - localSystem = system; - overlays = with self.overlays; [ - hyprland-packages - hyprland-extras - ]; - } - ); - pkgsCrossFor = eachSystem ( - system: crossSystem: - import nixpkgs { - localSystem = system; - inherit crossSystem; - overlays = with self.overlays; [ - hyprland-packages - hyprland-extras - ]; - } - ); - pkgsDebugFor = eachSystem ( - system: - import nixpkgs { - localSystem = system; - overlays = with self.overlays; [ - hyprland-debug - ]; - } - ); - pkgsDebugCrossFor = eachSystem ( - system: crossSystem: - import nixpkgs { - localSystem = system; - inherit crossSystem; - overlays = with self.overlays; [ - hyprland-debug - ]; - } - ); - in - { - overlays = import ./nix/overlays.nix { inherit self lib inputs; }; + outputs = inputs @ { + self, + nixpkgs, + systems, + ... + }: let + inherit (nixpkgs) lib; + eachSystem = lib.genAttrs (import systems); + pkgsFor = eachSystem (system: + import nixpkgs { + localSystem = system; + overlays = with self.overlays; [ + hyprland-packages + hyprland-extras + ]; + }); + pkgsCrossFor = eachSystem (system: crossSystem: + import nixpkgs { + localSystem = system; + inherit crossSystem; + overlays = with self.overlays; [ + hyprland-packages + hyprland-extras + ]; + }); + in { + overlays = import ./nix/overlays.nix {inherit self lib inputs;}; - checks = eachSystem ( - system: - (lib.filterAttrs ( - n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n) - ) self.packages.${system}) - // { - inherit (self.packages.${system}) xdg-desktop-portal-hyprland; - pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run { - src = ./.; - hooks = { - hyprland-treewide-formatter = { - enable = true; - entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter"; - pass_filenames = false; - excludes = [ "subprojects" ]; - always_run = true; - }; + checks = eachSystem (system: + (lib.filterAttrs + (n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n)) + self.packages.${system}) + // { + inherit (self.packages.${system}) xdg-desktop-portal-hyprland; + pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + hyprland-treewide-formatter = { + enable = true; + entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter"; + pass_filenames = false; + excludes = ["subprojects"]; + always_run = true; }; }; - } - // (import ./nix/tests inputs pkgsFor.${system}) - ); - - packages = eachSystem (system: { - default = self.packages.${system}.hyprland; - inherit (pkgsFor.${system}) - # hyprland-packages - hyprland - hyprland-unwrapped - hyprland-with-tests - # hyprland-extras - xdg-desktop-portal-hyprland - ; - inherit (pkgsDebugFor.${system}) hyprland-debug; - hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland; - hyprland-debug-cross = (pkgsDebugCrossFor.${system} "aarch64-linux").hyprland-debug; + }; }); - devShells = eachSystem (system: { - default = - pkgsFor.${system}.mkShell.override - { - inherit (self.packages.${system}.default) stdenv; - } - { - name = "hyprland-shell"; - hardeningDisable = [ "fortify" ]; - inputsFrom = [ pkgsFor.${system}.hyprland ]; - packages = [ pkgsFor.${system}.clang-tools ]; - inherit (self.checks.${system}.pre-commit-check) shellHook; - }; - }); + packages = eachSystem (system: { + default = self.packages.${system}.hyprland; + inherit + (pkgsFor.${system}) + # hyprland-packages + hyprland + hyprland-debug + hyprland-legacy-renderer + hyprland-unwrapped + # hyprland-extras + xdg-desktop-portal-hyprland + ; + hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland; + }); - formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix { }); + devShells = eachSystem (system: { + default = + pkgsFor.${system}.mkShell.override { + inherit (self.packages.${system}.default) stdenv; + } { + name = "hyprland-shell"; + hardeningDisable = ["fortify"]; + inputsFrom = [pkgsFor.${system}.hyprland]; + packages = [pkgsFor.${system}.clang-tools]; + inherit (self.checks.${system}.pre-commit-check) shellHook; + }; + }); - nixosModules.default = import ./nix/module.nix inputs; - homeManagerModules.default = import ./nix/hm-module.nix self; + formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {}); - # Hydra build jobs - # Recent versions of Hydra can aggregate jobsets from 'hydraJobs' instead of a release.nix - # or similar. Remember to filter large or incompatible attributes here. More eval jobs can - # be added by merging, e.g., self.packages // self.devShells. - hydraJobs = self.packages; - }; + nixosModules.default = import ./nix/module.nix inputs; + homeManagerModules.default = import ./nix/hm-module.nix self; + + # Hydra build jobs + # Recent versions of Hydra can aggregate jobsets from 'hydraJobs' intead of a release.nix + # or similar. Remember to filter large or incompatible attributes here. More eval jobs can + # be added by merging, e.g., self.packages // self.devShells. + hydraJobs = self.packages; + }; } diff --git a/hyprctl/CMakeLists.txt b/hyprctl/CMakeLists.txt index 7071ede9..db5ef615 100644 --- a/hyprctl/CMakeLists.txt +++ b/hyprctl/CMakeLists.txt @@ -5,32 +5,11 @@ project( DESCRIPTION "Control utility for Hyprland" ) -pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 hyprwire re2) +pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 re2) -file(GLOB_RECURSE HYPRCTL_SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "hw-protocols/*.cpp" "include/*.hpp") - -add_executable(hyprctl ${HYPRCTL_SRCFILES}) +add_executable(hyprctl "main.cpp") target_link_libraries(hyprctl PUBLIC PkgConfig::hyprctl_deps) -target_include_directories(hyprctl PRIVATE "hw-protocols") - -# Hyprwire - -function(hyprprotocol protoPath protoName) - set(path ${CMAKE_CURRENT_SOURCE_DIR}/${protoPath}) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-spec.hpp - COMMAND hyprwire-scanner --client ${path}/${protoName}.xml - ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/ - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - target_sources(hyprctl PRIVATE hw-protocols/${protoName}-client.cpp - hw-protocols/${protoName}-client.hpp - hw-protocols/${protoName}-spec.hpp) -endfunction() - -hyprprotocol(hw-protocols hyprpaper_core) # binary install(TARGETS hyprctl) diff --git a/hyprctl/src/Strings.hpp b/hyprctl/Strings.hpp similarity index 92% rename from hyprctl/src/Strings.hpp rename to hyprctl/Strings.hpp index 549d84bb..c70ca737 100644 --- a/hyprctl/src/Strings.hpp +++ b/hyprctl/Strings.hpp @@ -49,7 +49,6 @@ commands: the same format as in colors in config. Will reset when Hyprland's config is reloaded setprop ... → Sets a window property - getprop ... → Gets a window property splash → Get the current splash switchxkblayout ... → Sets the xkb layout index for a keyboard systeminfo → Get system info @@ -74,8 +73,11 @@ flags: const std::string_view HYPRPAPER_HELP = R"#(usage: hyprctl [flags] hyprpaper requests: - wallpaper → Issue a wallpaper to call a config wallpaper dynamically. - Arguments are [mon],[path],[fit_mode]. Fit mode is optional. + listactive → Lists all active images + listloaded → Lists all loaded images + preload → Preloads image + unload → Unloads image. Pass 'all' as path to unload all images + wallpaper → Issue a wallpaper to call a config wallpaper dynamically flags: See 'hyprctl --help')#"; @@ -144,7 +146,7 @@ regex: Regular expression by which a window will be searched property: - See https://wiki.hypr.land/Configuring/Using-hyprctl/#setprop for list + See https://wiki.hyprland.org/Configuring/Using-hyprctl/#setprop for list of properties value: @@ -157,18 +159,6 @@ lock: flags: See 'hyprctl --help')#"; -const std::string_view GETPROP_HELP = R"#(usage: hyprctl [flags] getprop - -regex: - Regular expression by which a window will be searched - -property: - See https://wiki.hypr.land/Configuring/Using-hyprctl/#setprop for list - of properties - -flags: - See 'hyprctl --help')#"; - const std::string_view SWITCHXKBLAYOUT_HELP = R"#(usage: [flags] switchxkblayout device: diff --git a/hyprctl/hw-protocols/hyprpaper_core.xml b/hyprctl/hw-protocols/hyprpaper_core.xml deleted file mode 100644 index 3d26a102..00000000 --- a/hyprctl/hw-protocols/hyprpaper_core.xml +++ /dev/null @@ -1,172 +0,0 @@ - - - - BSD 3-Clause License - - Copyright (c) 2025, Hypr Development - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - - - This is the core manager object for hyprpaper operations - - - - - Creates a wallpaper object - - - - - - - Emitted when a new monitor is added. - - - - - - - Emitted when a monitor is removed. - - - - - - - Destroys this object. Children remain alive until destroyed. - - - - - - Creates a status object - - - - - - - - - - - - - - - - - - - - - - - - - This is an object describing a wallpaper - - - - - Set a file path for the wallpaper. This has to be an absolute path from the fs root. - This is required. - - - - - - - Set a fit mode for the wallpaper. This is set to cover by default. - - - - - - - Set a monitor for the wallpaper. Setting this to empty (or not setting at all) will - treat this as a wildcard fallback. - - See hyprpaper_core_manager.add_monitor and hyprpaper_core_manager.remove_monitor - for tracking monitor names. - - - - - - - Applies this object's state to the wallpaper state. Will emit .success on success, - and .failed on failure. - - This object becomes inert after .succeess or .failed, the only valid operation - is to destroy it afterwards. - - - - - - Wallpaper was applied successfully. - - - - - - Wallpaper was not applied. See the error field for more information. - - - - - - - Destroys this object. - - - - - - - This is an object which will emit various status updates. - - - - - Sends the active wallpaper for a given monitor. This will be emitted - immediately after binding, and then every time the path changes. - - - - - - - - Destroys this object. - - - - diff --git a/hyprctl/hyprctl.bash b/hyprctl/hyprctl.bash index ba541653..e26e623e 100644 --- a/hyprctl/hyprctl.bash +++ b/hyprctl/hyprctl.bash @@ -23,7 +23,7 @@ _hyprctl () { local words cword _get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword - declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize sendkeystate) + declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize) declare -A literal_transitions literal_transitions[0]="([120]=14 [43]=2 [125]=21 [81]=2 [3]=21 [51]=2 [50]=2 [128]=2 [89]=2 [58]=21 [8]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [102]=21 [133]=7 [100]=2 [137]=2 [22]=2 [19]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [78]=21 [114]=2 [37]=2 [151]=2 [116]=2 [121]=13 [123]=21 [39]=11 [42]=21 [79]=15 [118]=12)" literal_transitions[1]="([81]=2 [51]=2 [50]=2 [128]=2 [8]=2 [89]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [133]=7 [100]=2 [22]=2 [19]=2 [137]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [114]=2 [37]=2 [151]=2 [116]=2 [39]=11 [118]=12 [121]=13 [120]=14 [79]=15 [43]=2)" diff --git a/hyprctl/hyprctl.fish b/hyprctl/hyprctl.fish index 6ad32041..11309416 100644 --- a/hyprctl/hyprctl.fish +++ b/hyprctl/hyprctl.fish @@ -29,7 +29,7 @@ function _hyprctl set COMP_CWORD (count $COMP_WORDS) end - set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate" + set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" set descriptions set descriptions[1] "Resize the active window" @@ -48,7 +48,7 @@ function _hyprctl set descriptions[22] "Focus the urgent window or the last window" set descriptions[23] "Get the list of defined workspace rules" set descriptions[24] "Move the active workspace to a monitor" - set descriptions[25] "Move window doesn't switch to the workspace" + set descriptions[25] "Move window doesnt switch to the workspace" set descriptions[26] "Interact with hyprpaper if present" set descriptions[29] "Swap the active window with the next or previous in a group" set descriptions[30] "Move the cursor to the corner of the active window" diff --git a/hyprctl/hyprctl.usage b/hyprctl/hyprctl.usage index e13fc9d5..b2c55682 100644 --- a/hyprctl/hyprctl.usage +++ b/hyprctl/hyprctl.usage @@ -1,4 +1,4 @@ -# This is a file fed to complgen to generate bash/fish/zsh completions +# This is a file feeded to complgen to generate bash/fish/zsh completions # Repo: https://github.com/adaszko/complgen # Generate completion scripts: "complgen aot --bash-script hyprctl.bash --fish-script hyprctl.fish --zsh-script hyprctl.zsh ./hyprctl.usage" @@ -106,12 +106,11 @@ hyprctl []... | (execr) "Execute a raw shell command" | (pass) "Pass the key to a specified window" | (sendshortcut) "On shortcut X sends shortcut Y to a specified window" - | (sendkeystate) "Send a key with specific state (down/repeat/up) to a specified window (window must keep focus for events to continue)" | (killactive) "Close the active window" | (closewindow) "Close a specified window" | (workspace) "Change the workspace" | (movetoworkspace) "Move the focused window to a workspace" - | (movetoworkspacesilent) "Move window doesn't switch to the workspace" + | (movetoworkspacesilent) "Move window doesnt switch to the workspace" | (togglefloating) "Toggle the current window's floating state" | (setfloating) "Set the current window's floating state to true" | (settiled) "Set the current window's floating state to false" diff --git a/hyprctl/hyprctl.zsh b/hyprctl/hyprctl.zsh index e0c48ab0..9a858100 100644 --- a/hyprctl/hyprctl.zsh +++ b/hyprctl/hyprctl.zsh @@ -17,7 +17,7 @@ _hyprctl_cmd_0 () { } _hyprctl () { - local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate") + local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize") local -A descriptions descriptions[1]="Resize the active window" @@ -36,7 +36,7 @@ _hyprctl () { descriptions[22]="Focus the urgent window or the last window" descriptions[23]="Get the list of defined workspace rules" descriptions[24]="Move the active workspace to a monitor" - descriptions[25]="Move window doesn't switch to the workspace" + descriptions[25]="Move window doesnt switch to the workspace" descriptions[26]="Interact with hyprpaper if present" descriptions[29]="Swap the active window with the next or previous in a group" descriptions[30]="Move the cursor to the corner of the active window" diff --git a/hyprctl/src/main.cpp b/hyprctl/main.cpp similarity index 74% rename from hyprctl/src/main.cpp rename to hyprctl/main.cpp index 0a33f3ed..25a4eef3 100644 --- a/hyprctl/src/main.cpp +++ b/hyprctl/main.cpp @@ -14,9 +14,6 @@ #include #include #include -#include -#include -#include #include #include @@ -26,12 +23,11 @@ #include #include #include -#include using namespace Hyprutils::String; -using namespace Hyprutils::Memory; #include "Strings.hpp" -#include "hyprpaper/Hyprpaper.hpp" + +#define PAD std::string instanceSignature; bool quiet = false; @@ -41,9 +37,10 @@ struct SInstanceData { uint64_t time; uint64_t pid; std::string wlSocket; + bool valid = true; }; -void log(const std::string_view str) { +void log(const std::string& str) { if (quiet) return; @@ -67,74 +64,47 @@ std::string getRuntimeDir() { return std::string{XDG} + "/hypr"; } -static std::optional toUInt64(const std::string_view str) { - uint64_t value = 0; - const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value); - if (ec != std::errc() || ptr != str.data() + str.size()) - return std::nullopt; - return value; -} - -static std::optional parseInstance(const std::filesystem::directory_entry& entry) { - if (!entry.is_directory()) - return std::nullopt; - - const auto lockPath = entry.path() / "hyprland.lock"; - std::ifstream ifs(lockPath); - if (!ifs.is_open()) - return std::nullopt; - - SInstanceData data; - data.id = entry.path().filename().string(); - - const auto first = std::string_view{data.id}.find_first_of('_'); - const auto last = std::string_view{data.id}.find_last_of('_'); - if (first == std::string_view::npos || last == std::string_view::npos || last <= first) - return std::nullopt; - - auto time = toUInt64(std::string_view{data.id}.substr(first + 1, last - first - 1)); - if (!time) - return std::nullopt; - data.time = *time; - - std::string line; - if (!std::getline(ifs, line)) - return std::nullopt; - - auto pid = toUInt64(std::string_view{line}); - if (!pid) - return std::nullopt; - data.pid = *pid; - - if (!std::getline(ifs, data.wlSocket)) - return std::nullopt; - - if (std::getline(ifs, line) && !line.empty()) - return std::nullopt; // more lines than expected - - return data; -} - std::vector instances() { std::vector result; - std::error_code ec; - const auto runtimeDir = getRuntimeDir(); - if (!std::filesystem::exists(runtimeDir, ec) || ec) - return result; + try { + if (!std::filesystem::exists(getRuntimeDir())) + return {}; + } catch (std::exception& e) { return {}; } - std::filesystem::directory_iterator it(runtimeDir, std::filesystem::directory_options::skip_permission_denied, ec); - if (ec) - return result; + for (const auto& el : std::filesystem::directory_iterator(getRuntimeDir())) { + if (!el.is_directory() || !std::filesystem::exists(el.path().string() + "/hyprland.lock")) + continue; - for (const auto& el : it) { - if (auto instance = parseInstance(el)) - result.emplace_back(std::move(*instance)); + // read lock + SInstanceData* data = &result.emplace_back(); + data->id = el.path().filename().string(); + + try { + data->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1, data->id.find_last_of('_') - (data->id.find_first_of('_') + 1))); + } catch (std::exception& e) { continue; } + + // read file + std::ifstream ifs(el.path().string() + "/hyprland.lock"); + + int i = 0; + for (std::string line; std::getline(ifs, line); ++i) { + if (i == 0) { + try { + data->pid = std::stoull(line); + } catch (std::exception& e) { continue; } + } else if (i == 1) { + data->wlSocket = line; + } else + break; + } + + ifs.close(); } - std::erase_if(result, [](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; }); + std::erase_if(result, [&](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; }); - std::ranges::sort(result, {}, &SInstanceData::time); + std::sort(result.begin(), result.end(), [&](const auto& a, const auto& b) { return a.time < b.time; }); return result; } @@ -176,7 +146,7 @@ int rollingRead(const int socket) { return 0; } -int request(std::string_view arg, int minArgs = 0, bool needRoll = false) { +int request(std::string arg, int minArgs = 0, bool needRoll = false) { const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0); if (SERVERSOCKET < 0) { @@ -202,19 +172,21 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) { return 3; } - sockaddr_un serverAddress = {0}; - serverAddress.sun_family = AF_UNIX; + const std::string USERID = std::to_string(getUID()); + + sockaddr_un serverAddress = {0}; + serverAddress.sun_family = AF_UNIX; std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.socket.sock"; strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1); - if (connect(SERVERSOCKET, rc(&serverAddress), SUN_LEN(&serverAddress)) < 0) { + if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { log("Couldn't connect to " + socketPath + ". (4)"); return 4; } - auto sizeWritten = write(SERVERSOCKET, arg.data(), arg.size()); + auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length()); if (sizeWritten < 0) { log("Couldn't write (5)"); @@ -228,23 +200,23 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) { constexpr size_t BUFFER_SIZE = 8192; char buffer[BUFFER_SIZE] = {0}; - // read all data until server closes the connection - // this handles partial writes on the server side under high load - while (true) { - sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); + sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); + if (sizeWritten < 0) { + if (errno == EWOULDBLOCK) + log("Hyprland IPC didn't respond in time\n"); + log("Couldn't read (6)"); + return 6; + } + + reply += std::string(buffer, sizeWritten); + + while (sizeWritten == BUFFER_SIZE) { + sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); if (sizeWritten < 0) { - if (errno == EWOULDBLOCK) - log("Hyprland IPC didn't respond in time\n"); log("Couldn't read (6)"); return 6; } - - if (sizeWritten == 0) { - // server closed connection, we're done - break; - } - reply += std::string(buffer, sizeWritten); } @@ -255,7 +227,7 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) { return 0; } -int requestIPC(std::string_view filename, std::string_view arg) { +int requestIPC(std::string filename, std::string arg) { const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0); if (SERVERSOCKET < 0) { @@ -271,11 +243,13 @@ int requestIPC(std::string_view filename, std::string_view arg) { sockaddr_un serverAddress = {0}; serverAddress.sun_family = AF_UNIX; - std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/" + filename; + const std::string USERID = std::to_string(getUID()); + + std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/" + filename; strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1); - if (connect(SERVERSOCKET, rc(&serverAddress), SUN_LEN(&serverAddress)) < 0) { + if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { log("Couldn't connect to " + socketPath + ". (3)"); return 3; } @@ -283,7 +257,7 @@ int requestIPC(std::string_view filename, std::string_view arg) { arg = arg.substr(arg.find_first_of('/') + 1); // strip flags arg = arg.substr(arg.find_first_of(' ') + 1); // strip "hyprpaper" - auto sizeWritten = write(SERVERSOCKET, arg.data(), arg.size()); + auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length()); if (sizeWritten < 0) { log("Couldn't write (4)"); @@ -306,12 +280,16 @@ int requestIPC(std::string_view filename, std::string_view arg) { return 0; } -int requestHyprsunset(std::string_view arg) { +int requestHyprpaper(std::string arg) { + return requestIPC(".hyprpaper.sock", arg); +} + +int requestHyprsunset(std::string arg) { return requestIPC(".hyprsunset.sock", arg); } -void batchRequest(std::string_view arg, bool json) { - std::string commands(arg.substr(arg.find_first_of(' ') + 1)); +void batchRequest(std::string arg, bool json) { + std::string commands = arg.substr(arg.find_first_of(' ') + 1); if (json) { RE2::GlobalReplace(&commands, ";\\s*", ";j/"); @@ -382,7 +360,7 @@ int main(int argc, char** argv) { parseArgs = false; continue; } - if (parseArgs && (ARGS[i][0] == '-') && !(isNumber(ARGS[i], true) || isNumber(ARGS[i].substr(0, ARGS[i].length() - 1), true)) /* For stuff like -2 or -2, */) { + if (parseArgs && (ARGS[i][0] == '-') && !isNumber(ARGS[i], true) /* For stuff like -2 */) { // parse if (ARGS[i] == "-j" && !fullArgs.contains("j")) { fullArgs += "j"; @@ -424,8 +402,6 @@ int main(int argc, char** argv) { std::println("{}", PLUGIN_HELP); } else if (cmd == "setprop") { std::println("{}", SETPROP_HELP); - } else if (cmd == "getprop") { - std::println("{}", GETPROP_HELP); } else if (cmd == "switchxkblayout") { std::println("{}", SWITCHXKBLAYOUT_HELP); } else { @@ -476,7 +452,7 @@ int main(int argc, char** argv) { const auto INSTANCES = instances(); - if (INSTANCENO < 0 || sc(INSTANCENO) >= INSTANCES.size()) { + if (INSTANCENO < 0 || static_cast(INSTANCENO) >= INSTANCES.size()) { log("no such instance\n"); return 1; } @@ -497,12 +473,9 @@ int main(int argc, char** argv) { if (fullRequest.contains("/--batch")) batchRequest(fullRequest, json); - else if (fullRequest.contains("/hyprpaper")) { - auto result = Hyprpaper::makeHyprpaperRequest(fullRequest); - if (!result) - log(std::format("error: {}", result.error())); - exitStatus = !result; - } else if (fullRequest.contains("/hyprsunset")) + else if (fullRequest.contains("/hyprpaper")) + exitStatus = requestHyprpaper(fullRequest); + else if (fullRequest.contains("/hyprsunset")) exitStatus = requestHyprsunset(fullRequest); else if (fullRequest.contains("/switchxkblayout")) exitStatus = request(fullRequest, 2); diff --git a/hyprctl/meson.build b/hyprctl/meson.build new file mode 100644 index 00000000..d6769b84 --- /dev/null +++ b/hyprctl/meson.build @@ -0,0 +1,27 @@ +executable( + 'hyprctl', + 'main.cpp', + dependencies: [ + dependency('hyprutils', version: '>= 0.1.1'), + dependency('re2', required: true) + ], + install: true, +) + +install_data( + 'hyprctl.bash', + install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'), + install_tag: 'runtime', + rename: 'hyprctl', +) +install_data( + 'hyprctl.fish', + install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'), + install_tag: 'runtime', +) +install_data( + 'hyprctl.zsh', + install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'), + install_tag: 'runtime', + rename: '_hyprctl', +) diff --git a/hyprctl/src/helpers/Memory.hpp b/hyprctl/src/helpers/Memory.hpp deleted file mode 100644 index 1d3a9e07..00000000 --- a/hyprctl/src/helpers/Memory.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include -#include - -using namespace Hyprutils::Memory; - -#define SP CSharedPointer -#define WP CWeakPointer -#define UP CUniquePointer diff --git a/hyprctl/src/hyprpaper/Hyprpaper.cpp b/hyprctl/src/hyprpaper/Hyprpaper.cpp deleted file mode 100644 index 93f4182a..00000000 --- a/hyprctl/src/hyprpaper/Hyprpaper.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "Hyprpaper.hpp" -#include "../helpers/Memory.hpp" - -#include -#include -#include -#include - -#include - -#include -using namespace Hyprutils::String; - -using namespace std::string_literals; - -constexpr const char* SOCKET_NAME = ".hyprpaper.sock"; -static SP g_coreImpl; - -constexpr const uint32_t PROTOCOL_VERSION_SUPPORTED = 2; - -// -static hyprpaperCoreWallpaperFitMode fitFromString(const std::string_view& sv) { - if (sv == "contain") - return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_CONTAIN; - if (sv == "fit" || sv == "stretch") - return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_STRETCH; - if (sv == "tile") - return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_TILE; - return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_COVER; -} - -static std::expected resolvePath(const std::string_view& sv) { - std::error_code ec; - auto can = std::filesystem::canonical(sv, ec); - - if (ec) - return std::unexpected(std::format("invalid path: {}", ec.message())); - - return can; -} - -static std::expected getFullPath(const std::string_view& sv) { - if (sv.empty()) - return std::unexpected("empty path"); - - if (sv[0] == '~') { - static auto HOME = getenv("HOME"); - if (!HOME || HOME[0] == '\0') - return std::unexpected("home path but no $HOME"); - - return resolvePath(std::string{HOME} + "/"s + std::string{sv.substr(1)}); - } - - return resolvePath(sv); -} - -static std::expected doWallpaper(const std::string_view& RHS) { - CVarList2 args(std::string{RHS}, 0, ','); - - const std::string MONITOR = std::string{args[0]}; - const auto& PATH_RAW = args[1]; - const auto& FIT = args[2]; - - if (PATH_RAW.empty()) - return std::unexpected("not enough args"); - - const auto RTDIR = getenv("XDG_RUNTIME_DIR"); - - if (!RTDIR || RTDIR[0] == '\0') - return std::unexpected("can't send: no XDG_RUNTIME_DIR"); - - const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE"); - - if (!HIS || HIS[0] == '\0') - return std::unexpected("can't send: no HYPRLAND_INSTANCE_SIGNATURE (not running under hyprland)"); - - const auto PATH = getFullPath(PATH_RAW); - - if (!PATH) - return std::unexpected(std::format("bad path: {}", PATH_RAW)); - - auto socketPath = RTDIR + "/hypr/"s + HIS + "/"s + SOCKET_NAME; - - auto socket = Hyprwire::IClientSocket::open(socketPath); - - if (!socket) - return std::unexpected("can't send: failed to connect to hyprpaper (is it running?)"); - - g_coreImpl = makeShared(PROTOCOL_VERSION_SUPPORTED); - - socket->addImplementation(g_coreImpl); - - if (!socket->waitForHandshake()) - return std::unexpected("can't send: wire handshake failed"); - - auto spec = socket->getSpec(g_coreImpl->protocol()->specName()); - - if (!spec) - return std::unexpected("can't send: hyprpaper doesn't have the spec?!"); - - auto manager = makeShared(socket->bindProtocol(g_coreImpl->protocol(), std::min(PROTOCOL_VERSION_SUPPORTED, spec->specVer()))); - - if (!manager) - return std::unexpected("wire error: couldn't create manager"); - - auto wallpaper = makeShared(manager->sendGetWallpaperObject()); - - if (!wallpaper) - return std::unexpected("wire error: couldn't create wallpaper object"); - - bool canExit = false; - std::optional err; - - wallpaper->setFailed([&canExit, &err](uint32_t code) { - canExit = true; - switch (code) { - case HYPRPAPER_CORE_APPLYING_ERROR_INVALID_PATH: err = std::format("failed to set wallpaper: Invalid path", code); break; - case HYPRPAPER_CORE_APPLYING_ERROR_INVALID_MONITOR: err = std::format("failed to set wallpaper: Invalid monitor", code); break; - default: err = std::format("failed to set wallpaper: unknown error, code {}", code); break; - } - }); - wallpaper->setSuccess([&canExit]() { canExit = true; }); - - wallpaper->sendPath(PATH->c_str()); - wallpaper->sendMonitorName(MONITOR.c_str()); - if (!FIT.empty()) - wallpaper->sendFitMode(fitFromString(FIT)); - - wallpaper->sendApply(); - - while (!canExit) { - socket->dispatchEvents(true); - } - - if (err) - return std::unexpected(*err); - - return {}; -} - -static std::expected doListActive() { - const auto RTDIR = getenv("XDG_RUNTIME_DIR"); - - if (!RTDIR || RTDIR[0] == '\0') - return std::unexpected("can't send: no XDG_RUNTIME_DIR"); - - const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE"); - - if (!HIS || HIS[0] == '\0') - return std::unexpected("can't send: no HYPRLAND_INSTANCE_SIGNATURE (not running under hyprland)"); - - auto socketPath = RTDIR + "/hypr/"s + HIS + "/"s + SOCKET_NAME; - - auto socket = Hyprwire::IClientSocket::open(socketPath); - - if (!socket) - return std::unexpected("can't send: failed to connect to hyprpaper (is it running?)"); - - g_coreImpl = makeShared(PROTOCOL_VERSION_SUPPORTED); - - socket->addImplementation(g_coreImpl); - - if (!socket->waitForHandshake()) - return std::unexpected("can't send: wire handshake failed"); - - auto spec = socket->getSpec(g_coreImpl->protocol()->specName()); - - if (!spec) - return std::unexpected("can't send: hyprpaper doesn't have the spec?!"); - - if (spec->specVer() < 2) - return std::unexpected("can't send: hyprpaper protocol version too low (hyprpaper too old)"); - - auto manager = makeShared(socket->bindProtocol(g_coreImpl->protocol(), std::min(PROTOCOL_VERSION_SUPPORTED, spec->specVer()))); - - if (!manager) - return std::unexpected("wire error: couldn't create manager"); - - auto status = makeShared(manager->sendGetStatusObject()); - - status->setActiveWallpaper([](const char* mon, const char* wp) { std::println("{}: {}", mon, wp); }); - - socket->roundtrip(); - - return {}; -} - -std::expected Hyprpaper::makeHyprpaperRequest(const std::string_view& rq) { - if (!rq.contains(' ')) - return std::unexpected("Invalid request"); - - if (!rq.starts_with("/hyprpaper ")) - return std::unexpected("Invalid request"); - - std::string_view LHS, RHS; - auto spacePos = rq.find(' ', 12); - LHS = rq.substr(11, spacePos - 11); - RHS = rq.substr(spacePos + 1); - - if (LHS == "wallpaper") - return doWallpaper(RHS); - else if (LHS == "listactive") - return doListActive(); - else - return std::unexpected("invalid hyprpaper request"); - - return {}; -} diff --git a/hyprctl/src/hyprpaper/Hyprpaper.hpp b/hyprctl/src/hyprpaper/Hyprpaper.hpp deleted file mode 100644 index 167b0a8d..00000000 --- a/hyprctl/src/hyprpaper/Hyprpaper.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include - -namespace Hyprpaper { - std::expected makeHyprpaperRequest(const std::string_view& rq); -}; \ No newline at end of file diff --git a/hyprland.pc.in b/hyprland.pc.in index bf5764f3..81a0ef11 100644 --- a/hyprland.pc.in +++ b/hyprland.pc.in @@ -4,5 +4,4 @@ Name: Hyprland URL: https://github.com/hyprwm/Hyprland Description: Hyprland header files Version: @HYPRLAND_VERSION@ -Requires: aquamarine >= @AQUAMARINE_MINIMUM_VERSION@, hyprcursor >= @HYPRCURSOR_MINIMUM_VERSION@, hyprgraphics >= @HYPRGRAPHICS_MINIMUM_VERSION@, hyprlang >= @HYPRLANG_MINIMUM_VERSION@, hyprutils >= @HYPRUTILS_MINIMUM_VERSION@, libdrm, egl, cairo, xkbcommon >= @XKBCOMMON_MINIMUM_VERSION@, libinput >= @LIBINPUT_MINIMUM_VERSION@, wayland-server >= @WAYLAND_SERVER_MINIMUM_VERSION@@PKGCONFIG_XWAYLAND_DEPENDENCIES@ Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland diff --git a/hyprpm/CMakeLists.txt b/hyprpm/CMakeLists.txt index 7d5b8eda..d744ac16 100644 --- a/hyprpm/CMakeLists.txt +++ b/hyprpm/CMakeLists.txt @@ -9,11 +9,11 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") set(CMAKE_CXX_STANDARD 23) -pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0) +pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.2.4) -find_package(glaze 6.0.1 QUIET) +find_package(glaze QUIET) if (NOT glaze_FOUND) - set(GLAZE_VERSION v6.0.1) + set(GLAZE_VERSION v4.2.3) message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent") include(FetchContent) FetchContent_Declare( @@ -21,7 +21,6 @@ if (NOT glaze_FOUND) GIT_REPOSITORY https://github.com/stephenberry/glaze.git GIT_TAG ${GLAZE_VERSION} GIT_SHALLOW TRUE - EXCLUDE_FROM_ALL ) FetchContent_MakeAvailable(glaze) endif() diff --git a/hyprpm/hyprpm.fish b/hyprpm/hyprpm.fish index 56e301eb..82561bd8 100644 --- a/hyprpm/hyprpm.fish +++ b/hyprpm/hyprpm.fish @@ -29,8 +29,8 @@ function _hyprpm set descriptions[6] "Show help menu" set descriptions[7] "Check and update all plugins if needed" set descriptions[8] "Install a new plugin repository from git" - set descriptions[9] "Enable too much logging" - set descriptions[10] "Enable too much logging" + set descriptions[9] "Enable too much loggin" + set descriptions[10] "Enable too much loggin" set descriptions[11] "Force an operation ignoring checks (e.g. update -f)" set descriptions[12] "Disable shallow cloning of Hyprland sources" set descriptions[13] "Remove a plugin repository" diff --git a/hyprpm/hyprpm.usage b/hyprpm/hyprpm.usage index 0d7c07fb..c38bdd62 100644 --- a/hyprpm/hyprpm.usage +++ b/hyprpm/hyprpm.usage @@ -3,7 +3,7 @@ hyprpm []... ::= (--notify | -n) "Send a hyprland notification for important events (e.g. load fail)" | (--help | -h) "Show help menu" - | (--verbose | -v) "Enable too much logging" + | (--verbose | -v) "Enable too much loggin" | (--force | -f) "Force an operation ignoring checks (e.g. update -f)" | (--no-shallow | -s) "Disable shallow cloning of Hyprland sources" ; diff --git a/hyprpm/hyprpm.zsh b/hyprpm/hyprpm.zsh index 3946a695..859c5313 100644 --- a/hyprpm/hyprpm.zsh +++ b/hyprpm/hyprpm.zsh @@ -19,8 +19,8 @@ _hyprpm () { descriptions[6]="Show help menu" descriptions[7]="Check and update all plugins if needed" descriptions[8]="Install a new plugin repository from git" - descriptions[9]="Enable too much logging" - descriptions[10]="Enable too much logging" + descriptions[9]="Enable too much loggin" + descriptions[10]="Enable too much loggin" descriptions[11]="Force an operation ignoring checks (e.g. update -f)" descriptions[12]="Disable shallow cloning of Hyprland sources" descriptions[13]="Remove a plugin repository" diff --git a/hyprpm/src/core/DataState.cpp b/hyprpm/src/core/DataState.cpp index 64f3cfa0..55f148a4 100644 --- a/hyprpm/src/core/DataState.cpp +++ b/hyprpm/src/core/DataState.cpp @@ -1,44 +1,22 @@ #include "DataState.hpp" -#include #include #include -#include #include #include "PluginManager.hpp" -#include "../helpers/Die.hpp" -#include "../helpers/Sys.hpp" -#include "../helpers/StringUtils.hpp" - -static std::string getTempRoot() { - static auto ENV = getenv("XDG_RUNTIME_DIR"); - if (!ENV) { - std::cerr << "\nERROR: XDG_RUNTIME_DIR not set!\n"; - exit(1); - } - - const auto STR = ENV + std::string{"/hyprpm/"}; - - if (!std::filesystem::exists(STR)) - mkdir(STR.c_str(), S_IRWXU); - - return STR; -} - -// write the state to a file -static bool writeState(const std::string& str, const std::string& to) { - // create temp file in a safe temp root - std::ofstream of(getTempRoot() + ".temp-state", std::ios::trunc); - if (!of.good()) - return false; - - of << str; - of.close(); - - return NSys::root::install(getTempRoot() + ".temp-state", to, "644"); -} std::filesystem::path DataState::getDataStatePath() { - return std::filesystem::path("/var/cache/hyprpm/" + g_pPluginManager->m_szUsername); + const auto HOME = getenv("HOME"); + if (!HOME) { + std::println(stderr, "DataState: no $HOME"); + throw std::runtime_error("no $HOME"); + return ""; + } + + const auto XDG_DATA_HOME = getenv("XDG_DATA_HOME"); + + if (XDG_DATA_HOME) + return std::filesystem::path{XDG_DATA_HOME} / "hyprpm"; + return std::filesystem::path{HOME} / ".local/share/hyprpm"; } std::string DataState::getHeadersPath() { @@ -63,37 +41,25 @@ std::vector DataState::getPluginStates() { } void DataState::ensureStateStoreExists() { - std::error_code ec; - if (!std::filesystem::exists(getHeadersPath(), ec) || ec) { - std::println("{}", infoString("The hyprpm state store doesn't exist. Creating now...")); - if (!std::filesystem::exists("/var/cache/hyprpm/", ec) || ec) { - if (!NSys::root::createDirectory("/var/cache/hyprpm", "755")) - Debug::die("ensureStateStoreExists: Failed to run a superuser cmd"); - } - if (!std::filesystem::exists(getDataStatePath(), ec) || ec) { - if (!NSys::root::createDirectory(getDataStatePath().string(), "755")) - Debug::die("ensureStateStoreExists: Failed to run a superuser cmd"); - } - if (!NSys::root::createDirectory(getHeadersPath(), "755")) - Debug::die("ensureStateStoreExists: Failed to run a superuser cmd"); - } + const auto PATH = getDataStatePath(); + + if (!std::filesystem::exists(PATH)) + std::filesystem::create_directories(PATH); + + if (!std::filesystem::exists(getHeadersPath())) + std::filesystem::create_directories(getHeadersPath()); } void DataState::addNewPluginRepo(const SPluginRepository& repo) { ensureStateStoreExists(); - const auto PATH = getDataStatePath() / repo.name; + const auto PATH = getDataStatePath() / repo.name; - std::error_code ec; - if (!std::filesystem::exists(PATH, ec) || ec) { - if (!NSys::root::createDirectory(PATH.string(), "755")) - Debug::die("addNewPluginRepo: failed to create cache dir"); - } + std::filesystem::create_directories(PATH); // clang-format off auto DATA = toml::table{ {"repository", toml::table{ {"name", repo.name}, - {"author", repo.author}, {"hash", repo.hash}, {"url", repo.url}, {"rev", repo.rev} @@ -102,11 +68,9 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) { for (auto const& p : repo.plugins) { const auto filename = p.name + ".so"; - // copy .so to the good place and chmod 755 - if (std::filesystem::exists(p.filename)) { - if (!NSys::root::install(p.filename, (PATH / filename).string(), "0755")) - Debug::die("addNewPluginRepo: failed to install so file"); - } + // copy .so to the good place + if (std::filesystem::exists(p.filename)) + std::filesystem::copy_file(p.filename, PATH / filename); DATA.emplace(p.name, toml::table{ {"filename", filename}, @@ -116,39 +80,40 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) { } // clang-format on - std::stringstream ss; - ss << DATA; - - if (!writeState(ss.str(), (PATH / "state.toml").string())) - Debug::die("{}", failureString("Failed to write plugin state")); + std::ofstream ofs(PATH / "state.toml", std::ios::trunc); + ofs << DATA; + ofs.close(); } -bool DataState::pluginRepoExists(const SPluginRepoIdentifier identifier) { +bool DataState::pluginRepoExists(const std::string& urlOrName) { ensureStateStoreExists(); - for (const auto& stateFile : getPluginStates()) { - const auto STATE = toml::parse_file(stateFile.c_str()); - const auto NAME = STATE["repository"]["name"].value_or(""); - const auto AUTHOR = STATE["repository"]["author"].value_or(""); - const auto URL = STATE["repository"]["url"].value_or(""); + const auto PATH = getDataStatePath(); - if (identifier.matches(URL, NAME, AUTHOR)) + for (const auto& stateFile : getPluginStates()) { + const auto STATE = toml::parse_file(stateFile.c_str()); + const auto NAME = STATE["repository"]["name"].value_or(""); + const auto URL = STATE["repository"]["url"].value_or(""); + + if (URL == urlOrName || NAME == urlOrName) return true; } return false; } -void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) { +void DataState::removePluginRepo(const std::string& urlOrName) { ensureStateStoreExists(); - for (const auto& stateFile : getPluginStates()) { - const auto STATE = toml::parse_file(stateFile.c_str()); - const auto NAME = STATE["repository"]["name"].value_or(""); - const auto AUTHOR = STATE["repository"]["author"].value_or(""); - const auto URL = STATE["repository"]["url"].value_or(""); + const auto PATH = getDataStatePath(); + + for (const auto& stateFile : getPluginStates()) { + const auto STATE = toml::parse_file(stateFile.c_str()); + const auto NAME = STATE["repository"]["name"].value_or(""); + const auto URL = STATE["repository"]["url"].value_or(""); + + if (URL == urlOrName || NAME == urlOrName) { - if (identifier.matches(URL, NAME, AUTHOR)) { // unload the plugins!! for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) { if (!file.path().string().ends_with(".so")) @@ -157,14 +122,7 @@ void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) { g_pPluginManager->loadUnloadPlugin(std::filesystem::absolute(file.path()), false); } - const auto PATH = stateFile.parent_path().string(); - - if (!PATH.starts_with("/var/cache/hyprpm") || PATH.contains('\'')) - return; // WTF? - - // scary! - if (!NSys::root::removeRecursive(PATH)) - Debug::die("removePluginRepo: failed to remove dir"); + std::filesystem::remove_all(stateFile.parent_path()); return; } } @@ -173,43 +131,36 @@ void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) { void DataState::updateGlobalState(const SGlobalState& state) { ensureStateStoreExists(); - const auto PATH = getDataStatePath(); + const auto PATH = getDataStatePath(); - std::error_code ec; - if (!std::filesystem::exists(PATH, ec) || ec) { - if (!NSys::root::createDirectory(PATH.string(), "755")) - Debug::die("updateGlobalState: failed to create dir"); - } + std::filesystem::create_directories(PATH); // clang-format off auto DATA = toml::table{ {"state", toml::table{ - {"hash", state.headersAbiCompiled}, + {"hash", state.headersHashCompiled}, {"dont_warn_install", state.dontWarnInstall} }} }; // clang-format on - std::stringstream ss; - ss << DATA; - - if (!writeState(ss.str(), (PATH / "state.toml").string())) - Debug::die("{}", failureString("Failed to write plugin state")); + std::ofstream ofs(PATH / "state.toml", std::ios::trunc); + ofs << DATA; + ofs.close(); } SGlobalState DataState::getGlobalState() { ensureStateStoreExists(); - const auto stateFile = getDataStatePath() / "state.toml"; + const auto stateFile = getDataStatePath() / "state.toml"; - std::error_code ec; - if (!std::filesystem::exists(stateFile, ec) || ec) + if (!std::filesystem::exists(stateFile)) return SGlobalState{}; auto DATA = toml::parse_file(stateFile.c_str()); SGlobalState state; - state.headersAbiCompiled = DATA["state"]["hash"].value_or(""); - state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false); + state.headersHashCompiled = DATA["state"]["hash"].value_or(""); + state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false); return state; } @@ -217,22 +168,22 @@ SGlobalState DataState::getGlobalState() { std::vector DataState::getAllRepositories() { ensureStateStoreExists(); + const auto PATH = getDataStatePath(); + std::vector repos; for (const auto& stateFile : getPluginStates()) { const auto STATE = toml::parse_file(stateFile.c_str()); - const auto NAME = STATE["repository"]["name"].value_or(""); - const auto AUTHOR = STATE["repository"]["author"].value_or(""); - const auto URL = STATE["repository"]["url"].value_or(""); - const auto REV = STATE["repository"]["rev"].value_or(""); - const auto HASH = STATE["repository"]["hash"].value_or(""); + const auto NAME = STATE["repository"]["name"].value_or(""); + const auto URL = STATE["repository"]["url"].value_or(""); + const auto REV = STATE["repository"]["rev"].value_or(""); + const auto HASH = STATE["repository"]["hash"].value_or(""); SPluginRepository repo; - repo.hash = HASH; - repo.name = NAME; - repo.author = AUTHOR; - repo.url = URL; - repo.rev = REV; + repo.hash = HASH; + repo.name = NAME; + repo.url = URL; + repo.rev = REV; for (const auto& [key, val] : STATE) { if (key == "repository") @@ -251,26 +202,19 @@ std::vector DataState::getAllRepositories() { return repos; } -bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled) { +bool DataState::setPluginEnabled(const std::string& name, bool enabled) { ensureStateStoreExists(); + const auto PATH = getDataStatePath(); + for (const auto& stateFile : getPluginStates()) { const auto STATE = toml::parse_file(stateFile.c_str()); for (const auto& [key, val] : STATE) { if (key == "repository") continue; - switch (identifier.type) { - case IDENTIFIER_NAME: - if (key.str() != identifier.name) - continue; - break; - case IDENTIFIER_AUTHOR_NAME: - if (STATE["repository"]["author"] != identifier.author || key.str() != identifier.name) - continue; - break; - default: return false; - } + if (key.str() != name) + continue; const auto FAILED = STATE[key]["failed"].value_or(false); @@ -280,11 +224,9 @@ bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool en auto modifiedState = STATE; (*modifiedState[key].as_table()).insert_or_assign("enabled", enabled); - std::stringstream ss; - ss << modifiedState; - - if (!writeState(ss.str(), stateFile.string())) - Debug::die("{}", failureString("Failed to write plugin state")); + std::ofstream state(stateFile, std::ios::trunc); + state << modifiedState; + state.close(); return true; } @@ -292,18 +234,3 @@ bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool en return false; } - -void DataState::purgeAllCache() { - std::error_code ec; - if (!std::filesystem::exists(getDataStatePath()) && !ec) { - std::println("{}", infoString("Nothing to do")); - return; - } - - const auto PATH = getDataStatePath().string(); - if (PATH.contains('\'')) - return; - // scary! - if (!NSys::root::removeRecursive(PATH)) - Debug::die("Failed to run a superuser cmd"); -} diff --git a/hyprpm/src/core/DataState.hpp b/hyprpm/src/core/DataState.hpp index d9872b90..ebb550ba 100644 --- a/hyprpm/src/core/DataState.hpp +++ b/hyprpm/src/core/DataState.hpp @@ -5,8 +5,8 @@ #include "Plugin.hpp" struct SGlobalState { - std::string headersAbiCompiled = ""; - bool dontWarnInstall = false; + std::string headersHashCompiled = ""; + bool dontWarnInstall = false; }; namespace DataState { @@ -15,11 +15,10 @@ namespace DataState { std::vector getPluginStates(); void ensureStateStoreExists(); void addNewPluginRepo(const SPluginRepository& repo); - void removePluginRepo(const SPluginRepoIdentifier identifier); - bool pluginRepoExists(const SPluginRepoIdentifier identifier); + void removePluginRepo(const std::string& urlOrName); + bool pluginRepoExists(const std::string& urlOrName); void updateGlobalState(const SGlobalState& state); - void purgeAllCache(); SGlobalState getGlobalState(); - bool setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled); + bool setPluginEnabled(const std::string& name, bool enabled); std::vector getAllRepositories(); -}; +}; \ No newline at end of file diff --git a/hyprpm/src/core/HyprlandSocket.cpp b/hyprpm/src/core/HyprlandSocket.cpp deleted file mode 100644 index 47142e54..00000000 --- a/hyprpm/src/core/HyprlandSocket.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "HyprlandSocket.hpp" -#include -#include -#include "../helpers/StringUtils.hpp" -#include -#include -#include -#include -#include - -using namespace Hyprutils::Memory; - -static int getUID() { - const auto UID = getuid(); - const auto PWUID = getpwuid(UID); - return PWUID ? PWUID->pw_uid : UID; -} - -static std::string getRuntimeDir() { - const auto XDG = getenv("XDG_RUNTIME_DIR"); - - if (!XDG) { - const std::string USERID = std::to_string(getUID()); - return "/run/user/" + USERID + "/hypr"; - } - - return std::string{XDG} + "/hypr"; -} - -std::string NHyprlandSocket::send(const std::string& cmd) { - const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0); - - if (SERVERSOCKET < 0) { - std::println("{}", failureString("Couldn't open a socket (1)")); - return ""; - } - - const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE"); - - if (!HIS) { - std::println("{}", failureString("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?) (3)")); - return ""; - } - - sockaddr_un serverAddress = {0}; - serverAddress.sun_family = AF_UNIX; - - std::string socketPath = getRuntimeDir() + "/" + HIS + "/.socket.sock"; - - strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1); - - if (connect(SERVERSOCKET, rc(&serverAddress), SUN_LEN(&serverAddress)) < 0) { - std::println("{}", failureString("Couldn't connect to " + socketPath + ". (4)")); - return ""; - } - - auto sizeWritten = write(SERVERSOCKET, cmd.c_str(), cmd.length()); - - if (sizeWritten < 0) { - std::println("{}", failureString("Couldn't write (5)")); - return ""; - } - - std::string reply = ""; - constexpr size_t BUFFER_SIZE = 8192; - char buffer[BUFFER_SIZE] = {0}; - - sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); - - if (sizeWritten < 0) { - std::println("{}", failureString("Couldn't read (6)")); - return ""; - } - - reply += std::string(buffer, sizeWritten); - - while (sizeWritten == BUFFER_SIZE) { - sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE); - if (sizeWritten < 0) { - std::println("{}", failureString("Couldn't read (7)")); - return ""; - } - reply += std::string(buffer, sizeWritten); - } - - close(SERVERSOCKET); - - return reply; -} diff --git a/hyprpm/src/core/HyprlandSocket.hpp b/hyprpm/src/core/HyprlandSocket.hpp deleted file mode 100644 index e33be5ca..00000000 --- a/hyprpm/src/core/HyprlandSocket.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -namespace NHyprlandSocket { - std::string send(const std::string& cmd); -}; \ No newline at end of file diff --git a/hyprpm/src/core/Manifest.cpp b/hyprpm/src/core/Manifest.cpp index 90f6fbff..754d9d69 100644 --- a/hyprpm/src/core/Manifest.cpp +++ b/hyprpm/src/core/Manifest.cpp @@ -1,12 +1,6 @@ #include "Manifest.hpp" #include -#include - -// Alphanumerics and -_ allowed for plugin names. No magic names. -// [A-Za-z0-9\-_]* -static bool validManifestName(const std::string_view& n) { - return std::ranges::all_of(n, [](const char& c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '_' || c == '=' || (c >= '0' && c <= '9'); }); -} +#include CManifest::CManifest(const eManifestType type, const std::string& path) { auto manifest = toml::parse_file(path); @@ -17,17 +11,11 @@ CManifest::CManifest(const eManifestType type, const std::string& path) { continue; CManifest::SManifestPlugin plugin; - - if (!validManifestName(key.str())) { - m_good = false; - return; - } - plugin.name = key; - m_plugins.push_back(plugin); + m_vPlugins.push_back(plugin); } - for (auto& plugin : m_plugins) { + for (auto& plugin : m_vPlugins) { plugin.description = manifest[plugin.name]["description"].value_or("?"); plugin.version = manifest[plugin.name]["version"].value_or("?"); plugin.output = manifest[plugin.name]["build"]["output"].value_or("?"); @@ -49,21 +37,21 @@ CManifest::CManifest(const eManifestType type, const std::string& path) { } if (plugin.output.empty() || plugin.buildSteps.empty()) { - m_good = false; + m_bGood = false; return; } } } else if (type == MANIFEST_HYPRPM) { - m_repository.name = manifest["repository"]["name"].value_or(""); - auto authors = manifest["repository"]["authors"].as_array(); + m_sRepository.name = manifest["repository"]["name"].value_or(""); + auto authors = manifest["repository"]["authors"].as_array(); if (authors) { for (auto&& a : *authors) { - m_repository.authors.push_back(a.as_string()->value_or("?")); + m_sRepository.authors.push_back(a.as_string()->value_or("?")); } } else { auto author = manifest["repository"]["author"].value_or(""); if (!std::string{author}.empty()) - m_repository.authors.push_back(author); + m_sRepository.authors.push_back(author); } auto pins = manifest["repository"]["commit_pins"].as_array(); @@ -71,7 +59,7 @@ CManifest::CManifest(const eManifestType type, const std::string& path) { for (auto&& pin : *pins) { auto pinArr = pin.as_array(); if (pinArr && pinArr->get(1)) - m_repository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get())); + m_sRepository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get())); } } @@ -80,17 +68,11 @@ CManifest::CManifest(const eManifestType type, const std::string& path) { continue; CManifest::SManifestPlugin plugin; - - if (!validManifestName(key.str())) { - m_good = false; - return; - } - plugin.name = key; - m_plugins.push_back(plugin); + m_vPlugins.push_back(plugin); } - for (auto& plugin : m_plugins) { + for (auto& plugin : m_vPlugins) { plugin.description = manifest[plugin.name]["description"].value_or("?"); plugin.output = manifest[plugin.name]["output"].value_or("?"); plugin.since = manifest[plugin.name]["since_hyprland"].value_or(0); @@ -112,12 +94,12 @@ CManifest::CManifest(const eManifestType type, const std::string& path) { } if (plugin.output.empty() || plugin.buildSteps.empty()) { - m_good = false; + m_bGood = false; return; } } } else { // ??? - m_good = false; + m_bGood = false; } } \ No newline at end of file diff --git a/hyprpm/src/core/Manifest.hpp b/hyprpm/src/core/Manifest.hpp index 41a49350..19f967eb 100644 --- a/hyprpm/src/core/Manifest.hpp +++ b/hyprpm/src/core/Manifest.hpp @@ -27,8 +27,8 @@ class CManifest { std::string name; std::vector authors; std::vector> commitPins; - } m_repository; + } m_sRepository; - std::vector m_plugins; - bool m_good = true; + std::vector m_vPlugins; + bool m_bGood = true; }; \ No newline at end of file diff --git a/hyprpm/src/core/Plugin.cpp b/hyprpm/src/core/Plugin.cpp deleted file mode 100644 index 3aaa8592..00000000 --- a/hyprpm/src/core/Plugin.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "Plugin.hpp" - -SPluginRepoIdentifier SPluginRepoIdentifier::fromUrl(const std::string& url) { - return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = url}; -} - -SPluginRepoIdentifier SPluginRepoIdentifier::fromName(const std::string& name) { - return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = name}; -} - -SPluginRepoIdentifier SPluginRepoIdentifier::fromAuthorName(const std::string& author, const std::string& name) { - return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author}; -} - -SPluginRepoIdentifier SPluginRepoIdentifier::fromString(const std::string& string) { - if (string.find(':') != std::string::npos) { - return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = string}; - } else { - auto slashPos = string.find('/'); - if (slashPos != std::string::npos) { - std::string author = string.substr(0, slashPos); - std::string name = string.substr(slashPos + 1, string.size() - slashPos - 1); - return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author}; - } else { - return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = string}; - } - } -} - -std::string SPluginRepoIdentifier::toString() const { - switch (type) { - case IDENTIFIER_NAME: return name; - case IDENTIFIER_AUTHOR_NAME: return author + '/' + name; - case IDENTIFIER_URL: return url; - } - - return ""; -} - -bool SPluginRepoIdentifier::matches(const std::string& url, const std::string& name, const std::string& author) const { - switch (type) { - case IDENTIFIER_URL: return this->url == url; - case IDENTIFIER_NAME: return this->name == name; - case IDENTIFIER_AUTHOR_NAME: return this->author == author && this->name == name; - } - - return false; -} diff --git a/hyprpm/src/core/Plugin.hpp b/hyprpm/src/core/Plugin.hpp index a8c74084..e66031c9 100644 --- a/hyprpm/src/core/Plugin.hpp +++ b/hyprpm/src/core/Plugin.hpp @@ -14,27 +14,6 @@ struct SPluginRepository { std::string url; std::string rev; std::string name; - std::string author; std::vector plugins; std::string hash; -}; - -enum ePluginRepoIdentifierType { - IDENTIFIER_URL, - IDENTIFIER_NAME, - IDENTIFIER_AUTHOR_NAME -}; - -struct SPluginRepoIdentifier { - ePluginRepoIdentifierType type; - std::string url = ""; - std::string name = ""; - std::string author = ""; - - static SPluginRepoIdentifier fromString(const std::string& string); - static SPluginRepoIdentifier fromUrl(const std::string& Url); - static SPluginRepoIdentifier fromName(const std::string& name); - static SPluginRepoIdentifier fromAuthorName(const std::string& author, const std::string& name); - std::string toString() const; - bool matches(const std::string& url, const std::string& name, const std::string& author) const; -}; +}; \ No newline at end of file diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index 6621a49f..3343cbe0 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -4,14 +4,10 @@ #include "../progress/CProgressBar.hpp" #include "Manifest.hpp" #include "DataState.hpp" -#include "HyprlandSocket.hpp" -#include "../helpers/Sys.hpp" -#include "../helpers/Die.hpp" #include #include #include -#include #include #include #include @@ -27,10 +23,8 @@ #include #include -#include using namespace Hyprutils::String; using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; static std::string execAndGet(std::string cmd) { cmd += " 2>&1"; @@ -55,13 +49,6 @@ static std::string getTempRoot() { return STR; } -CPluginManager::CPluginManager() { - if (NSys::isSuperuser()) - Debug::die("Don't run hyprpm as a superuser."); - - m_szUsername = getpwuid(NSys::getUID())->pw_name; -} - SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) { static bool onceRunning = false; static bool onceInstalled = false; @@ -79,33 +66,40 @@ SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) { else onceInstalled = true; - const auto HLVERCALL = running ? NHyprlandSocket::send("j/version") : execAndGet("Hyprland --version-json"); + const auto HLVERCALL = running ? execAndGet("hyprctl version") : execAndGet("Hyprland --version"); + if (m_bVerbose) + std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL)); - auto jsonQuery = glz::read_json(HLVERCALL); - - if (!jsonQuery) { - std::println("{}", failureString("failed to get the current hyprland version. Are you running hyprland?")); + if (!HLVERCALL.contains("Tag:")) { + std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland.")); return SHyprlandVersion{}; } - auto hlbranch = (*jsonQuery)["branch"].get_string(); - auto hlcommit = (*jsonQuery)["commit"].get_string(); - auto abiHash = (*jsonQuery)["abiHash"].get_string(); - auto hldate = (*jsonQuery)["commit_date"].get_string(); - auto hlcommits = (*jsonQuery)["commits"].get_string(); + std::string hlcommit = HLVERCALL.substr(HLVERCALL.find("at commit") + 10); + hlcommit = hlcommit.substr(0, hlcommit.find_first_of(' ')); - auto flags = (*jsonQuery)["flags"].get_array(); - bool isNix = std::ranges::any_of(flags, [](const auto& f) { return f.is_string() && f.get_string() == std::string_view{"nix"}; }); + std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12); + hlbranch = hlbranch.substr(0, hlbranch.find(" at commit ")); - size_t commits = 0; + std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6); + hldate = hldate.substr(0, hldate.find('\n')); + + std::string hlcommits; + + if (HLVERCALL.contains("commits:")) { + hlcommits = HLVERCALL.substr(HLVERCALL.find("commits:") + 9); + hlcommits = hlcommits.substr(0, hlcommits.find(' ')); + } + + int commits = 0; try { - commits = std::stoull(hlcommits); + commits = std::stoi(hlcommits); } catch (...) { ; } if (m_bVerbose) - std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}, nix: {}", hlcommit, hlbranch, hldate, commits, isNix)); + std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits)); - auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, abiHash, commits, isNix}; + auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits}; if (running) verRunning = ver; @@ -131,24 +125,15 @@ bool CPluginManager::createSafeDirectory(const std::string& path) { return true; } -bool CPluginManager::validArg(const std::string& s) { - return !s.contains("'") && !s.ends_with("\\") && !s.starts_with("\\"); -} - bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) { const auto HLVER = getHyprlandVersion(); - if (!validArg(url) || !validArg(rev)) { - std::println(stderr, "\n{}", failureString("url or rev invalid")); - return false; - } - if (!hasDeps()) { - std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc")); + std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config")); return false; } - if (DataState::pluginRepoExists(SPluginRepoIdentifier::fromUrl(url))) { + if (DataState::pluginRepoExists(url)) { std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed.")); return false; } @@ -156,18 +141,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& auto GLOBALSTATE = DataState::getGlobalState(); if (!GLOBALSTATE.dontWarnInstall) { std::println("{}!{} Disclaimer: {}", Colors::YELLOW, Colors::RED, Colors::RESET); - std::println("plugins, especially not official, have no guarantee of stability, availability or security.\n" + std::println("plugins, especially not official, have no guarantee of stability, availablity or security.\n" "Run them at your own risk.\n" "This message will not appear again."); GLOBALSTATE.dontWarnInstall = true; DataState::updateGlobalState(GLOBALSTATE); } - if (GLOBALSTATE.headersAbiCompiled.empty()) { - std::println("\n{}", failureString("Cannot find headers in the global state. Try running hyprpm update first.")); - return false; - } - std::cout << Colors::GREEN << "✔" << Colors::RESET << Colors::RED << " adding a new plugin repository " << Colors::RESET << "from " << url << "\n " << Colors::RED << "MAKE SURE" << Colors::RESET << " that you trust the authors. " << Colors::RED << "DO NOT" << Colors::RESET << " install random plugins without verifying the code and author.\n " @@ -207,7 +187,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& progress.printMessageAbove(infoString("Cloning {}", url)); - std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), url, USERNAME)); + std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), url, USERNAME)); if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) { std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret)); @@ -245,14 +225,14 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& return false; } - if (!pManifest->m_good) { + if (!pManifest->m_bGood) { std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest")); return false; } progress.m_iSteps = 2; - progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_plugins.size()) + " plugins:")); - for (auto const& pl : pManifest->m_plugins) { + progress.printMessageAbove(successString("parsed manifest, found " + std::to_string(pManifest->m_vPlugins.size()) + " plugins:")); + for (auto const& pl : pManifest->m_vPlugins) { std::string message = "→ " + pl.name + " by "; for (auto const& a : pl.authors) { message += a + ", "; @@ -265,12 +245,12 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& progress.printMessageAbove(message); } - if (rev.empty() && !pManifest->m_repository.commitPins.empty()) { + if (!pManifest->m_sRepository.commitPins.empty()) { // check commit pins - progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_repository.commitPins.size())); + progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size())); - for (auto const& [hl, plugin] : pManifest->m_repository.commitPins) { + for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) { if (hl != HLVER.hash) continue; @@ -293,7 +273,6 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& if (HEADERSSTATUS != HEADERS_OK) { std::println("\n{}", headerError(HEADERSSTATUS)); - std::println("\n{}", infoString("if the problem persists, try running hyprpm purge-cache.")); return false; } @@ -302,7 +281,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& progress.m_szCurrentMessage = "Building plugin(s)"; progress.print(); - for (auto& p : pManifest->m_plugins) { + for (auto& p : pManifest->m_vPlugins) { std::string out; if (p.since > HLVER.commits && HLVER.commits >= 1 /* for --depth 1 clones, we can't check this. */) { @@ -314,14 +293,8 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& progress.printMessageAbove(infoString("Building {}", p.name)); for (auto const& bs : p.buildSteps) { - const auto CMD_RAW = nixDevelopIfNeeded(std::format("cd {} && PKG_CONFIG_PATH=\"{}\" {}", m_szWorkingPluginDirectory, getPkgConfigPath(), bs), HLVER); - - if (!CMD_RAW) { - progress.printMessageAbove(failureString("Failed to build {}: {}", p.name, CMD_RAW.error())); - break; - } - - out += " -> " + *CMD_RAW + "\n" + execAndGet(*CMD_RAW) + "\n"; + const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs); + out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n"; } if (m_bVerbose) @@ -351,14 +324,11 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD"); if (repohash.length() > 0) repohash.pop_back(); - auto lastSlash = url.find_last_of('/'); - auto secondLastSlash = url.find_last_of('/', lastSlash - 1); - repo.name = pManifest->m_repository.name.empty() ? url.substr(lastSlash + 1) : pManifest->m_repository.name; - repo.author = url.substr(secondLastSlash + 1, lastSlash - secondLastSlash - 1); - repo.url = url; - repo.rev = rev; - repo.hash = repohash; - for (auto const& p : pManifest->m_plugins) { + repo.name = pManifest->m_sRepository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_sRepository.name; + repo.url = url; + repo.rev = rev; + repo.hash = repohash; + for (auto const& p : pManifest->m_vPlugins) { repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed}); } DataState::addNewPluginRepo(repo); @@ -377,13 +347,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& return true; } -bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) { - if (!DataState::pluginRepoExists(identifier)) { +bool CPluginManager::removePluginRepo(const std::string& urlOrName) { + if (!DataState::pluginRepoExists(urlOrName)) { std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed.")); return false; } - std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << identifier.toString() << "\n " + std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n " << "Are you sure? [Y/n] "; std::fflush(stdout); std::string input; @@ -394,7 +364,7 @@ bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) { return false; } - DataState::removePluginRepo(identifier); + DataState::removePluginRepo(urlOrName); return true; } @@ -406,7 +376,7 @@ eHeadersErrors CPluginManager::headersValid() { return HEADERS_MISSING; // find headers commit - const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}\" pkgconf --cflags --keep-system-cflags hyprland", getPkgConfigPath()); + const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath()); auto headers = execAndGet(cmd); if (!headers.contains("-I/")) @@ -455,22 +425,17 @@ eHeadersErrors CPluginManager::headersValid() { if (hash != HLVER.hash) return HEADERS_MISMATCHED; - // check ABI hash too - const auto GLOBALSTATE = DataState::getGlobalState(); - - if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash) - return HEADERS_ABI_MISMATCH; - return HEADERS_OK; } bool CPluginManager::updateHeaders(bool force) { + DataState::ensureStateStoreExists(); const auto HLVER = getHyprlandVersion(false); if (!hasDeps()) { - std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc")); + std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config")); return false; } @@ -512,11 +477,11 @@ bool CPluginManager::updateHeaders(bool force) { progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE)); std::string ret = - execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : ""))); + execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : ""))); if (!std::filesystem::exists(WORKINGDIR)) { progress.printMessageAbove(failureString("Clone failed. Retrying without shallow.")); - ret = execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}", getTempRoot(), HL_URL, USERNAME)); + ret = execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}", getTempRoot(), HL_URL, USERNAME)); } if (!std::filesystem::exists(WORKINGDIR + "/.git")) { @@ -559,17 +524,8 @@ bool CPluginManager::updateHeaders(bool force) { if (m_bVerbose) progress.printMessageAbove(verboseString("setting PREFIX for cmake to {}", DataState::getHeadersPath())); - const auto CONFIGURE_CMD = - nixDevelopIfNeeded(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build", WORKINGDIR, - DataState::getHeadersPath()), - HLVER); - - if (!CONFIGURE_CMD) { - std::println(stderr, "\n{}", failureString("Could not configure hyprland: {}", CONFIGURE_CMD.error())); - return false; - } - - ret = execAndGet(*CONFIGURE_CMD); + ret = execAndGet(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", WORKINGDIR, + DataState::getHeadersPath())); if (m_bVerbose) progress.printMessageAbove(verboseString("cmake returned: {}", ret)); @@ -592,21 +548,13 @@ bool CPluginManager::updateHeaders(bool force) { progress.m_szCurrentMessage = "Installing sources"; progress.print(); - std::string cmd = std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile", DataState::getHeadersPath(), WORKINGDIR); + const std::string& cmd = + std::format("sed -i -e \"s#PREFIX = /usr/local#PREFIX = {}#\" {}/Makefile && cd {} && make installheaders", DataState::getHeadersPath(), WORKINGDIR, WORKINGDIR); if (m_bVerbose) - progress.printMessageAbove(verboseString("prepare install will run: {}", cmd)); + progress.printMessageAbove(verboseString("installation will run: {}", cmd)); ret = execAndGet(cmd); - cmd = std::format("make -C '{}' installheaders && chmod -R 644 '{}' && find '{}' -type d -exec chmod a+x {{}} \\;", WORKINGDIR, DataState::getHeadersPath(), - DataState::getHeadersPath()); - - if (m_bVerbose) - progress.printMessageAbove(verboseString("install will run as sudo: {}", cmd)); - - // WORKINGDIR and headersPath should not contain anything unsafe. Usernames can't contain cmd exec parts. - ret = NSys::root::runAsSuperuserUnsafe(cmd); - if (m_bVerbose) std::println("{}", verboseString("installer returned: {}", ret)); @@ -614,21 +562,15 @@ bool CPluginManager::updateHeaders(bool force) { std::filesystem::remove_all(WORKINGDIR); auto HEADERSVALID = headersValid(); - - if (HEADERSVALID == HEADERS_OK || HEADERSVALID == HEADERS_MISMATCHED || HEADERSVALID == HEADERS_ABI_MISMATCH) { + if (HEADERSVALID == HEADERS_OK) { progress.printMessageAbove(successString("installed headers")); progress.m_iSteps = 5; progress.m_szCurrentMessage = "Done!"; progress.print(); - auto GLOBALSTATE = DataState::getGlobalState(); - GLOBALSTATE.headersAbiCompiled = HLVER.abiHash; - DataState::updateGlobalState(GLOBALSTATE); - std::print("\n"); } else { - progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", sc(HEADERSVALID), headerErrorShort(HEADERSVALID))); - progress.printMessageAbove(infoString("if the problem persists, try running hyprpm purge-cache.")); + progress.printMessageAbove(failureString("failed to install headers with error code {} ({})", (int)HEADERSVALID, headerErrorShort(HEADERSVALID))); progress.m_iSteps = 5; progress.m_szCurrentMessage = "Failed"; progress.print(); @@ -657,7 +599,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { const auto HLVER = getHyprlandVersion(false); CProgressBar progress; - progress.m_iMaxSteps = (REPOS.size() * 2) + 2; + progress.m_iMaxSteps = REPOS.size() * 2 + 2; progress.m_iSteps = 0; progress.m_szCurrentMessage = "Updating repositories"; progress.print(); @@ -678,7 +620,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { progress.printMessageAbove(infoString("Cloning {}", repo.url)); - std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), repo.url, USERNAME)); + std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), repo.url, USERNAME)); if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) { std::println("{}", failureString("could not clone repo: shell returned: {}", ret)); @@ -688,7 +630,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { if (!repo.rev.empty()) { progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev)); - std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules \'" + repo.rev + "\'"); + std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev); if (ret.compare(0, 6, "fatal:") == 0) { std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret)); @@ -735,17 +677,17 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { continue; } - if (!pManifest->m_good) { - std::println(stderr, "\n{}", failureString("The provided plugin repository has a bad manifest")); + if (!pManifest->m_bGood) { + std::println(stderr, "\n{}", failureString("The provided plugin repository has a corrupted manifest")); continue; } - if (repo.rev.empty() && !pManifest->m_repository.commitPins.empty()) { + if (repo.rev.empty() && !pManifest->m_sRepository.commitPins.empty()) { // check commit pins unless a revision is specified - progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_repository.commitPins.size())); + progress.printMessageAbove(infoString("Manifest has {} pins, checking", pManifest->m_sRepository.commitPins.size())); - for (auto const& [hl, plugin] : pManifest->m_repository.commitPins) { + for (auto const& [hl, plugin] : pManifest->m_sRepository.commitPins) { if (hl != HLVER.hash) continue; @@ -755,7 +697,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { } } - for (auto& p : pManifest->m_plugins) { + for (auto& p : pManifest->m_vPlugins) { std::string out; if (p.since > HLVER.commits && HLVER.commits >= 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */) { @@ -767,14 +709,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { progress.printMessageAbove(infoString("Building {}", p.name)); for (auto const& bs : p.buildSteps) { - const auto CMD_RAW = nixDevelopIfNeeded(std::format("cd {} && PKG_CONFIG_PATH=\"{}\" {}", m_szWorkingPluginDirectory, getPkgConfigPath(), bs), HLVER); - - if (!CMD_RAW) { - progress.printMessageAbove(failureString("Failed to build {}: {}", p.name, CMD_RAW.error())); - break; - } - - out += " -> " + *CMD_RAW + "\n" + execAndGet(*CMD_RAW) + "\n"; + const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs); + out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n"; } if (m_bVerbose) @@ -803,11 +739,11 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { if (repohash.length() > 0) repohash.pop_back(); newrepo.hash = repohash; - for (auto const& p : pManifest->m_plugins) { - const auto OLDPLUGINIT = std::ranges::find_if(repo.plugins, [&](const auto& other) { return other.name == p.name; }); - newrepo.plugins.emplace_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false}); + for (auto const& p : pManifest->m_vPlugins) { + const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; }); + newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false}); } - DataState::removePluginRepo(SPluginRepoIdentifier::fromName(newrepo.name)); + DataState::removePluginRepo(newrepo.name); DataState::addNewPluginRepo(newrepo); std::filesystem::remove_all(m_szWorkingPluginDirectory); @@ -819,8 +755,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { progress.m_szCurrentMessage = "Updating global state..."; progress.print(); - auto GLOBALSTATE = DataState::getGlobalState(); - GLOBALSTATE.headersAbiCompiled = HLVER.abiHash; + auto GLOBALSTATE = DataState::getGlobalState(); + GLOBALSTATE.headersHashCompiled = HLVER.hash; DataState::updateGlobalState(GLOBALSTATE); progress.m_iSteps++; @@ -832,23 +768,17 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) { return true; } -bool CPluginManager::enablePlugin(const SPluginRepoIdentifier identifier) { - bool ret = false; - - switch (identifier.type) { - case IDENTIFIER_NAME: - case IDENTIFIER_AUTHOR_NAME: ret = DataState::setPluginEnabled(identifier, true); break; - default: return false; - } +bool CPluginManager::enablePlugin(const std::string& name) { + bool ret = DataState::setPluginEnabled(name, true); if (ret) - std::println("{}", successString("Enabled {}", identifier.name)); + std::println("{}", successString("Enabled {}", name)); return ret; } -bool CPluginManager::disablePlugin(const SPluginRepoIdentifier identifier) { - bool ret = DataState::setPluginEnabled(identifier, false); +bool CPluginManager::disablePlugin(const std::string& name) { + bool ret = DataState::setPluginEnabled(name, false); if (ret) - std::println("{}", successString("Disabled {}", identifier.name)); + std::println("{}", successString("Disabled {}", name)); return ret; } @@ -866,9 +796,9 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload) } const auto HYPRPMPATH = DataState::getDataStatePath(); - const auto json = glz::read_json(NHyprlandSocket::send("j/plugins list")); + const auto json = glz::read_json(execAndGet("hyprctl plugins list -j")); if (!json) { - std::println(stderr, "PluginManager: couldn't parse plugin list output"); + std::println(stderr, "PluginManager: couldn't parse hyprctl output"); return LOADSTATE_FAIL; } @@ -931,7 +861,7 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload) if (!p.enabled) continue; - if (!forceReload && std::ranges::find_if(loadedPlugins, [&](const auto& other) { return other == p.name; }) != loadedPlugins.end()) + if (!forceReload && std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end()) continue; if (!loadUnloadPlugin(HYPRPMPATH / repoForName(p.name) / p.filename, true)) { @@ -951,16 +881,15 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) { auto state = DataState::getGlobalState(); auto HLVER = getHyprlandVersion(true); - if (state.headersAbiCompiled != HLVER.abiHash) { - if (load) - std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersAbiCompiled)); + if (state.headersHashCompiled != HLVER.hash) { + std::println("{}", infoString("Running Hyprland version differs from plugin state, please restart Hyprland.")); return false; } if (load) - NHyprlandSocket::send("/plugin load " + path); + execAndGet("hyprctl plugin load " + path); else - NHyprlandSocket::send("/plugin unload " + path); + execAndGet("hyprctl plugin unload " + path); return true; } @@ -969,7 +898,7 @@ void CPluginManager::listAllPlugins() { const auto REPOS = DataState::getAllRepositories(); for (auto const& r : REPOS) { - std::println("{}", infoString("Repository {} (by {}):", r.name, r.author)); + std::println("{}", infoString("Repository {}:", r.name)); for (auto const& p : r.plugins) { std::println(" │ Plugin {}", p.name); @@ -985,7 +914,7 @@ void CPluginManager::listAllPlugins() { } void CPluginManager::notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message) { - NHyprlandSocket::send("/notify " + std::to_string(icon) + " " + std::to_string(durationMs) + " " + std::to_string(color) + " " + message); + execAndGet("hyprctl notify " + std::to_string((int)icon) + " " + std::to_string(durationMs) + " " + std::to_string(color) + " " + message); } std::string CPluginManager::headerError(const eHeadersErrors err) { @@ -994,7 +923,6 @@ std::string CPluginManager::headerError(const eHeadersErrors err) { case HEADERS_MISMATCHED: return failureString("Headers version mismatch. Please run hyprpm update to fix those.\n"); case HEADERS_NOT_HYPRLAND: return failureString("It doesn't seem you are running on hyprland.\n"); case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n"); - case HEADERS_ABI_MISMATCH: return failureString("ABI is mismatched. Please run hyprpm update to fix that.\n"); case HEADERS_DUPLICATED: { return failureString("Headers duplicated!!! This is a very bad sign.\n" "This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n" @@ -1019,107 +947,11 @@ std::string CPluginManager::headerErrorShort(const eHeadersErrors err) { } bool CPluginManager::hasDeps() { - if (!m_bNoNix && getHyprlandVersion().isNix) - return true; // dep check not needed if we are on nix - - bool hasAllDeps = true; - std::vector deps = {"cpio", "cmake", "pkg-config", "g++", "gcc", "git"}; - + std::vector deps = {"meson", "cpio", "cmake", "pkg-config"}; for (auto const& d : deps) { - if (!execAndGet("command -v " + d).contains("/")) { - std::println(stderr, "{}", failureString("Missing dependency: {}", d)); - hasAllDeps = false; - } + if (!execAndGet("command -v " + d).contains("/")) + return false; } - return hasAllDeps; -} - -const std::string& CPluginManager::getPkgConfigPath() { - static const auto str = std::format("{}/share/pkgconfig:$PKG_CONFIG_PATH", DataState::getHeadersPath()); - return str; -} - -static std::expected getNixDevelopFromPath(const std::string& argv0) { - std::string fullStorePath; - - if (argv0.starts_with("/")) { - // we can use this directly - fullStorePath = argv0; - } else { - // use hyprpm, find in path - auto exe = NSys::findInPath("hyprpm"); - if (!exe) - return std::unexpected("hyprpm not found in PATH"); - - fullStorePath = *exe; - } - - if (fullStorePath.empty() || !fullStorePath.ends_with("/bin/hyprpm")) - return std::unexpected("couldn't get a real path for hyprpm (1)"); - - // canonicalize to get the real nix-store path - std::error_code ec; - fullStorePath = std::filesystem::canonical(fullStorePath, ec); - - if (ec || fullStorePath.empty() || !fullStorePath.starts_with("/nix")) - return std::unexpected("couldn't get a real path for hyprpm"); - - fullStorePath = fullStorePath.substr(0, fullStorePath.length() - std::string_view{"/bin/hyprpm"}.length()); - - auto deriver = trim(execAndGet(std::format("echo \"$(nix-store --query --deriver '{}')\"", fullStorePath))); - - if (deriver.starts_with("unknown")) - return std::unexpected("couldn't nix deriver"); - - return deriver; -} - -static std::expected getNixDevelopFromProfile() { - const auto NIX_PROFILE_STR = execAndGet("nix profile list --json"); - - auto rawJson = glz::read_json(NIX_PROFILE_STR); - - if (!rawJson) - return std::unexpected("failed to parse nix profile list --json"); - - auto& json = *rawJson; - - if (!json.contains("elements") || !json["elements"].is_object()) - return std::unexpected("nix profile list --json returned a wonky json"); - - if (!json["elements"].contains("hyprland") && !json["elements"].contains("Hyprland")) - return std::unexpected("nix profile list --json doesn't contain Hyprland (did you uninstall?)"); - - auto& hyprlandJson = json["elements"].contains("hyprland") ? json["elements"]["hyprland"] : json["elements"]["Hyprland"]; - - if (!hyprlandJson.contains("originalUrl")) - return std::unexpected("nix profile list --json's hyprland doesn't contain originalUrl?"); - - return hyprlandJson["originalUrl"].get_string(); -} - -std::expected CPluginManager::nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver) { - if (m_bNoNix || !ver.isNix) - return cmd; - - // Escape single quotes - std::string newCmd = cmd; - replaceInString(newCmd, "'", "\\'"); - - auto NIX_DEVELOP = getNixDevelopFromPath(m_szArgv0); - - if (NIX_DEVELOP) - return std::format("nix develop '{}' --command bash -c $'{}'", *NIX_DEVELOP, newCmd); - else if (m_bVerbose) - std::println("{}", verboseString("Failed nix from path: {}", NIX_DEVELOP.error())); - - NIX_DEVELOP = getNixDevelopFromProfile(); - - if (NIX_DEVELOP) - return std::format("nix develop '{}' --command bash -c $'{}'", *NIX_DEVELOP, newCmd); - else if (m_bVerbose) - std::println("{}", verboseString("Failed nix from profile: {}", NIX_DEVELOP.error())); - - return std::unexpected("hyprland is nix, but hyprpm failed to obtain a nix develop shell for build cmd"); + return true; } diff --git a/hyprpm/src/core/PluginManager.hpp b/hyprpm/src/core/PluginManager.hpp index 25878f54..b159b921 100644 --- a/hyprpm/src/core/PluginManager.hpp +++ b/hyprpm/src/core/PluginManager.hpp @@ -1,11 +1,8 @@ #pragma once -#include #include -#include #include -#include -#include "Plugin.hpp" +#include enum eHeadersErrors { HEADERS_OK = 0, @@ -13,7 +10,6 @@ enum eHeadersErrors { HEADERS_MISSING, HEADERS_CORRUPTED, HEADERS_MISMATCHED, - HEADERS_ABI_MISMATCH, HEADERS_DUPLICATED }; @@ -39,17 +35,13 @@ struct SHyprlandVersion { std::string branch; std::string hash; std::string date; - std::string abiHash; int commits = 0; - bool isNix = false; }; class CPluginManager { public: - CPluginManager(); - bool addNewPluginRepo(const std::string& url, const std::string& rev); - bool removePluginRepo(const SPluginRepoIdentifier identifier); + bool removePluginRepo(const std::string& urlOrName); eHeadersErrors headersValid(); bool updateHeaders(bool force = false); @@ -57,8 +49,8 @@ class CPluginManager { void listAllPlugins(); - bool enablePlugin(const SPluginRepoIdentifier identifier); - bool disablePlugin(const SPluginRepoIdentifier identifier); + bool enablePlugin(const std::string& name); + bool disablePlugin(const std::string& name); ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false); bool loadUnloadPlugin(const std::string& path, bool load); @@ -66,26 +58,20 @@ class CPluginManager { void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message); - const std::string& getPkgConfigPath(); - bool hasDeps(); bool m_bVerbose = false; bool m_bNoShallow = false; - bool m_bNoNix = false; - std::string m_szCustomHlUrl, m_szUsername, m_szArgv0; + std::string m_szCustomHlUrl; // will delete recursively if exists!! bool createSafeDirectory(const std::string& path); private: - std::string headerError(const eHeadersErrors err); - std::string headerErrorShort(const eHeadersErrors err); - bool validArg(const std::string& s); + std::string headerError(const eHeadersErrors err); + std::string headerErrorShort(const eHeadersErrors err); - std::expected nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver); - - std::string m_szWorkingPluginDirectory; + std::string m_szWorkingPluginDirectory; }; inline std::unique_ptr g_pPluginManager; diff --git a/hyprpm/src/helpers/Die.hpp b/hyprpm/src/helpers/Die.hpp deleted file mode 100644 index 0c00bcf3..00000000 --- a/hyprpm/src/helpers/Die.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -// NOLINTNEXTLINE -namespace Debug { - template - void die(std::format_string fmt, Args&&... args) { - const std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...)); - - std::cout << "\n[ERR] " << logMsg << "\n"; - exit(1); - } -}; \ No newline at end of file diff --git a/hyprpm/src/helpers/Sys.cpp b/hyprpm/src/helpers/Sys.cpp deleted file mode 100644 index e9dd4c85..00000000 --- a/hyprpm/src/helpers/Sys.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include "Sys.hpp" -#include "Die.hpp" -#include "StringUtils.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace Hyprutils::OS; -using namespace Hyprutils::String; - -inline constexpr std::array SUPERUSER_BINARIES = { - "sudo", - "doas", - "run0", -}; - -static std::string validSubinsAsStr() { - std::ostringstream oss; - auto it = SUPERUSER_BINARIES.begin(); - if (it != SUPERUSER_BINARIES.end()) { - oss << *it++; - for (; it != SUPERUSER_BINARIES.end(); ++it) - oss << ", " << *it; - } - - return oss.str(); -} - -static bool executableExistsInPath(const std::string& exe) { - return NSys::findInPath(exe).has_value(); -} - -std::optional NSys::findInPath(const std::string& exe) { - const char* PATHENV = std::getenv("PATH"); - if (!PATHENV) - return std::nullopt; - - CVarList paths(PATHENV, 0, ':', true); - std::error_code ec; - - for (const auto& PATH : paths) { - std::filesystem::path candidate = std::filesystem::path(PATH) / exe; - if (!std::filesystem::exists(candidate, ec) || ec) - continue; - if (!std::filesystem::is_regular_file(candidate, ec) || ec) - continue; - auto perms = std::filesystem::status(candidate, ec).permissions(); - if (ec) - continue; - if ((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) - return candidate.string(); - } - - return std::nullopt; -} - -static std::string subin() { - static std::string bin; - static bool once = true; - if (!once) - return bin; - - for (const auto& BIN : SUPERUSER_BINARIES) { - if (!executableExistsInPath(std::string{BIN})) - continue; - - bin = BIN; - break; - } - - once = false; - - if (bin.empty()) - Debug::die("{}", failureString("No valid superuser binary present. Supported: {}", validSubinsAsStr())); - - return bin; -} - -static bool verifyStringValid(const std::string& s) { - return std::ranges::none_of(s, [](const char& c) { return c == '`' || c == '$' || c == '(' || c == ')' || c == '\'' || c == '"'; }); -} - -int NSys::getUID() { - const auto UID = getuid(); - const auto PWUID = getpwuid(UID); - return PWUID ? PWUID->pw_uid : UID; -} - -int NSys::getEUID() { - const auto UID = geteuid(); - const auto PWUID = getpwuid(UID); - return PWUID ? PWUID->pw_uid : UID; -} - -bool NSys::isSuperuser() { - return getuid() != geteuid() || geteuid() == 0; -} - -void NSys::root::cacheSudo() { - // "caches" the sudo so that the prompt later doesn't pop up in a weird spot - // sudo will not ask us again for a moment - CProcess proc(subin(), {"echo", "hyprland"}); - proc.runSync(); -} - -void NSys::root::dropSudo() { - if (subin() != "sudo") { - std::println("{}", infoString("Don't know how to drop timestamp for '{}', ignoring.", subin())); - return; - } - - CProcess proc(subin(), {"-k"}); - proc.runSync(); -} - -bool NSys::root::createDirectory(const std::string& path, const std::string& mode) { - if (!verifyStringValid(path)) - return false; - - if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; })) - return false; - - CProcess proc(subin(), {"mkdir", "-p", "-m", mode, path}); - - return proc.runSync() && proc.exitCode() == 0; -} - -bool NSys::root::removeRecursive(const std::string& path) { - if (!verifyStringValid(path)) - return false; - - std::error_code ec; - const std::string PATH_ABSOLUTE = std::filesystem::canonical(path, ec); - - if (ec) - return false; - - if (!PATH_ABSOLUTE.starts_with("/var/cache/hyprpm")) - return false; - - CProcess proc(subin(), {"rm", "-fr", PATH_ABSOLUTE}); - - return proc.runSync() && proc.exitCode() == 0; -} - -bool NSys::root::install(const std::string& what, const std::string& where, const std::string& mode) { - if (!verifyStringValid(what) || !verifyStringValid(where)) - return false; - - if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; })) - return false; - - CProcess proc(subin(), {"install", "-m" + mode, "-o", "0", "-g", "0", what, where}); - - return proc.runSync() && proc.exitCode() == 0; -} - -std::string NSys::root::runAsSuperuserUnsafe(const std::string& cmd) { - CProcess proc(subin(), {"/bin/sh", "-c", cmd}); - - if (!proc.runSync()) - return ""; - - return proc.stdOut(); -} diff --git a/hyprpm/src/helpers/Sys.hpp b/hyprpm/src/helpers/Sys.hpp deleted file mode 100644 index 03d5a0de..00000000 --- a/hyprpm/src/helpers/Sys.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -namespace NSys { - bool isSuperuser(); - int getUID(); - int getEUID(); - std::optional findInPath(const std::string& exe); - - // NOLINTNEXTLINE - namespace root { - void cacheSudo(); - void dropSudo(); - - // - [[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool createDirectory(const std::string& path, const std::string& mode); - [[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool removeRecursive(const std::string& path); - [[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool install(const std::string& what, const std::string& where, const std::string& mode); - - // Do not use this unless absolutely necessary! - std::string runAsSuperuserUnsafe(const std::string& cmd); - }; -}; diff --git a/hyprpm/src/main.cpp b/hyprpm/src/main.cpp index f5e14bbb..0d5efd1d 100644 --- a/hyprpm/src/main.cpp +++ b/hyprpm/src/main.cpp @@ -2,37 +2,34 @@ #include "helpers/StringUtils.hpp" #include "core/PluginManager.hpp" #include "core/DataState.hpp" -#include "helpers/Sys.hpp" +#include #include #include #include - -#include -using namespace Hyprutils::Utils; +#include +#include constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager ┃ -┣ add [git rev] → Install a new plugin repository from git. Git revision -┃ is optional, when set, commit locks are ignored. -┣ remove → Remove an installed plugin repository. -┣ enable → Enable a plugin. -┣ disable → Disable a plugin. -┣ update → Check and update all plugins if needed. -┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded. -┣ list → List all installed plugins. -┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers. +┣ add [url] [git rev] → Install a new plugin repository from git. Git revision +┃ is optional, when set, commit locks are ignored. +┣ remove [url/name] → Remove an installed plugin repository +┣ enable [name] → Enable a plugin +┣ disable [name] → Disable a plugin +┣ update → Check and update all plugins if needed +┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded. +┣ list → List all installed plugins ┃ ┣ Flags: ┃ -┣ --no-nix | → Disable `nix develop` for build commands, even if Hyprland is nix. -┣ --notify | -n → Send a hyprland notification confirming successful plugin load. -┃ Warnings/Errors trigger notifications regardless of this flag. -┣ --help | -h → Show this menu. -┣ --verbose | -v → Enable too much logging. -┣ --force | -f → Force an operation ignoring checks (e.g. update -f). -┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources. -┣ --hl-url | → Pass a custom hyprland source url. +┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events) +┣ --notify-fail | -nn → Send a hyprland notification for fail events only +┣ --help | -h → Show this menu +┣ --verbose | -v → Enable too much logging +┣ --force | -f → Force an operation ignoring checks (e.g. update -f) +┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources +┣ --hl-url | → Pass a custom hyprland source url ┗ )#"; @@ -48,7 +45,7 @@ int main(int argc, char** argv, char** envp) { } std::vector command; - bool notify = false, verbose = false, force = false, noShallow = false, noNix = false; + bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false; std::string customHlUrl; for (int i = 1; i < argc; ++i) { @@ -59,13 +56,9 @@ int main(int argc, char** argv, char** envp) { } else if (ARGS[i] == "--notify" || ARGS[i] == "-n") { notify = true; } else if (ARGS[i] == "--notify-fail" || ARGS[i] == "-nn") { - // TODO: Deprecated since v.053.0. Remove in version>0.56.0 - std::println(stderr, "{}", failureString("Deprececated flag.")); - g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] -n flag is deprecated, see hyprpm --help."); + notifyFail = notify = true; } else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") { verbose = true; - } else if (ARGS[i] == "--no-nix") { - noNix = true; } else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") { noShallow = true; } else if (ARGS[i] == "--hl-url") { @@ -94,9 +87,7 @@ int main(int argc, char** argv, char** envp) { g_pPluginManager = std::make_unique(); g_pPluginManager->m_bVerbose = verbose; g_pPluginManager->m_bNoShallow = noShallow; - g_pPluginManager->m_bNoNix = noNix; g_pPluginManager->m_szCustomHlUrl = customHlUrl; - g_pPluginManager->m_szArgv0 = argv[0]; if (command[0] == "add") { if (command.size() < 2) { @@ -105,44 +96,25 @@ int main(int argc, char** argv, char** envp) { } std::string rev = ""; - if (command.size() >= 3) + if (command.size() >= 3) { rev = command[2]; - - const auto HLVER = g_pPluginManager->getHyprlandVersion(); - auto GLOBALSTATE = DataState::getGlobalState(); - - if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash) { - std::println(stderr, "{}", failureString("Headers outdated, please run hyprpm update.")); - return 1; } - NSys::root::cacheSudo(); - CScopeGuard x([] { NSys::root::dropSudo(); }); - - g_pPluginManager->updateHeaders(false); - return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1; } else if (command[0] == "remove") { - if (command.size() < 2) { + if (ARGS.size() < 2) { std::println(stderr, "{}", failureString("Not enough args for remove.")); return 1; } - NSys::root::cacheSudo(); - CScopeGuard x([] { NSys::root::dropSudo(); }); - - return g_pPluginManager->removePluginRepo(SPluginRepoIdentifier::fromString(command[1])) ? 0 : 1; + return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1; } else if (command[0] == "update") { - NSys::root::cacheSudo(); - CScopeGuard x([] { NSys::root::dropSudo(); }); - - bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK; - bool headers = g_pPluginManager->updateHeaders(force); - + bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK; + bool headers = g_pPluginManager->updateHeaders(force); if (headers) { const auto HLVER = g_pPluginManager->getHyprlandVersion(false); auto GLOBALSTATE = DataState::getGlobalState(); - const auto COMPILEDOUTDATED = HLVER.abiHash != GLOBALSTATE.headersAbiCompiled; + const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled; bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED); @@ -156,24 +128,20 @@ int main(int argc, char** argv, char** envp) { if (ret2 != LOADSTATE_OK) return 1; - } else { + } else if (notify) g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers"); - } } else if (command[0] == "enable") { - if (command.size() < 2) { + if (ARGS.size() < 2) { std::println(stderr, "{}", failureString("Not enough args for enable.")); return 1; } - if (!g_pPluginManager->enablePlugin(SPluginRepoIdentifier::fromString(command[1]))) { + if (!g_pPluginManager->enablePlugin(command[1])) { std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)")); return 1; } - NSys::root::cacheSudo(); - CScopeGuard x([] { NSys::root::dropSudo(); }); - - auto ret = g_pPluginManager->ensurePluginsLoadState(); + auto ret = g_pPluginManager->ensurePluginsLoadState(); if (ret == LOADSTATE_HYPRLAND_UPDATED) g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Enabled plugin, but Hyprland was updated. Please restart Hyprland."); @@ -186,39 +154,33 @@ int main(int argc, char** argv, char** envp) { return 1; } - if (!g_pPluginManager->disablePlugin(SPluginRepoIdentifier::fromString(command[1]))) { + if (!g_pPluginManager->disablePlugin(command[1])) { std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)")); return 1; } - NSys::root::cacheSudo(); - CScopeGuard x([] { NSys::root::dropSudo(); }); - - auto ret = g_pPluginManager->ensurePluginsLoadState(); - + auto ret = g_pPluginManager->ensurePluginsLoadState(); if (ret != LOADSTATE_OK) return 1; } else if (command[0] == "reload") { auto ret = g_pPluginManager->ensurePluginsLoadState(force); if (ret != LOADSTATE_OK) { - switch (ret) { - case LOADSTATE_FAIL: - case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break; - case LOADSTATE_HEADERS_OUTDATED: - g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually."); - break; - default: break; + if (notify) { + switch (ret) { + case LOADSTATE_FAIL: + case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break; + case LOADSTATE_HEADERS_OUTDATED: + g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually."); + break; + default: break; + } } return 1; - } else if (notify) { + } else if (notify && !notifyFail) { g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins"); } - } else if (command[0] == "purge-cache") { - NSys::root::cacheSudo(); - CScopeGuard x([] { NSys::root::dropSudo(); }); - DataState::purgeAllCache(); } else if (command[0] == "list") { g_pPluginManager->listAllPlugins(); } else { diff --git a/hyprpm/src/meson.build b/hyprpm/src/meson.build new file mode 100644 index 00000000..fd914f9d --- /dev/null +++ b/hyprpm/src/meson.build @@ -0,0 +1,32 @@ +globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true) +src = globber.stdout().strip().split('\n') + +executable( + 'hyprpm', + src, + dependencies: [ + dependency('hyprutils', version: '>= 0.1.1'), + dependency('threads'), + dependency('tomlplusplus'), + dependency('glaze', method: 'cmake'), + ], + install: true, +) + +install_data( + '../hyprpm.bash', + install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'), + install_tag: 'runtime', + rename: 'hyprpm', +) +install_data( + '../hyprpm.fish', + install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'), + install_tag: 'runtime', +) +install_data( + '../hyprpm.zsh', + install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'), + install_tag: 'runtime', + rename: '_hyprpm', +) diff --git a/hyprpm/src/progress/CProgressBar.cpp b/hyprpm/src/progress/CProgressBar.cpp index 19a14d9c..9f2df08a 100644 --- a/hyprpm/src/progress/CProgressBar.cpp +++ b/hyprpm/src/progress/CProgressBar.cpp @@ -1,80 +1,82 @@ #include "CProgressBar.hpp" #include -#include +#include #include #include -#include -#include -#include -#include -#include +#include +#include +#include + #include "../helpers/Colors.hpp" -using namespace Hyprutils::Memory; - -static winsize getTerminalSize() { - winsize w{}; - ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - return w; -} - -static void clearCurrentLine() { - std::print("\r\33[2K"); // ansi escape sequence to clear entire line -} - void CProgressBar::printMessageAbove(const std::string& msg) { - clearCurrentLine(); - std::print("\r{}\n", msg); + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - print(); // reprint bar underneath + std::string spaces; + spaces.reserve(w.ws_col); + for (size_t i = 0; i < w.ws_col; ++i) { + spaces += ' '; + } + + std::println("\r{}\r{}", spaces, msg); + print(); } void CProgressBar::print() { - const auto w = getTerminalSize(); + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - if (m_bFirstPrint) { + if (m_bFirstPrint) std::print("\n"); - m_bFirstPrint = false; + m_bFirstPrint = false; + + std::string spaces; + spaces.reserve(w.ws_col); + for (size_t i = 0; i < w.ws_col; ++i) { + spaces += ' '; } - clearCurrentLine(); + std::print("\r{}\r", spaces); - float percentDone = 0.0f; - if (m_fPercentage >= 0.0f) + std::string message = ""; + + float percentDone = 0; + if (m_fPercentage >= 0) percentDone = m_fPercentage; - else { - // check for divide-by-zero - percentDone = m_iMaxSteps > 0 ? sc(m_iSteps) / m_iMaxSteps : 0.0f; + else + percentDone = (float)m_iSteps / (float)m_iMaxSteps; + + const auto BARWIDTH = std::clamp(w.ws_col - static_cast(m_szCurrentMessage.length()) - 2, 0UL, 50UL); + + // draw bar + message += std::string{" "} + Colors::GREEN; + size_t i = 0; + for (; i < std::floor(percentDone * BARWIDTH); ++i) { + message += "━"; } - // clamp to ensure no overflows (sanity check) - percentDone = std::clamp(percentDone, 0.0f, 1.0f); - - const size_t BARWIDTH = std::clamp(w.ws_col - m_szCurrentMessage.length() - 2, 0, 50); - - std::ostringstream oss; - oss << ' ' << Colors::GREEN; - - size_t filled = std::floor(percentDone * BARWIDTH); - size_t i = 0; - - for (; i < filled; ++i) - oss << "━"; if (i < BARWIDTH) { - oss << "╍" << Colors::RESET; - ++i; - for (; i < BARWIDTH; ++i) - oss << "━"; + i++; + + message += std::string{"╍"} + Colors::RESET; + + for (; i < BARWIDTH; ++i) { + message += "━"; + } } else - oss << Colors::RESET; + message += Colors::RESET; - if (m_fPercentage >= 0.0f) - oss << " " << std::format("{}%", sc(percentDone * 100.0)) << ' '; + // draw progress + if (m_fPercentage >= 0) + message += " " + std::format("{}%", static_cast(percentDone * 100.0)) + " "; else - oss << " " << std::format("{} / {}", m_iSteps, m_iMaxSteps) << ' '; + message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " "; + + // draw message + std::print("{} {}", message, m_szCurrentMessage); - std::print("{} {}", oss.str(), m_szCurrentMessage); std::fflush(stdout); } diff --git a/hyprtester/CMakeLists.txt b/hyprtester/CMakeLists.txt deleted file mode 100644 index f17f73b1..00000000 --- a/hyprtester/CMakeLists.txt +++ /dev/null @@ -1,104 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -project(hyprtester DESCRIPTION "Hyprland test suite") - -include(GNUInstallDirs) - -set(CMAKE_CXX_STANDARD 26) -set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) - -find_package(PkgConfig REQUIRED) - -pkg_check_modules(hyprtester_deps REQUIRED IMPORTED_TARGET hyprutils>=0.5.0) - -file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") - -add_executable(hyprtester ${SRCFILES}) -add_custom_command( - TARGET hyprtester - POST_BUILD - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/plugin/build.sh - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/plugin) - -target_link_libraries(hyprtester PUBLIC PkgConfig::hyprtester_deps) - -install(TARGETS hyprtester) - -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test.conf - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr) - -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/plugin/hyprtestplugin.so - DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) - -file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/src/tests/clients/build.hpp - "#include \n" - "static const std::string binaryDir = \"${CMAKE_CURRENT_BINARY_DIR}\";" -) - -######## wayland protocols testing stuff - -if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) - set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE) -endif() - -find_package(hyprwayland-scanner 0.4.0 REQUIRED) -pkg_check_modules( - protocols_deps - REQUIRED - IMPORTED_TARGET - hyprutils>=0.8.0 - wayland-client - wayland-protocols -) - -pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) -message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") -pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir) -message(STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}") - -# gen core wayland stuff -add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.hpp - COMMAND hyprwayland-scanner --wayland-enums --client - ${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_CURRENT_SOURCE_DIR}/protocols/ - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - -function(protocolNew protoPath protoName external) - if(external) - set(path ${CMAKE_CURRENT_SOURCE_DIR}/${protoPath}) - else() - set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) - endif() - - add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.hpp - COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/ - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -endfunction() -function(clientNew sourceName) - cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "PROTOS") - - add_executable(${sourceName} clients/${sourceName}.cpp) - - target_include_directories(${sourceName} BEFORE PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/protocols") - target_link_libraries(${sourceName} PUBLIC PkgConfig::protocols_deps) - - target_sources(${sourceName} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.hpp) - - foreach(protoName IN LISTS ARG_PROTOS) - target_sources(${sourceName} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.hpp) - endforeach() -endfunction() - -protocolnew("staging/pointer-warp" "pointer-warp-v1" false) -protocolnew("stable/xdg-shell" "xdg-shell" false) -protocolnew("unstable/keyboard-shortcuts-inhibit" "keyboard-shortcuts-inhibit-unstable-v1" false) - -clientNew("pointer-warp" PROTOS "pointer-warp-v1" "xdg-shell") -clientNew("pointer-scroll" PROTOS "xdg-shell") -clientNew("child-window" PROTOS "xdg-shell") -clientNew("shortcut-inhibitor" PROTOS "xdg-shell" "keyboard-shortcuts-inhibit-unstable-v1") diff --git a/hyprtester/clients/child-window.cpp b/hyprtester/clients/child-window.cpp deleted file mode 100644 index 5f66be6b..00000000 --- a/hyprtester/clients/child-window.cpp +++ /dev/null @@ -1,336 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -using Hyprutils::Math::Vector2D; -using namespace Hyprutils::Memory; - -struct SWlState { - wl_display* display; - CSharedPointer registry; - - // protocols - CSharedPointer wlCompositor; - CSharedPointer wlSeat; - CSharedPointer wlShm; - CSharedPointer xdgShell; - - // shm/buffer stuff - CSharedPointer shmPool; - CSharedPointer shmBuf; - CSharedPointer shmBuf2; - int shmFd = 0; - size_t shmBufSize = 0; - bool xrgb8888_support = false; - - // surface/toplevel stuff - CSharedPointer surf; - CSharedPointer xdgSurf; - CSharedPointer xdgToplevel; - Vector2D geom; - - // pointer - CSharedPointer pointer; - uint32_t enterSerial = 0; -}; - -bool debug, shouldExit, started; - -template -//NOLINTNEXTLINE -static void clientLog(std::format_string fmt, Args&&... args) { - std::string text = std::vformat(fmt.get(), std::make_format_args(args...)); - std::println("{}", text); - std::fflush(stdout); -} - -template -//NOLINTNEXTLINE -static void debugLog(std::format_string fmt, Args&&... args) { - std::string text = std::vformat(fmt.get(), std::make_format_args(args...)); - if (!debug) - return; - std::println("{}", text); - std::fflush(stdout); -} - -static bool bindRegistry(SWlState& state) { - state.registry = makeShared((wl_proxy*)wl_display_get_registry(state.display)); - - state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) { - const std::string NAME = name; - if (NAME == "wl_compositor") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlCompositor = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6)); - } else if (NAME == "wl_shm") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlShm = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1)); - } else if (NAME == "wl_seat") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlSeat = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9)); - } else if (NAME == "xdg_wm_base") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.xdgShell = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1)); - } - }); - state.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t id) { debugLog("Global {} removed", id); }); - - wl_display_roundtrip(state.display); - - if (!state.wlCompositor || !state.wlShm || !state.wlSeat || !state.xdgShell) { - clientLog("Failed to get protocols from Hyprland"); - return false; - } - - return true; -} - -static bool createShm(SWlState& state, Vector2D geom) { - if (!state.xrgb8888_support) - return false; - - size_t stride = geom.x * 4; - size_t size = geom.y * stride; - if (!state.shmPool) { - const char* name = "/wl-shm-pointer-warp"; - state.shmFd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (state.shmFd < 0) - return false; - - if (shm_unlink(name) < 0 || ftruncate(state.shmFd, size * 2) < 0) { - close(state.shmFd); - return false; - } - - state.shmPool = makeShared(state.wlShm->sendCreatePool(state.shmFd, size * 2)); - if (!state.shmPool->resource()) { - close(state.shmFd); - state.shmFd = -1; - state.shmPool.reset(); - return false; - } - state.shmBufSize = size; - } else if (size > state.shmBufSize) { - if (ftruncate(state.shmFd, size) < 0) { - close(state.shmFd); - state.shmFd = -1; - state.shmPool.reset(); - return false; - } - - state.shmPool->sendResize(size * 2); - state.shmBufSize = size; - } - - auto buf = makeShared(state.shmPool->sendCreateBuffer(0, geom.x, geom.y, stride, WL_SHM_FORMAT_XRGB8888)); - if (!buf->resource()) - return false; - - if (state.shmBuf) { - state.shmBuf->sendDestroy(); - state.shmBuf.reset(); - } - state.shmBuf = buf; - - return true; -} - -static bool setupToplevel(SWlState& state) { - state.wlShm->setFormat([&](CCWlShm* p, uint32_t format) { - if (format == WL_SHM_FORMAT_XRGB8888) - state.xrgb8888_support = true; - }); - - state.xdgShell->setPing([&](CCXdgWmBase* p, uint32_t serial) { state.xdgShell->sendPong(serial); }); - - state.surf = makeShared(state.wlCompositor->sendCreateSurface()); - if (!state.surf->resource()) - return false; - - state.xdgSurf = makeShared(state.xdgShell->sendGetXdgSurface(state.surf->resource())); - if (!state.xdgSurf->resource()) - return false; - - state.xdgToplevel = makeShared(state.xdgSurf->sendGetToplevel()); - if (!state.xdgToplevel->resource()) - return false; - - state.xdgToplevel->setClose([&](CCXdgToplevel* p) { exit(0); }); - - state.xdgToplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) { - state.geom = {1280, 720}; - - if (!createShm(state, state.geom)) - exit(-1); - }); - - state.xdgSurf->setConfigure([&](CCXdgSurface* p, uint32_t serial) { - if (!state.shmBuf) - debugLog("xdgSurf configure but no buf made yet?"); - - state.xdgSurf->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y); - state.surf->sendAttach(state.shmBuf.get(), 0, 0); - state.surf->sendCommit(); - - state.xdgSurf->sendAckConfigure(serial); - - if (!started) { - started = true; - clientLog("started"); - } - }); - - state.xdgToplevel->sendSetTitle("child-test parent"); - state.xdgToplevel->sendSetAppId("child-test-parent"); - - state.surf->sendAttach(nullptr, 0, 0); - state.surf->sendCommit(); - - return true; -} - -static bool setupSeat(SWlState& state) { - state.pointer = makeShared(state.wlSeat->sendGetPointer()); - if (!state.pointer->resource()) - return false; - - state.pointer->setEnter([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf, wl_fixed_t x, wl_fixed_t y) { - debugLog("Got pointer enter event, serial {}, x {}, y {}", serial, x, y); - state.enterSerial = serial; - }); - - state.pointer->setLeave([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf) { debugLog("Got pointer leave event, serial {}", serial); }); - - state.pointer->setMotion([&](CCWlPointer* p, uint32_t serial, wl_fixed_t x, wl_fixed_t y) { debugLog("Got pointer motion event, serial {}, x {}, y {}", serial, x, y); }); - - return true; -} - -struct SChildWindow { - CSharedPointer surface; - CSharedPointer xSurface; - CSharedPointer toplevel; -}; - -static void parseRequest(SWlState& state, std::string str, SChildWindow& window) { - if (str.starts_with("exit")) { - shouldExit = true; - return; - } - - size_t index = str.find_first_of('\n'); - str = str.substr(0, index); - - if (str == "toplevel") { - window.surface = makeShared(state.wlCompositor->sendCreateSurface()); - window.xSurface = makeShared(state.xdgShell->sendGetXdgSurface(window.surface->resource())); - - window.xSurface->setConfigure([&](CCXdgSurface* p, uint32_t serial) { - if (!state.shmBuf) - debugLog("xdgSurf configure but no buf made yet?"); - - window.xSurface->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y); - window.surface->sendAttach(state.shmBuf2.get(), 0, 0); - window.surface->sendCommit(); - - window.xSurface->sendAckConfigure(serial); - }); - - window.toplevel = makeShared(window.xSurface->sendGetToplevel()); - - window.toplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) { - size_t stride = 1280 * 4; - size_t size = 720 * stride; - - auto buf = makeShared(state.shmPool->sendCreateBuffer(size, state.geom.x, state.geom.y, stride, WL_SHM_FORMAT_XRGB8888)); - if (!buf->resource()) - clientLog("Failed to create child buffer"); - - if (state.shmBuf2) { - state.shmBuf2->sendDestroy(); - state.shmBuf2.reset(); - } - state.shmBuf2 = buf; - }); - - window.toplevel->sendSetTitle("child-test child"); - window.toplevel->sendSetAppId("child-test-child"); - window.toplevel->sendSetParent(state.xdgToplevel.get()); - - window.surface->sendAttach(nullptr, 0, 0); - window.surface->sendCommit(); - clientLog("child started"); - return; - } -} - -int main(int argc, char** argv) { - if (argc != 1 && argc != 2) - clientLog("Only the \"--debug\" switch is allowed, it turns on debug logs."); - - if (argc == 2 && std::string{argv[1]} == "--debug") - debug = true; - - SWlState state; - SChildWindow window; - - // WAYLAND_DISPLAY env should be set to the correct one - state.display = wl_display_connect(nullptr); - if (!state.display) { - clientLog("Failed to connect to wayland display"); - return -1; - } - - if (!bindRegistry(state) || !setupSeat(state) || !setupToplevel(state)) - return -1; - - std::array readBuf; - readBuf.fill(0); - - wl_display_flush(state.display); - - struct pollfd fds[2] = {{.fd = wl_display_get_fd(state.display), .events = POLLIN | POLLOUT}, {.fd = STDIN_FILENO, .events = POLLIN}}; - while (!shouldExit && poll(fds, 2, 0) != -1) { - if (fds[0].revents & POLLIN) { - wl_display_flush(state.display); - - if (wl_display_prepare_read(state.display) == 0) { - wl_display_read_events(state.display); - wl_display_dispatch_pending(state.display); - } else - wl_display_dispatch(state.display); - - int ret = 0; - do { - ret = wl_display_dispatch_pending(state.display); - wl_display_flush(state.display); - } while (ret > 0); - } - - if (fds[1].revents & POLLIN) { - ssize_t bytesRead = read(fds[1].fd, readBuf.data(), 1023); - if (bytesRead == -1) - continue; - readBuf[bytesRead] = 0; - - parseRequest(state, std::string{readBuf.data()}, window); - } - } - - wl_display* display = state.display; - state = {}; - window = {}; - - wl_display_disconnect(display); - return 0; -} diff --git a/hyprtester/clients/pointer-scroll.cpp b/hyprtester/clients/pointer-scroll.cpp deleted file mode 100644 index 59120961..00000000 --- a/hyprtester/clients/pointer-scroll.cpp +++ /dev/null @@ -1,319 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -using Hyprutils::Math::Vector2D; -using namespace Hyprutils::Memory; - -struct SWlState { - wl_display* display; - CSharedPointer registry; - - // protocols - CSharedPointer wlCompositor; - CSharedPointer wlSeat; - CSharedPointer wlShm; - CSharedPointer xdgShell; - - // shm/buffer stuff - CSharedPointer shmPool; - CSharedPointer shmBuf; - int shmFd; - size_t shmBufSize; - bool xrgb8888_support = false; - - // surface/toplevel stuff - CSharedPointer surf; - CSharedPointer xdgSurf; - CSharedPointer xdgToplevel; - Vector2D geom; - - // pointer - CSharedPointer pointer; - uint32_t enterSerial; - - // last delta - float lastScrollDelta = -1.F; - bool writeDelta = false; -}; - -static std::ofstream logfile; - -static bool debug, started, shouldExit; - -template -//NOLINTNEXTLINE -static void clientLog(std::format_string fmt, Args&&... args) { - std::string text = std::vformat(fmt.get(), std::make_format_args(args...)); - std::println("{}", text); - logfile << text << std::endl; - std::fflush(stdout); -} - -template -//NOLINTNEXTLINE -static void debugLog(std::format_string fmt, Args&&... args) { - std::string text = std::vformat(fmt.get(), std::make_format_args(args...)); - logfile << text << std::endl; - if (!debug) - return; - std::println("{}", text); - std::fflush(stdout); -} - -static bool bindRegistry(SWlState& state) { - state.registry = makeShared((wl_proxy*)wl_display_get_registry(state.display)); - - state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) { - const std::string NAME = name; - if (NAME == "wl_compositor") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlCompositor = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6)); - } else if (NAME == "wl_shm") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlShm = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1)); - } else if (NAME == "wl_seat") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlSeat = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9)); - } else if (NAME == "xdg_wm_base") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.xdgShell = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1)); - } - }); - state.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t id) { debugLog("Global {} removed", id); }); - - wl_display_roundtrip(state.display); - - if (!state.wlCompositor || !state.wlShm || !state.wlSeat || !state.xdgShell) { - clientLog("Failed to get protocols from Hyprland"); - return false; - } - - return true; -} - -static bool createShm(SWlState& state, Vector2D geom) { - if (!state.xrgb8888_support) - return false; - - size_t stride = geom.x * 4; - size_t size = geom.y * stride; - if (!state.shmPool) { - const char* name = "/wl-shm-pointer-scroll"; - state.shmFd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (state.shmFd < 0) - return false; - - if (shm_unlink(name) < 0 || ftruncate(state.shmFd, size) < 0) { - close(state.shmFd); - return false; - } - - state.shmPool = makeShared(state.wlShm->sendCreatePool(state.shmFd, size)); - if (!state.shmPool->resource()) { - close(state.shmFd); - state.shmFd = -1; - state.shmPool.reset(); - return false; - } - state.shmBufSize = size; - } else if (size > state.shmBufSize) { - if (ftruncate(state.shmFd, size) < 0) { - close(state.shmFd); - state.shmFd = -1; - state.shmPool.reset(); - return false; - } - - state.shmPool->sendResize(size); - state.shmBufSize = size; - } - - auto buf = makeShared(state.shmPool->sendCreateBuffer(0, geom.x, geom.y, stride, WL_SHM_FORMAT_XRGB8888)); - if (!buf->resource()) - return false; - - if (state.shmBuf) { - state.shmBuf->sendDestroy(); - state.shmBuf.reset(); - } - - state.shmBuf = buf; - - return true; -} - -static bool setupToplevel(SWlState& state) { - state.wlShm->setFormat([&](CCWlShm* p, uint32_t format) { - if (format == WL_SHM_FORMAT_XRGB8888) - state.xrgb8888_support = true; - }); - - state.xdgShell->setPing([&](CCXdgWmBase* p, uint32_t serial) { state.xdgShell->sendPong(serial); }); - - state.surf = makeShared(state.wlCompositor->sendCreateSurface()); - if (!state.surf->resource()) - return false; - - state.xdgSurf = makeShared(state.xdgShell->sendGetXdgSurface(state.surf->resource())); - if (!state.xdgSurf->resource()) - return false; - - state.xdgToplevel = makeShared(state.xdgSurf->sendGetToplevel()); - if (!state.xdgToplevel->resource()) - return false; - - state.xdgToplevel->setClose([&](CCXdgToplevel* p) { exit(0); }); - - state.xdgToplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) { - state.geom = {1280, 720}; - - if (!createShm(state, state.geom)) - exit(-1); - }); - - state.xdgSurf->setConfigure([&](CCXdgSurface* p, uint32_t serial) { - if (!state.shmBuf) - debugLog("xdgSurf configure but no buf made yet?"); - - state.xdgSurf->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y); - state.surf->sendAttach(state.shmBuf.get(), 0, 0); - state.surf->sendCommit(); - - state.xdgSurf->sendAckConfigure(serial); - - if (!started) { - started = true; - clientLog("started"); - } - }); - - state.xdgToplevel->sendSetTitle("pointer-scroll test client"); - state.xdgToplevel->sendSetAppId("pointer-scroll"); - - state.surf->sendAttach(nullptr, 0, 0); - state.surf->sendCommit(); - - return true; -} - -static bool setupSeat(SWlState& state) { - state.pointer = makeShared(state.wlSeat->sendGetPointer()); - if (!state.pointer->resource()) - return false; - - state.pointer->setEnter([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf, wl_fixed_t x, wl_fixed_t y) { - debugLog("Got pointer enter event, serial {}, x {}, y {}", serial, x, y); - state.enterSerial = serial; - }); - - state.pointer->setAxis([&](CCWlPointer* p, uint32_t time, wl_pointer_axis axis, wl_fixed_t delta) { - debugLog("axis: ax {} delta {}", (int)axis, wl_fixed_to_double(delta)); - - if (state.writeDelta) { - clientLog("{:.2f}", wl_fixed_to_double(delta)); - state.writeDelta = false; - state.lastScrollDelta = -1; - return; - } - - state.lastScrollDelta = wl_fixed_to_double(delta); - state.writeDelta = true; - }); - - state.pointer->setLeave([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf) { debugLog("Got pointer leave event, serial {}", serial); }); - - state.pointer->setMotion([&](CCWlPointer* p, uint32_t serial, wl_fixed_t x, wl_fixed_t y) { debugLog("Got pointer motion event, serial {}, x {}, y {}", serial, x, y); }); - - return true; -} - -// return last delta after axis -static void parseRequest(SWlState& state, std::string req) { - if (!state.writeDelta) { - state.writeDelta = true; - return; - } - - clientLog("{:.2f}", state.lastScrollDelta); - state.writeDelta = false; - state.lastScrollDelta = -1; -} - -int main(int argc, char** argv) { - logfile.open("pointer-scroll.txt", std::ios::trunc); - - if (argc != 1 && argc != 2) - clientLog("Only the \"--debug\" switch is allowed, it turns on debug logs."); - - if (argc == 2 && std::string{argv[1]} == "--debug") - debug = true; - - SWlState state; - - // WAYLAND_DISPLAY env should be set to the correct one - state.display = wl_display_connect(nullptr); - if (!state.display) { - clientLog("Failed to connect to wayland display"); - return -1; - } - - if (!bindRegistry(state) || !setupSeat(state) || !setupToplevel(state)) - return -1; - - std::array readBuf; - readBuf.fill(0); - - wl_display_flush(state.display); - - struct pollfd fds[2] = {{.fd = wl_display_get_fd(state.display), .events = POLLIN | POLLOUT}, {.fd = STDIN_FILENO, .events = POLLIN}}; - while (!shouldExit && poll(fds, 2, 0) != -1) { - if (fds[0].revents & POLLIN) { - wl_display_flush(state.display); - - if (wl_display_prepare_read(state.display) == 0) { - wl_display_read_events(state.display); - wl_display_dispatch_pending(state.display); - } else - wl_display_dispatch(state.display); - - int ret = 0; - do { - ret = wl_display_dispatch_pending(state.display); - wl_display_flush(state.display); - } while (ret > 0); - } - - if (fds[1].revents & POLLIN) { - ssize_t bytesRead = read(fds[1].fd, readBuf.data(), 1023); - if (bytesRead == -1) - continue; - readBuf[bytesRead] = 0; - - parseRequest(state, std::string{readBuf.data()}); - } - } - - wl_display* display = state.display; - state = {}; - - wl_display_disconnect(display); - logfile.flush(); - logfile.close(); - return 0; -} diff --git a/hyprtester/clients/pointer-warp.cpp b/hyprtester/clients/pointer-warp.cpp deleted file mode 100644 index a57f99ae..00000000 --- a/hyprtester/clients/pointer-warp.cpp +++ /dev/null @@ -1,318 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -using Hyprutils::Math::Vector2D; -using namespace Hyprutils::Memory; - -struct SWlState { - wl_display* display; - CSharedPointer registry; - - // protocols - CSharedPointer wlCompositor; - CSharedPointer wlSeat; - CSharedPointer wlShm; - CSharedPointer xdgShell; - CSharedPointer pointerWarp; - - // shm/buffer stuff - CSharedPointer shmPool; - CSharedPointer shmBuf; - int shmFd; - size_t shmBufSize; - bool xrgb8888_support = false; - - // surface/toplevel stuff - CSharedPointer surf; - CSharedPointer xdgSurf; - CSharedPointer xdgToplevel; - Vector2D geom; - - // pointer - CSharedPointer pointer; - uint32_t enterSerial; -}; - -static bool debug, started, shouldExit; - -template -//NOLINTNEXTLINE -static void clientLog(std::format_string fmt, Args&&... args) { - std::println("{}", std::vformat(fmt.get(), std::make_format_args(args...))); - std::fflush(stdout); -} - -template -//NOLINTNEXTLINE -static void debugLog(std::format_string fmt, Args&&... args) { - if (!debug) - return; - std::println("{}", std::vformat(fmt.get(), std::make_format_args(args...))); - std::fflush(stdout); -} - -static bool bindRegistry(SWlState& state) { - state.registry = makeShared((wl_proxy*)wl_display_get_registry(state.display)); - - state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) { - const std::string NAME = name; - if (NAME == "wl_compositor") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlCompositor = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6)); - } else if (NAME == "wl_shm") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlShm = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1)); - } else if (NAME == "wl_seat") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlSeat = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9)); - } else if (NAME == "xdg_wm_base") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.xdgShell = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1)); - } else if (NAME == "wp_pointer_warp_v1") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.pointerWarp = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wp_pointer_warp_v1_interface, 1)); - } - }); - state.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t id) { debugLog("Global {} removed", id); }); - - wl_display_roundtrip(state.display); - - if (!state.wlCompositor || !state.wlShm || !state.wlSeat || !state.xdgShell || !state.pointerWarp) { - clientLog("Failed to get protocols from Hyprland"); - return false; - } - - return true; -} - -static bool createShm(SWlState& state, Vector2D geom) { - if (!state.xrgb8888_support) - return false; - - size_t stride = geom.x * 4; - size_t size = geom.y * stride; - if (!state.shmPool) { - const char* name = "/wl-shm-pointer-warp"; - state.shmFd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (state.shmFd < 0) - return false; - - if (shm_unlink(name) < 0 || ftruncate(state.shmFd, size) < 0) { - close(state.shmFd); - return false; - } - - state.shmPool = makeShared(state.wlShm->sendCreatePool(state.shmFd, size)); - if (!state.shmPool->resource()) { - close(state.shmFd); - state.shmFd = -1; - state.shmPool.reset(); - return false; - } - state.shmBufSize = size; - } else if (size > state.shmBufSize) { - if (ftruncate(state.shmFd, size) < 0) { - close(state.shmFd); - state.shmFd = -1; - state.shmPool.reset(); - return false; - } - - state.shmPool->sendResize(size); - state.shmBufSize = size; - } - - auto buf = makeShared(state.shmPool->sendCreateBuffer(0, geom.x, geom.y, stride, WL_SHM_FORMAT_XRGB8888)); - if (!buf->resource()) - return false; - - if (state.shmBuf) { - state.shmBuf->sendDestroy(); - state.shmBuf.reset(); - } - - state.shmBuf = buf; - - return true; -} - -static bool setupToplevel(SWlState& state) { - state.wlShm->setFormat([&](CCWlShm* p, uint32_t format) { - if (format == WL_SHM_FORMAT_XRGB8888) - state.xrgb8888_support = true; - }); - - state.xdgShell->setPing([&](CCXdgWmBase* p, uint32_t serial) { state.xdgShell->sendPong(serial); }); - - state.surf = makeShared(state.wlCompositor->sendCreateSurface()); - if (!state.surf->resource()) - return false; - - state.xdgSurf = makeShared(state.xdgShell->sendGetXdgSurface(state.surf->resource())); - if (!state.xdgSurf->resource()) - return false; - - state.xdgToplevel = makeShared(state.xdgSurf->sendGetToplevel()); - if (!state.xdgToplevel->resource()) - return false; - - state.xdgToplevel->setClose([&](CCXdgToplevel* p) { exit(0); }); - - state.xdgToplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) { - state.geom = {1280, 720}; - - if (!createShm(state, state.geom)) - exit(-1); - }); - - state.xdgSurf->setConfigure([&](CCXdgSurface* p, uint32_t serial) { - if (!state.shmBuf) - debugLog("xdgSurf configure but no buf made yet?"); - - state.xdgSurf->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y); - state.surf->sendAttach(state.shmBuf.get(), 0, 0); - state.surf->sendCommit(); - - state.xdgSurf->sendAckConfigure(serial); - - if (!started) { - started = true; - clientLog("started"); - } - }); - - state.xdgToplevel->sendSetTitle("pointer-warp test client"); - state.xdgToplevel->sendSetAppId("pointer-warp"); - - state.surf->sendAttach(nullptr, 0, 0); - state.surf->sendCommit(); - - return true; -} - -static bool setupSeat(SWlState& state) { - state.pointer = makeShared(state.wlSeat->sendGetPointer()); - if (!state.pointer->resource()) - return false; - - state.pointer->setEnter([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf, wl_fixed_t x, wl_fixed_t y) { - debugLog("Got pointer enter event, serial {}, x {}, y {}", serial, x, y); - state.enterSerial = serial; - }); - - state.pointer->setLeave([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf) { debugLog("Got pointer leave event, serial {}", serial); }); - - state.pointer->setMotion([&](CCWlPointer* p, uint32_t serial, wl_fixed_t x, wl_fixed_t y) { debugLog("Got pointer motion event, serial {}, x {}, y {}", serial, x, y); }); - - return true; -} - -// format is like below -// "warp 20 20\n" would ask to warp cursor to x=20,y=20 in surface local coords -static void parseRequest(SWlState& state, std::string req) { - if (req.contains("exit")) { - shouldExit = true; - return; - } - - if (!req.starts_with("warp ")) - return; - - auto it = req.find_first_of('\n'); - if (it == std::string::npos) - return; - - req = req.substr(0, it); - - it = req.find_first_of(' '); - if (it == std::string::npos) - return; - - req = req.substr(it + 1); - - it = req.find_first_of(' '); - - int x = std::stoi(req.substr(0, it)); - int y = std::stoi(req.substr(it + 1)); - - state.pointerWarp->sendWarpPointer(state.surf->resource(), state.pointer->resource(), wl_fixed_from_int(x), wl_fixed_from_int(y), state.enterSerial); - - // sync the request then reply - wl_display_roundtrip(state.display); - - clientLog("parsed request to move to x:{}, y:{}", x, y); -} - -int main(int argc, char** argv) { - if (argc != 1 && argc != 2) - clientLog("Only the \"--debug\" switch is allowed, it turns on debug logs."); - - if (argc == 2 && std::string{argv[1]} == "--debug") - debug = true; - - SWlState state; - - // WAYLAND_DISPLAY env should be set to the correct one - state.display = wl_display_connect(nullptr); - if (!state.display) { - clientLog("Failed to connect to wayland display"); - return -1; - } - - if (!bindRegistry(state) || !setupSeat(state) || !setupToplevel(state)) - return -1; - - std::array readBuf; - readBuf.fill(0); - - wl_display_flush(state.display); - - struct pollfd fds[2] = {{.fd = wl_display_get_fd(state.display), .events = POLLIN | POLLOUT}, {.fd = STDIN_FILENO, .events = POLLIN}}; - while (!shouldExit && poll(fds, 2, 0) != -1) { - if (fds[0].revents & POLLIN) { - wl_display_flush(state.display); - - if (wl_display_prepare_read(state.display) == 0) { - wl_display_read_events(state.display); - wl_display_dispatch_pending(state.display); - } else - wl_display_dispatch(state.display); - - int ret = 0; - do { - ret = wl_display_dispatch_pending(state.display); - wl_display_flush(state.display); - } while (ret > 0); - } - - if (fds[1].revents & POLLIN) { - ssize_t bytesRead = read(fds[1].fd, readBuf.data(), 1023); - if (bytesRead == -1) - continue; - readBuf[bytesRead] = 0; - - parseRequest(state, std::string{readBuf.data()}); - } - } - - wl_display* display = state.display; - state = {}; - - wl_display_disconnect(display); - return 0; -} diff --git a/hyprtester/clients/shortcut-inhibitor.cpp b/hyprtester/clients/shortcut-inhibitor.cpp deleted file mode 100644 index 0c6b4341..00000000 --- a/hyprtester/clients/shortcut-inhibitor.cpp +++ /dev/null @@ -1,297 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -using Hyprutils::Math::Vector2D; -using namespace Hyprutils::Memory; - -struct SWlState { - wl_display* display; - CSharedPointer registry; - - // protocols - CSharedPointer wlCompositor; - CSharedPointer wlSeat; - CSharedPointer wlShm; - CSharedPointer xdgShell; - CSharedPointer inhibitManager; - - // shm/buffer stuff - CSharedPointer shmPool; - CSharedPointer shmBuf; - int shmFd; - size_t shmBufSize; - bool xrgb8888_support = false; - - // surface/toplevel stuff - CSharedPointer surf; - CSharedPointer xdgSurf; - CSharedPointer xdgToplevel; - Vector2D geom; - - // pointer - CSharedPointer pointer; - uint32_t enterSerial; - - // shortcut inhibiting - CSharedPointer inhibitor; -}; - -static bool debug, started, shouldExit; - -template -//NOLINTNEXTLINE -static void clientLog(std::format_string fmt, Args&&... args) { - std::string text = std::vformat(fmt.get(), std::make_format_args(args...)); - std::println("{}", text); - std::fflush(stdout); -} - -template -//NOLINTNEXTLINE -static void debugLog(std::format_string fmt, Args&&... args) { - std::string text = std::vformat(fmt.get(), std::make_format_args(args...)); - if (!debug) - return; - std::println("{}", text); - std::fflush(stdout); -} - -static bool bindRegistry(SWlState& state) { - state.registry = makeShared((wl_proxy*)wl_display_get_registry(state.display)); - - state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) { - const std::string NAME = name; - if (NAME == "wl_compositor") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlCompositor = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6)); - } else if (NAME == "wl_shm") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlShm = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1)); - } else if (NAME == "wl_seat") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.wlSeat = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9)); - } else if (NAME == "xdg_wm_base") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.xdgShell = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1)); - } else if (NAME == "zwp_keyboard_shortcuts_inhibit_manager_v1") { - debugLog(" > binding to global: {} (version {}) with id {}", name, version, id); - state.inhibitManager = makeShared( - (wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1)); - } - }); - state.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t id) { debugLog("Global {} removed", id); }); - - wl_display_roundtrip(state.display); - - if (!state.wlCompositor || !state.wlShm || !state.wlSeat || !state.xdgShell || !state.inhibitManager) { - clientLog("Failed to get protocols from Hyprland"); - return false; - } - - return true; -} - -static bool createShm(SWlState& state, Vector2D geom) { - if (!state.xrgb8888_support) - return false; - - size_t stride = geom.x * 4; - size_t size = geom.y * stride; - if (!state.shmPool) { - const char* name = "/wl-shm-shortcut-inhibitor"; - state.shmFd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (state.shmFd < 0) - return false; - - if (shm_unlink(name) < 0 || ftruncate(state.shmFd, size) < 0) { - close(state.shmFd); - return false; - } - - state.shmPool = makeShared(state.wlShm->sendCreatePool(state.shmFd, size)); - if (!state.shmPool->resource()) { - close(state.shmFd); - state.shmFd = -1; - state.shmPool.reset(); - return false; - } - state.shmBufSize = size; - } else if (size > state.shmBufSize) { - if (ftruncate(state.shmFd, size) < 0) { - close(state.shmFd); - state.shmFd = -1; - state.shmPool.reset(); - return false; - } - - state.shmPool->sendResize(size); - state.shmBufSize = size; - } - - auto buf = makeShared(state.shmPool->sendCreateBuffer(0, geom.x, geom.y, stride, WL_SHM_FORMAT_XRGB8888)); - if (!buf->resource()) - return false; - - if (state.shmBuf) { - state.shmBuf->sendDestroy(); - state.shmBuf.reset(); - } - - state.shmBuf = buf; - - return true; -} - -static bool setupToplevel(SWlState& state) { - state.wlShm->setFormat([&](CCWlShm* p, uint32_t format) { - if (format == WL_SHM_FORMAT_XRGB8888) - state.xrgb8888_support = true; - }); - - state.xdgShell->setPing([&](CCXdgWmBase* p, uint32_t serial) { state.xdgShell->sendPong(serial); }); - - state.surf = makeShared(state.wlCompositor->sendCreateSurface()); - if (!state.surf->resource()) - return false; - - state.xdgSurf = makeShared(state.xdgShell->sendGetXdgSurface(state.surf->resource())); - if (!state.xdgSurf->resource()) - return false; - - state.xdgToplevel = makeShared(state.xdgSurf->sendGetToplevel()); - if (!state.xdgToplevel->resource()) - return false; - - state.xdgToplevel->setClose([&](CCXdgToplevel* p) { exit(0); }); - - state.xdgToplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) { - state.geom = {1280, 720}; - - if (!createShm(state, state.geom)) - exit(-1); - }); - - state.xdgSurf->setConfigure([&](CCXdgSurface* p, uint32_t serial) { - if (!state.shmBuf) - debugLog("xdgSurf configure but no buf made yet?"); - - state.xdgSurf->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y); - state.surf->sendAttach(state.shmBuf.get(), 0, 0); - state.surf->sendCommit(); - - state.xdgSurf->sendAckConfigure(serial); - - if (!started) { - started = true; - clientLog("started"); - } - }); - - state.xdgToplevel->sendSetTitle("shortcut-inhibitor test client"); - state.xdgToplevel->sendSetAppId("shortcut-inhibitor"); - - state.surf->sendAttach(nullptr, 0, 0); - state.surf->sendCommit(); - - return true; -} - -static bool setupSeat(SWlState& state) { - state.pointer = makeShared(state.wlSeat->sendGetPointer()); - if (!state.pointer->resource()) - return false; - - state.pointer->setEnter([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf, wl_fixed_t x, wl_fixed_t y) { - debugLog("Got pointer enter event, serial {}, x {}, y {}", serial, x, y); - state.enterSerial = serial; - }); - - state.pointer->setLeave([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf) { debugLog("Got pointer leave event, serial {}", serial); }); - - state.pointer->setMotion([&](CCWlPointer* p, uint32_t serial, wl_fixed_t x, wl_fixed_t y) { debugLog("Got pointer motion event, serial {}, x {}, y {}", serial, x, y); }); - - return true; -} - -static void parseRequest(SWlState& state, std::string req) { - if (req.starts_with("on")) { - state.inhibitor = makeShared(state.inhibitManager->sendInhibitShortcuts(state.surf->resource(), state.wlSeat->resource())); - - state.inhibitor->setActive([&](CCZwpKeyboardShortcutsInhibitorV1* inhibitorV1) { clientLog("inhibiting"); }); - state.inhibitor->setInactive([&](CCZwpKeyboardShortcutsInhibitorV1* inhibitorV1) { clientLog("inhibit disabled by compositor"); }); - } else if (req.starts_with("off")) { - state.inhibitor->sendDestroy(); - state.inhibitor.reset(); - shouldExit = true; - clientLog("inhibit disabled by request"); - } -} - -int main(int argc, char** argv) { - if (argc != 1 && argc != 2) - clientLog("Only the \"--debug\" switch is allowed, it turns on debug logs."); - - if (argc == 2 && std::string{argv[1]} == "--debug") - debug = true; - - SWlState state; - - // WAYLAND_DISPLAY env should be set to the correct one - state.display = wl_display_connect(nullptr); - if (!state.display) { - clientLog("Failed to connect to wayland display"); - return -1; - } - - if (!bindRegistry(state) || !setupSeat(state) || !setupToplevel(state)) - return -1; - - std::array readBuf; - readBuf.fill(0); - - wl_display_flush(state.display); - - struct pollfd fds[2] = {{.fd = wl_display_get_fd(state.display), .events = POLLIN | POLLOUT}, {.fd = STDIN_FILENO, .events = POLLIN}}; - while (!shouldExit && poll(fds, 2, 0) != -1) { - if (fds[0].revents & POLLIN) { - wl_display_flush(state.display); - - if (wl_display_prepare_read(state.display) == 0) { - wl_display_read_events(state.display); - wl_display_dispatch_pending(state.display); - } else - wl_display_dispatch(state.display); - - int ret = 0; - do { - ret = wl_display_dispatch_pending(state.display); - wl_display_flush(state.display); - } while (ret > 0); - } - - if (fds[1].revents & POLLIN) { - ssize_t bytesRead = read(fds[1].fd, readBuf.data(), 1023); - if (bytesRead == -1) - continue; - readBuf[bytesRead] = 0; - - parseRequest(state, std::string{readBuf.data()}); - } - } - - wl_display* display = state.display; - state = {}; - - wl_display_disconnect(display); - return 0; -} diff --git a/hyprtester/plugin/Makefile b/hyprtester/plugin/Makefile deleted file mode 100644 index 0fcf5f2e..00000000 --- a/hyprtester/plugin/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -CXXFLAGS = -shared -fPIC -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 diff --git a/hyprtester/plugin/build.sh b/hyprtester/plugin/build.sh deleted file mode 100755 index 5bb5d017..00000000 --- a/hyprtester/plugin/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -make clean -make all diff --git a/hyprtester/plugin/src/globals.hpp b/hyprtester/plugin/src/globals.hpp deleted file mode 100644 index 37e8363b..00000000 --- a/hyprtester/plugin/src/globals.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -inline HANDLE PHANDLE = nullptr; \ No newline at end of file diff --git a/hyprtester/plugin/src/main.cpp b/hyprtester/plugin/src/main.cpp deleted file mode 100644 index ce83c5b4..00000000 --- a/hyprtester/plugin/src/main.cpp +++ /dev/null @@ -1,387 +0,0 @@ -#include -#include -#include -#include - -#define private public -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#undef private - -#include -#include -using namespace Hyprutils::Utils; -using namespace Hyprutils::String; - -#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 = Desktop::focusState()->window(); - 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_layoutManager->performSnap(pos, size, PLASTWINDOW->layoutTarget(), MBIND_MOVE, -1, size); - - PLASTWINDOW->layoutTarget()->setPositionGlobal(CBox{pos, size}); - - return {}; -} - -class CTestKeyboard : public IKeyboard { - public: - static SP create(bool isVirtual) { - auto keeb = SP(new CTestKeyboard()); - keeb->m_self = keeb; - keeb->m_isVirtual = isVirtual; - keeb->m_shareStates = !isVirtual; - return keeb; - } - - virtual bool isVirtual() { - return m_isVirtual; - } - - virtual SP aq() { - return nullptr; - } - - void sendKey(uint32_t key, bool pressed) { - auto event = IKeyboard::SKeyEvent{ - .timeMs = sc(Time::millis(Time::steadyNow())), - .keycode = key, - .state = pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, - }; - updatePressed(event.keycode, pressed); - m_keyboardEvents.key.emit(event); - } - - void destroy() { - m_events.destroy.emit(); - } - - private: - bool m_isVirtual = false; -}; - -class CTestMouse : public IPointer { - public: - static SP create(bool isVirtual) { - auto maus = SP(new CTestMouse()); - maus->m_self = maus; - maus->m_isVirtual = isVirtual; - maus->m_deviceName = "test-mouse"; - maus->m_hlName = "test-mouse"; - return maus; - } - - virtual bool isVirtual() { - return m_isVirtual; - } - - virtual SP aq() { - return nullptr; - } - - void destroy() { - m_events.destroy.emit(); - } - - private: - bool m_isVirtual = false; -}; - -SP g_mouse; -SP g_keyboard; - -static SDispatchResult pressAlt(std::string in) { - g_pInputManager->m_lastMods = in == "1" ? HL_MODIFIER_ALT : 0; - - return {.success = true}; -} - -static SDispatchResult simulateGesture(std::string in) { - CVarList data(in); - - uint32_t fingers = 3; - try { - fingers = std::stoul(data[1]); - } catch (...) { return {.success = false}; } - - if (data[0] == "down") { - g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{}); - g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {0, 300}}); - g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{}); - } else if (data[0] == "up") { - g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{}); - g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {0, -300}}); - g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{}); - } else if (data[0] == "left") { - g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{}); - g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {-300, 0}}); - g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{}); - } else { - g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{}); - g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {300, 0}}); - g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{}); - } - - return {.success = true}; -} - -static SDispatchResult vkb(std::string in) { - auto tkb0 = CTestKeyboard::create(false); - auto tkb1 = CTestKeyboard::create(false); - auto vkb0 = CTestKeyboard::create(true); - - g_pInputManager->newKeyboard(tkb0); - g_pInputManager->newKeyboard(tkb1); - g_pInputManager->newKeyboard(vkb0); - - CScopeGuard x([&] { - tkb0->destroy(); - tkb1->destroy(); - vkb0->destroy(); - }); - - const auto& PRESSED = g_pInputManager->getKeysFromAllKBs(); - const uint32_t TESTKEY = 1; - - tkb0->sendKey(TESTKEY, true); - if (!std::ranges::contains(PRESSED, TESTKEY)) { - return { - .success = false, - .error = "Expected pressed key not found", - }; - } - - tkb1->sendKey(TESTKEY, true); - tkb0->sendKey(TESTKEY, false); - if (!std::ranges::contains(PRESSED, TESTKEY)) { - return { - .success = false, - .error = "Expected pressed key not found (kb share state)", - }; - } - - vkb0->sendKey(TESTKEY, true); - tkb1->sendKey(TESTKEY, false); - if (std::ranges::contains(PRESSED, TESTKEY)) { - return { - .success = false, - .error = "Expected released key found in pressed (vkb no share state)", - }; - } - - return {}; -} - -static SDispatchResult scroll(std::string in) { - double by; - try { - by = std::stod(in); - } catch (...) { return SDispatchResult{.success = false, .error = "invalid input"}; } - - Log::logger->log(Log::DEBUG, "tester: scrolling by {}", by); - - g_mouse->m_pointerEvents.axis.emit(IPointer::SAxisEvent{ - .delta = by, - .deltaDiscrete = 120, - .mouse = true, - }); - - return {}; -} - -static SDispatchResult click(std::string in) { - CVarList2 data(std::move(in)); - - uint32_t button; - bool pressed; - try { - button = std::stoul(std::string{data[0]}); - pressed = std::stoul(std::string{data[1]}) == 1; - } catch (...) { return {.success = false, .error = "invalid input"}; } - - Log::logger->log(Log::DEBUG, "tester: mouse button {} state {}", button, pressed); - - g_mouse->m_pointerEvents.button.emit(IPointer::SButtonEvent{ - .timeMs = sc(Time::millis(Time::steadyNow())), - .button = button, - .state = pressed ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED, - .mouse = true, - }); - - return {}; -} - -static SDispatchResult keybind(std::string in) { - CVarList2 data(std::move(in)); - // 0 = release, 1 = press - bool press; - // See src/devices/IKeyboard.hpp : eKeyboardModifiers for modifier bitmasks - // 0 = none, eKeyboardModifiers is shifted to start at 1 - uint32_t modifier; - // keycode - uint32_t key; - try { - press = std::stoul(std::string{data[0]}) == 1; - modifier = std::stoul(std::string{data[1]}); - key = std::stoul(std::string{data[2]}) - 8; // xkb offset - } catch (...) { return {.success = false, .error = "invalid input"}; } - - uint32_t modifierMask = 0; - if (modifier > 0) - modifierMask = 1 << (modifier - 1); - g_pInputManager->m_lastMods = modifierMask; - g_keyboard->sendKey(key, press); - - return {}; -} - -static Desktop::Rule::CWindowRuleEffectContainer::storageType windowRuleIDX = 0; - -// -static SDispatchResult addWindowRule(std::string in) { - windowRuleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule"); - - if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != windowRuleIDX) - return {.success = false, .error = "re-registering returned a different id?"}; - return {}; -} - -static SDispatchResult checkWindowRule(std::string in) { - const auto PLASTWINDOW = Desktop::focusState()->window(); - - if (!PLASTWINDOW) - return {.success = false, .error = "No window"}; - - if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(windowRuleIDX)) - return {.success = false, .error = "No rule"}; - - if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[windowRuleIDX]->effect != "effect") - return {.success = false, .error = "Effect isn't \"effect\""}; - - return {}; -} - -static Desktop::Rule::CLayerRuleEffectContainer::storageType layerRuleIDX = 0; - -static SDispatchResult addLayerRule(std::string in) { - layerRuleIDX = Desktop::Rule::layerEffects()->registerEffect("plugin_rule"); - - if (Desktop::Rule::layerEffects()->registerEffect("plugin_rule") != layerRuleIDX) - return {.success = false, .error = "re-registering returned a different id?"}; - return {}; -} - -static SDispatchResult checkLayerRule(std::string in) { - if (g_pCompositor->m_layers.size() != 3) - return {.success = false, .error = "Layers under test not here"}; - - for (const auto& layer : g_pCompositor->m_layers) { - if (layer->m_namespace == "rule-layer") { - - if (!layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX)) - return {.success = false, .error = "No rule"}; - - if (layer->m_ruleApplicator->m_otherProps.props[layerRuleIDX]->effect != "effect") - return {.success = false, .error = "Effect isn't \"effect\""}; - - } else if (layer->m_namespace == "norule-layer") { - - if (layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX)) - return {.success = false, .error = "Rule even though it shouldn't"}; - - } else - return {.success = false, .error = "Unrecognized layer"}; - } - - return {}; -} - -static SDispatchResult floatingFocusOnFullscreen(std::string in) { - const auto PLASTWINDOW = Desktop::focusState()->window(); - - if (!PLASTWINDOW) - return {.success = false, .error = "No window"}; - - if (!PLASTWINDOW->m_isFloating) - return {.success = false, .error = "Window must be floating"}; - - if (PLASTWINDOW->m_alpha != 1.f) - return {.success = false, .error = "floating window doesnt restore it opacity when focused on fullscreen workspace"}; - - if (!PLASTWINDOW->m_createdOverFullscreen) - return {.success = false, .error = "floating window doesnt get flagged as createdOverFullscreen"}; - - 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); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:vkb", ::vkb); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:alt", ::pressAlt); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:click", ::click); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_window_rule", ::addWindowRule); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_window_rule", ::checkWindowRule); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_layer_rule", ::addLayerRule); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_layer_rule", ::checkLayerRule); - HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:floating_focus_on_fullscreen", ::floatingFocusOnFullscreen); - - // init mouse - g_mouse = CTestMouse::create(false); - g_pInputManager->newMouse(g_mouse); - - // init keyboard - g_keyboard = CTestKeyboard::create(false); - g_pInputManager->newKeyboard(g_keyboard); - - return {"hyprtestplugin", "hyprtestplugin", "Vaxry", "1.0"}; -} - -APICALL EXPORT void PLUGIN_EXIT() { - g_mouse->destroy(); - g_mouse.reset(); - g_keyboard->destroy(); - g_keyboard.reset(); -} diff --git a/hyprtester/protocols/.gitignore b/hyprtester/protocols/.gitignore deleted file mode 100644 index d6b7ef32..00000000 --- a/hyprtester/protocols/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/hyprtester/src/Log.hpp b/hyprtester/src/Log.hpp deleted file mode 100644 index 7eeb3ece..00000000 --- a/hyprtester/src/Log.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -#include -#include - -namespace NLog { - template - //NOLINTNEXTLINE - void log(std::format_string fmt, Args&&... args) { - std::string logMsg = ""; - - logMsg += std::vformat(fmt.get(), std::make_format_args(args...)); - - std::println("{}", logMsg); - std::fflush(stdout); - } -} \ No newline at end of file diff --git a/hyprtester/src/hyprctlCompat.cpp b/hyprtester/src/hyprctlCompat.cpp deleted file mode 100644 index 2c8e0644..00000000 --- a/hyprtester/src/hyprctlCompat.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "hyprctlCompat.hpp" -#include "shared.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -using namespace Hyprutils::Memory; - -static int getUID() { - const auto UID = getuid(); - const auto PWUID = getpwuid(UID); - return PWUID ? PWUID->pw_uid : UID; -} - -static std::string getRuntimeDir() { - const auto XDG = getenv("XDG_RUNTIME_DIR"); - - if (!XDG) { - const std::string USERID = std::to_string(getUID()); - return "/run/user/" + USERID + "/hypr"; - } - - return std::string{XDG} + "/hypr"; -} - -std::vector instances() { - std::vector 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, rc(&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; -} \ No newline at end of file diff --git a/hyprtester/src/hyprctlCompat.hpp b/hyprtester/src/hyprctlCompat.hpp deleted file mode 100644 index bf4ce2a7..00000000 --- a/hyprtester/src/hyprctlCompat.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include -#include - -struct SInstanceData { - std::string id; - uint64_t time; - uint64_t pid; - std::string wlSocket; - bool valid = true; -}; - -std::vector instances(); -std::string getFromSocket(const std::string& cmd); \ No newline at end of file diff --git a/hyprtester/src/main.cpp b/hyprtester/src/main.cpp deleted file mode 100644 index a1bb7d9a..00000000 --- a/hyprtester/src/main.cpp +++ /dev/null @@ -1,261 +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/clients/tests.hpp" -#include "tests/plugin/plugin.hpp" - -#include -#include -#include -#include -using namespace Hyprutils::Memory; - -#include -#include -#include -#include -#include -#include -#include - -#include "Log.hpp" - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define SP CSharedPointer - -static int ret = 0; -static SP 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(binaryPath, std::vector{"--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(); - - if (argc > 1) { - std::span args{argv + 1, sc(argc - 1)}; - - for (auto it = args.begin(); it != args.end(); it++) { - std::string_view value = *it; - - if (value == "--config" || value == "-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 (value == "--binary" || value == "-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 (value == "--plugin" || value == "-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 (value == "--help" || value == "-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); - - NLog::log("{}Running main tests", Colors::YELLOW); - - for (const auto& fn : testFns) { - EXPECT(fn(), true); - } - - NLog::log("{}Running protocol client tests", Colors::YELLOW); - - for (const auto& fn : clientTestFns) { - EXPECT(fn(), true); - } - - NLog::log("{}running plugin test", Colors::YELLOW); - EXPECT(testPlugin(), true); - - NLog::log("{}running vkb test from plugin", Colors::YELLOW); - EXPECT(testVkb(), 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; -} diff --git a/hyprtester/src/shared.hpp b/hyprtester/src/shared.hpp deleted file mode 100644 index 941788fd..00000000 --- a/hyprtester/src/shared.hpp +++ /dev/null @@ -1,111 +0,0 @@ -// Stolen from hyprutils - -#pragma once -#include - -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_MAX_DELTA(expr, desired, delta) \ - if (const auto RESULT = expr; std::abs(RESULT - (desired)) > delta) { \ - NLog::log("{}Failed: {}{}, expected max delta of {}, got delta {} ({} - {}). Source: {}@{}.", Colors::RED, Colors::RESET, #expr, delta, (RESULT - (desired)), RESULT, \ - desired, __FILE__, __LINE__); \ - ret = 1; \ - TESTS_FAILED++; \ - } else { \ - NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, (RESULT - (desired))); \ - TESTS_PASSED++; \ - } - -#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_NOT(expr, val) \ - if (const auto RESULT = expr; RESULT == (val)) { \ - NLog::log("{}Failed: {}{}, expected not {}, 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, #needle, __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") diff --git a/hyprtester/src/tests/clients/.gitignore b/hyprtester/src/tests/clients/.gitignore deleted file mode 100644 index 77a34c55..00000000 --- a/hyprtester/src/tests/clients/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build.hpp diff --git a/hyprtester/src/tests/clients/child-window.cpp b/hyprtester/src/tests/clients/child-window.cpp deleted file mode 100644 index a5680d4f..00000000 --- a/hyprtester/src/tests/clients/child-window.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "../shared.hpp" -#include "tests.hpp" -#include "build.hpp" - -#include -#include - -#include -#include -#include -#include - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define SP CSharedPointer - -struct SClient { - SP proc; - std::array readBuf; - CFileDescriptor readFd, writeFd; - struct pollfd fds; -}; - -static int ret = 0; - -static bool waitForWindow(SP proc, int windowsBefore) { - int counter = 0; - while (Tests::processAlive(proc->pid()) && Tests::windowCount() == windowsBefore) { - counter++; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - if (counter > 50) - return false; - } - - NLog::log("{}Waited {} milliseconds for window to open", Colors::YELLOW, counter * 100); - return Tests::processAlive(proc->pid()); -} - -static bool startClient(SClient& client) { - NLog::log("{}Attempting to start child-window client", Colors::YELLOW); - - client.proc = makeShared(binaryDir + "/child-window", std::vector{}); - - client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); - - int procInPipeFd[2], procOutPipeFd[2]; - if (pipe(procInPipeFd) != 0 || pipe(procOutPipeFd) != 0) { - NLog::log("{}Unable to open pipe to client", Colors::RED); - return false; - } - - client.writeFd = CFileDescriptor(procInPipeFd[1]); - client.proc->setStdinFD(procInPipeFd[0]); - - client.readFd = CFileDescriptor(procOutPipeFd[0]); - client.proc->setStdoutFD(procOutPipeFd[1]); - - if (!client.proc->runAsync()) { - NLog::log("{}Failed to run client", Colors::RED); - return false; - } - - close(procInPipeFd[0]); - close(procOutPipeFd[1]); - - if (!waitForWindow(client.proc, Tests::windowCount())) { - NLog::log("{}Window took too long to open", Colors::RED); - return false; - } - - NLog::log("{}Started child-window client", Colors::YELLOW); - return true; -} - -static void stopClient(SClient& client) { - std::string cmd = "exit\n"; - write(client.writeFd.get(), cmd.c_str(), cmd.length()); - - kill(client.proc->pid(), SIGKILL); - client.proc.reset(); -} - -static bool createChild(SClient& client) { - std::string cmd = "toplevel\n"; - if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) - return false; - - if (!waitForWindow(client.proc, Tests::windowCount())) - NLog::log("{}Child window took too long to open", Colors::RED); - - if (getFromSocket("/dispatch focuswindow class:child-test-child") != "ok") { - NLog::log("{}Failed to focus child window", Colors::RED); - return false; - } - - return true; -} - -static bool test() { - SClient client; - - if (!startClient(client)) - return false; - OK(getFromSocket("/dispatch setfloating class:child-test-parent")); - OK(getFromSocket("/dispatch pin class:child-test-parent")); - - createChild(client); - EXPECT(Tests::windowCount(), 2) - EXPECT_COUNT_STRING(getFromSocket("/clients"), "pinned: 1", 2); - - stopClient(client); - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - // test that child windows (shouldBeFloated) are not auto-grouped - NLog::log("{}Test child windows are not auto-grouped", Colors::GREEN); - auto kitty = Tests::spawnKitty(); - if (!kitty) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - - // create group and enable auto-grouping - OK(getFromSocket("/dispatch togglegroup")); - OK(getFromSocket("/keyword group:auto_group true")); - - SClient client2; - if (!startClient(client2)) - return false; - - EXPECT(Tests::windowCount(), 2); - createChild(client2); - EXPECT(Tests::windowCount(), 3); - - // child has set_parent so shouldBeFloated returns true, it should not be auto-grouped - EXPECT_COUNT_STRING(getFromSocket("/clients"), "grouped: 0", 1); - - stopClient(client2); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - return !ret; -} - -REGISTER_CLIENT_TEST_FN(test); diff --git a/hyprtester/src/tests/clients/pointer-scroll.cpp b/hyprtester/src/tests/clients/pointer-scroll.cpp deleted file mode 100644 index b5fb68fb..00000000 --- a/hyprtester/src/tests/clients/pointer-scroll.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "../shared.hpp" -#include "tests.hpp" -#include "build.hpp" - -#include -#include - -#include -#include -#include -#include - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define SP CSharedPointer - -struct SClient { - SP proc; - std::array readBuf; - CFileDescriptor readFd, writeFd; - struct pollfd fds; -}; - -static int ret = 0; - -static bool startClient(SClient& client) { - client.proc = makeShared(binaryDir + "/pointer-scroll", std::vector{}); - - client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); - - int pipeFds1[2], pipeFds2[2]; - if (pipe(pipeFds1) != 0 || pipe(pipeFds2) != 0) { - NLog::log("{}Unable to open pipe to client", Colors::RED); - return false; - } - - client.writeFd = CFileDescriptor(pipeFds1[1]); - client.proc->setStdinFD(pipeFds1[0]); - - client.readFd = CFileDescriptor(pipeFds2[0]); - client.proc->setStdoutFD(pipeFds2[1]); - - const int COUNT_BEFORE = Tests::windowCount(); - client.proc->runAsync(); - - close(pipeFds1[0]); - close(pipeFds2[1]); - - client.fds = {.fd = client.readFd.get(), .events = POLLIN}; - if (poll(&client.fds, 1, 1000) != 1 || !(client.fds.revents & POLLIN)) - return false; - - client.readBuf.fill(0); - if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) - return false; - - std::string ret = std::string{client.readBuf.data()}; - if (ret.find("started") == std::string::npos) { - NLog::log("{}Failed to start pointer-scroll client, read {}", Colors::RED, ret); - return false; - } - - // wait for window to appear - int counter = 0; - while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { - counter++; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - if (counter > 50) { - NLog::log("{}pointer-scroll client took too long to open", Colors::RED); - return false; - } - } - - if (getFromSocket(std::format("/dispatch setprop pid:{} no_anim 1", client.proc->pid())) != "ok") { - NLog::log("{}Failed to disable animations for client window", Colors::RED, ret); - return false; - } - - if (getFromSocket(std::format("/dispatch focuswindow pid:{}", client.proc->pid())) != "ok") { - NLog::log("{}Failed to focus pointer-scroll client", Colors::RED, ret); - return false; - } - - NLog::log("{}Started pointer-scroll client", Colors::YELLOW); - - return true; -} - -static void stopClient(SClient& client) { - std::string cmd = "exit\n"; - write(client.writeFd.get(), cmd.c_str(), cmd.length()); - - kill(client.proc->pid(), SIGKILL); - client.proc.reset(); -} - -static int getLastDelta(SClient& client) { - std::string cmd = "hypr"; - if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) - return false; - - if (poll(&client.fds, 1, 1500) != 1 || !(client.fds.revents & POLLIN)) - return false; - ssize_t bytesRead = read(client.fds.fd, client.readBuf.data(), 1023); - if (bytesRead == -1) - return false; - - client.readBuf[bytesRead] = 0; - std::string received = std::string{client.readBuf.data()}; - received.pop_back(); - - try { - return std::stoi(received); - } catch (...) { return -1; } -} - -static bool sendScroll(int delta) { - return getFromSocket(std::format("/dispatch plugin:test:scroll {}", delta)) == "ok"; -} - -static bool test() { - SClient client; - - if (!startClient(client)) - return false; - - EXPECT(getFromSocket("/keyword input:emulate_discrete_scroll 0"), "ok"); - - EXPECT(sendScroll(10), true); - EXPECT(getLastDelta(client), 10); - - EXPECT(getFromSocket("/keyword input:scroll_factor 2"), "ok"); - EXPECT(sendScroll(10), true); - EXPECT(getLastDelta(client), 20); - - EXPECT(getFromSocket("r/keyword device[test-mouse-1]:scroll_factor 3"), "ok"); - EXPECT(sendScroll(10), true); - EXPECT(getLastDelta(client), 30); - - EXPECT(getFromSocket("r/dispatch setprop active scroll_mouse 4"), "ok"); - EXPECT(sendScroll(10), true); - EXPECT(getLastDelta(client), 40); - - stopClient(client); - - NLog::log("{}Reloading the config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_CLIENT_TEST_FN(test); diff --git a/hyprtester/src/tests/clients/pointer-warp.cpp b/hyprtester/src/tests/clients/pointer-warp.cpp deleted file mode 100644 index be992566..00000000 --- a/hyprtester/src/tests/clients/pointer-warp.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "../shared.hpp" -#include "tests.hpp" -#include "build.hpp" - -#include -#include - -#include -#include -#include -#include - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define SP CSharedPointer - -struct SClient { - SP proc; - std::array readBuf; - CFileDescriptor readFd, writeFd; - struct pollfd fds; -}; - -static int ret = 0; - -static bool startClient(SClient& client) { - client.proc = makeShared(binaryDir + "/pointer-warp", std::vector{}); - - client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); - - int pipeFds1[2], pipeFds2[2]; - if (pipe(pipeFds1) != 0 || pipe(pipeFds2) != 0) { - NLog::log("{}Unable to open pipe to client", Colors::RED); - return false; - } - - client.writeFd = CFileDescriptor(pipeFds1[1]); - client.proc->setStdinFD(pipeFds1[0]); - - client.readFd = CFileDescriptor(pipeFds2[0]); - client.proc->setStdoutFD(pipeFds2[1]); - - const int COUNT_BEFORE = Tests::windowCount(); - client.proc->runAsync(); - - close(pipeFds1[0]); - close(pipeFds2[1]); - - client.fds = {.fd = client.readFd.get(), .events = POLLIN}; - if (poll(&client.fds, 1, 1000) != 1 || !(client.fds.revents & POLLIN)) - return false; - - client.readBuf.fill(0); - if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) - return false; - - std::string ret = std::string{client.readBuf.data()}; - if (ret.find("started") == std::string::npos) { - NLog::log("{}Failed to start pointer-warp client, read {}", Colors::RED, ret); - return false; - } - - // wait for window to appear - int counter = 0; - while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { - counter++; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - if (counter > 50) { - NLog::log("{}pointer-warp client took too long to open", Colors::RED); - return false; - } - } - - if (getFromSocket(std::format("/dispatch setprop pid:{} no_anim 1", client.proc->pid())) != "ok") { - NLog::log("{}Failed to disable animations for client window", Colors::RED, ret); - return false; - } - - if (getFromSocket(std::format("/dispatch focuswindow pid:{}", client.proc->pid())) != "ok") { - NLog::log("{}Failed to focus pointer-warp client", Colors::RED, ret); - return false; - } - - NLog::log("{}Started pointer-warp client", Colors::YELLOW); - - return true; -} - -static void stopClient(SClient& client) { - std::string cmd = "exit\n"; - write(client.writeFd.get(), cmd.c_str(), cmd.length()); - - kill(client.proc->pid(), SIGKILL); - client.proc.reset(); -} - -// format is like below -// "warp 20 20\n" would ask to warp cursor to x=20,y=20 in surface local coords -static bool sendWarp(SClient& client, int x, int y) { - std::string cmd = std::format("warp {} {}\n", x, y); - if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) - return false; - - if (poll(&client.fds, 1, 1500) != 1 || !(client.fds.revents & POLLIN)) - return false; - ssize_t bytesRead = read(client.fds.fd, client.readBuf.data(), 1023); - if (bytesRead == -1) - return false; - - client.readBuf[bytesRead] = 0; - std::string recieved = std::string{client.readBuf.data()}; - recieved.pop_back(); - - return true; -} - -static bool isCursorPos(int x, int y) { - // TODO: add a better way to do this using test plugin? - std::string res = getFromSocket("/cursorpos"); - if (res == "error") { - NLog::log("{}Cursorpos err'd: {}", Colors::RED, res); - return false; - } - - auto it = res.find_first_of(' '); - if (res.at(it - 1) != ',') { - NLog::log("{}Cursorpos err'd: {}", Colors::RED, res); - return false; - } - - int cursorX = std::stoi(res.substr(0, it - 1)); - int cursorY = std::stoi(res.substr(it + 1)); - - // somehow this is always gives 1 less than surfbox->pos()?? - res = getFromSocket("/activewindow"); - it = res.find("at: ") + 4; - res = res.substr(it, res.find_first_of('\n', it) - it); - - it = res.find_first_of(','); - int clientX = cursorX - std::stoi(res.substr(0, it)) + 1; - int clientY = cursorY - std::stoi(res.substr(it + 1)) + 1; - - return clientX == x && clientY == y; -} - -static bool test() { - SClient client; - - if (!startClient(client)) - return false; - - EXPECT(sendWarp(client, 100, 100), true); - EXPECT(isCursorPos(100, 100), true); - - EXPECT(sendWarp(client, 0, 0), true); - EXPECT(isCursorPos(0, 0), true); - - EXPECT(sendWarp(client, 200, 200), true); - EXPECT(isCursorPos(200, 200), true); - - EXPECT(sendWarp(client, 100, -100), true); - EXPECT(isCursorPos(200, 200), true); - - EXPECT(sendWarp(client, 234, 345), true); - EXPECT(isCursorPos(234, 345), true); - - EXPECT(sendWarp(client, -1, -1), true); - EXPECT(isCursorPos(234, 345), true); - - EXPECT(sendWarp(client, 1, -1), true); - EXPECT(isCursorPos(234, 345), true); - - EXPECT(sendWarp(client, 13, 37), true); - EXPECT(isCursorPos(13, 37), true); - - EXPECT(sendWarp(client, -100, 100), true); - EXPECT(isCursorPos(13, 37), true); - - EXPECT(sendWarp(client, -1, 1), true); - EXPECT(isCursorPos(13, 37), true); - - stopClient(client); - - NLog::log("{}Reloading the config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_CLIENT_TEST_FN(test); diff --git a/hyprtester/src/tests/clients/shortcut-inhibitor.cpp b/hyprtester/src/tests/clients/shortcut-inhibitor.cpp deleted file mode 100644 index 91c3376c..00000000 --- a/hyprtester/src/tests/clients/shortcut-inhibitor.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "../shared.hpp" -#include "tests.hpp" -#include "build.hpp" - -#include -#include - -#include -#include -#include -#include - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define SP CSharedPointer - -struct SClient { - SP proc; - std::array readBuf; - CFileDescriptor readFd, writeFd; - struct pollfd fds; -}; - -static int ret = 0; - -static bool startClient(SClient& client) { - Tests::killAllWindows(); - client.proc = makeShared(binaryDir + "/shortcut-inhibitor", std::vector{}); - - client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); - - int pipeFds1[2], pipeFds2[2]; - if (pipe(pipeFds1) != 0 || pipe(pipeFds2) != 0) { - NLog::log("{}Unable to open pipe to client", Colors::RED); - return false; - } - - client.writeFd = CFileDescriptor(pipeFds1[1]); - client.proc->setStdinFD(pipeFds1[0]); - - client.readFd = CFileDescriptor(pipeFds2[0]); - client.proc->setStdoutFD(pipeFds2[1]); - - const int COUNT_BEFORE = Tests::windowCount(); - client.proc->runAsync(); - - close(pipeFds1[0]); - close(pipeFds2[1]); - - client.fds = {.fd = client.readFd.get(), .events = POLLIN}; - if (poll(&client.fds, 1, 1000) != 1 || !(client.fds.revents & POLLIN)) { - NLog::log("{}shortcut-inhibitor client failed poll", Colors::RED); - return false; - } - - client.readBuf.fill(0); - if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) { - NLog::log("{}shortcut-inhibitor client read failed", Colors::RED); - return false; - } - - std::string ret = std::string{client.readBuf.data()}; - if (ret.find("started") == std::string::npos) { - NLog::log("{}Failed to start shortcut-inhibitor client, read {}", Colors::RED, ret); - return false; - } - - // wait for window to appear - int counter = 0; - while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { - counter++; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - if (counter > 50) { - NLog::log("{}shortcut-inhibitor client took too long to open", Colors::RED); - return false; - } - } - - if (!Tests::processAlive(client.proc->pid())) { - NLog::log("{}shortcut-inhibitor client not alive", Colors::RED); - return false; - } - - if (getFromSocket(std::format("/dispatch focuswindow pid:{}", client.proc->pid())) != "ok") { - NLog::log("{}Failed to focus shortcut-inhibitor client", Colors::RED, ret); - return false; - } - - std::string command = "on\n"; - if (write(client.writeFd.get(), command.c_str(), command.length()) == -1) { - NLog::log("{}shortcut-inhibitor client write failed", Colors::RED); - return false; - } - - client.readBuf.fill(0); - if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) - return false; - - ret = std::string{client.readBuf.data()}; - if (ret.find("inhibiting") == std::string::npos) { - NLog::log("{}shortcut-inhibitor client didn't return inhibiting", Colors::RED); - return false; - } - - NLog::log("{}Started shortcut-inhibitor client", Colors::YELLOW); - - return true; -} - -static void stopClient(SClient& client) { - std::string cmd = "off\n"; - write(client.writeFd.get(), cmd.c_str(), cmd.length()); - - kill(client.proc->pid(), SIGKILL); - client.proc.reset(); -} - -static std::string flagFile = "/tmp/hyprtester-keybinds.txt"; - -static bool checkFlag() { - bool exists = std::filesystem::exists(flagFile); - std::filesystem::remove(flagFile); - return exists; -} - -static bool attemptCheckFlag(int attempts, int intervalMs) { - for (int i = 0; i < attempts; i++) { - if (checkFlag()) - return true; - - std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs)); - } - - return false; -} - -static bool test() { - SClient client; - if (!startClient(client)) - return false; - - NLog::log("{}Testing keybinds", Colors::GREEN); - //basic keybind test - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bind SUPER,Y,exec,touch " + flagFile), "ok"); - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - EXPECT(attemptCheckFlag(20, 50), false); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); - - //keybind bypass flag test - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bindp SUPER,Y,exec,touch " + flagFile), "ok"); - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - EXPECT(attemptCheckFlag(20, 50), true); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); - - NLog::log("{}Testing gestures", Colors::GREEN); - //basic gesture test - OK(getFromSocket("/dispatch plugin:test:gesture right,3")); - EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "floating: 1"); - - //gesture bypass flag test - OK(getFromSocket("/dispatch plugin:test:gesture right,2")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "floating: 1"); - - stopClient(client); - - NLog::log("{}Reloading the config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_CLIENT_TEST_FN(test); diff --git a/hyprtester/src/tests/clients/tests.hpp b/hyprtester/src/tests/clients/tests.hpp deleted file mode 100644 index 31746fe5..00000000 --- a/hyprtester/src/tests/clients/tests.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include - -inline std::vector> clientTestFns; - -#define REGISTER_CLIENT_TEST_FN(fn) \ - static auto _register_fn = [] { \ - clientTestFns.emplace_back(fn); \ - return 1; \ - }(); diff --git a/hyprtester/src/tests/main/animations.cpp b/hyprtester/src/tests/main/animations.cpp deleted file mode 100644 index e464dcbd..00000000 --- a/hyprtester/src/tests/main/animations.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "../../Log.hpp" -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include - -static int ret = 0; - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -static bool test() { - NLog::log("{}Testing animations", Colors::GREEN); - - auto str = getFromSocket("/animations"); - NLog::log("{}Testing bezier curve output from `hyprctl animations`", Colors::YELLOW); - {EXPECT_CONTAINS(str, std::format("beziers:\n\n\tname: quick\n\t\tX0: 0.15\n\t\tY0: 0.00\n\t\tX1: 0.10\n\t\tY1: 1.00"))}; - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/colors.cpp b/hyprtester/src/tests/main/colors.cpp deleted file mode 100644 index deb4254c..00000000 --- a/hyprtester/src/tests/main/colors.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "../shared.hpp" - -static int ret = 0; - -static bool test() { - NLog::log("{}Testing hyprctl monitors", Colors::GREEN); - - std::string monitorsSpec = getFromSocket("j/monitors"); - EXPECT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "srgb")"); - - EXPECT_CONTAINS(getFromSocket("/keyword monitor HEADLESS-2,1920x1080x60.00000,0x0,1.0,bitdepth,10,cm,wide"), "ok") - monitorsSpec = getFromSocket("j/monitors"); - EXPECT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "wide")"); - - EXPECT_CONTAINS(getFromSocket("/keyword monitor HEADLESS-2,1920x1080x60.00000,0x0,1.0,bitdepth,10,cm,srgb,sdrbrightness,1.2,sdrsaturation,0.98"), "ok") - monitorsSpec = getFromSocket("j/monitors"); - EXPECT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "srgb")"); - EXPECT_CONTAINS(monitorsSpec, R"("sdrBrightness": 1.20)"); - EXPECT_CONTAINS(monitorsSpec, R"("sdrSaturation": 0.98)"); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/dwindle.cpp b/hyprtester/src/tests/main/dwindle.cpp deleted file mode 100644 index 234bfc33..00000000 --- a/hyprtester/src/tests/main/dwindle.cpp +++ /dev/null @@ -1,254 +0,0 @@ -#include "../shared.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "tests.hpp" - -static int ret = 0; - -static void testFloatClamp() { - for (auto const& win : {"a", "b", "c"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/keyword dwindle:force_split 2")); - OK(getFromSocket("/keyword monitor HEADLESS-2, addreserved, 0, 20, 0, 20")); - OK(getFromSocket("/dispatch focuswindow class:c")); - OK(getFromSocket("/dispatch setfloating class:c")); - OK(getFromSocket("/dispatch resizewindowpixel exact 1200 900,class:c")); - OK(getFromSocket("/dispatch settiled class:c")); - OK(getFromSocket("/dispatch setfloating class:c")); - - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 698,158"); - EXPECT_CONTAINS(str, "size: 1200,900"); - } - - OK(getFromSocket("/keyword dwindle:force_split 0")); - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - OK(getFromSocket("/reload")); -} - -static void test13349() { - - // Test if dwindle properly uses a focal point to place a new window. - // exposed by #13349 as a regression from #12890 - - for (auto const& win : {"a", "b", "c"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:c")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 967,547"); - EXPECT_CONTAINS(str, "size: 931,511"); - } - - OK(getFromSocket("/dispatch movewindow l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 497,22"); - EXPECT_CONTAINS(str, "size: 456,1036"); - } - - OK(getFromSocket("/dispatch movewindow r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 967,22"); - EXPECT_CONTAINS(str, "size: 456,1036"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testSplit() { - // Test various split methods - - Tests::spawnKitty("a"); - - // these must not crash - EXPECT_NOT(getFromSocket("/dispatch layoutmsg swapsplit"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio 1 exact"), "ok"); - - Tests::spawnKitty("b"); - - OK(getFromSocket("/dispatch focuswindow class:a")); - OK(getFromSocket("/dispatch layoutmsg splitratio -0.2")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 743,1036"); - } - - OK(getFromSocket("/dispatch layoutmsg splitratio 1.6 exact")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1495,1036"); - } - - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio fhne exact"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio exact"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio -....9"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio ..9"), "ok"); - EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio"), "ok"); - - OK(getFromSocket("/dispatch layoutmsg togglesplit")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,823"); - } - - OK(getFromSocket("/dispatch layoutmsg swapsplit")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,859"); - EXPECT_CONTAINS(str, "size: 1876,199"); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testRotatesplit() { - OK(getFromSocket("r/keyword general:gaps_in 0")); - OK(getFromSocket("r/keyword general:gaps_out 0")); - OK(getFromSocket("r/keyword general:border_size 0")); - - for (auto const& win : {"a", "b"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - // test 4 repeated rotations by 90 degrees - OK(getFromSocket("/dispatch layoutmsg rotatesplit")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 1920,540"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 960,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,540"); - EXPECT_CONTAINS(str, "size: 1920,540"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - // test different angles - OK(getFromSocket("/dispatch layoutmsg rotatesplit 180")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 960,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit 270")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,540"); - EXPECT_CONTAINS(str, "size: 1920,540"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit 360")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 1920,540"); - } - - // test negative angles - OK(getFromSocket("/dispatch layoutmsg rotatesplit -90")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 0,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - OK(getFromSocket("/dispatch layoutmsg rotatesplit -180")); - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "at: 960,0"); - EXPECT_CONTAINS(str, "size: 960,1080"); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - OK(getFromSocket("/reload")); -} - -static bool test() { - NLog::log("{}Testing Dwindle layout", Colors::GREEN); - - // test - NLog::log("{}Testing float clamp", Colors::GREEN); - testFloatClamp(); - - NLog::log("{}Testing #13349", Colors::GREEN); - test13349(); - - NLog::log("{}Testing splits", Colors::GREEN); - testSplit(); - - NLog::log("{}Testing rotatesplit", Colors::GREEN); - testRotatesplit(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - getFromSocket("/dispatch workspace 1"); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/exec.cpp b/hyprtester/src/tests/main/exec.cpp deleted file mode 100644 index a410494a..00000000 --- a/hyprtester/src/tests/main/exec.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#include -#include "../shared.hpp" - -static int ret = 0; - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define UP CUniquePointer -#define SP CSharedPointer - -const static auto SLEEP_DURATIONS = std::array{1, 10}; - -static bool test() { - NLog::log("{}Testing process spawning", Colors::GREEN); - - for (const auto duration : SLEEP_DURATIONS) { - // Note: POSIX sleep does not support fractional seconds, so - // can't sleep for less than 1 second. - OK(getFromSocket(std::format("/dispatch exec sleep {}", duration))); - - // Ensure that sleep is our child - const std::string sleepPidS = Tests::execAndGet("pgrep sleep"); - pid_t sleepPid; - try { - sleepPid = std::stoull(sleepPidS); - } catch (...) { - NLog::log("{}Sleep was not spawned or several sleeps are running: pgrep returned '{}'", Colors::RED, sleepPidS); - continue; - } - - const std::string sleepParentComm = Tests::execAndGet("cat \"/proc/$(ps -o ppid:1= -p " + sleepPidS + ")/comm\""); - NLog::log("{}Expecting that sleep's parent is Hyprland", Colors::YELLOW); - EXPECT_CONTAINS(sleepParentComm, "Hyprland"); - - std::this_thread::sleep_for(std::chrono::seconds(duration)); - - // Ensure that sleep did not become a zombie - EXPECT(Tests::processAlive(sleepPid), false); - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return !ret; - } - - return false; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/gestures.cpp b/hyprtester/src/tests/main/gestures.cpp deleted file mode 100644 index 07cc4ca9..00000000 --- a/hyprtester/src/tests/main/gestures.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#include -#include -#include -#include "../shared.hpp" - -static int ret = 0; - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define UP CUniquePointer -#define SP CSharedPointer - -static bool waitForWindowCount(int expectedWindowCnt, std::string_view expectation, int waitMillis = 100, int maxWaitCnt = 50) { - int counter = 0; - while (Tests::windowCount() != expectedWindowCnt) { - counter++; - std::this_thread::sleep_for(std::chrono::milliseconds(waitMillis)); - - if (counter > maxWaitCnt) { - NLog::log("{}Unmet expectation: {}", Colors::RED, expectation); - return false; - } - } - return true; -} - -static bool test() { - NLog::log("{}Testing gestures", Colors::GREEN); - - EXPECT(Tests::windowCount(), 0); - - // test on workspace "window" - NLog::log("{}Switching to workspace 1", Colors::YELLOW); - getFromSocket("/dispatch workspace 1"); // no OK: we might be on 1 already - - Tests::spawnKitty(); - EXPECT(Tests::windowCount(), 1); - - // Give the shell a moment to initialize - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - - OK(getFromSocket("/dispatch plugin:test:gesture up,5")); - OK(getFromSocket("/dispatch plugin:test:gesture down,5")); - OK(getFromSocket("/dispatch plugin:test:gesture left,5")); - OK(getFromSocket("/dispatch plugin:test:gesture right,5")); - OK(getFromSocket("/dispatch plugin:test:gesture right,4")); - - EXPECT(waitForWindowCount(0, "Gesture sent paste exit + enter to kitty"), true); - - EXPECT(Tests::windowCount(), 0); - - OK(getFromSocket("/dispatch plugin:test:gesture left,3")); - - EXPECT(waitForWindowCount(1, "Gesture spawned kitty"), true); - - EXPECT(Tests::windowCount(), 1); - - OK(getFromSocket("/dispatch plugin:test:gesture right,3")); - - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "floating: 1"); - } - - OK(getFromSocket("/dispatch plugin:test:gesture down,3")); - - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - } - - OK(getFromSocket("/dispatch plugin:test:gesture down,3")); - - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "fullscreen: 0"); - } - - OK(getFromSocket("/dispatch plugin:test:alt 1")); - - OK(getFromSocket("/dispatch plugin:test:gesture left,3")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_CONTAINS(str, "ID 2 (2)"); - } - - OK(getFromSocket("/dispatch plugin:test:gesture right,3")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_NOT_CONTAINS(str, "ID 2 (2)"); - } - - // check for crashes - OK(getFromSocket("/dispatch plugin:test:gesture right,3")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_NOT_CONTAINS(str, "ID 2 (2)"); - } - - OK(getFromSocket("/keyword gestures:workspace_swipe_invert 0")); - - OK(getFromSocket("/dispatch plugin:test:gesture right,3")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_CONTAINS(str, "ID 2 (2)"); - } - - OK(getFromSocket("/dispatch plugin:test:gesture left,3")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_NOT_CONTAINS(str, "ID 2 (2)"); - } - - OK(getFromSocket("/keyword gestures:workspace_swipe_invert 1")); - OK(getFromSocket("/keyword gestures:workspace_swipe_create_new 0")); - - OK(getFromSocket("/dispatch plugin:test:gesture left,3")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_NOT_CONTAINS(str, "ID 2 (2)"); - EXPECT_CONTAINS(str, "ID 1 (1)"); - } - - OK(getFromSocket("/dispatch plugin:test:gesture down,3")); - - { - auto str = getFromSocket("/clients"); - EXPECT_CONTAINS(str, "floating: 0"); - } - - OK(getFromSocket("/dispatch plugin:test:alt 0")); - - OK(getFromSocket("/dispatch plugin:test:gesture up,3")); - - EXPECT(waitForWindowCount(0, "Gesture closed kitty"), true); - - EXPECT(Tests::windowCount(), 0); - - // This test ensures that `movecursortocorner`, which expects - // a single-character direction argument, is parsed correctly. - Tests::spawnKitty(); - OK(getFromSocket("/dispatch movecursortocorner 0")); - const std::string cursorPos1 = getFromSocket("/cursorpos"); - OK(getFromSocket("/dispatch plugin:test:gesture left,4")); - const std::string cursorPos2 = getFromSocket("/cursorpos"); - // The cursor should have moved because of the gesture - EXPECT(cursorPos1 != cursorPos2, true); - - // Test that `workspace previous` works correctly after a workspace gesture. - { - OK(getFromSocket("/keyword gestures:workspace_swipe_invert 0")); - OK(getFromSocket("/keyword gestures:workspace_swipe_create_new 1")); - OK(getFromSocket("/dispatch workspace 3")); - - // Come to workspace 5 from workspace 3: 5 will remember that. - OK(getFromSocket("/dispatch workspace 5")); - Tests::spawnKitty(); // Keep workspace 5 open - - // Swipe from 1 to 5: 5 shall remember that. - OK(getFromSocket("/dispatch workspace 1")); - OK(getFromSocket("/dispatch plugin:test:alt 1")); - OK(getFromSocket("/dispatch plugin:test:gesture right,3")); - OK(getFromSocket("/dispatch plugin:test:alt 0")); - EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 5 (5)"); - - // Must return to 1 rather than 3 - OK(getFromSocket("/dispatch workspace previous")); - EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 1 (1)"); - - OK(getFromSocket("/dispatch workspace previous")); - EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 5 (5)"); - - OK(getFromSocket("/dispatch workspace 1")); - } - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - // reload cfg - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/groups.cpp b/hyprtester/src/tests/main/groups.cpp deleted file mode 100644 index 2f9c5062..00000000 --- a/hyprtester/src/tests/main/groups.cpp +++ /dev/null @@ -1,300 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#include -#include -#include -#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; - } - - // test movegroupwindow: focus should follow the moved window - NLog::log("{}Test movegroupwindow focus follows window", Colors::YELLOW); - try { - auto str = getFromSocket("/activewindow"); - auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - OK(getFromSocket("/dispatch movegroupwindow f")); - str = getFromSocket("/activewindow"); - auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - EXPECT(activeAfterMove, activeBeforeMove); - } catch (...) { - NLog::log("{}Fail at getting prop", Colors::RED); - ret = 1; - } - - // and backwards - NLog::log("{}Test movegroupwindow backwards", Colors::YELLOW); - try { - auto str = getFromSocket("/activewindow"); - auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - OK(getFromSocket("/dispatch movegroupwindow b")); - str = getFromSocket("/activewindow"); - auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - EXPECT(activeAfterMove, activeBeforeMove); - } 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); - - // test movewindoworgroup: direction should be respected when extracting from group - NLog::log("{}Test movewindoworgroup respects direction out of group", Colors::YELLOW); - OK(getFromSocket("/keyword group:groupbar:enabled 0")); - { - auto kittyE = Tests::spawnKitty(); - if (!kittyE) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - - // group kitty, and new windows should be auto-grouped - OK(getFromSocket("/dispatch togglegroup")); - - auto kittyF = Tests::spawnKitty(); - if (!kittyF) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - EXPECT(Tests::windowCount(), 2); - - // both windows should be grouped at the same position - { - auto str = getFromSocket("/clients"); - EXPECT_COUNT_STRING(str, "at: 22,22", 2); - } - - // move active window out of group to the right - NLog::log("{}Test movewindoworgroup r", Colors::YELLOW); - OK(getFromSocket("/dispatch movewindoworgroup r")); - - // the group should stay at x=22, the extracted window should be to the right - { - auto str = getFromSocket("/clients"); - EXPECT_COUNT_STRING(str, "at: 22,22", 1); - } - - // move it back into the group - OK(getFromSocket("/dispatch moveintogroup l")); - - // move active window out of group downward - NLog::log("{}Test movewindoworgroup d", Colors::YELLOW); - OK(getFromSocket("/dispatch movewindoworgroup d")); - - // the group should stay at y=22, the extracted window should be below - { - auto str = getFromSocket("/clients"); - EXPECT_COUNT_STRING(str, "at: 22,22", 1); - } - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - } - - // test that we deny a floated window getting auto-grouped into a tiled group. - NLog::log("{}Test that we deny a floated window getting auto-grouped into a tiled group.", Colors::GREEN); - - OK(getFromSocket("/keyword windowrule[kitty-tiled]:match:class kitty_tiled")); - OK(getFromSocket("/keyword windowrule[kitty-tiled]:tile yes")); - auto kittyProcE = Tests::spawnKitty("kitty_tiled"); - if (!kittyProcE) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - OK(getFromSocket("/dispatch togglegroup")); - - OK(getFromSocket("/keyword windowrule[kitty-floated]:match:class kitty_floated")); - OK(getFromSocket("/keyword windowrule[kitty-floated]:float yes")); - auto kittyProcF = Tests::spawnKitty("kitty_floated"); - if (!kittyProcF) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - - EXPECT(Tests::windowCount(), 2); - - { - auto clients = getFromSocket("/clients"); - auto classPos = clients.find("class: kitty_floated"); - if (classPos == std::string::npos) { - NLog::log("{}Could not find kitty_floated in clients output", Colors::RED); - ret = 1; - } else { - auto entryStart = clients.rfind("Window ", classPos); - auto entryEnd = clients.find("\n\n", classPos); - auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); - EXPECT_CONTAINS(windowEntry, "floating: 1"); - EXPECT_CONTAINS(windowEntry, "grouped: 0"); - } - } - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/hyprctl.cpp b/hyprtester/src/tests/main/hyprctl.cpp deleted file mode 100644 index e5e6f1fc..00000000 --- a/hyprtester/src/tests/main/hyprctl.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../shared.hpp" - -static int ret = 0; - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define UP CUniquePointer -#define SP CSharedPointer - -static std::string getCommandStdOut(std::string command) { - CProcess process("bash", {"-c", command}); - process.addEnv("HYPRLAND_INSTANCE_SIGNATURE", HIS); - process.runSync(); - - const std::string& stdOut = process.stdOut(); - - // Remove trailing new line - return stdOut.substr(0, stdOut.length() - 1); -} - -static bool testDevicesActiveLayoutIndex() { - NLog::log("{}Testing hyprctl devices active_layout_index", Colors::GREEN); - - // configure layouts - getFromSocket("/keyword input:kb_layout us,pl,ua"); - - for (uint8_t i = 0; i < 3; i++) { - // set layout - getFromSocket("/switchxkblayout all " + std::to_string(i)); - std::string devicesJson = getFromSocket("j/devices"); - std::string expected = R"("active_layout_index": )" + std::to_string(i); - // check layout index - EXPECT_CONTAINS(devicesJson, expected); - } - - return true; -} - -static bool testGetprop() { - NLog::log("{}Testing hyprctl getprop", Colors::GREEN); - if (!Tests::spawnKitty()) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - - // animation - EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "(unset)"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": ""})"); - getFromSocket("/dispatch setprop class:kitty animation teststyle"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "teststyle"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": "teststyle"})"); - - // max_size - EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "inf inf"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [null,null]})"); - getFromSocket("/dispatch setprop class:kitty max_size 200 150"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "200 150"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [200,150]})"); - - // min_size - EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "20 20"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [20,20]})"); - getFromSocket("/dispatch setprop class:kitty min_size 100 50"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "100 50"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [100,50]})"); - - // expr-based min/max _size - getFromSocket("/dispatch setfloating class:kitty"); // need to set floating for tests below - getFromSocket("/dispatch setprop class:kitty max_size 90+10 25*2"); // set max to the same as min above, forcing window to 100*50 - EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "100 50"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [100,50]})"); - getFromSocket("/dispatch setprop class:kitty min_size window_w*0.5 window_h-10"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "50 40"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [50,40]})"); - getFromSocket("/dispatch settiled class:kitty"); // go back to tiled for consistency - - // opacity - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "1"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 1})"); - getFromSocket("/dispatch setprop class:kitty opacity 0.3"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "0.3"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 0.3})"); - - // opacity_inactive - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "1"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 1})"); - getFromSocket("/dispatch setprop class:kitty opacity_inactive 0.5"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "0.5"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 0.5})"); - - // opacity_fullscreen - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "1"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 1})"); - getFromSocket("/dispatch setprop class:kitty opacity_fullscreen 0.75"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "0.75"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 0.75})"); - - // opacity_override - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "false"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": false})"); - getFromSocket("/dispatch setprop class:kitty opacity_override true"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "true"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": true})"); - - // opacity_inactive_override - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "false"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": false})"); - getFromSocket("/dispatch setprop class:kitty opacity_inactive_override true"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "true"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": true})"); - - // opacity_fullscreen_override - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "false"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": false})"); - getFromSocket("/dispatch setprop class:kitty opacity_fullscreen_override true"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "true"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": true})"); - - // active_border_color - EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ee33ccff ee00ff99 45deg"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ee33ccff ee00ff99 45deg"})"); - getFromSocket("/dispatch setprop class:kitty active_border_color rgb(abcdef)"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ffabcdef 0deg"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ffabcdef 0deg"})"); - - // bool window properties - EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "false"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": false})"); - getFromSocket("/dispatch setprop class:kitty allows_input true"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "true"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": true})"); - - // int window properties - EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "10"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 10})"); - getFromSocket("/dispatch setprop class:kitty rounding 4"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "4"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 4})"); - - // float window properties - EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "2"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 2})"); - getFromSocket("/dispatch setprop class:kitty rounding_power 1.25"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "1.25"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 1.25})"); - - // errors - EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args"); - EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animation"), "window not found"); - EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found"); - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; -} - -static bool test() { - NLog::log("{}Testing hyprctl", Colors::GREEN); - - { - NLog::log("{}Testing hyprctl descriptions for any json errors", Colors::GREEN); - CProcess jqProc("bash", {"-c", "hyprctl descriptions | jq"}); - jqProc.addEnv("HYPRLAND_INSTANCE_SIGNATURE", HIS); - jqProc.runSync(); - EXPECT(jqProc.exitCode(), 0); - } - - testGetprop(); - testDevicesActiveLayoutIndex(); - getFromSocket("/reload"); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/keybinds.cpp b/hyprtester/src/tests/main/keybinds.cpp deleted file mode 100644 index a87a8b07..00000000 --- a/hyprtester/src/tests/main/keybinds.cpp +++ /dev/null @@ -1,545 +0,0 @@ -#include -#include -#include -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "../shared.hpp" -#include "tests.hpp" - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -static int ret = 0; -static std::string flagFile = "/tmp/hyprtester-keybinds.txt"; - -// Because i don't feel like changing someone elses code. -enum eKeyboardModifierIndex : uint8_t { - MOD_SHIFT = 1, - MOD_CAPS, - MOD_CTRL, - MOD_ALT, - MOD_MOD2, - MOD_MOD3, - MOD_META, - MOD_MOD5 -}; - -static void clearFlag() { - std::filesystem::remove(flagFile); -} - -static bool checkFlag() { - bool exists = std::filesystem::exists(flagFile); - clearFlag(); - return exists; -} - -static bool attemptCheckFlag(int attempts, int intervalMs) { - for (int i = 0; i < attempts; i++) { - if (checkFlag()) - return true; - - std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs)); - } - - return false; -} - -static std::string readKittyOutput() { - std::string output = Tests::execAndGet("kitten @ --to unix:/tmp/hyprtester-kitty.sock get-text --extent all"); - // chop off shell prompt - std::size_t pos = output.rfind("$"); - if (pos != std::string::npos) { - pos += 1; - if (pos < output.size()) - output.erase(0, pos); - } - // NLog::log("Kitty output: '{}'", output); - return output; -} - -static void awaitKittyPrompt() { - // wait until we see the shell prompt, meaning it's ready for test inputs - for (int i = 0; i < 10; i++) { - std::string output = Tests::execAndGet("kitten @ --to unix:/tmp/hyprtester-kitty.sock get-text --extent all"); - if (output.rfind("$") == std::string::npos) { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - continue; - } - return; - } - NLog::log("{}Error: timed out waiting for kitty prompt", Colors::RED); -} - -static CUniquePointer spawnRemoteControlKitty() { - auto kittyProc = Tests::spawnKitty("keybinds_test", {"-o", "allow_remote_control=yes", "--listen-on", "unix:/tmp/hyprtester-kitty.sock", "--config", "NONE", "/bin/sh"}); - // wait a bit to ensure shell prompt is sent, we are going to read the text after it - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - if (kittyProc) - awaitKittyPrompt(); - return kittyProc; -} - -static void testBind() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bind SUPER,Y,exec,touch " + flagFile), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // await flag - EXPECT(attemptCheckFlag(20, 50), true); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); -} - -static void testBindKey() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bind ,Y,exec,touch " + flagFile), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29")); - // await flag - EXPECT(attemptCheckFlag(20, 50), true); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind ,Y"), "ok"); -} - -static void testLongPress() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // check no flag on short press - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - EXPECT(checkFlag(), false); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), true); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); -} - -static void testKeyLongPress() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bindo ,Y,exec,touch " + flagFile), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29")); - // check no flag on short press - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - EXPECT(checkFlag(), false); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), true); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind ,Y"), "ok"); -} - -static void testLongPressRelease() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // check no flag on short press - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - EXPECT(checkFlag(), false); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); -} - -static void testLongPressOnlyKeyRelease() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // check no flag on short press - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - EXPECT(checkFlag(), false); - // release key, keep modifier - OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), false); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); -} - -static void testRepeat() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // await flag - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), true); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), true); - // check that it continues repeating - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), true); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); -} - -static void testKeyRepeat() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword binde ,Y,exec,touch " + flagFile), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29")); - // await flag - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - EXPECT(checkFlag(), true); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), true); - // check that it continues repeating - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), true); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind ,Y"), "ok"); -} - -static void testRepeatRelease() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // await flag - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - EXPECT(checkFlag(), true); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - clearFlag(); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), false); - // check that it is not repeating - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); -} - -static void testRepeatOnlyKeyRelease() { - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // await flag - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), true); - // release key, keep modifier - OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - clearFlag(); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), false); - // check that it is not repeating - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - EXPECT(checkFlag(), false); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); -} - -static void testShortcutBind() { - auto kittyProc = spawnRemoteControlKitty(); - if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; - } - EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); - EXPECT(getFromSocket("/keyword bind SUPER,Y,sendshortcut,,q,"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // release keybind - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - const std::string output = readKittyOutput(); - EXPECT_COUNT_STRING(output, "y", 0); - EXPECT_COUNT_STRING(output, "q", 1); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); - Tests::killAllWindows(); -} - -static void testShortcutBindKey() { - auto kittyProc = spawnRemoteControlKitty(); - if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; - } - EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); - EXPECT(getFromSocket("/keyword bind ,Y,sendshortcut,,q,"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29")); - // release keybind - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - const std::string output = readKittyOutput(); - EXPECT_COUNT_STRING(output, "y", 0); - // disabled: doesn't work in CI - // EXPECT_COUNT_STRING(output, "q", 1); - EXPECT(getFromSocket("/keyword unbind ,Y"), "ok"); - Tests::killAllWindows(); -} - -static void testShortcutLongPress() { - auto kittyProc = spawnRemoteControlKitty(); - if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; - } - EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); - EXPECT(getFromSocket("/keyword bindo SUPER,Y,sendshortcut,,q,"), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_rate 10"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - const std::string output = readKittyOutput(); - int yCount = Tests::countOccurrences(output, "y"); - // sometimes 1, sometimes 2, not sure why - // keybind press sends 1 y immediately - // then repeat triggers, sending 1 y - // final release stop repeats, and shouldn't send any more - EXPECT(true, yCount == 1 || yCount == 2); - EXPECT_COUNT_STRING(output, "q", 1); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); - Tests::killAllWindows(); -} - -static void testShortcutLongPressKeyRelease() { - auto kittyProc = spawnRemoteControlKitty(); - if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; - } - EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); - EXPECT(getFromSocket("/keyword bindo SUPER,Y,sendshortcut,,q,"), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_rate 10"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - // release key, keep modifier - OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); - // await repeat delay - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - const std::string output = readKittyOutput(); - // disabled: doesn't work on CI - // EXPECT_COUNT_STRING(output, "y", 1); - EXPECT_COUNT_STRING(output, "q", 0); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); - Tests::killAllWindows(); -} - -static void testShortcutRepeat() { - auto kittyProc = spawnRemoteControlKitty(); - if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; - } - EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); - EXPECT(getFromSocket("/keyword binde SUPER,Y,sendshortcut,,q,"), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_rate 5"), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 200"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - // await repeat - std::this_thread::sleep_for(std::chrono::milliseconds(210)); - // release keybind - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - std::this_thread::sleep_for(std::chrono::milliseconds(450)); - const std::string output = readKittyOutput(); - EXPECT_COUNT_STRING(output, "y", 0); - int qCount = Tests::countOccurrences(output, "q"); - // sometimes 2, sometimes 3, not sure why - // keybind press sends 1 q immediately - // then repeat triggers, sending 1 q - // final release stop repeats, and shouldn't send any more - EXPECT(true, qCount == 2 || qCount == 3); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); - Tests::killAllWindows(); -} - -static void testShortcutRepeatKeyRelease() { - auto kittyProc = spawnRemoteControlKitty(); - if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; - } - EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); - EXPECT(getFromSocket("/keyword binde SUPER,Y,sendshortcut,,q,"), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_rate 5"), "ok"); - EXPECT(getFromSocket("/keyword input:repeat_delay 200"), "ok"); - // press keybind - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - std::this_thread::sleep_for(std::chrono::milliseconds(210)); - // release key, keep modifier - OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); - // if repeat was still active, we'd get 2 more q's here - std::this_thread::sleep_for(std::chrono::milliseconds(450)); - // release modifier - const std::string output = readKittyOutput(); - EXPECT_COUNT_STRING(output, "y", 0); - int qCount = Tests::countOccurrences(output, "q"); - // sometimes 2, sometimes 3, not sure why - // keybind press sends 1 q immediately - // then repeat triggers, sending 1 q - // final release stop repeats, and shouldn't send any more - EXPECT(true, qCount == 2 || qCount == 3); - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); - Tests::killAllWindows(); -} - -static void testSubmap() { - const auto press = [](const uint32_t key, const uint32_t mod = 0) { - // +8 because udev -> XKB keycode. - getFromSocket("/dispatch plugin:test:keybind 1," + std::to_string(mod) + "," + std::to_string(key + 8)); - getFromSocket("/dispatch plugin:test:keybind 0," + std::to_string(mod) + "," + std::to_string(key + 8)); - }; - - NLog::log("{}Testing submaps", Colors::GREEN); - // submap 1 no resets - press(KEY_U, MOD_META); - EXPECT_CONTAINS(getFromSocket("/submap"), "submap1"); - press(KEY_O); - Tests::waitUntilWindowsN(1); - EXPECT_CONTAINS(getFromSocket("/submap"), "submap1"); - // submap 2 resets to submap 1 - press(KEY_U); - EXPECT_CONTAINS(getFromSocket("/submap"), "submap2"); - press(KEY_O); - Tests::waitUntilWindowsN(2); - EXPECT_CONTAINS(getFromSocket("/submap"), "submap1"); - // submap 3 resets to default - press(KEY_I); - EXPECT_CONTAINS(getFromSocket("/submap"), "submap3"); - press(KEY_O); - Tests::waitUntilWindowsN(3); - EXPECT_CONTAINS(getFromSocket("/submap"), "default"); - // submap 1 reset via keybind - press(KEY_U, MOD_META); - EXPECT_CONTAINS(getFromSocket("/submap"), "submap1"); - press(KEY_P); - EXPECT_CONTAINS(getFromSocket("/submap"), "default"); - - Tests::killAllWindows(); -} - -static void testBindsAfterScroll() { - NLog::log("{}Testing binds after scroll", Colors::GREEN); - - clearFlag(); - OK(getFromSocket("/keyword binds Alt_R,w,exec,touch " + flagFile)); - - // press keybind before scroll - OK(getFromSocket("/dispatch plugin:test:keybind 1,0,108")); // Alt_R press - OK(getFromSocket("/dispatch plugin:test:keybind 1,4,25")); // w press - EXPECT(attemptCheckFlag(20, 50), true); - OK(getFromSocket("/dispatch plugin:test:keybind 0,4,25")); // w release - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,108")); // Alt_R release - - // scroll - OK(getFromSocket("/dispatch plugin:test:scroll 120")); - OK(getFromSocket("/dispatch plugin:test:scroll -120")); - OK(getFromSocket("/dispatch plugin:test:scroll 120")); - - // press keybind after scroll - OK(getFromSocket("/dispatch plugin:test:keybind 1,0,108")); // Alt_R press - OK(getFromSocket("/dispatch plugin:test:keybind 1,4,25")); // w press - EXPECT(attemptCheckFlag(20, 50), true); - OK(getFromSocket("/dispatch plugin:test:keybind 0,4,25")); // w release - OK(getFromSocket("/dispatch plugin:test:keybind 0,0,108")); // Alt_R release - - clearFlag(); - OK(getFromSocket("/keyword unbind Alt_R,w")); -} - -static void testSubmapUniversal() { - NLog::log("{}Testing submap universal", Colors::GREEN); - - EXPECT(checkFlag(), false); - EXPECT(getFromSocket("/keyword bindu SUPER,Y,exec,touch " + flagFile), "ok"); - EXPECT_CONTAINS(getFromSocket("/submap"), "default"); - - // keybind works on default submap - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); - EXPECT(attemptCheckFlag(30, 5), true); - - // keybind works on submap1 - getFromSocket("/dispatch plugin:test:keybind 1,7,30"); - getFromSocket("/dispatch plugin:test:keybind 0,7,30"); - EXPECT_CONTAINS(getFromSocket("/submap"), "submap1"); - OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); - OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); - EXPECT(attemptCheckFlag(30, 5), true); - - // reset to default submap - getFromSocket("/dispatch plugin:test:keybind 1,0,33"); - getFromSocket("/dispatch plugin:test:keybind 0,0,33"); - EXPECT_CONTAINS(getFromSocket("/submap"), "default"); - - EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); -} - -static bool test() { - NLog::log("{}Testing keybinds", Colors::GREEN); - - clearFlag(); - - testBind(); - testBindKey(); - testLongPress(); - testKeyLongPress(); - testLongPressRelease(); - testLongPressOnlyKeyRelease(); - testRepeat(); - testKeyRepeat(); - testRepeatRelease(); - testRepeatOnlyKeyRelease(); - testShortcutBind(); - testShortcutBindKey(); - testShortcutLongPress(); - testShortcutLongPressKeyRelease(); - testShortcutRepeat(); - testShortcutRepeatKeyRelease(); - testSubmap(); - testSubmapUniversal(); - testBindsAfterScroll(); - - clearFlag(); - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/layer.cpp b/hyprtester/src/tests/main/layer.cpp deleted file mode 100644 index 73e30ba6..00000000 --- a/hyprtester/src/tests/main/layer.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "../../Log.hpp" -#include "../shared.hpp" -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include - -static int ret = 0; - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -static bool spawnLayer(const std::string& namespace_) { - NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, namespace_); - if (!Tests::spawnLayerKitty(namespace_)) { - NLog::log("{}Error: {} layer did not spawn", Colors::RED, namespace_); - return false; - } - return true; -} - -static bool test() { - NLog::log("{}Testing plugin layerrules", Colors::GREEN); - - if (!spawnLayer("rule-layer")) - return false; - - OK(getFromSocket("/dispatch plugin:test:add_layer_rule")); - OK(getFromSocket("/reload")); - - OK(getFromSocket("/keyword layerrule match:namespace rule-layer, plugin_rule effect")); - - if (!spawnLayer("rule-layer")) - return false; - - if (!spawnLayer("norule-layer")) - return false; - - OK(getFromSocket("/dispatch plugin:test:check_layer_rule")); - - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all layers", Colors::YELLOW); - Tests::killAllLayers(); - - NLog::log("{}Expecting 0 layers", Colors::YELLOW); - EXPECT(Tests::layerCount(), 0); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/layout.cpp b/hyprtester/src/tests/main/layout.cpp deleted file mode 100644 index 186d7034..00000000 --- a/hyprtester/src/tests/main/layout.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "../shared.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "tests.hpp" - -static int ret = 0; - -static void swar() { - OK(getFromSocket("/keyword layout:single_window_aspect_ratio 1 1")); - - Tests::spawnKitty(); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 442,22"); - EXPECT_CONTAINS(str, "size: 1036,1036"); - } - - Tests::spawnKitty(); - - OK(getFromSocket("/dispatch killwindow activewindow")); - - Tests::waitUntilWindowsN(1); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 442,22"); - EXPECT_CONTAINS(str, "size: 1036,1036"); - } - - // don't use swar on maximized - OK(getFromSocket("/dispatch fullscreen 1")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,1036"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -// Don't crash when focus after global geometry changes -static void testCrashOnGeomUpdate() { - Tests::spawnKitty(); - Tests::spawnKitty(); - Tests::spawnKitty(); - - // move the layout - OK(getFromSocket("/keyword monitor HEADLESS-2,1920x1080@60,1000x0,1")); - - // shouldnt crash - OK(getFromSocket("/dispatch movefocus r")); - - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -// Test if size + pos is preserved after fs cycle -static void testPosPreserve() { - Tests::spawnKitty(); - - OK(getFromSocket("/dispatch setfloating class:kitty")); - OK(getFromSocket("/dispatch resizewindowpixel exact 1337 69, class:kitty")); - OK(getFromSocket("/dispatch movewindowpixel exact 420 420, class:kitty")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 420,420"); - EXPECT_CONTAINS(str, "size: 1337,69"); - } - - OK(getFromSocket("/dispatch fullscreen")); - OK(getFromSocket("/dispatch fullscreen")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "size: 1337,69"); - } - - OK(getFromSocket("/dispatch movewindow r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 581,420"); - EXPECT_CONTAINS(str, "size: 1337,69"); - } - - OK(getFromSocket("/dispatch fullscreen")); - OK(getFromSocket("/dispatch fullscreen")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 581,420"); - EXPECT_CONTAINS(str, "size: 1337,69"); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static bool test() { - NLog::log("{}Testing layout generic", Colors::GREEN); - - // setup - OK(getFromSocket("/dispatch workspace 10")); - - // test - NLog::log("{}Testing `single_window_aspect_ratio`", Colors::GREEN); - swar(); - - testCrashOnGeomUpdate(); - testPosPreserve(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - OK(getFromSocket("/dispatch workspace 1")); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/master.cpp b/hyprtester/src/tests/main/master.cpp deleted file mode 100644 index 9aaa4cf0..00000000 --- a/hyprtester/src/tests/main/master.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "../shared.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "tests.hpp" - -static int ret = 0; - -// reqs 1 master 3 slaves -static void testOrientations() { - OK(getFromSocket("/keyword master:orientation top")); - - // top - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876"); - } - - // cycle = top, right, bottom, center, left - - // right - OK(getFromSocket("/dispatch layoutmsg orientationnext")); - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 873,22"); - EXPECT_CONTAINS(str, "size: 1025,1036"); - } - - // bottom - OK(getFromSocket("/dispatch layoutmsg orientationnext")); - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,495"); - EXPECT_CONTAINS(str, "size: 1876"); - } - - // center - OK(getFromSocket("/dispatch layoutmsg orientationnext")); - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 450,22"); - EXPECT_CONTAINS(str, "size: 1020,1036"); - } - - // left - OK(getFromSocket("/dispatch layoutmsg orientationnext")); - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1025,1036"); - } -} - -static void focusMasterPrevious() { - // setup - NLog::log("{}Spawning 1 master and 3 slave windows", Colors::YELLOW); - // order of windows set according to new_status = master (set in test.conf) - for (auto const& win : {"slave1", "slave2", "slave3", "master"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - NLog::log("{}Ensuring focus is on master before testing", Colors::YELLOW); - OK(getFromSocket("/dispatch layoutmsg focusmaster master")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master"); - - // test - NLog::log("{}Testing fallback to focusmaster auto", Colors::YELLOW); - - OK(getFromSocket("/dispatch layoutmsg focusmaster previous")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave1"); - - NLog::log("{}Testing focusing from slave to master", Colors::YELLOW); - - OK(getFromSocket("/dispatch layoutmsg cyclenext noloop")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave2"); - OK(getFromSocket("/dispatch layoutmsg focusmaster previous")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master"); - - NLog::log("{}Testing focusing on previous window", Colors::YELLOW); - - OK(getFromSocket("/dispatch layoutmsg focusmaster previous")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave2"); - - NLog::log("{}Testing focusing back to master", Colors::YELLOW); - - OK(getFromSocket("/dispatch layoutmsg focusmaster previous")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master"); - - testOrientations(); - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testFsBehavior() { - // Master will re-send data to fullscreen / maximized windows, which can interfere with misc:on_focus_under_fullscreen - // check that it doesn't. - - for (auto const& win : {"master", "slave1", "slave2"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:master")); - OK(getFromSocket("/dispatch fullscreen 1")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,1036"); - EXPECT_CONTAINS(str, "class: master"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1")); - - Tests::spawnKitty("new_master"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,1036"); - EXPECT_CONTAINS(str, "class: new_master"); - EXPECT_CONTAINS(str, "fullscreen: 1"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0")); - - Tests::spawnKitty("ignored"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "at: 22,22"); - EXPECT_CONTAINS(str, "size: 1876,1036"); - EXPECT_CONTAINS(str, "class: new_master"); - EXPECT_CONTAINS(str, "fullscreen: 1"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2")); - - Tests::spawnKitty("vaxwashere"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: vaxwashere"); - EXPECT_CONTAINS(str, "fullscreen: 0"); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static bool test() { - NLog::log("{}Testing Master layout", Colors::GREEN); - - // setup - OK(getFromSocket("/dispatch workspace name:master")); - OK(getFromSocket("/keyword general:layout master")); - - // test - NLog::log("{}Testing `focusmaster previous` layoutmsg", Colors::GREEN); - focusMasterPrevious(); - - NLog::log("{}Testing fs behavior", Colors::GREEN); - testFsBehavior(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - OK(getFromSocket("/dispatch workspace 1")); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/misc.cpp b/hyprtester/src/tests/main/misc.cpp deleted file mode 100644 index 471eef7a..00000000 --- a/hyprtester/src/tests/main/misc.cpp +++ /dev/null @@ -1,336 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#include -#include -#include -#include "../shared.hpp" - -static int ret = 0; - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -#define UP CUniquePointer -#define SP CSharedPointer - -// Uncomment once test vm can run hyprland-dialog -// static void testAnrDialogs() { -// NLog::log("{}Testing ANR dialogs", Colors::YELLOW); -// -// OK(getFromSocket("/keyword misc:enable_anr_dialog true")); -// OK(getFromSocket("/keyword misc:anr_missed_pings 1")); -// -// NLog::log("{}ANR dialog: regular workspaces", Colors::YELLOW); -// { -// OK(getFromSocket("/dispatch workspace 2")); -// -// auto kitty = Tests::spawnKitty("bad_kitty"); -// -// if (!kitty) { -// ret = 1; -// return; -// } -// -// { -// auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "workspace: 2"); -// } -// -// OK(getFromSocket("/dispatch workspace 1")); -// -// ::kill(kitty->pid(), SIGSTOP); -// Tests::waitUntilWindowsN(2); -// -// { -// auto str = getFromSocket("/activeworkspace"); -// EXPECT_CONTAINS(str, "windows: 0"); -// } -// -// { -// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog")) -// auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "workspace: 2"); -// } -// } -// -// Tests::killAllWindows(); -// -// NLog::log("{}ANR dialog: named workspaces", Colors::YELLOW); -// { -// OK(getFromSocket("/dispatch workspace name:yummy")); -// -// auto kitty = Tests::spawnKitty("bad_kitty"); -// -// if (!kitty) { -// ret = 1; -// return; -// } -// -// { -// auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "yummy"); -// } -// -// OK(getFromSocket("/dispatch workspace 1")); -// -// ::kill(kitty->pid(), SIGSTOP); -// Tests::waitUntilWindowsN(2); -// -// { -// auto str = getFromSocket("/activeworkspace"); -// EXPECT_CONTAINS(str, "windows: 0"); -// } -// -// { -// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog")) -// auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "yummy"); -// } -// } -// -// Tests::killAllWindows(); -// -// NLog::log("{}ANR dialog: special workspaces", Colors::YELLOW); -// { -// OK(getFromSocket("/dispatch workspace special:apple")); -// -// auto kitty = Tests::spawnKitty("bad_kitty"); -// -// if (!kitty) { -// ret = 1; -// return; -// } -// -// { -// auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "special:apple"); -// } -// -// OK(getFromSocket("/dispatch togglespecialworkspace apple")); -// OK(getFromSocket("/dispatch workspace 1")); -// -// ::kill(kitty->pid(), SIGSTOP); -// Tests::waitUntilWindowsN(2); -// -// { -// auto str = getFromSocket("/activeworkspace"); -// EXPECT_CONTAINS(str, "windows: 0"); -// } -// -// { -// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog")) -// auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "special:apple"); -// } -// } -// -// OK(getFromSocket("/reload")); -// Tests::killAllWindows(); -// } - -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:on_focus_under_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("/dispatch focuswindow class:kitty_B")); - - { - // should be ignored as per focus_under_fullscreen 0 - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - EXPECT_CONTAINS(str, "kitty_A"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1")); - - Tests::spawnKitty("kitty_C"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - EXPECT_CONTAINS(str, "kitty_C"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2")); - - Tests::spawnKitty("kitty_D"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 0"); - EXPECT_CONTAINS(str, "kitty_D"); - } - - OK(getFromSocket("/keyword misc:on_focus_under_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"); - } - - Tests::killAllWindows(); - - NLog::log("{}Testing fullscreen and fullscreenstate dispatcher", Colors::YELLOW); - - Tests::spawnKitty("kitty_A"); - Tests::spawnKitty("kitty_B"); - - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - OK(getFromSocket("/dispatch fullscreen 0 set")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - } - - OK(getFromSocket("/dispatch fullscreen 0 unset")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 0"); - } - - OK(getFromSocket("/dispatch fullscreen 1 toggle")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 1"); - } - - OK(getFromSocket("/dispatch fullscreen 1 toggle")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 0"); - } - - OK(getFromSocket("/dispatch fullscreenstate 2 2 set")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - } - - OK(getFromSocket("/dispatch fullscreenstate 2 2 set")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - } - - OK(getFromSocket("/dispatch fullscreenstate 2 2 toggle")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 0"); - } - - OK(getFromSocket("/dispatch fullscreenstate 2 2 toggle")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - } - - // Ensure that the process autostarted in the config does not - // become a zombie even if it terminates very quickly. - EXPECT(Tests::execAndGet("pgrep -f 'sleep 0'").empty(), true); - - // 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); diff --git a/hyprtester/src/tests/main/persistent.cpp b/hyprtester/src/tests/main/persistent.cpp deleted file mode 100644 index 5e2fdc5a..00000000 --- a/hyprtester/src/tests/main/persistent.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#include -#include -#include -#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 persistent workspaces", Colors::GREEN); - - EXPECT(Tests::windowCount(), 0); - - // test on workspace "window" - NLog::log("{}Switching to workspace 1", Colors::YELLOW); - getFromSocket("/dispatch workspace 1"); // no OK: we might be on 1 already - - OK(getFromSocket("/keyword workspace 5, monitor:HEADLESS-2, persistent:1")); - OK(getFromSocket("/keyword workspace 6, monitor:HEADLESS-PERSISTENT-TEST, persistent:1")); - OK(getFromSocket("/keyword workspace name:PERSIST, monitor:HEADLESS-PERSISTENT-TEST, persistent:1")); - OK(getFromSocket("/keyword workspace name:PERSIST-2, monitor:HEADLESS-PERSISTENT-TEST, persistent:1")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_CONTAINS(str, "ID 5 (5)"); - EXPECT_COUNT_STRING(str, "workspace ID ", 2); - } - - OK(getFromSocket("/output create headless HEADLESS-PERSISTENT-TEST")); - - { - auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "HEADLESS-PERSISTENT-TEST"); - } - - OK(getFromSocket("/dispatch focusmonitor HEADLESS-PERSISTENT-TEST")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_CONTAINS(str, "ID 2 (2)"); // this should be automatically generated by hl - EXPECT_CONTAINS(str, "ID 5 (5)"); - EXPECT_CONTAINS(str, "ID 6 (6)"); - EXPECT_CONTAINS(str, "(PERSIST) on monitor"); - EXPECT_CONTAINS(str, "(PERSIST-2) on monitor"); - EXPECT_COUNT_STRING(str, "workspace ID ", 6); - } - - OK(getFromSocket("/reload")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_NOT_CONTAINS(str, "ID 5 (5)"); - EXPECT_NOT_CONTAINS(str, "ID 6 (6)"); - EXPECT_NOT_CONTAINS(str, "(PERSIST) on monitor"); - EXPECT_COUNT_STRING(str, "workspace ID ", 2); - } - - OK(getFromSocket("/output remove HEADLESS-PERSISTENT-TEST")); - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - // reload cfg - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp deleted file mode 100644 index 8bb950dd..00000000 --- a/hyprtester/src/tests/main/scroll.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include "../shared.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "tests.hpp" - -static int ret = 0; - -static void testFocusCycling() { - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); - } - - OK(getFromSocket("/dispatch movewindow l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); - } - - OK(getFromSocket("/dispatch movefocus u")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testFocusWrapping() { - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - // set wrap_focus to true - OK(getFromSocket("/keyword scrolling:wrap_focus true")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch layoutmsg focus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); - } - - OK(getFromSocket("/dispatch layoutmsg focus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: a"); - } - - // set wrap_focus to false - OK(getFromSocket("/keyword scrolling:wrap_focus false")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch layoutmsg focus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: a"); - } - - OK(getFromSocket("/dispatch focuswindow class:d")); - - OK(getFromSocket("/dispatch layoutmsg focus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testSwapcolWrapping() { - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - // set wrap_swapcol to true - OK(getFromSocket("/keyword scrolling:wrap_swapcol true")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch layoutmsg swapcol l")); - OK(getFromSocket("/dispatch layoutmsg focus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:d")); - OK(getFromSocket("/dispatch layoutmsg swapcol r")); - OK(getFromSocket("/dispatch layoutmsg focus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - for (auto const& win : {"a", "b", "c", "d"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - // set wrap_swapcol to false - OK(getFromSocket("/keyword scrolling:wrap_swapcol false")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - - OK(getFromSocket("/dispatch layoutmsg swapcol l")); - OK(getFromSocket("/dispatch layoutmsg focus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - OK(getFromSocket("/dispatch focuswindow class:d")); - - OK(getFromSocket("/dispatch layoutmsg swapcol r")); - OK(getFromSocket("/dispatch layoutmsg focus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static bool test() { - NLog::log("{}Testing Scroll layout", Colors::GREEN); - - // setup - OK(getFromSocket("/dispatch workspace name:scroll")); - OK(getFromSocket("/keyword general:layout scrolling")); - - // test - NLog::log("{}Testing focus cycling", Colors::GREEN); - testFocusCycling(); - - // test - NLog::log("{}Testing focus wrap", Colors::GREEN); - testFocusWrapping(); - - // test - NLog::log("{}Testing swapcol wrap", Colors::GREEN); - testSwapcolWrapping(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - OK(getFromSocket("/dispatch workspace 1")); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/snap.cpp b/hyprtester/src/tests/main/snap.cpp deleted file mode 100644 index 9c25de11..00000000 --- a/hyprtester/src/tests/main/snap.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include -#include -#include - -#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 testWindowSnap(const bool RESPECTGAPS) { - const int BORDERSIZE = 2; - const int WINDOWSIZE = 100; - - const int OTHER = 500; - const int WINDOWGAP = 8; - const int GAPSIN = 5; - const int GAP = (RESPECTGAPS ? 2 * GAPSIN : 0) + (2 * BORDERSIZE); - const int END = GAP + WINDOWSIZE; - - int 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})); -} - -static void testMonitorSnap(const bool RESPECTGAPS, const bool OVERLAP) { - const int BORDERSIZE = 2; - const int WINDOWSIZE = 100; - - const int MONITORGAP = 10; - const int GAPSOUT = 20; - const int RESP = (RESPECTGAPS ? GAPSOUT : 0); - const int GAP = RESP + (OVERLAP ? 0 : BORDERSIZE); - const int END = GAP + WINDOWSIZE; - - int 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 int RESERVED = 200; - const int RGAP = RESERVED + RESP + BORDERSIZE; - const int 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(""); - testWindowSnap(false); - testMonitorSnap(false, false); - - NLog::log("\n{}Turning on respect_gaps", Colors::YELLOW); - OK(getFromSocket("/keyword general:snap:respect_gaps true")); - testWindowSnap(true); - testMonitorSnap(true, false); - - NLog::log("\n{}Turning on border_overlap", Colors::YELLOW); - OK(getFromSocket("/keyword general:snap:respect_gaps false")); - OK(getFromSocket("/keyword general:snap:border_overlap true")); - testMonitorSnap(false, true); - - NLog::log("\n{}Turning on both border_overlap and respect_gaps", Colors::YELLOW); - OK(getFromSocket("/keyword general:snap:respect_gaps true")); - testMonitorSnap(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")); - OK(getFromSocket("/dispatch workspace 1")); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/solitary.cpp b/hyprtester/src/tests/main/solitary.cpp deleted file mode 100644 index 6f4d1373..00000000 --- a/hyprtester/src/tests/main/solitary.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#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 solitary clients", Colors::GREEN); - - OK(getFromSocket("/keyword general:allow_tearing false")); - OK(getFromSocket("/keyword render:direct_scanout 0")); - OK(getFromSocket("/keyword cursor:no_hardware_cursors 1")); - NLog::log("{}Expecting blocked solitary/DS/tearing", Colors::YELLOW); - { - auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "solitary: 0\n"); - EXPECT_CONTAINS(str, "solitaryBlockedBy: windowed mode,missing candidate"); - EXPECT_CONTAINS(str, "activelyTearing: false"); - EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,user settings,not supported by monitor,missing candidate"); - EXPECT_CONTAINS(str, "directScanoutTo: 0\n"); - EXPECT_CONTAINS(str, "directScanoutBlockedBy: user settings,software renders/cursors,missing candidate"); - } - - // FIXME: need a reliable client with solitary opaque surface in fullscreen. kitty doesn't work all the time - // NLog::log("{}Spawning kittyProcA", Colors::YELLOW); - // auto kittyProcA = Tests::spawnKitty(); - - // if (!kittyProcA) { - // NLog::log("{}Error: kitty did not spawn", Colors::RED); - // return false; - // } - - // OK(getFromSocket("/keyword general:allow_tearing true")); - // OK(getFromSocket("/keyword render:direct_scanout 1")); - // NLog::log("{}", getFromSocket("/clients")); - // OK(getFromSocket("/dispatch fullscreen")); - // NLog::log("{}", getFromSocket("/clients")); - // std::this_thread::sleep_for(std::chrono::milliseconds(100)); - // NLog::log("{}Expecting kitty to almost pass for solitary/DS/tearing", Colors::YELLOW); - // { - // auto str = getFromSocket("/monitors"); - // EXPECT_NOT_CONTAINS(str, "solitary: 0\n"); - // EXPECT_CONTAINS(str, "solitaryBlockedBy: null"); - // EXPECT_CONTAINS(str, "activelyTearing: false"); - // EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,not supported by monitor,window settings"); - // } - - // OK(getFromSocket("/dispatch setprop active immediate 1")); - // NLog::log("{}Expecting kitty to almost pass for tearing", Colors::YELLOW); - // { - // auto str = getFromSocket("/monitors"); - // EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,not supported by monitor\n"); - // } - - // // kill all - // NLog::log("{}Killing all windows", Colors::YELLOW); - // Tests::killAllWindows(); - - NLog::log("{}Reloading the config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/tags.cpp b/hyprtester/src/tests/main/tags.cpp deleted file mode 100644 index c345fe71..00000000 --- a/hyprtester/src/tests/main/tags.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "../shared.hpp" -#include "tests.hpp" - -static int ret = 0; - -static bool testTags() { - NLog::log("{}Testing tags", Colors::GREEN); - - EXPECT(Tests::windowCount(), 0); - - NLog::log("{}Spawning kittyProcA&B on ws 1", Colors::YELLOW); - auto kittyProcA = Tests::spawnKitty("tagged"); - auto kittyProcB = Tests::spawnKitty("untagged"); - - if (!kittyProcA || !kittyProcB) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } - - NLog::log("{}Testing testTag tags", Colors::YELLOW); - - OK(getFromSocket("/keyword windowrule[tag-test-1]:tag +testTag")); - OK(getFromSocket("/keyword windowrule[tag-test-1]:match:class tagged")); - OK(getFromSocket("/keyword windowrule[tag-test-2]:match:tag negative:testTag")); - OK(getFromSocket("/keyword windowrule[tag-test-2]:no_shadow true")); - OK(getFromSocket("/keyword windowrule[tag-test-3]:match:tag testTag")); - OK(getFromSocket("/keyword windowrule[tag-test-3]:no_dim true")); - - EXPECT(Tests::windowCount(), 2); - OK(getFromSocket("/dispatch focuswindow class:tagged")); - NLog::log("{}Testing tagged window for no_dim 0 & no_shadow", Colors::YELLOW); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "testTag"); - EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "true"); - EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "false"); - NLog::log("{}Testing untagged window for no_dim & no_shadow", Colors::YELLOW); - OK(getFromSocket("/dispatch focuswindow class:untagged")); - EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "testTag"); - EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "true"); - EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "false"); - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - OK(getFromSocket("/reload")); - - return ret == 0; -} - -REGISTER_TEST_FN(testTags) diff --git a/hyprtester/src/tests/main/tests.hpp b/hyprtester/src/tests/main/tests.hpp deleted file mode 100644 index 757052b6..00000000 --- a/hyprtester/src/tests/main/tests.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include - -inline std::vector> testFns; - -#define REGISTER_TEST_FN(fn) \ - static auto _register_fn = [] { \ - testFns.emplace_back(fn); \ - return 1; \ - }(); diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp deleted file mode 100644 index beac0298..00000000 --- a/hyprtester/src/tests/main/window.cpp +++ /dev/null @@ -1,1138 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include "../shared.hpp" -#include "tests.hpp" - -static int ret = 0; - -static bool spawnKitty(const std::string& class_, const std::vector& args = {}) { - NLog::log("{}Spawning {}", Colors::YELLOW, class_); - if (!Tests::spawnKitty(class_, args)) { - NLog::log("{}Error: {} did not spawn", Colors::RED, class_); - return false; - } - return true; -} - -/// Spawns a kitty and creates a file and returns its name. The removal of the file triggers -/// activation of the spawned kitty window. -/// -/// On failure, returns an empty string, possibly leaving a temporary file. -static std::string spawnKittyActivating(const std::string& class_ = "kitty_activating") { - // `XXXXXX` is what `mkstemp` expects to find in the string - std::string tmpFilename = (std::filesystem::temp_directory_path() / "XXXXXX").string(); - int fd = mkstemp(tmpFilename.data()); - if (fd < 0) { - NLog::log("{}Error: could not create tmp file: errno {}", Colors::RED, errno); - return ""; - } - (void)close(fd); - bool ok = - spawnKitty(class_, {"-o", "allow_remote_control=yes", "--", "/bin/sh", "-c", "while [ -f \"" + tmpFilename + "\" ]; do :; done; kitten @ focus-window; sleep infinity"}); - if (!ok) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return ""; - } - return tmpFilename; -} - -static std::string getWindowAttribute(const std::string& winInfo, const std::string& attr) { - auto pos = winInfo.find(attr); - if (pos == std::string::npos) { - NLog::log("{}Wrong window attribute", Colors::RED); - ret = 1; - return "Wrong window attribute"; - } - auto pos2 = winInfo.find('\n', pos); - return winInfo.substr(pos, pos2 - pos); -} - -static std::string getWindowAddress(const std::string& winInfo) { - auto pos = winInfo.find("Window "); - auto pos2 = winInfo.find(" -> "); - if (pos == std::string::npos || pos2 == std::string::npos) { - NLog::log("{}Wrong window info", Colors::RED); - ret = 1; - return "Wrong window info"; - } - return winInfo.substr(pos + 7, pos2 - pos - 7); -} - -static void testSwapWindow() { - NLog::log("{}Testing swapwindow", Colors::GREEN); - - // test on workspace "swapwindow" - NLog::log("{}Switching to workspace \"swapwindow\"", Colors::YELLOW); - getFromSocket("/dispatch workspace name:swapwindow"); - - if (!Tests::spawnKitty("kitty_A")) { - ret = 1; - return; - } - - if (!Tests::spawnKitty("kitty_B")) { - ret = 1; - return; - } - - NLog::log("{}Expecting 2 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 2); - - // Test swapwindow by direction - { - getFromSocket("/dispatch focuswindow class:kitty_A"); - auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:"); - NLog::log("{}Testing kitty_A {}, swapwindow with direction 'r'", Colors::YELLOW, pos); - - OK(getFromSocket("/dispatch swapwindow r")); - OK(getFromSocket("/dispatch focuswindow class:kitty_B")); - - EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos)); - } - - // Test swapwindow by class - { - getFromSocket("/dispatch focuswindow class:kitty_A"); - auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:"); - NLog::log("{}Testing kitty_A {}, swapwindow with class:kitty_B", Colors::YELLOW, pos); - - OK(getFromSocket("/dispatch swapwindow class:kitty_B")); - OK(getFromSocket("/dispatch focuswindow class:kitty_B")); - - EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos)); - } - - // Test swapwindow by address - { - getFromSocket("/dispatch focuswindow class:kitty_B"); - auto addr = getWindowAddress(getFromSocket("/activewindow")); - getFromSocket("/dispatch focuswindow class:kitty_A"); - auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:"); - NLog::log("{}Testing kitty_A {}, swapwindow with address:0x{}(kitty_B)", Colors::YELLOW, pos, addr); - - OK(getFromSocket(std::format("/dispatch swapwindow address:0x{}", addr))); - OK(getFromSocket(std::format("/dispatch focuswindow address:0x{}", addr))); - - EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos)); - } - - NLog::log("{}Testing swapwindow with fullscreen. Expecting to fail", Colors::YELLOW); - { - OK(getFromSocket("/dispatch fullscreen")); - - auto str = getFromSocket("/dispatch swapwindow l"); - EXPECT_CONTAINS(str, "Can't swap fullscreen window"); - - OK(getFromSocket("/dispatch fullscreen")); - } - - NLog::log("{}Testing swapwindow with different workspace", Colors::YELLOW); - { - getFromSocket("/dispatch focuswindow class:kitty_B"); - auto addr = getWindowAddress(getFromSocket("/activewindow")); - auto ws = getWindowAttribute(getFromSocket("/activewindow"), "workspace:"); - NLog::log("{}Sending address:0x{}(kitty_B) to workspace \"swapwindow2\"", Colors::YELLOW, addr); - - OK(getFromSocket("/dispatch movetoworkspacesilent name:swapwindow2")); - OK(getFromSocket(std::format("/dispatch swapwindow address:0x{}", addr))); - getFromSocket("/dispatch focuswindow class:kitty_B"); - EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", ws)); - } - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); -} - -static void testGroupRules() { - NLog::log("{}Testing group window rules", Colors::YELLOW); - - OK(getFromSocket("/keyword general:border_size 8")); - OK(getFromSocket("/keyword workspace w[tv1], bordersize:0")); - OK(getFromSocket("/keyword workspace f[1], bordersize:0")); - OK(getFromSocket("/keyword windowrule match:workspace w[tv1], border_size 0")); - OK(getFromSocket("/keyword windowrule match:workspace f[1], border_size 0")); - - if (!Tests::spawnKitty("kitty_A")) { - ret = 1; - return; - } - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "0"); - } - - if (!Tests::spawnKitty("kitty_B")) { - ret = 1; - return; - } - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "8"); - } - - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - OK(getFromSocket("/dispatch togglegroup")); - OK(getFromSocket("/dispatch focuswindow class:kitty_B")); - OK(getFromSocket("/dispatch moveintogroup l")); - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "0"); - } - - OK(getFromSocket("/dispatch changegroupactive f")); - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "0"); - } - - if (!Tests::spawnKitty("kitty_C")) { - ret = 1; - return; - } - - OK(getFromSocket("/dispatch moveoutofgroup r")); - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "8"); - } - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); -} - -static bool isActiveWindow(const std::string& class_, char fullscreen = '0', bool log = true) { - std::string activeWin = getFromSocket("/activewindow"); - auto winClass = getWindowAttribute(activeWin, "class:"); - auto winFullscreen = getWindowAttribute(activeWin, "fullscreen:").back(); - if (winClass.substr(strlen("class: ")) == class_ && winFullscreen == fullscreen) - return true; - else { - if (log) - NLog::log("{}Wrong active window: expected class {} fullscreen '{}', found class {}, fullscreen '{}'", Colors::RED, class_, fullscreen, winClass, winFullscreen); - return false; - } -} - -static bool waitForActiveWindow(const std::string& class_, char fullscreen = '0', bool logLastCheck = true, int maxTries = 50) { - int cnt = 0; - while (!isActiveWindow(class_, fullscreen, false)) { - ++cnt; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - if (cnt > maxTries) { - return isActiveWindow(class_, fullscreen, logLastCheck); - } - } - return true; -} - -/// Tests behavior of a window being focused when on that window's workspace -/// another fullscreen window exists. -static bool testWindowFocusOnFullscreenConflict() { - if (!spawnKitty("kitty_A")) - return false; - if (!spawnKitty("kitty_B")) - return false; - - OK(getFromSocket("/keyword misc:focus_on_activate true")); - - // Unfullscreen on conflict - { - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2")); - - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - OK(getFromSocket("/dispatch fullscreen 0 set")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - - // Dispatch-focus the same window - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - - // Dispatch-focus a different window - OK(getFromSocket("/dispatch focuswindow class:kitty_B")); - EXPECT(isActiveWindow("kitty_B", '0'), true); - - // Make a window that will request focus - const std::string removeToActivate = spawnKittyActivating(); - if (removeToActivate.empty()) - return false; - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - OK(getFromSocket("/dispatch fullscreen 0 set")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - std::filesystem::remove(removeToActivate); - EXPECT(waitForActiveWindow("kitty_activating", '0'), true); - OK(getFromSocket("/dispatch forcekillactive")); - Tests::waitUntilWindowsN(2); - } - - // Take over on conflict - { - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1")); - - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - OK(getFromSocket("/dispatch fullscreen 0 set")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - - // Dispatch-focus the same window - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - - // Dispatch-focus a different window - OK(getFromSocket("/dispatch focuswindow class:kitty_B")); - EXPECT(isActiveWindow("kitty_B", '2'), true); - OK(getFromSocket("/dispatch fullscreenstate 0 0")); - - // Make a window that will request focus - const std::string removeToActivate = spawnKittyActivating(); - if (removeToActivate.empty()) - return false; - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - OK(getFromSocket("/dispatch fullscreen 0 set")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - std::filesystem::remove(removeToActivate); - EXPECT(waitForActiveWindow("kitty_activating", '2'), true); - OK(getFromSocket("/dispatch forcekillactive")); - Tests::waitUntilWindowsN(2); - } - - // Keep the old focus on conflict - { - OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0")); - - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - OK(getFromSocket("/dispatch fullscreen 0 set")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - - // Dispatch-focus the same window - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - - // Make a window that will request focus - the setting is treated normally - const std::string removeToActivate = spawnKittyActivating(); - if (removeToActivate.empty()) - return false; - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - OK(getFromSocket("/dispatch fullscreen 0 set")); - EXPECT(isActiveWindow("kitty_A", '2'), true); - std::filesystem::remove(removeToActivate); - EXPECT(waitForActiveWindow("kitty_A", '2'), true); - } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; -} - -static void testMaximizeSize() { - NLog::log("{}Testing maximize size", Colors::GREEN); - - EXPECT(spawnKitty("kitty_A"), true); - - // check kitty properties. Maximizing shouldnt change its size - { - auto str = getFromSocket("/clients"); - EXPECT(str.contains("at: 22,22"), true); - EXPECT(str.contains("size: 1876,1036"), true); - EXPECT(str.contains("fullscreen: 0"), true); - } - - OK(getFromSocket("/dispatch fullscreen 1")); - - { - auto str = getFromSocket("/clients"); - EXPECT(str.contains("at: 22,22"), true); - EXPECT(str.contains("size: 1876,1036"), true); - EXPECT(str.contains("fullscreen: 1"), true); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); -} - -static void testFloatingFocusOnFullscreen() { - NLog::log("{}Testing floating focus on fullscreen", Colors::GREEN); - - EXPECT(spawnKitty("kitty_A"), true); - OK(getFromSocket("/dispatch togglefloating")); - - EXPECT(spawnKitty("kitty_B"), true); - OK(getFromSocket("/dispatch fullscreen 1")); - - OK(getFromSocket("/dispatch cyclenext")); - - OK(getFromSocket("/dispatch plugin:test:floating_focus_on_fullscreen")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); -} - -static void testGroupFallbackFocus() { - NLog::log("{}Testing group fallback focus", Colors::GREEN); - - EXPECT(spawnKitty("kitty_A"), true); - - OK(getFromSocket("/dispatch togglegroup")); - - EXPECT(spawnKitty("kitty_B"), true); - EXPECT(spawnKitty("kitty_C"), true); - EXPECT(spawnKitty("kitty_D"), true); - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("class: kitty_D"), true); - } - - OK(getFromSocket("/dispatch focuswindow class:kitty_B")); - OK(getFromSocket("/dispatch focuswindow class:kitty_D")); - OK(getFromSocket("/dispatch killactive")); - - Tests::waitUntilWindowsN(3); - - // Focus must return to the last focus, in this case B. - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("class: kitty_B"), true); - } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); -} - -static void testBringActiveToTopMouseMovement() { - NLog::log("{}Testing bringactivetotop mouse movement", Colors::GREEN); - - Tests::killAllWindows(); - OK(getFromSocket("/keyword input:follow_mouse 2")); - OK(getFromSocket("/keyword input:float_switch_override_focus 0")); - - EXPECT(spawnKitty("a"), true); - OK(getFromSocket("/dispatch setfloating")); - OK(getFromSocket("/dispatch movewindowpixel exact 500 300,activewindow")); - OK(getFromSocket("/dispatch resizewindowpixel exact 400 400,activewindow")); - - EXPECT(spawnKitty("b"), true); - OK(getFromSocket("/dispatch setfloating")); - OK(getFromSocket("/dispatch movewindowpixel exact 500 300,activewindow")); - OK(getFromSocket("/dispatch resizewindowpixel exact 400 400,activewindow")); - - auto getTopWindow = []() -> std::string { - auto clients = getFromSocket("/clients"); - return (clients.rfind("class: a") > clients.rfind("class: b")) ? "a" : "b"; - }; - - EXPECT(getTopWindow(), std::string("b")); - OK(getFromSocket("/dispatch movecursor 700 500")); - - OK(getFromSocket("/dispatch focuswindow class:a")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: a"); - - OK(getFromSocket("/dispatch bringactivetotop")); - EXPECT(getTopWindow(), std::string("a")); - - OK(getFromSocket("/dispatch plugin:test:click 272,1")); - OK(getFromSocket("/dispatch plugin:test:click 272,0")); - - EXPECT(getTopWindow(), std::string("a")); - - Tests::killAllWindows(); -} - -static void testInitialFloatSize() { - NLog::log("{}Testing initial float size", Colors::GREEN); - - Tests::killAllWindows(); - OK(getFromSocket("/keyword windowrule match:class kitty, float yes")); - OK(getFromSocket("/keyword input:float_switch_override_focus 0")); - - EXPECT(spawnKitty("kitty"), true); - - { - // Kitty by default opens as 640x400, if this changes this test will break - auto str = getFromSocket("/clients"); - EXPECT(str.contains("size: 640,400"), true); - } - - OK(getFromSocket("/reload")); - - Tests::killAllWindows(); - - OK(getFromSocket("/dispatch exec [float yes]kitty")); - - Tests::waitUntilWindowsN(1); - - { - // Kitty by default opens as 640x400, if this changes this test will break - auto str = getFromSocket("/clients"); - EXPECT(str.contains("size: 640,400"), true); - EXPECT(str.contains("floating: 1"), true); - } - - Tests::killAllWindows(); -} - -/// Tests that the `focus_on_activate` effect of window rules always overrides -/// the `misc:focus_on_activate` variable. -static bool testWindowRuleFocusOnActivate() { - OK(getFromSocket("/reload")); - - if (!spawnKitty("kitty_default")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - // Do not focus anyone automatically - ///////////OK(getFromSocket("/keyword windowrule match:class .*, no_initial_focus true")); - - // `focus_on_activate off` takes over - { - OK(getFromSocket("/keyword misc:focus_on_activate true")); - OK(getFromSocket("/keyword windowrule match:class kitty_antifocus, focus_on_activate off")); - - const std::string removeToActivate = spawnKittyActivating("kitty_antifocus"); - if (removeToActivate.empty()) { - return false; - } - EXPECT(waitForActiveWindow("kitty_antifocus"), true); - OK(getFromSocket("/dispatch focuswindow class:kitty_default")); - EXPECT(isActiveWindow("kitty_default"), true); - - std::filesystem::remove(removeToActivate); - // The focus should NOT transition, since the window rule explicitly forbids that - EXPECT(waitForActiveWindow("kitty_antifocus", '0', false), false); - } - - // `focus_on_activate on` takes over - { - OK(getFromSocket("/keyword misc:focus_on_activate false")); - OK(getFromSocket("/keyword windowrule match:class kitty_superfocus, focus_on_activate on")); - - const std::string removeToActivate = spawnKittyActivating("kitty_superfocus"); - if (removeToActivate.empty()) { - return false; - } - EXPECT(waitForActiveWindow("kitty_superfocus"), true); - OK(getFromSocket("/dispatch focuswindow class:kitty_default")); - EXPECT(isActiveWindow("kitty_default"), true); - - std::filesystem::remove(removeToActivate); - // Now that we requested activation, the focus SHOULD transition to kitty_superfocus, according to the window rule - EXPECT(waitForActiveWindow("kitty_superfocus"), true); - } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; -} - -// tests if a pinned window contains the valid workspace after change -static bool testPinnedWorkspacesValid() { - OK(getFromSocket("/reload")); - getFromSocket("/dispatch workspace 1337"); - - if (!spawnKitty("kitty")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - OK(getFromSocket("/dispatch setfloating class:kitty")); - OK(getFromSocket("/dispatch pin class:kitty")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 1337"), true); - EXPECT(str.contains("pinned: 1"), true); - } - - getFromSocket("/dispatch workspace 1338"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 1338"), true); - EXPECT(str.contains("pinned: 1"), true); - } - - OK(getFromSocket("/dispatch settiled class:kitty")) - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 1338"), true); - EXPECT(str.contains("pinned: 0"), true); - } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; -} - -static bool testWindowRuleWorkspaceEmpty() { - NLog::log("{}Testing windowrule workspace empty", Colors::YELLOW); - OK(getFromSocket("/reload")); - - OK(getFromSocket("/keyword windowrule match:class kitty_A, workspace empty")); - OK(getFromSocket("/keyword windowrule match:class kitty_B, workspace emptyn")); - - getFromSocket("/dispatch workspace 3"); - - if (!spawnKitty("kitty")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 3"), true); - } - - if (!spawnKitty("kitty_A")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 1"), true); - } - - getFromSocket("/dispatch workspace 3"); - if (!spawnKitty("kitty_B")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; - } - - { - auto str = getFromSocket("/activewindow"); - EXPECT(str.contains("workspace: 4"), true); - } - - Tests::killAllWindows(); - - return true; -} - -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"); - - if (!spawnKitty("kitty_A")) - return false; - - // check kitty properties. One kitty should take the entire screen, as this is smart gaps - NLog::log("{}Expecting kitty_A 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("{}Testing window split ratios", Colors::YELLOW); - { - const double INITIAL_RATIO = 1.25; - const int GAPSIN = 5; - const int GAPSOUT = 20; - const int BORDERSIZE = 2; - const int BORDERS = BORDERSIZE * 2; - const int MONITOR_W = 1920; - const int MONITOR_H = 1080; - - const float totalAvailableHeight = MONITOR_H - (GAPSOUT * 2); - const int HEIGHT = std::floor(totalAvailableHeight) - BORDERS; - const float availableWidthForSplit = MONITOR_W - (GAPSOUT * 2) - GAPSIN; - - auto calculateFinalWidth = [&](double boxWidth, bool isLeftWindow) { - double gapLeft = isLeftWindow ? GAPSOUT : GAPSIN; - double gapRight = isLeftWindow ? GAPSIN : GAPSOUT; - return std::floor(boxWidth - gapLeft - gapRight - BORDERS); - }; - - double geomBoxWidthA_R1 = (availableWidthForSplit * INITIAL_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0); - double geomBoxWidthB_R1 = MONITOR_W - geomBoxWidthA_R1; - const int WIDTH1 = calculateFinalWidth(geomBoxWidthB_R1, false); - - const double INVERTED_RATIO = 0.75; - double geomBoxWidthA_R2 = (availableWidthForSplit * INVERTED_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0); - double geomBoxWidthB_R2 = MONITOR_W - geomBoxWidthA_R2; - const int WIDTH2 = calculateFinalWidth(geomBoxWidthB_R2, false); - const int WIDTH_A_FINAL = calculateFinalWidth(geomBoxWidthA_R2, true); - - OK(getFromSocket("/keyword dwindle:default_split_ratio 1.25")); - - if (!spawnKitty("kitty_B")) - return false; - - NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH1, HEIGHT); - EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT)); - - OK(getFromSocket("/dispatch killwindow activewindow")); - Tests::waitUntilWindowsN(1); - - NLog::log("{}Inverting the split ratio", Colors::YELLOW); - OK(getFromSocket("/keyword dwindle:default_split_ratio 0.75")); - - if (!spawnKitty("kitty_B")) - return false; - - try { - NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH2, HEIGHT); - - { - auto data = getFromSocket("/activewindow"); - data = data.substr(data.find("size:") + 5); - data = data.substr(0, data.find('\n')); - - Hyprutils::String::CVarList2 sizes(std::move(data), 0, ','); - - EXPECT_MAX_DELTA(std::stoi(std::string{sizes[0]}), WIDTH2, 2); - EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2); - } - - OK(getFromSocket("/dispatch focuswindow class:kitty_A")); - NLog::log("{}Expecting kitty_A size: {},{}", Colors::YELLOW, WIDTH_A_FINAL, HEIGHT); - - { - auto data = getFromSocket("/activewindow"); - data = data.substr(data.find("size:") + 5); - data = data.substr(0, data.find('\n')); - - Hyprutils::String::CVarList2 sizes(std::move(data), 0, ','); - - EXPECT_MAX_DELTA(std::stoi(std::string{sizes[0]}), WIDTH_A_FINAL, 2); - EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2); - } - - } catch (...) { - NLog::log("{}Exception thrown", Colors::RED); - EXPECT(false, true); - } - - OK(getFromSocket("/keyword dwindle:default_split_ratio 1")); - } - - // open xeyes - NLog::log("{}Spawning xeyes", Colors::YELLOW); - getFromSocket("/dispatch exec xeyes"); - - NLog::log("{}Keep checking if xeyes spawned", Colors::YELLOW); - Tests::waitUntilWindowsN(3); - - 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 float it - { - auto str = getFromSocket("/clients"); - EXPECT_NOT_CONTAINS(str, "floating: 1"); - getFromSocket("/dispatch setfloating class:XEyes"); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - str = getFromSocket("/clients"); - EXPECT_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); - - testSwapWindow(); - - getFromSocket("/dispatch workspace 1"); - - if (!testWindowFocusOnFullscreenConflict()) { - ret = 1; - return false; - } - - NLog::log("{}Testing spawning a floating window over a fullscreen window", Colors::YELLOW); - { - if (!spawnKitty("kitty_A")) - return false; - OK(getFromSocket("/dispatch fullscreen 0 set")); - EXPECT(Tests::windowCount(), 1); - - OK(getFromSocket("/dispatch exec [float] kitty")); - Tests::waitUntilWindowsN(2); - - OK(getFromSocket("/dispatch focuswindow class:^kitty$")); - const auto focused1 = getFromSocket("/activewindow"); - EXPECT_CONTAINS(focused1, "class: kitty\n"); - - OK(getFromSocket("/dispatch killwindow activewindow")); - Tests::waitUntilWindowsN(1); - - // The old window should be focused again - const auto focused2 = getFromSocket("/activewindow"); - EXPECT_CONTAINS(focused2, "class: kitty_A\n"); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - } - - NLog::log("{}Testing minsize/maxsize rules for tiled windows", Colors::YELLOW); - { - // Enable the config for testing, test max/minsize for tiled windows and centering - OK(getFromSocket("/keyword misc:size_limits_tiled 1")); - OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match:class kitty_maxsize")); - OK(getFromSocket("/keyword windowrule[kitty-max-rule]:max_size 1500 500")); - OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:min_size 1200 500")); - if (!spawnKitty("kitty_maxsize")) - return false; - - auto dwindle = getFromSocket("/activewindow"); - EXPECT_CONTAINS(dwindle, "size: 1500,500"); - EXPECT_CONTAINS(dwindle, "at: 210,290"); - - // Fuck this test, it's fucking stupid - vax - // if (!spawnKitty("kitty_maxsize")) - // return false; - // EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500"); - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - OK(getFromSocket("/keyword general:layout master")); - - if (!spawnKitty("kitty_maxsize")) - return false; - - auto master = getFromSocket("/activewindow"); - EXPECT_CONTAINS(master, "size: 1500,500"); - EXPECT_CONTAINS(master, "at: 210,290"); - - if (!spawnKitty("kitty_maxsize")) - return false; - - // FIXME: I can't be arsed. - OK(getFromSocket("/dispatch focuswindow class:kitty_maxsize")); - // EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500") - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - } - - NLog::log("{}Testing minsize/maxsize rules", Colors::YELLOW); - { - // Disable size limits tiled and check if props are working and not getting skipped - OK(getFromSocket("/keyword misc:size_limits_tiled 0")); - OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match:class kitty_maxsize")); - OK(getFromSocket("/keyword windowrule[kitty-max-rule]:max_size 1500 500")); - OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:min_size 1200 500")); - if (!spawnKitty("kitty_maxsize")) - return false; - - { - auto res = getFromSocket("/getprop active max_size"); - EXPECT_CONTAINS(res, "1500"); - EXPECT_CONTAINS(res, "500"); - } - - { - auto res = getFromSocket("/getprop active min_size"); - EXPECT_CONTAINS(res, "1200"); - EXPECT_CONTAINS(res, "500"); - } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - } - - { - // Set float - OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match:class kitty_maxsize")); - OK(getFromSocket("/keyword windowrule[kitty-max-rule]:max_size 1200 500")); - OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:min_size 1200 500")); - OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:float yes")); - if (!spawnKitty("kitty_maxsize")) - return false; - - { - auto res = getFromSocket("/getprop active max_size"); - EXPECT_CONTAINS(res, "1200"); - EXPECT_CONTAINS(res, "500"); - } - - { - auto res = getFromSocket("/getprop active min_size"); - EXPECT_CONTAINS(res, "1200"); - EXPECT_CONTAINS(res, "500"); - } - - { - auto res = getFromSocket("/activewindow"); - EXPECT_CONTAINS(res, "size: 1200,500"); - } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - } - - NLog::log("{}Testing window rules", Colors::YELLOW); - if (!spawnKitty("wr_kitty")) - return false; - { - auto str = getFromSocket("/activewindow"); - const int SIZE = 200; - EXPECT_CONTAINS(str, "floating: 1"); - EXPECT_CONTAINS(str, std::format("size: {},{}", SIZE, SIZE)); - EXPECT_NOT_CONTAINS(str, "pinned: 1"); - } - - OK(getFromSocket("/keyword windowrule[wr-kitty-stuff]:opacity 0.5 0.5 override")); - - { - auto str = getFromSocket("/getprop active opacity"); - EXPECT_CONTAINS(str, "0.5"); - } - - OK(getFromSocket("/keyword windowrule[special-magic-kitty]:match:class magic_kitty")); - OK(getFromSocket("/keyword windowrule[special-magic-kitty]:workspace special:magic")); - - if (!spawnKitty("magic_kitty")) - return false; - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "special:magic"); - EXPECT_NOT_CONTAINS(str, "workspace: 9"); - } - - if (auto str = getFromSocket("/monitors"); str.contains("magic)")) { - OK(getFromSocket("/dispatch togglespecialworkspace magic")); - } - - Tests::killAllWindows(); - - OK(getFromSocket("/keyword windowrule[border-magic-kitty]:match:class border_kitty")); - OK(getFromSocket("/keyword windowrule[border-magic-kitty]:border_color rgba(c6ff00ff) rgba(ff0000ee) 45deg")); - - if (!spawnKitty("border_kitty")) - return false; - - OK(getFromSocket("/dispatch focuswindow class:border_kitty")); - - { - auto str = getFromSocket("/getprop active active_border_color"); - EXPECT_CONTAINS(str, "ffc6ff00"); - EXPECT_CONTAINS(str, "eeff0000"); - EXPECT_CONTAINS(str, "45deg"); - } - - Tests::killAllWindows(); - - if (!spawnKitty("tag_kitty")) - return false; - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "floating: 1"); - } - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - // test rules that overlap effects but don't overlap props - OK(getFromSocket("/keyword windowrule match:class overlap_kitty, border_size 0")); - OK(getFromSocket("/keyword windowrule match:fullscreen false, border_size 10")); - - if (!spawnKitty("overlap_kitty")) - return false; - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "10"); - } - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - // test persistent_size between floating window launches - OK(getFromSocket("/keyword windowrule match:class persistent_size_kitty, persistent_size true, float true")); - - if (!spawnKitty("persistent_size_kitty")) - return false; - - OK(getFromSocket("/dispatch resizeactive exact 600 400")) - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "size: 600,400"); - EXPECT_CONTAINS(str, "floating: 1"); - } - - Tests::killAllWindows(); - - if (!spawnKitty("persistent_size_kitty")) - return false; - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "size: 600,400"); - EXPECT_CONTAINS(str, "floating: 1"); - } - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - OK(getFromSocket("/keyword general:border_size 0")); - OK(getFromSocket("/keyword windowrule match:float true, border_size 10")); - - if (!spawnKitty("border_kitty")) - return false; - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "0"); - } - - OK(getFromSocket("/dispatch togglefloating")); - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "10"); - } - - OK(getFromSocket("/dispatch togglefloating")); - - { - auto str = getFromSocket("/getprop active border_size"); - EXPECT_CONTAINS(str, "0"); - } - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - // test expression rules - OK(getFromSocket("/keyword windowrule match:class expr_kitty, float yes, size monitor_w*0.5 monitor_h*0.5, min_size monitor_w*0.25 monitor_h*0.25, " - "max_size monitor_w*0.75 monitor_h*0.75, move 20+(monitor_w*0.1) monitor_h*0.5")); - - if (!spawnKitty("expr_kitty")) - return false; - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "floating: 1"); - EXPECT_CONTAINS(str, "at: 212,540"); - EXPECT_CONTAINS(str, "size: 960,540"); - - auto min = getFromSocket("/getprop active min_size"); - EXPECT_CONTAINS(min, "480"); - EXPECT_CONTAINS(min, "270"); - - auto max = getFromSocket("/getprop active max_size"); - EXPECT_CONTAINS(max, "1440"); - EXPECT_CONTAINS(max, "810"); - } - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - OK(getFromSocket("/dispatch plugin:test:add_window_rule")); - OK(getFromSocket("/reload")); - - OK(getFromSocket("/keyword windowrule match:class plugin_kitty, plugin_rule effect")); - - if (!spawnKitty("plugin_kitty")) - return false; - - OK(getFromSocket("/dispatch plugin:test:check_window_rule")); - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - OK(getFromSocket("/dispatch plugin:test:add_window_rule")); - OK(getFromSocket("/reload")); - - OK(getFromSocket("/keyword windowrule[test-plugin-rule]:match:class plugin_kitty")); - OK(getFromSocket("/keyword windowrule[test-plugin-rule]:plugin_rule effect")); - - if (!spawnKitty("plugin_kitty")) - return false; - - OK(getFromSocket("/dispatch plugin:test:check_window_rule")); - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - testGroupRules(); - testMaximizeSize(); - testFloatingFocusOnFullscreen(); - testBringActiveToTopMouseMovement(); - testGroupFallbackFocus(); - testInitialFloatSize(); - testWindowRuleFocusOnActivate(); - testPinnedWorkspacesValid(); - testWindowRuleWorkspaceEmpty(); - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - 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) diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp deleted file mode 100644 index 122cd619..00000000 --- a/hyprtester/src/tests/main/workspaces.cpp +++ /dev/null @@ -1,750 +0,0 @@ -#include "tests.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include "../shared.hpp" - -static int ret = 0; - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; -using namespace Hyprutils::Utils; - -#define UP CUniquePointer -#define SP CSharedPointer - -static bool testSpecialWorkspaceFullscreen() { - NLog::log("{}Testing special workspace fullscreen detection", Colors::YELLOW); - - CScopeGuard guard = {[&]() { - NLog::log("{}Cleaning up special workspace fullscreen test", Colors::YELLOW); - // Close special workspace if open - auto monitors = getFromSocket("/monitors"); - if (monitors.contains("(special:test_fs_special)") && !monitors.contains("special workspace: 0 ()")) - getFromSocket("/dispatch togglespecialworkspace test_fs_special"); - Tests::killAllWindows(); - OK(getFromSocket("/reload")); - }}; - - getFromSocket("/dispatch workspace 1"); - EXPECT(Tests::windowCount(), 0); - - NLog::log("{}Test 1: Fullscreen detection on special workspace", Colors::YELLOW); - - OK(getFromSocket("/dispatch workspace special:test_fs_special")); - - if (!Tests::spawnKitty("kitty_special")) - return false; - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_special"); - EXPECT_CONTAINS(str, "(special:test_fs_special)"); - } - - OK(getFromSocket("/dispatch fullscreen 0")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - } - - { - auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "(special:test_fs_special)"); - } - - NLog::log("{}Test 2: Special workspace fullscreen precedence", Colors::YELLOW); - - // Close special workspace before spawning on regular workspace - OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special")); - getFromSocket("/dispatch workspace 1"); - - if (!Tests::spawnKitty("kitty_regular")) - return false; - - OK(getFromSocket("/dispatch fullscreen 0")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_regular"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - } - - OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special")); - OK(getFromSocket("/dispatch focuswindow class:kitty_special")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_special"); - } - - NLog::log("{}Test 3: Toggle special workspace hides it", Colors::YELLOW); - - OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special")); - OK(getFromSocket("/dispatch focuswindow class:kitty_regular")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_regular"); - EXPECT_CONTAINS(str, "fullscreen: 2"); - } - - { - auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "special workspace: 0 ()"); - } - - return true; -} - -static bool testAsymmetricGaps() { - NLog::log("{}Testing asymmetric gap splits", Colors::YELLOW); - { - - CScopeGuard guard = {[&]() { - NLog::log("{}Cleaning up asymmetric gap test", Colors::YELLOW); - Tests::killAllWindows(); - OK(getFromSocket("/reload")); - }}; - - OK(getFromSocket("/dispatch workspace name:gap_split_test")); - OK(getFromSocket("r/keyword general:gaps_in 0")); - OK(getFromSocket("r/keyword general:border_size 0")); - OK(getFromSocket("r/keyword dwindle:split_width_multiplier 1.0")); - OK(getFromSocket("r/keyword workspace name:gap_split_test,gapsout:0 1000 0 0")); - - NLog::log("{}Testing default split (force_split = 0)", Colors::YELLOW); - OK(getFromSocket("r/keyword dwindle:force_split 0")); - - if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) - return false; - - NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - NLog::log("{}Testing force_split = 1", Colors::YELLOW); - OK(getFromSocket("r/keyword dwindle:force_split 1")); - - if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) - return false; - - NLog::log("{}Expecting vertical split (B above A)", Colors::YELLOW); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); - - NLog::log("{}Expecting horizontal split (C left of B)", Colors::YELLOW); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); - - if (!Tests::spawnKitty("gaps_kitty_C")) - return false; - - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0"); - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - NLog::log("{}Testing force_split = 2", Colors::YELLOW); - OK(getFromSocket("r/keyword dwindle:force_split 2")); - - if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) - return false; - - NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); - - NLog::log("{}Expecting horizontal split (C right of A)", Colors::YELLOW); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); - - if (!Tests::spawnKitty("gaps_kitty_C")) - return false; - - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0"); - } - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - return true; -} - -static void testMultimonBAF() { - NLog::log("{}Testing multimon back and forth", Colors::YELLOW); - - OK(getFromSocket("/keyword binds:workspace_back_and_forth 1")); - - OK(getFromSocket("/dispatch focusmonitor HEADLESS-2")); - OK(getFromSocket("/dispatch workspace 1")); - - Tests::spawnKitty(); - - OK(getFromSocket("/dispatch workspace 2")); - - Tests::spawnKitty(); - - OK(getFromSocket("/dispatch focusmonitor HEADLESS-3")); - OK(getFromSocket("/dispatch workspace 3")); - - Tests::spawnKitty(); - - OK(getFromSocket("/dispatch workspace 3")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 2 "); - } - - OK(getFromSocket("/dispatch workspace 4")); - OK(getFromSocket("/dispatch workspace 4")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 2 "); - } - - OK(getFromSocket("/dispatch workspace 2")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 4 "); - } - - OK(getFromSocket("/dispatch workspace 3")); - OK(getFromSocket("/dispatch workspace 3")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 4 "); - } - - OK(getFromSocket("/dispatch workspace 2")); - OK(getFromSocket("/dispatch workspace 3")); - OK(getFromSocket("/dispatch workspace 1")); - OK(getFromSocket("/dispatch workspace 1")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 3 "); - } - - Tests::killAllWindows(); -} - -static void testMultimonFocus() { - NLog::log("{}Testing multimon focus and move", Colors::YELLOW); - - OK(getFromSocket("/keyword input:follow_mouse 0")); - OK(getFromSocket("/dispatch focusmonitor HEADLESS-3")); - OK(getFromSocket("/dispatch workspace 8")); - OK(getFromSocket("/dispatch focusmonitor HEADLESS-2")); - OK(getFromSocket("/dispatch workspace 7")); - - for (auto const& win : {"a", "b"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - - OK(getFromSocket("/dispatch focuswindow class:a")); - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 7 "); - } - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - Tests::spawnKitty("c"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/dispatch movefocus l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 7 "); - } - - OK(getFromSocket("/dispatch movewindow r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/dispatch movefocus r")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/dispatch movefocus l")); - - OK(getFromSocket("/keyword general:no_focus_fallback true")); - OK(getFromSocket("/keyword binds:window_direction_monitor_fallback false")); - - EXPECT_NOT(getFromSocket("/dispatch movefocus l"), "ok"); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/dispatch movewindow l")); - - { - auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); - } - - { - auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); - } - - OK(getFromSocket("/reload")); - - Tests::killAllWindows(); -} - -static void testDynamicWsEffects() { - // test dynamic workspace effects, they shouldn't lag - - OK(getFromSocket("/dispatch workspace 69")); - - Tests::spawnKitty("bitch"); - - OK(getFromSocket("r/keyword workspace 69,bordersize:20")); - OK(getFromSocket("r/keyword workspace 69,rounding:false")); - - EXPECT(getFromSocket("/getprop class:bitch border_size"), "20"); - EXPECT(getFromSocket("/getprop class:bitch rounding"), "0"); - - OK(getFromSocket("/reload")); - - Tests::killAllWindows(); -} - -static bool test() { - NLog::log("{}Testing workspaces", Colors::GREEN); - - EXPECT(Tests::windowCount(), 0); - - // test on workspace "window" - NLog::log("{}Switching to workspace 1", Colors::YELLOW); - getFromSocket("/dispatch workspace 1"); - - NLog::log("{}Checking persistent no-mon", Colors::YELLOW); - OK(getFromSocket("r/keyword workspace 966,persistent:1")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_CONTAINS(str, "workspace ID 966 (966)"); - } - - OK(getFromSocket("/reload")); - - 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")); - - { - auto str = getFromSocket("/workspaces"); - EXPECT_NOT_CONTAINS(str, "workspace ID 2 (2)"); - } - - 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 shouldn't 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"); - } - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - testMultimonBAF(); - testMultimonFocus(); - - // destroy the headless output - OK(getFromSocket("/output remove HEADLESS-3")); - - testSpecialWorkspaceFullscreen(); - testAsymmetricGaps(); - testDynamicWsEffects(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return !ret; -} - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/plugin/plugin.cpp b/hyprtester/src/tests/plugin/plugin.cpp deleted file mode 100644 index ffcc351a..00000000 --- a/hyprtester/src/tests/plugin/plugin.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "plugin.hpp" -#include "../../shared.hpp" -#include "../../hyprctlCompat.hpp" -#include -#include -#include -#include -#include -#include -#include -#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; -} - -bool testVkb() { - const auto RESPONSE = getFromSocket("/dispatch plugin:test:vkb"); - - if (RESPONSE != "ok") { - NLog::log("{}Vkb tests failed, tests returned:\n{}{}", Colors::RED, Colors::RESET, RESPONSE); - return false; - } - return true; -} diff --git a/hyprtester/src/tests/plugin/plugin.hpp b/hyprtester/src/tests/plugin/plugin.hpp deleted file mode 100644 index 1766c66e..00000000 --- a/hyprtester/src/tests/plugin/plugin.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -bool testPlugin(); -bool testVkb(); diff --git a/hyprtester/src/tests/shared.cpp b/hyprtester/src/tests/shared.cpp deleted file mode 100644 index f6e2fce9..00000000 --- a/hyprtester/src/tests/shared.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "shared.hpp" -#include -#include -#include -#include -#include -#include "../shared.hpp" -#include "../hyprctlCompat.hpp" - -using namespace Hyprutils::OS; -using namespace Hyprutils::Memory; - -CUniquePointer Tests::spawnKitty(const std::string& class_, const std::vector args) { - const auto COUNT_BEFORE = windowCount(); - - std::vector programArgs = args; - if (!class_.empty()) { - programArgs.insert(programArgs.begin(), "--class"); - programArgs.insert(programArgs.begin() + 1, class_); - } - CUniquePointer kitty = makeUnique("kitty", programArgs); - 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; -} - -CUniquePointer Tests::spawnLayerKitty(const std::string& namespace_, const std::vector args) { - std::vector programArgs = args; - if (!namespace_.empty()) { - programArgs.insert(programArgs.begin(), "--class"); - programArgs.insert(programArgs.begin() + 1, namespace_); - } - - programArgs.insert(programArgs.begin(), "+kitten"); - programArgs.insert(programArgs.begin() + 1, "panel"); - - CUniquePointer kitty = makeUnique("kitty", programArgs); - kitty->addEnv("WAYLAND_DISPLAY", WLDISPLAY); - kitty->runAsync(); - - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - // wait while the layer spawns - int counter = 0; - while (processAlive(kitty->pid()) && countOccurrences(getFromSocket("/layers"), std::format("pid: {}", kitty->pid())) == 0) { - 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()); - } - - 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; - } - } -} - -int Tests::layerCount() { - return countOccurrences(getFromSocket("/layers"), "namespace: "); -} - -bool Tests::killAllLayers() { - auto str = getFromSocket("/layers"); - auto pos = str.find("pid: "); - while (pos != std::string::npos) { - auto pid = stoi(str.substr(pos + 5, str.find('\n', pos))); - kill(pid, 15); - - // we need to wait for a bit because for some reason otherwise we'll end up - // with layers with pid -1 if they are all removed at the same time - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - pos = str.find("pid: ", pos + 5); - } - - int counter = 0; - while (Tests::layerCount() != 0) { - counter++; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - if (counter > 50) { - std::println("{}Timed out waiting for layers to close", Colors::RED); - return false; - } - } - - return true; -} - -std::string Tests::execAndGet(const std::string& cmd) { - CProcess proc("/bin/sh", {"-c", cmd}); - - if (!proc.runSync()) { - return "error"; - } - - return proc.stdOut(); -} - -bool Tests::writeFile(const std::string& name, const std::string& contents) { - std::ofstream of(name, std::ios::trunc); - if (!of.good()) - return false; - - of << contents; - of.close(); - - return true; -} diff --git a/hyprtester/src/tests/shared.hpp b/hyprtester/src/tests/shared.hpp deleted file mode 100644 index bf875f8b..00000000 --- a/hyprtester/src/tests/shared.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../Log.hpp" - -//NOLINTNEXTLINE -namespace Tests { - Hyprutils::Memory::CUniquePointer spawnKitty(const std::string& class_ = "", const std::vector args = {}); - Hyprutils::Memory::CUniquePointer spawnLayerKitty(const std::string& namespace_ = "", const std::vector args = {}); - bool processAlive(pid_t pid); - int windowCount(); - int countOccurrences(const std::string& in, const std::string& what); - bool killAllWindows(); - void waitUntilWindowsN(int n); - int layerCount(); - bool killAllLayers(); - std::string execAndGet(const std::string& cmd); - bool writeFile(const std::string& name, const std::string& contents); -}; diff --git a/hyprtester/test.conf b/hyprtester/test.conf deleted file mode 100644 index ab4f8ee3..00000000 --- a/hyprtester/test.conf +++ /dev/null @@ -1,413 +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=HEADLESS-PERSISTENT-TEST,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 = sleep 0 # Terminates very quickly -# 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 -} - -device { - name = test-mouse-1 - enabled = true -} - -# 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 - split_bias = 1 -} - -# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more -master { - new_status = master -} - -scrolling { - fullscreen_on_one_column = true - column_width = 0.5 - focus_fit_method = 1 - follow_focus = true - follow_min_visible = 1 - explicit_column_widths = 0.25, 0.333, 0.5, 0.667, 0.75, 1.0 - wrap_focus = true - wrap_swapcol = true -} - -# 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 { - -} - -# 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 -} - -debug { - disable_logs = false -} - - -################### -### 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, layoutmsg, 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 - -bind = $mainMod, u, submap, submap1 - -submap = submap1 -bind = , u, submap, submap2 -bind = , i, submap, submap3 -bind = , o, exec, $terminal -bind = , p, submap, reset - -submap = submap2, submap1 -bind = , o, exec, $terminal - -submap = submap3, reset -bind = , o, exec, $terminal - -submap = reset - - -############################## -### WINDOWS AND WORKSPACES ### -############################## - -windowrule { - # Ignore maximize requests from apps. You'll probably like this. - name = suppress-maximize-events - match:class = .* - - suppress_event = maximize -} - -windowrule { - # Fix some dragging issues with XWayland - name = fix-xwayland-drags - match:class = ^$ - match:title = ^$ - match:xwayland = true - match:float = true - match:fullscreen = false - match:pin = false - - no_focus = true -} - -workspace = n[s:window] w[tv1], gapsout:0, gapsin:0 -workspace = n[s:window] f[1], gapsout:0, gapsin:0 - -windowrule { - name = smart-gaps-1 - match:float = false - match:workspace = n[s:window] w[tv1] - - border_size = 0 - rounding = 0 -} - -windowrule { - name = smart-gaps-2 - match:float = false - match:workspace = n[s:window] f[1] - - border_size = 0 - rounding = 0 -} - -windowrule { - name = wr-kitty-stuff - match:class = wr_kitty - - float = true - size = 200 200 - pin = false -} - -windowrule { - name = tagged-kitty-floats - match:tag = tag_kitty - - float = true -} - -windowrule { - name = static-kitty-tag - match:class = tag_kitty - - tag = +tag_kitty -} - -gesture = 3, left, dispatcher, exec, kitty -gesture = 3, right, float -gesture = 3, up, close -gesture = 3, down, fullscreen - -gesture = 3, down, mod:ALT, float - -gesture = 3, horizontal, mod:ALT, workspace - -gesture = 5, up, dispatcher, sendshortcut, , e, activewindow -gesture = 5, down, dispatcher, sendshortcut, , x, activewindow -gesture = 5, left, dispatcher, sendshortcut, , i, activewindow -gesture = 5, right, dispatcher, sendshortcut, , t, activewindow -gesture = 4, right, dispatcher, sendshortcut, , return, activewindow -gesture = 4, left, dispatcher, movecursortocorner, 1 - -gesturep = 2, right, float diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..d9635a27 --- /dev/null +++ b/meson.build @@ -0,0 +1,127 @@ +project( + 'Hyprland', + 'cpp', + 'c', + version: run_command('cat', join_paths(meson.project_source_root(), 'VERSION'), check: true).stdout().strip(), + default_options: [ + 'warning_level=2', + 'default_library=static', + 'optimization=3', + 'buildtype=release', + 'debug=false', + 'cpp_std=c++26', + ], +) + +datarootdir = '-DDATAROOTDIR="' + get_option('prefix') / get_option('datadir') + '"' +add_project_arguments( + [ + '-Wno-unused-parameter', + '-Wno-unused-value', + '-Wno-missing-field-initializers', + '-Wno-narrowing', + '-Wno-pointer-arith', datarootdir, + '-DHYPRLAND_VERSION="' + meson.project_version() + '"', + ], + language: 'cpp', +) + +cpp_compiler = meson.get_compiler('cpp') +if cpp_compiler.check_header('execinfo.h') + add_project_arguments('-DHAS_EXECINFO', language: 'cpp') +endif + +aquamarine = dependency('aquamarine', version: '>=0.8.0') +hyprcursor = dependency('hyprcursor', version: '>=0.1.7') +hyprgraphics = dependency('hyprgraphics', version: '>= 0.1.1') +hyprlang = dependency('hyprlang', version: '>= 0.3.2') +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') +add_project_arguments(['-DAQUAMARINE_VERSION_MINOR=@0@'.format(aquamarine_version_list.get(1))], language: 'cpp') +add_project_arguments(['-DAQUAMARINE_VERSION_PATCH=@0@'.format(aquamarine_version_list.get(2))], language: 'cpp') +add_project_arguments(['-DHYPRCURSOR_VERSION="@0@"'.format(hyprcursor.version())], language: 'cpp') +add_project_arguments(['-DHYPRGRAPHICS_VERSION="@0@"'.format(hyprgraphics.version())], language: 'cpp') +add_project_arguments(['-DHYPRLANG_VERSION="@0@"'.format(hyprlang.version())], language: 'cpp') +add_project_arguments(['-DHYPRUTILS_VERSION="@0@"'.format(hyprutils.version())], language: 'cpp') + +xcb_dep = dependency('xcb', required: get_option('xwayland')) +xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland')) +xcb_errors_dep = dependency('xcb-errors', required: get_option('xwayland')) +xcb_icccm_dep = dependency('xcb-icccm', required: get_option('xwayland')) +xcb_render_dep = dependency('xcb-render', required: get_option('xwayland')) +xcb_res_dep = dependency('xcb-res', required: get_option('xwayland')) +xcb_xfixes_dep = dependency('xcb-xfixes', required: get_option('xwayland')) + +gio_dep = dependency('gio-2.0', required: true) + +if not xcb_dep.found() + add_project_arguments('-DNO_XWAYLAND', language: 'cpp') +endif + +backtrace_dep = cpp_compiler.find_library('execinfo', required: false) +epoll_dep = dependency('epoll-shim', required: false) # timerfd on BSDs +inotify_dep = dependency('libinotify', required: false) # inotify on BSDs + +re2 = dependency('re2', required: true) + +# Handle options +systemd_option = get_option('systemd') +systemd = dependency('systemd', required: systemd_option) +systemd_option.enable_auto_if(systemd.found()) + +if (systemd_option.enabled()) + message('Enabling systemd integration') + add_project_arguments('-DUSES_SYSTEMD', language: 'cpp') + 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) + +# Install headers +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) +endforeach + +tracy = dependency('tracy', static: true, required: get_option('tracy_enable')) + +if get_option('tracy_enable') and get_option('buildtype') != 'debugoptimized' + warning('Profiling builds should set -- buildtype = debugoptimized') +endif + + + +subdir('protocols') +subdir('src') +subdir('hyprctl') +subdir('assets') +subdir('example') +subdir('docs') + +if get_option('hyprpm').enabled() + subdir('hyprpm/src') +endif + +# Generate hyprland.pc +pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig') + +import('pkgconfig').generate( + name: 'Hyprland', + filebase: 'hyprland', + url: 'https://github.com/hyprwm/Hyprland', + description: 'Hyprland header files', + install_dir: pkg_install_dir, + subdirs: ['', 'hyprland/protocols', 'hyprland'], +) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..3eb01696 --- /dev/null +++ b/meson_options.txt @@ -0,0 +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') diff --git a/nix/default.nix b/nix/default.nix index bc5ba309..2d9ee99f 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -6,37 +6,27 @@ pkgconf, makeWrapper, cmake, + meson, + ninja, aquamarine, binutils, cairo, - epoll-shim, git, - glaze-hyprland, - glslang, - gtest, + glaze, hyprcursor, hyprgraphics, hyprland-protocols, - hyprland-guiutils, + hyprland-qtutils, hyprlang, hyprutils, hyprwayland-scanner, - hyprwire, - lcms2, libGL, libdrm, libexecinfo, libinput, - libxcb, - libxcb-errors, - libxcb-render-util, - libxcb-wm, - libxdmcp, - libxcursor, libxkbcommon, libuuid, libgbm, - muparser, pango, pciutils, re2, @@ -46,10 +36,11 @@ wayland, wayland-protocols, wayland-scanner, + xorg, xwayland, debug ? false, - withTests ? false, enableXWayland ? true, + legacyRenderer ? false, withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd, wrapRuntimeDeps ? true, version ? "git", @@ -60,27 +51,13 @@ enableNvidiaPatches ? false, nvidiaPatches ? false, hidpiXWayland ? false, - legacyRenderer ? false, - withHyprtester ? 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 - cmakeBool - trim - ; - fs = lib.fileset; + inherit (lib.lists) flatten concatLists optional optionals; + inherit (lib.sources) cleanSourceWith cleanSource; + inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable trim; adapters = flatten [ stdenvAdapters.useMoldLinker @@ -91,64 +68,32 @@ 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 (!withHyprtester) "The option `withHyprtester` has been removed. Hyprtester is always built now."; + 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 withTests; + inherit version; - src = fs.toSource { - root = ../.; - fileset = - fs.intersection - # allows non-flake builds to only include files tracked by git - (fs.gitTracked ../.) - ( - fs.unions (flatten [ - ../assets/hyprland-portals.conf - ../assets/install - ../hyprctl - ../hyprland.pc.in - ../hyprpm - ../LICENSE - ../protocols - ../src - ../start - ../systemd - ../VERSION - (fs.fileFilter (file: file.hasExt "1") ../docs) - (fs.fileFilter (file: file.hasExt "conf" || file.hasExt "in") ../example) - (fs.fileFilter (file: file.hasExt "sh") ../scripts) - (fs.fileFilter (file: file.name == "CMakeLists.txt") ../.) - (optional withTests [ - ../tests - ../hyprtester - ]) - ]) - ); + src = cleanSourceWith { + filter = name: _type: let + baseName = baseNameOf (toString name); + in + ! (hasSuffix ".nix" baseName); + src = cleanSource ../.; }; postPatch = '' # Fix hardcoded paths to /usr installation sed -i "s#/usr#$out#" src/render/OpenGL.cpp - # Remove extra @PREFIX@ to fix some paths + # Remove extra @PREFIX@ to fix pkg-config paths sed -i "s#@PREFIX@/##g" hyprland.pc.in - sed -i "s#@PREFIX@/##g" example/hyprland.desktop.in ''; - env = { - GIT_COMMITS = revCount; - GIT_COMMIT_DATE = date; - GIT_COMMIT_HASH = commit; - GIT_DIRTY = - if (commit == "") - then "clean" - else "dirty"; - GIT_TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}"; - }; + COMMITS = revCount; + DATE = date; + DIRTY = optionalString (commit == "") "dirty"; + HASH = commit; + TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}"; depsBuildBuild = [ pkg-config @@ -156,9 +101,10 @@ in nativeBuildInputs = [ hyprwayland-scanner - hyprwire makeWrapper - cmake + meson + ninja + cmake # needed for glaze pkg-config ]; @@ -173,24 +119,18 @@ in aquamarine cairo git - glaze-hyprland - glslang - gtest + glaze hyprcursor hyprgraphics hyprland-protocols hyprlang hyprutils - hyprwire - lcms2 libdrm - libgbm libGL libinput libuuid - libxcursor libxkbcommon - muparser + libgbm pango pciutils re2 @@ -199,70 +139,51 @@ in wayland wayland-protocols wayland-scanner + xorg.libXcursor ] - (optionals customStdenv.hostPlatform.isBSD [epoll-shim]) (optionals customStdenv.hostPlatform.isMusl [libexecinfo]) (optionals enableXWayland [ - libxcb - libxcb-errors - libxcb-render-util - libxcb-wm - libxdmcp + xorg.libxcb + xorg.libXdmcp + xorg.xcbutilerrors + xorg.xcbutilrenderutil + xorg.xcbutilwm xwayland ]) (optional withSystemd systemd) ]; - strictDeps = true; - - cmakeBuildType = + mesonBuildType = if debug - then "Debug" - else "RelWithDebInfo"; + then "debug" + else "release"; - # we want as much debug info as possible - dontStrip = debug; - - cmakeFlags = mapAttrsToList cmakeBool { - "BUILT_WITH_NIX" = true; - "NO_XWAYLAND" = !enableXWayland; - "LEGACY_RENDERER" = legacyRenderer; - "NO_SYSTEMD" = !withSystemd; - "CMAKE_DISABLE_PRECOMPILE_HEADERS" = true; - "NO_UWSM" = !withSystemd; - "TRACY_ENABLE" = false; - "WITH_TESTS" = withTests; - }; - - preConfigure = '' - substituteInPlace hyprtester/CMakeLists.txt --replace-fail \ - "\''${CMAKE_CURRENT_BINARY_DIR}" \ - "${placeholder "out"}/bin" - ''; + mesonFlags = flatten [ + (mapAttrsToList mesonEnable { + "xwayland" = enableXWayland; + "legacy_renderer" = legacyRenderer; + "uwsm" = false; + "hyprpm" = false; + }) + (mapAttrsToList mesonBool { + "b_pch" = false; + "tracy_enable" = false; + }) + ]; postInstall = '' ${optionalString wrapRuntimeDeps '' wrapProgram $out/bin/Hyprland \ - --suffix PATH : ${ - makeBinPath [ - binutils - hyprland-guiutils - pciutils - pkgconf - ] - } - ''} - - ${optionalString withTests '' - install hyprtester/pointer-warp -t $out/bin - install hyprtester/pointer-scroll -t $out/bin - install hyprtester/shortcut-inhibitor -t $out/bin - install hyprland_gtests -t $out/bin - install hyprtester/child-window -t $out/bin + --suffix PATH : ${makeBinPath [ + binutils + hyprland-qtutils + pciutils + pkgconf + ]} ''} ''; - passthru.providedSessions = ["hyprland"] ++ optionals withSystemd ["hyprland-uwsm"]; + passthru.providedSessions = ["hyprland"]; meta = { homepage = "https://github.com/hyprwm/Hyprland"; diff --git a/nix/formatter.nix b/nix/formatter.nix index ac340ae2..66721c2c 100644 --- a/nix/formatter.nix +++ b/nix/formatter.nix @@ -2,7 +2,7 @@ writeShellApplication, deadnix, statix, - nixfmt, + alejandra, llvmPackages_19, fd, }: @@ -11,7 +11,7 @@ writeShellApplication { runtimeInputs = [ deadnix statix - nixfmt + alejandra llvmPackages_19.clang-tools fd ]; @@ -24,14 +24,14 @@ writeShellApplication { nix_format() { if [ "$*" = 0 ]; then fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \; - fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X nixfmt {} \; + fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X alejandra {} \; elif [ -d "$1" ]; then fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \; - fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X nixfmt {} \; + fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X alejandra {} \; else statix fix -- "$1" deadnix -e "$1" - nixfmt "$1" + alejandra "$1" fi } diff --git a/nix/hm-module.nix b/nix/hm-module.nix index 948b8217..e3c788d0 100644 --- a/nix/hm-module.nix +++ b/nix/hm-module.nix @@ -1,15 +1,13 @@ -self: -{ +self: { + config, lib, pkgs, ... -}: -let +}: let inherit (pkgs.stdenv.hostPlatform) system; package = self.packages.${system}.default; -in -{ +in { config = { wayland.windowManager.hyprland.package = lib.mkDefault package; }; diff --git a/nix/lib.nix b/nix/lib.nix index 54d23440..cb5223b9 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,5 +1,4 @@ -lib: -let +lib: let inherit (lib) attrNames filterAttrs @@ -18,7 +17,7 @@ let This function takes a nested attribute set and converts it into Hyprland-compatible configuration syntax, supporting top, bottom, and regular command sections. - + Commands are flattened using the `flattenAttrs` function, and attributes are formatted as `key = value` pairs. Lists are expanded as duplicate keys to match Hyprland's expected format. @@ -82,51 +81,44 @@ let ::: */ - toHyprlang = - { - topCommandsPrefixes ? [ - "$" - "bezier" - ], - bottomCommandsPrefixes ? [ ], - }: - attrs: - let - toHyprlang' = - attrs: - let - # Specially configured `toKeyValue` generator with support for duplicate keys - # and a legible key-value separator. - mkCommands = generators.toKeyValue { - mkKeyValue = generators.mkKeyValueDefault { } " = "; - listsAsDuplicateKeys = true; - indent = ""; # No indent, since we don't have nesting - }; + toHyprlang = { + topCommandsPrefixes ? ["$"], + bottomCommandsPrefixes ? [], + }: attrs: let + toHyprlang' = attrs: let + # Specially configured `toKeyValue` generator with support for duplicate keys + # and a legible key-value separator. + mkCommands = generators.toKeyValue { + mkKeyValue = generators.mkKeyValueDefault {} " = "; + listsAsDuplicateKeys = true; + indent = ""; # No indent, since we don't have nesting + }; - # Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`. - # Uses `flattenAttrs` with a colon separator. - commands = flattenAttrs (p: k: "${p}:${k}") attrs; + # Flatten the attrset, combining keys in a "path" like `"a:b:c" = "x"`. + # Uses `flattenAttrs` with a colon separator. + commands = flattenAttrs (p: k: "${p}:${k}") attrs; - # General filtering function to check if a key starts with any prefix in a given list. - filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list; + # General filtering function to check if a key starts with any prefix in a given list. + filterCommands = list: n: + foldl (acc: prefix: acc || hasPrefix prefix n) false list; - # Partition keys into top commands and the rest - result = partition (filterCommands topCommandsPrefixes) (attrNames commands); - topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; - remainingCommands = removeAttrs commands result.right; + # Partition keys into top commands and the rest + result = partition (filterCommands topCommandsPrefixes) (attrNames commands); + topCommands = filterAttrs (n: _: builtins.elem n result.right) commands; + remainingCommands = removeAttrs commands result.right; - # Partition remaining commands into bottom commands and regular commands - result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; - bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; - regularCommands = removeAttrs remainingCommands result2.right; - in - # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. - concatMapStrings mkCommands [ - topCommands - regularCommands - bottomCommands - ]; + # Partition remaining commands into bottom commands and regular commands + result2 = partition (filterCommands bottomCommandsPrefixes) result.wrong; + bottomCommands = filterAttrs (n: _: builtins.elem n result2.right) remainingCommands; + regularCommands = removeAttrs remainingCommands result2.right; in + # Concatenate strings from mapping `mkCommands` over top, regular, and bottom commands. + concatMapStrings mkCommands [ + topCommands + regularCommands + bottomCommands + ]; + in toHyprlang' attrs; /** @@ -139,7 +131,7 @@ let Configuration: * `pred` - A function `(string -> string -> string)` defining how keys should be concatenated. - + # Inputs Structured function argument: @@ -147,7 +139,7 @@ let : pred (required) : A function that determines how parent and child keys should be combined into a single key. It takes a `prefix` (parent key) and `key` (current key) and returns the joined key. - + Value: : The nested attribute set to be flattened. @@ -182,21 +174,26 @@ let ``` ::: + */ - flattenAttrs = - pred: attrs: - let - flattenAttrs' = - prefix: attrs: - builtins.foldl' ( - acc: key: - let - value = attrs.${key}; - newKey = if prefix == "" then key else pred prefix key; - in - acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; }) - ) { } (builtins.attrNames attrs); - in + flattenAttrs = pred: attrs: let + flattenAttrs' = prefix: attrs: + builtins.foldl' ( + acc: key: let + value = attrs.${key}; + newKey = + if prefix == "" + then key + else pred prefix key; + in + acc + // ( + if builtins.isAttrs value + then flattenAttrs' newKey value + else {"${newKey}" = value;} + ) + ) {} (builtins.attrNames attrs); + in flattenAttrs' "" attrs; in { diff --git a/nix/module.nix b/nix/module.nix index 32263943..0b8c4f42 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -1,21 +1,18 @@ -inputs: -{ +inputs: { config, lib, pkgs, ... -}: -let +}: let inherit (pkgs.stdenv.hostPlatform) system; selflib = import ./lib.nix lib; cfg = config.programs.hyprland; -in -{ +in { options = { programs.hyprland = { plugins = lib.mkOption { type = with lib.types; listOf (either package path); - default = [ ]; + default = []; description = '' List of Hyprland plugins to use. Can either be packages or absolute plugin paths. @@ -23,29 +20,27 @@ in }; settings = lib.mkOption { - type = - with lib.types; - let - valueType = - nullOr (oneOf [ - bool - int - float - str - path - (attrsOf valueType) - (listOf valueType) - ]) - // { - description = "Hyprland configuration value"; - }; - in + type = with lib.types; let + valueType = + nullOr (oneOf [ + bool + int + float + str + path + (attrsOf valueType) + (listOf valueType) + ]) + // { + description = "Hyprland configuration value"; + }; + in valueType; - default = { }; + default = {}; description = '' Hyprland configuration written in Nix. Entries with the same key should be written as lists. Variables' and colors' names should be - quoted. See for more examples. + quoted. See for more examples. Special categories (e.g `devices`) should be written as `"devices[device-name]"`. @@ -97,15 +92,8 @@ in topPrefixes = lib.mkOption { type = with lib.types; listOf str; - default = [ - "$" - "bezier" - ]; - example = [ - "$" - "bezier" - "source" - ]; + default = ["$" "bezier"]; + example = ["$" "bezier" "source"]; description = '' List of prefix of attributes to put at the top of the config. ''; @@ -113,8 +101,8 @@ in bottomPrefixes = lib.mkOption { type = with lib.types; listOf str; - default = [ ]; - example = [ "source" ]; + default = []; + example = ["source"]; description = '' List of prefix of attributes to put at the bottom of the config. ''; @@ -129,36 +117,34 @@ in }; } (lib.mkIf cfg.enable { - environment.etc."xdg/hypr/hyprland.conf" = - let - shouldGenerate = cfg.extraConfig != "" || cfg.settings != { } || cfg.plugins != [ ]; + environment.etc."xdg/hypr/hyprland.conf" = let + shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != []; - pluginsToHyprlang = - _plugins: - selflib.toHyprlang - { + pluginsToHyprlang = plugins: + selflib.toHyprlang { + topCommandsPrefixes = cfg.topPrefixes; + bottomCommandsPrefixes = cfg.bottomPrefixes; + } + { + plugin = let + mkEntry = entry: + if lib.types.package.check entry + then "${entry}/lib/lib${entry.pname}.so" + else entry; + in + map mkEntry cfg.plugins; + }; + in + lib.mkIf shouldGenerate { + text = + lib.optionalString (cfg.plugins != []) + (pluginsToHyprlang cfg.plugins) + + lib.optionalString (cfg.settings != {}) + (selflib.toHyprlang { topCommandsPrefixes = cfg.topPrefixes; bottomCommandsPrefixes = cfg.bottomPrefixes; } - { - "exec-once" = - 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; - }; - in - lib.mkIf shouldGenerate { - text = - lib.optionalString (cfg.plugins != [ ]) (pluginsToHyprlang cfg.plugins) - + lib.optionalString (cfg.settings != { }) ( - selflib.toHyprlang { - topCommandsPrefixes = cfg.topPrefixes; - bottomCommandsPrefixes = cfg.bottomPrefixes; - } cfg.settings - ) + cfg.settings) + lib.optionalString (cfg.extraConfig != "") cfg.extraConfig; }; }) diff --git a/nix/overlays.nix b/nix/overlays.nix index 0d157701..b632d0b4 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -2,27 +2,20 @@ self, lib, inputs, -}: -let - mkDate = - longDate: - (lib.concatStringsSep "-" [ - (builtins.substring 0 4 longDate) - (builtins.substring 4 2 longDate) - (builtins.substring 6 2 longDate) - ]); - ver = lib.removeSuffix "\n" (builtins.readFile ../VERSION); -in -{ +}: let + mkDate = longDate: (lib.concatStringsSep "-" [ + (builtins.substring 0 4 longDate) + (builtins.substring 4 2 longDate) + (builtins.substring 6 2 longDate) + ]); + 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. - default = lib.composeManyExtensions ( - with self.overlays; - [ - hyprland-packages - hyprland-extras - ] - ); + default = lib.composeManyExtensions (with self.overlays; [ + hyprland-packages + hyprland-extras + ]); # Packages for variations of Hyprland, dependencies included. hyprland-packages = lib.composeManyExtensions [ @@ -31,65 +24,47 @@ in inputs.hyprcursor.overlays.default inputs.hyprgraphics.overlays.default inputs.hyprland-protocols.overlays.default - inputs.hyprland-guiutils.overlays.default + inputs.hyprland-qtutils.overlays.default inputs.hyprlang.overlays.default inputs.hyprutils.overlays.default inputs.hyprwayland-scanner.overlays.default - inputs.hyprwire.overlays.default self.overlays.udis86 - self.overlays.glaze # 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; - commit = self.rev or ""; - revCount = self.sourceInfo.revCount or ""; - inherit date version; - }; - hyprland-unwrapped = final.hyprland.override { wrapRuntimeDeps = false; }; + (final: _prev: let + date = mkDate (self.lastModifiedDate or "19700101"); + in { + hyprland = final.callPackage ./default.nix { + stdenv = final.gcc14Stdenv; + version = "${version}+date=${date}_${self.shortRev or "dirty"}"; + commit = self.rev or ""; + revCount = self.sourceInfo.revCount or ""; + inherit date; + }; + hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;}; - hyprland-with-tests = final.hyprland.override { withTests = true; }; + # 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;}; - hyprland-with-hyprtester = builtins.trace '' - hyprland-with-hyprtester was removed. Please use the hyprland package. - Hyprtester is always built now. - '' final.hyprland; - - # 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 '' + # deprecated packages + hyprland-nvidia = + builtins.trace '' hyprland-nvidia was removed. Please use the hyprland package. Nvidia patches are no longer needed. - '' final.hyprland; + '' + final.hyprland; - hyprland-hidpi = builtins.trace '' + hyprland-hidpi = + builtins.trace '' hyprland-hidpi was removed. Please use the hyprland package. - For more information, refer to https://wiki.hypr.land/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; }; + For more information, refer to https://wiki.hyprland.org/Configuring/XWayland. + '' + final.hyprland; }) ]; @@ -103,26 +78,15 @@ in # this version is the one used in the git submodule, and allows us to # fetch the source without '?submodules=1' udis86 = final: prev: { - udis86-hyprland = prev.udis86.overrideAttrs ( - _self: _super: { - src = final.fetchFromGitHub { - owner = "canihavesomecoffee"; - repo = "udis86"; - rev = "5336633af70f3917760a6d441ff02d93477b0c86"; - hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g="; - }; + udis86-hyprland = prev.udis86.overrideAttrs (_self: _super: { + src = final.fetchFromGitHub { + owner = "canihavesomecoffee"; + repo = "udis86"; + rev = "5336633af70f3917760a6d441ff02d93477b0c86"; + hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g="; + }; - patches = [ ]; - } - ); - }; - - # Even though glaze itself disables it by default, nixpkgs sets ENABLE_SSL set to true. - # Since we don't include openssl, the build failes without the `enableSSL = false;` override - glaze = _final: prev: { - glaze-hyprland = prev.glaze.override { - enableSSL = false; - enableInterop = false; - }; + patches = []; + }); }; } diff --git a/nix/tests/default.nix b/nix/tests/default.nix deleted file mode 100644 index 25c4077b..00000000 --- a/nix/tests/default.nix +++ /dev/null @@ -1,105 +0,0 @@ -inputs: pkgs: -let - flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}; - hyprland = flake.hyprland-with-tests; -in -{ - tests = pkgs.testers.runNixOSTest { - name = "hyprland-tests"; - - nodes.machine = - { pkgs, ... }: - { - environment.systemPackages = with pkgs; [ - # Programs needed for tests - jq - kitty - wl-clipboard - 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"; - "KITTY_CONFIG_DIRECTORY" = "/etc/kitty"; - }; - - environment.etc."kitty/kitty.conf".text = '' - confirm_os_window_close 0 - remember_window_size no - initial_window_width 640 - initial_window_height 400 - ''; - - 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 = "${hyprland}/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 gtests - print("Running gtests") - exit_status, _out = machine.execute("su - alice -c 'hyprland_gtests 2>&1 | tee /tmp/gtestslog; exit ''${PIPESTATUS[0]}'") - machine.execute(f'echo {exit_status} > /tmp/exit_status_gtests') - - # 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 ${hyprland}/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/gtestslog") - 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() - ''; - }; -} diff --git a/nix/update-inputs.sh b/nix/update-inputs.sh index c34e40f6..eb4b7dd7 100755 --- a/nix/update-inputs.sh +++ b/nix/update-inputs.sh @@ -1,25 +1,16 @@ #!/usr/bin/env -S nix shell nixpkgs#jq -c bash -# Update inputs when the Mesa or QT version is outdated. We don't want +# Update inputs when the Mesa version is outdated. We don't want # incompatibilities between the user's system and Hyprland. # get the current Nixpkgs revision REV=$(jq $MESA_NEW" - echo "Qt: $QT_OLD -> $QT_NEW" +if [ "$CRT_VER" != "$NEW_VER" ]; then + echo "Updating Mesa $CRT_VER -> $NEW_VER and flake inputs" # update inputs to latest versions nix flake update diff --git a/protocols/frog-color-management-v1.xml b/protocols/frog-color-management-v1.xml new file mode 100644 index 00000000..aab235a7 --- /dev/null +++ b/protocols/frog-color-management-v1.xml @@ -0,0 +1,366 @@ + + + + + Copyright © 2023 Joshua Ashton for Valve Software + Copyright © 2023 Xaver Hugl + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + The aim of this color management extension is to get HDR games working quickly, + and have an easy way to test implementations in the wild before the upstream + protocol is ready to be merged. + For that purpose it's intentionally limited and cut down and does not serve + all uses cases. + + + + + The color management factory singleton creates color managed surface objects. + + + + + + + + + + + + + + + + Interface for changing surface color management and HDR state. + + An implementation must: support every part of the version + of the frog_color_managed_surface interface it exposes. + Including all known enums associated with a given version. + + + + + Destroying the color managed surface resets all known color + state for the surface back to 'undefined' implementation-specific + values. + + + + + + Extended information on the transfer functions described + here can be found in the Khronos Data Format specification: + https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + Extended information on render intents described + here can be found in ICC.1:2022: + + https://www.color.org/specification/ICC.1-2022-05.pdf + + + + + + + NOTE: On a surface with "perceptual" (default) render intent, handling of the container's + color volume + is implementation-specific, and may differ between different transfer functions it is paired + with: + ie. sRGB + 709 rendering may have it's primaries widened to more of the available display's + gamut + to be be more pleasing for the viewer. + Compared to scRGB Linear + 709 being treated faithfully as 709 + (including utilizing negatives out of the 709 gamut triangle) + + + + + + + Forwards HDR metadata from the client to the compositor. + + HDR Metadata Infoframe as per CTA 861.G spec. + + Usage of this HDR metadata is implementation specific and + outside of the scope of this protocol. + + + + Mastering Red Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Red Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Green Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Green Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Blue Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering Blue Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering White Point X Coordinate of the Data. + + These are coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Mastering White Point Y Coordinate of the Data. + + These are coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Max Mastering Display Luminance. + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + Min Mastering Display Luminance. + This value is coded as an unsigned 16-bit value in units of + 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF + represents 6.5535 cd/m2. + + + + + Max Content Light Level. + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + Max Frame Average Light Level. + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + + + Current preferred metadata for a surface. + The application should use this information to tone-map its buffers + to this target before committing. + + This metadata does not necessarily correspond to any physical output, but + rather what the compositor thinks would be best for a given surface. + + + + Specifies a known transfer function that corresponds to the + output the surface is targeting. + + + + + Output Red Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Red Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Green Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Green Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Blue Color Primary X Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output Blue Color Primary Y Coordinate of the Data. + + Coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output White Point X Coordinate of the Data. + + These are coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Output White Point Y Coordinate of the Data. + + These are coded as unsigned 16-bit values in units of + 0.00002, where 0x0000 represents zero and 0xC350 + represents 1.0000. + + + + + Max Output Luminance + The max luminance in nits that the output is capable of rendering in small areas. + Content should: not exceed this value to avoid clipping. + + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + Min Output Luminance + The min luminance that the output is capable of rendering. + Content should: not exceed this value to avoid clipping. + + This value is coded as an unsigned 16-bit value in units of + 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF + represents 6.5535 cd/m2. + + + + + Max Full Frame Luminance + The max luminance in nits that the output is capable of rendering for the + full frame sustained. + + This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + + + + + \ No newline at end of file diff --git a/protocols/meson.build b/protocols/meson.build new file mode 100644 index 00000000..97594f06 --- /dev/null +++ b/protocols/meson.build @@ -0,0 +1,111 @@ +wayland_protos = dependency( + 'wayland-protocols', + version: '>=1.41', + fallback: 'wayland-protocols', + default_options: ['tests=false'], +) + +hyprland_protos = dependency( + 'hyprland-protocols', + version: '>=0.6.2', + fallback: 'hyprland-protocols', +) + +wayland_protocol_dir = wayland_protos.get_variable('pkgdatadir') +hyprland_protocol_dir = hyprland_protos.get_variable('pkgdatadir') + +hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.10', native: true) +hyprwayland_scanner = find_program( + hyprwayland_scanner_dep.get_variable('hyprwayland_scanner'), + native: true, +) + +protocols = [ + 'wlr-gamma-control-unstable-v1.xml', + 'wlr-foreign-toplevel-management-unstable-v1.xml', + 'wlr-output-power-management-unstable-v1.xml', + 'input-method-unstable-v2.xml', + 'virtual-keyboard-unstable-v1.xml', + 'wlr-virtual-pointer-unstable-v1.xml', + 'wlr-output-management-unstable-v1.xml', + 'kde-server-decoration.xml', + 'wlr-layer-shell-unstable-v1.xml', + 'wayland-drm.xml', + 'wlr-data-control-unstable-v1.xml', + 'wlr-screencopy-unstable-v1.xml', + 'xx-color-management-v4.xml', + '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-focus-grab-v1.xml', + hyprland_protocol_dir / 'protocols/hyprland-ctm-control-v1.xml', + hyprland_protocol_dir / 'protocols/hyprland-surface-v1.xml', + hyprland_protocol_dir / 'protocols/hyprland-lock-notify-v1.xml', + wayland_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', + wayland_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', + wayland_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', + wayland_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', + wayland_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml', + wayland_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml', + wayland_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml', + wayland_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml', + wayland_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', + wayland_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml', + wayland_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml', + wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml', + wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v1.xml', + wayland_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', + wayland_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml', + wayland_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', + wayland_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', + wayland_protocol_dir / 'stable/tablet/tablet-v2.xml', + wayland_protocol_dir / 'stable/presentation-time/presentation-time.xml', + wayland_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', + wayland_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml', + wayland_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', + wayland_protocol_dir / 'stable/viewporter/viewporter.xml', + wayland_protocol_dir / 'stable/linux-dmabuf/linux-dmabuf-v1.xml', + wayland_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', + wayland_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml', + wayland_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml', + wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml', + wayland_protocol_dir / 'staging/security-context/security-context-v1.xml', + wayland_protocol_dir / 'staging/content-type/content-type-v1.xml', + wayland_protocol_dir / 'staging/color-management/color-management-v1.xml', +] + +wl_protocols = [] +foreach protocol : protocols + wl_protocols += custom_target( + protocol.underscorify(), + input: protocol, + install: true, + install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')], + output: ['@BASENAME@.cpp', '@BASENAME@.hpp'], + command: [hyprwayland_scanner, '@INPUT@', '@OUTDIR@'], + ) +endforeach + +# wayland.xml generation +wayland_scanner = dependency('wayland-scanner') +wayland_scanner_datadir = wayland_scanner.get_variable('pkgdatadir') + +wayland_xml = wayland_scanner_datadir / 'wayland.xml' +wayland_protocol = custom_target( + wayland_xml.underscorify(), + input: wayland_xml, + install: true, + install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')], + output: ['@BASENAME@.cpp', '@BASENAME@.hpp'], + command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'], +) + +lib_server_protos = static_library( + 'server_protos', + wl_protocols + wayland_protocol, +) + +server_protos = declare_dependency( + link_with: lib_server_protos, + sources: wl_protocols + wayland_protocol, +) diff --git a/protocols/xx-color-management-v4.xml b/protocols/xx-color-management-v4.xml new file mode 100644 index 00000000..23ff716e --- /dev/null +++ b/protocols/xx-color-management-v4.xml @@ -0,0 +1,1457 @@ + + + + Copyright 2019 Sebastian Wick + Copyright 2019 Erwin Burema + Copyright 2020 AMD + Copyright 2020-2024 Collabora, Ltd. + Copyright 2024 Xaver Hugl + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + The aim of the color management extension is to allow clients to know + the color properties of outputs, and to tell the compositor about the color + properties of their content on surfaces. Doing this enables a compositor + to perform automatic color management of content for different outputs + according to how content is intended to look like. + + The color properties are represented as an image description object which + is immutable after it has been created. A wl_output always has an + associated image description that clients can observe. A wl_surface + always has an associated preferred image description as a hint chosen by + the compositor that clients can also observe. Clients can set an image + description on a wl_surface to denote the color characteristics of the + surface contents. + + An image description includes SDR and HDR colorimetry and encoding, HDR + metadata, and viewing environment parameters. An image description does + not include the properties set through color-representation extension. + It is expected that the color-representation extension is used in + conjunction with the color management extension when necessary, + particularly with the YUV family of pixel formats. + + Recommendation ITU-T H.273 + "Coding-independent code points for video signal type identification" + shall be referred to as simply H.273 here. + + The color-and-hdr repository + (https://gitlab.freedesktop.org/pq/color-and-hdr) contains + background information on the protocol design and legacy color management. + It also contains a glossary, learning resources for digital color, tools, + samples and more. + + The terminology used in this protocol is based on common color science and + color encoding terminology where possible. The glossary in the color-and-hdr + repository shall be the authority on the definition of terms in this + protocol. + + + + + A global interface used for getting color management extensions for + wl_surface and wl_output objects, and for creating client defined image + description objects. The extension interfaces allow + getting the image description of outputs and setting the image + description of surfaces. + + + + + Destroy the xx_color_manager_v4 object. This does not affect any other + objects in any way. + + + + + + + + + + + See the ICC.1:2022 specification from the International Color Consortium + for more details about rendering intents. + + The principles of ICC defined rendering intents apply with all types of + image descriptions, not only those with ICC file profiles. + + Compositors must support the perceptual rendering intent. Other + rendering intents are optional. + + + + + + + + + + + + + + + + + + + + The compositor supports set_mastering_display_primaries request with a + target color volume fully contained inside the primary color volume. + + + + + The compositor additionally supports target color volumes that + extend outside of the primary color volume. + + This can only be advertised if feature set_mastering_display_primaries + is supported as well. + + + + + + + Named color primaries used to encode well-known sets of primaries. H.273 + is the authority, when it comes to the exact values of primaries and + authoritative specifications, where an equivalent code point exists. + + Descriptions do list the specifications for convenience. + + + + + Color primaries as defined by + - Rec. ITU-R BT.709-6 + - Rec. ITU-R BT.1361-0 conventional colour gamut system and extended + colour gamut system (historical) + - IEC 61966-2-1 sRGB or sYCC + - IEC 61966-2-4 + - Society of Motion Picture and Television Engineers (SMPTE) RP 177 + (1993) Annex B + Equivalent to H.273 ColourPrimaries code point 1. + + + + + Color primaries as defined by + - Rec. ITU-R BT.470-6 System M (historical) + - United States National Television System Committee 1953 + Recommendation for transmission standards for color television + - United States Federal Communications Commission (2003) Title 47 Code + of Federal Regulations 73.682 (a)(20) + Equivalent to H.273 ColourPrimaries code point 4. + + + + + Color primaries as defined by + - Rec. ITU-R BT.470-6 System B, G (historical) + - Rec. ITU-R BT.601-7 625 + - Rec. ITU-R BT.1358-0 625 (historical) + - Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM + Equivalent to H.273 ColourPrimaries code point 5. + + + + + Color primaries as defined by + - Rec. ITU-R BT.601-7 525 + - Rec. ITU-R BT.1358-1 525 or 625 (historical) + - Rec. ITU-R BT.1700-0 NTSC + - SMPTE 170M (2004) + - SMPTE 240M (1999) (historical) + Equivalent to H.273 ColourPrimaries code point 6 and 7. + + + + + Color primaries as defined by H.273 for generic film. + Equivalent to H.273 ColourPrimaries code point 8. + + + + + Color primaries as defined by + - Rec. ITU-R BT.2020-2 + - Rec. ITU-R BT.2100-0 + Equivalent to H.273 ColourPrimaries code point 9. + + + + + Color primaries as defined as the maximum of the CIE 1931 XYZ color + space by + - SMPTE ST 428-1 + - (CIE 1931 XYZ as in ISO 11664-1) + Equivalent to H.273 ColourPrimaries code point 10. + + + + + Color primaries as defined by Digital Cinema System and published in + SMPTE RP 431-2 (2011). Equivalent to H.273 ColourPrimaries code point + 11. + + + + + Color primaries as defined by Digital Cinema System and published in + SMPTE EG 432-1 (2010). + Equivalent to H.273 ColourPrimaries code point 12. + + + + + Color primaries as defined by Adobe as "Adobe RGB" and later published + by ISO 12640-4 (2011). + + + + + + + Named transfer functions used to encode well-known transfer + characteristics. H.273 is the authority, when it comes to the exact + formulas and authoritative specifications, where an equivalent code + point exists. + + Descriptions do list the specifications for convenience. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.709-6 + - Rec. ITU-R BT.1361-0 conventional colour gamut system (historical) + Equivalent to H.273 TransferCharacteristics code point 1, 6, 14, 15. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.470-6 System M (historical) + - United States National Television System Committee 1953 + Recommendation for transmission standards for color television + - United States Federal Communications Commission (2003) Title 47 Code + of Federal Regulations 73.682 (a) (20) + - Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM + Equivalent to H.273 TransferCharacteristics code point 4. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.470-6 System B, G (historical) + Equivalent to H.273 TransferCharacteristics code point 5. + + + + + Transfer characteristics as defined by + - SMPTE ST 240 (1999) + Equivalent to H.273 TransferCharacteristics code point 7. + + + + + Linear transfer characteristics. + Equivalent to H.273 TransferCharacteristics code point 8. + + + + + Logarithmic transfer characteristic (100:1 range). + Equivalent to H.273 TransferCharacteristics code point 9. + + + + + Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range). + Equivalent to H.273 TransferCharacteristics code point 10. + + + + + Transfer characteristics as defined by + - IEC 61966-2-4 + Equivalent to H.273 TransferCharacteristics code point 11. + + + + + Transfer characteristics as defined by + - Rec. ITU-R BT.1361-0 extended colour gamut system (historical) + Equivalent to H.273 TransferCharacteristics code point 12. + + + + + Transfer characteristics as defined by + - IEC 61966-2-1 sRGB + Equivalent to H.273 TransferCharacteristics code point 13 with + MatrixCoefficients set to 0. + + + + + Transfer characteristics as defined by + - IEC 61966-2-1 sYCC + Equivalent to H.273 TransferCharacteristics code point 13 with + MatrixCoefficients set to anything but 0. + + + + + Transfer characteristics as defined by + - SMPTE ST 2084 (2014) for 10-, 12-, 14- and 16-bit systems + - Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system + Equivalent to H.273 TransferCharacteristics code point 16. + + This TF implies these default luminances + - primary color volume minimum: 0.005 cd/m² + - primary color volume maximum: 10000 cd/m² + - reference white: 203 cd/m² + + + + + Transfer characteristics as defined by + - SMPTE ST 428-1 (2019) + Equivalent to H.273 TransferCharacteristics code point 17. + + + + + Transfer characteristics as defined by + - ARIB STD-B67 (2015) + - Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system + Equivalent to H.273 TransferCharacteristics code point 18. + + This TF implies these default luminances + - primary color volume minimum: 0.005 cd/m² + - primary color volume maximum: 1000 cd/m² + - reference white: 203 cd/m² + Note: HLG is a scene referred signal. All absolute luminance values + used here for HLG assume a 1000 cd/m² display. + + + + + + + This creates a new xx_color_management_output_v4 object for the + given wl_output. + + See the xx_color_management_output_v4 interface for more details. + + + + + + + + + If a xx_color_management_surface_v4 object already exists for the given + wl_surface, the protocol error surface_exists is raised. + + This creates a new color xx_color_management_surface_v4 object for the + given wl_surface. + + See the xx_color_management_surface_v4 interface for more details. + + + + + + + + + This creates a new color xx_color_management_feedback_surface_v4 object + for the given wl_surface. + + See the xx_color_management_feedback_surface_v4 interface for more + details. + + + + + + + + + Makes a new ICC-based image description creator object with all + properties initially unset. The client can then use the object's + interface to define all the required properties for an image description + and finally create a xx_image_description_v4 object. + + This request can be used when the compositor advertises + xx_color_manager_v4.feature.icc_v2_v4. + Otherwise this request raises the protocol error unsupported_feature. + + + + + + + + Makes a new parametric image description creator object with all + properties initially unset. The client can then use the object's + interface to define all the required properties for an image description + and finally create a xx_image_description_v4 object. + + This request can be used when the compositor advertises + xx_color_manager_v4.feature.parametric. + Otherwise this request raises the protocol error unsupported_feature. + + + + + + + + When this object is created, it shall immediately send this event once + for each rendering intent the compositor supports. + + + + + + + + When this object is created, it shall immediately send this event once + for each compositor supported feature listed in the enumeration. + + + + + + + + When this object is created, it shall immediately send this event once + for each named transfer function the compositor supports with the + parametric image description creator. + + + + + + + + When this object is created, it shall immediately send this event once + for each named set of primaries the compositor supports with the + parametric image description creator. + + + + + + + + + A xx_color_management_output_v4 describes the color properties of an + output. + + The xx_color_management_output_v4 is associated with the wl_output global + underlying the wl_output object. Therefore the client destroying the + wl_output object has no impact, but the compositor removing the output + global makes the xx_color_management_output_v4 object inert. + + + + + Destroy the color xx_color_management_output_v4 object. This does not + affect any remaining protocol objects. + + + + + + This event is sent whenever the image description of the output changed, + followed by one wl_output.done event common to output events across all + extensions. + + If the client wants to use the updated image description, it needs to do + get_image_description again, because image description objects are + immutable. + + + + + + This creates a new xx_image_description_v4 object for the current image + description of the output. There always is exactly one image description + active for an output so the client should destroy the image description + created by earlier invocations of this request. This request is usually + sent as a reaction to the image_description_changed event or when + creating a xx_color_management_output_v4 object. + + The image description of an output represents the color encoding the + output expects. There might be performance and power advantages, as well + as improved color reproduction, if a content update matches the image + description of the output it is being shown on. If a content update is + shown on any other output than the one it matches the image description + of, then the color reproduction on those outputs might be considerably + worse. + + The created xx_image_description_v4 object preserves the image + description of the output from the time the object was created. + + The resulting image description object allows get_information request. + + If this protocol object is inert, the resulting image description object + shall immediately deliver the xx_image_description_v4.failed event with + the no_output cause. + + If the interface version is inadequate for the output's image + description, meaning that the client does not support all the events + needed to deliver the crucial information, the resulting image + description object shall immediately deliver the + xx_image_description_v4.failed event with the low_version cause. + + Otherwise the object shall immediately deliver the ready event. + + + + + + + + + A xx_color_management_surface_v4 allows the client to set the color + space and HDR properties of a surface. + + If the wl_surface associated with the xx_color_management_surface_v4 is + destroyed, the xx_color_management_surface_v4 object becomes inert. + + + + + Destroy the xx_color_management_surface_v4 object and do the same as + unset_image_description. + + + + + + + + + + + + Set the image description of the underlying surface. The image + description and rendering intent are double-buffered state, see + wl_surface.commit. + + It is the client's responsibility to understand the image description + it sets on a surface, and to provide content that matches that image + description. Compositors might convert images to match their own or any + other image descriptions. + + Image description whose creation gracefully failed (received + xx_image_description_v4.failed) are forbidden in this request, and in + such case the protocol error image_description is raised. + + All image descriptions whose creation succeeded (received + xx_image_description_v4.ready) are allowed and must always be accepted + by the compositor. + + A rendering intent provides the client's preference on how content + colors should be mapped to each output. The render_intent value must + be one advertised by the compositor with + xx_color_manager_v4.render_intent event, otherwise the protocol error + render_intent is raised. + + By default, a surface does not have an associated image description + nor a rendering intent. The handling of color on such surfaces is + compositor implementation defined. Compositors should handle such + surfaces as sRGB but may handle them differently if they have specific + requirements. + + + + + + + + + This request removes any image description from the surface. See + set_image_description for how a compositor handles a surface without + an image description. This is double-buffered state, see + wl_surface.commit. + + + + + + + A xx_color_management_feedback_surface_v4 allows the client to get the + preferred color description of a surface. + + If the wl_surface associated with this object is destroyed, the + xx_color_management_feedback_surface_v4 object becomes inert. + + + + + Destroy the xx_color_management_feedback_surface_v4 object. + + + + + + + + + + + The preferred image description is the one which likely has the most + performance and/or quality benefits for the compositor if used by the + client for its wl_surface contents. This event is sent whenever the + compositor changes the wl_surface's preferred image description. + + This event is merely a notification. When the client wants to know + what the preferred image description is, it shall use the get_preferred + request. + + The preferred image description is not automatically used for anything. + It is only a hint, and clients may set any valid image description with + set_image_description but there might be performance and color accuracy + improvements by providing the wl_surface contents in the preferred + image description. Therefore clients that can, should render according + to the preferred image description + + + + + + If this protocol object is inert, the protocol error inert is raised. + + The preferred image description represents the compositor's preferred + color encoding for this wl_surface at the current time. There might be + performance and power advantages, as well as improved color + reproduction, if the image description of a content update matches the + preferred image description. + + This creates a new xx_image_description_v4 object for the currently + preferred image description for the wl_surface. The client should + stop using and destroy the image descriptions created by earlier + invocations of this request for the associated wl_surface. + This request is usually sent as a reaction to the preferred_changed + event or when creating a xx_color_management_feedback_surface_v4 object + if the client is capable of adapting to image descriptions. + + The created xx_image_description_v4 object preserves the preferred image + description of the wl_surface from the time the object was created. + + The resulting image description object allows get_information request. + + If the interface version is inadequate for the preferred image + description, meaning that the client does not support all the + events needed to deliver the crucial information, the resulting image + description object shall immediately deliver the + xx_image_description_v4.failed event with the low_version cause, + otherwise the object shall immediately deliver the ready event. + + + + + + + + + This type of object is used for collecting all the information required + to create a xx_image_description_v4 object from an ICC file. A complete + set of required parameters consists of these properties: + - ICC file + + Each required property must be set exactly once if the client is to create + an image description. The set requests verify that a property was not + already set. The create request verifies that all required properties are + set. There may be several alternative requests for setting each property, + and in that case the client must choose one of them. + + Once all properties have been set, the create request must be used to + create the image description object, destroying the creator in the + process. + + + + + + + + + + + + + + + Create an image description object based on the ICC information + previously set on this object. A compositor must parse the ICC data in + some undefined but finite amount of time. + + The completeness of the parameter set is verified. If the set is not + complete, the protocol error incomplete_set is raised. For the + definition of a complete set, see the description of this interface. + + If the particular combination of the information is not supported + by the compositor, the resulting image description object shall + immediately deliver the xx_image_description_v4.failed event with the + 'unsupported' cause. If a valid image description was created from the + information, the xx_image_description_v4.ready event will eventually + be sent instead. + + This request destroys the xx_image_description_creator_icc_v4 object. + + The resulting image description object does not allow get_information + request. + + + + + + + + Sets the ICC profile file to be used as the basis of the image + description. + + The data shall be found through the given fd at the given offset, having + the given length. The fd must seekable and readable. Violating these + requirements raises the bad_fd protocol error. + + If reading the data fails due to an error independent of the client, the + compositor shall send the xx_image_description_v4.failed event on the + created xx_image_description_v4 with the 'operating_system' cause. + + The maximum size of the ICC profile is 4 MB. If length is greater than + that or zero, the protocol error bad_size is raised. If offset + length + exceeds the file size, the protocol error out_of_file is raised. + + A compositor may read the file at any time starting from this request + and only until whichever happens first: + - If create request was issued, the xx_image_description_v4 object + delivers either failed or ready event; or + - if create request was not issued, this + xx_image_description_creator_icc_v4 object is destroyed. + + A compositor shall not modify the contents of the file, and the fd may + be sealed for writes and size changes. The client must ensure to its + best ability that the data does not change while the compositor is + reading it. + + The data must represent a valid ICC profile. The ICC profile version + must be 2 or 4, it must be a 3 channel profile and the class must be + Display or ColorSpace. Violating these requirements will not result in a + protocol error but will eventually send the + xx_image_description_v4.failed event on the created + xx_image_description_v4 with the 'unsupported' cause. + + See the International Color Consortium specification ICC.1:2022 for more + details about ICC profiles. + + If ICC file has already been set on this object, the protocol error + already_set is raised. + + + + + + + + + + + This type of object is used for collecting all the parameters required + to create a xx_image_description_v4 object. A complete set of required + parameters consists of these properties: + - transfer characteristic function (tf) + - chromaticities of primaries and white point (primary color volume) + + The following properties are optional and have a well-defined default + if not explicitly set: + - primary color volume luminance range + - reference white luminance level + - mastering display primaries and white point (target color volume) + - mastering luminance range + - maximum content light level + - maximum frame-average light level + + Each required property must be set exactly once if the client is to create + an image description. The set requests verify that a property was not + already set. The create request verifies that all required properties are + set. There may be several alternative requests for setting each property, + and in that case the client must choose one of them. + + Once all properties have been set, the create request must be used to + create the image description object, destroying the creator in the + process. + + + + + + + + + + + + + + + + + + Create an image description object based on the parameters previously + set on this object. + + The completeness of the parameter set is verified. If the set is not + complete, the protocol error incomplete_set is raised. For the + definition of a complete set, see the description of this interface. + + Also, the combination of the parameter set is verified. If the set is + not consistent, the protocol error inconsistent_set is raised. + + If the particular combination of the parameter set is not supported + by the compositor, the resulting image description object shall + immediately deliver the xx_image_description_v4.failed event with the + 'unsupported' cause. If a valid image description was created from the + parameter set, the xx_image_description_v4.ready event will eventually + be sent instead. + + This request destroys the xx_image_description_creator_params_v4 + object. + + The resulting image description object does not allow get_information + request. + + + + + + + + Sets the transfer characteristic using explicitly enumerated named + functions. + + When the resulting image description is attached to an image, the + content should be encoded and decoded according to the industry standard + practices for the transfer characteristic. + + Only names advertised with xx_color_manager_v4 event supported_tf_named + are allowed. Other values shall raise the protocol error invalid_tf. + + If transfer characteristic has already been set on this object, the + protocol error already_set is raised. + + + + + + + + Sets the color component transfer characteristic to a power curve with + the given exponent. This curve represents the conversion from electrical + to optical pixel or color values. + + When the resulting image description is attached to an image, the + content should be encoded with the inverse of the power curve. + + The curve exponent shall be multiplied by 10000 to get the argument eexp + value to carry the precision of 4 decimals. + + The curve exponent must be at least 1.0 and at most 10.0. Otherwise the + protocol error invalid_tf is raised. + + If transfer characteristic has already been set on this object, the + protocol error already_set is raised. + + This request can be used when the compositor advertises + xx_color_manager_v4.feature.set_tf_power. Otherwise this request raises + the protocol error unsupported_feature. + + + + + + + + Sets the color primaries and white point using explicitly named sets. + This describes the primary color volume which is the basis for color + value encoding. + + Only names advertised with xx_color_manager_v4 event + supported_primaries_named are allowed. Other values shall raise the + protocol error invalid_primaries. + + If primaries have already been set on this object, the protocol error + already_set is raised. + + + + + + + + Sets the color primaries and white point using CIE 1931 xy chromaticity + coordinates. This describes the primary color volume which is the basis + for color value encoding. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + If primaries have already been set on this object, the protocol error + already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v4.feature.set_primaries. Otherwise this request raises + the protocol error unsupported_feature. + + + + + + + + + + + + + + + Sets the primary color volume luminance range and the reference white + luminance level. + + The default luminances are + - primary color volume minimum: 0.2 cd/m² + - primary color volume maximum: 80 cd/m² + - reference white: 80 cd/m² + + Setting a named transfer characteristic can imply other default + luminances. + + The default luminances get overwritten when this request is used. + + 'min_lum' and 'max_lum' specify the minimum and maximum luminances of + the primary color volume as reproduced by the targeted display. + + 'reference_lum' specifies the luminance of the reference white as + reproduced by the targeted display, and reflects the targeted viewing + environment. + + Compositors should make sure that all content is anchored, meaning that + an input signal level of 'reference_lum' on one image description and + another input signal level of 'reference_lum' on another image + description should produce the same output level, even though the + 'reference_lum' on both image representations can be different. + + If 'max_lum' is less than the 'reference_lum', or 'reference_lum' is + less than or equal to 'min_lum', the protocol error invalid_luminance is + raised. + + The minimum luminance is multiplied by 10000 to get the argument + 'min_lum' value and carries precision of 4 decimals. The maximum + luminance and reference white luminance values are unscaled. + + If the primary color volume luminance range and the reference white + luminance level have already been set on this object, the protocol error + already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v4.feature.set_luminances. Otherwise this request + raises the protocol error unsupported_feature. + + + + + + + + + + Provides the color primaries and white point of the mastering display + using CIE 1931 xy chromaticity coordinates. This is compatible with the + SMPTE ST 2086 definition of HDR static metadata. + + The mastering display primaries define the target color volume. + + If mastering display primaries are not explicitly set, the target color + volume is assumed to be equal to the primary color volume. + + The target color volume is defined by all tristimulus values between 0.0 + and 1.0 (inclusive) of the color space defined by the given mastering + display primaries and white point. The colorimetry is identical between + the container color space and the mastering display color space, + including that no chromatic adaptation is applied even if the white + points differ. + + The target color volume can exceed the primary color volume to allow for + a greater color volume with an existing color space definition (for + example scRGB). It can be smaller than the primary color volume to + minimize gamut and tone mapping distances for big color spaces (HDR + metadata). + + To make use of the entire target color volume a suitable pixel format + has to be chosen (e.g. floating point to exceed the primary color + volume, or abusing limited quantization range as with xvYCC). + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + If mastering display primaries have already been set on this object, the + protocol error already_set is raised. + + This request can be used if the compositor advertises + xx_color_manager_v4.feature.set_mastering_display_primaries. Otherwise + this request raises the protocol error unsupported_feature. The + advertisement implies support only for target color volumes fully + contained within the primary color volume. + + If a compositor additionally supports target color volume exceeding the + primary color volume, it must advertise + xx_color_manager_v4.feature.extended_target_volume. If a client uses + target color volume exceeding the primary color volume and the + compositor does not support it, the result is implementation defined. + Compositors are recommended to detect this case and fail the image + description gracefully, but it may as well result in color artifacts. + + + + + + + + + + + + + + + Sets the luminance range that was used during the content mastering + process as the minimum and maximum absolute luminance L. This is + compatible with the SMPTE ST 2086 definition of HDR static metadata. + + The mastering luminance range is undefined by default. + + If max L is less than or equal to min L, the protocol error + invalid_luminance is raised. + + Min L value is multiplied by 10000 to get the argument min_lum value + and carry precision of 4 decimals. Max L value is unscaled for max_lum. + + + + + + + + + Sets the maximum content light level (max_cll) as defined by CTA-861-H. + + This can only be set when set_tf_cicp is used to set the transfer + characteristic to Rec. ITU-R BT.2100-2 perceptual quantization system. + Otherwise, 'create' request shall raise inconsistent_set protocol + error. + + max_cll is undefined by default. + + + + + + + + Sets the maximum frame-average light level (max_fall) as defined by + CTA-861-H. + + This can only be set when set_tf_cicp is used to set the transfer + characteristic to Rec. ITU-R BT.2100-2 perceptual quantization system. + Otherwise, 'create' request shall raise inconsistent_set protocol error. + + max_fall is undefined by default. + + + + + + + + + An image description carries information about the color encoding used on + a surface when attached to a wl_surface via + xx_color_management_surface_v4.set_image_description. A compositor can use + this information to decode pixel values into colorimetrically meaningful + quantities. + + Note, that the xx_image_description_v4 object is not ready to be used + immediately after creation. The object eventually delivers either the + 'ready' or the 'failed' event, specified in all requests creating it. The + object is deemed "ready" after receiving the 'ready' event. + + An object which is not ready is illegal to use, it can only be destroyed. + Any other request in this interface shall result in the 'not_ready' + protocol error. Attempts to use an object which is not ready through other + interfaces shall raise protocol errors defined there. + + Once created and regardless of how it was created, a + xx_image_description_v4 object always refers to one fixed image + description. It cannot change after creation. + + + + + Destroy this object. It is safe to destroy an object which is not ready. + + Destroying a xx_image_description_v4 object has no side-effects, not + even if a xx_color_management_surface_v4.set_image_description has not + yet been followed by a wl_surface.commit. + + + + + + + + + + + + + + + + + + + + + + If creating a xx_image_description_v4 object fails for a reason that is + not defined as a protocol error, this event is sent. + + The requests that create image description objects define whether and + when this can occur. Only such creation requests can trigger this event. + This event cannot be triggered after the image description was + successfully formed. + + Once this event has been sent, the xx_image_description_v4 object will + never become ready and it can only be destroyed. + + + + + + + + + Once this event has been sent, the xx_image_description_v4 object is + deemed "ready". Ready objects can be used to send requests and can be + used through other interfaces. + + Every ready xx_image_description_v4 protocol object refers to an + underlying image description record in the compositor. Multiple protocol + objects may end up referring to the same record. Clients may identify + these "copies" by comparing their id numbers: if the numbers from two + protocol objects are identical, the protocol objects refer to the same + image description record. Two different image description records + cannot have the same id number simultaneously. The id number does not + change during the lifetime of the image description record. + + The id number is valid only as long as the protocol object is alive. If + all protocol objects referring to the same image description record are + destroyed, the id number may be recycled for a different image + description record. + + Image description id number is not a protocol object id. Zero is + reserved as an invalid id number. It shall not be possible for a client + to refer to an image description by its id number in protocol. The id + numbers might not be portable between Wayland connections. + + This identity allows clients to de-duplicate image description records + and avoid get_information request if they already have the image + description information. + + + + + + + + Creates a xx_image_description_info_v4 object which delivers the + information that makes up the image description. + + Not all image description protocol objects allow get_information + request. Whether it is allowed or not is defined by the request that + created the object. If get_information is not allowed, the protocol + error no_information is raised. + + + + + + + + + Sends all matching events describing an image description object exactly + once and finally sends the 'done' event. + + Once a xx_image_description_info_v4 object has delivered a 'done' event it + is automatically destroyed. + + Every xx_image_description_info_v4 created from the same + xx_image_description_v4 shall always return the exact same data. + + + + + Signals the end of information events and destroys the object. + + + + + + The icc argument provides a file descriptor to the client which may be + memory-mapped to provide the ICC profile matching the image description. + The fd is read-only, and if mapped then it must be mapped with + MAP_PRIVATE by the client. + + The ICC profile version and other details are determined by the + compositor. There is no provision for a client to ask for a specific + kind of a profile. + + + + + + + + + + Delivers the primary color volume primaries and white point using CIE + 1931 xy chromaticity coordinates. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + + + + + + + + + + + + + + Delivers the primary color volume primaries and white point using an + explicitly enumerated named set. + + + + + + + + The color component transfer characteristic of this image description is + a pure power curve. This event provides the exponent of the power + function. This curve represents the conversion from electrical to + optical pixel or color values. + + The curve exponent has been multiplied by 10000 to get the argument eexp + value to carry the precision of 4 decimals. + + + + + + + + Delivers the transfer characteristic using an explicitly enumerated + named function. + + + + + + + + Delivers the primary color volume luminance range and the reference + white luminance level. + + The minimum luminance is multiplied by 10000 to get the argument + 'min_lum' value and carries precision of 4 decimals. The maximum + luminance and reference white luminance values are unscaled. + + + + + + + + + + Provides the color primaries and white point of the target color volume + using CIE 1931 xy chromaticity coordinates. This is compatible with the + SMPTE ST 2086 definition of HDR static metadata for mastering displays. + + While primary color volume is about how color is encoded, the target + color volume is the actually displayable color volume. If target color + volume is equal to the primary color volume, then this event is not + sent. + + Each coordinate value is multiplied by 10000 to get the argument value + to carry precision of 4 decimals. + + + + + + + + + + + + + + + Provides the luminance range that the image description is targeting as + the minimum and maximum absolute luminance L. This is compatible with + the SMPTE ST 2086 definition of HDR static metadata. + + This luminance range is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + Min L value is multiplied by 10000 to get the argument min_lum value and + carry precision of 4 decimals. Max L value is unscaled for max_lum. + + + + + + + + + Provides the targeted max_cll of the image description. max_cll is + defined by CTA-861-H. + + This luminance is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + + + + + + + Provides the targeted max_fall of the image description. max_fall is + defined by CTA-861-H. + + This luminance is only theoretical and may not correspond to the + luminance of light emitted on an actual display. + + + + + + \ No newline at end of file diff --git a/scripts/generateShaderIncludes.sh b/scripts/generateShaderIncludes.sh deleted file mode 100755 index c9419031..00000000 --- a/scripts/generateShaderIncludes.sh +++ /dev/null @@ -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 ' >> ./src/render/shaders/Shaders.hpp -echo 'static const std::map SHADERS = {' >> ./src/render/shaders/Shaders.hpp - -for filename in `ls ${SHADERS_SRC}`; do - echo "-- ${filename}" - - { echo -n '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 diff --git a/scripts/generateVersion.sh b/scripts/generateVersion.sh new file mode 100755 index 00000000..9313815d --- /dev/null +++ b/scripts/generateVersion.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# if the git directory doesn't exist, don't gather data to avoid overwriting, unless +# the version file is missing altogether (otherwise compiling will fail) +if [ ! -d ./.git ]; then + if [ -f ./src/version.h ]; then + exit 0 + fi +fi + +cp -fr ./src/version.h.in ./src/version.h + +HASH=${HASH-$(git rev-parse HEAD)} +BRANCH=${BRANCH-$(git branch --show-current)} +MESSAGE=${MESSAGE-$(git show | head -n 5 | tail -n 1 | sed -e 's/#//g' -e 's/\"//g')} +DATE=${DATE-$(git show --no-patch --format=%cd --date=local)} +DIRTY=${DIRTY-$(git diff-index --quiet HEAD -- || echo dirty)} +TAG=${TAG-$(git describe --tags)} +COMMITS=${COMMITS-$(git rev-list --count HEAD)} + +sed -i -e "s#@HASH@#${HASH}#" ./src/version.h +sed -i -e "s#@BRANCH@#${BRANCH}#" ./src/version.h +sed -i -e "s#@MESSAGE@#${MESSAGE}#" ./src/version.h +sed -i -e "s#@DATE@#${DATE}#" ./src/version.h +sed -i -e "s#@DIRTY@#${DIRTY}#" ./src/version.h +sed -i -e "s#@TAG@#${TAG}#" ./src/version.h +sed -i -e "s#@COMMITS@#${COMMITS}#" ./src/version.h diff --git a/src/Compositor.cpp b/src/Compositor.cpp index b0fc1545..a4acbecc 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -2,12 +2,8 @@ #include #include "Compositor.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #include "desktop/DesktopTypes.hpp" -#include "desktop/state/FocusState.hpp" -#include "desktop/history/WindowHistoryTracker.hpp" -#include "desktop/history/WorkspaceHistoryTracker.hpp" -#include "desktop/view/Group.hpp" #include "helpers/Splashes.hpp" #include "config/ConfigValue.hpp" #include "config/ConfigWatcher.hpp" @@ -19,8 +15,6 @@ #include "managers/DonationNagManager.hpp" #include "managers/ANRManager.hpp" #include "managers/eventLoop/EventLoopManager.hpp" -#include "managers/permissions/DynamicPermissionManager.hpp" -#include "managers/screenshare/ScreenshareManager.hpp" #include #include #include @@ -31,12 +25,11 @@ #include #include #include "debug/HyprCtl.hpp" -#include "debug/crash/CrashReporter.hpp" +#include "debug/CrashReporter.hpp" #ifdef USES_SYSTEMD #include // for SdNotify #endif #include "helpers/fs/FsUtils.hpp" -#include "helpers/env/Env.hpp" #include "protocols/FractionalScale.hpp" #include "protocols/PointerConstraints.hpp" #include "protocols/LayerShell.hpp" @@ -46,8 +39,7 @@ #include "protocols/ColorManagement.hpp" #include "protocols/core/Compositor.hpp" #include "protocols/core/Subcompositor.hpp" -#include "desktop/view/LayerSurface.hpp" -#include "layout/space/Space.hpp" +#include "desktop/LayerSurface.hpp" #include "render/Renderer.hpp" #include "xwayland/XWayland.hpp" #include "helpers/ByteOperations.hpp" @@ -60,21 +52,16 @@ #include "config/ConfigManager.hpp" #include "render/OpenGL.hpp" #include "managers/input/InputManager.hpp" -#include "managers/animation/AnimationManager.hpp" -#include "managers/animation/DesktopAnimationManager.hpp" +#include "managers/AnimationManager.hpp" #include "managers/EventManager.hpp" +#include "managers/HookSystemManager.hpp" #include "managers/ProtocolManager.hpp" -#include "managers/WelcomeManager.hpp" -#include "render/AsyncResourceGatherer.hpp" +#include "managers/LayoutManager.hpp" #include "plugins/PluginSystem.hpp" +#include "helpers/Watchdog.hpp" #include "hyprerror/HyprError.hpp" #include "debug/HyprNotificationOverlay.hpp" #include "debug/HyprDebugOverlay.hpp" -#include "helpers/MonitorFrameScheduler.hpp" -#include "i18n/Engine.hpp" -#include "layout/LayoutManager.hpp" -#include "layout/target/WindowTarget.hpp" -#include "event/EventBus.hpp" #include #include @@ -85,7 +72,6 @@ #include #include #include -#include using namespace Hyprutils::String; using namespace Aquamarine; @@ -93,7 +79,7 @@ using enum NContentType::eContentType; using namespace NColorManagement; static int handleCritSignal(int signo, void* data) { - Log::logger->log(Log::DEBUG, "Hyprland received signal {}", signo); + Debug::log(LOG, "Hyprland received signal {}", signo); if (signo == SIGTERM || signo == SIGINT || signo == SIGKILL) g_pCompositor->stopCompositor(); @@ -107,6 +93,11 @@ static void handleUnrecoverableSignal(int sig) { signal(SIGABRT, SIG_DFL); signal(SIGSEGV, SIG_DFL); + if (g_pHookSystem && g_pHookSystem->m_bCurrentEventPlugin) { + longjmp(g_pHookSystem->m_jbHookFaultJumpBuf, 1); + return; + } + // Kill the program if the crash-reporter is caught in a deadlock. signal(SIGALRM, [](int _) { char const* msg = "\nCrashReporter exceeded timeout, forcefully exiting\n"; @@ -115,7 +106,7 @@ static void handleUnrecoverableSignal(int sig) { }); alarm(15); - CrashReporter::createAndSaveCrash(sig); + NCrashReporter::createAndSaveCrash(sig); abort(); } @@ -127,44 +118,52 @@ static void handleUserSignal(int sig) { } } -bool CCompositor::setWatchdogFd(int fd) { - m_watchdogWriteFd = Hyprutils::OS::CFileDescriptor{fd}; - return m_watchdogWriteFd.isValid() && !m_watchdogWriteFd.isClosed(); +static eLogLevel aqLevelToHl(Aquamarine::eBackendLogLevel level) { + switch (level) { + case Aquamarine::eBackendLogLevel::AQ_LOG_TRACE: return TRACE; + case Aquamarine::eBackendLogLevel::AQ_LOG_DEBUG: return LOG; + case Aquamarine::eBackendLogLevel::AQ_LOG_ERROR: return ERR; + case Aquamarine::eBackendLogLevel::AQ_LOG_WARNING: return WARN; + case Aquamarine::eBackendLogLevel::AQ_LOG_CRITICAL: return CRIT; + default: break; + } + + return NONE; +} + +static void aqLog(Aquamarine::eBackendLogLevel level, std::string msg) { + Debug::log(aqLevelToHl(level), "[AQ] {}", msg); } void CCompositor::bumpNofile() { - if (!getrlimit(RLIMIT_NOFILE, &m_originalNofile)) - Log::logger->log(Log::DEBUG, "Old rlimit: soft -> {}, hard -> {}", m_originalNofile.rlim_cur, m_originalNofile.rlim_max); + if (!getrlimit(RLIMIT_NOFILE, &m_sOriginalNofile)) + Debug::log(LOG, "Old rlimit: soft -> {}, hard -> {}", m_sOriginalNofile.rlim_cur, m_sOriginalNofile.rlim_max); else { - Log::logger->log(Log::ERR, "Failed to get NOFILE rlimits"); - m_originalNofile.rlim_max = 0; + Debug::log(ERR, "Failed to get NOFILE rlimits"); + m_sOriginalNofile.rlim_max = 0; return; } - rlimit newLimit = m_originalNofile; + rlimit newLimit = m_sOriginalNofile; newLimit.rlim_cur = newLimit.rlim_max; if (setrlimit(RLIMIT_NOFILE, &newLimit) < 0) { - Log::logger->log(Log::ERR, "Failed bumping NOFILE limits higher"); - m_originalNofile.rlim_max = 0; + Debug::log(ERR, "Failed bumping NOFILE limits higher"); + m_sOriginalNofile.rlim_max = 0; return; } if (!getrlimit(RLIMIT_NOFILE, &newLimit)) - Log::logger->log(Log::DEBUG, "New rlimit: soft -> {}, hard -> {}", newLimit.rlim_cur, newLimit.rlim_max); + Debug::log(LOG, "New rlimit: soft -> {}, hard -> {}", newLimit.rlim_cur, newLimit.rlim_max); } void CCompositor::restoreNofile() { - if (m_originalNofile.rlim_max <= 0) + if (m_sOriginalNofile.rlim_max <= 0) return; - if (setrlimit(RLIMIT_NOFILE, &m_originalNofile) < 0) - Log::logger->log(Log::ERR, "Failed restoring NOFILE limits"); -} - -bool CCompositor::supportsDrmSyncobjTimeline() const { - return m_drm.syncobjSupport || m_drmRenderNode.syncObjSupport; + if (setrlimit(RLIMIT_NOFILE, &m_sOriginalNofile) < 0) + Debug::log(ERR, "Failed restoring NOFILE limits"); } void CCompositor::setMallocThreshold() { @@ -177,76 +176,76 @@ void CCompositor::setMallocThreshold() { #endif } -CCompositor::CCompositor(bool onlyConfig) : m_onlyConfigVerification(onlyConfig), m_hyprlandPID(getpid()) { +CCompositor::CCompositor(bool onlyConfig) : m_bOnlyConfigVerification(onlyConfig), m_iHyprlandPID(getpid()) { if (onlyConfig) return; setMallocThreshold(); - m_hyprTempDataRoot = std::string{getenv("XDG_RUNTIME_DIR")} + "/hypr"; + m_szHyprTempDataRoot = std::string{getenv("XDG_RUNTIME_DIR")} + "/hypr"; - if (m_hyprTempDataRoot.starts_with("/hypr")) { + if (m_szHyprTempDataRoot.starts_with("/hypr")) { std::println("Bailing out, $XDG_RUNTIME_DIR is invalid"); throw std::runtime_error("CCompositor() failed"); } - if (!m_hyprTempDataRoot.starts_with("/run/user")) + if (!m_szHyprTempDataRoot.starts_with("/run/user")) std::println("[!!WARNING!!] XDG_RUNTIME_DIR looks non-standard. Proceeding anyways..."); std::random_device dev; std::mt19937 engine(dev()); std::uniform_int_distribution<> distribution(0, INT32_MAX); - m_instanceSignature = std::format("{}_{}_{}", GIT_COMMIT_HASH, std::time(nullptr), distribution(engine)); + m_szInstanceSignature = std::format("{}_{}_{}", GIT_COMMIT_HASH, std::time(nullptr), distribution(engine)); - setenv("HYPRLAND_INSTANCE_SIGNATURE", m_instanceSignature.c_str(), true); + setenv("HYPRLAND_INSTANCE_SIGNATURE", m_szInstanceSignature.c_str(), true); - if (!std::filesystem::exists(m_hyprTempDataRoot)) - mkdir(m_hyprTempDataRoot.c_str(), S_IRWXU); - else if (!std::filesystem::is_directory(m_hyprTempDataRoot)) { - std::println("Bailing out, {} is not a directory", m_hyprTempDataRoot); + if (!std::filesystem::exists(m_szHyprTempDataRoot)) + mkdir(m_szHyprTempDataRoot.c_str(), S_IRWXU); + else if (!std::filesystem::is_directory(m_szHyprTempDataRoot)) { + std::println("Bailing out, {} is not a directory", m_szHyprTempDataRoot); throw std::runtime_error("CCompositor() failed"); } - m_instancePath = m_hyprTempDataRoot + "/" + m_instanceSignature; + m_szInstancePath = m_szHyprTempDataRoot + "/" + m_szInstanceSignature; - if (std::filesystem::exists(m_instancePath)) { - std::println("Bailing out, {} exists??", m_instancePath); + if (std::filesystem::exists(m_szInstancePath)) { + std::println("Bailing out, {} exists??", m_szInstancePath); throw std::runtime_error("CCompositor() failed"); } - if (mkdir(m_instancePath.c_str(), S_IRWXU) < 0) { - std::println("Bailing out, couldn't create {}", m_instancePath); + if (mkdir(m_szInstancePath.c_str(), S_IRWXU) < 0) { + std::println("Bailing out, couldn't create {}", m_szInstancePath); throw std::runtime_error("CCompositor() failed"); } - Log::logger->initIS(m_instancePath); + Debug::init(m_szInstancePath); - Log::logger->log(Log::DEBUG, "Instance Signature: {}", m_instanceSignature); + Debug::log(LOG, "Instance Signature: {}", m_szInstanceSignature); - Log::logger->log(Log::DEBUG, "Runtime directory: {}", m_instancePath); + Debug::log(LOG, "Runtime directory: {}", m_szInstancePath); - Log::logger->log(Log::DEBUG, "Hyprland PID: {}", m_hyprlandPID); + Debug::log(LOG, "Hyprland PID: {}", m_iHyprlandPID); - Log::logger->log(Log::DEBUG, "===== SYSTEM INFO: ====="); + Debug::log(LOG, "===== SYSTEM INFO: ====="); logSystemInfo(); - Log::logger->log(Log::DEBUG, "========================"); + Debug::log(LOG, "========================"); - Log::logger->log(Log::DEBUG, "\n\n"); // pad + Debug::log(NONE, "\n\n"); // pad - Log::logger->log(Log::INFO, "If you are crashing, or encounter any bugs, please consult https://wiki.hypr.land/Crashes-and-Bugs/\n\n"); + Debug::log(INFO, "If you are crashing, or encounter any bugs, please consult https://wiki.hyprland.org/Crashes-and-Bugs/\n\n"); setRandomSplash(); - Log::logger->log(Log::DEBUG, "\nCurrent splash: {}\n\n", m_currentSplash); + Debug::log(LOG, "\nCurrent splash: {}\n\n", m_szCurrentSplash); bumpNofile(); } CCompositor::~CCompositor() { - if (!m_isShuttingDown && !m_onlyConfigVerification) + if (!m_bIsShuttingDown && !m_bOnlyConfigVerification) cleanup(); } @@ -265,7 +264,7 @@ void CCompositor::setRandomSplash() { std::mt19937 engine(dev()); std::uniform_int_distribution<> distribution(0, SPLASHES->size() - 1); - m_currentSplash = SPLASHES->at(distribution(engine)); + m_szCurrentSplash = SPLASHES->at(distribution(engine)); } static std::vector> pendingOutputs; @@ -281,7 +280,8 @@ static bool filterGlobals(const wl_client* client, const wl_global* global, void // void CCompositor::initServer(std::string socketName, int socketFd) { - if (m_onlyConfigVerification) { + if (m_bOnlyConfigVerification) { + g_pHookSystem = makeUnique(); g_pKeybindManager = makeUnique(); g_pAnimationManager = makeUnique(); g_pConfigManager = makeUnique(); @@ -290,16 +290,16 @@ void CCompositor::initServer(std::string socketName, int socketFd) { return; } - m_wlDisplay = wl_display_create(); + m_sWLDisplay = wl_display_create(); - wl_display_set_global_filter(m_wlDisplay, ::filterGlobals, nullptr); + wl_display_set_global_filter(m_sWLDisplay, ::filterGlobals, nullptr); - m_wlEventLoop = wl_display_get_event_loop(m_wlDisplay); + m_sWLEventLoop = wl_display_get_event_loop(m_sWLDisplay); // register crit signal handler - m_critSigSource = wl_event_loop_add_signal(m_wlEventLoop, SIGTERM, handleCritSignal, nullptr); + m_critSigSource = wl_event_loop_add_signal(m_sWLEventLoop, SIGTERM, handleCritSignal, nullptr); - if (!Env::envEnabled("HYPRLAND_NO_CRASHREPORTER")) { + if (!envEnabled("HYPRLAND_NO_CRASHREPORTER")) { signal(SIGSEGV, handleUnrecoverableSignal); signal(SIGABRT, handleUnrecoverableSignal); } @@ -307,16 +307,14 @@ void CCompositor::initServer(std::string socketName, int socketFd) { initManagers(STAGE_PRIORITY); - Log::logger->initCallbacks(); + if (envEnabled("HYPRLAND_TRACE")) + Debug::trace = true; // set the buffer size to 1MB to avoid disconnects due to an app hanging for a short while - wl_display_set_default_max_buffer_size(m_wlDisplay, 1_MB); + wl_display_set_default_max_buffer_size(m_sWLDisplay, 1_MB); - Aquamarine::SBackendOptions options{}; - SP conn = makeShared(Log::logger->hu()); - conn->setLogLevel(Log::DEBUG); - conn->setName("aquamarine"); - options.logConnection = std::move(conn); + Aquamarine::SBackendOptions options{}; + options.logFunction = aqLog; std::vector implementations; Aquamarine::SBackendImplementationOptions option; @@ -330,13 +328,12 @@ void CCompositor::initServer(std::string socketName, int socketFd) { option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_FALLBACK; implementations.emplace_back(option); - m_aqBackend = CBackend::create(implementations, options); + m_pAqBackend = CBackend::create(implementations, options); - if (!m_aqBackend) { - Log::logger->log( - Log::CRIT, - "m_pAqBackend was null! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a Wayland " - "session, NOT an X11 one."); + if (!m_pAqBackend) { + Debug::log(CRIT, + "m_pAqBackend was null! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a Wayland " + "session, NOT an X11 one."); throwError("CBackend::create() failed!"); } @@ -344,82 +341,56 @@ void CCompositor::initServer(std::string socketName, int socketFd) { initAllSignals(); - if (!m_aqBackend->start()) { - Log::logger->log( - Log::CRIT, - "m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a " - "Wayland session, NOT an X11 one."); + if (!m_pAqBackend->start()) { + Debug::log(CRIT, + "m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a " + "Wayland session, NOT an X11 one."); throwError("CBackend::create() failed!"); } - m_initialized = true; + m_bInitialized = true; - m_drm.fd = m_aqBackend->drmFD(); - Log::logger->log(Log::DEBUG, "Running on DRMFD: {}", m_drm.fd); - - m_drmRenderNode.fd = m_aqBackend->drmRenderNodeFD(); - Log::logger->log(Log::DEBUG, "Using RENDERNODEFD: {}", m_drmRenderNode.fd); - -#if defined(__linux__) - auto syncObjSupport = [](auto fd) { - if (fd < 0) - return false; - - uint64_t cap = 0; - int ret = drmGetCap(fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap); - return ret == 0 && cap != 0; - }; - - m_drm.syncobjSupport = syncObjSupport(m_drm.fd); - Log::logger->log(Log::DEBUG, "DRM DisplayNode syncobj timeline support: {}", m_drm.syncobjSupport ? "yes" : "no"); - - m_drmRenderNode.syncObjSupport = syncObjSupport(m_drmRenderNode.fd); - Log::logger->log(Log::DEBUG, "DRM RenderNode syncobj timeline support: {}", m_drmRenderNode.syncObjSupport ? "yes" : "no"); - - if (!m_drm.syncobjSupport && !m_drmRenderNode.syncObjSupport) - Log::logger->log(Log::DEBUG, "DRM no syncobj support, disabling explicit sync"); -#else - Log::logger->log(Log::DEBUG, "DRM syncobj timeline support: no (not linux)"); -#endif + m_iDRMFD = m_pAqBackend->drmFD(); + Debug::log(LOG, "Running on DRMFD: {}", m_iDRMFD); if (!socketName.empty() && socketFd != -1) { fcntl(socketFd, F_SETFD, FD_CLOEXEC); - const auto RETVAL = wl_display_add_socket_fd(m_wlDisplay, socketFd); + const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd); if (RETVAL >= 0) { - m_wlDisplaySocket = socketName; - Log::logger->log(Log::DEBUG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL); + m_szWLDisplaySocket = socketName; + Debug::log(LOG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL); } else - Log::logger->log(Log::WARN, "wl_display_add_socket_fd for {} returned {}: skipping", socketName, RETVAL); + Debug::log(WARN, "wl_display_add_socket_fd for {} returned {}: skipping", socketName, RETVAL); } else { // get socket, avoid using 0 for (int candidate = 1; candidate <= 32; candidate++) { const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate)); - const auto RETVAL = wl_display_add_socket(m_wlDisplay, CANDIDATESTR.c_str()); + const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str()); if (RETVAL >= 0) { - m_wlDisplaySocket = CANDIDATESTR; - Log::logger->log(Log::DEBUG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL); + m_szWLDisplaySocket = CANDIDATESTR; + Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL); break; } else - Log::logger->log(Log::WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate); + Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate); } } - if (m_wlDisplaySocket.empty()) { - Log::logger->log(Log::WARN, "All candidates failed, trying wl_display_add_socket_auto"); - const auto SOCKETSTR = wl_display_add_socket_auto(m_wlDisplay); + if (m_szWLDisplaySocket.empty()) { + Debug::log(WARN, "All candidates failed, trying wl_display_add_socket_auto"); + const auto SOCKETSTR = wl_display_add_socket_auto(m_sWLDisplay); if (SOCKETSTR) - m_wlDisplaySocket = SOCKETSTR; + m_szWLDisplaySocket = SOCKETSTR; } - if (m_wlDisplaySocket.empty()) { - Log::logger->log(Log::CRIT, "m_szWLDisplaySocket NULL!"); + if (m_szWLDisplaySocket.empty()) { + Debug::log(CRIT, "m_szWLDisplaySocket NULL!"); throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)"); } - setenv("WAYLAND_DISPLAY", m_wlDisplaySocket.c_str(), 1); + setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1); if (!getenv("XDG_CURRENT_DESKTOP")) { setenv("XDG_CURRENT_DESKTOP", "Hyprland", 1); - m_desktopEnvSet = true; + m_bDesktopEnvSet = true; } initManagers(STAGE_BASICINIT); @@ -433,71 +404,90 @@ void CCompositor::initServer(std::string socketName, int socketFd) { } void CCompositor::initAllSignals() { - m_aqBackend->events.newOutput.listenStatic([this](const SP& output) { - Log::logger->log(Log::DEBUG, "New aquamarine output with name {}", output->name); - if (m_initialized) - onNewMonitor(output); - else - pendingOutputs.emplace_back(output); - }); + m_pAqBackend->events.newOutput.registerStaticListener( + [this](void* p, std::any data) { + auto output = std::any_cast>(data); + Debug::log(LOG, "New aquamarine output with name {}", output->name); + if (m_bInitialized) + onNewMonitor(output); + else + pendingOutputs.emplace_back(output); + }, + nullptr); - m_aqBackend->events.newPointer.listenStatic([](const SP& dev) { - Log::logger->log(Log::DEBUG, "New aquamarine pointer with name {}", dev->getName()); - g_pInputManager->newMouse(dev); - g_pInputManager->updateCapabilities(); - }); + m_pAqBackend->events.newPointer.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine pointer with name {}", dev->getName()); + g_pInputManager->newMouse(dev); + g_pInputManager->updateCapabilities(); + }, + nullptr); - m_aqBackend->events.newKeyboard.listenStatic([](const SP& dev) { - Log::logger->log(Log::DEBUG, "New aquamarine keyboard with name {}", dev->getName()); - g_pInputManager->newKeyboard(dev); - g_pInputManager->updateCapabilities(); - }); + m_pAqBackend->events.newKeyboard.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine keyboard with name {}", dev->getName()); + g_pInputManager->newKeyboard(dev); + g_pInputManager->updateCapabilities(); + }, + nullptr); - m_aqBackend->events.newTouch.listenStatic([](const SP& dev) { - Log::logger->log(Log::DEBUG, "New aquamarine touch with name {}", dev->getName()); - g_pInputManager->newTouchDevice(dev); - g_pInputManager->updateCapabilities(); - }); + m_pAqBackend->events.newTouch.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine touch with name {}", dev->getName()); + g_pInputManager->newTouchDevice(dev); + g_pInputManager->updateCapabilities(); + }, + nullptr); - m_aqBackend->events.newSwitch.listenStatic([](const SP& dev) { - Log::logger->log(Log::DEBUG, "New aquamarine switch with name {}", dev->getName()); - g_pInputManager->newSwitch(dev); - }); + m_pAqBackend->events.newSwitch.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine switch with name {}", dev->getName()); + g_pInputManager->newSwitch(dev); + }, + nullptr); - m_aqBackend->events.newTablet.listenStatic([](const SP& dev) { - Log::logger->log(Log::DEBUG, "New aquamarine tablet with name {}", dev->getName()); - g_pInputManager->newTablet(dev); - }); + m_pAqBackend->events.newTablet.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine tablet with name {}", dev->getName()); + g_pInputManager->newTablet(dev); + }, + nullptr); - m_aqBackend->events.newTabletPad.listenStatic([](const SP& dev) { - Log::logger->log(Log::DEBUG, "New aquamarine tablet pad with name {}", dev->getName()); - g_pInputManager->newTabletPad(dev); - }); + m_pAqBackend->events.newTabletPad.registerStaticListener( + [](void* data, std::any d) { + auto dev = std::any_cast>(d); + Debug::log(LOG, "New aquamarine tablet pad with name {}", dev->getName()); + g_pInputManager->newTabletPad(dev); + }, + nullptr); - if (m_aqBackend->hasSession()) { - m_aqBackend->session->events.changeActive.listenStatic([this] { - if (m_aqBackend->session->active) { - Log::logger->log(Log::DEBUG, "Session got activated!"); + if (m_pAqBackend->hasSession()) { + m_pAqBackend->session->events.changeActive.registerStaticListener( + [this](void*, std::any) { + if (m_pAqBackend->session->active) { + Debug::log(LOG, "Session got activated!"); - m_sessionActive = true; + m_bSessionActive = true; - // Reset animation tick state to avoid stale timer issues after suspend/wake - if (g_pAnimationManager) - g_pAnimationManager->resetTickState(); + for (auto const& m : m_vMonitors) { + scheduleFrameForMonitor(m); + m->applyMonitorRule(&m->activeMonitorRule, true); + } - for (auto const& m : m_monitors) { - scheduleFrameForMonitor(m); - m->applyMonitorRule(&m->m_activeMonitorRule, true); + g_pConfigManager->m_bWantsMonitorReload = true; + g_pCursorManager->syncGsettings(); + } else { + Debug::log(LOG, "Session got deactivated!"); + + m_bSessionActive = false; } - - g_pConfigManager->m_wantsMonitorReload = true; - g_pCursorManager->syncGsettings(); - } else { - Log::logger->log(Log::DEBUG, "Session got deactivated!"); - - m_sessionActive = false; - } - }); + }, + nullptr); } } @@ -514,10 +504,10 @@ void CCompositor::cleanEnvironment() { // in main unsetenv("HYPRLAND_CMD"); unsetenv("XDG_BACKEND"); - if (m_desktopEnvSet) + if (m_bDesktopEnvSet) unsetenv("XDG_CURRENT_DESKTOP"); - if (m_aqBackend->hasSession() && !Env::envEnabled("HYPRLAND_NO_SD_VARS")) { + if (m_pAqBackend->hasSession() && !envEnabled("HYPRLAND_NO_SD_VARS")) { const auto CMD = #ifdef USES_SYSTEMD "systemctl --user unset-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash " @@ -529,29 +519,27 @@ void CCompositor::cleanEnvironment() { } void CCompositor::stopCompositor() { - Log::logger->log(Log::DEBUG, "Hyprland is stopping!"); + Debug::log(LOG, "Hyprland is stopping!"); // this stops the wayland loop, wl_display_run - wl_display_terminate(m_wlDisplay); - m_isShuttingDown = true; + wl_display_terminate(m_sWLDisplay); + m_bIsShuttingDown = true; } void CCompositor::cleanup() { - if (!m_wlDisplay) + if (!m_sWLDisplay) return; - if (m_watchdogWriteFd.isValid()) - write(m_watchdogWriteFd.get(), "end", 3); - signal(SIGABRT, SIG_DFL); signal(SIGSEGV, SIG_DFL); removeLockFile(); - m_isShuttingDown = true; + m_bIsShuttingDown = true; + Debug::shuttingDown = true; #ifdef USES_SYSTEMD - if (NSystemd::sdBooted() > 0 && !Env::envEnabled("HYPRLAND_NO_SD_NOTIFY")) + if (NSystemd::sdBooted() > 0 && !envEnabled("HYPRLAND_NO_SD_NOTIFY")) NSystemd::sdNotify(0, "STOPPING=1"); #endif @@ -561,22 +549,27 @@ void CCompositor::cleanup() { // still in a normal working state. g_pPluginSystem->unloadAllPlugins(); - m_workspaces.clear(); - m_windows.clear(); + m_pLastFocus.reset(); + m_pLastWindow.reset(); - for (auto const& m : m_monitors) { + m_vWorkspaces.clear(); + m_vWindows.clear(); + + for (auto const& m : m_vMonitors) { g_pHyprOpenGL->destroyMonitorResources(m); + + m->output->state->setEnabled(false); + m->state.commit(); } g_pXWayland.reset(); - m_monitors.clear(); + m_vMonitors.clear(); - wl_display_destroy_clients(g_pCompositor->m_wlDisplay); + wl_display_destroy_clients(g_pCompositor->m_sWLDisplay); removeAllSignals(); g_pInputManager.reset(); - g_pDynamicPermissionManager.reset(); g_pDecorationPositioner.reset(); g_pCursorManager.reset(); g_pPluginSystem.reset(); @@ -588,10 +581,12 @@ void CCompositor::cleanup() { g_pHyprRenderer.reset(); g_pHyprOpenGL.reset(); g_pConfigManager.reset(); - g_layoutManager.reset(); + g_pLayoutManager.reset(); g_pHyprError.reset(); g_pConfigManager.reset(); g_pKeybindManager.reset(); + g_pHookSystem.reset(); + g_pWatchdog.reset(); g_pXWaylandManager.reset(); g_pPointerManager.reset(); g_pSeatManager.reset(); @@ -599,137 +594,131 @@ void CCompositor::cleanup() { g_pEventLoopManager.reset(); g_pVersionKeeperMgr.reset(); g_pDonationNagManager.reset(); - g_pWelcomeManager.reset(); g_pANRManager.reset(); g_pConfigWatcher.reset(); - g_pAsyncResourceGatherer.reset(); - if (m_aqBackend) - m_aqBackend.reset(); + if (m_pAqBackend) + m_pAqBackend.reset(); if (m_critSigSource) wl_event_source_remove(m_critSigSource); // this frees all wayland resources, including sockets - wl_display_destroy(m_wlDisplay); + wl_display_destroy(m_sWLDisplay); + + Debug::close(); } void CCompositor::initManagers(eManagersInitStage stage) { switch (stage) { case STAGE_PRIORITY: { - Log::logger->log(Log::DEBUG, "Creating the EventLoopManager!"); - g_pEventLoopManager = makeUnique(m_wlDisplay, m_wlEventLoop); + Debug::log(LOG, "Creating the EventLoopManager!"); + g_pEventLoopManager = makeUnique(m_sWLDisplay, m_sWLEventLoop); - Log::logger->log(Log::DEBUG, "Creating the KeybindManager!"); + Debug::log(LOG, "Creating the HookSystem!"); + g_pHookSystem = makeUnique(); + + Debug::log(LOG, "Creating the KeybindManager!"); g_pKeybindManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the AnimationManager!"); + Debug::log(LOG, "Creating the AnimationManager!"); g_pAnimationManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the DynamicPermissionManager!"); - g_pDynamicPermissionManager = makeUnique(); - - Log::logger->log(Log::DEBUG, "Creating the ConfigManager!"); + Debug::log(LOG, "Creating the ConfigManager!"); g_pConfigManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the CHyprError!"); + Debug::log(LOG, "Creating the CHyprError!"); g_pHyprError = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the LayoutManager!"); - g_layoutManager = makeUnique(); + Debug::log(LOG, "Creating the LayoutManager!"); + g_pLayoutManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the TokenManager!"); + Debug::log(LOG, "Creating the TokenManager!"); g_pTokenManager = makeUnique(); g_pConfigManager->init(); + g_pWatchdog = makeUnique(); // requires config + // wait for watchdog to initialize to not hit data races in reading config values. + while (!g_pWatchdog->m_bWatchdogInitialized) { + std::this_thread::yield(); + } - Log::logger->log(Log::DEBUG, "Creating the PointerManager!"); + Debug::log(LOG, "Creating the PointerManager!"); g_pPointerManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the EventManager!"); + Debug::log(LOG, "Creating the EventManager!"); g_pEventManager = makeUnique(); - - Log::logger->log(Log::DEBUG, "Creating the AsyncResourceGatherer!"); - g_pAsyncResourceGatherer = makeUnique(); } break; case STAGE_BASICINIT: { - Log::logger->log(Log::DEBUG, "Creating the CHyprOpenGLImpl!"); + Debug::log(LOG, "Creating the CHyprOpenGLImpl!"); g_pHyprOpenGL = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the ProtocolManager!"); + Debug::log(LOG, "Creating the ProtocolManager!"); g_pProtocolManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the SeatManager!"); + Debug::log(LOG, "Creating the SeatManager!"); g_pSeatManager = makeUnique(); - - // init focus state els - Desktop::History::windowTracker(); - Desktop::History::workspaceTracker(); - } break; case STAGE_LATE: { - Log::logger->log(Log::DEBUG, "Creating CHyprCtl"); + Debug::log(LOG, "Creating CHyprCtl"); g_pHyprCtl = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the InputManager!"); + Debug::log(LOG, "Creating the InputManager!"); g_pInputManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the HyprRenderer!"); + Debug::log(LOG, "Creating the HyprRenderer!"); g_pHyprRenderer = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the XWaylandManager!"); + Debug::log(LOG, "Creating the XWaylandManager!"); g_pXWaylandManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the SessionLockManager!"); + Debug::log(LOG, "Creating the SessionLockManager!"); g_pSessionLockManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the HyprDebugOverlay!"); + Debug::log(LOG, "Creating the HyprDebugOverlay!"); g_pDebugOverlay = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the HyprNotificationOverlay!"); + Debug::log(LOG, "Creating the HyprNotificationOverlay!"); g_pHyprNotificationOverlay = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the PluginSystem!"); + Debug::log(LOG, "Creating the PluginSystem!"); g_pPluginSystem = makeUnique(); g_pConfigManager->handlePluginLoads(); - Log::logger->log(Log::DEBUG, "Creating the DecorationPositioner!"); + Debug::log(LOG, "Creating the DecorationPositioner!"); g_pDecorationPositioner = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the CursorManager!"); + Debug::log(LOG, "Creating the CursorManager!"); g_pCursorManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the VersionKeeper!"); + Debug::log(LOG, "Creating the VersionKeeper!"); g_pVersionKeeperMgr = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the DonationNag!"); + Debug::log(LOG, "Creating the DonationNag!"); g_pDonationNagManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Creating the WelcomeManager!"); - g_pWelcomeManager = makeUnique(); - - Log::logger->log(Log::DEBUG, "Creating the ANRManager!"); + Debug::log(LOG, "Creating the ANRManager!"); g_pANRManager = makeUnique(); - Log::logger->log(Log::DEBUG, "Starting XWayland"); - g_pXWayland = makeUnique(g_pCompositor->m_wantsXwayland); + Debug::log(LOG, "Starting XWayland"); + g_pXWayland = makeUnique(g_pCompositor->m_bWantsXwayland); } break; default: UNREACHABLE(); } } void CCompositor::createLockFile() { - const auto PATH = m_instancePath + "/hyprland.lock"; + const auto PATH = m_szInstancePath + "/hyprland.lock"; std::ofstream ofs(PATH, std::ios::trunc); - ofs << m_hyprlandPID << "\n" << m_wlDisplaySocket << "\n"; + ofs << m_iHyprlandPID << "\n" << m_szWLDisplaySocket << "\n"; ofs.close(); } void CCompositor::removeLockFile() { - const auto PATH = m_instancePath + "/hyprland.lock"; + const auto PATH = m_szInstancePath + "/hyprland.lock"; if (std::filesystem::exists(PATH)) std::filesystem::remove(PATH); @@ -738,7 +727,7 @@ void CCompositor::removeLockFile() { void CCompositor::prepareFallbackOutput() { // create a backup monitor SP headless; - for (auto const& impl : m_aqBackend->getImplementations()) { + for (auto const& impl : m_pAqBackend->getImplementations()) { if (impl->type() == Aquamarine::AQ_BACKEND_HEADLESS) { headless = impl; break; @@ -746,14 +735,11 @@ void CCompositor::prepareFallbackOutput() { } if (!headless) { - Log::logger->log(Log::WARN, "No headless in prepareFallbackOutput?!"); + Debug::log(WARN, "No headless in prepareFallbackOutput?!"); return; } headless->createOutput(); - - if (m_monitors.empty()) - enterUnsafeState(); } void CCompositor::startCompositor() { @@ -761,9 +747,9 @@ void CCompositor::startCompositor() { if ( /* Session-less Hyprland usually means a nest, don't update the env in that case */ - m_aqBackend->hasSession() && + m_pAqBackend->hasSession() && /* Activation environment management is not disabled */ - !Env::envEnabled("HYPRLAND_NO_SD_VARS")) { + !envEnabled("HYPRLAND_NO_SD_VARS")) { const auto CMD = #ifdef USES_SYSTEMD "systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash " @@ -773,7 +759,7 @@ void CCompositor::startCompositor() { CKeybindManager::spawn(CMD); } - Log::logger->log(Log::DEBUG, "Running on WAYLAND_DISPLAY: {}", m_wlDisplaySocket); + Debug::log(LOG, "Running on WAYLAND_DISPLAY: {}", m_szWLDisplaySocket); prepareFallbackOutput(); @@ -782,27 +768,24 @@ void CCompositor::startCompositor() { #ifdef USES_SYSTEMD if (NSystemd::sdBooted() > 0) { // tell systemd that we are ready so it can start other bond, following, related units - if (!Env::envEnabled("HYPRLAND_NO_SD_NOTIFY")) + if (!envEnabled("HYPRLAND_NO_SD_NOTIFY")) NSystemd::sdNotify(0, "READY=1"); } else - Log::logger->log(Log::DEBUG, "systemd integration is baked in but system itself is not booted à la systemd!"); + Debug::log(LOG, "systemd integration is baked in but system itself is not booted à la systemd!"); #endif createLockFile(); - Event::bus()->m_events.ready.emit(); - - if (m_watchdogWriteFd.isValid()) - write(m_watchdogWriteFd.get(), "vax", 3); + EMIT_HOOK_EVENT("ready", nullptr); // This blocks until we are done. - Log::logger->log(Log::DEBUG, "Hyprland is ready, running the event loop!"); + Debug::log(LOG, "Hyprland is ready, running the event loop!"); g_pEventLoopManager->enterLoop(); } PHLMONITOR CCompositor::getMonitorFromID(const MONITORID& id) { - for (auto const& m : m_monitors) { - if (m->m_id == id) { + for (auto const& m : m_vMonitors) { + if (m->ID == id) { return m; } } @@ -811,8 +794,8 @@ PHLMONITOR CCompositor::getMonitorFromID(const MONITORID& id) { } PHLMONITOR CCompositor::getMonitorFromName(const std::string& name) { - for (auto const& m : m_monitors) { - if (m->m_name == name) { + for (auto const& m : m_vMonitors) { + if (m->szName == name) { return m; } } @@ -820,8 +803,8 @@ PHLMONITOR CCompositor::getMonitorFromName(const std::string& name) { } PHLMONITOR CCompositor::getMonitorFromDesc(const std::string& desc) { - for (auto const& m : m_monitors) { - if (m->m_description.starts_with(desc)) + for (auto const& m : m_vMonitors) { + if (m->szDescription.starts_with(desc)) return m; } return nullptr; @@ -832,14 +815,14 @@ PHLMONITOR CCompositor::getMonitorFromCursor() { } PHLMONITOR CCompositor::getMonitorFromVector(const Vector2D& point) { - if (m_monitors.empty()) { - Log::logger->log(Log::WARN, "getMonitorFromVector called with empty monitor list"); + if (m_vMonitors.empty()) { + Debug::log(WARN, "getMonitorFromVector called with empty monitor list"); return nullptr; } PHLMONITOR mon; - for (auto const& m : m_monitors) { - if (CBox{m->m_position, m->m_size}.containsPoint(point)) { + for (auto const& m : m_vMonitors) { + if (CBox{m->vecPosition, m->vecSize}.containsPoint(point)) { mon = m; break; } @@ -849,8 +832,8 @@ PHLMONITOR CCompositor::getMonitorFromVector(const Vector2D& point) { float bestDistance = 0.f; PHLMONITOR pBestMon; - for (auto const& m : m_monitors) { - float dist = vecToRectDistanceSquared(point, m->m_position, m->m_position + m->m_size); + for (auto const& m : m_vMonitors) { + float dist = vecToRectDistanceSquared(point, m->vecPosition, m->vecPosition + m->vecSize); if (dist < bestDistance || !pBestMon) { bestDistance = dist; @@ -859,8 +842,8 @@ PHLMONITOR CCompositor::getMonitorFromVector(const Vector2D& point) { } if (!pBestMon) { // ????? - Log::logger->log(Log::WARN, "getMonitorFromVector no close mon???"); - return m_monitors.front(); + Debug::log(WARN, "getMonitorFromVector no close mon???"); + return m_vMonitors.front(); } return pBestMon; @@ -870,49 +853,37 @@ PHLMONITOR CCompositor::getMonitorFromVector(const Vector2D& point) { } void CCompositor::removeWindowFromVectorSafe(PHLWINDOW pWindow) { - if (!pWindow->m_fadingOut) { - Event::bus()->m_events.window.destroy.emit(pWindow); + if (!pWindow->m_bFadingOut) { + EMIT_HOOK_EVENT("destroyWindow", pWindow); - std::erase_if(m_windows, [&](SP& el) { return el == pWindow; }); - std::erase_if(m_windowsFadingOut, [&](PHLWINDOWREF el) { return el.lock() == pWindow; }); + std::erase_if(m_vWindows, [&](SP& el) { return el == pWindow; }); + std::erase_if(m_vWindowsFadingOut, [&](PHLWINDOWREF el) { return el.lock() == pWindow; }); } } bool CCompositor::monitorExists(PHLMONITOR pMonitor) { - return std::ranges::any_of(m_realMonitors, [&](const PHLMONITOR& m) { return m == pMonitor; }); + return std::ranges::any_of(m_vRealMonitors, [&](const PHLMONITOR& m) { return m == pMonitor; }); } PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t properties, PHLWINDOW pIgnoreWindow) { - const auto PMONITOR = getMonitorFromVector(pos); - if (!PMONITOR) - return nullptr; - - static auto PRESIZEONBORDER = CConfigValue("general:resize_on_border"); - static auto PBORDERSIZE = CConfigValue("general:border_size"); - static auto PBORDERGRABEXTEND = CConfigValue("general:extend_border_grab_area"); - static auto PSPECIALFALLTHRU = CConfigValue("input:special_fallthrough"); - static auto PMODALPARENTBLOCKING = CConfigValue("general:modal_parent_blocking"); - const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0; - const bool ONLY_PRIORITY = properties & Desktop::View::FOCUS_PRIORITY; - - const auto isShadowedByModal = [](PHLWINDOW w) -> bool { - return *PMODALPARENTBLOCKING && w->m_xdgSurface && w->m_xdgSurface->m_toplevel && w->m_xdgSurface->m_toplevel->anyChildModal(); - }; + const auto PMONITOR = getMonitorFromVector(pos); + static auto PRESIZEONBORDER = CConfigValue("general:resize_on_border"); + static auto PBORDERSIZE = CConfigValue("general:border_size"); + static auto PBORDERGRABEXTEND = CConfigValue("general:extend_border_grab_area"); + static auto PSPECIALFALLTHRU = CConfigValue("input:special_fallthrough"); + const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0; // pinned windows on top of floating regardless - if (properties & Desktop::View::ALLOW_FLOATING) { - for (auto const& w : m_windows | std::views::reverse) { - if (ONLY_PRIORITY && !w->priorityFocus()) - continue; - - if (w->m_isFloating && w->m_isMapped && !w->isHidden() && !w->m_X11ShouldntFocus && w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() && - w != pIgnoreWindow && !isShadowedByModal(w)) { + if (properties & ALLOW_FLOATING) { + for (auto const& w : m_vWindows | std::views::reverse) { + if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned && !w->m_sWindowData.noFocus.valueOrDefault() && + w != pIgnoreWindow) { const auto BB = w->getWindowBoxUnified(properties); CBox box = BB.copy().expand(!w->isX11OverrideRedirect() ? BORDER_GRAB_AREA : 0); if (box.containsPoint(g_pPointerManager->position())) return w; - if (!w->m_isX11) { + if (!w->m_bIsX11) { if (w->hasPopupAt(pos)) return w; } @@ -922,47 +893,45 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper auto windowForWorkspace = [&](bool special) -> PHLWINDOW { auto floating = [&](bool aboveFullscreen) -> PHLWINDOW { - for (auto const& w : m_windows | std::views::reverse) { + for (auto const& w : m_vWindows | std::views::reverse) { if (special && !w->onSpecialWorkspace()) // because special floating may creep up into regular continue; - if (!w->m_workspace) + if (!w->m_pWorkspace) continue; - if (ONLY_PRIORITY && !w->priorityFocus()) - continue; - - const auto PWINDOWMONITOR = w->m_monitor.lock(); + const auto PWINDOWMONITOR = w->m_pMonitor.lock(); // to avoid focusing windows behind special workspaces from other monitors - if (!*PSPECIALFALLTHRU && PWINDOWMONITOR && PWINDOWMONITOR->m_activeSpecialWorkspace && w->m_workspace != PWINDOWMONITOR->m_activeSpecialWorkspace) { + if (!*PSPECIALFALLTHRU && PWINDOWMONITOR && PWINDOWMONITOR->activeSpecialWorkspace && w->m_pWorkspace != PWINDOWMONITOR->activeSpecialWorkspace) { const auto BB = w->getWindowBoxUnified(properties); - if (BB.x >= PWINDOWMONITOR->m_position.x && BB.y >= PWINDOWMONITOR->m_position.y && - BB.x + BB.width <= PWINDOWMONITOR->m_position.x + PWINDOWMONITOR->m_size.x && BB.y + BB.height <= PWINDOWMONITOR->m_position.y + PWINDOWMONITOR->m_size.y) + if (BB.x >= PWINDOWMONITOR->vecPosition.x && BB.y >= PWINDOWMONITOR->vecPosition.y && + BB.x + BB.width <= PWINDOWMONITOR->vecPosition.x + PWINDOWMONITOR->vecSize.x && + BB.y + BB.height <= PWINDOWMONITOR->vecPosition.y + PWINDOWMONITOR->vecSize.y) continue; } - if (w->m_isFloating && w->m_isMapped && w->m_workspace->isVisible() && !w->isHidden() && !w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() && - w != pIgnoreWindow && (!aboveFullscreen || w->m_createdOverFullscreen) && !isShadowedByModal(w)) { + if (w->m_bIsFloating && w->m_bIsMapped && w->m_pWorkspace->isVisible() && !w->isHidden() && !w->m_bPinned && !w->m_sWindowData.noFocus.valueOrDefault() && + w != pIgnoreWindow && (!aboveFullscreen || w->m_bCreatedOverFullscreen)) { // OR windows should add focus to parent - if (w->m_X11ShouldntFocus && !w->isX11OverrideRedirect()) + if (w->m_bX11ShouldntFocus && !w->isX11OverrideRedirect()) continue; const auto BB = w->getWindowBoxUnified(properties); CBox box = BB.copy().expand(!w->isX11OverrideRedirect() ? BORDER_GRAB_AREA : 0); if (box.containsPoint(g_pPointerManager->position())) { - if (w->m_isX11 && w->isX11OverrideRedirect() && !w->m_xwaylandSurface->wantsFocus()) { + if (w->m_bIsX11 && w->isX11OverrideRedirect() && !w->m_pXWaylandSurface->wantsFocus()) { // Override Redirect - return Desktop::focusState()->window(); // we kinda trick everything here. - // TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases. + return g_pCompositor->m_pLastWindow.lock(); // we kinda trick everything here. + // TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases. } return w; } - if (!w->m_isX11) { + if (!w->m_bIsX11) { if (w->hasPopupAt(pos)) return w; } @@ -972,69 +941,51 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper return nullptr; }; - if (properties & Desktop::View::ALLOW_FLOATING) { + if (properties & ALLOW_FLOATING) { // first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter. auto found = floating(true); if (found) return found; } - if (properties & Desktop::View::FLOATING_ONLY) + if (properties & FLOATING_ONLY) return floating(false); const WORKSPACEID WSPID = special ? PMONITOR->activeSpecialWorkspaceID() : PMONITOR->activeWorkspaceID(); const auto PWORKSPACE = getWorkspaceByID(WSPID); - if (PWORKSPACE->m_hasFullscreenWindow && !(properties & Desktop::View::SKIP_FULLSCREEN_PRIORITY) && !ONLY_PRIORITY) { - const auto FS_WINDOW = PWORKSPACE->getFullscreenWindow(); - - if (!FS_WINDOW) - return nullptr; - - // for maximized windows, don't return a window if we are not directly on it. - if (FS_WINDOW->m_fullscreenState.internal != FSMODE_MAXIMIZED || FS_WINDOW->getWindowBoxUnified(properties).containsPoint(pos)) - return PWORKSPACE->getFullscreenWindow(); - else - return nullptr; - } + if (PWORKSPACE->m_bHasFullscreenWindow) + return PWORKSPACE->getFullscreenWindow(); auto found = floating(false); if (found) return found; // for windows, we need to check their extensions too, first. - for (auto const& w : m_windows) { - if (ONLY_PRIORITY && !w->priorityFocus()) - continue; - + for (auto const& w : m_vWindows) { if (special != w->onSpecialWorkspace()) continue; - if (!w->m_workspace) + if (!w->m_pWorkspace) continue; - if (!w->m_isX11 && !w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus && - !w->m_ruleApplicator->noFocus().valueOrDefault() && w != pIgnoreWindow && !isShadowedByModal(w)) { + if (!w->m_bIsX11 && !w->m_bIsFloating && w->m_bIsMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_bX11ShouldntFocus && + !w->m_sWindowData.noFocus.valueOrDefault() && w != pIgnoreWindow) { if (w->hasPopupAt(pos)) return w; } } - for (auto const& w : m_windows) { - if (ONLY_PRIORITY && !w->priorityFocus()) - continue; - + for (auto const& w : m_vWindows) { if (special != w->onSpecialWorkspace()) continue; - if (!w->m_workspace) + if (!w->m_pWorkspace) continue; - if (!w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus && !w->m_ruleApplicator->noFocus().valueOrDefault() && - w != pIgnoreWindow && !isShadowedByModal(w)) { - CBox box = (properties & Desktop::View::USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_position, w->m_size}; - if ((properties & Desktop::View::INPUT_EXTENTS) && BORDER_GRAB_AREA > 0 && !w->isX11OverrideRedirect()) - box.expand(BORDER_GRAB_AREA); + if (!w->m_bIsFloating && w->m_bIsMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_bX11ShouldntFocus && !w->m_sWindowData.noFocus.valueOrDefault() && + w != pIgnoreWindow) { + CBox box = (properties & USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_vPosition, w->m_vSize}; if (box.containsPoint(pos)) return w; } @@ -1044,10 +995,10 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper }; // special workspace - if (PMONITOR->m_activeSpecialWorkspace && !*PSPECIALFALLTHRU) + if (PMONITOR->activeSpecialWorkspace && !*PSPECIALFALLTHRU) return windowForWorkspace(true); - if (PMONITOR->m_activeSpecialWorkspace) { + if (PMONITOR->activeSpecialWorkspace) { const auto PWINDOW = windowForWorkspace(true); if (PWINDOW) @@ -1062,18 +1013,18 @@ SP CCompositor::vectorWindowToSurface(const Vector2D& pos, P if (!validMapped(pWindow)) return nullptr; - RASSERT(!pWindow->m_isX11, "Cannot call vectorWindowToSurface on an X11 window!"); + RASSERT(!pWindow->m_bIsX11, "Cannot call vectorWindowToSurface on an X11 window!"); // try popups first - const auto PPOPUP = pWindow->m_popupHead->at(pos); + const auto PPOPUP = pWindow->m_pPopupHead->at(pos); if (PPOPUP) { const auto OFF = PPOPUP->coordsRelativeToParent(); - sl = pos - pWindow->m_realPosition->goal() - OFF; - return PPOPUP->wlSurface()->resource(); + sl = pos - pWindow->m_vRealPosition->goal() - OFF; + return PPOPUP->m_pWLSurface->resource(); } - auto [surf, local] = pWindow->wlSurface()->resource()->at(pos - pWindow->m_realPosition->goal(), true); + auto [surf, local] = pWindow->m_pWLSurface->resource()->at(pos - pWindow->m_vRealPosition->goal(), true); if (surf) { sl = local; return surf; @@ -1086,34 +1037,34 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo if (!validMapped(pWindow)) return {}; - if (pWindow->m_isX11) - return vec - pWindow->m_realPosition->goal(); + if (pWindow->m_bIsX11) + return vec - pWindow->m_vRealPosition->goal(); - const auto PPOPUP = pWindow->m_popupHead->at(vec); + const auto PPOPUP = pWindow->m_pPopupHead->at(vec); if (PPOPUP) return vec - PPOPUP->coordsGlobal(); std::tuple, Vector2D> iterData = {pSurface, {-1337, -1337}}; - pWindow->wlSurface()->resource()->breadthfirst( + pWindow->m_pWLSurface->resource()->breadthfirst( [](SP surf, const Vector2D& offset, void* data) { - const auto PDATA = sc, Vector2D>*>(data); + const auto PDATA = (std::tuple, Vector2D>*)data; if (surf == std::get<0>(*PDATA)) std::get<1>(*PDATA) = offset; }, &iterData); - CBox geom = pWindow->m_xdgSurface->m_current.geometry; + CBox geom = pWindow->m_pXDGSurface->current.geometry; if (std::get<1>(iterData) == Vector2D{-1337, -1337}) - return vec - pWindow->m_realPosition->goal(); + return vec - pWindow->m_vRealPosition->goal(); - return vec - pWindow->m_realPosition->goal() - std::get<1>(iterData) + Vector2D{geom.x, geom.y}; + return vec - pWindow->m_vRealPosition->goal() - std::get<1>(iterData) + Vector2D{geom.x, geom.y}; } PHLMONITOR CCompositor::getMonitorFromOutput(SP out) { - for (auto const& m : m_monitors) { - if (m->m_output == out) { + for (auto const& m : m_vMonitors) { + if (m->output == out) { return m; } } @@ -1122,8 +1073,8 @@ PHLMONITOR CCompositor::getMonitorFromOutput(SP out) { } PHLMONITOR CCompositor::getRealMonitorFromOutput(SP out) { - for (auto const& m : m_realMonitors) { - if (m->m_output == out) { + for (auto const& m : m_vRealMonitors) { + if (m->output == out) { return m; } } @@ -1131,18 +1082,205 @@ PHLMONITOR CCompositor::getRealMonitorFromOutput(SP out) { return nullptr; } +void CCompositor::focusWindow(PHLWINDOW pWindow, SP pSurface, bool preserveFocusHistory) { + + static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); + static auto PSPECIALFALLTHROUGH = CConfigValue("input:special_fallthrough"); + + if (g_pSessionLockManager->isSessionLocked()) { + Debug::log(LOG, "Refusing a keyboard focus to a window because of a sessionlock"); + return; + } + + if (!g_pInputManager->m_dExclusiveLSes.empty()) { + Debug::log(LOG, "Refusing a keyboard focus to a window because of an exclusive ls"); + return; + } + + if (pWindow && pWindow->m_bIsX11 && pWindow->isX11OverrideRedirect() && !pWindow->m_pXWaylandSurface->wantsFocus()) + return; + + g_pLayoutManager->getCurrentLayout()->bringWindowToTop(pWindow); + + if (!pWindow || !validMapped(pWindow)) { + + if (m_pLastWindow.expired() && !pWindow) + return; + + const auto PLASTWINDOW = m_pLastWindow.lock(); + m_pLastWindow.reset(); + + if (PLASTWINDOW && PLASTWINDOW->m_bIsMapped) { + updateWindowAnimatedDecorationValues(PLASTWINDOW); + + g_pXWaylandManager->activateWindow(PLASTWINDOW, false); + } + + g_pSeatManager->setKeyboardFocus(nullptr); + + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""}); + + EMIT_HOOK_EVENT("activeWindow", (PHLWINDOW) nullptr); + + g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(nullptr); + + m_pLastFocus.reset(); + + g_pInputManager->recheckIdleInhibitorStatus(); + return; + } + + if (pWindow->m_sWindowData.noFocus.valueOrDefault()) { + Debug::log(LOG, "Ignoring focus to nofocus window!"); + return; + } + + if (m_pLastWindow.lock() == pWindow && g_pSeatManager->state.keyboardFocus == pSurface && g_pSeatManager->state.keyboardFocus) + return; + + if (pWindow->m_bPinned) + pWindow->m_pWorkspace = m_pLastMonitor->activeWorkspace; + + const auto PMONITOR = pWindow->m_pMonitor.lock(); + + if (!pWindow->m_pWorkspace || !pWindow->m_pWorkspace->isVisible()) { + const auto PWORKSPACE = pWindow->m_pWorkspace; + // This is to fix incorrect feedback on the focus history. + PWORKSPACE->m_pLastFocusedWindow = pWindow; + if (m_pLastMonitor->activeWorkspace) + PWORKSPACE->rememberPrevWorkspace(m_pLastMonitor->activeWorkspace); + if (PWORKSPACE->m_bIsSpecialWorkspace) + m_pLastMonitor->changeWorkspace(PWORKSPACE, false, true); // if special ws, open on current monitor + else if (PMONITOR) + PMONITOR->changeWorkspace(PWORKSPACE, false, true); + // changeworkspace already calls focusWindow + return; + } + + const auto PLASTWINDOW = m_pLastWindow.lock(); + m_pLastWindow = pWindow; + + /* If special fallthrough is enabled, this behavior will be disabled, as I have no better idea of nicely tracking which + window focuses are "via keybinds" and which ones aren't. */ + if (PMONITOR && PMONITOR->activeSpecialWorkspace && PMONITOR->activeSpecialWorkspace != pWindow->m_pWorkspace && !pWindow->m_bPinned && !*PSPECIALFALLTHROUGH) + PMONITOR->setSpecialWorkspace(nullptr); + + // we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window + if (PLASTWINDOW && PLASTWINDOW->m_bIsMapped) { + PLASTWINDOW->updateDynamicRules(); + + updateWindowAnimatedDecorationValues(PLASTWINDOW); + + if (!pWindow->m_bIsX11 || !pWindow->isX11OverrideRedirect()) + g_pXWaylandManager->activateWindow(PLASTWINDOW, false); + } + + m_pLastWindow = PLASTWINDOW; + + const auto PWINDOWSURFACE = pSurface ? pSurface : pWindow->m_pWLSurface->resource(); + + focusSurface(PWINDOWSURFACE, pWindow); + + g_pXWaylandManager->activateWindow(pWindow, true); // sets the m_pLastWindow + + pWindow->updateDynamicRules(); + pWindow->onFocusAnimUpdate(); + + updateWindowAnimatedDecorationValues(pWindow); + + if (pWindow->m_bIsUrgent) + pWindow->m_bIsUrgent = false; + + // Send an event + g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = pWindow->m_szClass + "," + pWindow->m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = std::format("{:x}", (uintptr_t)pWindow.get())}); + + EMIT_HOOK_EVENT("activeWindow", pWindow); + + g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(pWindow); + + g_pInputManager->recheckIdleInhibitorStatus(); + + if (!preserveFocusHistory) { + // move to front of the window history + const auto HISTORYPIVOT = std::ranges::find_if(m_vWindowFocusHistory, [&](const auto& other) { return other.lock() == pWindow; }); + if (HISTORYPIVOT == m_vWindowFocusHistory.end()) + Debug::log(ERR, "BUG THIS: {} has no pivot in history", pWindow); + else + std::rotate(m_vWindowFocusHistory.begin(), HISTORYPIVOT, HISTORYPIVOT + 1); + } + + if (*PFOLLOWMOUSE == 0) + g_pInputManager->sendMotionEventsToFocused(); + + if (pWindow->m_sGroupData.pNextWindow) + pWindow->deactivateGroupMembers(); +} + +void CCompositor::focusSurface(SP pSurface, PHLWINDOW pWindowOwner) { + + if (g_pSeatManager->state.keyboardFocus == pSurface || (pWindowOwner && g_pSeatManager->state.keyboardFocus == pWindowOwner->m_pWLSurface->resource())) + return; // Don't focus when already focused on this. + + if (g_pSessionLockManager->isSessionLocked() && pSurface && !g_pSessionLockManager->isSurfaceSessionLock(pSurface)) + return; + + if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(pSurface)) { + Debug::log(LOG, "surface {:x} won't receive kb focus becuase grab rejected it", (uintptr_t)pSurface.get()); + return; + } + + const auto PLASTSURF = m_pLastFocus.lock(); + + // Unfocus last surface if should + if (m_pLastFocus && !pWindowOwner) + g_pXWaylandManager->activateSurface(m_pLastFocus.lock(), false); + + if (!pSurface) { + g_pSeatManager->setKeyboardFocus(nullptr); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = ","}); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = ""}); + EMIT_HOOK_EVENT("keyboardFocus", (SP)nullptr); + m_pLastFocus.reset(); + return; + } + + if (g_pSeatManager->keyboard) + g_pSeatManager->setKeyboardFocus(pSurface); + + if (pWindowOwner) + Debug::log(LOG, "Set keyboard focus to surface {:x}, with {}", (uintptr_t)pSurface.get(), pWindowOwner); + else + Debug::log(LOG, "Set keyboard focus to surface {:x}", (uintptr_t)pSurface.get()); + + g_pXWaylandManager->activateSurface(pSurface, true); + m_pLastFocus = pSurface; + + EMIT_HOOK_EVENT("keyboardFocus", pSurface); + + const auto SURF = CWLSurface::fromResource(pSurface); + const auto OLDSURF = CWLSurface::fromResource(PLASTSURF); + + if (OLDSURF && OLDSURF->constraint()) + OLDSURF->constraint()->deactivate(); + + if (SURF && SURF->constraint()) + SURF->constraint()->activate(); +} + SP CCompositor::vectorToLayerPopupSurface(const Vector2D& pos, PHLMONITOR monitor, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) { - for (auto const& lsl : monitor->m_layerSurfaceLayers | std::views::reverse) { + for (auto const& lsl : monitor->m_aLayerSurfaceLayers | std::views::reverse) { for (auto const& ls : lsl | std::views::reverse) { - if (!ls->aliveAndVisible()) + if (!ls->mapped || ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->mapped) || ls->alpha->value() == 0.f) continue; - auto SURFACEAT = ls->m_popupHead->at(pos, true); + auto SURFACEAT = ls->popupHead->at(pos, true); if (SURFACEAT) { *ppLayerSurfaceFound = ls.lock(); *sCoords = pos - SURFACEAT->coordsGlobal(); - return SURFACEAT->wlSurface()->resource(); + return SURFACEAT->m_pWLSurface->resource(); } } } @@ -1150,17 +1288,15 @@ SP CCompositor::vectorToLayerPopupSurface(const Vector2D& po return nullptr; } -SP CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector* layerSurfaces, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound, - bool aboveLockscreen) { - +SP CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector* layerSurfaces, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) { for (auto const& ls : *layerSurfaces | std::views::reverse) { - if (!ls->aliveAndVisible() || (aboveLockscreen && ls->m_ruleApplicator->aboveLock().valueOrDefault() != 2)) + if (!ls->mapped || ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->surface->mapped) || ls->alpha->value() == 0.f) continue; - auto [surf, local] = ls->m_layerSurface->m_surface->at(pos - ls->m_geometry.pos(), true); + auto [surf, local] = ls->layerSurface->surface->at(pos - ls->geometry.pos(), true); if (surf) { - if (surf->m_current.input.empty()) + if (surf->current.input.empty()) continue; *ppLayerSurfaceFound = ls.lock(); @@ -1175,20 +1311,15 @@ SP CCompositor::vectorToLayerSurface(const Vector2D& pos, st } PHLWINDOW CCompositor::getWindowFromSurface(SP pSurface) { - if (!pSurface || !pSurface->m_hlSurface) + if (!pSurface || !pSurface->hlSurface) return nullptr; - const auto VIEW = pSurface->m_hlSurface->view(); - - if (!VIEW || VIEW->type() != Desktop::View::VIEW_TYPE_WINDOW) - return nullptr; - - return dynamicPointerCast(VIEW); + return pSurface->hlSurface->getWindow(); } PHLWINDOW CCompositor::getWindowFromHandle(uint32_t handle) { - for (auto const& w : m_windows) { - if (sc(rc(w.get()) & 0xFFFFFFFF) == handle) { + for (auto const& w : m_vWindows) { + if ((uint32_t)(((uint64_t)w.get()) & 0xFFFFFFFF) == handle) { return w; } } @@ -1197,17 +1328,32 @@ PHLWINDOW CCompositor::getWindowFromHandle(uint32_t handle) { } PHLWORKSPACE CCompositor::getWorkspaceByID(const WORKSPACEID& id) { - for (auto const& w : getWorkspaces()) { - if (w->m_id == id && !w->inert()) - return w.lock(); + for (auto const& w : m_vWorkspaces) { + if (w->m_iID == id && !w->inert()) + return w; } return nullptr; } +void CCompositor::sanityCheckWorkspaces() { + auto it = m_vWorkspaces.begin(); + while (it != m_vWorkspaces.end()) { + const auto& WORKSPACE = *it; + + // If ref == 1, only the compositor holds a ref, which means it's inactive and has no mapped windows. + if (!WORKSPACE->m_bPersistent && WORKSPACE.strongRef() == 1) { + it = m_vWorkspaces.erase(it); + continue; + } + + ++it; + } +} + PHLWINDOW CCompositor::getUrgentWindow() { - for (auto const& w : m_windows) { - if (w->m_isMapped && w->m_isUrgent) + for (auto const& w : m_vWindows) { + if (w->m_bIsMapped && w->m_bIsUrgent) return w; } @@ -1215,15 +1361,15 @@ PHLWINDOW CCompositor::getUrgentWindow() { } bool CCompositor::isWindowActive(PHLWINDOW pWindow) { - if (!Desktop::focusState()->window() && !Desktop::focusState()->surface()) + if (m_pLastWindow.expired() && !m_pLastFocus) return false; - if (!pWindow->m_isMapped) + if (!pWindow->m_bIsMapped) return false; - const auto PSURFACE = pWindow->wlSurface()->resource(); + const auto PSURFACE = pWindow->m_pWLSurface->resource(); - return PSURFACE == Desktop::focusState()->surface() || pWindow == Desktop::focusState()->window(); + return PSURFACE == m_pLastFocus || pWindow == m_pLastWindow.lock(); } void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) { @@ -1231,33 +1377,33 @@ void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) { return; if (top) - pWindow->m_createdOverFullscreen = true; + pWindow->m_bCreatedOverFullscreen = true; - if (pWindow == (top ? m_windows.back() : m_windows.front())) + if (pWindow == (top ? m_vWindows.back() : m_vWindows.front())) return; auto moveToZ = [&](PHLWINDOW pw, bool top) -> void { if (top) { - for (auto it = m_windows.begin(); it != m_windows.end(); ++it) { + for (auto it = m_vWindows.begin(); it != m_vWindows.end(); ++it) { if (*it == pw) { - std::rotate(it, it + 1, m_windows.end()); + std::rotate(it, it + 1, m_vWindows.end()); break; } } } else { - for (auto it = m_windows.rbegin(); it != m_windows.rend(); ++it) { + for (auto it = m_vWindows.rbegin(); it != m_vWindows.rend(); ++it) { if (*it == pw) { - std::rotate(it, it + 1, m_windows.rend()); + std::rotate(it, it + 1, m_vWindows.rend()); break; } } } - if (pw->m_isMapped) - g_pHyprRenderer->damageMonitor(pw->m_monitor.lock()); + if (pw->m_bIsMapped) + g_pHyprRenderer->damageMonitor(pw->m_pMonitor.lock()); }; - if (!pWindow->m_isX11) + if (!pWindow->m_bIsX11) moveToZ(pWindow, top); else { // move X11 window stack @@ -1270,8 +1416,8 @@ void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) { else toMove.insert(toMove.begin(), pw); - for (auto const& w : m_windows) { - if (w->m_isMapped && !w->isHidden() && w->m_isX11 && w->x11TransientFor() == pw && w != pw && std::ranges::find(toMove, w) == toMove.end()) { + for (auto const& w : m_vWindows) { + if (w->m_bIsMapped && !w->isHidden() && w->m_bIsX11 && w->x11TransientFor() == pw && w != pw && std::ranges::find(toMove, w) == toMove.end()) { x11Stack(w, top, x11Stack); } } @@ -1285,32 +1431,32 @@ void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) { } void CCompositor::cleanupFadingOut(const MONITORID& monid) { - for (auto const& ww : m_windowsFadingOut) { + for (auto const& ww : m_vWindowsFadingOut) { auto w = ww.lock(); - if (w->monitorID() != monid && w->m_monitor) + if (w->monitorID() != monid && w->m_pMonitor) continue; - if (!w->m_fadingOut || w->m_alpha->value() == 0.f) { + if (!w->m_bFadingOut || w->m_fAlpha->value() == 0.f) { - w->m_fadingOut = false; + w->m_bFadingOut = false; - if (!w->m_readyToDelete) + if (!w->m_bReadyToDelete) continue; removeWindowFromVectorSafe(w); w.reset(); - Log::logger->log(Log::DEBUG, "Cleanup: destroyed a window"); + Debug::log(LOG, "Cleanup: destroyed a window"); return; } } bool layersDirty = false; - for (auto const& lsr : m_surfacesFadingOut) { + for (auto const& lsr : m_vSurfacesFadingOut) { auto ls = lsr.lock(); @@ -1319,79 +1465,77 @@ void CCompositor::cleanupFadingOut(const MONITORID& monid) { continue; } - if (ls->monitorID() != monid && ls->m_monitor) + if (ls->monitorID() != monid && ls->monitor) continue; // mark blur for recalc - if (ls->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || ls->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) + if (ls->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || ls->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) g_pHyprOpenGL->markBlurDirtyForMonitor(getMonitorFromID(monid)); - if (ls->m_fadingOut && ls->m_readyToDelete && ls->isFadedOut()) { - for (auto const& m : m_monitors) { - for (auto& lsl : m->m_layerSurfaceLayers) { + if (ls->fadingOut && ls->readyToDelete && ls->isFadedOut()) { + for (auto const& m : m_vMonitors) { + for (auto& lsl : m->m_aLayerSurfaceLayers) { if (!lsl.empty() && std::ranges::find_if(lsl, [&](auto& other) { return other == ls; }) != lsl.end()) { std::erase_if(lsl, [&](auto& other) { return other == ls || !other; }); } } } - std::erase_if(m_surfacesFadingOut, [ls](const auto& el) { return el.lock() == ls; }); - std::erase_if(m_layers, [ls](const auto& el) { return el == ls; }); + std::erase_if(m_vSurfacesFadingOut, [ls](const auto& el) { return el.lock() == ls; }); + std::erase_if(m_vLayers, [ls](const auto& el) { return el == ls; }); ls.reset(); - Log::logger->log(Log::DEBUG, "Cleanup: destroyed a layersurface"); + Debug::log(LOG, "Cleanup: destroyed a layersurface"); + glFlush(); // to free mem NOW. return; } } if (layersDirty) - std::erase_if(m_surfacesFadingOut, [](const auto& el) { return el.expired(); }); + std::erase_if(m_vSurfacesFadingOut, [](const auto& el) { return el.expired(); }); } void CCompositor::addToFadingOutSafe(PHLLS pLS) { - const auto FOUND = std::ranges::find_if(m_surfacesFadingOut, [&](auto& other) { return other.lock() == pLS; }); + const auto FOUND = std::ranges::find_if(m_vSurfacesFadingOut, [&](auto& other) { return other.lock() == pLS; }); - if (FOUND != m_surfacesFadingOut.end()) + if (FOUND != m_vSurfacesFadingOut.end()) return; // if it's already added, don't add it. - m_surfacesFadingOut.emplace_back(pLS); + m_vSurfacesFadingOut.emplace_back(pLS); } void CCompositor::removeFromFadingOutSafe(PHLLS ls) { - std::erase(m_surfacesFadingOut, ls); + std::erase(m_vSurfacesFadingOut, ls); } void CCompositor::addToFadingOutSafe(PHLWINDOW pWindow) { - const auto FOUND = std::ranges::find_if(m_windowsFadingOut, [&](PHLWINDOWREF& other) { return other.lock() == pWindow; }); + const auto FOUND = std::ranges::find_if(m_vWindowsFadingOut, [&](PHLWINDOWREF& other) { return other.lock() == pWindow; }); - if (FOUND != m_windowsFadingOut.end()) + if (FOUND != m_vWindowsFadingOut.end()) return; // if it's already added, don't add it. - m_windowsFadingOut.emplace_back(pWindow); + m_vWindowsFadingOut.emplace_back(pWindow); } -PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, Math::eDirection dir) { - if (dir == Math::DIRECTION_DEFAULT) +PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) { + if (!isDirection(dir)) return nullptr; - const auto PMONITOR = pWindow->m_monitor.lock(); + const auto PMONITOR = pWindow->m_pMonitor.lock(); if (!PMONITOR) return nullptr; // ?? - const auto WINDOWIDEALBB = pWindow->isFullscreen() ? CBox{PMONITOR->m_position, PMONITOR->m_size} : pWindow->getWindowIdealBoundingBoxIgnoreReserved(); - const auto PWORKSPACE = pWindow->m_workspace; + const auto WINDOWIDEALBB = pWindow->isFullscreen() ? CBox{PMONITOR->vecPosition, PMONITOR->vecSize} : pWindow->getWindowIdealBoundingBoxIgnoreReserved(); + const auto PWORKSPACE = pWindow->m_pWorkspace; - if (!PWORKSPACE) - return nullptr; // ?? - - return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow, pWindow->m_isFloating); + return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow, pWindow->m_bIsFloating); } -PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow, bool useVectorAngles) { - if (dir == Math::DIRECTION_DEFAULT) +PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, char dir, PHLWINDOW ignoreWindow, bool useVectorAngles) { + if (!isDirection(dir)) return nullptr; // 0 -> history, 1 -> shared length @@ -1405,46 +1549,17 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks PHLWINDOW leaderWindow = nullptr; if (!useVectorAngles) { - // helper to check if two rectangles are adjacent along an axis, considering slight overlaps. - // returns true if: STICKS (delta <= 2) OR rectangles overlap but no more than 50% of the smaller dimension. - static auto isAdjacent = [](const double aMin, const double aMax, const double bMin, const double bMax) -> bool { - constexpr double STICK_THRESHOLD = 2.0; - constexpr double MAX_OVERLAP_RATIO = 0.5; - - const double aEdge = aMin; - const double bEdge = bMax; - const double delta = aEdge - bEdge; - - // old STICKS check for 2px - if (std::abs(delta) < STICK_THRESHOLD) - return true; - - if (delta >= 0) - return false; - - const double overlap = -delta; - const double sizeA = aMax - aMin; - const double sizeB = bMax - bMin; - - // reject if one rectangle fully contains the other - if ((bMin <= aMin && bMax >= aMax) || (aMin <= bMin && aMax >= bMax)) - return false; - - // accept if overlap is at most 50% of the smaller dimension - return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO; - }; - - for (auto const& w : m_windows) { - if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || w->isHidden() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible()) + for (auto const& w : m_vWindows) { + if (w == ignoreWindow || !w->m_pWorkspace || !w->m_bIsMapped || w->isHidden() || (!w->isFullscreen() && w->m_bIsFloating) || !w->m_pWorkspace->isVisible()) continue; - if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace) + if (pWorkspace->m_pMonitor == w->m_pMonitor && pWorkspace != w->m_pWorkspace) continue; - if (pWorkspace->m_hasFullscreenWindow && !w->isFullscreen() && !w->m_createdOverFullscreen) + if (pWorkspace->m_bHasFullscreenWindow && !w->isFullscreen() && !w->m_bCreatedOverFullscreen) continue; - if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor) + if (!*PMONITORFALLBACK && pWorkspace->m_pMonitor != w->m_pMonitor) continue; const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved(); @@ -1455,38 +1570,44 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks double intersectLength = -1; switch (dir) { - case Math::DIRECTION_LEFT: - if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.x, POSB.x + SIZEB.x)) + case 'l': + if (STICKS(POSA.x, POSB.x + SIZEB.x)) { intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); + } break; - case Math::DIRECTION_RIGHT: - if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x)) + case 'r': + if (STICKS(POSA.x + SIZEA.x, POSB.x)) { intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); + } break; - case Math::DIRECTION_UP: - if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.y, POSB.y + SIZEB.y)) + case 't': + case 'u': + if (STICKS(POSA.y, POSB.y + SIZEB.y)) { intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); + } break; - case Math::DIRECTION_DOWN: - if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y)) + case 'b': + case 'd': + if (STICKS(POSA.y + SIZEA.y, POSB.y)) { intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); + } break; - default: break; } if (*PMETHOD == 0 /* history */) { if (intersectLength > 0) { // get idx - int windowIDX = -1; - const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); - for (int64_t i = HISTORY.size() - 1; i >= 0; --i) { - if (HISTORY[i] == w) { + int windowIDX = -1; + for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) { + if (g_pCompositor->m_vWindowFocusHistory[i].lock() == w) { windowIDX = i; break; } } + windowIDX = g_pCompositor->m_vWindowFocusHistory.size() - windowIDX; + if (windowIDX > leaderValue) { leaderValue = windowIDX; leaderWindow = w; @@ -1500,8 +1621,12 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks } } } else { - static const std::unordered_map VECTORS = { - {Math::DIRECTION_RIGHT, {1, 0}}, {Math::DIRECTION_UP, {0, -1}}, {Math::DIRECTION_DOWN, {0, 1}}, {Math::DIRECTION_LEFT, {-1, 0}}}; + if (dir == 'u') + dir = 't'; + if (dir == 'd') + dir = 'b'; + + static const std::unordered_map VECTORS = {{'r', {1, 0}}, {'t', {0, -1}}, {'b', {0, 1}}, {'l', {-1, 0}}}; // auto vectorAngles = [](const Vector2D& a, const Vector2D& b) -> double { @@ -1513,17 +1638,17 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks float bestAngleAbs = 2.0 * M_PI; constexpr float THRESHOLD = 0.3 * M_PI; - for (auto const& w : m_windows) { - if (w == ignoreWindow || !w->m_isMapped || !w->m_workspace || w->isHidden() || (!w->isFullscreen() && !w->m_isFloating) || !w->m_workspace->isVisible()) + for (auto const& w : m_vWindows) { + if (w == ignoreWindow || !w->m_bIsMapped || !w->m_pWorkspace || w->isHidden() || (!w->isFullscreen() && !w->m_bIsFloating) || !w->m_pWorkspace->isVisible()) continue; - if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace) + if (pWorkspace->m_pMonitor == w->m_pMonitor && pWorkspace != w->m_pWorkspace) continue; - if (pWorkspace->m_hasFullscreenWindow && !w->isFullscreen() && !w->m_createdOverFullscreen) + if (pWorkspace->m_bHasFullscreenWindow && !w->isFullscreen() && !w->m_bCreatedOverFullscreen) continue; - if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor) + if (!*PMONITORFALLBACK && pWorkspace->m_pMonitor != w->m_pMonitor) continue; const auto DIST = w->middle().distance(box.middle()); @@ -1539,7 +1664,7 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks } } - if (!leaderWindow && pWorkspace->m_hasFullscreenWindow) + if (!leaderWindow && pWorkspace->m_bHasFullscreenWindow) leaderWindow = pWorkspace->getFullscreenWindow(); } @@ -1551,18 +1676,18 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks template static bool isWorkspaceMatches(WINDOWPTR pWindow, const WINDOWPTR w, bool anyWorkspace) { - return anyWorkspace ? w->m_workspace && w->m_workspace->isVisible() : w->m_workspace == pWindow->m_workspace; + return anyWorkspace ? w->m_pWorkspace && w->m_pWorkspace->isVisible() : w->m_pWorkspace == pWindow->m_pWorkspace; } template static bool isFloatingMatches(WINDOWPTR w, std::optional floating) { - return !floating.has_value() || w->m_isFloating == floating.value(); + return !floating.has_value() || w->m_bIsFloating == floating.value(); } template static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional floating, bool anyWorkspace = false) { return isFloatingMatches(w, floating) && - (w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_isMapped && !w->isHidden() && (!focusableOnly || !w->m_ruleApplicator->noFocus().valueOrDefault())); + (w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_bIsMapped && !w->isHidden() && (!focusableOnly || !w->m_sWindowData.noFocus.valueOrDefault())); } template @@ -1586,39 +1711,31 @@ static PHLWINDOW getWeakWindowPred(Iterator cur, Iterator end, Iterator begin, c PHLWINDOW CCompositor::getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly, std::optional floating, bool visible, bool next) { const auto FINDER = [&](const PHLWINDOWREF& w) { return isWindowAvailableForCycle(cur, w, focusableOnly, floating, visible); }; // also m_vWindowFocusHistory has reverse order, so when it is next - we need to reverse again - const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); - return next ? getWeakWindowPred(std::ranges::find(HISTORY, cur), HISTORY.end(), HISTORY.begin(), FINDER) : - getWeakWindowPred(std::ranges::find(HISTORY | std::views::reverse, cur), HISTORY.rend(), HISTORY.rbegin(), FINDER); + return next ? + getWeakWindowPred(std::ranges::find(std::ranges::reverse_view(m_vWindowFocusHistory), cur), m_vWindowFocusHistory.rend(), m_vWindowFocusHistory.rbegin(), FINDER) : + getWeakWindowPred(std::ranges::find(m_vWindowFocusHistory, cur), m_vWindowFocusHistory.end(), m_vWindowFocusHistory.begin(), FINDER); } PHLWINDOW CCompositor::getWindowCycle(PHLWINDOW cur, bool focusableOnly, std::optional floating, bool visible, bool prev) { const auto FINDER = [&](const PHLWINDOW& w) { return isWindowAvailableForCycle(cur, w, focusableOnly, floating, visible); }; - return prev ? getWindowPred(std::ranges::find(m_windows | std::views::reverse, cur), m_windows.rend(), m_windows.rbegin(), FINDER) : - getWindowPred(std::ranges::find(m_windows, cur), m_windows.end(), m_windows.begin(), FINDER); + return prev ? getWindowPred(std::ranges::find(std::ranges::reverse_view(m_vWindows), cur), m_vWindows.rend(), m_vWindows.rbegin(), FINDER) : + getWindowPred(std::ranges::find(m_vWindows, cur), m_vWindows.end(), m_vWindows.begin(), FINDER); } WORKSPACEID CCompositor::getNextAvailableNamedWorkspace() { WORKSPACEID lowest = -1337 + 1; - for (auto const& w : getWorkspaces()) { - if (w->m_id < -1 && w->m_id < lowest) - lowest = w->m_id; - } - - // Give priority to persistent workspaces to avoid any conflicts between them. - for (auto const& rule : g_pConfigManager->getAllWorkspaceRules()) { - if (!rule.isPersistent) - continue; - if (rule.workspaceId < -1 && rule.workspaceId < lowest) - lowest = rule.workspaceId; + for (auto const& w : m_vWorkspaces) { + if (w->m_iID < -1 && w->m_iID < lowest) + lowest = w->m_iID; } return lowest - 1; } PHLWORKSPACE CCompositor::getWorkspaceByName(const std::string& name) { - for (auto const& w : getWorkspaces()) { - if (w->m_name == name && !w->inert()) - return w.lock(); + for (auto const& w : m_vWorkspaces) { + if (w->m_szName == name && !w->inert()) + return w; } return nullptr; @@ -1631,67 +1748,47 @@ PHLWORKSPACE CCompositor::getWorkspaceByString(const std::string& str) { try { return getWorkspaceByID(getWorkspaceIDNameFromString(str).id); - } catch (std::exception& e) { Log::logger->log(Log::ERR, "Error in getWorkspaceByString, invalid id"); } + } catch (std::exception& e) { Debug::log(ERR, "Error in getWorkspaceByString, invalid id"); } return nullptr; } bool CCompositor::isPointOnAnyMonitor(const Vector2D& point) { return std::ranges::any_of( - m_monitors, [&](const PHLMONITOR& m) { return VECINRECT(point, m->m_position.x, m->m_position.y, m->m_size.x + m->m_position.x, m->m_size.y + m->m_position.y); }); + m_vMonitors, [&](const PHLMONITOR& m) { return VECINRECT(point, m->vecPosition.x, m->vecPosition.y, m->vecSize.x + m->vecPosition.x, m->vecSize.y + m->vecPosition.y); }); } bool CCompositor::isPointOnReservedArea(const Vector2D& point, const PHLMONITOR pMonitor) { const auto PMONITOR = pMonitor ? pMonitor : getMonitorFromVector(point); - auto box = PMONITOR->logicalBox(); - if (VECNOTINRECT(point, box.x - 1, box.y - 1, box.x + box.w + 1, box.y + box.h + 1)) - return false; + const auto XY1 = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft; + const auto XY2 = PMONITOR->vecPosition + PMONITOR->vecSize - PMONITOR->vecReservedBottomRight; - PMONITOR->m_reservedArea.applyip(box); - - return VECNOTINRECT(point, box.x, box.y, box.x + box.w, box.y + box.h); + return VECNOTINRECT(point, XY1.x, XY1.y, XY2.x, XY2.y); } -std::optional CCompositor::calculateX11WorkArea() { - static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - // We more than likely won't be able to calculate one - // and even if we could this is minor - if (m_monitors.size() > 1 || m_monitors.empty()) - return std::nullopt; - - const auto M = m_monitors.front(); - - // we ignore monitor->m_position on purpose - CBox box = M->logicalBoxMinusReserved().translate(-M->m_position); - if ((*PXWLFORCESCALEZERO)) - box.scale(M->m_scale); - - return box.translate(M->m_xwaylandPosition); +PHLMONITOR CCompositor::getMonitorInDirection(const char& dir) { + return getMonitorInDirection(m_pLastMonitor.lock(), dir); } -PHLMONITOR CCompositor::getMonitorInDirection(Math::eDirection dir) { - return getMonitorInDirection(Desktop::focusState()->monitor(), dir); -} - -PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, Math::eDirection dir) { +PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, const char& dir) { if (!pSourceMonitor) return nullptr; - const auto POSA = pSourceMonitor->m_position; - const auto SIZEA = pSourceMonitor->m_size; + const auto POSA = pSourceMonitor->vecPosition; + const auto SIZEA = pSourceMonitor->vecSize; auto longestIntersect = -1; PHLMONITOR longestIntersectMonitor = nullptr; - for (auto const& m : m_monitors) { - if (m == pSourceMonitor) + for (auto const& m : m_vMonitors) { + if (m == m_pLastMonitor) continue; - const auto POSB = m->m_position; - const auto SIZEB = m->m_size; + const auto POSB = m->vecPosition; + const auto SIZEB = m->vecSize; switch (dir) { - case Math::DIRECTION_LEFT: + case 'l': if (STICKS(POSA.x, POSB.x + SIZEB.x)) { const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); if (INTERSECTLEN > longestIntersect) { @@ -1700,7 +1797,7 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, Math::e } } break; - case Math::DIRECTION_RIGHT: + case 'r': if (STICKS(POSA.x + SIZEA.x, POSB.x)) { const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y)); if (INTERSECTLEN > longestIntersect) { @@ -1709,7 +1806,8 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, Math::e } } break; - case Math::DIRECTION_UP: + case 't': + case 'u': if (STICKS(POSA.y, POSB.y + SIZEB.y)) { const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); if (INTERSECTLEN > longestIntersect) { @@ -1718,7 +1816,8 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, Math::e } } break; - case Math::DIRECTION_DOWN: + case 'b': + case 'd': if (STICKS(POSA.y + SIZEA.y, POSB.y)) { const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x)); if (INTERSECTLEN > longestIntersect) { @@ -1727,7 +1826,6 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, Math::e } } break; - default: break; } } @@ -1738,149 +1836,236 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, Math::e } void CCompositor::updateAllWindowsAnimatedDecorationValues() { - for (auto const& w : m_windows) { - if (!w->m_isMapped) + for (auto const& w : m_vWindows) { + if (!w->m_bIsMapped) continue; - w->updateDecorationValues(); + updateWindowAnimatedDecorationValues(w); } } +void CCompositor::updateWindowAnimatedDecorationValues(PHLWINDOW pWindow) { + // optimization + static auto PACTIVECOL = CConfigValue("general:col.active_border"); + static auto PINACTIVECOL = CConfigValue("general:col.inactive_border"); + static auto PNOGROUPACTIVECOL = CConfigValue("general:col.nogroup_border_active"); + static auto PNOGROUPINACTIVECOL = CConfigValue("general:col.nogroup_border"); + static auto PGROUPACTIVECOL = CConfigValue("group:col.border_active"); + static auto PGROUPINACTIVECOL = CConfigValue("group:col.border_inactive"); + static auto PGROUPACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_active"); + static auto PGROUPINACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_inactive"); + static auto PINACTIVEALPHA = CConfigValue("decoration:inactive_opacity"); + static auto PACTIVEALPHA = CConfigValue("decoration:active_opacity"); + static auto PFULLSCREENALPHA = CConfigValue("decoration:fullscreen_opacity"); + static auto PSHADOWCOL = CConfigValue("decoration:shadow:color"); + static auto PSHADOWCOLINACTIVE = CConfigValue("decoration:shadow:color_inactive"); + static auto PDIMSTRENGTH = CConfigValue("decoration:dim_strength"); + static auto PDIMENABLED = CConfigValue("decoration:dim_inactive"); + + auto* const ACTIVECOL = (CGradientValueData*)(PACTIVECOL.ptr())->getData(); + auto* const INACTIVECOL = (CGradientValueData*)(PINACTIVECOL.ptr())->getData(); + auto* const NOGROUPACTIVECOL = (CGradientValueData*)(PNOGROUPACTIVECOL.ptr())->getData(); + auto* const NOGROUPINACTIVECOL = (CGradientValueData*)(PNOGROUPINACTIVECOL.ptr())->getData(); + auto* const GROUPACTIVECOL = (CGradientValueData*)(PGROUPACTIVECOL.ptr())->getData(); + auto* const GROUPINACTIVECOL = (CGradientValueData*)(PGROUPINACTIVECOL.ptr())->getData(); + auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPACTIVELOCKEDCOL.ptr())->getData(); + auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPINACTIVELOCKEDCOL.ptr())->getData(); + + auto setBorderColor = [&](CGradientValueData grad) -> void { + if (grad == pWindow->m_cRealBorderColor) + return; + + pWindow->m_cRealBorderColorPrevious = pWindow->m_cRealBorderColor; + pWindow->m_cRealBorderColor = grad; + pWindow->m_fBorderFadeAnimationProgress->setValueAndWarp(0.f); + *pWindow->m_fBorderFadeAnimationProgress = 1.f; + }; + + const bool IS_SHADOWED_BY_MODAL = pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel && pWindow->m_pXDGSurface->toplevel->anyChildModal(); + + // border + const auto RENDERDATA = g_pLayoutManager->getCurrentLayout()->requestRenderHints(pWindow); + if (RENDERDATA.isBorderGradient) + setBorderColor(*RENDERDATA.borderGradient); + else { + const bool GROUPLOCKED = pWindow->m_sGroupData.pNextWindow.lock() ? pWindow->getGroupHead()->m_sGroupData.locked : false; + if (pWindow == m_pLastWindow) { + const auto* const ACTIVECOLOR = + !pWindow->m_sGroupData.pNextWindow.lock() ? (!pWindow->m_sGroupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL); + setBorderColor(pWindow->m_sWindowData.activeBorderColor.valueOr(*ACTIVECOLOR)); + } else { + const auto* const INACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow.lock() ? (!pWindow->m_sGroupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : + (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL); + setBorderColor(pWindow->m_sWindowData.inactiveBorderColor.valueOr(*INACTIVECOLOR)); + } + } + + // opacity + const auto PWORKSPACE = pWindow->m_pWorkspace; + if (pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) { + *pWindow->m_fActiveInactiveAlpha = pWindow->m_sWindowData.alphaFullscreen.valueOrDefault().applyAlpha(*PFULLSCREENALPHA); + } else { + if (pWindow == m_pLastWindow) + *pWindow->m_fActiveInactiveAlpha = pWindow->m_sWindowData.alpha.valueOrDefault().applyAlpha(*PACTIVEALPHA); + else + *pWindow->m_fActiveInactiveAlpha = pWindow->m_sWindowData.alphaInactive.valueOrDefault().applyAlpha(*PINACTIVEALPHA); + } + + // dim + float goalDim = 1.F; + if (pWindow == m_pLastWindow.lock() || pWindow->m_sWindowData.noDim.valueOrDefault() || !*PDIMENABLED) + goalDim = 0; + else + goalDim = *PDIMSTRENGTH; + + if (IS_SHADOWED_BY_MODAL) + goalDim += (1.F - goalDim) / 2.F; + + *pWindow->m_fDimPercent = goalDim; + + // shadow + if (!pWindow->isX11OverrideRedirect() && !pWindow->m_bX11DoesntWantBorders) { + if (pWindow == m_pLastWindow) + *pWindow->m_cRealShadowColor = CHyprColor(*PSHADOWCOL); + else + *pWindow->m_cRealShadowColor = CHyprColor(*PSHADOWCOLINACTIVE != INT64_MAX ? *PSHADOWCOLINACTIVE : *PSHADOWCOL); + } else { + pWindow->m_cRealShadowColor->setValueAndWarp(CHyprColor(0, 0, 0, 0)); // no shadow + } + + pWindow->updateWindowDecos(); +} + MONITORID CCompositor::getNextAvailableMonitorID(std::string const& name) { // reuse ID if it's already in the map, and the monitor with that ID is not being used by another monitor - if (m_monitorIDMap.contains(name) && !std::ranges::any_of(m_realMonitors, [&](auto m) { return m->m_id == m_monitorIDMap[name]; })) - return m_monitorIDMap[name]; + if (m_mMonitorIDMap.contains(name) && !std::ranges::any_of(m_vRealMonitors, [&](auto m) { return m->ID == m_mMonitorIDMap[name]; })) + return m_mMonitorIDMap[name]; // otherwise, find minimum available ID that is not in the map std::unordered_set usedIDs; - for (auto const& monitor : m_realMonitors) { - usedIDs.insert(monitor->m_id); + for (auto const& monitor : m_vRealMonitors) { + usedIDs.insert(monitor->ID); } MONITORID nextID = 0; while (usedIDs.contains(nextID)) { nextID++; } - m_monitorIDMap[name] = nextID; + m_mMonitorIDMap[name] = nextID; return nextID; } void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitorB) { - const auto PWORKSPACEA = pMonitorA->m_activeWorkspace; - const auto PWORKSPACEB = pMonitorB->m_activeWorkspace; + const auto PWORKSPACEA = pMonitorA->activeWorkspace; + const auto PWORKSPACEB = pMonitorB->activeWorkspace; - PWORKSPACEA->m_monitor = pMonitorB; - PWORKSPACEA->m_events.monitorChanged.emit(); + PWORKSPACEA->m_pMonitor = pMonitorB; + PWORKSPACEA->moveToMonitor(pMonitorB->ID); - for (auto const& w : m_windows) { - if (w->m_workspace == PWORKSPACEA) { - if (w->m_pinned) { - w->m_workspace = PWORKSPACEB; + for (auto const& w : m_vWindows) { + if (w->m_pWorkspace == PWORKSPACEA) { + if (w->m_bPinned) { + w->m_pWorkspace = PWORKSPACEB; continue; } - w->m_monitor = pMonitorB; + w->m_pMonitor = pMonitorB; // additionally, move floating and fs windows manually - if (w->m_isFloating) - w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-pMonitorA->m_position + pMonitorB->m_position)); + if (w->m_bIsFloating) + *w->m_vRealPosition = w->m_vRealPosition->goal() - pMonitorA->vecPosition + pMonitorB->vecPosition; if (w->isFullscreen()) { - *w->m_realPosition = pMonitorB->m_position; - *w->m_realSize = pMonitorB->m_size; + *w->m_vRealPosition = pMonitorB->vecPosition; + *w->m_vRealSize = pMonitorB->vecSize; } w->updateToplevel(); } } - PWORKSPACEB->m_monitor = pMonitorA; - PWORKSPACEB->m_events.monitorChanged.emit(); + PWORKSPACEB->m_pMonitor = pMonitorA; + PWORKSPACEB->moveToMonitor(pMonitorA->ID); - for (auto const& w : m_windows) { - if (w->m_workspace == PWORKSPACEB) { - if (w->m_pinned) { - w->m_workspace = PWORKSPACEA; + for (auto const& w : m_vWindows) { + if (w->m_pWorkspace == PWORKSPACEB) { + if (w->m_bPinned) { + w->m_pWorkspace = PWORKSPACEA; continue; } - w->m_monitor = pMonitorA; + w->m_pMonitor = pMonitorA; // additionally, move floating and fs windows manually - if (w->m_isFloating) - w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-pMonitorB->m_position + pMonitorA->m_position)); + if (w->m_bIsFloating) + *w->m_vRealPosition = w->m_vRealPosition->goal() - pMonitorB->vecPosition + pMonitorA->vecPosition; if (w->isFullscreen()) { - *w->m_realPosition = pMonitorA->m_position; - *w->m_realSize = pMonitorA->m_size; + *w->m_vRealPosition = pMonitorA->vecPosition; + *w->m_vRealSize = pMonitorA->vecSize; } w->updateToplevel(); } } - pMonitorA->m_activeWorkspace = PWORKSPACEB; - pMonitorB->m_activeWorkspace = PWORKSPACEA; + pMonitorA->activeWorkspace = PWORKSPACEB; + pMonitorB->activeWorkspace = PWORKSPACEA; - g_layoutManager->recalculateMonitor(pMonitorA); - g_layoutManager->recalculateMonitor(pMonitorB); + PWORKSPACEA->rememberPrevWorkspace(PWORKSPACEB); + PWORKSPACEB->rememberPrevWorkspace(PWORKSPACEA); - g_pHyprRenderer->damageMonitor(pMonitorB); - g_pHyprRenderer->damageMonitor(pMonitorA); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitorA->ID); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitorB->ID); - g_pDesktopAnimationManager->setFullscreenFadeAnimation( - PWORKSPACEB, PWORKSPACEB->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); - g_pDesktopAnimationManager->setFullscreenFadeAnimation( - PWORKSPACEA, PWORKSPACEA->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); + updateFullscreenFadeOnWorkspace(PWORKSPACEB); + updateFullscreenFadeOnWorkspace(PWORKSPACEA); - if (pMonitorA->m_id == Desktop::focusState()->monitor()->m_id || pMonitorB->m_id == Desktop::focusState()->monitor()->m_id) { - const auto LASTWIN = pMonitorA->m_id == Desktop::focusState()->monitor()->m_id ? PWORKSPACEB->getLastFocusedWindow() : PWORKSPACEA->getLastFocusedWindow(); - Desktop::focusState()->fullWindowFocus( - LASTWIN ? LASTWIN : - (g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), - Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING)), - Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE); + if (pMonitorA->ID == g_pCompositor->m_pLastMonitor->ID || pMonitorB->ID == g_pCompositor->m_pLastMonitor->ID) { + const auto LASTWIN = pMonitorA->ID == g_pCompositor->m_pLastMonitor->ID ? PWORKSPACEB->getLastFocusedWindow() : PWORKSPACEA->getLastFocusedWindow(); + g_pCompositor->focusWindow(LASTWIN ? LASTWIN : + (g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING))); - const auto PNEWWORKSPACE = pMonitorA->m_id == Desktop::focusState()->monitor()->m_id ? PWORKSPACEB : PWORKSPACEA; - g_pEventManager->postEvent(SHyprIPCEvent{.event = "workspace", .data = PNEWWORKSPACE->m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "workspacev2", .data = std::format("{},{}", PNEWWORKSPACE->m_id, PNEWWORKSPACE->m_name)}); - Event::bus()->m_events.workspace.active.emit(PNEWWORKSPACE); + const auto PNEWWORKSPACE = pMonitorA->ID == g_pCompositor->m_pLastMonitor->ID ? PWORKSPACEB : PWORKSPACEA; + g_pEventManager->postEvent(SHyprIPCEvent{.event = "workspace", .data = PNEWWORKSPACE->m_szName}); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "workspacev2", .data = std::format("{},{}", PNEWWORKSPACE->m_iID, PNEWWORKSPACE->m_szName)}); + EMIT_HOOK_EVENT("workspace", PNEWWORKSPACE); } - // events - g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = PWORKSPACEA->m_name + "," + pMonitorB->m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", PWORKSPACEA->m_id, PWORKSPACEA->m_name, pMonitorB->m_name)}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = PWORKSPACEB->m_name + "," + pMonitorA->m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", PWORKSPACEB->m_id, PWORKSPACEB->m_name, pMonitorA->m_name)}); + // event + g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = PWORKSPACEA->m_szName + "," + pMonitorB->szName}); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", PWORKSPACEA->m_iID, PWORKSPACEA->m_szName, pMonitorB->szName)}); + EMIT_HOOK_EVENT("moveWorkspace", (std::vector{PWORKSPACEA, pMonitorB})); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = PWORKSPACEB->m_szName + "," + pMonitorA->szName}); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", PWORKSPACEB->m_iID, PWORKSPACEB->m_szName, pMonitorA->szName)}); - Event::bus()->m_events.workspace.moveToMonitor.emit(PWORKSPACEA, pMonitorB); - Event::bus()->m_events.workspace.moveToMonitor.emit(PWORKSPACEB, pMonitorA); + EMIT_HOOK_EVENT("moveWorkspace", (std::vector{PWORKSPACEB, pMonitorA})); } PHLMONITOR CCompositor::getMonitorFromString(const std::string& name) { if (name == "current") - return Desktop::focusState()->monitor(); + return g_pCompositor->m_pLastMonitor.lock(); else if (isDirection(name)) - return getMonitorInDirection(Math::fromChar(name[0])); + return getMonitorInDirection(name[0]); else if (name[0] == '+' || name[0] == '-') { // relative - if (m_monitors.size() == 1) - return *m_monitors.begin(); + if (m_vMonitors.size() == 1) + return *m_vMonitors.begin(); const auto OFFSET = name[0] == '-' ? name : name.substr(1); if (!isNumber(OFFSET)) { - Log::logger->log(Log::ERR, "Error in getMonitorFromString: Not a number in relative."); + Debug::log(ERR, "Error in getMonitorFromString: Not a number in relative."); return nullptr; } int offsetLeft = std::stoi(OFFSET); - offsetLeft = offsetLeft < 0 ? -((-offsetLeft) % m_monitors.size()) : offsetLeft % m_monitors.size(); + offsetLeft = offsetLeft < 0 ? -((-offsetLeft) % m_vMonitors.size()) : offsetLeft % m_vMonitors.size(); int currentPlace = 0; - for (int i = 0; i < sc(m_monitors.size()); i++) { - if (m_monitors[i] == Desktop::focusState()->monitor()) { + for (int i = 0; i < (int)m_vMonitors.size(); i++) { + if (m_vMonitors[i] == m_pLastMonitor) { currentPlace = i; break; } @@ -1889,17 +2074,17 @@ PHLMONITOR CCompositor::getMonitorFromString(const std::string& name) { currentPlace += offsetLeft; if (currentPlace < 0) { - currentPlace = m_monitors.size() + currentPlace; + currentPlace = m_vMonitors.size() + currentPlace; } else { - currentPlace = currentPlace % m_monitors.size(); + currentPlace = currentPlace % m_vMonitors.size(); } - if (currentPlace != std::clamp(currentPlace, 0, sc(m_monitors.size()) - 1)) { - Log::logger->log(Log::WARN, "Error in getMonitorFromString: Vaxry's code sucks."); - currentPlace = std::clamp(currentPlace, 0, sc(m_monitors.size()) - 1); + if (currentPlace != std::clamp(currentPlace, 0, (int)m_vMonitors.size() - 1)) { + Debug::log(WARN, "Error in getMonitorFromString: Vaxry's code sucks."); + currentPlace = std::clamp(currentPlace, 0, (int)m_vMonitors.size() - 1); } - return m_monitors[currentPlace]; + return m_vMonitors[currentPlace]; } else if (isNumber(name)) { // change by ID MONITORID monID = MONITOR_INVALID; @@ -1907,19 +2092,19 @@ PHLMONITOR CCompositor::getMonitorFromString(const std::string& name) { monID = std::stoi(name); } catch (std::exception& e) { // shouldn't happen but jic - Log::logger->log(Log::ERR, "Error in getMonitorFromString: invalid num"); + Debug::log(ERR, "Error in getMonitorFromString: invalid num"); return nullptr; } - if (monID > -1 && monID < sc(m_monitors.size())) { + if (monID > -1 && monID < (MONITORID)m_vMonitors.size()) { return getMonitorFromID(monID); } else { - Log::logger->log(Log::ERR, "Error in getMonitorFromString: invalid arg 1"); + Debug::log(ERR, "Error in getMonitorFromString: invalid arg 1"); return nullptr; } } else { - for (auto const& m : m_monitors) { - if (!m->m_output) + for (auto const& m : m_vMonitors) { + if (!m->output) continue; if (m->matchesStaticSelector(name)) { @@ -1932,30 +2117,26 @@ PHLMONITOR CCompositor::getMonitorFromString(const std::string& name) { } void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMonitor, bool noWarpCursor) { - static auto PHIDESPECIALONWORKSPACECHANGE = CConfigValue("binds:hide_special_on_workspace_change"); - if (!pWorkspace || !pMonitor) return; - if (pWorkspace->m_monitor == pMonitor) + if (pWorkspace->m_pMonitor == pMonitor) return; - Log::logger->log(Log::DEBUG, "moveWorkspaceToMonitor: Moving {} to monitor {}", pWorkspace->m_id, pMonitor->m_id); + Debug::log(LOG, "moveWorkspaceToMonitor: Moving {} to monitor {}", pWorkspace->m_iID, pMonitor->ID); - const auto POLDMON = pWorkspace->m_monitor.lock(); + const auto POLDMON = pWorkspace->m_pMonitor.lock(); - const bool SWITCHINGISACTIVE = POLDMON ? POLDMON->m_activeWorkspace == pWorkspace : false; + const bool SWITCHINGISACTIVE = POLDMON ? POLDMON->activeWorkspace == pWorkspace : false; // fix old mon WORKSPACEID nextWorkspaceOnMonitorID = WORKSPACE_INVALID; if (!SWITCHINGISACTIVE) - nextWorkspaceOnMonitorID = pWorkspace->m_id; + nextWorkspaceOnMonitorID = pWorkspace->m_iID; else { - PHLWORKSPACE newWorkspace; // for holding a ref to the new workspace that might be created - - for (auto const& w : getWorkspaces()) { - if (w->m_monitor == POLDMON && w->m_id != pWorkspace->m_id && !w->m_isSpecialWorkspace) { - nextWorkspaceOnMonitorID = w->m_id; + for (auto const& w : m_vWorkspaces) { + if (w->m_pMonitor == POLDMON && w->m_iID != pWorkspace->m_iID && !w->m_bIsSpecialWorkspace) { + nextWorkspaceOnMonitorID = w->m_iID; break; } } @@ -1969,219 +2150,213 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo }()) nextWorkspaceOnMonitorID++; - Log::logger->log(Log::DEBUG, "moveWorkspaceToMonitor: Plugging gap with new {}", nextWorkspaceOnMonitorID); + Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with new {}", nextWorkspaceOnMonitorID); - if (POLDMON) - newWorkspace = g_pCompositor->createNewWorkspace(nextWorkspaceOnMonitorID, POLDMON->m_id); + g_pCompositor->createNewWorkspace(nextWorkspaceOnMonitorID, POLDMON->ID); } - Log::logger->log(Log::DEBUG, "moveWorkspaceToMonitor: Plugging gap with existing {}", nextWorkspaceOnMonitorID); - if (POLDMON) - POLDMON->changeWorkspace(nextWorkspaceOnMonitorID, false, true, true); + Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with existing {}", nextWorkspaceOnMonitorID); + POLDMON->changeWorkspace(nextWorkspaceOnMonitorID, false, true, true); } // move the workspace - pWorkspace->m_monitor = pMonitor; - pWorkspace->m_space->recheckWorkArea(); - pWorkspace->m_events.monitorChanged.emit(); + pWorkspace->m_pMonitor = pMonitor; + pWorkspace->moveToMonitor(pMonitor->ID); - for (auto const& w : m_windows) { - if (w->m_workspace == pWorkspace) { - if (w->m_pinned) { - w->m_workspace = g_pCompositor->getWorkspaceByID(nextWorkspaceOnMonitorID); + for (auto const& w : m_vWindows) { + if (w->m_pWorkspace == pWorkspace) { + if (w->m_bPinned) { + w->m_pWorkspace = g_pCompositor->getWorkspaceByID(nextWorkspaceOnMonitorID); continue; } - w->m_monitor = pMonitor; + w->m_pMonitor = pMonitor; // additionally, move floating and fs windows manually - if (w->m_isMapped && !w->isHidden()) { + if (w->m_bIsMapped && !w->isHidden()) { if (POLDMON) { - if (w->m_isFloating) - w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-POLDMON->m_position + pMonitor->m_position)); + if (w->m_bIsFloating) + *w->m_vRealPosition = w->m_vRealPosition->goal() - POLDMON->vecPosition + pMonitor->vecPosition; if (w->isFullscreen()) { - *w->m_realPosition = pMonitor->m_position; - *w->m_realSize = pMonitor->m_size; + *w->m_vRealPosition = pMonitor->vecPosition; + *w->m_vRealSize = pMonitor->vecSize; } - } else - w->layoutTarget()->setPositionGlobal(CBox{Vector2D{ - (pMonitor->m_size.x != 0) ? sc(w->m_realPosition->goal().x) % sc(pMonitor->m_size.x) : 0, - (pMonitor->m_size.y != 0) ? sc(w->m_realPosition->goal().y) % sc(pMonitor->m_size.y) : 0, - }, - w->layoutTarget()->position().size()}); + } else { + *w->m_vRealPosition = Vector2D{(int)w->m_vRealPosition->goal().x % (int)pMonitor->vecSize.x, (int)w->m_vRealPosition->goal().y % (int)pMonitor->vecSize.y}; + } } w->updateToplevel(); } } - if (SWITCHINGISACTIVE && POLDMON == Desktop::focusState()->monitor()) { // if it was active, preserve its' status. If it wasn't, don't. - Log::logger->log(Log::DEBUG, "moveWorkspaceToMonitor: SWITCHINGISACTIVE, active {} -> {}", pMonitor->activeWorkspaceID(), pWorkspace->m_id); + if (SWITCHINGISACTIVE && POLDMON == g_pCompositor->m_pLastMonitor) { // if it was active, preserve its' status. If it wasn't, don't. + Debug::log(LOG, "moveWorkspaceToMonitor: SWITCHINGISACTIVE, active {} -> {}", pMonitor->activeWorkspaceID(), pWorkspace->m_iID); - if (valid(pMonitor->m_activeWorkspace)) { - pMonitor->m_activeWorkspace->m_visible = false; - g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_OUT, false); + if (valid(pMonitor->activeWorkspace)) { + pMonitor->activeWorkspace->m_bVisible = false; + pMonitor->activeWorkspace->startAnim(false, false); } - if (*PHIDESPECIALONWORKSPACECHANGE) - pMonitor->setSpecialWorkspace(nullptr); + setActiveMonitor(pMonitor); + pMonitor->activeWorkspace = pWorkspace; + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID); - Desktop::focusState()->rawMonitorFocus(pMonitor); - - auto oldWorkspace = pMonitor->m_activeWorkspace; - pMonitor->m_activeWorkspace = pWorkspace; - - if (oldWorkspace) - oldWorkspace->m_events.activeChanged.emit(); - - pWorkspace->m_events.activeChanged.emit(); - - g_layoutManager->recalculateMonitor(pMonitor); - g_pHyprRenderer->damageMonitor(pMonitor); - - g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); - pWorkspace->m_visible = true; + pWorkspace->startAnim(true, true, true); + pWorkspace->m_bVisible = true; if (!noWarpCursor) - g_pPointerManager->warpTo(pMonitor->m_position + pMonitor->m_transformedSize / 2.F); + g_pPointerManager->warpTo(pMonitor->vecPosition + pMonitor->vecTransformedSize / 2.F); g_pInputManager->sendMotionEventsToFocused(); } // finalize if (POLDMON) { - g_layoutManager->recalculateMonitor(POLDMON); - if (valid(POLDMON->m_activeWorkspace)) - g_pDesktopAnimationManager->setFullscreenFadeAnimation(POLDMON->m_activeWorkspace, - POLDMON->m_activeWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : - CDesktopAnimationManager::ANIMATION_TYPE_OUT); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID); + if (valid(POLDMON->activeWorkspace)) + updateFullscreenFadeOnWorkspace(POLDMON->activeWorkspace); updateSuspendedStates(); } - g_pDesktopAnimationManager->setFullscreenFadeAnimation( - pWorkspace, pWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); + updateFullscreenFadeOnWorkspace(pWorkspace); updateSuspendedStates(); // event - g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = pWorkspace->m_name + "," + pMonitor->m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", pWorkspace->m_id, pWorkspace->m_name, pMonitor->m_name)}); - - Event::bus()->m_events.workspace.moveToMonitor.emit(pWorkspace, pMonitor); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspace", .data = pWorkspace->m_szName + "," + pMonitor->szName}); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveworkspacev2", .data = std::format("{},{},{}", pWorkspace->m_iID, pWorkspace->m_szName, pMonitor->szName)}); + EMIT_HOOK_EVENT("moveWorkspace", (std::vector{pWorkspace, pMonitor})); } bool CCompositor::workspaceIDOutOfBounds(const WORKSPACEID& id) { WORKSPACEID lowestID = INT64_MAX; WORKSPACEID highestID = INT64_MIN; - for (auto const& w : getWorkspaces()) { - if (w->m_isSpecialWorkspace) + for (auto const& w : m_vWorkspaces) { + if (w->m_bIsSpecialWorkspace) continue; - lowestID = std::min(w->m_id, lowestID); - highestID = std::max(w->m_id, highestID); + lowestID = std::min(w->m_iID, lowestID); + highestID = std::max(w->m_iID, highestID); } return std::clamp(id, lowestID, highestID) != id; } -void CCompositor::changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON) { - setWindowFullscreenClient( - PWINDOW, - sc(ON ? sc(PWINDOW->m_fullscreenState.client) | sc(MODE) : (sc(PWINDOW->m_fullscreenState.client) & sc(~MODE)))); +void CCompositor::updateFullscreenFadeOnWorkspace(PHLWORKSPACE pWorkspace) { + + if (!pWorkspace) + return; + + const auto FULLSCREEN = pWorkspace->m_bHasFullscreenWindow; + + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace == pWorkspace) { + + if (w->m_bFadingOut || w->m_bPinned || w->isFullscreen()) + continue; + + if (!FULLSCREEN) + *w->m_fAlpha = 1.f; + else if (!w->isFullscreen()) + *w->m_fAlpha = !w->m_bCreatedOverFullscreen ? 0.f : 1.f; + } + } + + const auto PMONITOR = pWorkspace->m_pMonitor.lock(); + + if (pWorkspace->m_iID == PMONITOR->activeWorkspaceID() || pWorkspace->m_iID == PMONITOR->activeSpecialWorkspaceID()) { + for (auto const& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { + if (!ls->fadingOut) + *ls->alpha = FULLSCREEN && pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f; + } + } +} + +void CCompositor::changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON) { + setWindowFullscreenClient(PWINDOW, + (eFullscreenMode)(ON ? (uint8_t)PWINDOW->m_sFullscreenState.client | (uint8_t)MODE : ((uint8_t)PWINDOW->m_sFullscreenState.client & (uint8_t)~MODE))); } -// TODO: move fs functions to Desktop:: void CCompositor::setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) { - if (PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault()) - setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = MODE, .client = MODE}); + if (PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault()) + setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = MODE}); else - setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = MODE, .client = PWINDOW->m_fullscreenState.client}); + setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = PWINDOW->m_sFullscreenState.client}); } void CCompositor::setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) { - if (PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault()) - setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = MODE, .client = MODE}); + if (PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault()) + setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = MODE}); else - setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = PWINDOW->m_fullscreenState.internal, .client = MODE}); + setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = PWINDOW->m_sFullscreenState.internal, .client = MODE}); } -void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::View::SFullscreenState state) { +void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenState state) { static auto PDIRECTSCANOUT = CConfigValue("render:direct_scanout"); static auto PALLOWPINFULLSCREEN = CConfigValue("binds:allow_pin_fullscreen"); - if (!validMapped(PWINDOW) || g_pCompositor->m_unsafeState) + if (!validMapped(PWINDOW) || g_pCompositor->m_bUnsafeState) return; - state.internal = std::clamp(state.internal, sc(0), FSMODE_MAX); - state.client = std::clamp(state.client, sc(0), FSMODE_MAX); + state.internal = std::clamp(state.internal, (eFullscreenMode)0, FSMODE_MAX); + state.client = std::clamp(state.client, (eFullscreenMode)0, FSMODE_MAX); - const auto PMONITOR = PWINDOW->m_monitor.lock(); - const auto PWORKSPACE = PWINDOW->m_workspace; + const auto PMONITOR = PWINDOW->m_pMonitor.lock(); + const auto PWORKSPACE = PWINDOW->m_pWorkspace; - const eFullscreenMode CURRENT_EFFECTIVE_MODE = sc(std::bit_floor(sc(PWINDOW->m_fullscreenState.internal))); - const eFullscreenMode EFFECTIVE_MODE = sc(std::bit_floor(sc(state.internal))); + const eFullscreenMode CURRENT_EFFECTIVE_MODE = (eFullscreenMode)std::bit_floor((uint8_t)PWINDOW->m_sFullscreenState.internal); + const eFullscreenMode EFFECTIVE_MODE = (eFullscreenMode)std::bit_floor((uint8_t)state.internal); - if (PWINDOW->m_isFloating && CURRENT_EFFECTIVE_MODE == FSMODE_NONE && EFFECTIVE_MODE != FSMODE_NONE) - g_pHyprRenderer->damageWindow(PWINDOW); - - if (*PALLOWPINFULLSCREEN && !PWINDOW->m_pinFullscreened && !PWINDOW->isFullscreen() && PWINDOW->m_pinned) { - PWINDOW->m_pinned = false; - PWINDOW->m_pinFullscreened = true; + if (*PALLOWPINFULLSCREEN && !PWINDOW->m_bPinFullscreened && !PWINDOW->isFullscreen() && PWINDOW->m_bPinned) { + PWINDOW->m_bPinned = false; + PWINDOW->m_bPinFullscreened = true; } - if (PWORKSPACE->m_hasFullscreenWindow && !PWINDOW->isFullscreen()) + if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->isFullscreen()) setWindowFullscreenInternal(PWORKSPACE->getFullscreenWindow(), FSMODE_NONE); - const bool CHANGEINTERNAL = !PWINDOW->m_pinned && CURRENT_EFFECTIVE_MODE != EFFECTIVE_MODE; + const bool CHANGEINTERNAL = !PWINDOW->m_bPinned && CURRENT_EFFECTIVE_MODE != EFFECTIVE_MODE; - if (*PALLOWPINFULLSCREEN && PWINDOW->m_pinFullscreened && PWINDOW->isFullscreen() && !PWINDOW->m_pinned && state.internal == FSMODE_NONE) { - PWINDOW->m_pinned = true; - PWINDOW->m_pinFullscreened = false; + if (*PALLOWPINFULLSCREEN && PWINDOW->m_bPinFullscreened && PWINDOW->isFullscreen() && !PWINDOW->m_bPinned && state.internal == FSMODE_NONE) { + PWINDOW->m_bPinned = true; + PWINDOW->m_bPinFullscreened = false; } // TODO: update the state on syncFullscreen changes - if (!CHANGEINTERNAL && PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault()) + if (!CHANGEINTERNAL && PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault()) return; - PWINDOW->m_fullscreenState.client = state.client; + PWINDOW->m_sFullscreenState.client = state.client; g_pXWaylandManager->setWindowFullscreen(PWINDOW, state.client & FSMODE_FULLSCREEN); if (!CHANGEINTERNAL) { - PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT | - Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); - PWINDOW->updateDecorationValues(); - g_layoutManager->recalculateMonitor(PMONITOR); + PWINDOW->updateDynamicRules(); + updateWindowAnimatedDecorationValues(PWINDOW); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID()); return; } - PWORKSPACE->m_fullscreenMode = EFFECTIVE_MODE; - PWORKSPACE->m_hasFullscreenWindow = EFFECTIVE_MODE != FSMODE_NONE; + g_pLayoutManager->getCurrentLayout()->fullscreenRequestForWindow(PWINDOW, CURRENT_EFFECTIVE_MODE, EFFECTIVE_MODE); - g_layoutManager->fullscreenRequestForTarget(PWINDOW->layoutTarget(), CURRENT_EFFECTIVE_MODE, EFFECTIVE_MODE); + PWINDOW->m_sFullscreenState.internal = state.internal; + PWORKSPACE->m_efFullscreenMode = EFFECTIVE_MODE; + PWORKSPACE->m_bHasFullscreenWindow = EFFECTIVE_MODE != FSMODE_NONE; - PWINDOW->m_fullscreenState.internal = state.internal; + g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string((int)EFFECTIVE_MODE != FSMODE_NONE)}); + EMIT_HOOK_EVENT("fullscreen", PWINDOW); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc(EFFECTIVE_MODE) != FSMODE_NONE)}); - Event::bus()->m_events.window.fullscreen.emit(PWINDOW); + PWINDOW->updateDynamicRules(); + updateWindowAnimatedDecorationValues(PWINDOW); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID()); - PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT | - Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); - - PWINDOW->updateDecorationValues(); - g_layoutManager->recalculateMonitor(PMONITOR); - - // make all windows and layers on the same workspace under the fullscreen window - for (auto const& w : m_windows) { - if (w->m_workspace == PWORKSPACE && !w->isFullscreen() && !w->m_fadingOut && !w->m_pinned) - w->m_createdOverFullscreen = false; - } - for (auto const& ls : m_layers) { - if (ls->m_monitor == PMONITOR) - ls->m_aboveFullscreen = false; + // make all windows on the same workspace under the fullscreen window + for (auto const& w : m_vWindows) { + if (w->m_pWorkspace == PWORKSPACE && !w->isFullscreen() && !w->m_bFadingOut && !w->m_bPinned) + w->m_bCreatedOverFullscreen = false; } - g_pDesktopAnimationManager->setFullscreenFadeAnimation( - PWORKSPACE, PWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); + updateFullscreenFadeOnWorkspace(PWORKSPACE); PWINDOW->sendWindowSize(true); @@ -2195,24 +2370,21 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie // send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't. // ignore if DS is disabled. - if (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME)) { - auto surf = PWINDOW->getSolitaryResource(); - if (surf) - g_pHyprRenderer->setSurfaceScanoutMode(surf, EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->m_self.lock() : nullptr); - } + if (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME)) + g_pHyprRenderer->setSurfaceScanoutMode(PWINDOW->m_pWLSurface->resource(), EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->self.lock() : nullptr); g_pConfigManager->ensureVRR(PMONITOR); } PHLWINDOW CCompositor::getX11Parent(PHLWINDOW pWindow) { - if (!pWindow->m_isX11) + if (!pWindow->m_bIsX11) return nullptr; - for (auto const& w : m_windows) { - if (!w->m_isX11) + for (auto const& w : m_vWindows) { + if (!w->m_bIsX11) continue; - if (w->m_xwaylandSurface == pWindow->m_xwaylandSurface->m_parent) + if (w->m_pXWaylandSurface == pWindow->m_pXWaylandSurface->parent) return w; } @@ -2220,32 +2392,32 @@ PHLWINDOW CCompositor::getX11Parent(PHLWINDOW pWindow) { } void CCompositor::scheduleFrameForMonitor(PHLMONITOR pMonitor, IOutput::scheduleFrameReason reason) { - if ((m_aqBackend->hasSession() && !m_aqBackend->session->active) || !m_sessionActive) + if ((m_pAqBackend->hasSession() && !m_pAqBackend->session->active) || !m_bSessionActive) return; - if (!pMonitor->m_enabled) + if (!pMonitor->m_bEnabled) return; - if (pMonitor->m_renderingActive) - pMonitor->m_pendingFrame = true; + if (pMonitor->renderingActive) + pMonitor->pendingFrame = true; - pMonitor->m_output->scheduleFrame(reason); + pMonitor->output->scheduleFrame(reason); } PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp_) { auto regexp = trim(regexp_); if (regexp.starts_with("active")) - return Desktop::focusState()->window(); + return m_pLastWindow.lock(); else if (regexp.starts_with("floating") || regexp.starts_with("tiled")) { // first floating on the current ws - if (!Desktop::focusState()->window()) + if (!valid(m_pLastWindow)) return nullptr; const bool FLOAT = regexp.starts_with("floating"); - for (auto const& w : m_windows) { - if (!w->m_isMapped || w->m_isFloating != FLOAT || w->m_workspace != Desktop::focusState()->window()->m_workspace || w->isHidden()) + for (auto const& w : m_vWindows) { + if (!w->m_bIsMapped || w->m_bIsFloating != FLOAT || w->m_pWorkspace != m_pLastWindow->m_pWorkspace || w->isHidden()) continue; return w; @@ -2280,38 +2452,38 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp_) { matchCheck = regexp.substr(4); } - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_isMapped) + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped || (w->isHidden() && !g_pLayoutManager->getCurrentLayout()->isWindowReachable(w))) continue; switch (mode) { case MODE_CLASS_REGEX: { - const auto windowClass = w->m_class; + const auto windowClass = w->m_szClass; if (!RE2::FullMatch(windowClass, regexCheck)) continue; break; } case MODE_INITIAL_CLASS_REGEX: { - const auto initialWindowClass = w->m_initialClass; + const auto initialWindowClass = w->m_szInitialClass; if (!RE2::FullMatch(initialWindowClass, regexCheck)) continue; break; } case MODE_TITLE_REGEX: { - const auto windowTitle = w->m_title; + const auto windowTitle = w->m_szTitle; if (!RE2::FullMatch(windowTitle, regexCheck)) continue; break; } case MODE_INITIAL_TITLE_REGEX: { - const auto initialWindowTitle = w->m_initialTitle; + const auto initialWindowTitle = w->m_szInitialTitle; if (!RE2::FullMatch(initialWindowTitle, regexCheck)) continue; break; } case MODE_TAG_REGEX: { bool tagMatched = false; - for (auto const& t : w->m_ruleApplicator->m_tagKeeper.getTags()) { + for (auto const& t : w->m_tags.getTags()) { if (RE2::FullMatch(t, regexCheck)) { tagMatched = true; break; @@ -2322,7 +2494,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp_) { break; } case MODE_ADDRESS: { - std::string addr = std::format("0x{:x}", rc(w.get())); + std::string addr = std::format("0x{:x}", (uintptr_t)w.get()); if (matchCheck != addr) continue; break; @@ -2351,32 +2523,35 @@ void CCompositor::warpCursorTo(const Vector2D& pos, bool force) { if (*PNOWARPS && !force) { const auto PMONITORNEW = getMonitorFromVector(pos); - Desktop::focusState()->rawMonitorFocus(PMONITORNEW); + if (PMONITORNEW != m_pLastMonitor) + setActiveMonitor(PMONITORNEW); return; } g_pPointerManager->warpTo(pos); const auto PMONITORNEW = getMonitorFromVector(pos); - Desktop::focusState()->rawMonitorFocus(PMONITORNEW); + if (PMONITORNEW != m_pLastMonitor) + setActiveMonitor(PMONITORNEW); } void CCompositor::closeWindow(PHLWINDOW pWindow) { - if (pWindow && validMapped(pWindow)) + if (pWindow && validMapped(pWindow)) { g_pXWaylandManager->sendCloseWindow(pWindow); + } } PHLLS CCompositor::getLayerSurfaceFromSurface(SP pSurface) { std::pair, bool> result = {pSurface, false}; - for (auto const& ls : m_layers) { - if (!ls->aliveAndVisible()) - continue; - - if (ls->m_layerSurface->m_surface == pSurface) + for (auto const& ls : m_vLayers) { + if (ls->layerSurface && ls->layerSurface->surface == pSurface) return ls; - ls->m_layerSurface->m_surface->breadthfirst( + if (!ls->layerSurface || !ls->mapped) + continue; + + ls->layerSurface->surface->breadthfirst( [&result](SP surf, const Vector2D& offset, void* data) { if (surf == result.first) { result.second = true; @@ -2397,7 +2572,7 @@ Vector2D CCompositor::parseWindowVectorArgsRelative(const std::string& args, con if (!args.contains(' ') && !args.contains('\t')) return relativeTo; - const auto PMONITOR = Desktop::focusState()->monitor(); + const auto PMONITOR = m_pLastMonitor; bool xIsPercent = false; bool yIsPercent = false; @@ -2424,7 +2599,7 @@ Vector2D CCompositor::parseWindowVectorArgsRelative(const std::string& args, con } if (!isNumber(x) || !isNumber(y)) { - Log::logger->log(Log::ERR, "parseWindowVectorArgsRelative: args not numbers"); + Debug::log(ERR, "parseWindowVectorArgsRelative: args not numbers"); return relativeTo; } @@ -2432,8 +2607,8 @@ Vector2D CCompositor::parseWindowVectorArgsRelative(const std::string& args, con int Y = 0; if (isExact) { - X = xIsPercent ? std::stof(x) * 0.01 * PMONITOR->m_size.x : std::stoi(x); - Y = yIsPercent ? std::stof(y) * 0.01 * PMONITOR->m_size.y : std::stoi(y); + X = xIsPercent ? std::stof(x) * 0.01 * PMONITOR->vecSize.x : std::stoi(x); + Y = yIsPercent ? std::stof(y) * 0.01 * PMONITOR->vecSize.y : std::stoi(y); } else { X = xIsPercent ? (std::stof(x) * 0.01 * relativeTo.x) + relativeTo.x : std::stoi(x) + relativeTo.x; Y = yIsPercent ? (std::stof(y) * 0.01 * relativeTo.y) + relativeTo.y : std::stoi(y) + relativeTo.y; @@ -2448,189 +2623,174 @@ PHLWORKSPACE CCompositor::createNewWorkspace(const WORKSPACEID& id, const MONITO // check if bound if (const auto PMONITOR = g_pConfigManager->getBoundMonitorForWS(NAME); PMONITOR) - monID = PMONITOR->m_id; + monID = PMONITOR->ID; const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2; - const auto PMONITOR = getMonitorFromID(monID); - if (!PMONITOR) { - Log::logger->log(Log::ERR, "BUG THIS: No pMonitor for new workspace in createNewWorkspace"); - return nullptr; - } + const auto PWORKSPACE = m_vWorkspaces.emplace_back(CWorkspace::create(id, getMonitorFromID(monID), NAME, SPECIAL, isEmpty)); - const auto PWORKSPACE = CWorkspace::create(id, PMONITOR, NAME, SPECIAL, isEmpty); - - PWORKSPACE->m_alpha->setValueAndWarp(0); + PWORKSPACE->m_fAlpha->setValueAndWarp(0); return PWORKSPACE; } +void CCompositor::setActiveMonitor(PHLMONITOR pMonitor) { + if (m_pLastMonitor == pMonitor) + return; + + if (!pMonitor) { + m_pLastMonitor.reset(); + return; + } + + const auto PWORKSPACE = pMonitor->activeWorkspace; + + const auto WORKSPACE_ID = PWORKSPACE ? std::to_string(PWORKSPACE->m_iID) : std::to_string(WORKSPACE_INVALID); + const auto WORKSPACE_NAME = PWORKSPACE ? PWORKSPACE->m_szName : "?"; + + g_pEventManager->postEvent(SHyprIPCEvent{.event = "focusedmon", .data = pMonitor->szName + "," + WORKSPACE_NAME}); + g_pEventManager->postEvent(SHyprIPCEvent{.event = "focusedmonv2", .data = pMonitor->szName + "," + WORKSPACE_ID}); + + EMIT_HOOK_EVENT("focusedMon", pMonitor); + m_pLastMonitor = pMonitor->self; +} + bool CCompositor::isWorkspaceSpecial(const WORKSPACEID& id) { return id >= SPECIAL_WORKSPACE_START && id <= -2; } WORKSPACEID CCompositor::getNewSpecialID() { WORKSPACEID highest = SPECIAL_WORKSPACE_START; - for (auto const& ws : getWorkspaces()) { - if (ws->m_isSpecialWorkspace && ws->m_id > highest) - highest = ws->m_id; + for (auto const& ws : m_vWorkspaces) { + if (ws->m_bIsSpecialWorkspace && ws->m_iID > highest) { + highest = ws->m_iID; + } } return highest + 1; } -void CCompositor::registerWorkspace(PHLWORKSPACE w) { - m_workspaces.emplace_back(w); - w->m_events.destroy.listenStatic([this, weak = PHLWORKSPACEREF{w}] { std::erase(m_workspaces, weak); }); -} - -std::vector CCompositor::getWorkspacesCopy() { - std::vector wsp; - auto range = getWorkspaces(); - wsp.reserve(std::ranges::distance(range)); - for (auto& r : range) { - wsp.emplace_back(r.lock()); - } - return wsp; -} - void CCompositor::performUserChecks() { - static auto PNOCHECKXDG = CConfigValue("misc:disable_xdg_env_checks"); - static auto PNOCHECKGUIUTILS = CConfigValue("misc:disable_hyprland_guiutils_check"); - static auto PNOWATCHDOG = CConfigValue("misc:disable_watchdog_warning"); + static auto PNOCHECKXDG = CConfigValue("misc:disable_xdg_env_checks"); + static auto PNOCHECKQTUTILS = CConfigValue("misc:disable_hyprland_qtutils_check"); if (!*PNOCHECKXDG) { const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP"); if (!CURRENT_DESKTOP_ENV || std::string{CURRENT_DESKTOP_ENV} != "Hyprland") { g_pHyprNotificationOverlay->addNotification( - I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, {{"value", CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"}}), CHyprColor{}, 15000, - ICON_WARNING); + std::format("Your XDG_CURRENT_DESKTOP environment seems to be managed externally, and the current value is {}.\nThis might cause issues unless it's intentional.", + CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"), + CHyprColor{}, 15000, ICON_WARNING); } } - if (!*PNOCHECKGUIUTILS) { - if (!NFsUtils::executableExistsInPath("hyprland-dialog")) - g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_GUIUTILS), CHyprColor{}, 15000, ICON_WARNING); + if (!*PNOCHECKQTUTILS) { + if (!NFsUtils::executableExistsInPath("hyprland-dialog")) { + g_pHyprNotificationOverlay->addNotification( + "Your system does not have hyprland-qtutils installed. This is a runtime dependency for some dialogs. Consider installing it.", CHyprColor{}, 15000, ICON_WARNING); + } } - if (g_pHyprOpenGL->m_failedAssetsNo > 0) { - g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_FAILED_ASSETS, {{"count", std::to_string(g_pHyprOpenGL->m_failedAssetsNo)}}), + if (g_pHyprOpenGL->failedAssetsNo > 0) { + g_pHyprNotificationOverlay->addNotification(std::format("Hyprland failed to load {} essential asset{}, blame your distro's packager for doing a bad job at packaging!", + g_pHyprOpenGL->failedAssetsNo, g_pHyprOpenGL->failedAssetsNo > 1 ? "s" : ""), CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_ERROR); } - - if (!m_watchdogWriteFd.isValid() && !*PNOWATCHDOG) - g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_WATCHDOG), CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_WARNING); - - if (m_safeMode) - openSafeModeBox(); -} - -void CCompositor::openSafeModeBox() { - const auto OPT_LOAD = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG); - const auto OPT_OPEN = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR); - const auto OPT_OK = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD); - - auto box = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_TITLE), I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_DESCRIPTION), - { - OPT_LOAD, - OPT_OPEN, - OPT_OK, - }); - - box->open()->then([OPT_LOAD, OPT_OK, OPT_OPEN, this](SP> result) { - if (result->hasError()) - return; - - const auto RES = result->result(); - - if (RES.starts_with(OPT_LOAD)) { - m_safeMode = false; - g_pConfigManager->reload(); - } else if (RES.starts_with(OPT_OPEN)) { - std::string reportPath; - const auto HOME = getenv("HOME"); - const auto CACHE_HOME = getenv("XDG_CACHE_HOME"); - - if (CACHE_HOME && CACHE_HOME[0] != '\0') { - reportPath += CACHE_HOME; - reportPath += "/hyprland/"; - } else if (HOME && HOME[0] != '\0') { - reportPath += HOME; - reportPath += "/.cache/hyprland/"; - } - Hyprutils::OS::CProcess proc("xdg-open", {reportPath}); - - proc.runAsync(); - - // reopen - openSafeModeBox(); - } - }); } void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace) { if (!pWindow || !pWorkspace) return; - if (pWindow->m_pinned && pWorkspace->m_isSpecialWorkspace) + if (pWindow->m_bPinned && pWorkspace->m_bIsSpecialWorkspace) return; - if (pWindow->m_workspace == pWorkspace) + if (pWindow->m_pWorkspace == pWorkspace) return; const bool FULLSCREEN = pWindow->isFullscreen(); - const auto FULLSCREENMODE = pWindow->m_fullscreenState.internal; - const bool WASVISIBLE = pWindow->m_workspace && pWindow->m_workspace->isVisible(); + const auto FULLSCREENMODE = pWindow->m_sFullscreenState.internal; + const bool WASVISIBLE = pWindow->m_pWorkspace && pWindow->m_pWorkspace->isVisible(); if (FULLSCREEN) setWindowFullscreenInternal(pWindow, FSMODE_NONE); const PHLWINDOW pFirstWindowOnWorkspace = pWorkspace->getFirstWindow(); - const int visibleWindowsOnWorkspace = pWorkspace->getWindows(true, std::nullopt, true); - const auto POSTOMON = pWindow->m_realPosition->goal() - (pWindow->m_monitor ? pWindow->m_monitor->m_position : Vector2D{}); - const auto PWORKSPACEMONITOR = pWorkspace->m_monitor.lock(); + const int visibleWindowsOnWorkspace = pWorkspace->getWindows(std::nullopt, true); + const auto PWINDOWMONITOR = pWindow->m_pMonitor.lock(); + const auto POSTOMON = pWindow->m_vRealPosition->goal() - PWINDOWMONITOR->vecPosition; + const auto PWORKSPACEMONITOR = pWorkspace->m_pMonitor.lock(); + + if (!pWindow->m_bIsFloating) + g_pLayoutManager->getCurrentLayout()->onWindowRemovedTiling(pWindow); pWindow->moveToWorkspace(pWorkspace); - pWindow->m_monitor = pWorkspace->m_monitor; + pWindow->m_pMonitor = pWorkspace->m_pMonitor; static auto PGROUPONMOVETOWORKSPACE = CConfigValue("group:group_on_movetoworkspace"); - if (*PGROUPONMOVETOWORKSPACE && visibleWindowsOnWorkspace == 1 && pFirstWindowOnWorkspace && pFirstWindowOnWorkspace != pWindow && pFirstWindowOnWorkspace->m_group && - pWindow->canBeGroupedInto(pFirstWindowOnWorkspace->m_group)) { - pFirstWindowOnWorkspace->m_group->add(pWindow); + if (*PGROUPONMOVETOWORKSPACE && visibleWindowsOnWorkspace == 1 && pFirstWindowOnWorkspace && pFirstWindowOnWorkspace != pWindow && + pFirstWindowOnWorkspace->m_sGroupData.pNextWindow.lock() && pWindow->canBeGroupedInto(pFirstWindowOnWorkspace)) { + + pWindow->m_bIsFloating = pFirstWindowOnWorkspace->m_bIsFloating; // match the floating state. Needed to group tiled into floated and vice versa. + if (!pWindow->m_sGroupData.pNextWindow.expired()) { + PHLWINDOW next = pWindow->m_sGroupData.pNextWindow.lock(); + while (next != pWindow) { + next->m_bIsFloating = pFirstWindowOnWorkspace->m_bIsFloating; // match the floating state of group members + next = next->m_sGroupData.pNextWindow.lock(); + } + } + + static auto USECURRPOS = CConfigValue("group:insert_after_current"); + (*USECURRPOS ? pFirstWindowOnWorkspace : pFirstWindowOnWorkspace->getGroupTail())->insertWindowToGroup(pWindow); + + pFirstWindowOnWorkspace->setGroupCurrent(pWindow); + pWindow->updateWindowDecos(); + g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow); + + if (!pWindow->getDecorationByType(DECORATION_GROUPBAR)) + pWindow->addWindowDeco(makeUnique(pWindow)); + } else { - if (pWindow->m_isFloating) - pWindow->layoutTarget()->setPositionGlobal(CBox{POSTOMON + PWORKSPACEMONITOR->m_position, pWindow->layoutTarget()->position().size()}); + if (!pWindow->m_bIsFloating) + g_pLayoutManager->getCurrentLayout()->onWindowCreatedTiling(pWindow); + + if (pWindow->m_bIsFloating) + *pWindow->m_vRealPosition = POSTOMON + PWORKSPACEMONITOR->vecPosition; } pWindow->updateToplevel(); - pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE); + pWindow->updateDynamicRules(); pWindow->uncacheWindowDecos(); + pWindow->updateGroupOutputs(); - if (pWindow->m_group) - pWindow->m_group->updateWorkspace(pWorkspace); - - g_layoutManager->newTarget(pWindow->layoutTarget(), pWorkspace->m_space); + if (!pWindow->m_sGroupData.pNextWindow.expired()) { + PHLWINDOW next = pWindow->m_sGroupData.pNextWindow.lock(); + while (next != pWindow) { + next->updateToplevel(); + next = next->m_sGroupData.pNextWindow.lock(); + } + } if (FULLSCREEN) setWindowFullscreenInternal(pWindow, FULLSCREENMODE); pWorkspace->updateWindows(); - if (pWindow->m_workspace) - pWindow->m_workspace->updateWindows(); + if (pWindow->m_pWorkspace) + pWindow->m_pWorkspace->updateWindows(); g_pCompositor->updateSuspendedStates(); - if (!WASVISIBLE && pWindow->m_workspace && pWindow->m_workspace->isVisible()) { - pWindow->m_movingFromWorkspaceAlpha->setValueAndWarp(0.F); - *pWindow->m_movingFromWorkspaceAlpha = 1.F; + if (!WASVISIBLE && pWindow->m_pWorkspace && pWindow->m_pWorkspace->isVisible()) { + pWindow->m_fMovingFromWorkspaceAlpha->setValueAndWarp(0.F); + *pWindow->m_fMovingFromWorkspaceAlpha = 1.F; } } PHLWINDOW CCompositor::getForceFocus() { - for (auto const& w : m_windows) { - if (!w->m_isMapped || w->isHidden() || !w->m_workspace || !w->m_workspace->isVisible()) + for (auto const& w : m_vWindows) { + if (!w->m_bIsMapped || w->isHidden() || !w->m_pWorkspace || !w->m_pWorkspace->isVisible()) continue; - if (!w->m_ruleApplicator->stayFocused().valueOrDefault()) + if (!w->m_bStayFocused) continue; return w; @@ -2639,53 +2799,23 @@ PHLWINDOW CCompositor::getForceFocus() { return nullptr; } -void CCompositor::scheduleMonitorStateRecheck() { - static bool scheduled = false; - - if (!scheduled) { - scheduled = true; - g_pEventLoopManager->doLater([this] { - arrangeMonitors(); - checkMonitorOverlaps(); - - scheduled = false; - }); - } -} - -void CCompositor::checkMonitorOverlaps() { - CRegion monitorRegion; - - for (const auto& m : m_monitors) { - if (!monitorRegion.copy().intersect(m->logicalBox()).empty()) { - Log::logger->log(Log::ERR, "Monitor {}: detected overlap with layout", m->m_name); - g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, {{"name", m->m_name}}), CHyprColor{}, 15000, - ICON_WARNING); - - break; - } - - monitorRegion.add(m->logicalBox()); - } -} - void CCompositor::arrangeMonitors() { - static auto* const PXWLFORCESCALEZERO = rc(g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling")); + static auto* const PXWLFORCESCALEZERO = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"); - std::vector toArrange(m_monitors.begin(), m_monitors.end()); + std::vector toArrange(m_vMonitors.begin(), m_vMonitors.end()); std::vector arranged; arranged.reserve(toArrange.size()); - Log::logger->log(Log::DEBUG, "arrangeMonitors: {} to arrange", toArrange.size()); + Debug::log(LOG, "arrangeMonitors: {} to arrange", toArrange.size()); for (auto it = toArrange.begin(); it != toArrange.end();) { auto m = *it; - if (m->m_activeMonitorRule.offset != Vector2D{-INT32_MAX, -INT32_MAX}) { + if (m->activeMonitorRule.offset != Vector2D{-INT32_MAX, -INT32_MAX}) { // explicit. - Log::logger->log(Log::DEBUG, "arrangeMonitors: {} explicit {:j}", m->m_name, m->m_activeMonitorRule.offset); + Debug::log(LOG, "arrangeMonitors: {} explicit {:j}", m->szName, m->activeMonitorRule.offset); - m->moveTo(m->m_activeMonitorRule.offset); + m->moveTo(m->activeMonitorRule.offset); arranged.push_back(m); it = toArrange.erase(it); @@ -2710,12 +2840,12 @@ void CCompositor::arrangeMonitors() { maxYOffsetUp = 0; maxYOffsetDown = 0; - // Finds the max and min values of explicitly placed monitors. + // Finds the max and min values of explicitely placed monitors. for (auto const& m : arranged) { - maxXOffsetRight = std::max(m->m_position.x + m->m_size.x, maxXOffsetRight); - maxXOffsetLeft = std::min(m->m_position.x, maxXOffsetLeft); - maxYOffsetDown = std::max(m->m_position.y + m->m_size.y, maxYOffsetDown); - maxYOffsetUp = std::min(m->m_position.y, maxYOffsetUp); + maxXOffsetRight = std::max(m->vecPosition.x + m->vecSize.x, maxXOffsetRight); + maxXOffsetLeft = std::min(m->vecPosition.x, maxXOffsetLeft); + maxYOffsetDown = std::max(m->vecPosition.y + m->vecSize.y, maxYOffsetDown); + maxYOffsetUp = std::min(m->vecPosition.y, maxYOffsetUp); } }; @@ -2726,39 +2856,15 @@ void CCompositor::arrangeMonitors() { // Moves the monitor to their appropriate position on the x/y axis and // increments/decrements the corresponding max offset. Vector2D newPosition = {0, 0}; - switch (m->m_activeMonitorRule.autoDir) { - case eAutoDirs::DIR_AUTO_UP: newPosition.y = maxYOffsetUp - m->m_size.y; break; + switch (m->activeMonitorRule.autoDir) { + case eAutoDirs::DIR_AUTO_UP: newPosition.y = maxYOffsetUp - m->vecSize.y; break; case eAutoDirs::DIR_AUTO_DOWN: newPosition.y = maxYOffsetDown; break; - case eAutoDirs::DIR_AUTO_LEFT: newPosition.x = maxXOffsetLeft - m->m_size.x; break; + case eAutoDirs::DIR_AUTO_LEFT: newPosition.x = maxXOffsetLeft - m->vecSize.x; break; case eAutoDirs::DIR_AUTO_RIGHT: case eAutoDirs::DIR_AUTO_NONE: newPosition.x = maxXOffsetRight; break; - case eAutoDirs::DIR_AUTO_CENTER_UP: { - int width = maxXOffsetRight - maxXOffsetLeft; - newPosition.y = maxYOffsetUp - m->m_size.y; - newPosition.x = maxXOffsetLeft + (width - m->m_size.x) / 2; - break; - } - case eAutoDirs::DIR_AUTO_CENTER_DOWN: { - int width = maxXOffsetRight - maxXOffsetLeft; - newPosition.y = maxYOffsetDown; - newPosition.x = maxXOffsetLeft + (width - m->m_size.x) / 2; - break; - } - case eAutoDirs::DIR_AUTO_CENTER_LEFT: { - int height = maxYOffsetDown - maxYOffsetUp; - newPosition.x = maxXOffsetLeft - m->m_size.x; - newPosition.y = maxYOffsetUp + (height - m->m_size.y) / 2; - break; - } - case eAutoDirs::DIR_AUTO_CENTER_RIGHT: { - int height = maxYOffsetDown - maxYOffsetUp; - newPosition.x = maxXOffsetRight; - newPosition.y = maxYOffsetUp + (height - m->m_size.y) / 2; - break; - } default: UNREACHABLE(); } - Log::logger->log(Log::DEBUG, "arrangeMonitors: {} auto {:j}", m->m_name, m->m_position); + Debug::log(LOG, "arrangeMonitors: {} auto {:j}", m->szName, m->vecPosition); m->moveTo(newPosition); arranged.emplace_back(m); } @@ -2766,57 +2872,45 @@ void CCompositor::arrangeMonitors() { // reset maxXOffsetRight (reuse) // and set xwayland positions aka auto for all maxXOffsetRight = 0; - for (auto const& m : m_monitors) { - Log::logger->log(Log::DEBUG, "arrangeMonitors: {} xwayland [{}, {}]", m->m_name, maxXOffsetRight, 0); - m->m_xwaylandPosition = {maxXOffsetRight, 0}; - maxXOffsetRight += (*PXWLFORCESCALEZERO ? m->m_transformedSize.x : m->m_size.x); + for (auto const& m : m_vMonitors) { + Debug::log(LOG, "arrangeMonitors: {} xwayland [{}, {}]", m->szName, maxXOffsetRight, 0); + m->vecXWaylandPosition = {maxXOffsetRight, 0}; + maxXOffsetRight += (*PXWLFORCESCALEZERO ? m->vecTransformedSize.x : m->vecSize.x); if (*PXWLFORCESCALEZERO) - m->m_xwaylandScale = m->m_scale; + m->xwaylandScale = m->scale; else - m->m_xwaylandScale = 1.f; + m->xwaylandScale = 1.f; } PROTO::xdgOutput->updateAllOutputs(); - Event::bus()->m_events.monitor.layoutChanged.emit(); - -#ifndef NO_XWAYLAND - const auto box = g_pCompositor->calculateX11WorkArea(); - if (g_pXWayland && g_pXWayland->m_wm) { - if (box) - g_pXWayland->m_wm->updateWorkArea(box->x, box->y, box->w, box->h); - else - g_pXWayland->m_wm->updateWorkArea(0, 0, 0, 0); - } - -#endif } void CCompositor::enterUnsafeState() { - if (m_unsafeState) + if (m_bUnsafeState) return; - Log::logger->log(Log::DEBUG, "Entering unsafe state"); + Debug::log(LOG, "Entering unsafe state"); - if (!m_unsafeOutput->m_enabled) - m_unsafeOutput->onConnect(false); + if (!m_pUnsafeOutput->m_bEnabled) + m_pUnsafeOutput->onConnect(false); - m_unsafeState = true; + m_bUnsafeState = true; - Desktop::focusState()->rawMonitorFocus(m_unsafeOutput.lock()); + setActiveMonitor(m_pUnsafeOutput.lock()); } void CCompositor::leaveUnsafeState() { - if (!m_unsafeState) + if (!m_bUnsafeState) return; - Log::logger->log(Log::DEBUG, "Leaving unsafe state"); + Debug::log(LOG, "Leaving unsafe state"); - m_unsafeState = false; + m_bUnsafeState = false; PHLMONITOR pNewMonitor = nullptr; - for (auto const& pMonitor : m_monitors) { - if (pMonitor->m_output != m_unsafeOutput->m_output) { + for (auto const& pMonitor : m_vMonitors) { + if (pMonitor->output != m_pUnsafeOutput->output) { pNewMonitor = pMonitor; break; } @@ -2824,10 +2918,10 @@ void CCompositor::leaveUnsafeState() { RASSERT(pNewMonitor, "Tried to leave unsafe without a monitor"); - if (m_unsafeOutput->m_enabled) - m_unsafeOutput->onDisconnect(); + if (m_pUnsafeOutput->m_bEnabled) + m_pUnsafeOutput->onDisconnect(); - for (auto const& m : m_monitors) { + for (auto const& m : m_vMonitors) { scheduleFrameForMonitor(m); } } @@ -2836,34 +2930,34 @@ void CCompositor::setPreferredScaleForSurface(SP pSurface, d PROTO::fractional->sendScale(pSurface, scale); pSurface->sendPreferredScale(std::ceil(scale)); - const auto PSURFACE = Desktop::View::CWLSurface::fromResource(pSurface); + const auto PSURFACE = CWLSurface::fromResource(pSurface); if (!PSURFACE) { - Log::logger->log(Log::WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface", rc(pSurface.get())); + Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface.get()); return; } - PSURFACE->m_lastScaleFloat = scale; - PSURFACE->m_lastScaleInt = sc(std::ceil(scale)); + PSURFACE->m_fLastScale = scale; + PSURFACE->m_iLastScale = static_cast(std::ceil(scale)); } void CCompositor::setPreferredTransformForSurface(SP pSurface, wl_output_transform transform) { pSurface->sendPreferredTransform(transform); - const auto PSURFACE = Desktop::View::CWLSurface::fromResource(pSurface); + const auto PSURFACE = CWLSurface::fromResource(pSurface); if (!PSURFACE) { - Log::logger->log(Log::WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface", rc(pSurface.get())); + Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface.get()); return; } - PSURFACE->m_lastTransform = transform; + PSURFACE->m_eLastTransform = transform; } void CCompositor::updateSuspendedStates() { - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_isMapped) + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped) continue; - w->setSuspended(w->isHidden() || !w->m_workspace || !w->m_workspace->isVisible()); + w->setSuspended(w->isHidden() || !w->m_pWorkspace || !w->m_pWorkspace->isVisible()); } } @@ -2884,7 +2978,7 @@ static void checkDefaultCursorWarp(PHLMONITOR monitor) { } if (!cursorDefaultDone && *PCURSORMONITOR != STRVAL_EMPTY) { - if (*PCURSORMONITOR == monitor->m_name) { + if (*PCURSORMONITOR == monitor->szName) { cursorDefaultDone = true; g_pCompositor->warpCursorTo(POS, true); g_pInputManager->refocus(); @@ -2892,7 +2986,7 @@ static void checkDefaultCursorWarp(PHLMONITOR monitor) { } } - // modechange happened check if cursor is on that monitor and warp it to middle to not place it out of bounds if resolution changed. + // modechange happend check if cursor is on that monitor and warp it to middle to not place it out of bounds if resolution changed. if (g_pCompositor->getMonitorFromCursor() == monitor) { g_pCompositor->warpCursorTo(POS, true); g_pInputManager->refocus(); @@ -2901,96 +2995,72 @@ static void checkDefaultCursorWarp(PHLMONITOR monitor) { void CCompositor::onNewMonitor(SP output) { // add it to real - auto PNEWMONITOR = g_pCompositor->m_realMonitors.emplace_back(makeShared(output)); + auto PNEWMONITOR = g_pCompositor->m_vRealMonitors.emplace_back(makeShared(output)); if (std::string("HEADLESS-1") == output->name) { - g_pCompositor->m_unsafeOutput = PNEWMONITOR; - output->name = "FALLBACK"; // we are allowed to do this :) + g_pCompositor->m_pUnsafeOutput = PNEWMONITOR; + output->name = "FALLBACK"; // we are allowed to do this :) } - Log::logger->log(Log::DEBUG, "New output with name {}", output->name); + Debug::log(LOG, "New output with name {}", output->name); - PNEWMONITOR->m_name = output->name; - PNEWMONITOR->m_self = PNEWMONITOR; - const bool FALLBACK = g_pCompositor->m_unsafeOutput ? output == g_pCompositor->m_unsafeOutput->m_output : false; - PNEWMONITOR->m_id = FALLBACK ? MONITOR_INVALID : g_pCompositor->getNextAvailableMonitorID(output->name); - PNEWMONITOR->m_isUnsafeFallback = FALLBACK; + PNEWMONITOR->szName = output->name; + PNEWMONITOR->self = PNEWMONITOR; + const bool FALLBACK = g_pCompositor->m_pUnsafeOutput ? output == g_pCompositor->m_pUnsafeOutput->output : false; + PNEWMONITOR->ID = FALLBACK ? MONITOR_INVALID : g_pCompositor->getNextAvailableMonitorID(output->name); + PNEWMONITOR->isUnsafeFallback = FALLBACK; - Event::bus()->m_events.monitor.newMon.emit(PNEWMONITOR); + EMIT_HOOK_EVENT("newMonitor", PNEWMONITOR); if (!FALLBACK) PNEWMONITOR->onConnect(false); - if (!PNEWMONITOR->m_enabled || FALLBACK) + if (!PNEWMONITOR->m_bEnabled || FALLBACK) return; // ready to process if we have a real monitor - if ((!g_pHyprRenderer->m_mostHzMonitor || PNEWMONITOR->m_refreshRate > g_pHyprRenderer->m_mostHzMonitor->m_refreshRate) && PNEWMONITOR->m_enabled) - g_pHyprRenderer->m_mostHzMonitor = PNEWMONITOR; + if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled) + g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR; - g_pCompositor->m_readyToProcess = true; + g_pCompositor->m_bReadyToProcess = true; - g_pConfigManager->m_wantsMonitorReload = true; + g_pConfigManager->m_bWantsMonitorReload = true; g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR, IOutput::AQ_SCHEDULE_NEW_MONITOR); checkDefaultCursorWarp(PNEWMONITOR); - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_monitor == PNEWMONITOR) { - w->m_lastSurfaceMonitorID = MONITOR_INVALID; + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pMonitor == PNEWMONITOR) { + w->m_iLastSurfaceMonitorID = MONITOR_INVALID; w->updateSurfaceScaleTransformDetails(); } } g_pHyprRenderer->damageMonitor(PNEWMONITOR); - PNEWMONITOR->m_frameScheduler->onFrame(); + PNEWMONITOR->onMonitorFrame(); if (PROTO::colorManagement && shouldChangePreferredImageDescription()) { - Log::logger->log(Log::ERR, "FIXME: color management protocol is enabled, need a preferred image description id"); + Debug::log(ERR, "FIXME: color management protocol is enabled, need a preferred image description id"); PROTO::colorManagement->onImagePreferredChanged(0); } } -PImageDescription CCompositor::getPreferredImageDescription() { +SImageDescription CCompositor::getPreferredImageDescription() { if (!PROTO::colorManagement) { - Log::logger->log(Log::ERR, "FIXME: color management protocol is not enabled, returning empty image description"); - return DEFAULT_IMAGE_DESCRIPTION; + Debug::log(ERR, "FIXME: color management protocol is not enabled, returning empty image description"); + return SImageDescription{}; } - Log::logger->log(Log::WARN, "FIXME: color management protocol is enabled, determine correct preferred image description"); + Debug::log(WARN, "FIXME: color management protocol is enabled, determine correct preferred image description"); // should determine some common settings to avoid unnecessary transformations while keeping maximum displayable precision - return m_monitors.size() == 1 ? m_monitors[0]->m_imageDescription : CImageDescription::from(SImageDescription{.primaries = NColorPrimaries::BT709}); -} - -PImageDescription CCompositor::getHDRImageDescription() { - if (!PROTO::colorManagement) { - Log::logger->log(Log::ERR, "FIXME: color management protocol is not enabled, returning empty image description"); - return DEFAULT_IMAGE_DESCRIPTION; - } - - return m_monitors.size() == 1 && m_monitors[0]->m_output && m_monitors[0]->m_output->parsedEDID.hdrMetadata.has_value() ? - CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), - .masteringPrimaries = m_monitors[0]->getMasteringPrimaries(), - .luminances = {.min = m_monitors[0]->minLuminance(HDR_MIN_LUMINANCE), .max = m_monitors[0]->maxLuminance(HDR_MAX_LUMINANCE), .reference = HDR_REF_LUMINANCE}, - .masteringLuminances = m_monitors[0]->getMasteringLuminances(), - .maxCLL = m_monitors[0]->maxCLL(), - .maxFALL = m_monitors[0]->maxFALL()}) : - DEFAULT_HDR_IMAGE_DESCRIPTION; + return SImageDescription{.primaries = NColorPrimaries::BT709}; } bool CCompositor::shouldChangePreferredImageDescription() { - Log::logger->log(Log::WARN, "FIXME: color management protocol is enabled and outputs changed, check preferred image description changes"); + Debug::log(WARN, "FIXME: color management protocol is enabled and outputs changed, check preferred image description changes"); return false; } void CCompositor::ensurePersistentWorkspacesPresent(const std::vector& rules, PHLWORKSPACE pWorkspace) { - if (!Desktop::focusState()->monitor()) - return; - - std::vector persistentFound; for (const auto& rule : rules) { if (!rule.isPersistent) @@ -3004,10 +3074,7 @@ void CCompositor::ensurePersistentWorkspacesPresent(const std::vectorlog(Log::ERR, "ensurePersistentWorkspacesPresent: couldn't resolve id for workspace {}", rule.workspaceString); + Debug::log(ERR, "ensurePersistentWorkspacesPresent: couldn't resolve id for workspace {}", rule.workspaceString); continue; } PWORKSPACE = getWorkspaceByID(id); - if (!PMONITOR) - PMONITOR = Desktop::focusState()->monitor(); - if (!PWORKSPACE) - PWORKSPACE = createNewWorkspace(id, PMONITOR->m_id, wsname, false); - } - - if (!PMONITOR) { - Log::logger->log(Log::ERR, "ensurePersistentWorkspacesPresent: couldn't resolve monitor for {}, skipping", rule.monitor); - continue; + createNewWorkspace(id, PMONITOR ? PMONITOR : m_pLastMonitor.lock(), wsname, false); } if (PWORKSPACE) - PWORKSPACE->setPersistent(true); + PWORKSPACE->m_bPersistent = true; - if (!pWorkspace) - persistentFound.emplace_back(PWORKSPACE); + if (!PMONITOR) { + Debug::log(ERR, "ensurePersistentWorkspacesPresent: couldn't resolve monitor for {}, skipping", rule.monitor); + continue; + } if (PWORKSPACE) { - if (PWORKSPACE->m_monitor == PMONITOR) { - Log::logger->log(Log::DEBUG, "ensurePersistentWorkspacesPresent: workspace persistent {} already on {}", rule.workspaceString, PMONITOR->m_name); + if (PWORKSPACE->m_pMonitor == PMONITOR) { + Debug::log(LOG, "ensurePersistentWorkspacesPresent: workspace persistent {} already on {}", rule.workspaceString, PMONITOR->szName); continue; } - Log::logger->log(Log::DEBUG, "ensurePersistentWorkspacesPresent: workspace persistent {} not on {}, moving", rule.workspaceString, PMONITOR->m_name); + Debug::log(LOG, "ensurePersistentWorkspacesPresent: workspace persistent {} not on {}, moving", rule.workspaceString, PMONITOR->szName); moveWorkspaceToMonitor(PWORKSPACE, PMONITOR); continue; } } - if (!pWorkspace) { - // check non-persistent and downgrade if workspace is no longer persistent - std::vector toDowngrade; - for (auto& w : getWorkspaces()) { - if (!w->isPersistent()) - continue; - - if (std::ranges::contains(persistentFound, w.lock())) - continue; - - toDowngrade.emplace_back(w); - } - - for (auto& ws : toDowngrade) { - ws->setPersistent(false); - } - } -} - -void CCompositor::ensureWorkspacesOnAssignedMonitors() { - for (auto const& ws : getWorkspacesCopy()) { - if (!valid(ws) || ws->m_isSpecialWorkspace) - continue; - - const auto RULE = g_pConfigManager->getWorkspaceRuleFor(ws); - if (RULE.monitor.empty()) - continue; - - const auto PMONITOR = getMonitorFromString(RULE.monitor); - if (!PMONITOR) - continue; - - if (ws->m_monitor == PMONITOR) - continue; - - Log::logger->log(Log::DEBUG, "ensureWorkspacesOnAssignedMonitors: moving workspace {} to {}", ws->m_name, PMONITOR->m_name); - moveWorkspaceToMonitor(ws, PMONITOR, true); - } -} - -std::optional CCompositor::getVTNr() { - if (!m_aqBackend->hasSession()) - return std::nullopt; - - unsigned int ttynum = 0; - Hyprutils::OS::CFileDescriptor fd{open("/dev/tty", O_RDONLY | O_NOCTTY)}; - if (fd.isValid()) { -#if defined(VT_GETSTATE) - struct vt_stat st; - if (!ioctl(fd.get(), VT_GETSTATE, &st)) - ttynum = st.v_active; -#elif defined(VT_GETACTIVE) - int vt; - if (!ioctl(fd.get(), VT_GETACTIVE, &vt)) - ttynum = vt; -#endif - } - - return ttynum; -} - -bool CCompositor::isVRRActiveOnAnyMonitor() const { - return std::ranges::any_of(m_monitors, [](const PHLMONITOR& m) { return m->m_vrrActive; }); + // cleanup old + sanityCheckWorkspaces(); } diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 6d2044fe..2393662b 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -2,14 +2,11 @@ #include -#include - -#include "helpers/math/Direction.hpp" #include "managers/XWaylandManager.hpp" #include "managers/KeybindManager.hpp" #include "managers/SessionLockManager.hpp" -#include "desktop/view/Window.hpp" -#include "helpers/cm/ColorManagement.hpp" +#include "desktop/Window.hpp" +#include "protocols/types/ColorManagement.hpp" #include #include @@ -28,78 +25,67 @@ class CCompositor { CCompositor(bool onlyConfig = false); ~CCompositor(); - wl_display* m_wlDisplay = nullptr; - wl_event_loop* m_wlEventLoop = nullptr; - struct { - int fd = -1; - bool syncobjSupport = false; - } m_drm; + wl_display* m_sWLDisplay = nullptr; + wl_event_loop* m_sWLEventLoop = nullptr; + int m_iDRMFD = -1; + bool m_bInitialized = false; + SP m_pAqBackend; - struct { - int fd = -1; - bool syncObjSupport = false; - } m_drmRenderNode; + std::string m_szHyprTempDataRoot = ""; - bool m_initialized = false; - bool m_safeMode = false; - SP m_aqBackend; + std::string m_szWLDisplaySocket = ""; + std::string m_szInstanceSignature = ""; + std::string m_szInstancePath = ""; + std::string m_szCurrentSplash = "error"; - std::string m_hyprTempDataRoot = ""; + std::vector m_vMonitors; + std::vector m_vRealMonitors; // for all monitors, even those turned off + std::vector m_vWindows; + std::vector m_vLayers; + std::vector m_vWorkspaces; + std::vector m_vWindowsFadingOut; + std::vector m_vSurfacesFadingOut; - std::string m_wlDisplaySocket = ""; - std::string m_instanceSignature = ""; - std::string m_instancePath = ""; - std::string m_currentSplash = "error"; + std::unordered_map m_mMonitorIDMap; - std::vector m_monitors; - std::vector m_realMonitors; // for all monitors, even those turned off - std::vector m_windows; - std::vector m_layers; - std::vector m_windowsFadingOut; - std::vector m_surfacesFadingOut; - std::vector> m_otherViews; + void initServer(std::string socketName, int socketFd); + void startCompositor(); + void stopCompositor(); + void cleanup(); + void bumpNofile(); + void restoreNofile(); - std::unordered_map m_monitorIDMap; - std::unordered_map m_seenMonitorWorkspaceMap; // map of seen monitor names to workspace IDs + WP m_pLastFocus; + PHLWINDOWREF m_pLastWindow; + PHLMONITORREF m_pLastMonitor; - void initServer(std::string socketName, int socketFd); - void startCompositor(); - void stopCompositor(); - void cleanup(); - void bumpNofile(); - void restoreNofile(); - bool setWatchdogFd(int fd); + std::vector 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; // ------------------------------------------------- // - auto getWorkspaces() { - return std::views::filter(m_workspaces, [](const auto& e) { return e; }); - } - std::vector getWorkspacesCopy(); - void registerWorkspace(PHLWORKSPACE w); - - // - PHLMONITOR getMonitorFromID(const MONITORID&); PHLMONITOR getMonitorFromName(const std::string&); PHLMONITOR getMonitorFromDesc(const std::string&); PHLMONITOR getMonitorFromCursor(); PHLMONITOR getMonitorFromVector(const Vector2D&); void removeWindowFromVectorSafe(PHLWINDOW); + void focusWindow(PHLWINDOW, SP pSurface = nullptr, bool preserveFocusHistory = false); + void focusSurface(SP, PHLWINDOW pWindowOwner = nullptr); bool monitorExists(PHLMONITOR); PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr); - SP vectorToLayerSurface(const Vector2D&, std::vector*, Vector2D*, PHLLS*, bool aboveLockscreen = false); + SP vectorToLayerSurface(const Vector2D&, std::vector*, Vector2D*, PHLLS*); SP vectorToLayerPopupSurface(const Vector2D&, PHLMONITOR monitor, Vector2D*, PHLLS*); SP vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl); Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP); @@ -110,21 +96,22 @@ class CCompositor { PHLWORKSPACE getWorkspaceByID(const WORKSPACEID&); PHLWORKSPACE getWorkspaceByName(const std::string&); PHLWORKSPACE getWorkspaceByString(const std::string&); + void sanityCheckWorkspaces(); PHLWINDOW getUrgentWindow(); bool isWindowActive(PHLWINDOW); void changeWindowZOrder(PHLWINDOW, bool); void cleanupFadingOut(const MONITORID& monid); - PHLWINDOW getWindowInDirection(PHLWINDOW, Math::eDirection); - PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false); + PHLWINDOW getWindowInDirection(PHLWINDOW, char); + PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, char dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false); PHLWINDOW getWindowCycle(PHLWINDOW cur, bool focusableOnly = false, std::optional floating = std::nullopt, bool visible = false, bool prev = false); PHLWINDOW getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional floating = std::nullopt, bool visible = false, bool next = false); WORKSPACEID getNextAvailableNamedWorkspace(); bool isPointOnAnyMonitor(const Vector2D&); bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr); - std::optional calculateX11WorkArea(); - PHLMONITOR getMonitorInDirection(Math::eDirection); - PHLMONITOR getMonitorInDirection(PHLMONITOR, Math::eDirection); + PHLMONITOR getMonitorInDirection(const char&); + PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&); void updateAllWindowsAnimatedDecorationValues(); + void updateWindowAnimatedDecorationValues(PHLWINDOW); MONITORID getNextAvailableMonitorID(std::string const& name); void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false); void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR); @@ -132,8 +119,9 @@ class CCompositor { bool workspaceIDOutOfBounds(const WORKSPACEID&); void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE); void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE); - void setWindowFullscreenState(const PHLWINDOW PWINDOW, const Desktop::View::SFullscreenState state); + void setWindowFullscreenState(const PHLWINDOW PWINDOW, const SFullscreenState state); void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON); + void updateFullscreenFadeOnWorkspace(PHLWORKSPACE); PHLWINDOW getX11Parent(PHLWINDOW); void scheduleFrameForMonitor(PHLMONITOR, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN); void addToFadingOutSafe(PHLLS); @@ -144,52 +132,42 @@ class CCompositor { PHLLS getLayerSurfaceFromSurface(SP); void closeWindow(PHLWINDOW); Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&); - [[nodiscard]] PHLWORKSPACE createNewWorkspace(const WORKSPACEID&, const MONITORID&, const std::string& name = "", - bool isEmpty = true); // will be deleted next frame if left empty and unfocused! - bool isWorkspaceSpecial(const WORKSPACEID&); - WORKSPACEID getNewSpecialID(); - void performUserChecks(); - void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace); - PHLWINDOW getForceFocus(); - void scheduleMonitorStateRecheck(); - void arrangeMonitors(); - void checkMonitorOverlaps(); - void enterUnsafeState(); - void leaveUnsafeState(); - void setPreferredScaleForSurface(SP pSurface, double scale); - void setPreferredTransformForSurface(SP pSurface, wl_output_transform transform); - void updateSuspendedStates(); - void onNewMonitor(SP output); - void ensurePersistentWorkspacesPresent(const std::vector& rules, PHLWORKSPACE pWorkspace = nullptr); - void ensureWorkspacesOnAssignedMonitors(); - std::optional getVTNr(); - bool isVRRActiveOnAnyMonitor() const; + PHLWORKSPACE createNewWorkspace(const WORKSPACEID&, const MONITORID&, const std::string& name = "", + bool isEmpty = true); // will be deleted next frame if left empty and unfocused! + void setActiveMonitor(PHLMONITOR); + bool isWorkspaceSpecial(const WORKSPACEID&); + WORKSPACEID getNewSpecialID(); + void performUserChecks(); + void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace); + PHLWINDOW getForceFocus(); + void arrangeMonitors(); + void enterUnsafeState(); + void leaveUnsafeState(); + void setPreferredScaleForSurface(SP pSurface, double scale); + void setPreferredTransformForSurface(SP pSurface, wl_output_transform transform); + void updateSuspendedStates(); + void onNewMonitor(SP output); + void ensurePersistentWorkspacesPresent(const std::vector& rules, PHLWORKSPACE pWorkspace = nullptr); - NColorManagement::PImageDescription getPreferredImageDescription(); - NColorManagement::PImageDescription getHDRImageDescription(); + NColorManagement::SImageDescription getPreferredImageDescription(); bool shouldChangePreferredImageDescription(); - bool supportsDrmSyncobjTimeline() const; - std::string m_explicitConfigPath; + std::string explicitConfigPath; private: - void initAllSignals(); - void removeAllSignals(); - void cleanEnvironment(); - void setRandomSplash(); - void initManagers(eManagersInitStage stage); - void prepareFallbackOutput(); - void createLockFile(); - void removeLockFile(); - void setMallocThreshold(); - void openSafeModeBox(); + void initAllSignals(); + void removeAllSignals(); + void cleanEnvironment(); + void setRandomSplash(); + void initManagers(eManagersInitStage stage); + void prepareFallbackOutput(); + void createLockFile(); + void removeLockFile(); + void setMallocThreshold(); - uint64_t m_hyprlandPID = 0; - wl_event_source* m_critSigSource = nullptr; - rlimit m_originalNofile = {}; - Hyprutils::OS::CFileDescriptor m_watchdogWriteFd; - - std::vector m_workspaces; + uint64_t m_iHyprlandPID = 0; + wl_event_source* m_critSigSource = nullptr; + rlimit m_sOriginalNofile = {}; }; inline UP g_pCompositor; diff --git a/src/SharedDefs.hpp b/src/SharedDefs.hpp index bb7c601e..a46a2429 100644 --- a/src/SharedDefs.hpp +++ b/src/SharedDefs.hpp @@ -18,16 +18,15 @@ enum eIcons : uint8_t { }; enum eRenderStage : uint8_t { - RENDER_PRE = 0, /* Before binding the gl context */ - RENDER_BEGIN, /* Just when the rendering begins, nothing has been rendered yet. Damage, current render data in opengl valid. */ - RENDER_POST_WALLPAPER, /* After background layer, but before bottom and overlay layers */ - RENDER_PRE_WINDOWS, /* Pre windows, post bottom and overlay layers */ - RENDER_POST_WINDOWS, /* Post windows, pre top/overlay layers, etc */ - RENDER_LAST_MOMENT, /* Last moment to render with the gl context */ - RENDER_POST, /* After rendering is finished, gl context not available anymore */ - RENDER_POST_MIRROR, /* After rendering a mirror */ - RENDER_PRE_WINDOW, /* Before rendering a window (any pass) Note some windows (e.g. tiled) may have 2 passes (main & popup) */ - RENDER_POST_WINDOW, /* After rendering a window (any pass) */ + RENDER_PRE = 0, /* Before binding the gl context */ + RENDER_BEGIN, /* Just when the rendering begins, nothing has been rendered yet. Damage, current render data in opengl valid. */ + RENDER_PRE_WINDOWS, /* Pre windows, post bottom and overlay layers */ + RENDER_POST_WINDOWS, /* Post windows, pre top/overlay layers, etc */ + RENDER_LAST_MOMENT, /* Last moment to render with the gl context */ + RENDER_POST, /* After rendering is finished, gl context not available anymore */ + RENDER_POST_MIRROR, /* After rendering a mirror */ + RENDER_PRE_WINDOW, /* Before rendering a window (any pass) Note some windows (e.g. tiled) may have 2 passes (main & popup) */ + RENDER_POST_WINDOW, /* After rendering a window (any pass) */ }; enum eInputType : uint8_t { @@ -38,6 +37,10 @@ enum eInputType : uint8_t { INPUT_TYPE_MOTION }; +struct SCallbackInfo { + bool cancelled = false; /* on cancellable events, will cancel the event. */ +}; + enum eHyprCtlOutputFormat : uint8_t { FORMAT_NORMAL = 0, FORMAT_JSON @@ -55,6 +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; + +typedef std::function HOOK_CALLBACK_FN; diff --git a/src/config/ConfigDataValues.hpp b/src/config/ConfigDataValues.hpp index 8facfd9b..901fb317 100644 --- a/src/config/ConfigDataValues.hpp +++ b/src/config/ConfigDataValues.hpp @@ -2,13 +2,11 @@ #include "../defines.hpp" #include "../helpers/varlist/VarList.hpp" #include -#include 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 m_colors; + std::vector m_vColors; /* Vector containing pure colors for shoving into opengl */ - std::vector m_colorsOkLabA; + std::vector 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,62 +72,60 @@ 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", sc(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; - - void parseGapData(CVarList2 varlist) { - const auto toInt = [](std::string_view string) -> int { return std::stoi(std::string(string)); }; + int64_t top; + int64_t right; + int64_t bottom; + int64_t left; + void parseGapData(CVarList varlist) { switch (varlist.size()) { case 1: { - *this = CCssGapData(toInt(varlist[0])); + *this = CCssGapData(std::stoi(varlist[0])); break; } case 2: { - *this = CCssGapData(toInt(varlist[0]), toInt(varlist[1])); + *this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1])); break; } case 3: { - *this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2])); + *this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2])); break; } case 4: { - *this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2]), toInt(varlist[3])); + *this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]), std::stoi(varlist[3])); break; } default: { - Log::logger->log(Log::WARN, "Too many arguments provided for gaps."); - *this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2]), toInt(varlist[3])); + Debug::log(WARN, "Too many arguments provided for gaps."); + *this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]), std::stoi(varlist[3])); break; } } } 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() { @@ -137,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{ - {"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); } }; diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index f6f777d1..54223c1c 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include "ConfigManager.hpp" inline static const std::vector CONFIG_OPTIONS = { @@ -15,6 +14,12 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_INT, .data = SConfigOptionDescription::SRangeData{1, 0, 20}, }, + SConfigOptionDescription{ + .value = "general:no_border_on_floating", + .description = "disable borders for floating windows", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, SConfigOptionDescription{ .value = "general:gaps_in", .description = "gaps between windows\n\nsupports css style gaps (top, right, bottom, left -> 5 10 15 20)", @@ -27,13 +32,6 @@ inline static const std::vector 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.", @@ -130,24 +128,6 @@ inline static const std::vector 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}, - }, - SConfigOptionDescription{ - .value = "general:modal_parent_blocking", - .description = "if true, parent windows of modals will not be interactive.", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - SConfigOptionDescription{ - .value = "general:locale", - .description = "overrides the system locale", - .type = CONFIG_OPTION_STRING_SHORT, - .data = SConfigOptionDescription::SStringData{""}, - }, /* * decoration: @@ -161,7 +141,7 @@ inline static const std::vector CONFIG_OPTIONS = { }, SConfigOptionDescription{ .value = "decoration:rounding_power", - .description = "rounding power of corners (2 is a circle)", + .description = "rouding power of corners (2 is a circle)", .type = CONFIG_OPTION_FLOAT, .data = SConfigOptionDescription::SFloatData{2, 2, 10}, }, @@ -237,12 +217,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_FLOAT, .data = SConfigOptionDescription::SFloatData{1, 0, 1}, }, - SConfigOptionDescription{ - .value = "decoration:dim_modal", - .description = "enables dimming of parents of modal windows", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, SConfigOptionDescription{ .value = "decoration:dim_inactive", .description = "enables dimming of inactive windows", @@ -273,12 +247,6 @@ inline static const std::vector 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: @@ -393,8 +361,8 @@ inline static const std::vector CONFIG_OPTIONS = { .data = SConfigOptionDescription::SBoolData{true}, }, SConfigOptionDescription{ - .value = "animations:workspace_wraparound", - .description = "changes the direction of slide animations between the first and last workspaces", + .value = "animations:first_launch_animation", + .description = "enable first launch animation", .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{true}, }, @@ -435,7 +403,7 @@ inline static const std::vector CONFIG_OPTIONS = { }, SConfigOptionDescription{ .value = "input:kb_file", - .description = "Appropriate XKB keymap file", + .description = "Appropriate XKB keymap parameter", .type = CONFIG_OPTION_STRING_LONG, .data = SConfigOptionDescription::SStringData{""}, //##TODO UNSET? }, @@ -484,12 +452,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, - SConfigOptionDescription{ - .value = "input:rotation", - .description = "Sets the rotation of a device in degrees clockwise off the logical neutral position. Value is clamped to the range 0 to 359.", - .type = CONFIG_OPTION_INT, - .data = SConfigOptionDescription::SRangeData{0, 0, 359}, - }, SConfigOptionDescription{ .value = "input:left_handed", .description = "Switches RMB and LMB", @@ -636,10 +598,9 @@ inline static const std::vector 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{0, 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", @@ -659,12 +620,6 @@ inline static const std::vector 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: @@ -689,23 +644,6 @@ inline static const std::vector CONFIG_OPTIONS = { .data = SConfigOptionDescription::SBoolData{true}, }, - /* - * input:virtualkeyboard: - */ - - SConfigOptionDescription{ - .value = "input:virtualkeyboard:share_states", - .description = "Unify key down states and modifier states with other keyboards. 0 -> no, 1 -> yes, 2 -> yes unless IME client", - .type = CONFIG_OPTION_INT, - .data = SConfigOptionDescription::SRangeData{2, 0, 2}, - }, - SConfigOptionDescription{ - .value = "input:virtualkeyboard:release_pressed_on_close", - .description = "Release all pressed keys by virtual keyboard on close.", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, - /* * input:tablet: */ @@ -775,6 +713,24 @@ inline static const std::vector CONFIG_OPTIONS = { * gestures: */ + SConfigOptionDescription{ + .value = "gestures:workspace_swipe", + .description = "enable workspace swipe gesture on touchpad", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, + SConfigOptionDescription{ + .value = "gestures:workspace_swipe_fingers", + .description = "how many fingers for the touchpad gesture", + .type = CONFIG_OPTION_INT, + .data = SConfigOptionDescription::SRangeData{3, 0, 5}, //##TODO RANGE? + }, + SConfigOptionDescription{ + .value = "gestures:workspace_swipe_min_fingers", + .description = "if enabled, workspace_swipe_fingers is considered the minimum number of fingers to swipe", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, SConfigOptionDescription{ .value = "gestures:workspace_swipe_distance", .description = "in px, the distance of the touchpad gesture", @@ -841,12 +797,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, - SConfigOptionDescription{ - .value = "gestures:close_max_timeout", - .description = "Timeout for closing windows with the close gesture, in ms.", - .type = CONFIG_OPTION_INT, - .data = SConfigOptionDescription::SRangeData{1000, 10, 2000}, - }, /* * group: @@ -941,18 +891,6 @@ inline static const std::vector 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", @@ -971,12 +909,6 @@ inline static const std::vector 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", @@ -1013,24 +945,12 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_INT, .data = SConfigOptionDescription::SRangeData{1, 0, 20}, }, - SConfigOptionDescription{ - .value = "group:groupbar:rounding_power", - .description = "rounding power of groupbar corners (2 is a circle)", - .type = CONFIG_OPTION_FLOAT, - .data = SConfigOptionDescription::SFloatData{2, 2, 10}, - }, SConfigOptionDescription{ .value = "group:groupbar:gradient_rounding", .description = "how much to round the groupbar gradient", .type = CONFIG_OPTION_INT, .data = SConfigOptionDescription::SRangeData{1, 0, 20}, }, - SConfigOptionDescription{ - .value = "group:groupbar:gradient_rounding_power", - .description = "rounding power of groupbar gradient corners (2 is a circle)", - .type = CONFIG_OPTION_FLOAT, - .data = SConfigOptionDescription::SFloatData{2, 2, 10}, - }, SConfigOptionDescription{ .value = "group:groupbar:round_only_edges", .description = "if yes, will only round at the groupbar edges", @@ -1045,28 +965,10 @@ inline static const std::vector 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", @@ -1103,30 +1005,6 @@ inline static const std::vector 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}, - }, - SConfigOptionDescription{ - .value = "group:groupbar:text_padding", - .description = "set horizontal padding for a text", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SRangeData{0, 0, 22}, - }, - SConfigOptionDescription{ - .value = "group:groupbar:blur", - .description = "enable background blur for groupbars", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, /* * misc: @@ -1192,12 +1070,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, - SConfigOptionDescription{ - .value = "misc:name_vk_after_proc", - .description = "Name virtual keyboards after the processes that create them. E.g. /usr/bin/fcitx5 will have hl-virtual-keyboard-fcitx5.", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, SConfigOptionDescription{ .value = "misc:always_follow_on_dnd", .description = "Will make mouse focus follow the mouse when drag and dropping. Recommended to leave it enabled, especially for people using focus follows mouse at 0.", @@ -1261,14 +1133,20 @@ inline static const std::vector 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}, }, @@ -1285,11 +1163,11 @@ inline static const std::vector CONFIG_OPTIONS = { .data = SConfigOptionDescription::SBoolData{true}, }, SConfigOptionDescription{ - .value = "misc:on_focus_under_fullscreen", - .description = "if there is a fullscreen or maximized window, decide whether a tiled window requested to focus should replace it, stay behind or disable the " - "fullscreen/maximized state. 0 - ignore focus request (keep focus on fullscreen window), 1 - takes over, 2 - unfullscreen/unmaximize [0/1/2]", + .value = "misc:new_window_takes_over_fullscreen", + .description = "if there is a fullscreen or maximized window, decide whether a new tiled window opened should replace it, stay behind or disable the fullscreen/maximized " + "state. 0 - behind, 1 - takes over, 2 - unfullscreen/unmaxize [0/1/2]", .type = CONFIG_OPTION_INT, - .data = SConfigOptionDescription::SRangeData{2, 0, 2}, + .data = SConfigOptionDescription::SRangeData{0, 0, 2}, }, SConfigOptionDescription{ .value = "misc:exit_window_retains_fullscreen", @@ -1322,14 +1200,8 @@ inline static const std::vector CONFIG_OPTIONS = { .data = SConfigOptionDescription::SBoolData{false}, }, SConfigOptionDescription{ - .value = "misc:disable_hyprland_guiutils_check", - .description = "disable the warning if hyprland-guiutils is missing", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, - SConfigOptionDescription{ - .value = "misc:disable_watchdog_warning", - .description = "whether to disable the warning about not using start-hyprland.", + .value = "misc:disable_hyprland_qtutils_check", + .description = "disable the warning if hyprland-qtutils is missing", .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, @@ -1345,30 +1217,6 @@ inline static const std::vector 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{5, 1, 20}, - }, - SConfigOptionDescription{ - .value = "misc:screencopy_force_8b", - .description = "forces 8 bit screencopy (fixes apps that don't understand 10bit)", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - SConfigOptionDescription{ - .value = "misc:disable_scale_notification", - .description = "disables notification popup when a monitor fails to set a suitable scale and falls back to suggested", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, - SConfigOptionDescription{ - .value = "misc:size_limits_tiled", - .description = "whether to apply minsize and maxsize rules to tiled windows", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, /* * binds: @@ -1392,12 +1240,6 @@ inline static const std::vector 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 " @@ -1454,12 +1296,6 @@ inline static const std::vector 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: @@ -1505,6 +1341,18 @@ inline static const std::vector 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 " @@ -1542,69 +1390,14 @@ inline static const std::vector CONFIG_OPTIONS = { .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}, - }, - SConfigOptionDescription{ - .value = "render:non_shader_cm", - .description = "Enable CM without shader. 0 - disable, 1 - whenever possible, 2 - DS and passthrough only, 3 - disable and ignore CM issues", - .type = CONFIG_OPTION_CHOICE, - .data = SConfigOptionDescription::SChoiceData{0, "disable,always,ondemand,ignore"}, - }, - SConfigOptionDescription{ - .value = "render:cm_sdr_eotf", - .description = "Default transfer function for displaying SDR apps. default - Use default value (Gamma 2.2), gamma22 - Treat unspecified as Gamma 2.2, gamma22force - Treat " - "unspecified and sRGB as Gamma 2.2, srgb - Treat unspecified as sRGB", - .type = CONFIG_OPTION_STRING_SHORT, - .data = SConfigOptionDescription::SStringData{"default"}, - }, - SConfigOptionDescription{ - .value = "render:commit_timing_enabled", - .description = "Enable commit timing proto. Requires restart", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - SConfigOptionDescription{ - .value = "render:icc_vcgt_enabled", - .description = "Enable sending VCGT ramps to KMS with ICC profiles", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - { - .value = "render:use_shader_blur_blend", - .description = "Use experimental blurred bg blending", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, /* * cursor: */ - SConfigOptionDescription{ - .value = "cursor:invisible", - .description = "don't render cursors", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, 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"}, }, @@ -1651,13 +1444,6 @@ inline static const std::vector 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)", @@ -1676,18 +1462,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, - SConfigOptionDescription{ - .value = "cursor:zoom_disable_aa", - .description = "If enabled, when zooming, no antialiasing will be used (zoom will be pixelated)", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, - SConfigOptionDescription{ - .value = "cursor:zoom_detached_camera", - .description = "Detaches the camera from the mouse when zoomed in, only ever moving to keep the mouse in view", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, SConfigOptionDescription{ .value = "cursor:enable_hyprcursor", .description = "whether to enable hyprcursor support", @@ -1706,17 +1480,11 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{true}, }, - SConfigOptionDescription{ - .value = "cursor:hide_on_tablet", - .description = "Hides the cursor when the last input was a tablet input until a mouse input is done.", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, SConfigOptionDescription{ .value = "cursor:use_cpu_buffer", - .description = "Makes HW cursors use a CPU buffer. Required on Nvidia to have HW cursors. 0 - off, 1 - on, 2 - auto (nvidia only)", - .type = CONFIG_OPTION_INT, - .data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2}, + .description = "Makes HW cursors use a CPU buffer. Required on Nvidia to have HW cursors. Experimental", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, }, SConfigOptionDescription{ .value = "cursor:sync_gsettings_theme", @@ -1747,12 +1515,6 @@ inline static const std::vector 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: @@ -1770,12 +1532,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, - SConfigOptionDescription{ - .value = "debug:gl_debugging", - .description = "enable OpenGL debugging and error checking, they hurt performance.", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, SConfigOptionDescription{ .value = "debug:disable_logs", .description = "disable logging to a file", @@ -1812,6 +1568,12 @@ inline static const std::vector 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.", @@ -1854,46 +1616,6 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, - SConfigOptionDescription{ - .value = "debug:ds_handle_same_buffer", - .description = "Special case for DS with unmodified buffer", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - SConfigOptionDescription{ - .value = "debug:ds_handle_same_buffer_fifo", - .description = "Special case for DS with unmodified buffer unlocks fifo", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - SConfigOptionDescription{ - .value = "debug:fifo_pending_workaround", - .description = "Fifo workaround for empty pending list", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, - SConfigOptionDescription{ - .value = "debug:render_solitary_wo_damage", - .description = "Render solitary window with empty damage", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{false}, - }, - - /* - * layout: - */ - SConfigOptionDescription{ - .value = "layout: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 = "layout: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}, - }, /* * dwindle: @@ -1964,15 +1686,9 @@ inline static const std::vector CONFIG_OPTIONS = { }, SConfigOptionDescription{ .value = "dwindle:split_bias", - .description = "specifies which window will receive the split ratio. 0 -> directional (the top or left window), 1 -> the current window", + .description = "specifies which window will receive the larger half of a split. positional - 0, current window - 1, opening window - 2 [0/1/2]", .type = CONFIG_OPTION_CHOICE, - .data = SConfigOptionDescription::SChoiceData{0, "directional,current"}, - }, - 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}, + .data = SConfigOptionDescription::SChoiceData{0, "positional,current,opening"}, }, /* @@ -2022,16 +1738,24 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_STRING_SHORT, .data = SConfigOptionDescription::SStringData{"left"}, }, + SConfigOptionDescription{ + .value = "master:inherit_fullscreen", + .description = "inherit fullscreen status when cycling/swapping to another window (e.g. monocle layout)", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{true}, + }, SConfigOptionDescription{ .value = "master:slave_count_for_center_master", .description = "when using orientation=center, make the master window centered only when at least this many slave windows are open. (Set 0 to always_center_master)", .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", @@ -2052,87 +1776,16 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{true}, }, + 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}, }, - - /* - * scrolling: - */ - - SConfigOptionDescription{ - .value = "scrolling:fullscreen_on_one_column", - .description = "when enabled, a single column on a workspace will always span the entire screen.", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - SConfigOptionDescription{ - .value = "scrolling:column_width", - .description = "the default width of a column, [0.1 - 1.0].", - .type = CONFIG_OPTION_FLOAT, - .data = SConfigOptionDescription::SFloatData{.value = 0.5, .min = 0.1, .max = 1.0}, - }, - SConfigOptionDescription{ - .value = "scrolling:focus_fit_method", - .description = "When a column is focused, what method should be used to bring it into view", - .type = CONFIG_OPTION_CHOICE, - .data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "center,fit"}, - }, - SConfigOptionDescription{ - .value = "scrolling:follow_focus", - .description = "when a window is focused, should the layout move to bring it into view automatically", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{.value = true}, - }, - SConfigOptionDescription{ - .value = "scrolling:follow_min_visible", - .description = "when a window is focused, require that at least a given fraction of it is visible for focus to follow", - .type = CONFIG_OPTION_FLOAT, - .data = SConfigOptionDescription::SFloatData{.value = 0.4, .min = 0.0, .max = 1.0}, - }, - SConfigOptionDescription{ - .value = "scrolling:explicit_column_widths", - .description = "A comma-separated list of preconfigured widths for colresize +conf/-conf", - .type = CONFIG_OPTION_STRING_SHORT, - .data = SConfigOptionDescription::SStringData{"0.333, 0.5, 0.667, 1.0"}, - }, - SConfigOptionDescription{ - .value = "scrolling:direction", - .description = "Direction in which new windows appear and the layout scrolls", - .type = CONFIG_OPTION_CHOICE, - .data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "right,left,down,up"}, - }, - SConfigOptionDescription{ - .value = "scrolling:wrap_focus", - .description = "Determines if column focus wraps around when going before the first column or past the last column", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{.value = true}, - }, - SConfigOptionDescription{ - .value = "scrolling:wrap_swapcol", - .description = "Determines if column movement wraps around when moving to before the first column or past the last column", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{.value = true}, - }, - - /* - * Quirks - */ - - SConfigOptionDescription{ - .value = "quirks:prefer_hdr", - .description = "Prefer HDR mode. 0 - off, 1 - always, 2 - gamescope only", - .type = CONFIG_OPTION_INT, - .data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 2}, - }, - SConfigOptionDescription{ - .value = "quirks:skip_non_kms_dmabuf_formats", - .description = "Do not report dmabuf formats which cannot be imported into KMS", - .type = CONFIG_OPTION_BOOL, - .data = SConfigOptionDescription::SBoolData{true}, - }, - }; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 29967e58..983681c9 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -8,42 +8,24 @@ #include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "config/ConfigDataValues.hpp" #include "config/ConfigValue.hpp" +#include "helpers/varlist/VarList.hpp" #include "../protocols/LayerShell.hpp" #include "../xwayland/XWayland.hpp" #include "../protocols/OutputManagement.hpp" -#include "../managers/animation/AnimationManager.hpp" -#include "../desktop/view/LayerSurface.hpp" -#include "../desktop/rule/Engine.hpp" -#include "../desktop/rule/windowRule/WindowRule.hpp" -#include "../desktop/rule/layerRule/LayerRule.hpp" -#include "../debug/HyprCtl.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../layout/space/Space.hpp" -#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp" +#include "../managers/AnimationManager.hpp" +#include "../desktop/LayerSurface.hpp" #include "defaultConfig.hpp" #include "../render/Renderer.hpp" #include "../hyprerror/HyprError.hpp" #include "../managers/input/InputManager.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" +#include "../managers/LayoutManager.hpp" #include "../managers/EventManager.hpp" -#include "../managers/permissions/DynamicPermissionManager.hpp" #include "../debug/HyprNotificationOverlay.hpp" #include "../plugins/PluginSystem.hpp" -#include "../managers/input/trackpad/TrackpadGestures.hpp" -#include "../managers/input/trackpad/gestures/DispatcherGesture.hpp" -#include "../managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp" -#include "../managers/input/trackpad/gestures/ResizeGesture.hpp" -#include "../managers/input/trackpad/gestures/MoveGesture.hpp" -#include "../managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp" -#include "../managers/input/trackpad/gestures/CloseGesture.hpp" -#include "../managers/input/trackpad/gestures/FloatGesture.hpp" -#include "../managers/input/trackpad/gestures/FullscreenGesture.hpp" -#include "../managers/input/trackpad/gestures/CursorZoomGesture.hpp" - -#include "../event/EventBus.hpp" - +#include "../managers/HookSystemManager.hpp" #include "../protocols/types/ContentType.hpp" #include #include @@ -59,10 +41,10 @@ #include #include #include +#include #include #include #include -#include #include #include using namespace Hyprutils::String; @@ -80,10 +62,10 @@ static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** if (!*data) *data = new CGradientValueData(); - const auto DATA = sc(*data); + const auto DATA = reinterpret_cast(*data); - CVarList2 varlist(std::string(V), 0, ' '); - DATA->m_colors.clear(); + CVarList varlist(V, 0, ' '); + DATA->m_vColors.clear(); std::string parseError = ""; @@ -91,38 +73,38 @@ static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** if (var.find("deg") != std::string::npos) { // last arg try { - DATA->m_angle = std::stoi(std::string(var.substr(0, var.find("deg")))) * (PI / 180.0); // radians + DATA->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (PI / 180.0); // radians } catch (...) { - Log::logger->log(Log::WARN, "Error parsing gradient {}", V); + Debug::log(WARN, "Error parsing gradient {}", V); parseError = "Error parsing gradient " + V; } break; } - if (DATA->m_colors.size() >= 10) { - Log::logger->log(Log::WARN, "Error parsing gradient {}: max colors is 10.", V); + if (DATA->m_vColors.size() >= 10) { + Debug::log(WARN, "Error parsing gradient {}: max colors is 10.", V); parseError = "Error parsing gradient " + V + ": max colors is 10."; break; } try { - const auto COL = configStringToInt(std::string(var)); + const auto COL = configStringToInt(var); if (!COL) throw std::runtime_error(std::format("failed to parse {} as a color", var)); - DATA->m_colors.emplace_back(COL.value()); + DATA->m_vColors.emplace_back(COL.value()); } catch (std::exception& e) { - Log::logger->log(Log::WARN, "Error parsing gradient {}", V); + Debug::log(WARN, "Error parsing gradient {}", V); parseError = "Error parsing gradient " + V + ": " + e.what(); } } - if (DATA->m_colors.empty()) { - Log::logger->log(Log::WARN, "Error parsing gradient {}", V); + if (DATA->m_vColors.size() == 0) { + Debug::log(WARN, "Error parsing gradient {}", V); if (parseError.empty()) parseError = "Error parsing gradient " + V + ": No colors?"; - DATA->m_colors.emplace_back(0); // transparent + DATA->m_vColors.emplace_back(0); // transparent } DATA->updateColorsOk(); @@ -136,7 +118,7 @@ static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** static void configHandleGradientDestroy(void** data) { if (*data) - delete sc(*data); + delete reinterpret_cast(*data); } static Hyprlang::CParseResult configHandleGapSet(const char* VALUE, void** data) { @@ -145,8 +127,8 @@ static Hyprlang::CParseResult configHandleGapSet(const char* VALUE, void** data) if (!*data) *data = new CCssGapData(); - const auto DATA = sc(*data); - CVarList2 varlist((std::string(V))); + const auto DATA = reinterpret_cast(*data); + CVarList varlist(V); Hyprlang::CParseResult result; try { @@ -161,29 +143,7 @@ static Hyprlang::CParseResult configHandleGapSet(const char* VALUE, void** data) static void configHandleGapDestroy(void** data) { if (*data) - delete sc(*data); -} - -static Hyprlang::CParseResult configHandleFontWeightSet(const char* VALUE, void** data) { - if (!*data) - *data = new CFontWeightConfigValueData(); - - const auto DATA = sc(*data); - Hyprlang::CParseResult result; - - try { - DATA->parseWeight(VALUE); - } catch (...) { - std::string parseError = std::format("{} is not a valid font weight", VALUE); - result.setError(parseError.c_str()); - } - - return result; -} - -static void configHandleFontWeightDestroy(void** data) { - if (*data) - delete sc(*data); + delete reinterpret_cast(*data); } static Hyprlang::CParseResult handleExec(const char* c, const char* v) { @@ -306,6 +266,54 @@ static Hyprlang::CParseResult handleUnbind(const char* c, const char* v) { return result; } +static Hyprlang::CParseResult handleWindowRule(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleLayerRule(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleLayerRule(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleWindowRuleV2(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + +static Hyprlang::CParseResult handleBlurLS(const char* c, const char* v) { + const std::string VALUE = v; + const std::string COMMAND = c; + + const auto RESULT = g_pConfigManager->handleBlurLS(COMMAND, VALUE); + + Hyprlang::CParseResult result; + if (RESULT.has_value()) + result.setError(RESULT.value().c_str()); + return result; +} + static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) { const std::string VALUE = v; const std::string COMMAND = c; @@ -366,101 +374,41 @@ static Hyprlang::CParseResult handlePlugin(const char* c, const char* v) { return result; } -static Hyprlang::CParseResult handlePermission(const char* c, const char* v) { - const std::string VALUE = v; - const std::string COMMAND = c; - - const auto RESULT = g_pConfigManager->handlePermission(COMMAND, VALUE); - - Hyprlang::CParseResult result; - if (RESULT.has_value()) - result.setError(RESULT.value().c_str()); - return result; -} - -static Hyprlang::CParseResult handleGesture(const char* c, const char* v) { - const std::string VALUE = v; - const std::string COMMAND = c; - - const auto RESULT = g_pConfigManager->handleGesture(COMMAND, VALUE); - - Hyprlang::CParseResult result; - if (RESULT.has_value()) - result.setError(RESULT.value().c_str()); - return result; -} - -static Hyprlang::CParseResult handleWindowrule(const char* c, const char* v) { - const std::string VALUE = v; - const std::string COMMAND = c; - - const auto RESULT = g_pConfigManager->handleWindowrule(COMMAND, VALUE); - - Hyprlang::CParseResult result; - if (RESULT.has_value()) - result.setError(RESULT.value().c_str()); - return result; -} - -static Hyprlang::CParseResult handleWindowrulev2(const char* c, const char* v) { - Hyprlang::CParseResult res; - res.setError("windowrulev2 is deprecated. Correct syntax can be found on the wiki."); - return res; -} - -static Hyprlang::CParseResult handleLayerrule(const char* c, const char* v) { - const std::string VALUE = v; - const std::string COMMAND = c; - - const auto RESULT = g_pConfigManager->handleLayerrule(COMMAND, VALUE); - - Hyprlang::CParseResult result; - if (RESULT.has_value()) - result.setError(RESULT.value().c_str()); - return result; -} - -static Hyprlang::CParseResult handleLayerrulev2(const char* c, const char* v) { - Hyprlang::CParseResult res; - res.setError("layerrulev2 doesn't exist. Correct syntax can be found on the wiki."); - return res; -} - void CConfigManager::registerConfigVar(const char* name, const Hyprlang::INT& val) { m_configValueNumber++; - m_config->addConfigValue(name, val); + m_pConfig->addConfigValue(name, val); } void CConfigManager::registerConfigVar(const char* name, const Hyprlang::FLOAT& val) { m_configValueNumber++; - m_config->addConfigValue(name, val); + m_pConfig->addConfigValue(name, val); } void CConfigManager::registerConfigVar(const char* name, const Hyprlang::VEC2& val) { m_configValueNumber++; - m_config->addConfigValue(name, val); + m_pConfig->addConfigValue(name, val); } void CConfigManager::registerConfigVar(const char* name, const Hyprlang::STRING& val) { m_configValueNumber++; - m_config->addConfigValue(name, val); + m_pConfig->addConfigValue(name, val); } void CConfigManager::registerConfigVar(const char* name, Hyprlang::CUSTOMTYPE&& val) { m_configValueNumber++; - m_config->addConfigValue(name, std::move(val)); + m_pConfig->addConfigValue(name, std::move(val)); } CConfigManager::CConfigManager() { const auto ERR = verifyConfigExists(); m_configPaths.emplace_back(getMainConfigPath()); - m_config = makeUnique(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true}); + m_pConfig = makeUnique(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true}); registerConfigVar("general:border_size", Hyprlang::INT{1}); + registerConfigVar("general:no_border_on_floating", Hyprlang::INT{0}); registerConfigVar("general:gaps_in", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "5"}); registerConfigVar("general:gaps_out", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "20"}); - registerConfigVar("general:float_gaps", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "0"}); registerConfigVar("general:gaps_workspaces", Hyprlang::INT{0}); registerConfigVar("general:no_focus_fallback", Hyprlang::INT{0}); registerConfigVar("general:resize_on_border", Hyprlang::INT{0}); @@ -473,13 +421,10 @@ CConfigManager::CConfigManager() { registerConfigVar("general:snap:window_gap", Hyprlang::INT{10}); registerConfigVar("general:snap:monitor_gap", Hyprlang::INT{10}); registerConfigVar("general:snap:border_overlap", Hyprlang::INT{0}); - registerConfigVar("general:snap:respect_gaps", Hyprlang::INT{0}); registerConfigVar("general:col.active_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffffffff"}); registerConfigVar("general:col.inactive_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xff444444"}); registerConfigVar("general:col.nogroup_border", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffffaaff"}); registerConfigVar("general:col.nogroup_border_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0xffff00ff"}); - registerConfigVar("general:modal_parent_blocking", Hyprlang::INT{1}); - registerConfigVar("general:locale", {""}); registerConfigVar("misc:disable_hyprland_logo", Hyprlang::INT{0}); registerConfigVar("misc:disable_splash_rendering", Hyprlang::INT{0}); @@ -491,7 +436,6 @@ CConfigManager::CConfigManager() { registerConfigVar("misc:vrr", Hyprlang::INT{0}); registerConfigVar("misc:mouse_move_enables_dpms", Hyprlang::INT{0}); registerConfigVar("misc:key_press_enables_dpms", Hyprlang::INT{0}); - registerConfigVar("misc:name_vk_after_proc", Hyprlang::INT{1}); registerConfigVar("misc:always_follow_on_dnd", Hyprlang::INT{1}); registerConfigVar("misc:layers_hog_keyboard_focus", Hyprlang::INT{1}); registerConfigVar("misc:animate_manual_resizes", Hyprlang::INT{0}); @@ -502,24 +446,20 @@ CConfigManager::CConfigManager() { registerConfigVar("misc:swallow_exception_regex", {STRVAL_EMPTY}); registerConfigVar("misc:focus_on_activate", Hyprlang::INT{0}); registerConfigVar("misc:mouse_move_focuses_monitor", Hyprlang::INT{1}); + registerConfigVar("misc:render_ahead_of_time", Hyprlang::INT{0}); + registerConfigVar("misc:render_ahead_safezone", Hyprlang::INT{1}); registerConfigVar("misc:allow_session_lock_restore", Hyprlang::INT{0}); - registerConfigVar("misc:session_lock_xray", Hyprlang::INT{0}); registerConfigVar("misc:close_special_on_empty", Hyprlang::INT{1}); registerConfigVar("misc:background_color", Hyprlang::INT{0xff111111}); - registerConfigVar("misc:on_focus_under_fullscreen", Hyprlang::INT{2}); + registerConfigVar("misc:new_window_takes_over_fullscreen", Hyprlang::INT{0}); registerConfigVar("misc:exit_window_retains_fullscreen", Hyprlang::INT{0}); registerConfigVar("misc:initial_workspace_tracking", Hyprlang::INT{1}); registerConfigVar("misc:middle_click_paste", Hyprlang::INT{1}); registerConfigVar("misc:render_unfocused_fps", Hyprlang::INT{15}); registerConfigVar("misc:disable_xdg_env_checks", Hyprlang::INT{0}); - registerConfigVar("misc:disable_hyprland_guiutils_check", Hyprlang::INT{0}); - registerConfigVar("misc:disable_watchdog_warning", Hyprlang::INT{0}); + registerConfigVar("misc:disable_hyprland_qtutils_check", Hyprlang::INT{0}); registerConfigVar("misc:lockdead_screen_delay", Hyprlang::INT{1000}); registerConfigVar("misc:enable_anr_dialog", Hyprlang::INT{1}); - registerConfigVar("misc:anr_missed_pings", Hyprlang::INT{5}); - registerConfigVar("misc:screencopy_force_8b", Hyprlang::INT{1}); - registerConfigVar("misc:disable_scale_notification", Hyprlang::INT{0}); - registerConfigVar("misc:size_limits_tiled", Hyprlang::INT{0}); registerConfigVar("group:insert_after_current", Hyprlang::INT{1}); registerConfigVar("group:focus_removed_window", Hyprlang::INT{1}); @@ -531,54 +471,38 @@ CConfigManager::CConfigManager() { registerConfigVar("group:group_on_movetoworkspace", Hyprlang::INT{0}); registerConfigVar("group:groupbar:enabled", Hyprlang::INT{1}); registerConfigVar("group:groupbar:font_family", {STRVAL_EMPTY}); - registerConfigVar("group:groupbar:font_weight_active", Hyprlang::CConfigCustomValueType{&configHandleFontWeightSet, configHandleFontWeightDestroy, "normal"}); - registerConfigVar("group:groupbar:font_weight_inactive", Hyprlang::CConfigCustomValueType{&configHandleFontWeightSet, configHandleFontWeightDestroy, "normal"}); registerConfigVar("group:groupbar:font_size", Hyprlang::INT{8}); registerConfigVar("group:groupbar:gradients", Hyprlang::INT{0}); registerConfigVar("group:groupbar:height", Hyprlang::INT{14}); - registerConfigVar("group:groupbar:indicator_gap", Hyprlang::INT{0}); registerConfigVar("group:groupbar:indicator_height", Hyprlang::INT{3}); registerConfigVar("group:groupbar:priority", Hyprlang::INT{3}); registerConfigVar("group:groupbar:render_titles", Hyprlang::INT{1}); registerConfigVar("group:groupbar:scrolling", Hyprlang::INT{1}); registerConfigVar("group:groupbar:text_color", Hyprlang::INT{0xffffffff}); - registerConfigVar("group:groupbar:text_color_inactive", Hyprlang::INT{-1}); - registerConfigVar("group:groupbar:text_color_locked_active", Hyprlang::INT{-1}); - registerConfigVar("group:groupbar:text_color_locked_inactive", Hyprlang::INT{-1}); registerConfigVar("group:groupbar:stacked", Hyprlang::INT{0}); registerConfigVar("group:groupbar:rounding", Hyprlang::INT{1}); - registerConfigVar("group:groupbar:rounding_power", {2.F}); registerConfigVar("group:groupbar:gradient_rounding", Hyprlang::INT{2}); - registerConfigVar("group:groupbar:gradient_rounding_power", {2.F}); registerConfigVar("group:groupbar:round_only_edges", Hyprlang::INT{1}); registerConfigVar("group:groupbar:gradient_round_only_edges", Hyprlang::INT{1}); registerConfigVar("group:groupbar:gaps_out", Hyprlang::INT{2}); registerConfigVar("group:groupbar:gaps_in", Hyprlang::INT{2}); - registerConfigVar("group:groupbar:keep_upper_gap", Hyprlang::INT{1}); - registerConfigVar("group:groupbar:text_offset", Hyprlang::INT{0}); - registerConfigVar("group:groupbar:text_padding", Hyprlang::INT{0}); - registerConfigVar("group:groupbar:blur", Hyprlang::INT{0}); registerConfigVar("debug:log_damage", Hyprlang::INT{0}); registerConfigVar("debug:overlay", Hyprlang::INT{0}); registerConfigVar("debug:damage_blink", Hyprlang::INT{0}); registerConfigVar("debug:pass", Hyprlang::INT{0}); - registerConfigVar("debug:gl_debugging", Hyprlang::INT{0}); registerConfigVar("debug:disable_logs", Hyprlang::INT{1}); registerConfigVar("debug:disable_time", Hyprlang::INT{1}); registerConfigVar("debug:enable_stdout_logs", Hyprlang::INT{0}); - registerConfigVar("debug:damage_tracking", {sc(DAMAGE_TRACKING_FULL)}); + registerConfigVar("debug:damage_tracking", {(Hyprlang::INT)DAMAGE_TRACKING_FULL}); registerConfigVar("debug:manual_crash", Hyprlang::INT{0}); registerConfigVar("debug:suppress_errors", Hyprlang::INT{0}); registerConfigVar("debug:error_limit", Hyprlang::INT{5}); registerConfigVar("debug:error_position", Hyprlang::INT{0}); + registerConfigVar("debug:watchdog_timeout", Hyprlang::INT{5}); registerConfigVar("debug:disable_scale_checks", Hyprlang::INT{0}); registerConfigVar("debug:colored_stdout_logs", Hyprlang::INT{1}); registerConfigVar("debug:full_cm_proto", Hyprlang::INT{0}); - registerConfigVar("debug:ds_handle_same_buffer", Hyprlang::INT{1}); - registerConfigVar("debug:ds_handle_same_buffer_fifo", Hyprlang::INT{1}); - registerConfigVar("debug:fifo_pending_workaround", Hyprlang::INT{0}); - registerConfigVar("debug:render_solitary_wo_damage", Hyprlang::INT{0}); registerConfigVar("decoration:rounding", Hyprlang::INT{0}); registerConfigVar("decoration:rounding_power", {2.F}); @@ -609,17 +533,12 @@ CConfigManager::CConfigManager() { registerConfigVar("decoration:shadow:scale", {1.f}); registerConfigVar("decoration:shadow:sharp", Hyprlang::INT{0}); registerConfigVar("decoration:shadow:color", Hyprlang::INT{0xee1a1a1a}); - registerConfigVar("decoration:shadow:color_inactive", Hyprlang::INT{-1}); + registerConfigVar("decoration:shadow:color_inactive", {(Hyprlang::INT)INT64_MAX}); registerConfigVar("decoration:dim_inactive", Hyprlang::INT{0}); - registerConfigVar("decoration:dim_modal", Hyprlang::INT{1}); registerConfigVar("decoration:dim_strength", {0.5f}); registerConfigVar("decoration:dim_special", {0.2f}); registerConfigVar("decoration:dim_around", {0.4f}); registerConfigVar("decoration:screen_shader", {STRVAL_EMPTY}); - registerConfigVar("decoration:border_part_of_window", Hyprlang::INT{1}); - - registerConfigVar("layout:single_window_aspect_ratio", Hyprlang::VEC2{0, 0}); - registerConfigVar("layout:single_window_aspect_ratio_tolerance", {0.1f}); registerConfigVar("dwindle:pseudotile", Hyprlang::INT{0}); registerConfigVar("dwindle:force_split", Hyprlang::INT{0}); @@ -632,34 +551,24 @@ CConfigManager::CConfigManager() { registerConfigVar("dwindle:split_bias", Hyprlang::INT{0}); registerConfigVar("dwindle:smart_split", Hyprlang::INT{0}); registerConfigVar("dwindle:smart_resizing", Hyprlang::INT{1}); - registerConfigVar("dwindle:precise_mouse_move", Hyprlang::INT{0}); registerConfigVar("master:special_scale_factor", {1.f}); registerConfigVar("master:mfact", {0.55f}); registerConfigVar("master:new_status", {"slave"}); registerConfigVar("master:slave_count_for_center_master", Hyprlang::INT{2}); - registerConfigVar("master:center_master_fallback", {"left"}); + registerConfigVar("master:center_master_slaves_on_right", Hyprlang::INT{1}); registerConfigVar("master:center_ignores_reserved", Hyprlang::INT{0}); registerConfigVar("master:new_on_active", {"none"}); registerConfigVar("master:new_on_top", Hyprlang::INT{0}); registerConfigVar("master:orientation", {"left"}); + registerConfigVar("master:inherit_fullscreen", Hyprlang::INT{1}); registerConfigVar("master:allow_small_split", Hyprlang::INT{0}); registerConfigVar("master:smart_resizing", Hyprlang::INT{1}); registerConfigVar("master:drop_at_cursor", Hyprlang::INT{1}); registerConfigVar("master:always_keep_position", Hyprlang::INT{0}); - registerConfigVar("scrolling:fullscreen_on_one_column", Hyprlang::INT{1}); - registerConfigVar("scrolling:column_width", Hyprlang::FLOAT{0.5F}); - registerConfigVar("scrolling:focus_fit_method", Hyprlang::INT{1}); - registerConfigVar("scrolling:follow_focus", Hyprlang::INT{1}); - registerConfigVar("scrolling:follow_min_visible", Hyprlang::FLOAT{0.4}); - registerConfigVar("scrolling:explicit_column_widths", Hyprlang::STRING{"0.333, 0.5, 0.667, 1.0"}); - registerConfigVar("scrolling:direction", Hyprlang::STRING{"right"}); - registerConfigVar("scrolling:wrap_focus", Hyprlang::INT{1}); - registerConfigVar("scrolling:wrap_swapcol", Hyprlang::INT{1}); - registerConfigVar("animations:enabled", Hyprlang::INT{1}); - registerConfigVar("animations:workspace_wraparound", Hyprlang::INT{0}); + registerConfigVar("animations:first_launch_animation", Hyprlang::INT{1}); registerConfigVar("input:follow_mouse", Hyprlang::INT{1}); registerConfigVar("input:follow_mouse_threshold", Hyprlang::FLOAT{0}); @@ -669,7 +578,6 @@ CConfigManager::CConfigManager() { registerConfigVar("input:off_window_axis_events", Hyprlang::INT{1}); registerConfigVar("input:sensitivity", {0.f}); registerConfigVar("input:accel_profile", {STRVAL_EMPTY}); - registerConfigVar("input:rotation", Hyprlang::INT{0}); registerConfigVar("input:kb_file", {STRVAL_EMPTY}); registerConfigVar("input:kb_layout", {"us"}); registerConfigVar("input:kb_variant", {STRVAL_EMPTY}); @@ -701,12 +609,9 @@ CConfigManager::CConfigManager() { registerConfigVar("input:touchpad:scroll_factor", {1.f}); registerConfigVar("input:touchpad:flip_x", Hyprlang::INT{0}); registerConfigVar("input:touchpad:flip_y", Hyprlang::INT{0}); - registerConfigVar("input:touchpad:drag_3fg", Hyprlang::INT{0}); registerConfigVar("input:touchdevice:transform", Hyprlang::INT{-1}); registerConfigVar("input:touchdevice:output", {"[[Auto]]"}); registerConfigVar("input:touchdevice:enabled", Hyprlang::INT{1}); - registerConfigVar("input:virtualkeyboard:share_states", Hyprlang::INT{2}); - registerConfigVar("input:virtualkeyboard:release_pressed_on_close", Hyprlang::INT{0}); registerConfigVar("input:tablet:transform", Hyprlang::INT{0}); registerConfigVar("input:tablet:output", {STRVAL_EMPTY}); registerConfigVar("input:tablet:region_position", Hyprlang::VEC2{0, 0}); @@ -720,7 +625,6 @@ CConfigManager::CConfigManager() { registerConfigVar("binds:pass_mouse_when_bound", Hyprlang::INT{0}); registerConfigVar("binds:scroll_event_delay", Hyprlang::INT{300}); registerConfigVar("binds:workspace_back_and_forth", Hyprlang::INT{0}); - registerConfigVar("binds:hide_special_on_workspace_change", Hyprlang::INT{0}); registerConfigVar("binds:allow_workspace_cycles", Hyprlang::INT{0}); registerConfigVar("binds:workspace_center_on", Hyprlang::INT{1}); registerConfigVar("binds:focus_preferred_method", Hyprlang::INT{0}); @@ -728,10 +632,12 @@ CConfigManager::CConfigManager() { registerConfigVar("binds:movefocus_cycles_fullscreen", Hyprlang::INT{0}); registerConfigVar("binds:movefocus_cycles_groupfirst", Hyprlang::INT{0}); registerConfigVar("binds:disable_keybind_grabbing", Hyprlang::INT{0}); - registerConfigVar("binds:allow_pin_fullscreen", Hyprlang::INT{0}); - registerConfigVar("binds:drag_threshold", Hyprlang::INT{0}); registerConfigVar("binds:window_direction_monitor_fallback", Hyprlang::INT{1}); + registerConfigVar("binds:allow_pin_fullscreen", Hyprlang::INT{0}); + registerConfigVar("gestures:workspace_swipe", Hyprlang::INT{0}); + registerConfigVar("gestures:workspace_swipe_fingers", Hyprlang::INT{3}); + registerConfigVar("gestures:workspace_swipe_min_fingers", Hyprlang::INT{0}); registerConfigVar("gestures:workspace_swipe_distance", Hyprlang::INT{300}); registerConfigVar("gestures:workspace_swipe_invert", Hyprlang::INT{1}); registerConfigVar("gestures:workspace_swipe_min_speed_to_force", Hyprlang::INT{30}); @@ -743,7 +649,6 @@ CConfigManager::CConfigManager() { registerConfigVar("gestures:workspace_swipe_use_r", Hyprlang::INT{0}); registerConfigVar("gestures:workspace_swipe_touch", Hyprlang::INT{0}); registerConfigVar("gestures:workspace_swipe_touch_invert", Hyprlang::INT{0}); - registerConfigVar("gestures:close_max_timeout", Hyprlang::INT{1000}); registerConfigVar("xwayland:enabled", Hyprlang::INT{1}); registerConfigVar("xwayland:use_nearest_neighbor", Hyprlang::INT{1}); @@ -752,7 +657,6 @@ CConfigManager::CConfigManager() { registerConfigVar("opengl:nvidia_anti_flicker", Hyprlang::INT{1}); - registerConfigVar("cursor:invisible", Hyprlang::INT{0}); registerConfigVar("cursor:no_hardware_cursors", Hyprlang::INT{2}); registerConfigVar("cursor:no_break_fs_vrr", Hyprlang::INT{2}); registerConfigVar("cursor:min_refresh_rate", Hyprlang::INT{24}); @@ -761,17 +665,13 @@ CConfigManager::CConfigManager() { registerConfigVar("cursor:no_warps", Hyprlang::INT{0}); registerConfigVar("cursor:persistent_warps", Hyprlang::INT{0}); registerConfigVar("cursor:warp_on_change_workspace", Hyprlang::INT{0}); - registerConfigVar("cursor:warp_on_toggle_special", Hyprlang::INT{0}); registerConfigVar("cursor:default_monitor", {STRVAL_EMPTY}); registerConfigVar("cursor:zoom_factor", {1.f}); registerConfigVar("cursor:zoom_rigid", Hyprlang::INT{0}); - registerConfigVar("cursor:zoom_disable_aa", Hyprlang::INT{0}); - registerConfigVar("cursor:zoom_detached_camera", Hyprlang::INT{1}); registerConfigVar("cursor:enable_hyprcursor", Hyprlang::INT{1}); registerConfigVar("cursor:sync_gsettings_theme", Hyprlang::INT{1}); registerConfigVar("cursor:hide_on_key_press", Hyprlang::INT{0}); registerConfigVar("cursor:hide_on_touch", Hyprlang::INT{1}); - registerConfigVar("cursor:hide_on_tablet", Hyprlang::INT{0}); registerConfigVar("cursor:use_cpu_buffer", Hyprlang::INT{2}); registerConfigVar("cursor:warp_back_after_non_mouse_input", Hyprlang::INT{0}); @@ -787,219 +687,130 @@ CConfigManager::CConfigManager() { registerConfigVar("group:groupbar:col.locked_active", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66ff5500"}); registerConfigVar("group:groupbar:col.locked_inactive", Hyprlang::CConfigCustomValueType{&configHandleGradientSet, configHandleGradientDestroy, "0x66775500"}); + registerConfigVar("render:explicit_sync", Hyprlang::INT{2}); + registerConfigVar("render:explicit_sync_kms", Hyprlang::INT{2}); registerConfigVar("render:direct_scanout", Hyprlang::INT{0}); registerConfigVar("render:expand_undersized_textures", Hyprlang::INT{1}); registerConfigVar("render:xp_mode", Hyprlang::INT{0}); registerConfigVar("render:ctm_animation", Hyprlang::INT{2}); registerConfigVar("render:cm_fs_passthrough", Hyprlang::INT{2}); registerConfigVar("render:cm_enabled", Hyprlang::INT{1}); - registerConfigVar("render:send_content_type", Hyprlang::INT{1}); - registerConfigVar("render:cm_auto_hdr", Hyprlang::INT{1}); - registerConfigVar("render:new_render_scheduling", Hyprlang::INT{0}); - registerConfigVar("render:non_shader_cm", Hyprlang::INT{3}); - registerConfigVar("render:cm_sdr_eotf", {"default"}); - registerConfigVar("render:commit_timing_enabled", Hyprlang::INT{1}); - registerConfigVar("render:icc_vcgt_enabled", Hyprlang::INT{1}); - registerConfigVar("render:use_shader_blur_blend", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0}); - registerConfigVar("ecosystem:enforce_permissions", Hyprlang::INT{0}); - registerConfigVar("quirks:prefer_hdr", Hyprlang::INT{0}); - registerConfigVar("quirks:skip_non_kms_dmabuf_formats", Hyprlang::INT{0}); + registerConfigVar("experimental:xx_color_management_v4", Hyprlang::INT{0}); // devices - m_config->addSpecialCategory("device", {"name"}); - m_config->addSpecialConfigValue("device", "sensitivity", {0.F}); - m_config->addSpecialConfigValue("device", "accel_profile", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "rotation", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "kb_file", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "kb_layout", {"us"}); - m_config->addSpecialConfigValue("device", "kb_variant", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "kb_options", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "kb_rules", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "kb_model", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "repeat_rate", Hyprlang::INT{25}); - m_config->addSpecialConfigValue("device", "repeat_delay", Hyprlang::INT{600}); - m_config->addSpecialConfigValue("device", "natural_scroll", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "tap_button_map", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "numlock_by_default", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "resolve_binds_by_sym", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "disable_while_typing", Hyprlang::INT{1}); - m_config->addSpecialConfigValue("device", "clickfinger_behavior", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "middle_button_emulation", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "tap-to-click", Hyprlang::INT{1}); - m_config->addSpecialConfigValue("device", "tap-and-drag", Hyprlang::INT{1}); - m_config->addSpecialConfigValue("device", "drag_lock", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "left_handed", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "scroll_method", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "scroll_button", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "scroll_button_lock", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("device", "scroll_points", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "scroll_factor", Hyprlang::FLOAT{-1}); - m_config->addSpecialConfigValue("device", "transform", Hyprlang::INT{-1}); - m_config->addSpecialConfigValue("device", "output", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("device", "enabled", Hyprlang::INT{1}); // only for mice, touchpads, and touchdevices - m_config->addSpecialConfigValue("device", "region_position", Hyprlang::VEC2{0, 0}); // only for tablets - m_config->addSpecialConfigValue("device", "absolute_region_position", Hyprlang::INT{0}); // only for tablets - m_config->addSpecialConfigValue("device", "region_size", Hyprlang::VEC2{0, 0}); // only for tablets - m_config->addSpecialConfigValue("device", "relative_input", Hyprlang::INT{0}); // only for tablets - m_config->addSpecialConfigValue("device", "active_area_position", Hyprlang::VEC2{0, 0}); // only for tablets - m_config->addSpecialConfigValue("device", "active_area_size", Hyprlang::VEC2{0, 0}); // only for tablets - m_config->addSpecialConfigValue("device", "flip_x", Hyprlang::INT{0}); // only for touchpads - m_config->addSpecialConfigValue("device", "flip_y", Hyprlang::INT{0}); // only for touchpads - m_config->addSpecialConfigValue("device", "drag_3fg", Hyprlang::INT{0}); // only for touchpads - m_config->addSpecialConfigValue("device", "keybinds", Hyprlang::INT{1}); // enable/disable keybinds - m_config->addSpecialConfigValue("device", "share_states", Hyprlang::INT{0}); // only for virtualkeyboards - m_config->addSpecialConfigValue("device", "release_pressed_on_close", Hyprlang::INT{0}); // only for virtualkeyboards - - m_config->addSpecialCategory("monitorv2", {.key = "output"}); - m_config->addSpecialConfigValue("monitorv2", "disabled", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("monitorv2", "mode", {"preferred"}); - m_config->addSpecialConfigValue("monitorv2", "position", {"auto"}); - m_config->addSpecialConfigValue("monitorv2", "scale", {"auto"}); - m_config->addSpecialConfigValue("monitorv2", "addreserved", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("monitorv2", "mirror", {STRVAL_EMPTY}); - m_config->addSpecialConfigValue("monitorv2", "bitdepth", {STRVAL_EMPTY}); // TODO use correct type - m_config->addSpecialConfigValue("monitorv2", "cm", {"auto"}); - m_config->addSpecialConfigValue("monitorv2", "sdr_eotf", {"default"}); - m_config->addSpecialConfigValue("monitorv2", "sdrbrightness", Hyprlang::FLOAT{1.0}); - m_config->addSpecialConfigValue("monitorv2", "sdrsaturation", Hyprlang::FLOAT{1.0}); - m_config->addSpecialConfigValue("monitorv2", "vrr", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("monitorv2", "transform", {STRVAL_EMPTY}); // TODO use correct type - m_config->addSpecialConfigValue("monitorv2", "supports_wide_color", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("monitorv2", "supports_hdr", Hyprlang::INT{0}); - m_config->addSpecialConfigValue("monitorv2", "sdr_min_luminance", Hyprlang::FLOAT{0.2}); - m_config->addSpecialConfigValue("monitorv2", "sdr_max_luminance", Hyprlang::INT{80}); - m_config->addSpecialConfigValue("monitorv2", "min_luminance", Hyprlang::FLOAT{-1.0}); - m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1}); - m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1}); - m_config->addSpecialConfigValue("monitorv2", "icc", Hyprlang::STRING{""}); - - // windowrule v3 - m_config->addSpecialCategory("windowrule", {.key = "name"}); - m_config->addSpecialConfigValue("windowrule", "enable", Hyprlang::INT{1}); - - // layerrule v2 - m_config->addSpecialCategory("layerrule", {.key = "name"}); - m_config->addSpecialConfigValue("layerrule", "enable", Hyprlang::INT{1}); - - reloadRuleConfigs(); + m_pConfig->addSpecialCategory("device", {"name"}); + m_pConfig->addSpecialConfigValue("device", "sensitivity", {0.F}); + m_pConfig->addSpecialConfigValue("device", "accel_profile", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_file", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_layout", {"us"}); + m_pConfig->addSpecialConfigValue("device", "kb_variant", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_options", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_rules", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "kb_model", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "repeat_rate", Hyprlang::INT{25}); + m_pConfig->addSpecialConfigValue("device", "repeat_delay", Hyprlang::INT{600}); + m_pConfig->addSpecialConfigValue("device", "natural_scroll", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "tap_button_map", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "numlock_by_default", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "resolve_binds_by_sym", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "disable_while_typing", Hyprlang::INT{1}); + m_pConfig->addSpecialConfigValue("device", "clickfinger_behavior", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "middle_button_emulation", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "tap-to-click", Hyprlang::INT{1}); + m_pConfig->addSpecialConfigValue("device", "tap-and-drag", Hyprlang::INT{1}); + m_pConfig->addSpecialConfigValue("device", "drag_lock", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "left_handed", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "scroll_method", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "scroll_button", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "scroll_button_lock", Hyprlang::INT{0}); + m_pConfig->addSpecialConfigValue("device", "scroll_points", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "transform", Hyprlang::INT{-1}); + m_pConfig->addSpecialConfigValue("device", "output", {STRVAL_EMPTY}); + m_pConfig->addSpecialConfigValue("device", "enabled", Hyprlang::INT{1}); // only for mice, touchpads, and touchdevices + m_pConfig->addSpecialConfigValue("device", "region_position", Hyprlang::VEC2{0, 0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "absolute_region_position", Hyprlang::INT{0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "region_size", Hyprlang::VEC2{0, 0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "relative_input", Hyprlang::INT{0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "active_area_position", Hyprlang::VEC2{0, 0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "active_area_size", Hyprlang::VEC2{0, 0}); // only for tablets + m_pConfig->addSpecialConfigValue("device", "flip_x", Hyprlang::INT{0}); // only for touchpads + m_pConfig->addSpecialConfigValue("device", "flip_y", Hyprlang::INT{0}); // only for touchpads // keywords - m_config->registerHandler(&::handleExec, "exec", {false}); - m_config->registerHandler(&::handleRawExec, "execr", {false}); - m_config->registerHandler(&::handleExecOnce, "exec-once", {false}); - m_config->registerHandler(&::handleExecRawOnce, "execr-once", {false}); - m_config->registerHandler(&::handleExecShutdown, "exec-shutdown", {false}); - m_config->registerHandler(&::handleMonitor, "monitor", {false}); - m_config->registerHandler(&::handleBind, "bind", {true}); - m_config->registerHandler(&::handleUnbind, "unbind", {false}); - m_config->registerHandler(&::handleWorkspaceRules, "workspace", {false}); - m_config->registerHandler(&::handleWindowrule, "windowrule", {false}); - m_config->registerHandler(&::handleLayerrule, "layerrule", {false}); - m_config->registerHandler(&::handleBezier, "bezier", {false}); - m_config->registerHandler(&::handleAnimation, "animation", {false}); - m_config->registerHandler(&::handleSource, "source", {false}); - m_config->registerHandler(&::handleSubmap, "submap", {false}); - m_config->registerHandler(&::handlePlugin, "plugin", {false}); - m_config->registerHandler(&::handlePermission, "permission", {false}); - m_config->registerHandler(&::handleGesture, "gesture", {true}); - m_config->registerHandler(&::handleEnv, "env", {true}); - - // windowrulev2 and layerrulev2 errors - m_config->registerHandler(&::handleWindowrulev2, "windowrulev2", {false}); - m_config->registerHandler(&::handleLayerrulev2, "layerrulev2", {false}); + m_pConfig->registerHandler(&::handleExec, "exec", {false}); + m_pConfig->registerHandler(&::handleRawExec, "execr", {false}); + m_pConfig->registerHandler(&::handleExecOnce, "exec-once", {false}); + m_pConfig->registerHandler(&::handleExecRawOnce, "execr-once", {false}); + m_pConfig->registerHandler(&::handleExecShutdown, "exec-shutdown", {false}); + m_pConfig->registerHandler(&::handleMonitor, "monitor", {false}); + m_pConfig->registerHandler(&::handleBind, "bind", {true}); + m_pConfig->registerHandler(&::handleUnbind, "unbind", {false}); + m_pConfig->registerHandler(&::handleWorkspaceRules, "workspace", {false}); + m_pConfig->registerHandler(&::handleWindowRule, "windowrule", {false}); + m_pConfig->registerHandler(&::handleLayerRule, "layerrule", {false}); + m_pConfig->registerHandler(&::handleWindowRuleV2, "windowrulev2", {false}); + m_pConfig->registerHandler(&::handleBezier, "bezier", {false}); + m_pConfig->registerHandler(&::handleAnimation, "animation", {false}); + m_pConfig->registerHandler(&::handleSource, "source", {false}); + m_pConfig->registerHandler(&::handleSubmap, "submap", {false}); + m_pConfig->registerHandler(&::handleBlurLS, "blurls", {false}); + m_pConfig->registerHandler(&::handlePlugin, "plugin", {false}); + m_pConfig->registerHandler(&::handleEnv, "env", {true}); // pluginza - m_config->addSpecialCategory("plugin", {nullptr, true}); + m_pConfig->addSpecialCategory("plugin", {nullptr, true}); - m_config->commence(); + m_pConfig->commence(); resetHLConfig(); if (CONFIG_OPTIONS.size() != m_configValueNumber - 1 /* autogenerated is special */) - Log::logger->log(Log::DEBUG, "Warning: config descriptions have {} entries, but there are {} config values. This should fail tests!!", CONFIG_OPTIONS.size(), - m_configValueNumber); + Debug::log(LOG, "Warning: config descriptions have {} entries, but there are {} config values. This should fail tests!!", CONFIG_OPTIONS.size(), m_configValueNumber); - if (!g_pCompositor->m_onlyConfigVerification) { - Log::logger->log( - Log::DEBUG, + if (!g_pCompositor->m_bOnlyConfigVerification) { + Debug::log( + INFO, "!!!!HEY YOU, YES YOU!!!!: further logs to stdout / logfile are disabled by default. BEFORE SENDING THIS LOG, ENABLE THEM. Use debug:disable_logs = false to do so: " - "https://wiki.hypr.land/Configuring/Variables/#debug"); + "https://wiki.hyprland.org/Configuring/Variables/#debug"); } + Debug::disableLogs = reinterpret_cast(m_pConfig->getConfigValuePtr("debug:disable_logs")->getDataStaticPtr()); + Debug::disableTime = reinterpret_cast(m_pConfig->getConfigValuePtr("debug:disable_time")->getDataStaticPtr()); + if (g_pEventLoopManager && ERR.has_value()) g_pEventLoopManager->doLater([ERR] { g_pHyprError->queueCreate(ERR.value(), CHyprColor{1.0, 0.1, 0.1, 1.0}); }); } -void CConfigManager::reloadRuleConfigs() { - // FIXME: this should also remove old values if they are removed - - for (const auto& r : Desktop::Rule::allMatchPropStrings()) { - m_config->addSpecialConfigValue("windowrule", ("match:" + r).c_str(), Hyprlang::STRING{""}); - } - - for (const auto& r : Desktop::Rule::windowEffects()->allEffectStrings()) { - m_config->addSpecialConfigValue("windowrule", r.c_str(), Hyprlang::STRING{""}); - } - - for (const auto& r : Desktop::Rule::allMatchPropStrings()) { - m_config->addSpecialConfigValue("layerrule", ("match:" + r).c_str(), Hyprlang::STRING{""}); - } - - for (const auto& r : Desktop::Rule::layerEffects()->allEffectStrings()) { - m_config->addSpecialConfigValue("layerrule", r.c_str(), Hyprlang::STRING{""}); - } -} - -std::optional CConfigManager::generateConfig(std::string configPath, bool safeMode) { +std::optional CConfigManager::generateConfig(std::string configPath) { std::string parentPath = std::filesystem::path(configPath).parent_path(); - if (!parentPath.empty()) { - std::error_code ec; - bool created = std::filesystem::create_directories(parentPath, ec); - if (ec) { - Log::logger->log(Log::ERR, "Couldn't create config home directory ({}): {}", ec.message(), parentPath); - return "Config could not be generated."; - } - if (created) - Log::logger->log(Log::WARN, "Creating config home directory"); + if (!std::filesystem::is_directory(parentPath)) { + Debug::log(WARN, "Creating config home directory"); + try { + std::filesystem::create_directories(parentPath); + } catch (std::exception& e) { throw e; } } - Log::logger->log(Log::WARN, "No config file found; attempting to generate."); + Debug::log(WARN, "No config file found; attempting to generate."); std::ofstream ofs; ofs.open(configPath, std::ios::trunc); - if (!safeMode) { - ofs << AUTOGENERATED_PREFIX; - ofs << EXAMPLE_CONFIG; - } else { - std::string n = std::string{EXAMPLE_CONFIG}; - replaceInString(n, "\n$menu = hyprlauncher\n", "\n$menu = hyprland-run\n"); - ofs << n; - } + ofs << AUTOCONFIG; ofs.close(); - if (ofs.fail()) + if (!std::filesystem::exists(configPath)) return "Config could not be generated."; return configPath; } std::string CConfigManager::getMainConfigPath() { - static bool lastSafeMode = g_pCompositor->m_safeMode; - static auto getCfgPath = [this]() -> std::string { - lastSafeMode = g_pCompositor->m_safeMode; - m_firstExecDispatched = false; - - if (g_pCompositor->m_safeMode) { - const auto CONFIGPATH = g_pCompositor->m_instancePath + "/recoverycfg.conf"; - return generateConfig(CONFIGPATH, false).value(); - } - - if (!g_pCompositor->m_explicitConfigPath.empty()) - return g_pCompositor->m_explicitConfigPath; + static std::string CONFIG_PATH = [this]() -> std::string { + if (!g_pCompositor->explicitConfigPath.empty()) + return g_pCompositor->explicitConfigPath; if (const auto CFG_ENV = getenv("HYPRLAND_CONFIG"); CFG_ENV) return CFG_ENV; @@ -1012,13 +823,7 @@ std::string CConfigManager::getMainConfigPath() { return generateConfig(CONFIGPATH).value(); } else throw std::runtime_error("Neither HOME nor XDG_CONFIG_HOME are set in the environment. Could not find config in XDG_CONFIG_DIRS or /etc/xdg."); - }; - static std::string CONFIG_PATH = getCfgPath(); - - if (lastSafeMode != g_pCompositor->m_safeMode) { - CONFIG_PATH = getCfgPath(); - m_config->changeRootPath(CONFIG_PATH.c_str()); - } + }(); return CONFIG_PATH; } @@ -1040,7 +845,7 @@ std::string CConfigManager::getConfigString() { std::ifstream configFile(path); configString += ("\n\nConfig File: " + path + ": "); if (!configFile.is_open()) { - Log::logger->log(Log::DEBUG, "Config file not readable/found!"); + Debug::log(LOG, "Config file not readable/found!"); configString += "Read Failed\n"; continue; } @@ -1052,128 +857,98 @@ std::string CConfigManager::getConfigString() { } std::string CConfigManager::getErrors() { - return m_configErrors; -} - -static std::vector HL_VERSION_VARS = { - "HYPRLAND_V_0_53", -}; - -static void exportHlVersionVars() { - for (const auto& v : HL_VERSION_VARS) { - setenv(v, "1", 1); - } -} - -static void clearHlVersionVars() { - for (const auto& v : HL_VERSION_VARS) { - unsetenv(v); - } + return m_szConfigErrors; } void CConfigManager::reload() { - Event::bus()->m_events.config.preReload.emit(); + EMIT_HOOK_EVENT("preConfigReload", nullptr); setDefaultAnimationVars(); resetHLConfig(); - m_configCurrentPath = getMainConfigPath(); - - exportHlVersionVars(); - - const auto ERR = m_config->parse(); - - clearHlVersionVars(); - - const auto monitorError = handleMonitorv2(); - const auto ruleError = reloadRules(); - m_lastConfigVerificationWasSuccessful = !ERR.error && !monitorError.error; - postConfigReload(ERR.error || !monitorError.error ? ERR : monitorError); + configCurrentPath = getMainConfigPath(); + const auto ERR = m_pConfig->parse(); + m_bLastConfigVerificationWasSuccessful = !ERR.error; + postConfigReload(ERR); } std::string CConfigManager::verify() { setDefaultAnimationVars(); resetHLConfig(); - m_configCurrentPath = getMainConfigPath(); - const auto ERR = m_config->parse(); - m_lastConfigVerificationWasSuccessful = !ERR.error; + configCurrentPath = getMainConfigPath(); + const auto ERR = m_pConfig->parse(); + m_bLastConfigVerificationWasSuccessful = !ERR.error; if (ERR.error) return ERR.getError(); return "config ok"; } void CConfigManager::setDefaultAnimationVars() { - m_animationTree.createNode("__internal_fadeCTM"); - m_animationTree.createNode("global"); + m_AnimationTree.createNode("__internal_fadeCTM"); + m_AnimationTree.createNode("global"); // global - m_animationTree.createNode("windows", "global"); - m_animationTree.createNode("layers", "global"); - m_animationTree.createNode("fade", "global"); - m_animationTree.createNode("border", "global"); - m_animationTree.createNode("borderangle", "global"); - m_animationTree.createNode("workspaces", "global"); - m_animationTree.createNode("zoomFactor", "global"); - m_animationTree.createNode("monitorAdded", "global"); + m_AnimationTree.createNode("windows", "global"); + m_AnimationTree.createNode("layers", "global"); + m_AnimationTree.createNode("fade", "global"); + m_AnimationTree.createNode("border", "global"); + m_AnimationTree.createNode("borderangle", "global"); + m_AnimationTree.createNode("workspaces", "global"); // layer - m_animationTree.createNode("layersIn", "layers"); - m_animationTree.createNode("layersOut", "layers"); + m_AnimationTree.createNode("layersIn", "layers"); + m_AnimationTree.createNode("layersOut", "layers"); // windows - m_animationTree.createNode("windowsIn", "windows"); - m_animationTree.createNode("windowsOut", "windows"); - m_animationTree.createNode("windowsMove", "windows"); + m_AnimationTree.createNode("windowsIn", "windows"); + m_AnimationTree.createNode("windowsOut", "windows"); + m_AnimationTree.createNode("windowsMove", "windows"); // fade - m_animationTree.createNode("fadeIn", "fade"); - m_animationTree.createNode("fadeOut", "fade"); - m_animationTree.createNode("fadeSwitch", "fade"); - m_animationTree.createNode("fadeShadow", "fade"); - m_animationTree.createNode("fadeDim", "fade"); - m_animationTree.createNode("fadeLayers", "fade"); - m_animationTree.createNode("fadeLayersIn", "fadeLayers"); - m_animationTree.createNode("fadeLayersOut", "fadeLayers"); - m_animationTree.createNode("fadePopups", "fade"); - m_animationTree.createNode("fadePopupsIn", "fadePopups"); - m_animationTree.createNode("fadePopupsOut", "fadePopups"); - m_animationTree.createNode("fadeDpms", "fade"); + m_AnimationTree.createNode("fadeIn", "fade"); + m_AnimationTree.createNode("fadeOut", "fade"); + m_AnimationTree.createNode("fadeSwitch", "fade"); + m_AnimationTree.createNode("fadeShadow", "fade"); + m_AnimationTree.createNode("fadeDim", "fade"); + m_AnimationTree.createNode("fadeLayers", "fade"); + m_AnimationTree.createNode("fadeLayersIn", "fadeLayers"); + m_AnimationTree.createNode("fadeLayersOut", "fadeLayers"); // workspaces - m_animationTree.createNode("workspacesIn", "workspaces"); - m_animationTree.createNode("workspacesOut", "workspaces"); - m_animationTree.createNode("specialWorkspace", "workspaces"); - m_animationTree.createNode("specialWorkspaceIn", "specialWorkspace"); - m_animationTree.createNode("specialWorkspaceOut", "specialWorkspace"); + m_AnimationTree.createNode("workspacesIn", "workspaces"); + m_AnimationTree.createNode("workspacesOut", "workspaces"); + m_AnimationTree.createNode("specialWorkspace", "workspaces"); + m_AnimationTree.createNode("specialWorkspaceIn", "specialWorkspace"); + m_AnimationTree.createNode("specialWorkspaceOut", "specialWorkspace"); // init the root nodes - m_animationTree.setConfigForNode("global", 1, 8.f, "default"); - m_animationTree.setConfigForNode("__internal_fadeCTM", 1, 5.f, "linear"); - m_animationTree.setConfigForNode("borderangle", 0, 1, "default"); + m_AnimationTree.setConfigForNode("global", 1, 8.f, "default"); + m_AnimationTree.setConfigForNode("__internal_fadeCTM", 1, 5.f, "linear"); + m_AnimationTree.setConfigForNode("borderangle", 0, 1, "default"); } std::optional CConfigManager::resetHLConfig() { - m_monitorRules.clear(); + m_vMonitorRules.clear(); + m_vWindowRules.clear(); g_pKeybindManager->clearKeybinds(); g_pAnimationManager->removeAllBeziers(); g_pAnimationManager->addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0)); - g_pTrackpadGestures->clearGestures(); - m_workspaceRules.clear(); + m_mAdditionalReservedAreas.clear(); + m_dBlurLSNamespaces.clear(); + m_vWorkspaceRules.clear(); setDefaultAnimationVars(); // reset anims - m_declaredPlugins.clear(); - m_failedPluginConfigValues.clear(); - m_finalExecRequests.clear(); - m_keywordRules.clear(); + m_vDeclaredPlugins.clear(); + m_vLayerRules.clear(); + m_vFailedPluginConfigValues.clear(); + finalExecRequests.clear(); // paths m_configPaths.clear(); std::string mainConfigPath = getMainConfigPath(); - Log::logger->log(Log::DEBUG, "Using config: {}", mainConfigPath); + Debug::log(LOG, "Using config: {}", mainConfigPath); m_configPaths.emplace_back(mainConfigPath); const auto RET = verifyConfigExists(); - reloadRuleConfigs(); - return RET; } @@ -1182,220 +957,47 @@ void CConfigManager::updateWatcher() { g_pConfigWatcher->setWatchList(*PDISABLEAUTORELOAD ? std::vector{} : m_configPaths); } -std::optional CConfigManager::handleMonitorv2(const std::string& output) { - auto parser = CMonitorRuleParser(output); - auto VAL = m_config->getSpecialConfigValuePtr("monitorv2", "disabled", output.c_str()); - if (VAL && VAL->m_bSetByUser && std::any_cast(VAL->getValue())) - parser.setDisabled(); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "mode", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.parseMode(std::any_cast(VAL->getValue())); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "position", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.parsePosition(std::any_cast(VAL->getValue())); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "scale", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.parseScale(std::any_cast(VAL->getValue())); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "addreserved", output.c_str()); - if (VAL && VAL->m_bSetByUser) { - const auto ARGS = CVarList(std::any_cast(VAL->getValue())); - try { - // top, right, bottom, left - parser.setReserved({std::stoi(ARGS[0]), std::stoi(ARGS[3]), std::stoi(ARGS[1]), std::stoi(ARGS[2])}); - } catch (...) { return "parse error: invalid reserved area"; } - } - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "mirror", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.setMirror(std::any_cast(VAL->getValue())); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "bitdepth", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.parseBitdepth(std::any_cast(VAL->getValue())); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "cm", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.parseCM(std::any_cast(VAL->getValue())); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdr_eotf", output.c_str()); - if (VAL && VAL->m_bSetByUser) { - const std::string value = std::any_cast(VAL->getValue()); - // remap legacy - if (value == "0") - parser.rule().sdrEotf = NTransferFunction::TF_AUTO; - else if (value == "1") - parser.rule().sdrEotf = NTransferFunction::TF_SRGB; - else if (value == "2") - parser.rule().sdrEotf = NTransferFunction::TF_GAMMA22; - else - parser.rule().sdrEotf = NTransferFunction::fromString(value); - } - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdrbrightness", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().sdrBrightness = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdrsaturation", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().sdrSaturation = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "vrr", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().vrr = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "transform", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.parseTransform(std::any_cast(VAL->getValue())); - - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "supports_wide_color", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().supportsWideColor = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "supports_hdr", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().supportsHDR = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdr_min_luminance", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().sdrMinLuminance = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdr_max_luminance", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().sdrMaxLuminance = std::any_cast(VAL->getValue()); - - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "min_luminance", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().minLuminance = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "max_luminance", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().maxLuminance = std::any_cast(VAL->getValue()); - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "max_avg_luminance", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().maxAvgLuminance = std::any_cast(VAL->getValue()); - - VAL = m_config->getSpecialConfigValuePtr("monitorv2", "icc", output.c_str()); - if (VAL && VAL->m_bSetByUser) - parser.rule().iccFile = std::any_cast(VAL->getValue()); - - auto newrule = parser.rule(); - - std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; }); - - m_monitorRules.push_back(newrule); - - return parser.getError(); -} - -Hyprlang::CParseResult CConfigManager::handleMonitorv2() { - Hyprlang::CParseResult result; - for (const auto& output : m_config->listKeysForSpecialCategory("monitorv2")) { - const auto error = handleMonitorv2(output); - if (error.has_value()) { - result.setError(error.value().c_str()); - return result; - } - } - return result; -} - -std::optional CConfigManager::addRuleFromConfigKey(const std::string& name) { - const auto ENABLED = m_config->getSpecialConfigValuePtr("windowrule", "enable", name.c_str()); - if (ENABLED && ENABLED->m_bSetByUser && std::any_cast(ENABLED->getValue()) == 0) - return std::nullopt; - - SP rule = makeShared(name); - - for (const auto& r : Desktop::Rule::allMatchPropStrings()) { - auto VAL = m_config->getSpecialConfigValuePtr("windowrule", ("match:" + r).c_str(), name.c_str()); - if (VAL && VAL->m_bSetByUser) - rule->registerMatch(Desktop::Rule::matchPropFromString(r).value_or(Desktop::Rule::RULE_PROP_NONE), std::any_cast(VAL->getValue())); - } - - for (const auto& e : Desktop::Rule::windowEffects()->allEffectStrings()) { - auto VAL = m_config->getSpecialConfigValuePtr("windowrule", e.c_str(), name.c_str()); - if (VAL && VAL->m_bSetByUser) - rule->addEffect(Desktop::Rule::windowEffects()->get(e).value_or(Desktop::Rule::WINDOW_RULE_EFFECT_NONE), std::any_cast(VAL->getValue())); - } - - Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); - return std::nullopt; -} - -std::optional CConfigManager::addLayerRuleFromConfigKey(const std::string& name) { - - const auto ENABLED = m_config->getSpecialConfigValuePtr("layerrule", "enable", name.c_str()); - if (ENABLED && ENABLED->m_bSetByUser && std::any_cast(ENABLED->getValue()) != 0) - return std::nullopt; - - SP rule = makeShared(name); - - for (const auto& r : Desktop::Rule::allMatchPropStrings()) { - auto VAL = m_config->getSpecialConfigValuePtr("layerrule", ("match:" + r).c_str(), name.c_str()); - if (VAL && VAL->m_bSetByUser) - rule->registerMatch(Desktop::Rule::matchPropFromString(r).value_or(Desktop::Rule::RULE_PROP_NONE), std::any_cast(VAL->getValue())); - } - - for (const auto& e : Desktop::Rule::layerEffects()->allEffectStrings()) { - auto VAL = m_config->getSpecialConfigValuePtr("layerrule", e.c_str(), name.c_str()); - if (VAL && VAL->m_bSetByUser) - rule->addEffect(Desktop::Rule::layerEffects()->get(e).value_or(Desktop::Rule::LAYER_RULE_EFFECT_NONE), std::any_cast(VAL->getValue())); - } - - Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); - return std::nullopt; -} - -Hyprlang::CParseResult CConfigManager::reloadRules() { - Desktop::Rule::ruleEngine()->clearAllRules(); - - Hyprlang::CParseResult result; - for (const auto& name : m_config->listKeysForSpecialCategory("windowrule")) { - const auto error = addRuleFromConfigKey(name); - if (error.has_value()) - result.setError(error.value().c_str()); - } - for (const auto& name : m_config->listKeysForSpecialCategory("layerrule")) { - const auto error = addLayerRuleFromConfigKey(name); - if (error.has_value()) - result.setError(error.value().c_str()); - } - - for (auto& rule : m_keywordRules) { - Desktop::Rule::ruleEngine()->registerRule(SP{rule}); - } - - Desktop::Rule::ruleEngine()->updateAllRules(); - - return result; -} - void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { + static const auto PENABLEEXPLICIT = CConfigValue("render:explicit_sync"); + static int prevEnabledExplicit = *PENABLEEXPLICIT; + updateWatcher(); - for (auto const& w : g_pCompositor->m_windows) { + for (auto const& w : g_pCompositor->m_vWindows) { w->uncacheWindowDecos(); } - static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); - for (auto const& m : g_pCompositor->m_monitors) { - *(m->m_cursorZoom) = *PZOOMFACTOR; - if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(); - } + for (auto const& m : g_pCompositor->m_vMonitors) + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); // Update the keyboard layout to the cfg'd one if this is not the first launch - if (!m_isFirstLaunch) { + if (!isFirstLaunch) { g_pInputManager->setKeyboardLayout(); g_pInputManager->setPointerConfigs(); g_pInputManager->setTouchDeviceConfigs(); g_pInputManager->setTabletConfigs(); - g_pHyprOpenGL->m_reloadScreenShader = true; + g_pHyprOpenGL->m_bReloadScreenShader = true; + + g_pHyprOpenGL->ensureBackgroundTexturePresence(); } // parseError will be displayed next frame if (result.error) - m_configErrors = result.getError(); + m_szConfigErrors = result.getError(); else - m_configErrors = ""; + m_szConfigErrors = ""; - if (result.error && !std::any_cast(m_config->getConfigValue("debug:suppress_errors"))) + if (result.error && !std::any_cast(m_pConfig->getConfigValue("debug:suppress_errors"))) g_pHyprError->queueCreate(result.getError(), CHyprColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); - else if (std::any_cast(m_config->getConfigValue("autogenerated")) == 1) + else if (std::any_cast(m_pConfig->getConfigValue("autogenerated")) == 1) g_pHyprError->queueCreate( "Warning: You're using an autogenerated config! Edit the config file to get rid of this message. (config file: " + getMainConfigPath() + " )\nSUPER+Q -> kitty (if it doesn't launch, make sure it's installed or choose a different terminal in the config)\nSUPER+M -> exit Hyprland", CHyprColor(1.0, 1.0, 70.0 / 255.0, 1.0)); + else if (*PENABLEEXPLICIT != prevEnabledExplicit) + g_pHyprError->queueCreate("Warning: You changed the render:explicit_sync option, this requires you to restart Hyprland.", CHyprColor(0.9, 0.76, 0.221, 1.0)); else g_pHyprError->destroy(); @@ -1403,7 +1005,7 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { // not on first launch because monitors might not exist yet // and they'll be taken care of in the newMonitor event // ignore if nomonitorreload is set - if (!m_isFirstLaunch && !m_noMonitorReload) { + if (!isFirstLaunch && !m_bNoMonitorReload) { // check performMonitorReload(); ensureMonitorStatus(); @@ -1411,23 +1013,23 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { } #ifndef NO_XWAYLAND - const auto PENABLEXWAYLAND = std::any_cast(m_config->getConfigValue("xwayland:enabled")); - g_pCompositor->m_wantsXwayland = PENABLEXWAYLAND; + const auto PENABLEXWAYLAND = std::any_cast(m_pConfig->getConfigValue("xwayland:enabled")); + g_pCompositor->m_bWantsXwayland = PENABLEXWAYLAND; // enable/disable xwayland usage - if (!m_isFirstLaunch && + if (!isFirstLaunch && g_pXWayland /* XWayland has to be initialized by CCompositor::initManagers for this to make sense, and it doesn't have to be (e.g. very early plugin load) */) { bool prevEnabledXwayland = g_pXWayland->enabled(); - if (g_pCompositor->m_wantsXwayland != prevEnabledXwayland) - g_pXWayland = makeUnique(g_pCompositor->m_wantsXwayland); + if (g_pCompositor->m_bWantsXwayland != prevEnabledXwayland) + g_pXWayland = makeUnique(g_pCompositor->m_bWantsXwayland); } else - g_pCompositor->m_wantsXwayland = PENABLEXWAYLAND; + g_pCompositor->m_bWantsXwayland = PENABLEXWAYLAND; #endif - if (!m_isFirstLaunch && !g_pCompositor->m_unsafeState) + if (!isFirstLaunch && !g_pCompositor->m_bUnsafeState) refreshGroupBarGradients(); // Updates dynamic window and workspace rules - for (auto const& w : g_pCompositor->getWorkspaces()) { + for (auto const& w : g_pCompositor->m_vWorkspaces) { if (w->inert()) continue; w->updateWindows(); @@ -1437,48 +1039,50 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { // Update window border colors g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + // update layout + g_pLayoutManager->switchToLayout(std::any_cast(m_pConfig->getConfigValue("general:layout"))); + // manual crash - if (std::any_cast(m_config->getConfigValue("debug:manual_crash")) && !m_manualCrashInitiated) { - m_manualCrashInitiated = true; + if (std::any_cast(m_pConfig->getConfigValue("debug:manual_crash")) && !m_bManualCrashInitiated) { + m_bManualCrashInitiated = true; g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CHyprColor(0), 5000, ICON_INFO); - } else if (m_manualCrashInitiated && !std::any_cast(m_config->getConfigValue("debug:manual_crash"))) { + } else if (m_bManualCrashInitiated && !std::any_cast(m_pConfig->getConfigValue("debug:manual_crash"))) { // cowabunga it is g_pHyprRenderer->initiateManualCrash(); } - auto disableStdout = !std::any_cast(m_config->getConfigValue("debug:enable_stdout_logs")); - if (disableStdout && m_isFirstLaunch) - Log::logger->log(Log::DEBUG, "Disabling stdout logs! Check the log for further logs."); + Debug::disableStdout = !std::any_cast(m_pConfig->getConfigValue("debug:enable_stdout_logs")); + if (Debug::disableStdout && isFirstLaunch) + Debug::log(LOG, "Disabling stdout logs! Check the log for further logs."); - for (auto const& m : g_pCompositor->m_monitors) { + Debug::coloredLogs = reinterpret_cast(m_pConfig->getConfigValuePtr("debug:colored_stdout_logs")->getDataStaticPtr()); + + for (auto const& m : g_pCompositor->m_vMonitors) { // mark blur dirty g_pHyprOpenGL->markBlurDirtyForMonitor(m); g_pCompositor->scheduleFrameForMonitor(m); // Force the compositor to fully re-render all monitors - m->m_forceFullFrames = 2; + m->forceFullFrames = 2; // also force mirrors, as the aspect ratio could've changed - for (auto const& mirror : m->m_mirrors) - mirror->m_forceFullFrames = 3; + for (auto const& mirror : m->mirrors) + mirror->forceFullFrames = 3; } // Reset no monitor reload - m_noMonitorReload = false; + m_bNoMonitorReload = false; // update plugins handlePluginLoads(); // update persistent workspaces - if (!m_isFirstLaunch) + if (!isFirstLaunch) ensurePersistentWorkspacesPresent(); - // update layouts - Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts(); - - Event::bus()->m_events.config.reloaded.emit(); + EMIT_HOOK_EVENT("configReloaded", nullptr); if (g_pEventManager) g_pEventManager->postEvent(SHyprIPCEvent{"configreloaded", ""}); } @@ -1486,36 +1090,46 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { void CConfigManager::init() { g_pConfigWatcher->setOnChange([this](const CConfigWatcher::SConfigWatchEvent& e) { - Log::logger->log(Log::DEBUG, "CConfigManager: file {} modified, reloading", e.file); + Debug::log(LOG, "CConfigManager: file {} modified, reloading", e.file); reload(); }); + const std::string CONFIGPATH = getMainConfigPath(); reload(); - m_isFirstLaunch = false; + isFirstLaunch = false; } std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE) { - const auto RET = m_config->parseDynamic(COMMAND.c_str(), VALUE.c_str()); + static const auto PENABLEEXPLICIT = CConfigValue("render:explicit_sync"); + static int prevEnabledExplicit = *PENABLEEXPLICIT; + + const auto RET = m_pConfig->parseDynamic(COMMAND.c_str(), VALUE.c_str()); // invalidate layouts if they changed if (COMMAND == "monitor" || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) { - for (auto const& m : g_pCompositor->m_monitors) { - g_layoutManager->recalculateMonitor(m); - } + for (auto const& m : g_pCompositor->m_vMonitors) + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); + } + + if (COMMAND.contains("explicit")) { + if (*PENABLEEXPLICIT != prevEnabledExplicit) + g_pHyprError->queueCreate("Warning: You changed the render:explicit_sync option, this requires you to restart Hyprland.", CHyprColor(0.9, 0.76, 0.221, 1.0)); + else + g_pHyprError->destroy(); } // Update window border colors g_pCompositor->updateAllWindowsAnimatedDecorationValues(); // manual crash - if (std::any_cast(m_config->getConfigValue("debug:manual_crash")) && !m_manualCrashInitiated) { - m_manualCrashInitiated = true; + if (std::any_cast(m_pConfig->getConfigValue("debug:manual_crash")) && !m_bManualCrashInitiated) { + m_bManualCrashInitiated = true; if (g_pHyprNotificationOverlay) { g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CHyprColor(0), 5000, ICON_INFO); } - } else if (m_manualCrashInitiated && !std::any_cast(m_config->getConfigValue("debug:manual_crash"))) { + } else if (m_bManualCrashInitiated && !std::any_cast(m_pConfig->getConfigValue("debug:manual_crash"))) { // cowabunga it is g_pHyprRenderer->initiateManualCrash(); } @@ -1525,20 +1139,15 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std:: Hyprlang::CConfigValue* CConfigManager::getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback) { - const auto VAL = m_config->getSpecialConfigValuePtr("device", val.c_str(), dev.c_str()); + const auto VAL = m_pConfig->getSpecialConfigValuePtr("device", val.c_str(), dev.c_str()); - if ((!VAL || !VAL->m_bSetByUser) && !fallback.empty()) - return m_config->getConfigValuePtr(fallback.c_str()); + if ((!VAL || !VAL->m_bSetByUser) && !fallback.empty()) { + return m_pConfig->getConfigValuePtr(fallback.c_str()); + } return VAL; } -bool CConfigManager::deviceConfigExplicitlySet(const std::string& dev, const std::string& val) { - const auto VAL = m_config->getSpecialConfigValuePtr("device", val.c_str(), dev.c_str()); - - return VAL && VAL->m_bSetByUser; -} - int CConfigManager::getDeviceInt(const std::string& dev, const std::string& v, const std::string& fallback) { return std::any_cast(getConfigValueSafeDevice(dev, v, fallback)->getValue()); } @@ -1568,56 +1177,56 @@ SMonitorRule CConfigManager::getMonitorRuleFor(const PHLMONITOR PMONITOR) { if (!CONFIG) return rule; - Log::logger->log(Log::DEBUG, "CConfigManager::getMonitorRuleFor: found a wlr_output_manager override for {}", PMONITOR->m_name); + Debug::log(LOG, "CConfigManager::getMonitorRuleFor: found a wlr_output_manager override for {}", PMONITOR->szName); - Log::logger->log(Log::DEBUG, " > overriding enabled: {} -> {}", !rule.disabled, !CONFIG->enabled); + Debug::log(LOG, " > overriding enabled: {} -> {}", !rule.disabled, !CONFIG->enabled); rule.disabled = !CONFIG->enabled; if ((CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_MODE) || (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_CUSTOM_MODE)) { - Log::logger->log(Log::DEBUG, " > overriding mode: {:.0f}x{:.0f}@{:.2f}Hz -> {:.0f}x{:.0f}@{:.2f}Hz", rule.resolution.x, rule.resolution.y, rule.refreshRate, - CONFIG->resolution.x, CONFIG->resolution.y, CONFIG->refresh / 1000.F); + Debug::log(LOG, " > overriding mode: {:.0f}x{:.0f}@{:.2f}Hz -> {:.0f}x{:.0f}@{:.2f}Hz", rule.resolution.x, rule.resolution.y, rule.refreshRate, CONFIG->resolution.x, + CONFIG->resolution.y, CONFIG->refresh / 1000.F); rule.resolution = CONFIG->resolution; rule.refreshRate = CONFIG->refresh / 1000.F; } if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_POSITION) { - Log::logger->log(Log::DEBUG, " > overriding offset: {:.0f}, {:.0f} -> {:.0f}, {:.0f}", rule.offset.x, rule.offset.y, CONFIG->position.x, CONFIG->position.y); + Debug::log(LOG, " > overriding offset: {:.0f}, {:.0f} -> {:.0f}, {:.0f}", rule.offset.x, rule.offset.y, CONFIG->position.x, CONFIG->position.y); rule.offset = CONFIG->position; } if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_TRANSFORM) { - Log::logger->log(Log::DEBUG, " > overriding transform: {} -> {}", sc(rule.transform), sc(CONFIG->transform)); + Debug::log(LOG, " > overriding transform: {} -> {}", (uint8_t)rule.transform, (uint8_t)CONFIG->transform); rule.transform = CONFIG->transform; } if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_SCALE) { - Log::logger->log(Log::DEBUG, " > overriding scale: {} -> {}", sc(rule.scale), sc(CONFIG->scale)); + Debug::log(LOG, " > overriding scale: {} -> {}", (uint8_t)rule.scale, (uint8_t)CONFIG->scale); rule.scale = CONFIG->scale; } if (CONFIG->committedProperties & OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) { - Log::logger->log(Log::DEBUG, " > overriding vrr: {} -> {}", rule.vrr.value_or(0), CONFIG->adaptiveSync); - rule.vrr = sc(CONFIG->adaptiveSync); + Debug::log(LOG, " > overriding vrr: {} -> {}", rule.vrr.value_or(0), CONFIG->adaptiveSync); + rule.vrr = (int)CONFIG->adaptiveSync; } return rule; }; - for (auto const& r : m_monitorRules | std::views::reverse) { + for (auto const& r : m_vMonitorRules | std::views::reverse) { if (PMONITOR->matchesStaticSelector(r.name)) { return applyWlrOutputConfig(r); } } - Log::logger->log(Log::WARN, "No rule found for {}, trying to use the first.", PMONITOR->m_name); + Debug::log(WARN, "No rule found for {}, trying to use the first.", PMONITOR->szName); - for (auto const& r : m_monitorRules) { + for (auto const& r : m_vMonitorRules) { if (r.name.empty()) { return applyWlrOutputConfig(r); } } - Log::logger->log(Log::WARN, "No rules configured. Using the default hardcoded one."); + Debug::log(WARN, "No rules configured. Using the default hardcoded one."); return applyWlrOutputConfig(SMonitorRule{.autoDir = eAutoDirs::DIR_AUTO_RIGHT, .name = "", @@ -1628,7 +1237,7 @@ SMonitorRule CConfigManager::getMonitorRuleFor(const PHLMONITOR PMONITOR) { SWorkspaceRule CConfigManager::getWorkspaceRuleFor(PHLWORKSPACE pWorkspace) { SWorkspaceRule mergedRule{}; - for (auto const& rule : m_workspaceRules) { + for (auto const& rule : m_vWorkspaceRules) { if (!pWorkspace->matchesStaticSelector(rule.workspaceString)) continue; @@ -1658,8 +1267,6 @@ SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1, mergedRule.gapsIn = rule2.gapsIn; if (rule2.gapsOut.has_value()) mergedRule.gapsOut = rule2.gapsOut; - if (rule2.floatGaps) - mergedRule.floatGaps = rule2.floatGaps; if (rule2.borderSize.has_value()) mergedRule.borderSize = rule2.borderSize; if (rule2.noBorder.has_value()) @@ -1674,8 +1281,6 @@ SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1, mergedRule.onCreatedEmptyRunCmd = rule2.onCreatedEmptyRunCmd; if (rule2.defaultName.has_value()) mergedRule.defaultName = rule2.defaultName; - if (rule2.layout.has_value()) - mergedRule.layout = rule2.layout; if (!rule2.layoutopts.empty()) { for (const auto& layoutopt : rule2.layoutopts) { mergedRule.layoutopts[layoutopt.first] = layoutopt.second; @@ -1684,12 +1289,219 @@ SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1, return mergedRule; } +std::vector> CConfigManager::getMatchingRules(PHLWINDOW pWindow, bool dynamic, bool shadowExec) { + if (!valid(pWindow)) + return std::vector>(); + + // if the window is unmapped, don't process exec rules yet. + shadowExec = shadowExec || !pWindow->m_bIsMapped; + + std::vector> returns; + + Debug::log(LOG, "Searching for matching rules for {} (title: {})", pWindow->m_szClass, pWindow->m_szTitle); + + // since some rules will be applied later, we need to store some flags + bool hasFloating = pWindow->m_bIsFloating; + bool hasFullscreen = pWindow->isFullscreen(); + + // local tags for dynamic tag rule match + auto tags = pWindow->m_tags; + + for (auto const& rule : m_vWindowRules) { + // check if we have a matching rule + if (!rule->v2) { + try { + if (rule->szValue.starts_with("tag:") && !tags.isTagged(rule->szValue.substr(4))) + continue; + + if (rule->szValue.starts_with("title:") && !rule->rV1Regex.passes(pWindow->m_szTitle)) + continue; + + if (!rule->rV1Regex.passes(pWindow->m_szClass)) + continue; + + } catch (...) { + Debug::log(ERR, "Regex error at {}", rule->szValue); + continue; + } + } else { + try { + if (rule->bX11 != -1) { + if (pWindow->m_bIsX11 != rule->bX11) + continue; + } + + if (rule->bFloating != -1) { + if (hasFloating != rule->bFloating) + continue; + } + + if (rule->bFullscreen != -1) { + if (hasFullscreen != rule->bFullscreen) + continue; + } + + if (rule->bPinned != -1) { + if (pWindow->m_bPinned != rule->bPinned) + continue; + } + + if (rule->bFocus != -1) { + if (rule->bFocus != (g_pCompositor->m_pLastWindow.lock() == pWindow)) + continue; + } + + if (!rule->szFullscreenState.empty()) { + const auto ARGS = CVarList(rule->szFullscreenState, 2, ' '); + // + std::optional internalMode, clientMode; + + if (ARGS[0] == "*") + internalMode = std::nullopt; + else if (isNumber(ARGS[0])) + internalMode = (eFullscreenMode)std::stoi(ARGS[0]); + else + throw std::runtime_error("szFullscreenState internal mode not valid"); + + if (ARGS[1] == "*") + clientMode = std::nullopt; + else if (isNumber(ARGS[1])) + clientMode = (eFullscreenMode)std::stoi(ARGS[1]); + else + throw std::runtime_error("szFullscreenState client mode not valid"); + + if (internalMode.has_value() && pWindow->m_sFullscreenState.internal != internalMode) + continue; + + if (clientMode.has_value() && pWindow->m_sFullscreenState.client != clientMode) + continue; + } + + if (!rule->szOnWorkspace.empty()) { + const auto PWORKSPACE = pWindow->m_pWorkspace; + if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule->szOnWorkspace)) + continue; + } + + if (!rule->szContentType.empty()) { + try { + const auto contentType = NContentType::fromString(rule->szContentType); + if (pWindow->getContentType() != contentType) + continue; + } catch (std::exception& e) { Debug::log(ERR, "Rule \"content:{}\" failed with: {}", rule->szContentType, e.what()); } + } + + if (!rule->szWorkspace.empty()) { + const auto PWORKSPACE = pWindow->m_pWorkspace; + + if (!PWORKSPACE) + continue; + + if (rule->szWorkspace.starts_with("name:")) { + if (PWORKSPACE->m_szName != rule->szWorkspace.substr(5)) + continue; + } else { + // number + if (!isNumber(rule->szWorkspace)) + throw std::runtime_error("szWorkspace not name: or number"); + + const int64_t ID = std::stoll(rule->szWorkspace); + + if (PWORKSPACE->m_iID != ID) + continue; + } + } + + if (!rule->szTag.empty() && !tags.isTagged(rule->szTag)) + continue; + + if (!rule->szClass.empty() && !rule->rClass.passes(pWindow->m_szClass)) + continue; + + if (!rule->szTitle.empty() && !rule->rTitle.passes(pWindow->m_szTitle)) + continue; + + if (!rule->szInitialTitle.empty() && !rule->rInitialTitle.passes(pWindow->m_szInitialTitle)) + continue; + + if (!rule->szInitialClass.empty() && !rule->rInitialClass.passes(pWindow->m_szInitialClass)) + continue; + + } catch (std::exception& e) { + Debug::log(ERR, "Regex error at {} ({})", rule->szValue, e.what()); + continue; + } + } + + // applies. Read the rule and behave accordingly + Debug::log(LOG, "Window rule {} -> {} matched {}", rule->szRule, rule->szValue, pWindow); + + returns.emplace_back(rule); + + // apply tag with local tags + if (rule->ruleType == CWindowRule::RULE_TAG) { + CVarList vars{rule->szRule, 0, 's', true}; + if (vars.size() == 2 && vars[0] == "tag") + tags.applyTag(vars[1], true); + } + + if (dynamic) + continue; + + if (rule->szRule == "float") + hasFloating = true; + else if (rule->szRule == "fullscreen") + hasFullscreen = true; + } + + std::vector PIDs = {(uint64_t)pWindow->getPID()}; + while (getPPIDof(PIDs.back()) > 10) + PIDs.push_back(getPPIDof(PIDs.back())); + + bool anyExecFound = false; + + for (auto const& er : execRequestedRules) { + if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) { + returns.emplace_back(makeShared(er.szRule, "", false, true)); + anyExecFound = true; + } + } + + if (anyExecFound && !shadowExec) // remove exec rules to unclog searches in the future, why have the garbage here. + std::erase_if(execRequestedRules, [&](const SExecRequestedRule& other) { return std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == other.iPid; }); }); + + return returns; +} + +std::vector> CConfigManager::getMatchingRules(PHLLS pLS) { + std::vector> returns; + + if (!pLS->layerSurface || pLS->fadingOut) + return returns; + + for (auto const& lr : m_vLayerRules) { + if (lr->targetNamespace.starts_with("address:0x")) { + if (std::format("address:0x{:x}", (uintptr_t)pLS.get()) != lr->targetNamespace) + continue; + } else if (!lr->targetNamespaceRegex.passes(pLS->layerSurface->layerNamespace)) + continue; + + // hit + returns.emplace_back(lr); + } + + if (shouldBlurLS(pLS->layerSurface->layerNamespace)) + returns.emplace_back(makeShared(pLS->layerSurface->layerNamespace, "blur")); + + return returns; +} + void CConfigManager::dispatchExecOnce() { - if (m_firstExecDispatched || m_isFirstLaunch) + if (firstExecDispatched || isFirstLaunch) return; // update dbus env - if (g_pCompositor->m_aqBackend->hasSession()) + if (g_pCompositor->m_pAqBackend->hasSession()) handleRawExec("", #ifdef USES_SYSTEMD "systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash " @@ -1697,15 +1509,15 @@ void CConfigManager::dispatchExecOnce() { #endif "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS"); - m_firstExecDispatched = true; - m_isLaunchingExecOnce = true; + firstExecDispatched = true; + isLaunchingExecOnce = true; - for (auto const& c : m_firstExecRequests) { + for (auto const& c : firstExecRequests) { c.withRules ? handleExec("", c.exec) : handleRawExec("", c.exec); } - m_firstExecRequests.clear(); // free some kb of memory :P - m_isLaunchingExecOnce = false; + firstExecRequests.clear(); // free some kb of memory :P + isLaunchingExecOnce = false; // set input, fixes some certain issues g_pInputManager->setKeyboardLayout(); @@ -1714,39 +1526,33 @@ void CConfigManager::dispatchExecOnce() { g_pInputManager->setTabletConfigs(); // check for user's possible errors with their setup and notify them if needed - // this is additionally guarded because exiting safe mode will re-run this. - static bool once = true; - if (once) { - g_pCompositor->performUserChecks(); - once = false; - } + g_pCompositor->performUserChecks(); } void CConfigManager::dispatchExecShutdown() { - if (m_finalExecRequests.empty()) { - g_pCompositor->m_finalRequests = false; + if (finalExecRequests.empty()) { + g_pCompositor->m_bFinalRequests = false; return; } - g_pCompositor->m_finalRequests = true; + g_pCompositor->m_bFinalRequests = true; - for (auto const& c : m_finalExecRequests) { + for (auto const& c : finalExecRequests) { handleExecShutdown("", c); } - m_finalExecRequests.clear(); + finalExecRequests.clear(); // Actually exit now handleExecShutdown("", "hyprctl dispatch exit"); } void CConfigManager::performMonitorReload() { - handleMonitorv2(); bool overAgain = false; - for (auto const& m : g_pCompositor->m_realMonitors) { - if (!m->m_output || m->m_isUnsafeFallback) + for (auto const& m : g_pCompositor->m_vRealMonitors) { + if (!m->output || m->isUnsafeFallback) continue; auto rule = getMonitorRuleFor(m); @@ -1759,19 +1565,19 @@ void CConfigManager::performMonitorReload() { // ensure mirror m->setMirror(rule.mirrorOf); - g_pHyprRenderer->arrangeLayersForMonitor(m->m_id); + g_pHyprRenderer->arrangeLayersForMonitor(m->ID); } if (overAgain) performMonitorReload(); - m_wantsMonitorReload = false; + m_bWantsMonitorReload = false; - Event::bus()->m_events.monitor.layoutChanged.emit(); + EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr); } void* const* CConfigManager::getConfigValuePtr(const std::string& val) { - const auto VAL = m_config->getConfigValuePtr(val.c_str()); + const auto VAL = m_pConfig->getConfigValuePtr(val.c_str()); if (!VAL) return nullptr; return VAL->getDataStaticPtr(); @@ -1779,94 +1585,81 @@ void* const* CConfigManager::getConfigValuePtr(const std::string& val) { Hyprlang::CConfigValue* CConfigManager::getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat) { if (!specialCat.empty()) - return m_config->getSpecialConfigValuePtr(specialCat.c_str(), name.c_str(), nullptr); + return m_pConfig->getSpecialConfigValuePtr(specialCat.c_str(), name.c_str(), nullptr); - if (name.starts_with("plugin:")) - return m_config->getSpecialConfigValuePtr("plugin", name.substr(7).c_str(), nullptr); - - return m_config->getConfigValuePtr(name.c_str()); + return m_pConfig->getConfigValuePtr(name.c_str()); } bool CConfigManager::deviceConfigExists(const std::string& dev) { auto copy = dev; - std::ranges::replace(copy, ' ', '-'); + std::replace(copy.begin(), copy.end(), ' ', '-'); - return m_config->specialCategoryExistsForKey("device", copy.c_str()); + return m_pConfig->specialCategoryExistsForKey("device", copy.c_str()); +} + +bool CConfigManager::shouldBlurLS(const std::string& ns) { + for (auto const& bls : m_dBlurLSNamespaces) { + if (bls == ns) { + return true; + } + } + + return false; } void CConfigManager::ensureMonitorStatus() { - for (auto const& rm : g_pCompositor->m_realMonitors) { - if (!rm->m_output || rm->m_isUnsafeFallback) + for (auto const& rm : g_pCompositor->m_vRealMonitors) { + if (!rm->output || rm->isUnsafeFallback) continue; auto rule = getMonitorRuleFor(rm); - if (rule.disabled == rm->m_enabled) + if (rule.disabled == rm->m_bEnabled) rm->applyMonitorRule(&rule); } } void CConfigManager::ensureVRR(PHLMONITOR pMonitor) { - static auto PVRR = rc(getConfigValuePtr("misc:vrr")); + static auto PVRR = reinterpret_cast(getConfigValuePtr("misc:vrr")); static auto ensureVRRForDisplay = [&](PHLMONITOR m) -> void { - if (!m->m_output || m->m_createdByUser) + if (!m->output || m->createdByUser) return; - const auto USEVRR = m->m_activeMonitorRule.vrr.has_value() ? m->m_activeMonitorRule.vrr.value() : **PVRR; + const auto USEVRR = m->activeMonitorRule.vrr.has_value() ? m->activeMonitorRule.vrr.value() : **PVRR; if (USEVRR == 0) { - if (m->m_vrrActive) { - m->m_output->state->resetExplicitFences(); - m->m_output->state->setAdaptiveSync(false); + if (m->vrrActive) { + m->output->state->resetExplicitFences(); + m->output->state->setAdaptiveSync(false); - if (!m->m_state.commit()) - Log::logger->log(Log::ERR, "Couldn't commit output {} in ensureVRR -> false", m->m_output->name); + if (!m->state.commit()) + Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> false", m->output->name); } - m->m_vrrActive = false; + m->vrrActive = false; return; - } + } else if (USEVRR == 1) { + if (!m->vrrActive) { + m->output->state->resetExplicitFences(); + m->output->state->setAdaptiveSync(true); - const auto PWORKSPACE = m->m_activeWorkspace; - - if (USEVRR == 1) { - bool wantVRR = true; - if (PWORKSPACE && PWORKSPACE->m_hasFullscreenWindow && (PWORKSPACE->m_fullscreenMode & FSMODE_FULLSCREEN)) - wantVRR = !PWORKSPACE->getFullscreenWindow()->m_ruleApplicator->noVRR().valueOrDefault(); - - if (wantVRR) { - if (!m->m_vrrActive) { - m->m_output->state->resetExplicitFences(); - m->m_output->state->setAdaptiveSync(true); - - if (!m->m_state.test()) { - Log::logger->log(Log::DEBUG, "Pending output {} does not accept VRR.", m->m_output->name); - m->m_output->state->setAdaptiveSync(false); - } - - if (!m->m_state.commit()) - Log::logger->log(Log::ERR, "Couldn't commit output {} in ensureVRR -> true", m->m_output->name); + if (!m->state.test()) { + Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); + m->output->state->setAdaptiveSync(false); } - m->m_vrrActive = true; - } else { - if (m->m_vrrActive) { - m->m_output->state->resetExplicitFences(); - m->m_output->state->setAdaptiveSync(false); - if (!m->m_state.commit()) - Log::logger->log(Log::ERR, "Couldn't commit output {} in ensureVRR -> false", m->m_output->name); - } - m->m_vrrActive = false; + if (!m->state.commit()) + Debug::log(ERR, "Couldn't commit output {} in ensureVRR -> true", m->output->name); } + m->vrrActive = true; return; } else if (USEVRR == 2 || USEVRR == 3) { + const auto PWORKSPACE = m->activeWorkspace; + if (!PWORKSPACE) return; // ??? - bool wantVRR = PWORKSPACE->m_hasFullscreenWindow && (PWORKSPACE->m_fullscreenMode & FSMODE_FULLSCREEN); - if (wantVRR && PWORKSPACE->getFullscreenWindow()->m_ruleApplicator->noVRR().valueOrDefault()) - wantVRR = false; - + bool wantVRR = PWORKSPACE->m_bHasFullscreenWindow && (PWORKSPACE->m_efFullscreenMode & FSMODE_FULLSCREEN); if (wantVRR && USEVRR == 3) { const auto contentType = PWORKSPACE->getFullscreenWindow()->getContentType(); wantVRR = contentType == CONTENT_TYPE_GAME || contentType == CONTENT_TYPE_VIDEO; @@ -1874,20 +1667,20 @@ void CConfigManager::ensureVRR(PHLMONITOR pMonitor) { if (wantVRR) { /* fullscreen */ - m->m_vrrActive = true; + m->vrrActive = true; - if (!m->m_output->state->state().adaptiveSync) { - m->m_output->state->setAdaptiveSync(true); + if (!m->output->state->state().adaptiveSync) { + m->output->state->setAdaptiveSync(true); - if (!m->m_state.test()) { - Log::logger->log(Log::DEBUG, "Pending output {} does not accept VRR.", m->m_output->name); - m->m_output->state->setAdaptiveSync(false); + if (!m->state.test()) { + Debug::log(LOG, "Pending output {} does not accept VRR.", m->output->name); + m->output->state->setAdaptiveSync(false); } } } else { - m->m_vrrActive = false; + m->vrrActive = false; - m->m_output->state->setAdaptiveSync(false); + m->output->state->setAdaptiveSync(false); } } }; @@ -1897,13 +1690,13 @@ void CConfigManager::ensureVRR(PHLMONITOR pMonitor) { return; } - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { ensureVRRForDisplay(m); } } SP CConfigManager::getAnimationPropertyConfig(const std::string& name) { - return m_animationTree.getConfig(name); + return m_AnimationTree.getConfig(name); } void CConfigManager::addParseError(const std::string& err) { @@ -1912,14 +1705,14 @@ void CConfigManager::addParseError(const std::string& err) { PHLMONITOR CConfigManager::getBoundMonitorForWS(const std::string& wsname) { auto monitor = getBoundMonitorStringForWS(wsname); - if (monitor.starts_with("desc:")) - return g_pCompositor->getMonitorFromDesc(trim(monitor.substr(5))); + if (monitor.substr(0, 5) == "desc:") + return g_pCompositor->getMonitorFromDesc(monitor.substr(5)); else return g_pCompositor->getMonitorFromName(monitor); } std::string CConfigManager::getBoundMonitorStringForWS(const std::string& wsname) { - for (auto const& wr : m_workspaceRules) { + for (auto const& wr : m_vWorkspaceRules) { const auto WSNAME = wr.workspaceName.starts_with("name:") ? wr.workspaceName.substr(5) : wr.workspaceName; if (WSNAME == wsname) @@ -1930,7 +1723,11 @@ std::string CConfigManager::getBoundMonitorStringForWS(const std::string& wsname } const std::vector& CConfigManager::getAllWorkspaceRules() { - return m_workspaceRules; + return m_vWorkspaceRules; +} + +void CConfigManager::addExecRule(const SExecRequestedRule& rule) { + execRequestedRules.push_back(rule); } void CConfigManager::handlePluginLoads() { @@ -1938,7 +1735,18 @@ void CConfigManager::handlePluginLoads() { return; bool pluginsChanged = false; - g_pPluginSystem->updateConfigPlugins(m_declaredPlugins, pluginsChanged); + auto failedPlugins = g_pPluginSystem->updateConfigPlugins(m_vDeclaredPlugins, pluginsChanged); + + if (!failedPlugins.empty()) { + std::stringstream error; + error << "Failed to load the following plugins:"; + + for (const auto& path : failedPlugins) { + error << "\n" << path; + } + + g_pHyprError->queueCreate(error.str(), CHyprColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); + } if (pluginsChanged) { g_pHyprError->destroy(); @@ -1947,7 +1755,7 @@ void CConfigManager::handlePluginLoads() { } const std::unordered_map>& CConfigManager::getAnimationConfig() { - return m_animationTree.getFullConfig(); + return m_AnimationTree.getFullConfig(); } void CConfigManager::addPluginConfigVar(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value) { @@ -1956,41 +1764,41 @@ void CConfigManager::addPluginConfigVar(HANDLE handle, const std::string& name, std::string field = name.substr(7); - m_config->addSpecialConfigValue("plugin", field.c_str(), value); - m_pluginVariables.push_back({handle, field}); + m_pConfig->addSpecialConfigValue("plugin", field.c_str(), value); + pluginVariables.push_back({handle, field}); } void CConfigManager::addPluginKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fn, Hyprlang::SHandlerOptions opts) { - m_pluginKeywords.emplace_back(SPluginKeyword{handle, name, fn}); - m_config->registerHandler(fn, name.c_str(), opts); + pluginKeywords.emplace_back(SPluginKeyword{handle, name, fn}); + m_pConfig->registerHandler(fn, name.c_str(), opts); } void CConfigManager::removePluginConfig(HANDLE handle) { - for (auto const& k : m_pluginKeywords) { + for (auto const& k : pluginKeywords) { if (k.handle != handle) continue; - m_config->unregisterHandler(k.name.c_str()); + m_pConfig->unregisterHandler(k.name.c_str()); } - std::erase_if(m_pluginKeywords, [&](const auto& other) { return other.handle == handle; }); - for (auto const& [h, n] : m_pluginVariables) { + std::erase_if(pluginKeywords, [&](const auto& other) { return other.handle == handle; }); + for (auto const& [h, n] : pluginVariables) { if (h != handle) continue; - m_config->removeSpecialConfigValue("plugin", n.c_str()); + m_pConfig->removeSpecialConfigValue("plugin", n.c_str()); } - std::erase_if(m_pluginVariables, [handle](const auto& other) { return other.handle == handle; }); + std::erase_if(pluginVariables, [handle](const auto& other) { return other.handle == handle; }); } std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) { - for (auto other = m_workspaceRules.begin(); other != m_workspaceRules.end(); ++other) { + for (auto other = m_vWorkspaceRules.begin(); other != m_vWorkspaceRules.end(); ++other) { if (other->isDefault) { if (other->monitor == name) return other->workspaceString; - if (other->monitor.starts_with("desc:")) { - auto const monitor = g_pCompositor->getMonitorFromDesc(trim(other->monitor.substr(5))); - if (monitor && monitor->m_name == name) + if (other->monitor.substr(0, 5) == "desc:") { + auto const monitor = g_pCompositor->getMonitorFromDesc(other->monitor.substr(5)); + if (monitor && monitor->szName == name) return other->workspaceString; } } @@ -1999,8 +1807,8 @@ std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) { } std::optional CConfigManager::handleRawExec(const std::string& command, const std::string& args) { - if (m_isFirstLaunch) { - m_firstExecRequests.push_back({args, false}); + if (isFirstLaunch) { + firstExecRequests.push_back({args, false}); return {}; } @@ -2009,8 +1817,8 @@ std::optional CConfigManager::handleRawExec(const std::string& comm } std::optional CConfigManager::handleExec(const std::string& command, const std::string& args) { - if (m_isFirstLaunch) { - m_firstExecRequests.push_back({args, true}); + if (isFirstLaunch) { + firstExecRequests.push_back({args, true}); return {}; } @@ -2019,26 +1827,26 @@ std::optional CConfigManager::handleExec(const std::string& command } std::optional CConfigManager::handleExecOnce(const std::string& command, const std::string& args) { - if (m_isFirstLaunch) - m_firstExecRequests.push_back({args, true}); + if (isFirstLaunch) + firstExecRequests.push_back({args, true}); return {}; } std::optional CConfigManager::handleExecRawOnce(const std::string& command, const std::string& args) { - if (m_isFirstLaunch) - m_firstExecRequests.push_back({args, false}); + if (isFirstLaunch) + firstExecRequests.push_back({args, false}); return {}; } std::optional CConfigManager::handleExecShutdown(const std::string& command, const std::string& args) { - if (g_pCompositor->m_finalRequests) { + if (g_pCompositor->m_bFinalRequests) { g_pKeybindManager->spawn(args); return {}; } - m_finalExecRequests.push_back(args); + finalExecRequests.push_back(args); return {}; } @@ -2046,34 +1854,29 @@ static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { auto args = CVarList(modeline, 0, 's'); auto keyword = args[0]; - std::ranges::transform(keyword, keyword.begin(), ::tolower); + std::transform(keyword.begin(), keyword.end(), keyword.begin(), ::tolower); if (keyword != "modeline") return false; if (args.size() < 10) { - Log::logger->log(Log::ERR, "modeline parse error: expected at least 9 arguments, got {}", args.size() - 1); + Debug::log(ERR, "modeline parse error: expected at least 9 arguments, got {}", args.size() - 1); return false; } int argno = 1; - try { - mode.type = DRM_MODE_TYPE_USERDEF; - mode.clock = std::stof(args[argno++]) * 1000; - mode.hdisplay = std::stoi(args[argno++]); - mode.hsync_start = std::stoi(args[argno++]); - mode.hsync_end = std::stoi(args[argno++]); - mode.htotal = std::stoi(args[argno++]); - mode.vdisplay = std::stoi(args[argno++]); - mode.vsync_start = std::stoi(args[argno++]); - mode.vsync_end = std::stoi(args[argno++]); - mode.vtotal = std::stoi(args[argno++]); - mode.vrefresh = mode.clock * 1000.0 * 1000.0 / mode.htotal / mode.vtotal; - } catch (const std::exception& e) { - Log::logger->log(Log::ERR, "modeline parse error: invalid numeric value: {}", e.what()); - return false; - } + mode.type = DRM_MODE_TYPE_USERDEF; + mode.clock = std::stof(args[argno++]) * 1000; + mode.hdisplay = std::stoi(args[argno++]); + mode.hsync_start = std::stoi(args[argno++]); + mode.hsync_end = std::stoi(args[argno++]); + mode.htotal = std::stoi(args[argno++]); + mode.vdisplay = std::stoi(args[argno++]); + mode.vsync_start = std::stoi(args[argno++]); + mode.vsync_end = std::stoi(args[argno++]); + mode.vtotal = std::stoi(args[argno++]); + mode.vrefresh = mode.clock * 1000.0 * 1000.0 / mode.htotal / mode.vtotal; // clang-format off static std::unordered_map flagsmap = { @@ -2085,16 +1888,16 @@ static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { }; // clang-format on - for (; argno < sc(args.size()); argno++) { + for (; argno < static_cast(args.size()); argno++) { auto key = args[argno]; - std::ranges::transform(key, key.begin(), ::tolower); + std::transform(key.begin(), key.end(), key.begin(), ::tolower); auto it = flagsmap.find(key); if (it != flagsmap.end()) mode.flags |= it->second; else - Log::logger->log(Log::ERR, "Invalid flag {} in modeline", key); + Debug::log(ERR, "Invalid flag {} in modeline", key); } snprintf(mode.name, sizeof(mode.name), "%dx%d@%d", mode.hdisplay, mode.vdisplay, mode.vrefresh / 1000); @@ -2102,228 +1905,30 @@ static bool parseModeLine(const std::string& modeline, drmModeModeInfo& mode) { return true; } -CMonitorRuleParser::CMonitorRuleParser(const std::string& name) { - m_rule.name = name; -} - -const std::string& CMonitorRuleParser::name() { - return m_rule.name; -} - -SMonitorRule& CMonitorRuleParser::rule() { - return m_rule; -} - -std::optional CMonitorRuleParser::getError() { - if (m_error.empty()) - return {}; - return m_error; -} - -bool CMonitorRuleParser::parseMode(const std::string& value) { - if (value.starts_with("pref")) - m_rule.resolution = Vector2D(); - else if (value.starts_with("highrr")) - m_rule.resolution = Vector2D(-1, -1); - else if (value.starts_with("highres")) - m_rule.resolution = Vector2D(-1, -2); - else if (value.starts_with("maxwidth")) - m_rule.resolution = Vector2D(-1, -3); - else if (parseModeLine(value, m_rule.drmMode)) { - m_rule.resolution = Vector2D(m_rule.drmMode.hdisplay, m_rule.drmMode.vdisplay); - m_rule.refreshRate = sc(m_rule.drmMode.vrefresh) / 1000; - } else { - - if (!value.contains("x")) { - m_error += "invalid resolution "; - m_rule.resolution = Vector2D(); - return false; - } else { - try { - m_rule.resolution.x = stoi(value.substr(0, value.find_first_of('x'))); - m_rule.resolution.y = stoi(value.substr(value.find_first_of('x') + 1, value.find_first_of('@'))); - - if (value.contains("@")) - m_rule.refreshRate = stof(value.substr(value.find_first_of('@') + 1)); - } catch (...) { - m_error += "invalid resolution "; - m_rule.resolution = Vector2D(); - return false; - } - } - } - return true; -} - -bool CMonitorRuleParser::parsePosition(const std::string& value, bool isFirst) { - if (value.starts_with("auto")) { - m_rule.offset = Vector2D(-INT32_MAX, -INT32_MAX); - // If this is the first monitor rule needs to be on the right. - if (value == "auto-right" || value == "auto" || isFirst) - m_rule.autoDir = eAutoDirs::DIR_AUTO_RIGHT; - else if (value == "auto-left") - m_rule.autoDir = eAutoDirs::DIR_AUTO_LEFT; - else if (value == "auto-up") - m_rule.autoDir = eAutoDirs::DIR_AUTO_UP; - else if (value == "auto-down") - m_rule.autoDir = eAutoDirs::DIR_AUTO_DOWN; - else if (value == "auto-center-right") - m_rule.autoDir = eAutoDirs::DIR_AUTO_CENTER_RIGHT; - else if (value == "auto-center-left") - m_rule.autoDir = eAutoDirs::DIR_AUTO_CENTER_LEFT; - else if (value == "auto-center-up") - m_rule.autoDir = eAutoDirs::DIR_AUTO_CENTER_UP; - else if (value == "auto-center-down") - m_rule.autoDir = eAutoDirs::DIR_AUTO_CENTER_DOWN; - else { - Log::logger->log(Log::WARN, - "Invalid auto direction. Valid options are 'auto'," - "'auto-up', 'auto-down', 'auto-left', 'auto-right'," - "'auto-center-up', 'auto-center-down'," - "'auto-center-left', and 'auto-center-right'."); - m_error += "invalid auto direction "; - return false; - } - } else { - if (!value.contains("x")) { - m_error += "invalid offset "; - m_rule.offset = Vector2D(-INT32_MAX, -INT32_MAX); - return false; - } else { - try { - m_rule.offset.x = stoi(value.substr(0, value.find_first_of('x'))); - m_rule.offset.y = stoi(value.substr(value.find_first_of('x') + 1)); - } catch (...) { - m_error += "invalid offset "; - m_rule.offset = Vector2D(-INT32_MAX, -INT32_MAX); - return false; - } - } - } - return true; -} - -bool CMonitorRuleParser::parseScale(const std::string& value) { - if (value.starts_with("auto")) - m_rule.scale = -1; - else { - if (!isNumber(value, true)) { - m_error += "invalid scale "; - return false; - } else { - m_rule.scale = stof(value); - - if (m_rule.scale < 0.25f) { - m_error += "invalid scale "; - m_rule.scale = 1; - return false; - } - } - } - return true; -} - -bool CMonitorRuleParser::parseTransform(const std::string& value) { - if (!isNumber(value)) { - m_error += "invalid transform "; - return false; - } - - const auto TSF = std::stoi(value); - if (std::clamp(TSF, 0, 7) != TSF) { - Log::logger->log(Log::ERR, "Invalid transform {} in monitor", TSF); - m_error += "invalid transform "; - return false; - } - m_rule.transform = sc(TSF); - return true; -} - -bool CMonitorRuleParser::parseBitdepth(const std::string& value) { - m_rule.enable10bit = value == "10"; - return true; -} - -bool CMonitorRuleParser::parseCM(const std::string& value) { - auto parsedCM = NCMType::fromString(value); - if (!parsedCM.has_value()) { - m_error += "invalid cm "; - return false; - } - m_rule.cmType = parsedCM.value(); - return true; -} - -bool CMonitorRuleParser::parseSDRBrightness(const std::string& value) { - try { - m_rule.sdrBrightness = stof(value); - } catch (...) { - m_error += "invalid sdrbrightness "; - return false; - } - return true; -} - -bool CMonitorRuleParser::parseSDRSaturation(const std::string& value) { - try { - m_rule.sdrSaturation = stof(value); - } catch (...) { - m_error += "invalid sdrsaturation "; - return false; - } - return true; -} - -bool CMonitorRuleParser::parseVRR(const std::string& value) { - if (!isNumber(value)) { - m_error += "invalid vrr "; - return false; - } - - m_rule.vrr = std::stoi(value); - return true; -} - -bool CMonitorRuleParser::parseICC(const std::string& val) { - if (val.empty()) { - m_error += "invalid icc "; - return false; - } - m_rule.iccFile = val; - return true; -} - -void CMonitorRuleParser::setDisabled() { - m_rule.disabled = true; -} - -void CMonitorRuleParser::setMirror(const std::string& value) { - m_rule.mirrorOf = value; -} - -bool CMonitorRuleParser::setReserved(const Desktop::CReservedArea& value) { - m_rule.reservedArea = value; - return true; -} - std::optional CConfigManager::handleMonitor(const std::string& command, const std::string& args) { - // get the monitor config - const auto ARGS = CVarList2(std::string(args)); - auto parser = CMonitorRuleParser(std::string(ARGS[0])); + // get the monitor config + SMonitorRule newrule; + + const auto ARGS = CVarList(args); + + newrule.name = ARGS[0]; if (ARGS[1] == "disable" || ARGS[1] == "disabled" || ARGS[1] == "addreserved" || ARGS[1] == "transform") { if (ARGS[1] == "disable" || ARGS[1] == "disabled") - parser.setDisabled(); + newrule.disabled = true; else if (ARGS[1] == "transform") { - if (!parser.parseTransform(std::string(ARGS[2]))) - return parser.getError(); + const auto TSF = std::stoi(ARGS[2]); + if (std::clamp(TSF, 0, 7) != TSF) { + Debug::log(ERR, "Invalid transform {} in monitor", TSF); + return "invalid transform"; + } - const auto TRANSFORM = parser.rule().transform; + const auto TRANSFORM = (wl_output_transform)TSF; // overwrite if exists - for (auto& r : m_monitorRules) { - if (r.name == parser.name()) { + for (auto& r : m_vMonitorRules) { + if (r.name == newrule.name) { r.transform = TRANSFORM; return {}; } @@ -2331,91 +1936,188 @@ std::optional CConfigManager::handleMonitor(const std::string& comm return {}; } else if (ARGS[1] == "addreserved") { - std::optional area; - try { - // top, right, bottom, left - area = {std::stoi(std::string{ARGS[2]}), std::stoi(std::string{ARGS[5]}), std::stoi(std::string{ARGS[3]}), std::stoi(std::string{ARGS[4]})}; - } catch (...) { return "parse error: invalid reserved area"; } + int top = std::stoi(ARGS[2]); - if (!area.has_value()) - return "parse error: bad addreserved"; + int bottom = std::stoi(ARGS[3]); - auto rule = std::ranges::find_if(m_monitorRules, [n = ARGS[0]](const auto& other) { return other.name == n; }); - if (rule != m_monitorRules.end()) { - rule->reservedArea = area.value(); - return {}; - } + int left = std::stoi(ARGS[4]); - // fall + int right = std::stoi(ARGS[5]); + + m_mAdditionalReservedAreas[newrule.name] = {top, bottom, left, right}; + + return {}; } else { - Log::logger->log(Log::ERR, "ConfigManager parseMonitor, curitem bogus???"); + Debug::log(ERR, "ConfigManager parseMonitor, curitem bogus???"); return "parse error: curitem bogus"; } - std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == parser.name(); }); + std::erase_if(m_vMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); - m_monitorRules.push_back(parser.rule()); + m_vMonitorRules.push_back(newrule); return {}; } - parser.parseMode(std::string(ARGS[1])); - parser.parsePosition(std::string(ARGS[2])); - parser.parseScale(std::string(ARGS[3])); + std::string error = ""; + + if (ARGS[1].starts_with("pref")) { + newrule.resolution = Vector2D(); + } else if (ARGS[1].starts_with("highrr")) { + newrule.resolution = Vector2D(-1, -1); + } else if (ARGS[1].starts_with("highres")) { + newrule.resolution = Vector2D(-1, -2); + } else if (parseModeLine(ARGS[1], newrule.drmMode)) { + newrule.resolution = Vector2D(newrule.drmMode.hdisplay, newrule.drmMode.vdisplay); + newrule.refreshRate = float(newrule.drmMode.vrefresh) / 1000; + } else { + + if (!ARGS[1].contains("x")) { + error += "invalid resolution "; + newrule.resolution = Vector2D(); + } else { + try { + newrule.resolution.x = stoi(ARGS[1].substr(0, ARGS[1].find_first_of('x'))); + newrule.resolution.y = stoi(ARGS[1].substr(ARGS[1].find_first_of('x') + 1, ARGS[1].find_first_of('@'))); + + if (ARGS[1].contains("@")) + newrule.refreshRate = stof(ARGS[1].substr(ARGS[1].find_first_of('@') + 1)); + } catch (...) { + error += "invalid resolution "; + newrule.resolution = Vector2D(); + } + } + } + + if (ARGS[2].starts_with("auto")) { + newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX); + // If this is the first monitor rule needs to be on the right. + if (ARGS[2] == "auto-right" || ARGS[2] == "auto" || m_vMonitorRules.empty()) + newrule.autoDir = eAutoDirs::DIR_AUTO_RIGHT; + else if (ARGS[2] == "auto-left") + newrule.autoDir = eAutoDirs::DIR_AUTO_LEFT; + else if (ARGS[2] == "auto-up") + newrule.autoDir = eAutoDirs::DIR_AUTO_UP; + else if (ARGS[2] == "auto-down") + newrule.autoDir = eAutoDirs::DIR_AUTO_DOWN; + else { + Debug::log(WARN, + "Invalid auto direction. Valid options are 'auto'," + "'auto-up', 'auto-down', 'auto-left', and 'auto-right'."); + error += "invalid auto direction "; + } + } else { + if (!ARGS[2].contains("x")) { + error += "invalid offset "; + newrule.offset = Vector2D(-INT32_MAX, -INT32_MAX); + } else { + newrule.offset.x = stoi(ARGS[2].substr(0, ARGS[2].find_first_of('x'))); + newrule.offset.y = stoi(ARGS[2].substr(ARGS[2].find_first_of('x') + 1)); + } + } + + if (ARGS[3].starts_with("auto")) { + newrule.scale = -1; + } else { + if (!isNumber(ARGS[3], true)) + error += "invalid scale "; + else { + newrule.scale = stof(ARGS[3]); + + if (newrule.scale < 0.25f) { + error += "invalid scale "; + newrule.scale = 1; + } + } + } int argno = 4; - while (!ARGS[argno].empty()) { + while (ARGS[argno] != "") { if (ARGS[argno] == "mirror") { - parser.setMirror(std::string(ARGS[argno + 1])); + newrule.mirrorOf = ARGS[argno + 1]; argno++; } else if (ARGS[argno] == "bitdepth") { - parser.parseBitdepth(std::string(ARGS[argno + 1])); + newrule.enable10bit = ARGS[argno + 1] == "10"; argno++; } else if (ARGS[argno] == "cm") { - parser.parseCM(std::string(ARGS[argno + 1])); + if (ARGS[argno + 1] == "auto") + newrule.cmType = CM_AUTO; + else if (ARGS[argno + 1] == "srgb") + newrule.cmType = CM_SRGB; + else if (ARGS[argno + 1] == "wide") + newrule.cmType = CM_WIDE; + else if (ARGS[argno + 1] == "edid") + newrule.cmType = CM_EDID; + else if (ARGS[argno + 1] == "hdr") + newrule.cmType = CM_HDR; + else if (ARGS[argno + 1] == "hdredid") + newrule.cmType = CM_HDR_EDID; + else + error = "invalid cm "; argno++; } else if (ARGS[argno] == "sdrsaturation") { - parser.parseSDRSaturation(std::string(ARGS[argno + 1])); + try { + newrule.sdrSaturation = stof(ARGS[argno + 1]); + } catch (...) { error = "invalid sdrsaturation "; } argno++; } else if (ARGS[argno] == "sdrbrightness") { - parser.parseSDRBrightness(std::string(ARGS[argno + 1])); + try { + newrule.sdrBrightness = stof(ARGS[argno + 1]); + } catch (...) { error = "invalid sdrbrightness "; } argno++; } else if (ARGS[argno] == "transform") { - parser.parseTransform(std::string(ARGS[argno + 1])); + if (!isNumber(ARGS[argno + 1])) { + error = "invalid transform "; + argno++; + continue; + } + + const auto NUM = std::stoi(ARGS[argno + 1]); + + if (NUM < 0 || NUM > 7) { + error = "invalid transform "; + argno++; + continue; + } + + newrule.transform = (wl_output_transform)std::stoi(ARGS[argno + 1]); argno++; } else if (ARGS[argno] == "vrr") { - parser.parseVRR(std::string(ARGS[argno + 1])); - argno++; - } else if (ARGS[argno] == "icc") { - parser.parseICC(std::string(ARGS[argno + 1])); + if (!isNumber(ARGS[argno + 1])) { + error = "invalid vrr "; + argno++; + continue; + } + + newrule.vrr = std::stoi(ARGS[argno + 1]); argno++; } else if (ARGS[argno] == "workspace") { - const auto& [id, name, isAutoID] = getWorkspaceIDNameFromString(std::string(ARGS[argno + 1])); + const auto& [id, name] = getWorkspaceIDNameFromString(ARGS[argno + 1]); SWorkspaceRule wsRule; - wsRule.monitor = parser.name(); + wsRule.monitor = newrule.name; wsRule.workspaceString = ARGS[argno + 1]; - wsRule.workspaceId = isAutoID ? WORKSPACE_INVALID : id; + wsRule.workspaceId = id; wsRule.workspaceName = name; - m_workspaceRules.emplace_back(wsRule); + m_vWorkspaceRules.emplace_back(wsRule); argno++; } else { - Log::logger->log(Log::ERR, "Config error: invalid monitor syntax at \"{}\"", ARGS[argno]); - return "invalid syntax at \"" + std::string(ARGS[argno]) + "\""; + Debug::log(ERR, "Config error: invalid monitor syntax at \"{}\"", ARGS[argno]); + return "invalid syntax at \"" + ARGS[argno] + "\""; } argno++; } - auto newrule = parser.rule(); + std::erase_if(m_vMonitorRules, [&](const auto& other) { return other.name == newrule.name; }); - std::erase_if(m_monitorRules, [&](const auto& other) { return other.name == newrule.name; }); + m_vMonitorRules.push_back(newrule); - m_monitorRules.push_back(newrule); - - return parser.getError(); + if (error.empty()) + return {}; + return error; } std::optional CConfigManager::handleBezier(const std::string& command, const std::string& args) { @@ -2423,31 +2125,23 @@ std::optional CConfigManager::handleBezier(const std::string& comma std::string bezierName = ARGS[0]; - if (ARGS[1].empty()) + if (ARGS[1] == "") return "too few arguments"; - else if (!isNumber(ARGS[1], true)) - return "invalid point"; float p1x = std::stof(ARGS[1]); - if (ARGS[2].empty()) + if (ARGS[2] == "") return "too few arguments"; - else if (!isNumber(ARGS[2], true)) - return "invalid point"; float p1y = std::stof(ARGS[2]); - if (ARGS[3].empty()) + if (ARGS[3] == "") return "too few arguments"; - else if (!isNumber(ARGS[3], true)) - return "invalid point"; float p2x = std::stof(ARGS[3]); - if (ARGS[4].empty()) + if (ARGS[4] == "") return "too few arguments"; - else if (!isNumber(ARGS[4], true)) - return "invalid point"; float p2y = std::stof(ARGS[4]); - if (!ARGS[5].empty()) + if (ARGS[5] != "") return "too many arguments"; g_pAnimationManager->addBezierWithName(bezierName, Vector2D(p1x, p1y), Vector2D(p2x, p2y)); @@ -2463,7 +2157,7 @@ std::optional CConfigManager::handleAnimation(const std::string& co // anim name const auto ANIMNAME = ARGS[0]; - if (!m_animationTree.nodeExists(ANIMNAME)) + if (!m_AnimationTree.nodeExists(ANIMNAME)) return "no such animation"; // This helper casts strings like "1", "true", "off", "yes"... to int. @@ -2474,7 +2168,7 @@ std::optional CConfigManager::handleAnimation(const std::string& co return "invalid animation on/off state"; if (!enabledInt) { - m_animationTree.setConfigForNode(ANIMNAME, enabledInt, 1, "default"); + m_AnimationTree.setConfigForNode(ANIMNAME, enabledInt, 1, "default"); return {}; } @@ -2494,18 +2188,18 @@ std::optional CConfigManager::handleAnimation(const std::string& co } std::string bezierName = ARGS[3]; - m_animationTree.setConfigForNode(ANIMNAME, enabledInt, speed, ARGS[3], ARGS[4]); + m_AnimationTree.setConfigForNode(ANIMNAME, enabledInt, speed, ARGS[3], ARGS[4]); if (!g_pAnimationManager->bezierExists(bezierName)) { - const auto PANIMNODE = m_animationTree.getConfig(ANIMNAME); + const auto PANIMNODE = m_AnimationTree.getConfig(ANIMNAME); PANIMNODE->internalBezier = "default"; return "no such bezier"; } - if (!ARGS[4].empty()) { + if (ARGS[4] != "") { auto ERR = g_pAnimationManager->styleValidInConfigVar(ANIMNAME, ARGS[4]); - if (!ERR.empty()) + if (ERR != "") return ERR; } @@ -2528,45 +2222,44 @@ std::optional CConfigManager::handleBind(const std::string& command // bind[fl]=SUPER,G,exec,dmenu_run // flags - bool locked = false; - bool release = false; - bool repeat = false; - bool mouse = false; - bool nonConsuming = false; - bool transparent = false; - bool ignoreMods = false; - bool multiKey = false; - bool longPress = false; - bool hasDescription = false; - bool dontInhibit = false; - bool click = false; - bool drag = false; - bool submapUniversal = false; - const auto BINDARGS = command.substr(4); + bool locked = false; + bool release = false; + bool repeat = false; + bool mouse = false; + bool nonConsuming = false; + bool transparent = false; + bool ignoreMods = false; + bool multiKey = false; + bool longPress = false; + bool hasDescription = false; + bool dontInhibit = false; + const auto BINDARGS = command.substr(4); for (auto const& arg : BINDARGS) { - switch (arg) { - case 'l': locked = true; break; - case 'r': release = true; break; - case 'e': repeat = true; break; - case 'm': mouse = true; break; - case 'n': nonConsuming = true; break; - case 't': transparent = true; break; - case 'i': ignoreMods = true; break; - case 's': multiKey = true; break; - case 'o': longPress = true; break; - case 'd': hasDescription = true; break; - case 'p': dontInhibit = true; break; - case 'c': - click = true; - release = true; - break; - case 'g': - drag = true; - release = true; - break; - case 'u': submapUniversal = true; break; - default: return "bind: invalid flag"; + if (arg == 'l') { + locked = true; + } else if (arg == 'r') { + release = true; + } else if (arg == 'e') { + repeat = true; + } else if (arg == 'm') { + mouse = true; + } else if (arg == 'n') { + nonConsuming = true; + } else if (arg == 't') { + transparent = true; + } else if (arg == 'i') { + ignoreMods = true; + } else if (arg == 's') { + multiKey = true; + } else if (arg == 'o') { + longPress = true; + } else if (arg == 'd') { + hasDescription = true; + } else if (arg == 'p') { + dontInhibit = true; + } else { + return "bind: invalid flag"; } } @@ -2576,16 +2269,13 @@ std::optional CConfigManager::handleBind(const std::string& command if (mouse && (repeat || release || locked)) return "flag m is exclusive"; - if (click && drag) - return "flags c and g are mutually exclusive"; - const int numbArgs = hasDescription ? 5 : 4; const auto ARGS = CVarList(value, numbArgs); const int DESCR_OFFSET = hasDescription ? 1 : 0; if ((ARGS.size() < 3 && !mouse) || (ARGS.size() < 3 && mouse)) return "bind: too few args"; - else if ((ARGS.size() > sc(4) + DESCR_OFFSET && !mouse) || (ARGS.size() > sc(3) + DESCR_OFFSET && mouse)) + else if ((ARGS.size() > (size_t)4 + DESCR_OFFSET && !mouse) || (ARGS.size() > (size_t)3 + DESCR_OFFSET && mouse)) return "bind: too many args"; std::set KEYSYMS; @@ -2614,32 +2304,31 @@ std::optional CConfigManager::handleBind(const std::string& command HANDLER = "mouse"; // to lower - std::ranges::transform(HANDLER, HANDLER.begin(), ::tolower); + std::transform(HANDLER.begin(), HANDLER.end(), HANDLER.begin(), ::tolower); - const auto DISPATCHER = g_pKeybindManager->m_dispatchers.find(HANDLER); + const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(HANDLER); - if (DISPATCHER == g_pKeybindManager->m_dispatchers.end()) { - Log::logger->log(Log::ERR, "Invalid dispatcher: {}", HANDLER); + if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end()) { + Debug::log(ERR, "Invalid dispatcher: {}", HANDLER); return "Invalid dispatcher, requested \"" + HANDLER + "\" does not exist"; } - if (MOD == 0 && !MODSTR.empty()) { - Log::logger->log(Log::ERR, "Invalid mod: {}", MODSTR); + if (MOD == 0 && MODSTR != "") { + Debug::log(ERR, "Invalid mod: {}", MODSTR); return "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod."; } - if ((!KEY.empty()) || multiKey) { + if ((KEY != "") || multiKey) { SParsedKey parsedKey = parseKey(KEY); - if (parsedKey.catchAll && m_currentSubmap.name.empty()) { - Log::logger->log(Log::ERR, "Catchall not allowed outside of submap!"); + if (parsedKey.catchAll && m_szCurrentSubmap.empty()) { + Debug::log(ERR, "Catchall not allowed outside of submap!"); return "Invalid catchall, catchall keybinds are only allowed in submaps."; } g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, - COMMAND, locked, m_currentSubmap, DESCRIPTION, release, repeat, longPress, - mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription, dontInhibit, - click, drag, submapUniversal}); + COMMAND, locked, m_szCurrentSubmap, DESCRIPTION, release, repeat, longPress, + mouse, nonConsuming, transparent, ignoreMods, multiKey, hasDescription, dontInhibit}); } return {}; @@ -2648,13 +2337,6 @@ std::optional CConfigManager::handleBind(const std::string& command std::optional CConfigManager::handleUnbind(const std::string& command, const std::string& value) { const auto ARGS = CVarList(value); - if (ARGS.size() == 1 && ARGS[0] == "all") { - g_pKeybindManager->m_keybinds.clear(); - g_pKeybindManager->m_activeKeybinds.clear(); - g_pKeybindManager->m_lastLongPressKeybind.reset(); - return {}; - } - const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); const auto KEY = parseKey(ARGS[1]); @@ -2664,13 +2346,278 @@ std::optional CConfigManager::handleUnbind(const std::string& comma return {}; } +std::optional CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { + const auto RULE = trim(value.substr(0, value.find_first_of(','))); + const auto VALUE = value.substr(value.find_first_of(',') + 1); + + auto rule = makeShared(RULE, VALUE, true); + + if (rule->ruleType == CWindowRule::RULE_INVALID && RULE != "unset") { + Debug::log(ERR, "Invalid rulev2 found: {}", RULE); + return "Invalid rulev2 found: " + RULE; + } + + // now we estract shit from the value + const auto TAGPOS = VALUE.find("tag:"); + const auto TITLEPOS = VALUE.find("title:"); + const auto CLASSPOS = VALUE.find("class:"); + const auto INITIALTITLEPOS = VALUE.find("initialTitle:"); + const auto INITIALCLASSPOS = VALUE.find("initialClass:"); + const auto X11POS = VALUE.find("xwayland:"); + const auto FLOATPOS = VALUE.find("floating:"); + const auto FULLSCREENPOS = VALUE.find("fullscreen:"); + const auto PINNEDPOS = VALUE.find("pinned:"); + const auto FOCUSPOS = VALUE.find("focus:"); + const auto FULLSCREENSTATEPOS = VALUE.find("fullscreenstate:"); + const auto ONWORKSPACEPOS = VALUE.find("onworkspace:"); + const auto CONTENTTYPEPOS = VALUE.find("content:"); + + // find workspacepos that isn't onworkspacepos + size_t WORKSPACEPOS = std::string::npos; + size_t currentPos = VALUE.find("workspace:"); + while (currentPos != std::string::npos) { + if (currentPos == 0 || VALUE[currentPos - 1] != 'n') { + WORKSPACEPOS = currentPos; + break; + } + currentPos = VALUE.find("workspace:", currentPos + 1); + } + + const auto checkPos = std::unordered_set{TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS, + FULLSCREENPOS, PINNEDPOS, FULLSCREENSTATEPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS, CONTENTTYPEPOS}; + if (checkPos.size() == 1 && checkPos.contains(std::string::npos)) { + Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); + return "Invalid rulev2 syntax: " + VALUE; + } + + auto extract = [&](size_t pos) -> std::string { + std::string result; + result = VALUE.substr(pos); + + size_t min = 999999; + if (TAGPOS > pos && TAGPOS < min) + min = TAGPOS; + if (TITLEPOS > pos && TITLEPOS < min) + min = TITLEPOS; + if (CLASSPOS > pos && CLASSPOS < min) + min = CLASSPOS; + if (INITIALTITLEPOS > pos && INITIALTITLEPOS < min) + min = INITIALTITLEPOS; + if (INITIALCLASSPOS > pos && INITIALCLASSPOS < min) + min = INITIALCLASSPOS; + if (X11POS > pos && X11POS < min) + min = X11POS; + if (FLOATPOS > pos && FLOATPOS < min) + min = FLOATPOS; + if (FULLSCREENPOS > pos && FULLSCREENPOS < min) + min = FULLSCREENPOS; + if (PINNEDPOS > pos && PINNEDPOS < min) + min = PINNEDPOS; + if (FULLSCREENSTATEPOS > pos && FULLSCREENSTATEPOS < min) + min = FULLSCREENSTATEPOS; + if (ONWORKSPACEPOS > pos && ONWORKSPACEPOS < min) + min = ONWORKSPACEPOS; + if (WORKSPACEPOS > pos && WORKSPACEPOS < min) + min = WORKSPACEPOS; + if (FOCUSPOS > pos && FOCUSPOS < min) + min = FOCUSPOS; + if (CONTENTTYPEPOS > pos && CONTENTTYPEPOS < min) + min = CONTENTTYPEPOS; + + result = result.substr(0, min - pos); + + result = trim(result); + + if (!result.empty() && result.back() == ',') + result.pop_back(); + + return result; + }; + + if (TAGPOS != std::string::npos) + rule->szTag = extract(TAGPOS + 4); + + if (CLASSPOS != std::string::npos) { + rule->szClass = extract(CLASSPOS + 6); + rule->rClass = {rule->szClass}; + } + + if (TITLEPOS != std::string::npos) { + rule->szTitle = extract(TITLEPOS + 6); + rule->rTitle = {rule->szTitle}; + } + + if (INITIALCLASSPOS != std::string::npos) { + rule->szInitialClass = extract(INITIALCLASSPOS + 13); + rule->rInitialClass = {rule->szInitialClass}; + } + + if (INITIALTITLEPOS != std::string::npos) { + rule->szInitialTitle = extract(INITIALTITLEPOS + 13); + rule->rInitialTitle = {rule->szInitialTitle}; + } + + if (X11POS != std::string::npos) + rule->bX11 = extract(X11POS + 9) == "1" ? 1 : 0; + + if (FLOATPOS != std::string::npos) + rule->bFloating = extract(FLOATPOS + 9) == "1" ? 1 : 0; + + if (FULLSCREENPOS != std::string::npos) + rule->bFullscreen = extract(FULLSCREENPOS + 11) == "1" ? 1 : 0; + + if (PINNEDPOS != std::string::npos) + rule->bPinned = extract(PINNEDPOS + 7) == "1" ? 1 : 0; + + if (FULLSCREENSTATEPOS != std::string::npos) + rule->szFullscreenState = extract(FULLSCREENSTATEPOS + 16); + + if (WORKSPACEPOS != std::string::npos) + rule->szWorkspace = extract(WORKSPACEPOS + 10); + + if (FOCUSPOS != std::string::npos) + rule->bFocus = extract(FOCUSPOS + 6) == "1" ? 1 : 0; + + if (ONWORKSPACEPOS != std::string::npos) + rule->szOnWorkspace = extract(ONWORKSPACEPOS + 12); + + if (CONTENTTYPEPOS != std::string::npos) + rule->szContentType = extract(CONTENTTYPEPOS + 8); + + if (RULE == "unset") { + std::erase_if(m_vWindowRules, [&](const auto& other) { + if (!other->v2) + return other->szClass == rule->szClass && !rule->szClass.empty(); + else { + if (!rule->szTag.empty() && rule->szTag != other->szTag) + return false; + + if (!rule->szClass.empty() && rule->szClass != other->szClass) + return false; + + if (!rule->szTitle.empty() && rule->szTitle != other->szTitle) + return false; + + if (!rule->szInitialClass.empty() && rule->szInitialClass != other->szInitialClass) + return false; + + if (!rule->szInitialTitle.empty() && rule->szInitialTitle != other->szInitialTitle) + return false; + + if (rule->bX11 != -1 && rule->bX11 != other->bX11) + return false; + + if (rule->bFloating != -1 && rule->bFloating != other->bFloating) + return false; + + if (rule->bFullscreen != -1 && rule->bFullscreen != other->bFullscreen) + return false; + + if (rule->bPinned != -1 && rule->bPinned != other->bPinned) + return false; + + if (!rule->szFullscreenState.empty() && rule->szFullscreenState != other->szFullscreenState) + return false; + + if (!rule->szWorkspace.empty() && rule->szWorkspace != other->szWorkspace) + return false; + + if (rule->bFocus != -1 && rule->bFocus != other->bFocus) + return false; + + if (!rule->szOnWorkspace.empty() && rule->szOnWorkspace != other->szOnWorkspace) + return false; + + if (!rule->szContentType.empty() && rule->szContentType != other->szContentType) + return false; + + return true; + } + }); + return {}; + } + + if (RULE.starts_with("size") || RULE.starts_with("maxsize") || RULE.starts_with("minsize")) + m_vWindowRules.insert(m_vWindowRules.begin(), rule); + else + m_vWindowRules.push_back(rule); + + return {}; +} + +std::optional CConfigManager::handleLayerRule(const std::string& command, const std::string& value) { + const auto RULE = trim(value.substr(0, value.find_first_of(','))); + const auto VALUE = trim(value.substr(value.find_first_of(',') + 1)); + + // check rule and value + if (RULE.empty() || VALUE.empty()) + return "empty rule?"; + + if (RULE == "unset") { + std::erase_if(m_vLayerRules, [&](const auto& other) { return other->targetNamespace == VALUE; }); + return {}; + } + + auto rule = makeShared(RULE, VALUE); + + if (rule->ruleType == CLayerRule::RULE_INVALID) { + Debug::log(ERR, "Invalid rule found: {}", RULE); + return "Invalid rule found: " + RULE; + } + + rule->targetNamespaceRegex = {VALUE}; + + m_vLayerRules.emplace_back(rule); + + for (auto const& m : g_pCompositor->m_vMonitors) + for (auto const& lsl : m->m_aLayerSurfaceLayers) + for (auto const& ls : lsl) + ls->applyRules(); + + return {}; +} + +void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) { + const bool BYADDRESS = name.starts_with("address:"); + std::string matchName = name; + + if (BYADDRESS) + matchName = matchName.substr(8); + + for (auto const& m : g_pCompositor->m_vMonitors) { + for (auto const& lsl : m->m_aLayerSurfaceLayers) { + for (auto const& ls : lsl) { + if (BYADDRESS) { + if (std::format("0x{:x}", (uintptr_t)ls.get()) == matchName) + ls->forceBlur = forceBlur; + } else if (ls->szNamespace == matchName) + ls->forceBlur = forceBlur; + } + } + } +} + +std::optional CConfigManager::handleBlurLS(const std::string& command, const std::string& value) { + if (value.starts_with("remove,")) { + const auto TOREMOVE = trim(value.substr(7)); + if (std::erase_if(m_dBlurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; })) + updateBlurredLS(TOREMOVE, false); + return {}; + } + + m_dBlurLSNamespaces.emplace_back(value); + updateBlurredLS(value, true); + + return {}; +} + std::optional CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) { // This can either be the monitor or the workspace identifier const auto FIRST_DELIM = value.find_first_of(','); auto first_ident = trim(value.substr(0, FIRST_DELIM)); - const auto& [id, name, isAutoID] = getWorkspaceIDNameFromString(first_ident); + const auto& [id, name] = getWorkspaceIDNameFromString(first_ident); auto rules = value.substr(FIRST_DELIM + 1); SWorkspaceRule wsRule; @@ -2682,7 +2629,7 @@ std::optional CConfigManager::handleWorkspaceRules(const std::strin // auto wsIdent = removeBeginEndSpacesTabs(value.substr(FIRST_DELIM + 1, (WORKSPACE_DELIM - FIRST_DELIM - 1))); // id = getWorkspaceIDFromString(wsIdent, name); // if (id == WORKSPACE_INVALID) { - // Log::logger->log(Log::ERR, "Invalid workspace identifier found: {}", wsIdent); + // Debug::log(ERR, "Invalid workspace identifier found: {}", wsIdent); // return "Invalid workspace identifier found: " + wsIdent; // } // wsRule.monitor = first_ident; @@ -2704,14 +2651,14 @@ std::optional CConfigManager::handleWorkspaceRules(const std::strin auto assignRule = [&](std::string rule) -> std::optional { size_t delim = std::string::npos; if ((delim = rule.find("gapsin:")) != std::string::npos) { - CVarList2 varlist(rule.substr(delim + 7), 0, ' '); - wsRule.gapsIn = CCssGapData(); + CVarList varlist = CVarList(rule.substr(delim + 7), 0, ' '); + wsRule.gapsIn = CCssGapData(); try { wsRule.gapsIn->parseGapData(varlist); } catch (...) { return "Error parsing workspace rule gaps: {}", rule.substr(delim + 7); } } else if ((delim = rule.find("gapsout:")) != std::string::npos) { - CVarList2 varlist(rule.substr(delim + 8), 0, ' '); - wsRule.gapsOut = CCssGapData(); + CVarList varlist = CVarList(rule.substr(delim + 8), 0, ' '); + wsRule.gapsOut = CCssGapData(); try { wsRule.gapsOut->parseGapData(varlist); } catch (...) { return "Error parsing workspace rule gaps: {}", rule.substr(delim + 8); } @@ -2740,7 +2687,7 @@ std::optional CConfigManager::handleWorkspaceRules(const std::strin CHECK_OR_THROW(configStringToInt(rule.substr(delim + 11))) wsRule.isPersistent = *X; } else if ((delim = rule.find("defaultName:")) != std::string::npos) - wsRule.defaultName = trim(rule.substr(delim + 12)); + wsRule.defaultName = rule.substr(delim + 12); else if ((delim = rule.find(ruleOnCreatedEmpty)) != std::string::npos) { CHECK_OR_THROW(cleanCmdForWorkspace(name, rule.substr(delim + ruleOnCreatedEmptyLen))) wsRule.onCreatedEmptyRunCmd = *X; @@ -2748,7 +2695,7 @@ std::optional CConfigManager::handleWorkspaceRules(const std::strin std::string opt = rule.substr(delim + 10); if (!opt.contains(":")) { // invalid - Log::logger->log(Log::ERR, "Invalid workspace rule found: {}", rule); + Debug::log(ERR, "Invalid workspace rule found: {}", rule); return "Invalid workspace rule found: " + rule; } @@ -2756,9 +2703,6 @@ std::optional CConfigManager::handleWorkspaceRules(const std::strin opt = opt.substr(0, opt.find(':')); wsRule.layoutopts[opt] = val; - } else if ((delim = rule.find("layout:")) != std::string::npos) { - std::string layout = rule.substr(delim + 7); - wsRule.layout = std::move(layout); } return {}; @@ -2766,81 +2710,80 @@ std::optional CConfigManager::handleWorkspaceRules(const std::strin #undef CHECK_OR_THROW - CVarList2 rulesList(std::string(rules), 0, ',', true); + CVarList rulesList{rules, 0, ',', true}; for (auto const& r : rulesList) { - const auto R = assignRule(std::string(r)); + const auto R = assignRule(r); if (R.has_value()) return R; } + wsRule.workspaceId = id; wsRule.workspaceName = name; - wsRule.workspaceId = isAutoID ? WORKSPACE_INVALID : id; - const auto IT = std::ranges::find_if(m_workspaceRules, [&](const auto& other) { return other.workspaceString == wsRule.workspaceString; }); + const auto IT = std::find_if(m_vWorkspaceRules.begin(), m_vWorkspaceRules.end(), [&](const auto& other) { return other.workspaceString == wsRule.workspaceString; }); - if (IT == m_workspaceRules.end()) - m_workspaceRules.emplace_back(wsRule); + if (IT == m_vWorkspaceRules.end()) + m_vWorkspaceRules.emplace_back(wsRule); else *IT = mergeWorkspaceRules(*IT, wsRule); return {}; } -std::optional CConfigManager::handleSubmap(const std::string&, const std::string& submap) { - CVarList2 data((std::string(submap))); - m_currentSubmap.name = (data[0] == "reset") ? "" : data[0]; - m_currentSubmap.reset = data[1]; +std::optional CConfigManager::handleSubmap(const std::string& command, const std::string& submap) { + if (submap == "reset") + m_szCurrentSubmap = ""; + else + m_szCurrentSubmap = submap; + return {}; } std::optional CConfigManager::handleSource(const std::string& command, const std::string& rawpath) { if (rawpath.length() < 2) { - Log::logger->log(Log::ERR, "source= path garbage"); + Debug::log(ERR, "source= path garbage"); return "source= path " + rawpath + " bogus!"; } - std::unique_ptr glob_buf{sc(calloc(1, sizeof(glob_t))), // allocate and zero-initialize NOLINT(cppcoreguidelines-no-malloc) + std::unique_ptr glob_buf{static_cast(calloc(1, sizeof(glob_t))), // allocate and zero-initialize [](glob_t* g) { if (g) { globfree(g); // free internal resources allocated by glob() - free(g); // free the memory for the glob_t structure NOLINT(cppcoreguidelines-no-malloc) + free(g); // free the memory for the glob_t structure } }}; - if (auto r = glob(absolutePath(rawpath, m_configCurrentPath).c_str(), GLOB_TILDE, nullptr, glob_buf.get()); r != 0) { + if (auto r = glob(absolutePath(rawpath, configCurrentPath).c_str(), GLOB_TILDE, nullptr, glob_buf.get()); r != 0) { std::string err = std::format("source= globbing error: {}", r == GLOB_NOMATCH ? "found no match" : GLOB_ABORTED ? "read error" : "out of memory"); - Log::logger->log(Log::ERR, "{}", err); + Debug::log(ERR, "{}", err); return err; } std::string errorsFromParsing; for (size_t i = 0; i < glob_buf->gl_pathc; i++) { - auto value = absolutePath(glob_buf->gl_pathv[i], m_configCurrentPath); + auto value = absolutePath(glob_buf->gl_pathv[i], configCurrentPath); - std::error_code ec; - auto file_status = std::filesystem::status(value, ec); + if (!std::filesystem::is_regular_file(value)) { + if (std::filesystem::exists(value)) { + Debug::log(WARN, "source= skipping non-file {}", value); + continue; + } - if (ec) { - Log::logger->log(Log::ERR, "source= file from glob result is inaccessible ({}): {}", ec.message(), value); - return "source= file " + value + " is inaccessible!"; + Debug::log(ERR, "source= file doesn't exist: {}", value); + return "source= file " + value + " doesn't exist!"; } + m_configPaths.emplace_back(value); - if (std::filesystem::is_regular_file(file_status)) { - m_configPaths.emplace_back(value); - auto configCurrentPathBackup = m_configCurrentPath; - m_configCurrentPath = value; - const auto THISRESULT = m_config->parseFile(value.c_str()); - m_configCurrentPath = configCurrentPathBackup; - if (THISRESULT.error && errorsFromParsing.empty()) - errorsFromParsing += THISRESULT.getError(); - } else if (std::filesystem::is_directory(file_status)) { - Log::logger->log(Log::WARN, "source= skipping directory {}", value); - continue; - } else { - Log::logger->log(Log::WARN, "source= skipping non-regular-file {}", value); - continue; - } + auto configCurrentPathBackup = configCurrentPath; + configCurrentPath = value; + + const auto THISRESULT = m_pConfig->parseFile(value.c_str()); + + configCurrentPath = configCurrentPathBackup; + + if (THISRESULT.error && errorsFromParsing.empty()) + errorsFromParsing += THISRESULT.getError(); } if (errorsFromParsing.empty()) @@ -2849,18 +2792,14 @@ std::optional CConfigManager::handleSource(const std::string& comma } std::optional CConfigManager::handleEnv(const std::string& command, const std::string& value) { + if (!isFirstLaunch) + return {}; + const auto ARGS = CVarList(value, 2); if (ARGS[0].empty()) return "env empty"; - if (!m_isFirstLaunch) { - // check if env changed at all. If it didn't, ignore. If it did, update it. - const auto* ENV = getenv(ARGS[0].c_str()); - if (ENV && ENV == ARGS[1]) - return {}; // env hasn't changed - } - setenv(ARGS[0].c_str(), ARGS[1].c_str(), 1); if (command.back() == 'd') { @@ -2879,224 +2818,25 @@ std::optional CConfigManager::handleEnv(const std::string& command, } std::optional CConfigManager::handlePlugin(const std::string& command, const std::string& path) { - if (std::ranges::find(m_declaredPlugins, path) != m_declaredPlugins.end()) + if (std::find(m_vDeclaredPlugins.begin(), m_vDeclaredPlugins.end(), path) != m_vDeclaredPlugins.end()) return "plugin '" + path + "' declared twice"; - m_declaredPlugins.push_back(path); + m_vDeclaredPlugins.push_back(path); return {}; } -std::optional CConfigManager::handlePermission(const std::string& command, const std::string& value) { - CVarList2 data((std::string(value))); - - eDynamicPermissionType type = PERMISSION_TYPE_UNKNOWN; - eDynamicPermissionAllowMode mode = PERMISSION_RULE_ALLOW_MODE_UNKNOWN; - - if (data[1] == "screencopy") - type = PERMISSION_TYPE_SCREENCOPY; - else if (data[1] == "cursorpos") - type = PERMISSION_TYPE_CURSOR_POS; - else if (data[1] == "plugin") - type = PERMISSION_TYPE_PLUGIN; - else if (data[1] == "keyboard" || data[1] == "keeb") - type = PERMISSION_TYPE_KEYBOARD; - - if (data[2] == "ask") - mode = PERMISSION_RULE_ALLOW_MODE_ASK; - else if (data[2] == "allow") - mode = PERMISSION_RULE_ALLOW_MODE_ALLOW; - else if (data[2] == "deny") - mode = PERMISSION_RULE_ALLOW_MODE_DENY; - - if (type == PERMISSION_TYPE_UNKNOWN) - return "unknown permission type"; - if (mode == PERMISSION_RULE_ALLOW_MODE_UNKNOWN) - return "unknown permission allow mode"; - - if (m_isFirstLaunch && g_pDynamicPermissionManager) - g_pDynamicPermissionManager->addConfigPermissionRule(std::string(data[0]), type, mode); - - return {}; -} - -std::optional CConfigManager::handleGesture(const std::string& command, const std::string& value) { - CVarList2 data((std::string(value))); - - size_t fingerCount = 0; - eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE; - - try { - fingerCount = std::stoul(std::string{data[0]}); - } catch (...) { return std::format("Invalid value {} for finger count", data[0]); } - - if (fingerCount <= 1 || fingerCount >= 10) - return std::format("Invalid value {} for finger count", data[0]); - - direction = g_pTrackpadGestures->dirForString(data[1]); - - if (direction == TRACKPAD_GESTURE_DIR_NONE) - return std::format("Invalid direction: {}", data[1]); - - int startDataIdx = 2; - uint32_t modMask = 0; - float deltaScale = 1.F; - bool disableInhibit = false; - - for (const auto arg : command.substr(7)) { - switch (arg) { - case 'p': disableInhibit = true; break; - default: return "gesture: invalid flag"; - } - } - - while (true) { - - if (data[startDataIdx].starts_with("mod:")) { - modMask = g_pKeybindManager->stringToModMask(std::string{data[startDataIdx].substr(4)}); - startDataIdx++; - continue; - } else if (data[startDataIdx].starts_with("scale:")) { - try { - deltaScale = std::clamp(std::stof(std::string{data[startDataIdx].substr(6)}), 0.1F, 10.F); - startDataIdx++; - continue; - } catch (...) { return std::format("Invalid delta scale: {}", std::string{data[startDataIdx].substr(6)}); } - } - - break; - } - - std::expected result; - - if (data[startDataIdx] == "dispatcher") - result = g_pTrackpadGestures->addGesture(makeUnique(std::string{data[startDataIdx + 1]}, data.join(",", startDataIdx + 2)), fingerCount, - direction, modMask, deltaScale, disableInhibit); - else if (data[startDataIdx] == "workspace") - result = g_pTrackpadGestures->addGesture(makeUnique(), fingerCount, direction, modMask, deltaScale, disableInhibit); - else if (data[startDataIdx] == "resize") - result = g_pTrackpadGestures->addGesture(makeUnique(), fingerCount, direction, modMask, deltaScale, disableInhibit); - else if (data[startDataIdx] == "move") - result = g_pTrackpadGestures->addGesture(makeUnique(), fingerCount, direction, modMask, deltaScale, disableInhibit); - else if (data[startDataIdx] == "special") - result = - g_pTrackpadGestures->addGesture(makeUnique(std::string{data[startDataIdx + 1]}), fingerCount, direction, modMask, deltaScale, disableInhibit); - else if (data[startDataIdx] == "close") - result = g_pTrackpadGestures->addGesture(makeUnique(), fingerCount, direction, modMask, deltaScale, disableInhibit); - else if (data[startDataIdx] == "float") - result = - g_pTrackpadGestures->addGesture(makeUnique(std::string{data[startDataIdx + 1]}), fingerCount, direction, modMask, deltaScale, disableInhibit); - else if (data[startDataIdx] == "fullscreen") - result = g_pTrackpadGestures->addGesture(makeUnique(std::string{data[startDataIdx + 1]}), fingerCount, direction, modMask, deltaScale, - disableInhibit); - else if (data[startDataIdx] == "cursorZoom") { - result = g_pTrackpadGestures->addGesture(makeUnique(std::string{data[startDataIdx + 1]}, std::string{data[startDataIdx + 2]}), fingerCount, - direction, modMask, deltaScale, disableInhibit); - } else if (data[startDataIdx] == "unset") - result = g_pTrackpadGestures->removeGesture(fingerCount, direction, modMask, deltaScale, disableInhibit); - else - return std::format("Invalid gesture: {}", data[startDataIdx]); - - if (!result) - return result.error(); - - return std::nullopt; -} - -std::optional CConfigManager::handleWindowrule(const std::string& command, const std::string& value) { - CVarList2 data((std::string(value))); - - SP rule = makeShared(); - - const auto& PROPS = Desktop::Rule::allMatchPropStrings(); - const auto& EFFECTS = Desktop::Rule::windowEffects()->allEffectStrings(); - - for (const auto& el : data) { - // split on space, no need for a CVarList here - size_t spacePos = el.find(' '); - if (spacePos == std::string::npos) - return std::format("invalid field {}: missing a value", el); - - const bool FIRST_IS_PROP = el.starts_with("match:"); - const auto FIRST = FIRST_IS_PROP ? el.substr(6, spacePos - 6) : el.substr(0, spacePos); - if (FIRST_IS_PROP && std::ranges::contains(PROPS, FIRST)) { - // it's a prop - const auto PROP = Desktop::Rule::matchPropFromString(FIRST); - if (!PROP.has_value()) - return std::format("invalid prop {}", el); - rule->registerMatch(*PROP, std::string{el.substr(spacePos + 1)}); - } else if (!FIRST_IS_PROP && std::ranges::contains(EFFECTS, FIRST)) { - // it's an effect - const auto EFFECT = Desktop::Rule::windowEffects()->get(FIRST); - if (!EFFECT.has_value()) - return std::format("invalid effect {}", el); - rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)}); - } else - return std::format("invalid field type {}", FIRST); - } - - m_keywordRules.emplace_back(std::move(rule)); - if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.isDynamicKeyword) - Desktop::Rule::ruleEngine()->registerRule(SP{m_keywordRules.back()}); - - return std::nullopt; -} - -std::optional CConfigManager::handleLayerrule(const std::string& command, const std::string& value) { - CVarList2 data((std::string(value))); - - SP rule = makeShared(); - - const auto& PROPS = Desktop::Rule::allMatchPropStrings(); - const auto& EFFECTS = Desktop::Rule::layerEffects()->allEffectStrings(); - - for (const auto& el : data) { - // split on space, no need for a CVarList here - size_t spacePos = el.find(' '); - if (spacePos == std::string::npos) - return std::format("invalid field {}: missing a value", el); - - const bool FIRST_IS_PROP = el.starts_with("match:"); - const auto FIRST = FIRST_IS_PROP ? el.substr(6, spacePos - 6) : el.substr(0, spacePos); - if (FIRST_IS_PROP && std::ranges::contains(PROPS, FIRST)) { - // it's a prop - const auto PROP = Desktop::Rule::matchPropFromString(FIRST); - if (!PROP.has_value()) - return std::format("invalid prop {}", el); - rule->registerMatch(*PROP, std::string{el.substr(spacePos + 1)}); - } else if (!FIRST_IS_PROP && std::ranges::contains(EFFECTS, FIRST)) { - // it's an effect - const auto EFFECT = Desktop::Rule::layerEffects()->get(FIRST); - if (!EFFECT.has_value()) - return std::format("invalid effect {}", el); - rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)}); - } else - return std::format("invalid field type {}", FIRST); - } - - m_keywordRules.emplace_back(std::move(rule)); - - return std::nullopt; -} - const std::vector& CConfigManager::getAllDescriptions() { return CONFIG_OPTIONS; } bool CConfigManager::shouldUseSoftwareCursors(PHLMONITOR pMonitor) { - static auto PNOHW = CConfigValue("cursor:no_hardware_cursors"); - static auto PINVISIBLE = CConfigValue("cursor:invisible"); - - if (pMonitor->m_tearingState.activelyTearing) - return true; - - if (*PINVISIBLE != 0) - return true; + static auto PNOHW = CConfigValue("cursor:no_hardware_cursors"); switch (*PNOHW) { case 0: return false; case 1: return true; - case 2: return g_pHyprRenderer->isNvidia() && (g_pHyprRenderer->isMgpu() || g_pCompositor->isVRRActiveOnAnyMonitor()); + case 2: return pMonitor->tearingState.activelyTearing; default: break; } @@ -3107,9 +2847,9 @@ std::string SConfigOptionDescription::jsonify() const { auto parseData = [this]() -> std::string { return std::visit( [this](auto&& val) { - const auto PTR = g_pConfigManager->m_config->getConfigValuePtr(value.c_str()); + const auto PTR = g_pConfigManager->m_pConfig->getConfigValuePtr(value.c_str()); if (!PTR) { - Log::logger->log(Log::ERR, "invalid SConfigOptionDescription: no config option {} exists", value); + Debug::log(ERR, "invalid SConfigOptionDescription: no config option {} exists", value); return std::string{""}; } const char* const EXPLICIT = PTR->m_bSetByUser ? "true" : "false"; @@ -3123,20 +2863,20 @@ std::string SConfigOptionDescription::jsonify() const { else if (typeid(Hyprlang::FLOAT) == std::type_index(CONFIGVALUE.type())) currentValue = std::format("{:.2f}", std::any_cast(CONFIGVALUE)); else if (typeid(Hyprlang::STRING) == std::type_index(CONFIGVALUE.type())) - currentValue = std::format("\"{}\"", std::any_cast(CONFIGVALUE)); + currentValue = std::any_cast(CONFIGVALUE); else if (typeid(Hyprlang::VEC2) == std::type_index(CONFIGVALUE.type())) { const auto V = std::any_cast(CONFIGVALUE); - currentValue = std::format("\"{}, {}\"", V.x, V.y); + currentValue = std::format("{}, {}", V.x, V.y); } else if (typeid(void*) == std::type_index(CONFIGVALUE.type())) { - const auto DATA = sc(std::any_cast(CONFIGVALUE)); - currentValue = std::format("\"{}\"", DATA->toString()); + const auto DATA = (ICustomConfigValueData*)std::any_cast(CONFIGVALUE); + currentValue = DATA->toString(); } try { using T = std::decay_t; if constexpr (std::is_same_v) { return std::format(R"#( "value": "{}", - "current": {}, + "current": "{}", "explicit": {})#", val.value, currentValue, EXPLICIT); } else if constexpr (std::is_same_v) { @@ -3154,7 +2894,7 @@ std::string SConfigOptionDescription::jsonify() const { "explicit": {})#", val.value, val.min, val.max, currentValue, EXPLICIT); } else if constexpr (std::is_same_v) { - return std::format(R"#( "value": "{}", + return std::format(R"#( "value": {}, "current": {}, "explicit": {})#", val.color.getAsHex(), currentValue, EXPLICIT); @@ -3164,7 +2904,7 @@ std::string SConfigOptionDescription::jsonify() const { "explicit": {})#", val.value, currentValue, EXPLICIT); } else if constexpr (std::is_same_v) { - return std::format(R"#( "value": "{}", + return std::format(R"#( "value": {}, "firstIndex": {}, "current": {}, "explicit": {})#", @@ -3176,17 +2916,17 @@ std::string SConfigOptionDescription::jsonify() const { "min_y": {}, "max_x": {}, "max_y": {}, - "current": {}, + "current": "{}", "explicit": {})#", val.vec.x, val.vec.y, val.min.x, val.min.y, val.max.x, val.max.y, currentValue, EXPLICIT); } else if constexpr (std::is_same_v) { return std::format(R"#( "value": "{}", - "current": {}, + "current": "{}", "explicit": {})#", val.gradient, currentValue, EXPLICIT); } - } catch (std::bad_any_cast& e) { Log::logger->log(Log::ERR, "Bad any_cast on value {} in descriptions", value); } + } catch (std::bad_any_cast& e) { Debug::log(ERR, "Bad any_cast on value {} in descriptions", value); } return std::string{""}; }, data); @@ -3201,30 +2941,25 @@ std::string SConfigOptionDescription::jsonify() const { {} }} }})#", - value, escapeJSONStrings(description), sc(type), sc(flags), parseData()); + value, description, (uint16_t)type, (uint32_t)flags, parseData()); return json; } void CConfigManager::ensurePersistentWorkspacesPresent() { - g_pCompositor->ensurePersistentWorkspacesPresent(m_workspaceRules); + g_pCompositor->ensurePersistentWorkspacesPresent(m_vWorkspaceRules); } void CConfigManager::storeFloatingSize(PHLWINDOW window, const Vector2D& size) { - Log::logger->log(Log::DEBUG, "storing floating size {}x{} for window {}::{}", size.x, size.y, window->m_initialClass, window->m_initialTitle); - // true -> use m_initialClass and m_initialTitle - SFloatCache id{window, true}; + Debug::log(LOG, "storing floating size {}x{} for window {}::{}", size.x, size.y, window->m_szClass, window->m_szTitle); + SFloatCache id{window}; m_mStoredFloatingSizes[id] = size; } std::optional CConfigManager::getStoredFloatingSize(PHLWINDOW window) { - // At startup, m_initialClass and m_initialTitle are undefined - // and m_class and m_title are just "initial" ones. - // false -> use m_class and m_title - SFloatCache id{window, false}; - Log::logger->log(Log::DEBUG, "Hash for window {}::{} = {}", window->m_class, window->m_title, id.hash); + SFloatCache id{window}; if (m_mStoredFloatingSizes.contains(id)) { - Log::logger->log(Log::DEBUG, "got stored size {}x{} for window {}::{}", m_mStoredFloatingSizes[id].x, m_mStoredFloatingSizes[id].y, window->m_class, window->m_title); + Debug::log(LOG, "got stored size {}x{} for window {}::{}", m_mStoredFloatingSizes[id].x, m_mStoredFloatingSizes[id].y, window->m_szClass, window->m_szTitle); return m_mStoredFloatingSizes[id]; } return std::nullopt; diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 36e25f6b..4da123c9 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -12,23 +12,21 @@ #include #include #include "../helpers/Monitor.hpp" -#include "../desktop/view/Window.hpp" +#include "../desktop/Window.hpp" +#include "../desktop/LayerRule.hpp" #include "ConfigDataValues.hpp" #include "../SharedDefs.hpp" #include "../helpers/Color.hpp" #include "../desktop/DesktopTypes.hpp" -#include "../desktop/reserved/ReservedArea.hpp" #include "../helpers/memory/Memory.hpp" +#include "../desktop/WindowRule.hpp" #include "../managers/XWaylandManager.hpp" -#include "../managers/KeybindManager.hpp" #include #define HANDLE void* -class CConfigManager; - struct SWorkspaceRule { std::string monitor = ""; std::string workspaceString = ""; @@ -38,7 +36,6 @@ struct SWorkspaceRule { bool isPersistent = false; std::optional gapsIn; std::optional gapsOut; - std::optional floatGaps = gapsOut; std::optional borderSize; std::optional decorate; std::optional noRounding; @@ -46,10 +43,16 @@ struct SWorkspaceRule { std::optional noShadow; std::optional onCreatedEmptyRunCmd; std::optional defaultName; - std::optional layout; std::map layoutopts; }; +struct SMonitorAdditionalReservedArea { + int top = 0; + int bottom = 0; + int left = 0; + int right = 0; +}; + struct SPluginKeyword { HANDLE handle = nullptr; std::string name = ""; @@ -61,6 +64,11 @@ struct SPluginVariable { std::string name = ""; }; +struct SExecRequestedRule { + std::string szRule = ""; + uint64_t iPid = 0; +}; + enum eConfigOptionType : uint8_t { CONFIG_OPTION_BOOL = 0, CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/ @@ -133,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{}(window->m_initialClass) ^ (std::hash{}(window->m_initialTitle) << 1)) : - (std::hash{}(window->m_class) ^ (std::hash{}(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{}(tagValue) << 2); + SFloatCache(PHLWINDOW window) { + hash = std::hash{}(window->m_szClass) ^ (std::hash{}(window->m_szTitle) << 1); } bool operator==(const SFloatCache& other) const { @@ -161,65 +159,42 @@ namespace std { }; } -class CMonitorRuleParser { - public: - CMonitorRuleParser(const std::string& name); - - const std::string& name(); - SMonitorRule& rule(); - std::optional 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); - bool parseICC(const std::string& value); - - void setDisabled(); - void setMirror(const std::string& value); - bool setReserved(const Desktop::CReservedArea& value); - - private: - SMonitorRule m_rule; - std::string m_error = ""; -}; - class CConfigManager { public: CConfigManager(); - void init(); - void reload(); - std::string verify(); + void init(); + void reload(); + std::string verify(); - int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = ""); - float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = ""); - Vector2D getDeviceVec(const std::string&, const std::string&, const std::string& fallback = ""); - std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = ""); - bool deviceConfigExplicitlySet(const std::string&, const std::string&); - bool deviceConfigExists(const std::string&); - Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback); + int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = ""); + float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = ""); + Vector2D getDeviceVec(const std::string&, const std::string&, const std::string& fallback = ""); + std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = ""); + bool deviceConfigExists(const std::string&); + Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback); + bool shouldBlurLS(const std::string&); - void* const* getConfigValuePtr(const std::string&); - Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = ""); - std::string getMainConfigPath(); - std::string getConfigString(); + void* const* getConfigValuePtr(const std::string&); + Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = ""); + std::string getMainConfigPath(); + std::string getConfigString(); - SMonitorRule getMonitorRuleFor(const PHLMONITOR); - SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace); - std::string getDefaultWorkspaceFor(const std::string&); + SMonitorRule getMonitorRuleFor(const PHLMONITOR); + SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace); + std::string getDefaultWorkspaceFor(const std::string&); - PHLMONITOR getBoundMonitorForWS(const std::string&); - std::string getBoundMonitorStringForWS(const std::string&); - const std::vector& getAllWorkspaceRules(); + PHLMONITOR getBoundMonitorForWS(const std::string&); + std::string getBoundMonitorStringForWS(const std::string&); + const std::vector& getAllWorkspaceRules(); - void ensurePersistentWorkspacesPresent(); + std::vector> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false); + std::vector> getMatchingRules(PHLLS); + void ensurePersistentWorkspacesPresent(); - const std::vector& getAllDescriptions(); + const std::vector& getAllDescriptions(); + + std::unordered_map m_mAdditionalReservedAreas; const std::unordered_map>& getAnimationConfig(); @@ -244,6 +219,8 @@ class CConfigManager { SP getAnimationPropertyConfig(const std::string&); + void addExecRule(const SExecRequestedRule&); + void handlePluginLoads(); std::string getErrors(); @@ -256,73 +233,68 @@ class CConfigManager { std::optional handleMonitor(const std::string&, const std::string&); std::optional handleBind(const std::string&, const std::string&); std::optional handleUnbind(const std::string&, const std::string&); + std::optional handleWindowRule(const std::string&, const std::string&); + std::optional handleLayerRule(const std::string&, const std::string&); std::optional handleWorkspaceRules(const std::string&, const std::string&); std::optional handleBezier(const std::string&, const std::string&); std::optional handleAnimation(const std::string&, const std::string&); std::optional handleSource(const std::string&, const std::string&); std::optional handleSubmap(const std::string&, const std::string&); + std::optional handleBlurLS(const std::string&, const std::string&); std::optional handleBindWS(const std::string&, const std::string&); std::optional handleEnv(const std::string&, const std::string&); std::optional handlePlugin(const std::string&, const std::string&); - std::optional handlePermission(const std::string&, const std::string&); - std::optional handleGesture(const std::string&, const std::string&); - std::optional handleWindowrule(const std::string&, const std::string&); - std::optional handleLayerrule(const std::string&, const std::string&); - std::optional handleMonitorv2(const std::string& output); - Hyprlang::CParseResult handleMonitorv2(); - std::optional addRuleFromConfigKey(const std::string& name); - std::optional addLayerRuleFromConfigKey(const std::string& name); - Hyprlang::CParseResult reloadRules(); + 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 getStoredFloatingSize(PHLWINDOW window); private: - UP m_config; + UP m_pConfig; std::vector m_configPaths; - Hyprutils::Animation::CAnimationConfigTree m_animationTree; + Hyprutils::Animation::CAnimationConfigTree m_AnimationTree; - SSubmap m_currentSubmap; + std::string m_szCurrentSubmap = ""; // For storing the current keybind submap - std::vector m_declaredPlugins; - std::vector m_pluginKeywords; - std::vector m_pluginVariables; + std::vector execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty - std::vector> m_keywordRules; + std::vector m_vDeclaredPlugins; + std::vector pluginKeywords; + std::vector pluginVariables; - bool m_isFirstLaunch = true; // For exec-once + bool isFirstLaunch = true; // For exec-once - std::vector m_monitorRules; - std::vector m_workspaceRules; + std::vector m_vMonitorRules; + std::vector m_vWorkspaceRules; + std::vector> m_vWindowRules; + std::vector> m_vLayerRules; + std::vector m_dBlurLSNamespaces; - bool m_firstExecDispatched = false; - bool m_manualCrashInitiated = false; + bool firstExecDispatched = false; + bool m_bManualCrashInitiated = false; - std::vector m_firstExecRequests; // bool is for if with rules - std::vector m_finalExecRequests; + std::vector firstExecRequests; // bool is for if with rules + std::vector finalExecRequests; - std::vector> m_failedPluginConfigValues; // for plugin values of unloaded plugins - std::string m_configErrors = ""; + std::vector> m_vFailedPluginConfigValues; // for plugin values of unloaded plugins + std::string m_szConfigErrors = ""; uint32_t m_configValueNumber = 0; // internal methods + void updateBlurredLS(const std::string&, const bool); void setDefaultAnimationVars(); std::optional resetHLConfig(); - std::optional generateConfig(std::string configPath, bool safeMode = false); + std::optional generateConfig(std::string configPath); std::optional verifyConfigExists(); - void reloadRuleConfigs(); - void postConfigReload(const Hyprlang::CParseResult& result); SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&); @@ -335,7 +307,6 @@ class CConfigManager { std::unordered_map m_mStoredFloatingSizes; friend struct SConfigOptionDescription; - friend class CMonitorRuleParser; }; inline UP g_pConfigManager; diff --git a/src/config/ConfigValue.cpp b/src/config/ConfigValue.cpp deleted file mode 100644 index 9f6cc319..00000000 --- a/src/config/ConfigValue.cpp +++ /dev/null @@ -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()); -} \ No newline at end of file diff --git a/src/config/ConfigValue.hpp b/src/config/ConfigValue.hpp index 1da359b6..089ae8bf 100644 --- a/src/config/ConfigValue.hpp +++ b/src/config/ConfigValue.hpp @@ -4,19 +4,21 @@ #include #include #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 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,12 +26,10 @@ class CConfigValue { RASSERT(typeid(T) == TYPE || STRINGEX || CUSTOMEX, "Mismatched type in CConfigValue, got {} but has {}", typeid(T).name(), TYPE.name()); #endif - - local__configValuePopulate(&p_, val); } T* ptr() const { - return *rc(p_); + return *(T* const*)p_; } T operator*() const { @@ -48,26 +48,26 @@ inline std::string* CConfigValue::ptr() const { template <> inline std::string CConfigValue::operator*() const { - return std::string{*rc(p_)}; + return std::string{*(Hyprlang::STRING*)p_}; } template <> inline Hyprlang::STRING* CConfigValue::ptr() const { - return rc(*p_); + return (Hyprlang::STRING*)p_; } template <> inline Hyprlang::STRING CConfigValue::operator*() const { - return *rc(p_); + return *(Hyprlang::STRING*)p_; } template <> inline Hyprlang::CUSTOMTYPE* CConfigValue::ptr() const { - return *rc(p_); + return *(Hyprlang::CUSTOMTYPE* const*)p_; } template <> inline Hyprlang::CUSTOMTYPE CConfigValue::operator*() const { RASSERT(false, "Impossible to implement operator* of CConfigValue, use ptr()"); return *ptr(); -} \ No newline at end of file +} diff --git a/src/config/ConfigWatcher.cpp b/src/config/ConfigWatcher.cpp index 83f3011c..e5db1e86 100644 --- a/src/config/ConfigWatcher.cpp +++ b/src/config/ConfigWatcher.cpp @@ -1,9 +1,6 @@ #include "ConfigWatcher.hpp" -#if defined(__linux__) -#include -#endif #include -#include "../debug/log/Logger.hpp" +#include "../debug/Log.hpp" #include #include #include @@ -13,14 +10,14 @@ using namespace Hyprutils::OS; CConfigWatcher::CConfigWatcher() : m_inotifyFd(inotify_init()) { if (!m_inotifyFd.isValid()) { - Log::logger->log(Log::ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded"); + Debug::log(ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded"); return; } // TODO: make CFileDescriptor take F_GETFL, F_SETFL const int FLAGS = fcntl(m_inotifyFd.get(), F_GETFL, 0); if (fcntl(m_inotifyFd.get(), F_SETFL, FLAGS | O_NONBLOCK) < 0) { - Log::logger->log(Log::ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded"); + Debug::log(ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded"); m_inotifyFd.reset(); return; } @@ -68,34 +65,17 @@ void CConfigWatcher::setOnChange(const std::function buffer = {}; - const ssize_t bytesRead = read(m_inotifyFd.get(), buffer.data(), buffer.size()); - if (bytesRead <= 0) - return; + inotify_event ev; + while (read(m_inotifyFd.get(), &ev, sizeof(ev)) > 0) { + const auto WD = std::ranges::find_if(m_watches.begin(), m_watches.end(), [wd = ev.wd](const auto& e) { return e.wd == wd; }); - for (size_t offset = 0; offset < sc(bytesRead);) { - const auto* ev = rc(buffer.data() + offset); - - if (offset + sizeof(inotify_event) > sc(bytesRead)) { - Log::logger->log(Log::ERR, "CConfigWatcher: malformed inotify event, truncated header"); - break; + if (WD == m_watches.end()) { + Debug::log(ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev.wd); + return; } - if (offset + sizeof(inotify_event) + ev->len > sc(bytesRead)) { - Log::logger->log(Log::ERR, "CConfigWatcher: malformed inotify event, truncated name field"); - break; - } - - const auto WD = std::ranges::find_if(m_watches, [wd = ev->wd](const auto& e) { return e.wd == wd; }); - - if (WD == m_watches.end()) - Log::logger->log(Log::ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev->wd); - else - m_watchCallback(SConfigWatchEvent{ - .file = WD->file, - }); - - offset += sizeof(inotify_event) + ev->len; + m_watchCallback(SConfigWatchEvent{ + .file = WD->file, + }); } } diff --git a/src/config/defaultConfig.hpp b/src/config/defaultConfig.hpp index 8bdfea39..ff4a579d 100644 --- a/src/config/defaultConfig.hpp +++ b/src/config/defaultConfig.hpp @@ -2,17 +2,299 @@ #include -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 +# windowrule = bordersize 0, floating:0, onworkspace:w[tv1] +# windowrule = rounding 0, floating:0, onworkspace:w[tv1] +# windowrule = bordersize 0, floating:0, onworkspace:f[1] +# windowrule = rounding 0, floating:0, onworkspace:f[1] + +# See https://wiki.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 +# windowrule = float,class:^(kitty)$,title:^(kitty)$ + +# Ignore maximize requests from apps. You'll probably like this. +windowrule = suppressevent maximize, class:.* + +# Fix some dragging issues with XWayland +windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0 +)#"; diff --git a/src/debug/crash/CrashReporter.cpp b/src/debug/CrashReporter.cpp similarity index 70% rename from src/debug/crash/CrashReporter.cpp rename to src/debug/CrashReporter.cpp index fad6ad21..f6e0b55f 100644 --- a/src/debug/crash/CrashReporter.cpp +++ b/src/debug/CrashReporter.cpp @@ -6,43 +6,36 @@ #include #include #include -#include "../../helpers/MiscFunctions.hpp" +#include "../helpers/MiscFunctions.hpp" -#include "../../plugins/PluginSystem.hpp" -#include "SignalSafe.hpp" +#include "../plugins/PluginSystem.hpp" +#include "../signal-safe.hpp" #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) #include #endif -static char const* const MESSAGES[] = { - "Sorry, didn't mean to...", - "This was an accident, I swear!", - "Calm down, it was a misinput! MISINPUT!", - "Oops", - "Vaxry is going to be upset.", - "Who tried dividing by zero?!", - "Maybe you should try dusting your PC in the meantime?", - "I tried so hard, and got so far...", - "I don't feel so good...", - "*thud*", - "Well this is awkward.", - "\"stable\"", - "I hope you didn't have any unsaved progress.", - "All these computers...", - "The math isn't mathing...", - "We've got an imposter in the code!", - "Well, at least the crash reporter didn't crash!", - "Everything's just fi-", - "Have you tried asking Hyprland politely not to crash?", -}; +static char const* const MESSAGES[] = {"Sorry, didn't mean to...", + "This was an accident, I swear!", + "Calm down, it was a misinput! MISINPUT!", + "Oops", + "Vaxry is going to be upset.", + "Who tried dividing by zero?!", + "Maybe you should try dusting your PC in the meantime?", + "I tried so hard, and got so far...", + "I don't feel so good...", + "*thud*", + "Well this is awkward.", + "\"stable\"", + "I hope you didn't have any unsaved progress.", + "All these computers..."}; // is not async-signal-safe, fake it with time(NULL) instead -static char const* getRandomMessage() { +char const* getRandomMessage() { return MESSAGES[time(nullptr) % (sizeof(MESSAGES) / sizeof(MESSAGES[0]))]; } -[[noreturn]] static inline void exitWithError(char const* err) { +[[noreturn]] inline void exitWithError(char const* err) { write(STDERR_FILENO, err, strlen(err)); // perror() is not signal-safe, but we use it here // because if the crash-handler already crashed, it can't get any worse. @@ -50,17 +43,17 @@ static char const* getRandomMessage() { abort(); } -void CrashReporter::createAndSaveCrash(int sig) { +void NCrashReporter::createAndSaveCrash(int sig) { int reportFd = -1; // We're in the signal handler, so we *only* have stack memory. // To save as much stack memory as possible, // destroy things as soon as possible. { - SignalSafe::CMaxLengthCString<255> reportPath; + CMaxLengthCString<255> reportPath; - const auto HOME = SignalSafe::getenv("HOME"); - const auto CACHE_HOME = SignalSafe::getenv("XDG_CACHE_HOME"); + const auto HOME = sigGetenv("HOME"); + const auto CACHE_HOME = sigGetenv("XDG_CACHE_HOME"); if (CACHE_HOME && CACHE_HOME[0] != '\0') { reportPath += CACHE_HOME; @@ -74,30 +67,32 @@ void CrashReporter::createAndSaveCrash(int sig) { } int ret = mkdir(reportPath.getStr(), S_IRWXU); - if (ret < 0 && errno != EEXIST) + //__asm__("int $3"); + if (ret < 0 && errno != EEXIST) { exitWithError("failed to mkdir() crash report directory\n"); - + } reportPath += "/hyprlandCrashReport"; reportPath.writeNum(getpid()); reportPath += ".txt"; { - SignalSafe::CBufFileWriter<64> stderrOut(STDERR_FILENO); - stderrOut += "Hyprland has crashed :( Consult the crash report at "; - if (!reportPath.boundsExceeded()) - stderrOut += reportPath.getStr(); - else - stderrOut += "[ERROR: Crash report path does not fit into memory! Check if your $CACHE_HOME/$HOME is too deeply nested. Max 255 characters.]"; - - stderrOut += " for more information.\n"; - stderrOut.flush(); + CBufFileWriter<64> stderr(2); + stderr += "Hyprland has crashed :( Consult the crash report at "; + if (!reportPath.boundsExceeded()) { + stderr += reportPath.getStr(); + } else { + stderr += "[ERROR: Crash report path does not fit into memory! Check if your $CACHE_HOME/$HOME is too deeply nested. Max 255 characters.]"; + } + stderr += " for more information.\n"; + stderr.flush(); } reportFd = open(reportPath.getStr(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); - if (reportFd < 0) + if (reportFd < 0) { exitWithError("Failed to open crash report path for writing"); + } } - SignalSafe::CBufFileWriter<512> finalCrashReport(reportFd); + CBufFileWriter<512> finalCrashReport(reportFd); finalCrashReport += "--------------------------------------------\n Hyprland Crash Report\n--------------------------------------------\n"; finalCrashReport += getRandomMessage(); @@ -106,7 +101,7 @@ void CrashReporter::createAndSaveCrash(int sig) { finalCrashReport += "Hyprland received signal "; finalCrashReport.writeNum(sig); finalCrashReport += '('; - finalCrashReport += SignalSafe::strsignal(sig); + finalCrashReport += sigStrsignal(sig); finalCrashReport += ")\nVersion: "; finalCrashReport += GIT_COMMIT_HASH; finalCrashReport += "\nTag: "; @@ -114,6 +109,9 @@ void CrashReporter::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 @@ -132,11 +130,11 @@ void CrashReporter::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'; } @@ -170,10 +168,6 @@ void CrashReporter::createAndSaveCrash(int sig) { finalCrashReport += "\n\nos-release:\n"; finalCrashReport.writeCmdOutput("cat /etc/os-release | sed 's/^/\t/'"); - finalCrashReport += '\n'; - finalCrashReport += getBuiltSystemLibraryNames(); - finalCrashReport += '\n'; - // dladdr1()/backtrace_symbols()/this entire section allocates, and hence is NOT async-signal-safe. // Make sure that we save the current known crash report information, // so that if we are caught in a deadlock during a call to malloc(), @@ -198,7 +192,7 @@ void CrashReporter::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); @@ -215,8 +209,8 @@ void CrashReporter::createAndSaveCrash(int sig) { // convert in memory address to VMA address Dl_info info; struct link_map* linkMap; - dladdr1(CALLSTACK[i].adr, &info, rc(&linkMap), RTLD_DL_LINKMAP); - size_t vmaAddr = rc(CALLSTACK[i].adr) - linkMap->l_addr; + dladdr1((void*)CALLSTACK[i].adr, &info, (void**)&linkMap, RTLD_DL_LINKMAP); + size_t vmaAddr = (size_t)CALLSTACK[i].adr - linkMap->l_addr; #else // musl doesn't define dladdr1 size_t vmaAddr = (size_t)CALLSTACK[i].adr; @@ -248,5 +242,5 @@ void CrashReporter::createAndSaveCrash(int sig) { finalCrashReport += "\n\nLog tail:\n"; - finalCrashReport += Log::logger->rolling(); + finalCrashReport += std::string_view(Debug::rollingLog).substr(Debug::rollingLog.find('\n') + 1); } diff --git a/src/debug/crash/CrashReporter.hpp b/src/debug/CrashReporter.hpp similarity index 50% rename from src/debug/crash/CrashReporter.hpp rename to src/debug/CrashReporter.hpp index 661f702f..0ba48e7c 100644 --- a/src/debug/crash/CrashReporter.hpp +++ b/src/debug/CrashReporter.hpp @@ -1,5 +1,7 @@ #pragma once -namespace CrashReporter { +#include "../defines.hpp" + +namespace NCrashReporter { void createAndSaveCrash(int sig); }; \ No newline at end of file diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 3f51e8df..3f955f6c 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -1,5 +1,4 @@ #include "HyprCtl.hpp" -#include "helpers/Monitor.hpp" #include #include @@ -18,7 +17,6 @@ #include #include #include -#include #include #include @@ -26,7 +24,6 @@ #include #include -#include using namespace Hyprutils::String; using namespace Hyprutils::OS; #include @@ -40,50 +37,20 @@ using namespace Hyprutils::OS; #include "../devices/ITouch.hpp" #include "../devices/Tablet.hpp" #include "../protocols/GlobalShortcuts.hpp" -#include "debug/log/RollingLogFollow.hpp" +#include "debug/RollingLogFollow.hpp" #include "config/ConfigManager.hpp" #include "helpers/MiscFunctions.hpp" -#include "../desktop/view/LayerSurface.hpp" -#include "../desktop/view/Group.hpp" -#include "../desktop/rule/Engine.hpp" -#include "../desktop/history/WindowHistoryTracker.hpp" -#include "../desktop/state/FocusState.hpp" +#include "../desktop/LayerSurface.hpp" #include "../version.h" #include "../Compositor.hpp" #include "../managers/input/InputManager.hpp" #include "../managers/XWaylandManager.hpp" +#include "../managers/LayoutManager.hpp" #include "../plugins/PluginSystem.hpp" -#include "../managers/animation/AnimationManager.hpp" +#include "../managers/AnimationManager.hpp" #include "../debug/HyprNotificationOverlay.hpp" #include "../render/Renderer.hpp" -#include "../render/OpenGL.hpp" -#include "../layout/space/Space.hpp" -#include "../layout/algorithm/Algorithm.hpp" -#include "../layout/algorithm/TiledAlgorithm.hpp" -#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp" - -#if defined(__DragonFly__) || defined(__FreeBSD__) -#include -#define CRED_T xucred -#define CRED_LVL SOL_LOCAL -#define CRED_OPT LOCAL_PEERCRED -#define CRED_PID cr_pid -#elif defined(__NetBSD__) -#define CRED_T unpcbid -#define CRED_LVL SOL_LOCAL -#define CRED_OPT LOCAL_PEEREID -#define CRED_PID unp_pid -#else -#if defined(__OpenBSD__) -#define CRED_T sockpeercred -#else -#define CRED_T ucred -#endif -#define CRED_LVL SOL_SOCKET -#define CRED_OPT SO_PEERCRED -#define CRED_PID pid -#endif static void trimTrailingComma(std::string& str) { if (!str.empty() && str.back() == ',') @@ -105,7 +72,7 @@ static std::string formatToString(uint32_t drmFormat) { static std::string availableModesForOutput(PHLMONITOR pMonitor, eHyprCtlOutputFormat format) { std::string result; - for (auto const& m : pMonitor->m_output->modes) { + for (auto const& m : pMonitor->output->modes) { if (format == FORMAT_NORMAL) result += std::format("{}x{}@{:.2f}Hz ", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0); else @@ -117,94 +84,9 @@ static std::string availableModesForOutput(PHLMONITOR pMonitor, eHyprCtlOutputFo return result; } -const std::array SOLITARY_REASONS_JSON = { - "\"UNKNOWN\"", "\"NOTIFICATION\"", "\"LOCK\"", "\"WORKSPACE\"", "\"WINDOWED\"", "\"DND\"", "\"SPECIAL\"", "\"ALPHA\"", "\"OFFSET\"", - "\"CANDIDATE\"", "\"OPAQUE\"", "\"TRANSFORM\"", "\"OVERLAYS\"", "\"FLOAT\"", "\"WORKSPACES\"", "\"SURFACES\"", "\"CONFIGERROR\"", -}; - -const std::array SOLITARY_REASONS_TEXT = { - "unknown reason", "notification", "session lock", "invalid workspace", "windowed mode", "dnd active", - "special workspace", "alpha channel", "workspace offset", "missing candidate", "not opaque", "surface transformations", - "other overlays", "floating windows", "other workspaces", "subsurfaces", "config error", -}; - -std::string CHyprCtl::getSolitaryBlockedReason(Hyprutils::Memory::CSharedPointer m, eHyprCtlOutputFormat format) { - const auto reasons = m->isSolitaryBlocked(true); - if (!reasons) - return "null"; - - std::string reasonStr = ""; - const auto TEXTS = format == eHyprCtlOutputFormat::FORMAT_JSON ? SOLITARY_REASONS_JSON : SOLITARY_REASONS_TEXT; - - for (uint32_t i = 0; i < CMonitor::SC_CHECKS_COUNT; i++) { - if (reasons & (1 << i)) { - if (reasonStr != "") - reasonStr += ","; - reasonStr += TEXTS[i]; - } - } - - return format == eHyprCtlOutputFormat::FORMAT_JSON ? "[" + reasonStr + "]" : reasonStr; -} - -const std::array DS_REASONS_JSON = { - "\"UNKNOWN\"", "\"USER\"", "\"WINDOWED\"", "\"CONTENT\"", "\"MIRROR\"", "\"RECORD\"", "\"SW\"", - "\"CANDIDATE\"", "\"SURFACE\"", "\"TRANSFORM\"", "\"DMA\"", "\"FAILED\"", "\"CM\"", -}; - -const std::array DS_REASONS_TEXT = { - "unknown reason", "user settings", "windowed mode", "content type", "monitor mirrors", "screen record/screenshot", "software renders/cursors", - "missing candidate", "invalid surface", "surface transformations", "invalid buffer", "activation failed", "color management", -}; - -std::string CHyprCtl::getDSBlockedReason(Hyprutils::Memory::CSharedPointer m, eHyprCtlOutputFormat format) { - const auto reasons = m->isDSBlocked(true); - if (!reasons) - return "null"; - - std::string reasonStr = ""; - const auto TEXTS = format == eHyprCtlOutputFormat::FORMAT_JSON ? DS_REASONS_JSON : DS_REASONS_TEXT; - - for (int i = 0; i < CMonitor::DS_CHECKS_COUNT; i++) { - if (reasons & (1 << i)) { - if (reasonStr != "") - reasonStr += ","; - reasonStr += TEXTS[i]; - } - } - - return format == eHyprCtlOutputFormat::FORMAT_JSON ? "[" + reasonStr + "]" : reasonStr; -} - -const std::array TEARING_REASONS_JSON = { - "\"UNKNOWN\"", "\"NOT_TORN\"", "\"USER\"", "\"ZOOM\"", "\"SUPPORT\"", "\"CANDIDATE\"", "\"WINDOW\"", "\"HW_CURSOR\"", -}; - -const std::array TEARING_REASONS_TEXT = {"unknown reason", "next frame is not torn", "user settings", "zoom", - "not supported by monitor", "missing candidate", "window settings", "hw cursor"}; - -std::string CHyprCtl::getTearingBlockedReason(Hyprutils::Memory::CSharedPointer m, eHyprCtlOutputFormat format) { - const auto reasons = m->isTearingBlocked(true); - if (!reasons || (reasons == CMonitor::TC_NOT_TORN && m->m_tearingState.activelyTearing)) - return "null"; - - std::string reasonStr = ""; - const auto TEXTS = format == eHyprCtlOutputFormat::FORMAT_JSON ? TEARING_REASONS_JSON : TEARING_REASONS_TEXT; - - for (int i = 0; i < CMonitor::TC_CHECKS_COUNT; i++) { - if (reasons & (1 << i)) { - if (reasonStr != "") - reasonStr += ","; - reasonStr += TEXTS[i]; - } - } - - return format == eHyprCtlOutputFormat::FORMAT_JSON ? "[" + reasonStr + "]" : reasonStr; -} - std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer m, eHyprCtlOutputFormat format) { std::string result; - if (!m->m_output || m->m_id == -1) + if (!m->output || m->ID == -1) return ""; if (format == eHyprCtlOutputFormat::FORMAT_JSON) { @@ -219,8 +101,6 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer "serial": "{}", "width": {}, "height": {}, - "physicalWidth": {}, - "physicalHeight": {}, "refreshRate": {:.5f}, "x": {}, "y": {}, @@ -239,51 +119,35 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer "dpmsStatus": {}, "vrr": {}, "solitary": "{:x}", - "solitaryBlockedBy": {}, "activelyTearing": {}, - "tearingBlockedBy": {}, "directScanoutTo": "{:x}", - "directScanoutBlockedBy": {}, "disabled": {}, "currentFormat": "{}", "mirrorOf": "{}", - "availableModes": [{}], - "colorManagementPreset": "{}", - "sdrBrightness": {:.2f}, - "sdrSaturation": {:.2f}, - "sdrMinLuminance": {:.2f}, - "sdrMaxLuminance": {} + "availableModes": [{}] }},)#", - m->m_id, escapeJSONStrings(m->m_name), escapeJSONStrings(m->m_shortDescription), escapeJSONStrings(m->m_output->make), escapeJSONStrings(m->m_output->model), - escapeJSONStrings(m->m_output->serial), sc(m->m_pixelSize.x), sc(m->m_pixelSize.y), sc(m->m_output->physicalSize.x), - sc(m->m_output->physicalSize.y), m->m_refreshRate, sc(m->m_position.x), sc(m->m_position.y), m->activeWorkspaceID(), - (!m->m_activeWorkspace ? "" : escapeJSONStrings(m->m_activeWorkspace->m_name)), m->activeSpecialWorkspaceID(), - escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), sc(m->m_reservedArea.left()), sc(m->m_reservedArea.top()), - sc(m->m_reservedArea.right()), sc(m->m_reservedArea.bottom()), m->m_scale, sc(m->m_transform), - (m == Desktop::focusState()->monitor() ? "true" : "false"), (m->m_dpmsStatus ? "true" : "false"), (m->m_output->state->state().adaptiveSync ? "true" : "false"), - rc(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), (m->m_tearingState.activelyTearing ? "true" : "false"), - getTearingBlockedReason(m, format), rc(m->m_lastScanout.get()), getDSBlockedReason(m, format), (m->m_enabled ? "false" : "true"), - formatToString(m->m_output->state->state().drmFormat), m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format), - (NCMType::toString(m->m_cmType)), (m->m_sdrBrightness), (m->m_sdrSaturation), (m->m_sdrMinLuminance), (m->m_sdrMaxLuminance)); + m->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szShortDescription), escapeJSONStrings(m->output->make), escapeJSONStrings(m->output->model), + escapeJSONStrings(m->output->serial), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, + m->activeWorkspaceID(), (!m->activeWorkspace ? "" : escapeJSONStrings(m->activeWorkspace->m_szName)), m->activeSpecialWorkspaceID(), + escapeJSONStrings(m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, + (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (m == g_pCompositor->m_pLastMonitor ? "true" : "false"), + (m->dpmsStatus ? "true" : "false"), (m->output->state->state().adaptiveSync ? "true" : "false"), (uint64_t)m->solitaryClient.get(), + (m->tearingState.activelyTearing ? "true" : "false"), (uint64_t)m->lastScanout.get(), (m->m_bEnabled ? "false" : "true"), + formatToString(m->output->state->state().drmFormat), m->pMirrorOf ? std::format("{}", m->pMirrorOf->ID) : "none", availableModesForOutput(m, format)); } else { - result += std::format( - "Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tphysical size (mm): {}x{}\n\tserial: {}\n\tactive workspace: {} ({})\n\t" - "special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t" - "dpmsStatus: {}\n\tvrr: {}\n\tsolitary: {:x}\n\tsolitaryBlockedBy: {}\n\tactivelyTearing: {}\n\ttearingBlockedBy: {}\n\tdirectScanoutTo: " - "{:x}\n\tdirectScanoutBlockedBy: {}\n\tdisabled: " - "{}\n\tcurrentFormat: {}\n\tmirrorOf: " - "{}\n\tavailableModes: {}\n\tcolorManagementPreset: {}\n\tsdrBrightness: {:.2f}\n\tsdrSaturation: {:.2f}\n\tsdrMinLuminance: {:.2f}\n\tsdrMaxLuminance: {}\n\n", - m->m_name, m->m_id, sc(m->m_pixelSize.x), sc(m->m_pixelSize.y), m->m_refreshRate, sc(m->m_position.x), sc(m->m_position.y), m->m_shortDescription, - m->m_output->make, m->m_output->model, sc(m->m_output->physicalSize.x), sc(m->m_output->physicalSize.y), m->m_output->serial, m->activeWorkspaceID(), - (!m->m_activeWorkspace ? "" : m->m_activeWorkspace->m_name), m->activeSpecialWorkspaceID(), (m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), - sc(m->m_reservedArea.left()), sc(m->m_reservedArea.top()), sc(m->m_reservedArea.right()), sc(m->m_reservedArea.bottom()), m->m_scale, - sc(m->m_transform), (m == Desktop::focusState()->monitor() ? "yes" : "no"), sc(m->m_dpmsStatus), m->m_output->state->state().adaptiveSync, - rc(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), m->m_tearingState.activelyTearing, getTearingBlockedReason(m, format), - rc(m->m_lastScanout.get()), getDSBlockedReason(m, format), !m->m_enabled, formatToString(m->m_output->state->state().drmFormat), - m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format), (NCMType::toString(m->m_cmType)), (m->m_sdrBrightness), - (m->m_sdrSaturation), (m->m_sdrMinLuminance), (m->m_sdrMaxLuminance)); + result += std::format("Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t" + "special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t" + "dpmsStatus: {}\n\tvrr: {}\n\tsolitary: {:x}\n\tactivelyTearing: {}\n\tdirectScanoutTo: {:x}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tmirrorOf: " + "{}\n\tavailableModes: {}\n\n", + m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription, + m->output->make, m->output->model, m->output->serial, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName), + m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x, + (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, + (m == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, m->output->state->state().adaptiveSync, (uint64_t)m->solitaryClient.get(), + m->tearingState.activelyTearing, (uint64_t)m->lastScanout.get(), !m->m_bEnabled, formatToString(m->output->state->state().drmFormat), + m->pMirrorOf ? std::format("{}", m->pMirrorOf->ID) : "none", availableModesForOutput(m, format)); } return result; @@ -303,7 +167,7 @@ static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string requ if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; - for (auto const& m : allMonitors ? g_pCompositor->m_realMonitors : g_pCompositor->m_monitors) { + for (auto const& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) { result += CHyprCtl::getMonitorData(m, format); } @@ -311,8 +175,8 @@ static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string requ result += "]"; } else { - for (auto const& m : allMonitors ? g_pCompositor->m_realMonitors : g_pCompositor->m_monitors) { - if (!m->m_output || m->m_id == -1) + for (auto const& m : allMonitors ? g_pCompositor->m_vRealMonitors : g_pCompositor->m_vMonitors) { + if (!m->output || m->ID == -1) continue; result += CHyprCtl::getMonitorData(m, format); @@ -323,30 +187,34 @@ static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string requ } static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) { - const auto tags = w->m_ruleApplicator->m_tagKeeper.getTags(); + const auto tags = w->m_tags.getTags(); if (format == eHyprCtlOutputFormat::FORMAT_JSON) - return std::ranges::fold_left(tags, std::string(), - [](const std::string& a, const std::string& b) { return a.empty() ? std::format("\"{}\"", b) : std::format("{}, \"{}\"", a, b); }); + return std::accumulate(tags.begin(), tags.end(), std::string(), + [](const std::string& a, const std::string& b) { return a.empty() ? std::format("\"{}\"", b) : std::format("{}, \"{}\"", a, b); }); else - return std::ranges::fold_left(tags, std::string(), [](const std::string& a, const std::string& b) { return a.empty() ? b : a + ", " + b; }); + return std::accumulate(tags.begin(), tags.end(), std::string(), [](const std::string& a, const std::string& b) { return a.empty() ? b : a + ", " + b; }); } static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) { const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON; - if (!w->m_group) + if (w->m_sGroupData.pNextWindow.expired()) return isJson ? "" : "0"; std::ostringstream result; - for (const auto& curr : w->m_group->windows()) { + PHLWINDOW head = w->getGroupHead(); + PHLWINDOW curr = head; + while (true) { if (isJson) - result << std::format("\"0x{:x}\"", rc(curr.get())); + result << std::format("\"0x{:x}\"", (uintptr_t)curr.get()); else - result << std::format("{:x}", rc(curr.get())); - - if (curr != w->m_group->windows().back()) - result << (isJson ? ", " : ","); + result << std::format("{:x}", (uintptr_t)curr.get()); + curr = curr->m_sGroupData.pNextWindow.lock(); + // We've wrapped around to the start, break out without trailing comma + if (curr == head) + break; + result << (isJson ? ", " : ","); } return result.str(); @@ -354,10 +222,9 @@ static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) { std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { auto getFocusHistoryID = [](PHLWINDOW wnd) -> int { - const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); - for (size_t i = 0; i < HISTORY.size(); ++i) { - if (HISTORY[i].lock() == wnd) - return HISTORY.size() - i - 1; // reverse order for backwards compat + for (size_t i = 0; i < g_pCompositor->m_vWindowFocusHistory.size(); ++i) { + if (g_pCompositor->m_vWindowFocusHistory[i].lock() == wnd) + return i; } return -1; }; @@ -375,6 +242,7 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { "name": "{}" }}, "floating": {}, + "pseudo": {}, "monitor": {}, "class": "{}", "title": "{}", @@ -385,39 +253,31 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { "pinned": {}, "fullscreen": {}, "fullscreenClient": {}, - "overFullscreen": {}, "grouped": [{}], "tags": [{}], "swallowing": "0x{:x}", "focusHistoryID": {}, - "inhibitingIdle": {}, - "xdgTag": "{}", - "xdgDescription": "{}", - "contentType": "{}", - "stableId": "{:x}" + "inhibitingIdle": {} }},)#", - rc(w.get()), (w->m_isMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), sc(w->m_realPosition->goal().x), - sc(w->m_realPosition->goal().y), sc(w->m_realSize->goal().x), sc(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID, - escapeJSONStrings(!w->m_workspace ? "" : w->m_workspace->m_name), (sc(w->m_isFloating) == 1 ? "true" : "false"), w->monitorID(), escapeJSONStrings(w->m_class), - escapeJSONStrings(w->m_title), escapeJSONStrings(w->m_initialClass), escapeJSONStrings(w->m_initialTitle), w->getPID(), (sc(w->m_isX11) == 1 ? "true" : "false"), - (w->m_pinned ? "true" : "false"), sc(w->m_fullscreenState.internal), sc(w->m_fullscreenState.client), (w->m_createdOverFullscreen ? "true" : "false"), - getGroupedData(w, format), getTagsData(w, format), rc(w->m_swallowed.get()), getFocusHistoryID(w), - (g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), escapeJSONStrings(w->xdgDescription().value_or("")), - escapeJSONStrings(NContentType::toString(w->getContentType())), w->m_stableID); + (uintptr_t)w.get(), (w->m_bIsMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), (int)w->m_vRealPosition->goal().x, (int)w->m_vRealPosition->goal().y, + (int)w->m_vRealSize->goal().x, (int)w->m_vRealSize->goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, + escapeJSONStrings(!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), ((int)w->m_bIsFloating == 1 ? "true" : "false"), (w->m_bIsPseudotiled ? "true" : "false"), + (int64_t)w->monitorID(), escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass), + escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), + (uint8_t)w->m_sFullscreenState.internal, (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.get(), + getFocusHistoryID(w), (g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false")); } else { return std::format( - "Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: " + "Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: " "{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: " "{}\n\txwayland: {}\n\tpinned: " - "{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\toverFullscreen: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: " - "{}\n\txdgTag: " - "{}\n\txdgDescription: {}\n\tcontentType: {}\n\tstableID: {:x}\n\n", - rc(w.get()), w->m_title, sc(w->m_isMapped), sc(w->isHidden()), sc(w->m_realPosition->goal().x), sc(w->m_realPosition->goal().y), - sc(w->m_realSize->goal().x), sc(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID, - (!w->m_workspace ? "" : w->m_workspace->m_name), sc(w->m_isFloating), w->monitorID(), w->m_class, w->m_title, w->m_initialClass, w->m_initialTitle, w->getPID(), - sc(w->m_isX11), sc(w->m_pinned), sc(w->m_fullscreenState.internal), sc(w->m_fullscreenState.client), sc(w->m_createdOverFullscreen), - getGroupedData(w, format), getTagsData(w, format), rc(w->m_swallowed.get()), getFocusHistoryID(w), sc(g_pInputManager->isWindowInhibiting(w, false)), - w->xdgTag().value_or(""), w->xdgDescription().value_or(""), NContentType::toString(w->getContentType()), w->m_stableID); + "{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: {}\n\n", + (uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition->goal().x, (int)w->m_vRealPosition->goal().y, + (int)w->m_vRealSize->goal().x, (int)w->m_vRealSize->goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, + (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int)w->m_bIsPseudotiled, (int64_t)w->monitorID(), w->m_szClass, w->m_szTitle, + w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (uint8_t)w->m_sFullscreenState.internal, + (uint8_t)w->m_sFullscreenState.client, getGroupedData(w, format), getTagsData(w, format), (uintptr_t)w->m_pSwallowed.get(), getFocusHistoryID(w), + (int)g_pInputManager->isWindowInhibiting(w, false)); } } @@ -426,8 +286,8 @@ static std::string clientsRequest(eHyprCtlOutputFormat format, std::string reque if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_isMapped && !g_pHyprCtl->m_currentRequestParams.all) + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped && !g_pHyprCtl->m_sCurrentRequestParams.all) continue; result += CHyprCtl::getWindowData(w, format); @@ -437,29 +297,19 @@ static std::string clientsRequest(eHyprCtlOutputFormat format, std::string reque result += "]"; } else { - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_isMapped && !g_pHyprCtl->m_currentRequestParams.all) + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped && !g_pHyprCtl->m_sCurrentRequestParams.all) continue; result += CHyprCtl::getWindowData(w, format); } - - if (result.empty()) - return "no open windows"; } return result; } std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format) { - const auto PLASTW = w->getLastFocusedWindow(); - const auto PMONITOR = w->m_monitor.lock(); - - std::string layoutName = "unknown"; - if (w->m_space && w->m_space->algorithm() && w->m_space->algorithm()->tiledAlgo()) { - const auto& TILED_ALGO = w->m_space->algorithm()->tiledAlgo(); - layoutName = Layout::Supplementary::algoMatcher()->getNameForTiledAlgo(&typeid(*TILED_ALGO.get())); - } - + const auto PLASTW = w->getLastFocusedWindow(); + const auto PMONITOR = w->m_pMonitor.lock(); if (format == eHyprCtlOutputFormat::FORMAT_JSON) { return std::format(R"#({{ "id": {}, @@ -470,17 +320,16 @@ std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat form "hasfullscreen": {}, "lastwindow": "0x{:x}", "lastwindowtitle": "{}", - "ispersistent": {}, - "tiledLayout": "{}" + "ispersistent": {} }})#", - w->m_id, escapeJSONStrings(w->m_name), escapeJSONStrings(PMONITOR ? PMONITOR->m_name : "?"), - escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->m_id) : "null"), w->getWindows(), w->m_hasFullscreenWindow ? "true" : "false", - rc(PLASTW.get()), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "", w->isPersistent() ? "true" : "false", escapeJSONStrings(layoutName)); + w->m_iID, escapeJSONStrings(w->m_szName), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?"), + escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->ID) : "null"), w->getWindows(), w->m_bHasFullscreenWindow ? "true" : "false", + (uintptr_t)PLASTW.get(), PLASTW ? escapeJSONStrings(PLASTW->m_szTitle) : "", w->m_bPersistent ? "true" : "false"); } else { - return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: " - "{}\n\ttiledLayout: {}\n\n", - w->m_id, w->m_name, PMONITOR ? PMONITOR->m_name : "?", PMONITOR ? std::to_string(PMONITOR->m_id) : "null", w->getWindows(), - sc(w->m_hasFullscreenWindow), rc(PLASTW.get()), PLASTW ? PLASTW->m_title : "", sc(w->isPersistent()), layoutName); + return std::format( + "workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: {}\n\n", + w->m_iID, w->m_szName, PMONITOR ? PMONITOR->szName : "?", PMONITOR ? std::to_string(PMONITOR->ID) : "null", w->getWindows(), (int)w->m_bHasFullscreenWindow, + (uintptr_t)PLASTW.get(), PLASTW ? PLASTW->m_szTitle : "", (int)w->m_bPersistent); } } @@ -488,19 +337,19 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputF const auto boolToString = [](const bool b) -> std::string { return b ? "true" : "false"; }; if (format == eHyprCtlOutputFormat::FORMAT_JSON) { const std::string monitor = r.monitor.empty() ? "" : std::format(",\n \"monitor\": \"{}\"", escapeJSONStrings(r.monitor)); - const std::string default_ = sc(r.isDefault) ? std::format(",\n \"default\": {}", boolToString(r.isDefault)) : ""; - const std::string persistent = sc(r.isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.isPersistent)) : ""; - const std::string gapsIn = sc(r.gapsIn) ? - std::format(",\n \"gapsIn\": [{}, {}, {}, {}]", r.gapsIn.value().m_top, r.gapsIn.value().m_right, r.gapsIn.value().m_bottom, r.gapsIn.value().m_left) : + const std::string default_ = (bool)(r.isDefault) ? std::format(",\n \"default\": {}", boolToString(r.isDefault)) : ""; + const std::string persistent = (bool)(r.isPersistent) ? std::format(",\n \"persistent\": {}", boolToString(r.isPersistent)) : ""; + const std::string gapsIn = (bool)(r.gapsIn) ? + std::format(",\n \"gapsIn\": [{}, {}, {}, {}]", r.gapsIn.value().top, r.gapsIn.value().right, r.gapsIn.value().bottom, r.gapsIn.value().left) : ""; - const std::string gapsOut = sc(r.gapsOut) ? - std::format(",\n \"gapsOut\": [{}, {}, {}, {}]", r.gapsOut.value().m_top, r.gapsOut.value().m_right, r.gapsOut.value().m_bottom, r.gapsOut.value().m_left) : + const std::string gapsOut = (bool)(r.gapsOut) ? + std::format(",\n \"gapsOut\": [{}, {}, {}, {}]", r.gapsOut.value().top, r.gapsOut.value().right, r.gapsOut.value().bottom, r.gapsOut.value().left) : ""; - const std::string borderSize = sc(r.borderSize) ? std::format(",\n \"borderSize\": {}", r.borderSize.value()) : ""; - const std::string border = sc(r.noBorder) ? std::format(",\n \"border\": {}", boolToString(!r.noBorder.value())) : ""; - const std::string rounding = sc(r.noRounding) ? std::format(",\n \"rounding\": {}", boolToString(!r.noRounding.value())) : ""; - const std::string decorate = sc(r.decorate) ? std::format(",\n \"decorate\": {}", boolToString(r.decorate.value())) : ""; - const std::string shadow = sc(r.noShadow) ? std::format(",\n \"shadow\": {}", boolToString(!r.noShadow.value())) : ""; + const std::string borderSize = (bool)(r.borderSize) ? std::format(",\n \"borderSize\": {}", r.borderSize.value()) : ""; + const std::string border = (bool)(r.noBorder) ? std::format(",\n \"border\": {}", boolToString(!r.noBorder.value())) : ""; + const std::string rounding = (bool)(r.noRounding) ? std::format(",\n \"rounding\": {}", boolToString(!r.noRounding.value())) : ""; + const std::string decorate = (bool)(r.decorate) ? std::format(",\n \"decorate\": {}", boolToString(r.decorate.value())) : ""; + const std::string shadow = (bool)(r.noShadow) ? std::format(",\n \"shadow\": {}", boolToString(!r.noShadow.value())) : ""; const std::string defaultName = r.defaultName.has_value() ? std::format(",\n \"defaultName\": \"{}\"", escapeJSONStrings(r.defaultName.value())) : ""; std::string result = @@ -511,21 +360,20 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputF return result; } else { - const std::string monitor = std::format("\tmonitor: {}\n", r.monitor.empty() ? "" : escapeJSONStrings(r.monitor)); - const std::string default_ = std::format("\tdefault: {}\n", sc(r.isDefault) ? boolToString(r.isDefault) : ""); - const std::string persistent = std::format("\tpersistent: {}\n", sc(r.isPersistent) ? boolToString(r.isPersistent) : ""); - const std::string gapsIn = sc(r.gapsIn) ? std::format("\tgapsIn: {} {} {} {}\n", std::to_string(r.gapsIn.value().m_top), std::to_string(r.gapsIn.value().m_right), - std::to_string(r.gapsIn.value().m_bottom), std::to_string(r.gapsIn.value().m_left)) : - std::format("\tgapsIn: \n"); - const std::string gapsOut = sc(r.gapsOut) ? - std::format("\tgapsOut: {} {} {} {}\n", std::to_string(r.gapsOut.value().m_top), std::to_string(r.gapsOut.value().m_right), std::to_string(r.gapsOut.value().m_bottom), - std::to_string(r.gapsOut.value().m_left)) : - std::format("\tgapsOut: \n"); - const std::string borderSize = std::format("\tborderSize: {}\n", sc(r.borderSize) ? std::to_string(r.borderSize.value()) : ""); - const std::string border = std::format("\tborder: {}\n", sc(r.noBorder) ? boolToString(!r.noBorder.value()) : ""); - const std::string rounding = std::format("\trounding: {}\n", sc(r.noRounding) ? boolToString(!r.noRounding.value()) : ""); - const std::string decorate = std::format("\tdecorate: {}\n", sc(r.decorate) ? boolToString(r.decorate.value()) : ""); - const std::string shadow = std::format("\tshadow: {}\n", sc(r.noShadow) ? boolToString(!r.noShadow.value()) : ""); + const std::string monitor = std::format("\tmonitor: {}\n", r.monitor.empty() ? "" : escapeJSONStrings(r.monitor)); + const std::string default_ = std::format("\tdefault: {}\n", (bool)(r.isDefault) ? boolToString(r.isDefault) : ""); + const std::string persistent = std::format("\tpersistent: {}\n", (bool)(r.isPersistent) ? boolToString(r.isPersistent) : ""); + const std::string gapsIn = (bool)(r.gapsIn) ? std::format("\tgapsIn: {} {} {} {}\n", std::to_string(r.gapsIn.value().top), std::to_string(r.gapsIn.value().right), + std::to_string(r.gapsIn.value().bottom), std::to_string(r.gapsIn.value().left)) : + std::format("\tgapsIn: \n"); + const std::string gapsOut = (bool)(r.gapsOut) ? std::format("\tgapsOut: {} {} {} {}\n", std::to_string(r.gapsOut.value().top), std::to_string(r.gapsOut.value().right), + std::to_string(r.gapsOut.value().bottom), std::to_string(r.gapsOut.value().left)) : + std::format("\tgapsOut: \n"); + const std::string borderSize = std::format("\tborderSize: {}\n", (bool)(r.borderSize) ? std::to_string(r.borderSize.value()) : ""); + const std::string border = std::format("\tborder: {}\n", (bool)(r.noBorder) ? boolToString(!r.noBorder.value()) : ""); + const std::string rounding = std::format("\trounding: {}\n", (bool)(r.noRounding) ? boolToString(!r.noRounding.value()) : ""); + const std::string decorate = std::format("\tdecorate: {}\n", (bool)(r.decorate) ? boolToString(r.decorate.value()) : ""); + const std::string shadow = std::format("\tshadow: {}\n", (bool)(r.noShadow) ? boolToString(!r.noShadow.value()) : ""); const std::string defaultName = std::format("\tdefaultName: {}\n", r.defaultName.value_or("")); std::string result = std::format("Workspace rule {}:\n{}{}{}{}{}{}{}{}{}{}{}\n", escapeJSONStrings(r.workspaceString), monitor, default_, persistent, gapsIn, gapsOut, @@ -536,11 +384,11 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputF } static std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) { - if (!Desktop::focusState()->monitor()) + if (!g_pCompositor->m_pLastMonitor) return "unsafe state"; std::string result = ""; - auto w = Desktop::focusState()->monitor()->m_activeWorkspace; + auto w = g_pCompositor->m_pLastMonitor->activeWorkspace; if (!valid(w)) return "internal error"; @@ -553,16 +401,16 @@ static std::string workspacesRequest(eHyprCtlOutputFormat format, std::string re if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; - for (auto const& w : g_pCompositor->getWorkspaces()) { - result += CHyprCtl::getWorkspaceData(w.lock(), format); + for (auto const& w : g_pCompositor->m_vWorkspaces) { + result += CHyprCtl::getWorkspaceData(w, format); result += ","; } trimTrailingComma(result); result += "]"; } else { - for (auto const& w : g_pCompositor->getWorkspaces()) { - result += CHyprCtl::getWorkspaceData(w.lock(), format); + for (auto const& w : g_pCompositor->m_vWorkspaces) { + result += CHyprCtl::getWorkspaceData(w, format); } } @@ -590,7 +438,7 @@ static std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::strin } static std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) { - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!validMapped(PWINDOW)) return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid"; @@ -609,15 +457,15 @@ static std::string layersRequest(eHyprCtlOutputFormat format, std::string reques if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "{\n"; - for (auto const& mon : g_pCompositor->m_monitors) { + for (auto const& mon : g_pCompositor->m_vMonitors) { result += std::format( R"#("{}": {{ "levels": {{ )#", - escapeJSONStrings(mon->m_name)); + escapeJSONStrings(mon->szName)); int layerLevel = 0; - for (auto const& level : mon->m_layerSurfaceLayers) { + for (auto const& level : mon->m_aLayerSurfaceLayers) { result += std::format( R"#( "{}": [ @@ -634,13 +482,13 @@ static std::string layersRequest(eHyprCtlOutputFormat format, std::string reques "namespace": "{}", "pid": {} }},)#", - rc(layer.get()), layer->m_geometry.x, layer->m_geometry.y, layer->m_geometry.width, layer->m_geometry.height, - escapeJSONStrings(layer->m_namespace), layer->getPID()); + (uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width, layer->geometry.height, escapeJSONStrings(layer->szNamespace), + layer->getPID()); } trimTrailingComma(result); - if (!level.empty()) + if (level.size() > 0) result += "\n "; result += "],"; @@ -658,16 +506,16 @@ static std::string layersRequest(eHyprCtlOutputFormat format, std::string reques result += "\n}\n"; } else { - for (auto const& mon : g_pCompositor->m_monitors) { - result += std::format("Monitor {}:\n", mon->m_name); + for (auto const& mon : g_pCompositor->m_vMonitors) { + result += std::format("Monitor {}:\n", mon->szName); int layerLevel = 0; static const std::array levelNames = {"background", "bottom", "top", "overlay"}; - for (auto const& level : mon->m_layerSurfaceLayers) { + for (auto const& level : mon->m_aLayerSurfaceLayers) { result += std::format("\tLayer level {} ({}):\n", layerLevel, levelNames[layerLevel]); for (auto const& layer : level) { - result += std::format("\t\tLayer {:x}: xywh: {} {} {} {}, namespace: {}, pid: {}\n", rc(layer.get()), layer->m_geometry.x, layer->m_geometry.y, - layer->m_geometry.width, layer->m_geometry.height, layer->m_namespace, layer->getPID()); + result += std::format("\t\tLayer {:x}: xywh: {} {} {} {}, namespace: {}, pid: {}\n", (uintptr_t)layer.get(), layer->geometry.x, layer->geometry.y, + layer->geometry.width, layer->geometry.height, layer->szNamespace, layer->getPID()); } layerLevel++; @@ -679,6 +527,28 @@ static std::string layersRequest(eHyprCtlOutputFormat format, std::string reques return result; } +static std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) { + std::string result = ""; + if (format == eHyprCtlOutputFormat::FORMAT_JSON) { + result += "["; + + for (auto const& m : g_pLayoutManager->getAllLayoutNames()) { + result += std::format( + R"#( + "{}",)#", + m); + } + trimTrailingComma(result); + + result += "\n]\n"; + } else { + for (auto const& m : g_pLayoutManager->getAllLayoutNames()) { + result += std::format("{}\n", m); + } + } + return result; +} + static std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = ""; std::string currErrors = g_pConfigManager->getErrors(); @@ -706,38 +576,35 @@ static std::string devicesRequest(eHyprCtlOutputFormat format, std::string reque std::string result = ""; auto getModState = [](SP keyboard, const char* xkbModName) -> bool { - auto IDX = xkb_keymap_mod_get_index(keyboard->m_xkbKeymap, xkbModName); + auto IDX = xkb_keymap_mod_get_index(keyboard->xkbKeymap, xkbModName); if (IDX == XKB_MOD_INVALID) return false; - return (keyboard->m_modifiersState.locked & (1 << IDX)) > 0; + return (keyboard->modifiersState.locked & (1 << IDX)) > 0; }; if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "{\n"; result += "\"mice\": [\n"; - for (auto const& m : g_pInputManager->m_pointers) { + for (auto const& m : g_pInputManager->m_vPointers) { result += std::format( R"#( {{ "address": "0x{:x}", "name": "{}", - "defaultSpeed": {:.5f}, - "scrollFactor": {:.2f} + "defaultSpeed": {:.5f} }},)#", - rc(m.get()), escapeJSONStrings(m->m_hlName), - m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f, m->m_scrollFactor.value_or(-1)); + (uintptr_t)m.get(), escapeJSONStrings(m->hlName), + m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f); } trimTrailingComma(result); result += "\n],\n"; result += "\"keyboards\": [\n"; - for (auto const& k : g_pInputManager->m_keyboards) { - const auto INDEX_OPT = k->getActiveLayoutIndex(); - const auto KI = INDEX_OPT.has_value() ? std::to_string(INDEX_OPT.value()) : "none"; - const auto KM = k->getActiveLayout(); + for (auto const& k : g_pInputManager->m_vKeyboards) { + const auto KM = k->getActiveLayout(); result += std::format( R"#( {{ "address": "0x{:x}", @@ -747,15 +614,14 @@ static std::string devicesRequest(eHyprCtlOutputFormat format, std::string reque "layout": "{}", "variant": "{}", "options": "{}", - "active_layout_index": {}, "active_keymap": "{}", "capsLock": {}, "numLock": {}, "main": {} }},)#", - rc(k.get()), escapeJSONStrings(k->m_hlName), escapeJSONStrings(k->m_currentRules.rules), escapeJSONStrings(k->m_currentRules.model), - escapeJSONStrings(k->m_currentRules.layout), escapeJSONStrings(k->m_currentRules.variant), escapeJSONStrings(k->m_currentRules.options), KI, escapeJSONStrings(KM), - (getModState(k, XKB_MOD_NAME_CAPS) ? "true" : "false"), (getModState(k, XKB_MOD_NAME_NUM) ? "true" : "false"), (k->m_active ? "true" : "false")); + (uintptr_t)k.get(), escapeJSONStrings(k->hlName), escapeJSONStrings(k->currentRules.rules), escapeJSONStrings(k->currentRules.model), + escapeJSONStrings(k->currentRules.layout), escapeJSONStrings(k->currentRules.variant), escapeJSONStrings(k->currentRules.options), escapeJSONStrings(KM), + (getModState(k, XKB_MOD_NAME_CAPS) ? "true" : "false"), (getModState(k, XKB_MOD_NAME_NUM) ? "true" : "false"), (k->active ? "true" : "false")); } trimTrailingComma(result); @@ -763,7 +629,7 @@ static std::string devicesRequest(eHyprCtlOutputFormat format, std::string reque result += "\"tablets\": [\n"; - for (auto const& d : g_pInputManager->m_tabletPads) { + for (auto const& d : g_pInputManager->m_vTabletPads) { result += std::format( R"#( {{ "address": "0x{:x}", @@ -773,25 +639,25 @@ static std::string devicesRequest(eHyprCtlOutputFormat format, std::string reque "name": "{}" }} }},)#", - rc(d.get()), rc(d->m_parent.get()), escapeJSONStrings(d->m_parent ? d->m_parent->m_hlName : "")); + (uintptr_t)d.get(), (uintptr_t)d->parent.get(), escapeJSONStrings(d->parent ? d->parent->hlName : "")); } - for (auto const& d : g_pInputManager->m_tablets) { + for (auto const& d : g_pInputManager->m_vTablets) { result += std::format( R"#( {{ "address": "0x{:x}", "name": "{}" }},)#", - rc(d.get()), escapeJSONStrings(d->m_hlName)); + (uintptr_t)d.get(), escapeJSONStrings(d->hlName)); } - for (auto const& d : g_pInputManager->m_tabletTools) { + for (auto const& d : g_pInputManager->m_vTabletTools) { result += std::format( R"#( {{ "address": "0x{:x}", - "type": "tabletTool" + "type": "tabletTool", }},)#", - rc(d.get())); + (uintptr_t)d.get()); } trimTrailingComma(result); @@ -799,13 +665,13 @@ static std::string devicesRequest(eHyprCtlOutputFormat format, std::string reque result += "\"touch\": [\n"; - for (auto const& d : g_pInputManager->m_touches) { + for (auto const& d : g_pInputManager->m_vTouches) { result += std::format( R"#( {{ "address": "0x{:x}", "name": "{}" }},)#", - rc(d.get()), escapeJSONStrings(d->m_hlName)); + (uintptr_t)d.get(), escapeJSONStrings(d->hlName)); } trimTrailingComma(result); @@ -813,13 +679,13 @@ static std::string devicesRequest(eHyprCtlOutputFormat format, std::string reque result += "\"switches\": [\n"; - for (auto const& d : g_pInputManager->m_switches) { + for (auto const& d : g_pInputManager->m_lSwitches) { result += std::format( R"#( {{ "address": "0x{:x}", "name": "{}" }},)#", - rc(&d), escapeJSONStrings(d.pDevice ? d.pDevice->getName() : "")); + (uintptr_t)&d, escapeJSONStrings(d.pDevice ? d.pDevice->getName() : "")); } trimTrailingComma(result); @@ -830,51 +696,46 @@ static std::string devicesRequest(eHyprCtlOutputFormat format, std::string reque } else { result += "mice:\n"; - for (auto const& m : g_pInputManager->m_pointers) { - result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n\t\t\tscroll factor: {:.2f}\n", rc(m.get()), m->m_hlName, - (m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f), - m->m_scrollFactor.value_or(-1)); + for (auto const& m : g_pInputManager->m_vPointers) { + result += std::format("\tMouse at {:x}:\n\t\t{}\n\t\t\tdefault speed: {:.5f}\n", (uintptr_t)m.get(), m->hlName, + (m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f)); } result += "\n\nKeyboards:\n"; - for (auto const& k : g_pInputManager->m_keyboards) { - const auto INDEX_OPT = k->getActiveLayoutIndex(); - const auto KI = INDEX_OPT.has_value() ? std::to_string(INDEX_OPT.value()) : "none"; - const auto KM = k->getActiveLayout(); - result += std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive layout index: {}\n\t\t\tactive keymap: " - "{}\n\t\t\tcapsLock: " - "{}\n\t\t\tnumLock: {}\n\t\t\tmain: {}\n", - rc(k.get()), k->m_hlName, k->m_currentRules.rules, k->m_currentRules.model, k->m_currentRules.layout, k->m_currentRules.variant, - k->m_currentRules.options, KI, KM, (getModState(k, XKB_MOD_NAME_CAPS) ? "yes" : "no"), (getModState(k, XKB_MOD_NAME_NUM) ? "yes" : "no"), - (k->m_active ? "yes" : "no")); + for (auto const& k : g_pInputManager->m_vKeyboards) { + const auto KM = k->getActiveLayout(); + result += + std::format("\tKeyboard at {:x}:\n\t\t{}\n\t\t\trules: r \"{}\", m \"{}\", l \"{}\", v \"{}\", o \"{}\"\n\t\t\tactive keymap: {}\n\t\t\tcapsLock: " + "{}\n\t\t\tnumLock: {}\n\t\t\tmain: {}\n", + (uintptr_t)k.get(), k->hlName, k->currentRules.rules, k->currentRules.model, k->currentRules.layout, k->currentRules.variant, k->currentRules.options, + KM, (getModState(k, XKB_MOD_NAME_CAPS) ? "yes" : "no"), (getModState(k, XKB_MOD_NAME_NUM) ? "yes" : "no"), (k->active ? "yes" : "no")); } result += "\n\nTablets:\n"; - for (auto const& d : g_pInputManager->m_tabletPads) { - result += - std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", rc(d.get()), rc(d->m_parent.get()), d->m_parent ? d->m_parent->m_hlName : ""); + for (auto const& d : g_pInputManager->m_vTabletPads) { + result += std::format("\tTablet Pad at {:x} (belongs to {:x} -> {})\n", (uintptr_t)d.get(), (uintptr_t)d->parent.get(), d->parent ? d->parent->hlName : ""); } - for (auto const& d : g_pInputManager->m_tablets) { - result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", rc(d.get()), d->m_hlName, d->aq()->physicalSize.x, d->aq()->physicalSize.y); + for (auto const& d : g_pInputManager->m_vTablets) { + result += std::format("\tTablet at {:x}:\n\t\t{}\n\t\t\tsize: {}x{}mm\n", (uintptr_t)d.get(), d->hlName, d->aq()->physicalSize.x, d->aq()->physicalSize.y); } - for (auto const& d : g_pInputManager->m_tabletTools) { - result += std::format("\tTablet Tool at {:x}\n", rc(d.get())); + for (auto const& d : g_pInputManager->m_vTabletTools) { + result += std::format("\tTablet Tool at {:x}\n", (uintptr_t)d.get()); } result += "\n\nTouch:\n"; - for (auto const& d : g_pInputManager->m_touches) { - result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", rc(d.get()), d->m_hlName); + for (auto const& d : g_pInputManager->m_vTouches) { + result += std::format("\tTouch Device at {:x}:\n\t\t{}\n", (uintptr_t)d.get(), d->hlName); } result += "\n\nSwitches:\n"; - for (auto const& d : g_pInputManager->m_switches) { - result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", rc(&d), d.pDevice ? d.pDevice->getName() : ""); + for (auto const& d : g_pInputManager->m_lSwitches) { + result += std::format("\tSwitch Device at {:x}:\n\t\t{}\n", (uintptr_t)&d, d.pDevice ? d.pDevice->getName() : ""); } } @@ -887,16 +748,14 @@ static std::string animationsRequest(eHyprCtlOutputFormat format, std::string re ret += "animations:\n"; for (auto const& ac : g_pConfigManager->getAnimationConfig()) { - ret += std::format("\n\tname: {}\n\t\toverriden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, sc(ac.second->overridden), + ret += std::format("\n\tname: {}\n\t\toverriden: {}\n\t\tbezier: {}\n\t\tenabled: {}\n\t\tspeed: {:.2f}\n\t\tstyle: {}\n", ac.first, (int)ac.second->overridden, ac.second->internalBezier, ac.second->internalEnabled, ac.second->internalSpeed, ac.second->internalStyle); } ret += "beziers:\n"; for (auto const& bz : g_pAnimationManager->getAllBeziers()) { - auto& controlPoints = bz.second->getControlPoints(); - ret += std::format("\n\tname: {}\n\t\tX0: {:.2f}\n\t\tY0: {:.2f}\n\t\tX1: {:.2f}\n\t\tY1: {:.2f}", bz.first, controlPoints[1].x, controlPoints[1].y, controlPoints[2].x, - controlPoints[2].y); + ret += std::format("\n\tname: {}\n", bz.first); } } else { // json @@ -921,16 +780,11 @@ static std::string animationsRequest(eHyprCtlOutputFormat format, std::string re ret += ",\n["; for (auto const& bz : g_pAnimationManager->getAllBeziers()) { - auto& controlPoints = bz.second->getControlPoints(); ret += std::format(R"#( {{ - "name": "{}", - "X0": {:.2f}, - "Y0": {:.2f}, - "X1": {:.2f}, - "Y1": {:.2f} + "name": "{}" }},)#", - escapeJSONStrings(bz.first), controlPoints[1].x, controlPoints[1].y, controlPoints[2].x, controlPoints[2].y); + escapeJSONStrings(bz.first)); } trimTrailingComma(ret); @@ -946,10 +800,11 @@ static std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string re if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "[\n\"log\":\""; - result += escapeJSONStrings(Log::logger->rolling()); + result += escapeJSONStrings(Debug::rollingLog); result += "\"]"; - } else - result = Log::logger->rolling(); + } else { + result = Debug::rollingLog; + } return result; } @@ -983,7 +838,7 @@ static std::string globalShortcutsRequest(eHyprCtlOutputFormat format, std::stri static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) { std::string ret = ""; if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { - for (auto const& kb : g_pKeybindManager->m_keybinds) { + for (auto const& kb : g_pKeybindManager->m_vKeybinds) { ret += "bind"; if (kb->locked) ret += "l"; @@ -999,12 +854,12 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request ret += "d"; ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdescription: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb->modmask, - kb->submap.name, kb->key, kb->keycode, kb->catchAll, kb->description, kb->handler, kb->arg); + kb->submap, kb->key, kb->keycode, kb->catchAll, kb->description, kb->handler, kb->arg); } } else { // json ret += "["; - for (auto const& kb : g_pKeybindManager->m_keybinds) { + for (auto const& kb : g_pKeybindManager->m_vKeybinds) { ret += std::format( R"#( {{ @@ -1017,7 +872,6 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request "has_description": {}, "modmask": {}, "submap": "{}", - "submap_universal": "{}", "key": "{}", "keycode": {}, "catch_all": {}, @@ -1026,9 +880,8 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request "arg": "{}" }},)#", kb->locked ? "true" : "false", kb->mouse ? "true" : "false", kb->release ? "true" : "false", kb->repeat ? "true" : "false", kb->longPress ? "true" : "false", - kb->nonConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap.name), kb->submapUniversal, - escapeJSONStrings(kb->key), kb->keycode, kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description), escapeJSONStrings(kb->handler), - escapeJSONStrings(kb->arg)); + kb->nonConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap), escapeJSONStrings(kb->key), kb->keycode, + kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description), escapeJSONStrings(kb->handler), escapeJSONStrings(kb->arg)); } trimTrailingComma(ret); ret += "]"; @@ -1040,34 +893,29 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request std::string versionRequest(eHyprCtlOutputFormat format, std::string request) { auto commitMsg = trim(GIT_COMMIT_MESSAGE); - std::ranges::replace(commitMsg, '#', ' '); + std::replace(commitMsg.begin(), commitMsg.end(), '#', ' '); if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\n" "Date: {}\n" - "Tag: {}, commits: {}\n", - HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS); + "Tag: {}, commits: {}\n" + "built against:\n aquamarine {}\n hyprlang {}\n hyprutils {}\n hyprcursor {}\n hyprgraphics {}\n\n\n", + HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS, AQUAMARINE_VERSION, + HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION); - result += "\n"; - result += getBuiltSystemLibraryNames(); - result += "\n"; - result += "Version ABI string: "; - result += __hyprland_api_get_hash(); - result += "\n"; - -#if (!ISDEBUG && !defined(NO_XWAYLAND) && !defined(BUILT_WITH_NIX)) +#if (!defined(LEGACY_RENDERER) && !ISDEBUG && !defined(NO_XWAYLAND)) result += "no flags were set\n"; #else result += "flags set:\n"; +#ifdef LEGACY_RENDERER + result += "legacyrenderer\n"; +#endif #if ISDEBUG result += "debug\n"; #endif #ifdef NO_XWAYLAND result += "no xwayland\n"; #endif -#ifdef BUILT_WITH_NIX - result += "nix\n"; -#endif #endif return result; } else { @@ -1086,27 +934,19 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) { "buildHyprutils": "{}", "buildHyprcursor": "{}", "buildHyprgraphics": "{}", - "systemAquamarine": "{}", - "systemHyprlang": "{}", - "systemHyprutils": "{}", - "systemHyprcursor": "{}", - "systemHyprgraphics": "{}", - "abiHash": "{}", "flags": [)#", GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG, - GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION, getSystemLibraryVersion("aquamarine"), - getSystemLibraryVersion("hyprlang"), getSystemLibraryVersion("hyprutils"), getSystemLibraryVersion("hyprcursor"), getSystemLibraryVersion("hyprgraphics"), - __hyprland_api_get_hash()); + GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION); +#ifdef LEGACY_RENDERER + result += "\"legacyrenderer\","; +#endif #if ISDEBUG result += "\"debug\","; #endif #ifdef NO_XWAYLAND result += "\"no xwayland\","; #endif -#ifdef BUILT_WITH_NIX - result += "\"nix\","; -#endif trimTrailingComma(result); @@ -1121,17 +961,6 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) { std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) { std::string result = versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, ""); - static auto check = [](bool y) -> std::string { return y ? "✔️" : "❌"; }; - static auto backend = [](Aquamarine::eBackendType t) -> std::string { - switch (t) { - case Aquamarine::AQ_BACKEND_DRM: return "drm"; - case Aquamarine::AQ_BACKEND_HEADLESS: return "headless"; - case Aquamarine::AQ_BACKEND_WAYLAND: return "wayland"; - default: break; - } - return "?"; - }; - result += "\n\nSystem Information:\n"; struct utsname unameInfo; @@ -1142,9 +971,6 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) result += "Node name: " + std::string{unameInfo.nodename} + "\n"; result += "Release: " + std::string{unameInfo.release} + "\n"; result += "Version: " + std::string{unameInfo.version} + "\n"; - result += "\n"; - result += getBuiltSystemLibraryNames(); - result += "\n"; result += "\n\n"; @@ -1198,32 +1024,12 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) result += "plugins:\n"; if (g_pPluginSystem) { for (auto const& pl : g_pPluginSystem->getAllPlugins()) { - result += std::format(" {} by {} ver {}\n", pl->m_name, pl->m_author, pl->m_version); + result += std::format(" {} by {} ver {}\n", pl->name, pl->author, pl->version); } } else result += "\tunknown: not runtime\n"; - if (g_pHyprOpenGL) { - result += std::format("\nExplicit sync: {}", g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext ? "supported" : "missing"); - result += std::format("\nGL ver: {}", g_pHyprOpenGL->m_eglContextVersion == CHyprOpenGLImpl::EGL_CONTEXT_GLES_3_2 ? "3.2" : "3.0"); - } - - if (g_pCompositor) { - result += std::format("\nBackend: {}", g_pCompositor->m_aqBackend->hasSession() ? "drm" : "sessionless"); - - result += "\n\nMonitor info:"; - - for (const auto& m : g_pCompositor->m_monitors) { - result += std::format("\n\tPanel {}: {}x{}, {} {} {} {} -> backend {}\n\t\texplicit {}\n\t\tedid:\n\t\t\thdr {}\n\t\t\tchroma {}\n\t\t\tbt2020 {}\n\t\tvrr capable " - "{}\n\t\tnon-desktop {}\n\t\t", - m->m_name, sc(m->m_pixelSize.x), sc(m->m_pixelSize.y), m->m_output->name, m->m_output->make, m->m_output->model, m->m_output->serial, - backend(m->m_output->getBackend()->type()), check(m->m_output->supportsExplicit), check(m->m_output->parsedEDID.hdrMetadata.has_value()), - check(m->m_output->parsedEDID.chromaticityCoords.has_value()), check(m->m_output->parsedEDID.supportsBT2020), check(m->m_output->vrrCapable), - check(m->m_output->nonDesktop)); - } - } - - if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.sysInfoConfig) { + if (g_pHyprCtl && g_pHyprCtl->m_sCurrentRequestParams.sysInfoConfig) { result += "\n======Config-Start======\n"; result += g_pConfigManager->getConfigString(); result += "\n======Config-End========\n"; @@ -1239,16 +1045,16 @@ static std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in) const auto DISPATCHSTR = in.substr(0, in.find_first_of(' ')); auto DISPATCHARG = std::string(); - if (sc(in.find_first_of(' ')) != -1) + if ((int)in.find_first_of(' ') != -1) DISPATCHARG = in.substr(in.find_first_of(' ') + 1); - const auto DISPATCHER = g_pKeybindManager->m_dispatchers.find(DISPATCHSTR); - if (DISPATCHER == g_pKeybindManager->m_dispatchers.end()) + const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(DISPATCHSTR); + if (DISPATCHER == g_pKeybindManager->m_mDispatchers.end()) return "Invalid dispatcher"; SDispatchResult res = DISPATCHER->second(DISPATCHARG); - Log::logger->log(Log::DEBUG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error); + Debug::log(LOG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error); return res.success ? "ok" : res.error; } @@ -1275,23 +1081,11 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) if (COMMAND.empty()) return "Invalid input: command is empty"; - g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = true; - std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE); - g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = false; - - if (COMMAND == "source") { - g_pConfigManager->m_wantsMonitorReload = true; - g_pEventLoopManager->doLater([] { g_pConfigManager->reloadRules(); }); - } - // if we are executing a dynamic source we have to reload everything, so every if will have a check for source. - if (COMMAND == "monitor") - g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords - - if (COMMAND.contains("monitorv2")) - g_pEventLoopManager->doLater([] { g_pConfigManager->m_wantsMonitorReload = true; }); + if (COMMAND == "monitor" || COMMAND == "source") + g_pConfigManager->m_bWantsMonitorReload = true; // for monitor keywords if (COMMAND.contains("input") || COMMAND.contains("device") || COMMAND == "source") { g_pInputManager->setKeyboardLayout(); // update kb layout @@ -1300,14 +1094,16 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) g_pInputManager->setTabletConfigs(); // update tablets } - if (COMMAND.contains("general:layout") || (COMMAND.contains("workspace") && VALUE.contains("layout:"))) - Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts(); + static auto PLAYOUT = CConfigValue("general:layout"); + + if (COMMAND.contains("general:layout")) + g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout if (COMMAND.contains("decoration:screen_shader") || COMMAND == "source") - g_pHyprOpenGL->m_reloadScreenShader = true; + g_pHyprOpenGL->m_bReloadScreenShader = true; if (COMMAND.contains("blur") || COMMAND == "source") { - for (auto& [m, rd] : g_pHyprOpenGL->m_monitorRenderResources) { + for (auto& [m, rd] : g_pHyprOpenGL->m_mMonitorRenderResources) { rd.blurFBDirty = true; } } @@ -1316,33 +1112,17 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in) g_pConfigManager->updateWatcher(); // decorations will probably need a repaint - if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") { - static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); - for (auto const& m : g_pCompositor->m_monitors) { - *(m->m_cursorZoom) = *PZOOMFACTOR; + if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" || + COMMAND.starts_with("windowrule")) { + for (auto const& m : g_pCompositor->m_vMonitors) { g_pHyprRenderer->damageMonitor(m); - if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); } } - if (COMMAND.contains("windowrule ") || COMMAND.contains("windowrule[")) - g_pConfigManager->reloadRules(); + Debug::log(LOG, "Hyprctl: keyword {} : {}", COMMAND, VALUE); - if (COMMAND.contains("layerrule") || COMMAND.contains("layerrule[")) { - g_pConfigManager->reloadRules(); - // Damage all monitors to redraw static layers. - for (auto const& m : g_pCompositor->m_monitors) { - g_pHyprRenderer->damageMonitor(m); - } - } - - if (COMMAND.contains("workspace")) - g_pConfigManager->ensurePersistentWorkspacesPresent(); - - Log::logger->log(Log::DEBUG, "Hyprctl: keyword {} : {}", COMMAND, VALUE); - - if (retval.empty()) + if (retval == "") return "ok"; return retval; @@ -1353,7 +1133,7 @@ static std::string reloadRequest(eHyprCtlOutputFormat format, std::string reques const auto REQMODE = request.substr(request.find_last_of(' ') + 1); if (REQMODE == "config-only") - g_pConfigManager->m_noMonitorReload = true; + g_pConfigManager->m_bNoMonitorReload = true; g_pConfigManager->reload(); @@ -1367,14 +1147,14 @@ static std::string killRequest(eHyprCtlOutputFormat format, std::string request) } static std::string splashRequest(eHyprCtlOutputFormat format, std::string request) { - return g_pCompositor->m_currentSplash; + return g_pCompositor->m_szCurrentSplash; } static std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string request) { const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal().floor(); if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) { - return std::format("{}, {}", sc(CURSORPOS.x), sc(CURSORPOS.y)); + return std::format("{}, {}", (int)CURSORPOS.x, (int)CURSORPOS.y); } else { return std::format(R"#( {{ @@ -1382,7 +1162,7 @@ static std::string cursorPosRequest(eHyprCtlOutputFormat format, std::string req "y": {} }} )#", - sc(CURSORPOS.x), sc(CURSORPOS.y)); + (int)CURSORPOS.x, (int)CURSORPOS.y); } return "error"; @@ -1411,7 +1191,7 @@ static std::string dispatchBatch(eHyprCtlOutputFormat format, std::string reques } } - return reply.substr(0, std::max(sc(reply.size() - DELIMITER.size()), 0)); + return reply.substr(0, std::max(static_cast(reply.size() - DELIMITER.size()), 0)); } static std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) { @@ -1447,39 +1227,38 @@ static std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::stri SP pKeyboard; auto updateKeyboard = [](const SP KEEB, const std::string& CMD) -> std::optional { - const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->m_xkbKeymap); + const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->xkbKeymap); xkb_layout_index_t activeLayout = 0; while (activeLayout < LAYOUTS) { - if (xkb_state_layout_index_is_active(KEEB->m_xkbState, activeLayout, XKB_STATE_LAYOUT_EFFECTIVE) == 1) + if (xkb_state_layout_index_is_active(KEEB->xkbState, activeLayout, XKB_STATE_LAYOUT_EFFECTIVE) == 1) break; activeLayout++; } if (CMD == "next") - KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked, activeLayout > LAYOUTS ? 0 : activeLayout + 1); + KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout > LAYOUTS ? 0 : activeLayout + 1); else if (CMD == "prev") - KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked, - activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1); + KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1); else { int requestedLayout = 0; try { requestedLayout = std::stoi(CMD); } catch (std::exception& e) { return "invalid arg 2"; } - if (requestedLayout < 0 || sc(requestedLayout) > LAYOUTS - 1) { + if (requestedLayout < 0 || (uint64_t)requestedLayout > LAYOUTS - 1) { return "layout idx out of range of " + std::to_string(LAYOUTS); } - KEEB->updateModifiers(KEEB->m_modifiersState.depressed, KEEB->m_modifiersState.latched, KEEB->m_modifiersState.locked, requestedLayout); + KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, requestedLayout); } return std::nullopt; }; if (KB == "main" || KB == "active" || KB == "current") { - for (auto const& k : g_pInputManager->m_keyboards) { - if (!k->m_active) + for (auto const& k : g_pInputManager->m_vKeyboards) { + if (!k->active) continue; pKeyboard = k; @@ -1487,16 +1266,17 @@ static std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::stri } } else if (KB == "all") { std::string result = ""; - for (auto const& k : g_pInputManager->m_keyboards) { + for (auto const& k : g_pInputManager->m_vKeyboards) { auto res = updateKeyboard(k, CMD); if (res.has_value()) result += *res + "\n"; } return result.empty() ? "ok" : result; } else { - auto k = std::ranges::find_if(g_pInputManager->m_keyboards, [&](const auto& other) { return other->m_hlName == deviceNameToInternalString(KB); }); + auto k = std::find_if(g_pInputManager->m_vKeyboards.begin(), g_pInputManager->m_vKeyboards.end(), + [&](const auto& other) { return other->hlName == g_pInputManager->deviceNameToInternalString(KB); }); - if (k == g_pInputManager->m_keyboards.end()) + if (k == g_pInputManager->m_vKeyboards.end()) return "device not found"; pKeyboard = *k; @@ -1521,7 +1301,7 @@ static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string req if (vars.size() < 3) { g_pHyprError->destroy(); - if (vars.size() == 2 && !vars[1].contains("dis")) + if (vars.size() == 2 && !vars[1].find("dis")) return "var 1 not color or disable"; return "ok"; @@ -1542,186 +1322,9 @@ static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string req return "ok"; } -static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string request) { - CVarList vars(request, 0, ' '); - - if (vars.size() < 3) - return "not enough args"; - - const auto WINREGEX = vars[1]; - const auto PROP = vars[2]; - - const auto PWINDOW = g_pCompositor->getWindowByRegex(WINREGEX); - - if (!PWINDOW) - return "window not found"; - - const bool FORMNORM = format == FORMAT_NORMAL; - - auto sizeToString = [&](bool max) -> std::string { - auto sizeValue = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE)); - if (max) - sizeValue = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D(INFINITY, INFINITY)); - - if (FORMNORM) - return std::format("{} {}", sizeValue.x, sizeValue.y); - else { - std::string xSizeString = (sizeValue.x != INFINITY) ? std::to_string(sizeValue.x) : "null"; - std::string ySizeString = (sizeValue.y != INFINITY) ? std::to_string(sizeValue.y) : "null"; - return std::format(R"({{"{}": [{},{}]}})", PROP, xSizeString, ySizeString); - } - }; - - auto alphaToString = [&](Desktop::Types::COverridableVar& alpha, bool getAlpha) -> std::string { - if (FORMNORM) { - if (getAlpha) - return std::format("{}", alpha.valueOrDefault().alpha); - else - return std::format("{}", alpha.valueOrDefault().overridden); - } else { - if (getAlpha) - return std::format(R"({{"{}": {}}})", PROP, alpha.valueOrDefault().alpha); - else - return std::format(R"({{"{}": {}}})", PROP, alpha.valueOrDefault().overridden); - } - }; - - auto borderColorToString = [&](bool active) -> std::string { - static auto PACTIVECOL = CConfigValue("general:col.active_border"); - static auto PINACTIVECOL = CConfigValue("general:col.inactive_border"); - static auto PNOGROUPACTIVECOL = CConfigValue("general:col.nogroup_border_active"); - static auto PNOGROUPINACTIVECOL = CConfigValue("general:col.nogroup_border"); - static auto PGROUPACTIVECOL = CConfigValue("group:col.border_active"); - static auto PGROUPINACTIVECOL = CConfigValue("group:col.border_inactive"); - static auto PGROUPACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_active"); - static auto PGROUPINACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_inactive"); - - const bool GROUPLOCKED = PWINDOW->m_group ? PWINDOW->m_group->locked() : false; - - if (active) { - auto* const ACTIVECOL = (CGradientValueData*)(PACTIVECOL.ptr())->getData(); - auto* const NOGROUPACTIVECOL = (CGradientValueData*)(PNOGROUPACTIVECOL.ptr())->getData(); - auto* const GROUPACTIVECOL = (CGradientValueData*)(PGROUPACTIVECOL.ptr())->getData(); - auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPACTIVELOCKEDCOL.ptr())->getData(); - const auto* const ACTIVECOLOR = - !PWINDOW->m_group ? (!(PWINDOW->m_groupRules & Desktop::View::GROUP_DENY) ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL); - - std::string borderColorString = PWINDOW->m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR).toString(); - if (FORMNORM) - return borderColorString; - else - return std::format(R"({{"{}": "{}"}})", PROP, borderColorString); - } else { - auto* const INACTIVECOL = (CGradientValueData*)(PINACTIVECOL.ptr())->getData(); - auto* const NOGROUPINACTIVECOL = (CGradientValueData*)(PNOGROUPINACTIVECOL.ptr())->getData(); - auto* const GROUPINACTIVECOL = (CGradientValueData*)(PGROUPINACTIVECOL.ptr())->getData(); - auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPINACTIVELOCKEDCOL.ptr())->getData(); - const auto* const INACTIVECOLOR = !PWINDOW->m_group ? (!(PWINDOW->m_groupRules & Desktop::View::GROUP_DENY) ? INACTIVECOL : NOGROUPINACTIVECOL) : - (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL); - - std::string borderColorString = PWINDOW->m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR).toString(); - if (FORMNORM) - return borderColorString; - else - return std::format(R"({{"{}": "{}"}})", PROP, borderColorString); - } - }; - - auto windowPropToString = [&](auto& prop) -> std::string { - if (FORMNORM) - return std::format("{}", prop.valueOrDefault()); - else - return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault()); - }; - - if (PROP == "animation") { - auto& animationStyle = PWINDOW->m_ruleApplicator->animationStyle(); - if (FORMNORM) - return animationStyle.valueOr("(unset)"); - else - return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr("")); - } else if (PROP == "max_size") - return sizeToString(true); - else if (PROP == "min_size") - return sizeToString(false); - else if (PROP == "opacity") - return alphaToString(PWINDOW->m_ruleApplicator->alpha(), true); - else if (PROP == "opacity_inactive") - return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), true); - else if (PROP == "opacity_fullscreen") - return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), true); - else if (PROP == "opacity_override") - return alphaToString(PWINDOW->m_ruleApplicator->alpha(), false); - else if (PROP == "opacity_inactive_override") - return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), false); - else if (PROP == "opacity_fullscreen_override") - return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), false); - else if (PROP == "active_border_color") - return borderColorToString(true); - else if (PROP == "inactive_border_color") - return borderColorToString(false); - else if (PROP == "allows_input") - return windowPropToString(PWINDOW->m_ruleApplicator->allowsInput()); - else if (PROP == "decorate") - return windowPropToString(PWINDOW->m_ruleApplicator->decorate()); - else if (PROP == "focus_on_activate") - return windowPropToString(PWINDOW->m_ruleApplicator->focusOnActivate()); - else if (PROP == "keep_aspect_ratio") - return windowPropToString(PWINDOW->m_ruleApplicator->keepAspectRatio()); - else if (PROP == "nearest_neighbor") - return windowPropToString(PWINDOW->m_ruleApplicator->nearestNeighbor()); - else if (PROP == "no_anim") - return windowPropToString(PWINDOW->m_ruleApplicator->noAnim()); - else if (PROP == "no_blur") - return windowPropToString(PWINDOW->m_ruleApplicator->noBlur()); - else if (PROP == "no_dim") - return windowPropToString(PWINDOW->m_ruleApplicator->noDim()); - else if (PROP == "no_focus") - return windowPropToString(PWINDOW->m_ruleApplicator->noFocus()); - else if (PROP == "no_max_size") - return windowPropToString(PWINDOW->m_ruleApplicator->noMaxSize()); - else if (PROP == "no_shadow") - return windowPropToString(PWINDOW->m_ruleApplicator->noShadow()); - else if (PROP == "no_shortcuts_inhibit") - return windowPropToString(PWINDOW->m_ruleApplicator->noShortcutsInhibit()); - else if (PROP == "opaque") - return windowPropToString(PWINDOW->m_ruleApplicator->opaque()); - else if (PROP == "dim_around") - return windowPropToString(PWINDOW->m_ruleApplicator->dimAround()); - else if (PROP == "force_rgbx") - return windowPropToString(PWINDOW->m_ruleApplicator->RGBX()); - else if (PROP == "sync_fullscreen") - return windowPropToString(PWINDOW->m_ruleApplicator->syncFullscreen()); - else if (PROP == "immediate") - return windowPropToString(PWINDOW->m_ruleApplicator->tearing()); - else if (PROP == "xray") - return windowPropToString(PWINDOW->m_ruleApplicator->xray()); - else if (PROP == "render_unfocused") - return windowPropToString(PWINDOW->m_ruleApplicator->renderUnfocused()); - else if (PROP == "no_follow_mouse") - return windowPropToString(PWINDOW->m_ruleApplicator->noFollowMouse()); - else if (PROP == "no_screen_share") - return windowPropToString(PWINDOW->m_ruleApplicator->noScreenShare()); - else if (PROP == "no_vrr") - return windowPropToString(PWINDOW->m_ruleApplicator->noVRR()); - else if (PROP == "persistent_size") - return windowPropToString(PWINDOW->m_ruleApplicator->persistentSize()); - else if (PROP == "stay_focused") - return windowPropToString(PWINDOW->m_ruleApplicator->stayFocused()); - else if (PROP == "idle_inhibit") - return windowPropToString(PWINDOW->m_ruleApplicator->idleInhibitMode()); - else if (PROP == "border_size") - return windowPropToString(PWINDOW->m_ruleApplicator->borderSize()); - else if (PROP == "rounding") - return windowPropToString(PWINDOW->m_ruleApplicator->rounding()); - else if (PROP == "rounding_power") - return windowPropToString(PWINDOW->m_ruleApplicator->roundingPower()); - else if (PROP == "scroll_mouse") - return windowPropToString(PWINDOW->m_ruleApplicator->scrollMouse()); - else if (PROP == "scroll_touchpad") - return windowPropToString(PWINDOW->m_ruleApplicator->scrollTouchpad()); - - return "prop not found"; +static std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) { + auto result = g_pKeybindManager->m_mDispatchers["setprop"](request.substr(request.find_first_of(' ') + 1)); + return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error); } static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string request) { @@ -1762,7 +1365,7 @@ static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string re else if (TYPE == typeid(Hyprlang::STRING)) return std::format("str: {}\nset: {}", std::any_cast(VAL), VAR->m_bSetByUser); else if (TYPE == typeid(void*)) - return std::format("custom type: {}\nset: {}", sc(std::any_cast(VAL))->toString(), VAR->m_bSetByUser); + return std::format("custom type: {}\nset: {}", ((ICustomConfigValueData*)std::any_cast(VAL))->toString(), VAR->m_bSetByUser); } else { if (TYPE == typeid(Hyprlang::INT)) return std::format(R"({{"option": "{}", "int": {}, "set": {} }})", curitem, std::any_cast(VAL), VAR->m_bSetByUser); @@ -1774,7 +1377,7 @@ static std::string dispatchGetOption(eHyprCtlOutputFormat format, std::string re else if (TYPE == typeid(Hyprlang::STRING)) return std::format(R"({{"option": "{}", "str": "{}", "set": {} }})", curitem, escapeJSONStrings(std::any_cast(VAL)), VAR->m_bSetByUser); else if (TYPE == typeid(void*)) - return std::format(R"({{"option": "{}", "custom": "{}", "set": {} }})", curitem, sc(std::any_cast(VAL))->toString(), VAR->m_bSetByUser); + return std::format(R"({{"option": "{}", "custom": "{}", "set": {} }})", curitem, ((ICustomConfigValueData*)std::any_cast(VAL))->toString(), VAR->m_bSetByUser); } return "invalid type (internal error)"; @@ -1790,7 +1393,7 @@ static std::string decorationRequest(eHyprCtlOutputFormat format, std::string re std::string result = ""; if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; - for (auto const& wd : PWINDOW->m_windowDecorations) { + for (auto const& wd : PWINDOW->m_dWindowDecorations) { result += "{\n\"decorationName\": \"" + wd->getDisplayName() + "\",\n\"priority\": " + std::to_string(wd->getPositioningInfo().priority) + "\n},"; } @@ -1798,7 +1401,7 @@ static std::string decorationRequest(eHyprCtlOutputFormat format, std::string re result += "]"; } else { result = +"Decoration\tPriority\n"; - for (auto const& wd : PWINDOW->m_windowDecorations) { + for (auto const& wd : PWINDOW->m_dWindowDecorations) { result += wd->getDisplayName() + "\t" + std::to_string(wd->getPositioningInfo().priority) + "\n"; } } @@ -1817,8 +1420,8 @@ static std::string dispatchOutput(eHyprCtlOutputFormat format, std::string reque bool added = false; if (!vars[3].empty()) { - for (auto const& m : g_pCompositor->m_realMonitors) { - if (m->m_name == vars[3]) + for (auto const& m : g_pCompositor->m_vRealMonitors) { + if (m->szName == vars[3]) return "Name already taken"; } } @@ -1827,7 +1430,7 @@ static std::string dispatchOutput(eHyprCtlOutputFormat format, std::string reque if (g_pCompositor->getMonitorFromName(vars[3])) return "A real monitor already uses that name."; - for (auto const& impl : g_pCompositor->m_aqBackend->getImplementations() | std::views::reverse) { + for (auto const& impl : g_pCompositor->m_pAqBackend->getImplementations() | std::views::reverse) { auto type = impl->type(); if (type == Aquamarine::AQ_BACKEND_HEADLESS && (vars[2] == "headless" || vars[2] == "auto")) { @@ -1852,10 +1455,10 @@ static std::string dispatchOutput(eHyprCtlOutputFormat format, std::string reque if (!PMONITOR) return "output not found"; - if (!PMONITOR->m_createdByUser) + if (!PMONITOR->createdByUser) return "cannot remove a real display. Use the monitor keyword."; - PMONITOR->m_output->destroy(); + PMONITOR->output->destroy(); } return "ok"; @@ -1874,18 +1477,10 @@ static std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string reque if (vars.size() < 3) return "not enough args"; - g_pHyprCtl->m_currentRequestParams.pendingPromise = CPromise::make([PATH](SP> resolver) { - g_pPluginSystem->loadPlugin(PATH)->then([resolver, PATH](SP> result) { - if (result->hasError()) { - resolver->reject(result->error()); - return; - } + const auto PLUGIN = g_pPluginSystem->loadPlugin(PATH); - resolver->resolve("ok"); - }); - }); - - return "ok"; + if (!PLUGIN) + return "error in loading plugin, last error: " + g_pPluginSystem->m_szLastError; } else if (OPERATION == "unload") { if (vars.size() < 3) return "not enough args"; @@ -1903,7 +1498,7 @@ static std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string reque if (format == eHyprCtlOutputFormat::FORMAT_JSON) { result += "["; - if (PLUGINS.empty()) + if (PLUGINS.size() == 0) return "[]"; for (auto const& p : PLUGINS) { @@ -1916,17 +1511,17 @@ static std::string dispatchPlugin(eHyprCtlOutputFormat format, std::string reque "version": "{}", "description": "{}" }},)#", - escapeJSONStrings(p->m_name), escapeJSONStrings(p->m_author), rc(p->m_handle), escapeJSONStrings(p->m_version), escapeJSONStrings(p->m_description)); + escapeJSONStrings(p->name), escapeJSONStrings(p->author), (uintptr_t)p->m_pHandle, escapeJSONStrings(p->version), escapeJSONStrings(p->description)); } trimTrailingComma(result); result += "]"; } else { - if (PLUGINS.empty()) + if (PLUGINS.size() == 0) return "no plugins loaded"; for (auto const& p : PLUGINS) { - result += std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->m_name, p->m_author, rc(p->m_handle), p->m_version, - p->m_description); + result += + std::format("\nPlugin {} by {}:\n\tHandle: {:x}\n\tVersion: {}\n\tDescription: {}\n", p->name, p->author, (uintptr_t)p->m_pHandle, p->version, p->description); } } @@ -1988,7 +1583,7 @@ static std::string dispatchNotify(eHyprCtlOutputFormat format, std::string reque const auto MESSAGE = vars.join(" ", msgidx); - g_pHyprNotificationOverlay->addNotification(MESSAGE, color, time, sc(icon), fontsize); + g_pHyprNotificationOverlay->addNotification(MESSAGE, color, time, (eIcons)icon, fontsize); return "ok"; } @@ -2026,7 +1621,7 @@ static std::string getIsLocked(eHyprCtlOutputFormat format, std::string request) } static std::string getDescriptions(eHyprCtlOutputFormat format, std::string request) { - std::string json = "["; + std::string json = "{"; const auto& DESCS = g_pConfigManager->getAllDescriptions(); for (const auto& d : DESCS) { @@ -2036,30 +1631,18 @@ static std::string getDescriptions(eHyprCtlOutputFormat format, std::string requ json.pop_back(); json.pop_back(); - json += "]\n"; + json += "}\n"; return json; } static std::string submapRequest(eHyprCtlOutputFormat format, std::string request) { - std::string submap = g_pKeybindManager->getCurrentSubmap().name; + std::string submap = g_pKeybindManager->getCurrentSubmap(); if (submap.empty()) submap = "default"; return format == FORMAT_JSON ? std::format("{{\"{}\"}}\n", escapeJSONStrings(submap)) : (submap + "\n"); } -static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) { - CVarList vars(request, 0, ' '); - - if (vars.size() > 2) - return "too many args"; - - if (g_pHyprOpenGL && g_pHyprRenderer->reloadShaders(vars.size() == 2 ? vars[1] : "")) - return format == FORMAT_JSON ? "{\"ok\": true}" : "ok"; - else - return format == FORMAT_JSON ? "{\"ok\": false}" : "error"; -} - CHyprCtl::CHyprCtl() { registerCommand(SHyprCtlCommand{"workspaces", true, workspacesRequest}); registerCommand(SHyprCtlCommand{"workspacerules", true, workspaceRulesRequest}); @@ -2077,18 +1660,18 @@ CHyprCtl::CHyprCtl() { registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest}); registerCommand(SHyprCtlCommand{"animations", true, animationsRequest}); registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest}); + registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest}); registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest}); registerCommand(SHyprCtlCommand{"locked", true, getIsLocked}); registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions}); registerCommand(SHyprCtlCommand{"submap", true, submapRequest}); - registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = false, .fn = reloadShaders}); registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest}); registerCommand(SHyprCtlCommand{"reload", false, reloadRequest}); registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin}); registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify}); registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify}); - registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp}); + registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp}); registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror}); registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest}); registerCommand(SHyprCtlCommand{"output", false, dispatchOutput}); @@ -2110,18 +1693,17 @@ CHyprCtl::~CHyprCtl() { } SP CHyprCtl::registerCommand(SHyprCtlCommand cmd) { - return m_commands.emplace_back(makeShared(cmd)); + return m_vCommands.emplace_back(makeShared(cmd)); } void CHyprCtl::unregisterCommand(const SP& cmd) { - std::erase(m_commands, cmd); + std::erase(m_vCommands, cmd); } std::string CHyprCtl::getReply(std::string request) { - auto format = eHyprCtlOutputFormat::FORMAT_NORMAL; - bool reloadAll = false; - m_currentRequestParams.all = false; - m_currentRequestParams.sysInfoConfig = false; + auto format = eHyprCtlOutputFormat::FORMAT_NORMAL; + bool reloadAll = false; + m_sCurrentRequestParams = {}; // process flags for non-batch requests if (!request.starts_with("[[BATCH]]") && request.contains("/")) { @@ -2145,9 +1727,9 @@ std::string CHyprCtl::getReply(std::string request) { else if (c == 'r') reloadAll = true; else if (c == 'a') - m_currentRequestParams.all = true; + m_sCurrentRequestParams.all = true; else if (c == 'c') - m_currentRequestParams.sysInfoConfig = true; + m_sCurrentRequestParams.sysInfoConfig = true; } if (sepIndex < request.size()) @@ -2157,7 +1739,7 @@ std::string CHyprCtl::getReply(std::string request) { std::string result = ""; // parse exact cmds first, then non-exact. - for (auto const& cmd : m_commands) { + for (auto const& cmd : m_vCommands) { if (!cmd->exact) continue; @@ -2168,7 +1750,7 @@ std::string CHyprCtl::getReply(std::string request) { } if (result.empty()) - for (auto const& cmd : m_commands) { + for (auto const& cmd : m_vCommands) { if (cmd->exact) continue; @@ -2182,37 +1764,34 @@ std::string CHyprCtl::getReply(std::string request) { return "unknown request"; if (reloadAll) { - g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords + g_pConfigManager->m_bWantsMonitorReload = true; // for monitor keywords g_pInputManager->setKeyboardLayout(); // update kb layout g_pInputManager->setPointerConfigs(); // update mouse cfgs g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs g_pInputManager->setTabletConfigs(); // update tablets - g_pHyprOpenGL->m_reloadScreenShader = true; + static auto PLAYOUT = CConfigValue("general:layout"); - for (auto& [m, rd] : g_pHyprOpenGL->m_monitorRenderResources) { + g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout + + g_pHyprOpenGL->m_bReloadScreenShader = true; + + for (auto& [m, rd] : g_pHyprOpenGL->m_mMonitorRenderResources) { rd.blurFBDirty = true; } - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible()) + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped || !w->m_pWorkspace || !w->m_pWorkspace->isVisible()) continue; - Desktop::Rule::ruleEngine()->updateAllRules(); + w->updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(w); } - for (const auto& ws : g_pCompositor->getWorkspaces()) { - if (!ws) - continue; - - ws->updateWindows(); - ws->updateWindowData(); - ws->updateWindowDecos(); - } - - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { g_pHyprRenderer->damageMonitor(m); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); } } @@ -2224,52 +1803,30 @@ std::string CHyprCtl::makeDynamicCall(const std::string& input) { } static bool successWrite(int fd, const std::string& data, bool needLog = true) { - size_t totalWritten = 0; - size_t remaining = data.length(); - size_t waitsDone = 0; - constexpr const size_t MAX_WAITS = 20; // 2000µs = 2ms + if (write(fd, data.c_str(), data.length()) > 0) + return true; - while (totalWritten < data.length()) { - ssize_t written = write(fd, data.c_str() + totalWritten, remaining); + if (errno == EAGAIN) + return true; - if (waitsDone > MAX_WAITS) { - Log::logger->log(Log::ERR, "Couldn't write to socket. Buffer was full and the client couldn't read in time."); - return false; - } + if (needLog) + Debug::log(ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno))); - if (written < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - // socket buffer full, wait a bit and retry - std::this_thread::sleep_for(std::chrono::microseconds(100)); - waitsDone++; - continue; - } - if (needLog) - Log::logger->log(Log::ERR, "Couldn't write to socket. Error: {}", strerror(errno)); - return false; - } - - waitsDone = 0; - - totalWritten += written; - remaining -= written; - } - - return true; + return false; } static void runWritingDebugLogThread(const int conn) { using namespace std::chrono_literals; - Log::logger->log(Log::DEBUG, "In followlog thread, got connection, start writing: {}", conn); + Debug::log(LOG, "In followlog thread, got connection, start writing: {}", conn); //will be finished, when reading side close connection std::thread([conn]() { - while (Log::SRollingLogFollow::get().isRunning()) { - if (Log::SRollingLogFollow::get().isEmpty(conn)) { + while (Debug::SRollingLogFollow::get().isRunning()) { + if (Debug::SRollingLogFollow::get().isEmpty(conn)) { std::this_thread::sleep_for(1000ms); continue; } - auto line = Log::SRollingLogFollow::get().getLog(conn); + auto line = Debug::SRollingLogFollow::get().getLog(conn); if (!successWrite(conn, line)) // We cannot write, when connection is closed. So thread will successfully exit by itself break; @@ -2277,7 +1834,7 @@ static void runWritingDebugLogThread(const int conn) { std::this_thread::sleep_for(100ms); } close(conn); - Log::SRollingLogFollow::get().stopFor(conn); + Debug::SRollingLogFollow::get().stopFor(conn); }).detach(); } @@ -2289,26 +1846,16 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) { if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) return 0; - if (!g_pHyprCtl->m_socketFD.isValid()) + if (!g_pHyprCtl->m_iSocketFD.isValid()) return 0; sockaddr_in clientAddress; socklen_t clientSize = sizeof(clientAddress); - const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_socketFD.get(), rc(&clientAddress), &clientSize, SOCK_CLOEXEC); + const auto ACCEPTEDCONNECTION = accept4(g_pHyprCtl->m_iSocketFD.get(), (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC); std::array readBuffer; - // try to get creds - CRED_T creds; - uint32_t len = sizeof(creds); - if (getsockopt(ACCEPTEDCONNECTION, CRED_LVL, CRED_OPT, &creds, &len) == -1) - Log::logger->log(Log::ERR, "Hyprctl: failed to get peer creds"); - else { - g_pHyprCtl->m_currentRequestParams.pid = creds.CRED_PID; - Log::logger->log(Log::DEBUG, "Hyprctl: new connection from pid {}", creds.CRED_PID); - } - // pollfd pollfds[1] = { { @@ -2341,65 +1888,49 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) { try { reply = g_pHyprCtl->getReply(request); } catch (std::exception& e) { - Log::logger->log(Log::ERR, "Error in request: {}", e.what()); + Debug::log(ERR, "Error in request: {}", e.what()); reply = "Err: " + std::string(e.what()); } - if (g_pHyprCtl->m_currentRequestParams.pendingPromise) { - // we have a promise pending - g_pHyprCtl->m_currentRequestParams.pendingPromise->then([ACCEPTEDCONNECTION, request](SP> result) { - const auto RES = result->hasError() ? result->error() : result->result(); - successWrite(ACCEPTEDCONNECTION, RES); + successWrite(ACCEPTEDCONNECTION, reply); - // No rollinglog or ensureMonitor here. These are only for plugins for now. + if (isFollowUpRollingLogRequest(request)) { + Debug::log(LOG, "Followup rollinglog request received. Starting thread to write to socket."); + Debug::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION); + runWritingDebugLogThread(ACCEPTEDCONNECTION); + Debug::log(LOG, Debug::SRollingLogFollow::get().debugInfo()); + } else + close(ACCEPTEDCONNECTION); - close(ACCEPTEDCONNECTION); - }); - - g_pHyprCtl->m_currentRequestParams.pendingPromise.reset(); - } else { - successWrite(ACCEPTEDCONNECTION, reply); - - if (isFollowUpRollingLogRequest(request)) { - Log::logger->log(Log::DEBUG, "Followup rollinglog request received. Starting thread to write to socket."); - Log::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION); - runWritingDebugLogThread(ACCEPTEDCONNECTION); - Log::logger->log(Log::DEBUG, Log::SRollingLogFollow::get().debugInfo()); - } else - close(ACCEPTEDCONNECTION); - - if (g_pConfigManager->m_wantsMonitorReload) - g_pConfigManager->ensureMonitorStatus(); - - g_pHyprCtl->m_currentRequestParams.pid = 0; - } + if (g_pConfigManager->m_bWantsMonitorReload) + g_pConfigManager->ensureMonitorStatus(); return 0; } void CHyprCtl::startHyprCtlSocket() { - m_socketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)}; + m_iSocketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)}; - if (!m_socketFD.isValid()) { - Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work."); + if (!m_iSocketFD.isValid()) { + Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work."); return; } sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX}; - m_socketPath = g_pCompositor->m_instancePath + "/.socket.sock"; + m_socketPath = g_pCompositor->m_szInstancePath + "/.socket.sock"; - snprintf(SERVERADDRESS.sun_path, sizeof(SERVERADDRESS.sun_path), "%s", m_socketPath.c_str()); + strcpy(SERVERADDRESS.sun_path, m_socketPath.c_str()); - if (bind(m_socketFD.get(), rc(&SERVERADDRESS), SUN_LEN(&SERVERADDRESS)) < 0) { - Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work."); + if (bind(m_iSocketFD.get(), (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) { + Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work."); return; } // 10 max queued. - listen(m_socketFD.get(), 10); + listen(m_iSocketFD.get(), 10); - Log::logger->log(Log::DEBUG, "Hypr socket started at {}", m_socketPath); + Debug::log(LOG, "Hypr socket started at {}", m_socketPath); - m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_socketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr); + m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr); } diff --git a/src/debug/HyprCtl.hpp b/src/debug/HyprCtl.hpp index a6fa3721..a95a5f93 100644 --- a/src/debug/HyprCtl.hpp +++ b/src/debug/HyprCtl.hpp @@ -2,10 +2,8 @@ #include #include "../helpers/MiscFunctions.hpp" -#include "../helpers/defer/Promise.hpp" -#include "../desktop/view/Window.hpp" +#include "../desktop/Window.hpp" #include -#include #include // exposed for main.cpp @@ -22,27 +20,21 @@ class CHyprCtl { void unregisterCommand(const SP& cmd); std::string getReply(std::string); - Hyprutils::OS::CFileDescriptor m_socketFD; + Hyprutils::OS::CFileDescriptor m_iSocketFD; struct { - bool all = false; - bool sysInfoConfig = false; - bool isDynamicKeyword = false; - pid_t pid = 0; - SP> 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); - static std::string getSolitaryBlockedReason(Hyprutils::Memory::CSharedPointer m, eHyprCtlOutputFormat format); - static std::string getDSBlockedReason(Hyprutils::Memory::CSharedPointer m, eHyprCtlOutputFormat format); - static std::string getTearingBlockedReason(Hyprutils::Memory::CSharedPointer m, eHyprCtlOutputFormat format); static std::string getMonitorData(Hyprutils::Memory::CSharedPointer m, eHyprCtlOutputFormat format); private: void startHyprCtlSocket(); - std::vector> m_commands; + std::vector> m_vCommands; wl_event_source* m_eventSource = nullptr; std::string m_socketPath; }; diff --git a/src/debug/HyprDebugOverlay.cpp b/src/debug/HyprDebugOverlay.cpp index 17ce12fa..9ea7f1bc 100644 --- a/src/debug/HyprDebugOverlay.cpp +++ b/src/debug/HyprDebugOverlay.cpp @@ -4,11 +4,10 @@ #include "../Compositor.hpp" #include "../render/pass/TexPassElement.hpp" #include "../render/Renderer.hpp" -#include "../managers/animation/AnimationManager.hpp" -#include "../desktop/state/FocusState.hpp" +#include "../managers/AnimationManager.hpp" CHyprDebugOverlay::CHyprDebugOverlay() { - m_texture = g_pHyprRenderer->createTexture(); + m_pTexture = makeShared(); } void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) { @@ -17,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() > sc(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) { @@ -32,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() > sc(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) { @@ -47,36 +46,36 @@ void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) { if (!*PDEBUGOVERLAY) return; - m_lastFrametimes.emplace_back(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_lastFrame).count() / 1000.f); + m_dLastFrametimes.emplace_back(std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_tpLastFrame).count() / 1000.f); - if (m_lastFrametimes.size() > sc(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() : Desktop::focusState()->monitor(); - if (PMONITORFORTICKS == pMonitor) { - if (m_lastAnimationTicks.size() > sc(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) @@ -84,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) @@ -97,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) @@ -110,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) @@ -123,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("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()); @@ -138,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) { @@ -164,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", sc(FPS)); + 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); @@ -199,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 = {sc(g_pCompositor->m_monitors.front()->m_position.x) + MARGIN_LEFT - 1, - sc(g_pCompositor->m_monitors.front()->m_position.y) + offset + MARGIN_TOP - 1, sc(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; } @@ -213,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) { @@ -222,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) { @@ -231,38 +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 - m_texture = g_pHyprRenderer->createTexture(m_cairoSurface); + 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, 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(std::move(data))); + data.tex = m_pTexture; + data.box = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y}; + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } diff --git a/src/debug/HyprDebugOverlay.hpp b/src/debug/HyprDebugOverlay.hpp index bf188359..f4ef122f 100644 --- a/src/debug/HyprDebugOverlay.hpp +++ b/src/debug/HyprDebugOverlay.hpp @@ -17,13 +17,13 @@ class CHyprMonitorDebugOverlay { void frameData(PHLMONITOR pMonitor); private: - std::deque m_lastFrametimes; - std::deque m_lastRenderTimes; - std::deque m_lastRenderTimesNoOverlay; - std::deque m_lastAnimationTicks; - std::chrono::high_resolution_clock::time_point m_lastFrame; - PHLMONITORREF m_monitor; - CBox m_lastDrawnBox; + std::deque m_dLastFrametimes; + std::deque m_dLastRenderTimes; + std::deque m_dLastRenderTimesNoOverlay; + std::deque 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 m_monitorOverlays; + std::map 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 m_texture; + SP m_pTexture; friend class CHyprMonitorDebugOverlay; friend class CHyprRenderer; diff --git a/src/debug/HyprNotificationOverlay.cpp b/src/debug/HyprNotificationOverlay.cpp index e67b0434..4081af84 100644 --- a/src/debug/HyprNotificationOverlay.cpp +++ b/src/debug/HyprNotificationOverlay.cpp @@ -4,16 +4,16 @@ #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" #include "../render/pass/TexPassElement.hpp" -#include "../event/EventBus.hpp" -#include "../managers/animation/AnimationManager.hpp" +#include "../managers/AnimationManager.hpp" +#include "../managers/HookSystemManager.hpp" #include "../render/Renderer.hpp" static inline auto iconBackendFromLayout(PangoLayout* layout) { // preference: Nerd > FontAwesome > text auto eIconBackendChecks = std::array{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; @@ -22,23 +22,25 @@ static inline auto iconBackendFromLayout(PangoLayout* layout) { } CHyprNotificationOverlay::CHyprNotificationOverlay() { - static auto P = Event::bus()->m_events.monitor.focused.listen([&](PHLMONITOR mon) { - if (m_notifications.empty()) + static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) { + if (m_vNotifications.size() == 0) return; - g_pHyprRenderer->damageBox(m_lastDamage); + g_pHyprRenderer->damageBox(m_bLastDamage); }); + + m_pTexture = makeShared(); } 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()).get(); + const auto PNOTIF = m_vNotifications.emplace_back(makeUnique()).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; @@ -47,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, sc(m_notifications.size())); + const int AMT = std::min(amount, static_cast(m_vNotifications.size())); for (int i = 0; i < AMT; ++i) { - m_notifications.erase(m_notifications.begin()); + m_vNotifications.erase(m_vNotifications.begin()); } } } @@ -75,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("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()); @@ -90,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(sc(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 = @@ -137,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) { @@ -156,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; @@ -185,61 +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{sc(pMonitor->m_position.x + pMonitor->m_size.x - maxWidth - 20), sc(pMonitor->m_position.y), sc(maxWidth) + 20, sc(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; - m_texture = g_pHyprRenderer->createTexture(m_cairoSurface); + // copy the data to an OpenGL texture we have + 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(std::move(data))); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } bool CHyprNotificationOverlay::hasAny() { - return !m_notifications.empty(); + return !m_vNotifications.empty(); } diff --git a/src/debug/HyprNotificationOverlay.hpp b/src/debug/HyprNotificationOverlay.hpp index ec7aed72..b04cb73b 100644 --- a/src/debug/HyprNotificationOverlay.hpp +++ b/src/debug/HyprNotificationOverlay.hpp @@ -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" @@ -18,7 +18,7 @@ enum eIconBackend : uint8_t { static const std::array, 3 /* backends */> ICONS_ARRAY = { std::array{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""}, std::array{"", "", "", "", "", "󰸞", ""}, std::array{"", "", "", "", "", ""}}; -static const std::array ICONS_COLORS = {CHyprColor{1.0, 204 / 255.0, 102 / 255.0, 1.0}, +static const std::array ICONS_COLORS = {CHyprColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0}, CHyprColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0}, CHyprColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0}, CHyprColor{255 / 255.0, 77 / 255.0, 77 / 255.0, 1.0}, @@ -47,17 +47,17 @@ class CHyprNotificationOverlay { private: CBox drawNotifications(PHLMONITOR pMonitor); - CBox m_lastDamage; + CBox m_bLastDamage; - std::vector> m_notifications; + std::vector> 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 m_texture; + SP m_pTexture; }; inline UP g_pHyprNotificationOverlay; diff --git a/src/debug/Log.cpp b/src/debug/Log.cpp new file mode 100644 index 00000000..9cf86345 --- /dev/null +++ b/src/debug/Log.cpp @@ -0,0 +1,74 @@ +#include "Log.hpp" +#include "../defines.hpp" +#include "RollingLogFollow.hpp" + +#include +#include +#include + +void Debug::init(const std::string& IS) { + logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log"); + logOfs.open(logFile, std::ios::out | std::ios::app); + auto handle = logOfs.native_handle(); + fcntl(handle, F_SETFD, FD_CLOEXEC); +} + +void Debug::close() { + logOfs.close(); +} + +void Debug::log(eLogLevel level, std::string str) { + if (level == TRACE && !trace) + return; + + if (shuttingDown) + return; + + std::string coloredStr = str; + //NOLINTBEGIN + switch (level) { + case LOG: + str = "[LOG] " + str; + coloredStr = str; + break; + case WARN: + str = "[WARN] " + str; + coloredStr = "\033[1;33m" + str + "\033[0m"; // yellow + break; + case ERR: + str = "[ERR] " + str; + coloredStr = "\033[1;31m" + str + "\033[0m"; // red + break; + case CRIT: + str = "[CRITICAL] " + str; + coloredStr = "\033[1;35m" + str + "\033[0m"; // magenta + break; + case INFO: + str = "[INFO] " + str; + coloredStr = "\033[1;32m" + str + "\033[0m"; // green + break; + case TRACE: + str = "[TRACE] " + str; + coloredStr = "\033[1;34m" + str + "\033[0m"; // blue + break; + default: break; + } + //NOLINTEND + + 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 (!disableLogs || !**disableLogs) { + // log to a file + logOfs << str << "\n"; + logOfs.flush(); + } + + // log it to the stdout too. + if (!disableStdout) + std::println("{}", ((coloredLogs && !**coloredLogs) ? str : coloredStr)); +} diff --git a/src/debug/Log.hpp b/src/debug/Log.hpp new file mode 100644 index 00000000..3791aeac --- /dev/null +++ b/src/debug/Log.hpp @@ -0,0 +1,77 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#define LOGMESSAGESIZE 1024 +#define ROLLING_LOG_SIZE 4096 + +enum eLogLevel : int8_t { + NONE = -1, + LOG = 0, + WARN, + ERR, + CRIT, + INFO, + TRACE +}; + +// NOLINTNEXTLINE(readability-identifier-naming) +namespace Debug { + 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 rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log + inline std::mutex logMutex; + + void init(const std::string& IS); + void close(); + + // + void log(eLogLevel level, std::string str); + + template + //NOLINTNEXTLINE + void log(eLogLevel level, std::format_string fmt, Args&&... args) { + std::lock_guard guard(logMutex); + + if (level == TRACE && !trace) + return; + + if (shuttingDown) + return; + + std::string logMsg = ""; + + // print date and time to the ofs + 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()}; + const auto hms = std::chrono::hh_mm_ss{zt.get_local_time() - std::chrono::floor(zt.get_local_time())}; +#else + // TODO: current clang 17 does not support `zoned_time`, remove this once clang 19 is ready + const auto hms = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}; +#endif + logMsg += std::format("[{}] ", hms); + } + + // no need for try {} catch {} because std::format_string ensures that vformat never throw std::format_error + // because + // 1. any faulty format specifier that sucks will cause a compilation error. + // 2. and `std::bad_alloc` is catastrophic, (Almost any operation in stdlib could throw this.) + // 3. this is actually what std::format in stdlib does + logMsg += std::vformat(fmt.get(), std::make_format_args(args...)); + + log(level, logMsg); + } +}; diff --git a/src/debug/RollingLogFollow.hpp b/src/debug/RollingLogFollow.hpp new file mode 100644 index 00000000..0042cd8d --- /dev/null +++ b/src/debug/RollingLogFollow.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include + +// NOLINTNEXTLINE(readability-identifier-naming) +namespace Debug { + struct SRollingLogFollow { + std::unordered_map 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 r(m); + return socketToRollingLogFollowQueue[socket].empty(); + } + + std::string debugInfo() { + std::shared_lock r(m); + return std::format("RollingLogFollow, got {} connections", socketToRollingLogFollowQueue.size()); + } + + std::string getLog(int socket) { + std::unique_lock w(m); + + const std::string ret = socketToRollingLogFollowQueue[socket]; + socketToRollingLogFollowQueue[socket] = ""; + + return ret; + }; + + void addLog(const std::string& log) { + std::unique_lock w(m); + running = true; + std::vector to_erase; + for (const auto& p : socketToRollingLogFollowQueue) + socketToRollingLogFollowQueue[p.first] += log + "\n"; + } + + bool isRunning() { + std::shared_lock r(m); + return running; + } + + void stopFor(int socket) { + std::unique_lock w(m); + socketToRollingLogFollowQueue.erase(socket); + if (socketToRollingLogFollowQueue.empty()) + running = false; + } + + void startFor(int socket) { + std::unique_lock w(m); + socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket); + running = true; + } + + static SRollingLogFollow& get() { + static SRollingLogFollow instance; + static std::mutex gm; + std::lock_guard lock(gm); + return instance; + }; + }; +} diff --git a/src/debug/TracyDefines.hpp b/src/debug/TracyDefines.hpp index d06332f3..49d296f6 100644 --- a/src/debug/TracyDefines.hpp +++ b/src/debug/TracyDefines.hpp @@ -2,7 +2,7 @@ #ifdef USE_TRACY_GPU -#include "log/Logger.hpp" +#include "Log.hpp" #include #include diff --git a/src/debug/crash/SignalSafe.hpp b/src/debug/crash/SignalSafe.hpp deleted file mode 100644 index 8ec967fe..00000000 --- a/src/debug/crash/SignalSafe.hpp +++ /dev/null @@ -1,203 +0,0 @@ -#pragma once - -#include "defines.hpp" -#include - -namespace SignalSafe { - template - class CMaxLengthCString { - public: - CMaxLengthCString() { - m_str[0] = '\0'; - } - - void operator+=(char const* rhs) { - write(rhs, strlen(rhs)); - } - - void write(char const* data, size_t len) { - if (m_boundsExceeded || m_strPos + len >= N) { - m_boundsExceeded = true; - return; - } - memcpy(m_str + m_strPos, data, len); - m_strPos += len; - m_str[m_strPos] = '\0'; - } - - void write(char c) { - if (m_boundsExceeded || m_strPos + 1 >= N) { - m_boundsExceeded = true; - return; - } - m_str[m_strPos] = c; - m_strPos++; - } - - void writeNum(size_t num) { - size_t d = 1; - - while (num / 10 >= d) { - d *= 10; - } - - while (num > 0) { - char c = '0' + (num / d); - write(c); - num %= d; - d /= 10; - } - } - - char const* getStr() { - return m_str; - } - - bool boundsExceeded() { - return m_boundsExceeded; - } - - private: - char m_str[N]; - size_t m_strPos = 0; - bool m_boundsExceeded = false; - }; - - template - class CBufFileWriter { - public: - CBufFileWriter(int fd_) : m_fd(fd_) { - ; - } - - ~CBufFileWriter() { - flush(); - } - - void write(char const* data, size_t len) { - while (len > 0) { - size_t to_add = std::min(len, sc(BUFSIZE) - m_writeBufPos); - memcpy(m_writeBuf + m_writeBufPos, data, to_add); - data += to_add; - len -= to_add; - m_writeBufPos += to_add; - if (m_writeBufPos == BUFSIZE) - flush(); - } - } - - void write(char c) { - if (m_writeBufPos == BUFSIZE) - flush(); - m_writeBuf[m_writeBufPos] = c; - m_writeBufPos++; - } - - void operator+=(char const* str) { - write(str, strlen(str)); - } - - void operator+=(std::string_view str) { - write(str.data(), str.size()); - } - - void operator+=(char c) { - write(c); - } - - void writeNum(size_t num) { - size_t d = 1; - - while (num / 10 >= d) { - d *= 10; - } - - while (num > 0) { - char c = '0' + (num / d); - write(c); - num %= d; - d /= 10; - } - } - - void writeCmdOutput(const char* cmd) { - int pipefd[2]; - if (pipe(pipefd) < 0) { - *this += "(argv)); - - CBufFileWriter<64> failmsg(pipefd[1]); - failmsg += " 0) { - write(readbuf, len); - } - if (len < 0) { - *this += "m_events.config.reloaded.listen([this]() { recheckCfg(); }); - recheckCfg(); -} - -void CLogger::recheckCfg() { - static auto PDISABLELOGS = CConfigValue("debug:disable_logs"); - static auto PDISABLETIME = CConfigValue("debug:disable_time"); - static auto PENABLESTDOUT = CConfigValue("debug:enable_stdout_logs"); - static auto PENABLECOLOR = CConfigValue("debug:colored_stdout_logs"); - - m_logger.setEnableStdout(!*PDISABLELOGS && *PENABLESTDOUT); - m_logsEnabled = !*PDISABLELOGS; - m_logger.setTime(!*PDISABLETIME); - m_logger.setEnableColor(*PENABLECOLOR); -} - -const std::string& CLogger::rolling() { - return m_logger.rollingLog(); -} - -Hyprutils::CLI::CLogger& CLogger::hu() { - return m_logger; -} diff --git a/src/debug/log/Logger.hpp b/src/debug/log/Logger.hpp deleted file mode 100644 index d4e868de..00000000 --- a/src/debug/log/Logger.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include - -#include "../../helpers/memory/Memory.hpp" -#include "../../helpers/env/Env.hpp" - -namespace Log { - class CLogger { - public: - CLogger(); - ~CLogger() = default; - - void initIS(const std::string_view& IS); - void initCallbacks(); - - void log(Hyprutils::CLI::eLogLevel level, const std::string_view& str); - - template - //NOLINTNEXTLINE - void log(Hyprutils::CLI::eLogLevel level, std::format_string fmt, Args&&... args) { - static bool TRACE = Env::isTrace(); - - if (!m_logsEnabled) - return; - - if (level == Hyprutils::CLI::LOG_TRACE && !TRACE) - return; - - std::string logMsg = ""; - - // no need for try {} catch {} because std::format_string ensures that vformat never throw std::format_error - // because - // 1. any faulty format specifier that sucks will cause a compilation error. - // 2. and `std::bad_alloc` is catastrophic, (Almost any operation in stdlib could throw this.) - // 3. this is actually what std::format in stdlib does - logMsg += std::vformat(fmt.get(), std::make_format_args(args...)); - - log(level, logMsg); - } - - const std::string& rolling(); - Hyprutils::CLI::CLogger& hu(); - - private: - void recheckCfg(); - - Hyprutils::CLI::CLogger m_logger; - bool m_logsEnabled = true; - }; - - inline UP logger = makeUnique(); - - // - inline constexpr const Hyprutils::CLI::eLogLevel DEBUG = Hyprutils::CLI::LOG_DEBUG; - inline constexpr const Hyprutils::CLI::eLogLevel WARN = Hyprutils::CLI::LOG_WARN; - inline constexpr const Hyprutils::CLI::eLogLevel ERR = Hyprutils::CLI::LOG_ERR; - inline constexpr const Hyprutils::CLI::eLogLevel CRIT = Hyprutils::CLI::LOG_CRIT; - inline constexpr const Hyprutils::CLI::eLogLevel INFO = Hyprutils::CLI::LOG_DEBUG; - inline constexpr const Hyprutils::CLI::eLogLevel TRACE = Hyprutils::CLI::LOG_TRACE; -}; diff --git a/src/debug/log/RollingLogFollow.hpp b/src/debug/log/RollingLogFollow.hpp deleted file mode 100644 index c1cce9eb..00000000 --- a/src/debug/log/RollingLogFollow.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace Log { - struct SRollingLogFollow { - std::unordered_map m_socketToRollingLogFollowQueue; - std::shared_mutex m_mutex; - bool m_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 r(m_mutex); - return m_socketToRollingLogFollowQueue[socket].empty(); - } - - std::string debugInfo() { - std::shared_lock r(m_mutex); - return std::format("RollingLogFollow, got {} connections", m_socketToRollingLogFollowQueue.size()); - } - - std::string getLog(int socket) { - std::unique_lock w(m_mutex); - - const std::string ret = m_socketToRollingLogFollowQueue[socket]; - m_socketToRollingLogFollowQueue[socket] = ""; - - return ret; - }; - - void addLog(const std::string_view& log) { - std::unique_lock w(m_mutex); - m_running = true; - std::vector to_erase; - for (const auto& p : m_socketToRollingLogFollowQueue) { - m_socketToRollingLogFollowQueue[p.first] += log; - m_socketToRollingLogFollowQueue[p.first] += "\n"; - } - } - - bool isRunning() { - std::shared_lock r(m_mutex); - return m_running; - } - - void stopFor(int socket) { - std::unique_lock w(m_mutex); - m_socketToRollingLogFollowQueue.erase(socket); - if (m_socketToRollingLogFollowQueue.empty()) - m_running = false; - } - - void startFor(int socket) { - std::unique_lock w(m_mutex); - m_socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket); - m_running = true; - } - - static SRollingLogFollow& get() { - static SRollingLogFollow instance; - static std::mutex gm; - std::lock_guard lock(gm); - return instance; - }; - }; -} diff --git a/src/defines.hpp b/src/defines.hpp index 571679dc..5c70f21a 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -1,11 +1,7 @@ #pragma once #include "includes.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #include "helpers/Color.hpp" #include "macros.hpp" #include "desktop/DesktopTypes.hpp" - -#if !defined(__GXX_RTTI) -#error "Hyprland requires C++ RTTI. Shit will hit the fan otherwise. Do not even try." -#endif diff --git a/src/desktop/DesktopTypes.hpp b/src/desktop/DesktopTypes.hpp index b52f17cd..080f13d3 100644 --- a/src/desktop/DesktopTypes.hpp +++ b/src/desktop/DesktopTypes.hpp @@ -1,30 +1,26 @@ #pragma once #include "../helpers/memory/Memory.hpp" - class CWorkspace; +class CWindow; +class CLayerSurface; class CMonitor; -namespace Desktop::View { - class CWindow; - class CLayerSurface; -} - /* Shared pointer to a workspace */ -using PHLWORKSPACE = SP; +typedef SP PHLWORKSPACE; /* Weak pointer to a workspace */ -using PHLWORKSPACEREF = WP; +typedef WP PHLWORKSPACEREF; /* Shared pointer to a window */ -using PHLWINDOW = SP; +typedef SP PHLWINDOW; /* Weak pointer to a window */ -using PHLWINDOWREF = WP; +typedef WP PHLWINDOWREF; /* Shared pointer to a layer surface */ -using PHLLS = SP; +typedef SP PHLLS; /* Weak pointer to a layer surface */ -using PHLLSREF = WP; +typedef WP PHLLSREF; /* Shared pointer to a monitor */ -using PHLMONITOR = SP; +typedef SP PHLMONITOR; /* Weak pointer to a monitor */ -using PHLMONITORREF = WP; \ No newline at end of file +typedef WP PHLMONITORREF; diff --git a/src/desktop/LayerRule.cpp b/src/desktop/LayerRule.cpp new file mode 100644 index 00000000..791a557e --- /dev/null +++ b/src/desktop/LayerRule.cpp @@ -0,0 +1,38 @@ +#include +#include "LayerRule.hpp" +#include +#include +#include "../debug/Log.hpp" + +static const auto RULES = std::unordered_set{"noanim", "blur", "blurpopups", "dimaround"}; +static const auto RULES_PREFIX = std::unordered_set{"ignorealpha", "ignorezero", "xray", "animation", "order"}; + +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 (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?!"); + ruleType = RULE_INVALID; + } +} \ No newline at end of file diff --git a/src/desktop/LayerRule.hpp b/src/desktop/LayerRule.hpp new file mode 100644 index 00000000..8cdb332e --- /dev/null +++ b/src/desktop/LayerRule.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include "Rule.hpp" + +class CLayerRule { + public: + CLayerRule(const std::string& rule, const std::string& targetNS); + + enum eRuleType : uint8_t { + RULE_INVALID = 0, + RULE_NOANIM, + RULE_BLUR, + RULE_BLURPOPUPS, + RULE_DIMAROUND, + RULE_IGNOREALPHA, + RULE_IGNOREZERO, + RULE_XRAY, + RULE_ANIMATION, + RULE_ORDER, + RULE_ZUMBA, + }; + + eRuleType ruleType = RULE_INVALID; + + const std::string targetNamespace; + const std::string rule; + + CRuleRegexContainer targetNamespaceRegex; +}; \ No newline at end of file diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp new file mode 100644 index 00000000..428dcbbc --- /dev/null +++ b/src/desktop/LayerSurface.cpp @@ -0,0 +1,598 @@ +#include "LayerSurface.hpp" +#include "../Compositor.hpp" +#include "../events/Events.hpp" +#include "../protocols/LayerShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../managers/SeatManager.hpp" +#include "../managers/AnimationManager.hpp" +#include "../render/Renderer.hpp" +#include "../config/ConfigManager.hpp" +#include "../helpers/Monitor.hpp" +#include "../managers/input/InputManager.hpp" +#include "../managers/HookSystemManager.hpp" +#include "../managers/EventManager.hpp" + +PHLLS CLayerSurface::create(SP resource) { + PHLLS pLS = SP(new CLayerSurface(resource)); + + auto pMonitor = resource->monitor.empty() ? g_pCompositor->m_pLastMonitor.lock() : g_pCompositor->getMonitorFromName(resource->monitor); + + pLS->surface->assign(resource->surface.lock(), pLS); + + if (!pMonitor) { + Debug::log(ERR, "New LS has no monitor??"); + return pLS; + } + + if (pMonitor->pMirrorOf) + pMonitor = g_pCompositor->m_vMonitors.front(); + + pLS->self = pLS; + + pLS->szNamespace = resource->layerNamespace; + + pLS->layer = resource->current.layer; + pLS->popupHead = CPopup::create(pLS); + pLS->monitor = pMonitor; + pMonitor->m_aLayerSurfaceLayers[resource->current.layer].emplace_back(pLS); + + pLS->forceBlur = g_pConfigManager->shouldBlurLS(pLS->szNamespace); + + 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->alpha->setValueAndWarp(0.f); + + 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() { + alpha->setUpdateCallback([this](auto) { + if (dimAround) + g_pHyprRenderer->damageMonitor(monitor.lock()); + }); +} + +CLayerSurface::CLayerSurface(SP 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(); }); + + surface = CWLSurface::create(); +} + +CLayerSurface::~CLayerSurface() { + if (!g_pHyprOpenGL) + return; + + if (surface) + surface->unassign(); + g_pHyprRenderer->makeEGLCurrent(); + std::erase_if(g_pHyprOpenGL->m_mLayerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == self.lock(); }); + + for (auto const& mon : g_pCompositor->m_vRealMonitors) { + for (auto& lsl : mon->m_aLayerSurfaceLayers) { + std::erase_if(lsl, [this](auto& ls) { return ls.expired() || ls.get() == this; }); + } + } +} + +void CLayerSurface::onDestroy() { + Debug::log(LOG, "LayerSurface {:x} destroyed", (uintptr_t)layerSurface.get()); + + const auto PMONITOR = monitor.lock(); + + if (!PMONITOR) + Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)"); + + 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 (alpha) + alpha->setValueAndWarp(0.f); + fadingOut = true; + g_pCompositor->addToFadingOutSafe(self.lock()); + } + } + + popupHead.reset(); + + noProcess = true; + + // rearrange to fix the reserved areas + if (PMONITOR) { + g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID); + PMONITOR->scheduledRecalc = true; + + // and damage + CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height}; + g_pHyprRenderer->damageBox(geomFixed); + } + + readyToDelete = true; + layerSurface.reset(); + if (surface) + surface->unassign(); + + listeners.unmap.reset(); + listeners.destroy.reset(); + listeners.map.reset(); + listeners.commit.reset(); +} + +void CLayerSurface::onMap() { + Debug::log(LOG, "LayerSurface {:x} mapped", (uintptr_t)layerSurface.get()); + + mapped = true; + interactivity = layerSurface->current.interactivity; + + layerSurface->surface->map(); + + // this layer might be re-mapped. + fadingOut = false; + g_pCompositor->removeFromFadingOutSafe(self.lock()); + + // fix if it changed its mon + const auto PMONITOR = monitor.lock(); + + if (!PMONITOR) + return; + + applyRules(); + + PMONITOR->scheduledRecalc = true; + + g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID); + + surface->resource()->enter(PMONITOR->self.lock()); + + const bool ISEXCLUSIVE = layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE; + + if (ISEXCLUSIVE) + g_pInputManager->m_dExclusiveLSes.push_back(self); + + const bool GRABSFOCUS = ISEXCLUSIVE || + (layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE && + // don't focus if constrained + (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained())); + + if (GRABSFOCUS) { + // TODO: use the new superb really very cool grab + g_pSeatManager->setGrab(nullptr); + g_pInputManager->releaseAllMouseButtons(); + g_pCompositor->focusSurface(surface->resource()); + + 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; + } + + position = Vector2D(geometry.x, geometry.y); + + CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height}; + g_pHyprRenderer->damageBox(geomFixed); + const bool FULLSCREEN = PMONITOR->activeWorkspace && PMONITOR->activeWorkspace->m_bHasFullscreenWindow && PMONITOR->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN; + + startAnimation(!(layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && FULLSCREEN && !GRABSFOCUS)); + readyToDelete = false; + fadingOut = false; + + g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", szNamespace}); + EMIT_HOOK_EVENT("openLayer", self.lock()); + + 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)layerSurface.get()); + + g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", layerSurface->layerNamespace}); + EMIT_HOOK_EVENT("closeLayer", self.lock()); + + std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); }); + + if (!monitor || g_pCompositor->m_bUnsafeState) { + Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring."); + + g_pCompositor->addToFadingOutSafe(self.lock()); + + mapped = false; + if (layerSurface && layerSurface->surface) + layerSurface->surface->unmap(); + + startAnimation(false); + return; + } + + // make a snapshot and start fade + g_pHyprRenderer->makeLayerSnapshot(self.lock()); + + startAnimation(false); + + mapped = false; + if (layerSurface && layerSurface->surface) + layerSurface->surface->unmap(); + + g_pCompositor->addToFadingOutSafe(self.lock()); + + const auto PMONITOR = monitor.lock(); + + 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_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 = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height}; + g_pHyprRenderer->damageBox(geomFixed); + + 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->ID); +} + +void CLayerSurface::onCommit() { + if (!layerSurface) + return; + + if (!mapped) { + // we're re-mapping if this is the case + if (layerSurface->surface && !layerSurface->surface->current.texture) { + fadingOut = false; + geometry = {}; + g_pHyprRenderer->arrangeLayersForMonitor(monitorID()); + } + + return; + } + + const auto PMONITOR = monitor.lock(); + + if (!PMONITOR) + return; + + 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 = {geometry.x, geometry.y, geometry.width, geometry.height}; + g_pHyprRenderer->damageBox(geomFixed); + + if (layerSurface->current.committed != 0) { + if (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_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_aLayerSurfaceLayers[layerSurface->current.layer].emplace_back(*it); + PMONITOR->m_aLayerSurfaceLayers[layer].erase(it); + break; + } + } + + layer = layerSurface->current.layer; + + 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->ID); + + PMONITOR->scheduledRecalc = true; + } else { + position = Vector2D(geometry.x, geometry.y); + + // update geom if it changed + if (layerSurface->surface->current.scale == 1 && PMONITOR->scale != 1.f && layerSurface->surface->current.viewport.hasDestination) { + // fractional scaling. Dirty hack. + 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. + geometry = {geometry.pos(), layerSurface->surface->current.size}; + } + } + + if (realPosition->goal() != geometry.pos()) { + if (realPosition->isBeingAnimated()) + *realPosition = geometry.pos(); + else + realPosition->setValueAndWarp(geometry.pos()); + } + if (realSize->goal() != geometry.size()) { + if (realSize->isBeingAnimated()) + *realSize = geometry.size(); + else + realSize->setValueAndWarp(geometry.size()); + } + + if (mapped && (layerSurface->current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) { + bool WASLASTFOCUS = false; + layerSurface->surface->breadthfirst( + [&WASLASTFOCUS](SP surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->state.keyboardFocus == surf; }, + nullptr); + if (!WASLASTFOCUS && popupHead) { + popupHead->breadthfirst( + [&WASLASTFOCUS](WP popup, void* data) { + WASLASTFOCUS = WASLASTFOCUS || (popup->m_pWLSurface && g_pSeatManager->state.keyboardFocus == popup->m_pWLSurface->resource()); + }, + nullptr); + } + 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_dExclusiveLSes.push_back(self); + else if (WASEXCLUSIVE && !ISEXCLUSIVE) + 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 && 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(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(surface->resource()); + + 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; + } + } + + interactivity = layerSurface->current.interactivity; + + g_pHyprRenderer->damageSurface(surface->resource(), position.x, position.y); + + g_pCompositor->setPreferredScaleForSurface(surface->resource(), PMONITOR->scale); + g_pCompositor->setPreferredTransformForSurface(surface->resource(), PMONITOR->transform); +} + +void CLayerSurface::applyRules() { + noAnimations = false; + forceBlur = false; + ignoreAlpha = false; + ignoreAlphaValue = 0.f; + dimAround = false; + xray = -1; + animationStyle.reset(); + + for (auto const& rule : g_pConfigManager->getMatchingRules(self.lock())) { + switch (rule->ruleType) { + case CLayerRule::RULE_NOANIM: { + noAnimations = true; + break; + } + case CLayerRule::RULE_BLUR: { + forceBlur = true; + break; + } + case CLayerRule::RULE_BLURPOPUPS: { + forceBlurPopups = true; + break; + } + case CLayerRule::RULE_IGNOREALPHA: + case CLayerRule::RULE_IGNOREZERO: { + const auto FIRST_SPACE_POS = rule->rule.find_first_of(' '); + std::string alphaValue = ""; + if (FIRST_SPACE_POS != std::string::npos) + alphaValue = rule->rule.substr(FIRST_SPACE_POS + 1); + + try { + ignoreAlpha = true; + if (!alphaValue.empty()) + ignoreAlphaValue = std::stof(alphaValue); + } catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); } + break; + } + case CLayerRule::RULE_DIMAROUND: { + dimAround = true; + break; + } + case CLayerRule::RULE_XRAY: { + CVarList vars{rule->rule, 0, ' '}; + try { + xray = configStringToInt(vars[1]).value_or(false); + } catch (...) {} + break; + } + case CLayerRule::RULE_ANIMATION: { + CVarList vars{rule->rule, 2, 's'}; + animationStyle = vars[1]; + break; + } + case CLayerRule::RULE_ORDER: { + CVarList vars{rule->rule, 2, 's'}; + try { + order = std::stoi(vars[1]); + } catch (...) { Debug::log(ERR, "Invalid value passed to order"); } + break; + } + default: break; + } + } +} + +void CLayerSurface::startAnimation(bool in, bool instant) { + if (in) { + realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn")); + realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn")); + alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn")); + } else { + realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut")); + realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut")); + alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut")); + } + + const auto ANIMSTYLE = animationStyle.value_or(realPosition->getStyle()); + if (ANIMSTYLE.starts_with("slide")) { + // get closest edge + const auto MIDDLE = geometry.middle(); + + const auto PMONITOR = g_pCompositor->getMonitorFromVector(MIDDLE); + + int force = -1; + + CVarList args(ANIMSTYLE, 0, 's'); + if (args.size() > 1) { + const auto ARG2 = args[1]; + if (ARG2 == "top") + force = 0; + else if (ARG2 == "bottom") + force = 1; + else if (ARG2 == "left") + force = 2; + else if (ARG2 == "right") + force = 3; + } + + const std::array edgePoints = { + 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::max(); + int leader = force; + if (leader == -1) { + for (size_t i = 0; i < 4; ++i) { + float dist = MIDDLE.distance(edgePoints[i]); + if (dist < closest) { + leader = i; + closest = dist; + } + } + } + + 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 = {geometry.x, PMONITOR->vecPosition.y - geometry.h}; + break; + case 1: + // BOTTOM + prePos = {geometry.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y}; + break; + case 2: + // LEFT + prePos = {PMONITOR->vecPosition.x - geometry.w, geometry.y}; + break; + case 3: + // RIGHT + prePos = {PMONITOR->vecPosition.x + PMONITOR->vecSize.x, geometry.y}; + break; + default: UNREACHABLE(); + } + + if (in) { + realPosition->setValueAndWarp(prePos); + *realPosition = geometry.pos(); + } else { + realPosition->setValueAndWarp(geometry.pos()); + *realPosition = prePos; + } + + } else if (ANIMSTYLE.starts_with("popin")) { + float minPerc = 0.f; + if (ANIMSTYLE.find("%") != std::string::npos) { + try { + auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ')); + minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { + ; // oops + } + } + + minPerc *= 0.01; + + const auto GOALSIZE = (geometry.size() * minPerc).clamp({5, 5}); + const auto GOALPOS = geometry.pos() + (geometry.size() - GOALSIZE) / 2.f; + + alpha->setValueAndWarp(in ? 0.f : 1.f); + *alpha = in ? 1.f : 0.f; + + if (in) { + realSize->setValueAndWarp(GOALSIZE); + realPosition->setValueAndWarp(GOALPOS); + *realSize = geometry.size(); + *realPosition = geometry.pos(); + } else { + realSize->setValueAndWarp(geometry.size()); + realPosition->setValueAndWarp(geometry.pos()); + *realSize = GOALSIZE; + *realPosition = GOALPOS; + } + } else { + // fade + realPosition->setValueAndWarp(geometry.pos()); + realSize->setValueAndWarp(geometry.size()); + *alpha = in ? 1.f : 0.f; + } + + if (!in) + fadingOut = true; +} + +bool CLayerSurface::isFadedOut() { + if (!fadingOut) + return false; + + return !realPosition->isBeingAnimated() && !realSize->isBeingAnimated() && !alpha->isBeingAnimated(); +} + +int CLayerSurface::popupsCount() { + if (!layerSurface || !mapped || fadingOut) + return 0; + + int no = -1; // we have one dummy + popupHead->breadthfirst([](WP p, void* data) { *(int*)data += 1; }, &no); + return no; +} + +MONITORID CLayerSurface::monitorID() { + return monitor ? monitor->ID : MONITOR_INVALID; +} + +pid_t CLayerSurface::getPID() { + pid_t PID = -1; + + if (!layerSurface || !layerSurface->surface || !layerSurface->surface->getResource() || !layerSurface->surface->getResource()->resource() || + !layerSurface->surface->getResource()->resource()->client) + return -1; + + wl_client_get_credentials(layerSurface->surface->getResource()->resource()->client, &PID, nullptr, nullptr); + + return PID; +} diff --git a/src/desktop/LayerSurface.hpp b/src/desktop/LayerSurface.hpp new file mode 100644 index 00000000..6aba5591 --- /dev/null +++ b/src/desktop/LayerSurface.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include "../defines.hpp" +#include "WLSurface.hpp" +#include "../helpers/AnimatedVariable.hpp" + +class CLayerShellResource; + +class CLayerSurface { + public: + static PHLLS create(SP); + + private: + CLayerSurface(SP); + + public: + ~CLayerSurface(); + + void applyRules(); + void startAnimation(bool in, bool instant = false); + bool isFadedOut(); + int popupsCount(); + + PHLANIMVAR realPosition; + PHLANIMVAR realSize; + PHLANIMVAR alpha; + + WP layerSurface; + wl_list link; + + // the header providing the enum type cannot be imported here + int interactivity = 0; + + SP surface; + + bool mapped = false; + uint32_t layer = 0; + + PHLMONITORREF monitor; + + bool fadingOut = false; + bool readyToDelete = false; + bool noProcess = false; + bool noAnimations = 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 animationStyle; + + PHLLSREF self; + + CBox geometry = {0, 0, 0, 0}; + Vector2D position; + std::string szNamespace = ""; + UP popupHead; + + pid_t getPID(); + + void onDestroy(); + void onMap(); + void onUnmap(); + void onCommit(); + MONITORID monitorID(); + + private: + struct { + CHyprSignalListener destroy; + CHyprSignalListener map; + CHyprSignalListener unmap; + CHyprSignalListener commit; + } listeners; + + void registerCallbacks(); + + // For the list lookup + bool operator==(const CLayerSurface& rhs) const { + return layerSurface == rhs.layerSurface && monitor == rhs.monitor; + } +}; diff --git a/src/desktop/Popup.cpp b/src/desktop/Popup.cpp new file mode 100644 index 00000000..912069e7 --- /dev/null +++ b/src/desktop/Popup.cpp @@ -0,0 +1,376 @@ +#include "Popup.hpp" +#include "../config/ConfigValue.hpp" +#include "../Compositor.hpp" +#include "../protocols/LayerShell.hpp" +#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" +#include "../render/OpenGL.hpp" +#include + +UP CPopup::create(PHLWINDOW pOwner) { + auto popup = UP(new CPopup()); + popup->m_pWindowOwner = pOwner; + popup->m_pSelf = popup; + popup->initAllSignals(); + return popup; +} + +UP CPopup::create(PHLLS pOwner) { + auto popup = UP(new CPopup()); + popup->m_pLayerOwner = pOwner; + popup->m_pSelf = popup; + popup->initAllSignals(); + return popup; +} + +UP CPopup::create(SP resource, WP pOwner) { + auto popup = UP(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_vLastSize = resource->surface->current.geometry.size(); + popup->reposition(); + + popup->initAllSignals(); + return popup; +} + +CPopup::~CPopup() { + if (m_pWLSurface) + m_pWLSurface->unassign(); +} + +void CPopup::initAllSignals() { + + 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>(d)); }); + else if (!m_pLayerOwner.expired()) + listeners.newPopup = m_pLayerOwner->layerSurface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); + else + ASSERT(false); + + return; + } + + 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>(d)); }); +} + +void CPopup::onNewPopup(SP 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_bInert = true; + + if (!m_pParent) + return; // head node + + std::erase_if(m_pParent->m_vChildren, [this](const auto& other) { return other.get() == this; }); +} + +void CPopup::onMap() { + if (m_bMapped) + return; + + 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_pWLSurface->resource()->extends(); + box.translate(COORDS).expand(4); + g_pHyprRenderer->damageBox(box); + + m_vLastPos = coordsRelativeToParent(); + + g_pInputManager->simulateMouseMovement(); + + m_pSubsurfaceHead = CSubsurface::create(m_pSelf); + + //unconstrain(); + sendScale(); + m_pResource->surface->surface->enter(PMONITOR->self.lock()); + + 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_bMapped) + return; + + if (!m_pResource || !m_pResource->surface) { + Debug::log(ERR, "CPopup: orphaned (no surface/resource) and unmaps??"); + onDestroy(); + return; + } + + m_bMapped = false; + + m_vLastSize = m_pResource->surface->surface->current.size; + + const auto COORDS = coordsGlobal(); + + CBox box = m_pWLSurface->resource()->extends(); + box.translate(COORDS).expand(4); + g_pHyprRenderer->damageBox(box); + + m_pSubsurfaceHead.reset(); + + 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 p, void* data) { + if (!p->m_pResource) + return; + + auto box = CBox{p->coordsGlobal(), p->size()}; + g_pHyprRenderer->damageBox(box); + }, + nullptr); + + // TODO: probably refocus, but without a motion event? + // const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource(); + + // if (WASLASTFOCUS) + // g_pInputManager->simulateMouseMovement(); +} + +void CPopup::onCommit(bool ignoreSiblings) { + if (!m_pResource || !m_pResource->surface) { + Debug::log(ERR, "CPopup: orphaned (no surface/resource) and commits??"); + onDestroy(); + return; + } + + if (m_pResource->surface->initialCommit) { + m_pResource->surface->scheduleConfigure(); + return; + } + + 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("debug:log_damage"); + if (*PLOGDAMAGE) + Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowOwner.lock()); + return; + } + + if (!m_pResource->surface->mapped) + return; + + const auto COORDS = coordsGlobal(); + const auto COORDSLOCAL = coordsRelativeToParent(); + + 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_vLastSize = m_pResource->surface->surface->current.size; + box = {COORDS, m_vLastSize}; + g_pHyprRenderer->damageBox(box); + + m_vLastPos = COORDSLOCAL; + } + + if (!ignoreSiblings && m_pSubsurfaceHead) + m_pSubsurfaceHead->recheckDamageForSubsurfaces(); + + g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y); + + m_bRequestedReposition = false; + + 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_bRequestedReposition = true; + + m_vLastPos = coordsRelativeToParent(); + + reposition(); +} + +void CPopup::reposition() { + const auto COORDS = t1ParentCoords(); + const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS); + + if (!PMONITOR) + return; + + CBox box = {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; + m_pResource->applyPositioning(box, COORDS); +} + +SP CPopup::getT1Owner() { + if (m_pWindowOwner) + return m_pWindowOwner->m_pWLSurface; + else + return m_pLayerOwner->surface; +} + +Vector2D CPopup::coordsRelativeToParent() { + Vector2D offset; + + if (!m_pResource) + return {}; + + WP current = m_pSelf; + offset -= current->m_pResource->surface->current.geometry.pos(); + + while (current->m_pParent && current->m_pResource) { + + offset += current->m_pWLSurface->resource()->current.offset; + offset += current->m_pResource->geometry.pos(); + + current = current->m_pParent; + } + + return offset; +} + +Vector2D CPopup::coordsGlobal() { + return localToGlobal(coordsRelativeToParent()); +} + +Vector2D CPopup::localToGlobal(const Vector2D& rel) { + return t1ParentCoords() + rel; +} + +Vector2D CPopup::t1ParentCoords() { + 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 curr = m_pSelf; + while (curr->m_pParent) { + curr = curr->m_pParent; + } + + curr->recheckChildrenRecursive(); +} + +void CPopup::recheckChildrenRecursive() { + if (m_bInert || !m_pWLSurface) + return; + + std::vector> cpy; + std::ranges::for_each(m_vChildren, [&cpy](const auto& el) { cpy.emplace_back(el); }); + for (auto const& c : cpy) { + c->onCommit(true); + c->recheckChildrenRecursive(); + } +} + +Vector2D CPopup::size() { + return m_vLastSize; +} + +void CPopup::sendScale() { + 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_pWindowOwner.expired()) + return g_pHyprRenderer->shouldRenderWindow(m_pWindowOwner.lock()); + if (!m_pLayerOwner.expired()) + return true; + if (m_pParent) + return m_pParent->visible(); + + return false; +} + +void CPopup::bfHelper(std::vector> const& nodes, std::function, void*)> fn, void* data) { + for (auto const& n : nodes) { + fn(n, data); + } + + std::vector> nodes2; + nodes2.reserve(nodes.size() * 2); + + for (auto const& n : nodes) { + for (auto const& c : n->m_vChildren) { + nodes2.push_back(c->m_pSelf); + } + } + + if (!nodes2.empty()) + bfHelper(nodes2, fn, data); +} + +void CPopup::breadthfirst(std::function, void*)> fn, void* data) { + std::vector> popups; + popups.push_back(m_pSelf); + bfHelper(popups, fn, data); +} + +WP CPopup::at(const Vector2D& globalCoords, bool allowsInput) { + std::vector> popups; + breadthfirst([&popups](WP popup, void* data) { popups.push_back(popup); }, &popups); + + for (auto const& p : popups | std::views::reverse) { + if (!p->m_pResource || !p->m_bMapped) + continue; + + if (!allowsInput) { + const bool HASSURFACE = p->m_pResource && p->m_pResource->surface; + + 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(); + + const auto BOX = CBox{p->coordsGlobal() + offset, size}; + if (BOX.containsPoint(globalCoords)) + return p; + } else { + 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; + } + } + + return {}; +} + +bool CPopup::inert() const { + return m_bInert; +} diff --git a/src/desktop/Popup.hpp b/src/desktop/Popup.hpp new file mode 100644 index 00000000..f6ca65da --- /dev/null +++ b/src/desktop/Popup.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include +#include "Subsurface.hpp" +#include "../helpers/signal/Signal.hpp" +#include "../helpers/memory/Memory.hpp" + +class CXDGPopupResource; + +class CPopup { + public: + // dummy head nodes + static UP create(PHLWINDOW pOwner); + static UP create(PHLLS pOwner); + + // real nodes + static UP create(SP popup, WP pOwner); + + ~CPopup(); + + SP getT1Owner(); + Vector2D coordsRelativeToParent(); + Vector2D coordsGlobal(); + + Vector2D size(); + + void onNewPopup(SP popup); + void onDestroy(); + void onMap(); + void onUnmap(); + void onCommit(bool ignoreSiblings = false); + void onReposition(); + + void recheckTree(); + + bool visible(); + bool inert() const; + + // will also loop over this node + void breadthfirst(std::function, void*)> fn, void* data); + WP at(const Vector2D& globalCoords, bool allowsInput = false); + + // + SP m_pWLSurface; + WP m_pSelf; + bool m_bMapped = false; + + private: + CPopup() = default; + + // T1 owners, each popup has to have one of these + PHLWINDOWREF m_pWindowOwner; + PHLLSREF m_pLayerOwner; + + // T2 owners + WP m_pParent; + + WP m_pResource; + + Vector2D m_vLastSize = {}; + Vector2D m_vLastPos = {}; + + bool m_bRequestedReposition = false; + + bool m_bInert = false; + + // + std::vector> m_vChildren; + UP m_pSubsurfaceHead; + + struct { + CHyprSignalListener newPopup; + CHyprSignalListener destroy; + CHyprSignalListener map; + CHyprSignalListener unmap; + CHyprSignalListener commit; + CHyprSignalListener dismissed; + CHyprSignalListener reposition; + } listeners; + + void initAllSignals(); + void reposition(); + void recheckChildrenRecursive(); + void sendScale(); + + Vector2D localToGlobal(const Vector2D& rel); + Vector2D t1ParentCoords(); + static void bfHelper(std::vector> const& nodes, std::function, void*)> fn, void* data); +}; diff --git a/src/desktop/Rule.cpp b/src/desktop/Rule.cpp new file mode 100644 index 00000000..aa87a483 --- /dev/null +++ b/src/desktop/Rule.cpp @@ -0,0 +1,22 @@ +#include +#include "../helpers/memory/Memory.hpp" +#include "Rule.hpp" +#include "../debug/Log.hpp" + +CRuleRegexContainer::CRuleRegexContainer(const std::string& regex_) { + const bool NEGATIVE = regex_.starts_with("negative:"); + + negative = NEGATIVE; + regex = makeUnique(NEGATIVE ? regex_.substr(9) : regex_); + + // TODO: maybe pop an error? + if (!regex->ok()) + Debug::log(ERR, "RuleRegexContainer: regex {} failed to parse!", regex_); +} + +bool CRuleRegexContainer::passes(const std::string& str) const { + if (!regex) + return false; + + return RE2::FullMatch(str, *regex) != negative; +} \ No newline at end of file diff --git a/src/desktop/Rule.hpp b/src/desktop/Rule.hpp new file mode 100644 index 00000000..858b4ded --- /dev/null +++ b/src/desktop/Rule.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +//NOLINTNEXTLINE +namespace re2 { + class RE2; +}; + +class CRuleRegexContainer { + public: + CRuleRegexContainer() = default; + + CRuleRegexContainer(const std::string& regex); + + bool passes(const std::string& str) const; + + private: + Hyprutils::Memory::CUniquePointer regex; + bool negative = false; +}; \ No newline at end of file diff --git a/src/desktop/Subsurface.cpp b/src/desktop/Subsurface.cpp new file mode 100644 index 00000000..9846764c --- /dev/null +++ b/src/desktop/Subsurface.cpp @@ -0,0 +1,228 @@ +#include "Subsurface.hpp" +#include "../events/Events.hpp" +#include "../Compositor.hpp" +#include "../config/ConfigValue.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../protocols/core/Subcompositor.hpp" +#include "../render/Renderer.hpp" +#include "../managers/input/InputManager.hpp" + +UP CSubsurface::create(PHLWINDOW pOwner) { + auto subsurface = UP(new CSubsurface()); + subsurface->m_pWindowParent = pOwner; + subsurface->m_pSelf = subsurface; + + subsurface->initSignals(); + subsurface->initExistingSubsurfaces(pOwner->m_pWLSurface->resource()); + return subsurface; +} + +UP CSubsurface::create(WP pOwner) { + auto subsurface = UP(new CSubsurface()); + subsurface->m_pPopupParent = pOwner; + subsurface->m_pSelf = subsurface; + subsurface->initSignals(); + subsurface->initExistingSubsurfaces(pOwner->m_pWLSurface->resource()); + return subsurface; +} + +UP CSubsurface::create(SP pSubsurface, PHLWINDOW pOwner) { + auto subsurface = UP(new CSubsurface()); + subsurface->m_pWindowParent = pOwner; + subsurface->m_pSubsurface = pSubsurface; + subsurface->m_pSelf = subsurface; + subsurface->m_pWLSurface = CWLSurface::create(); + subsurface->m_pWLSurface->assign(pSubsurface->surface.lock(), subsurface.get()); + subsurface->initSignals(); + subsurface->initExistingSubsurfaces(pSubsurface->surface.lock()); + return subsurface; +} + +UP CSubsurface::create(SP pSubsurface, WP pOwner) { + auto subsurface = UP(new CSubsurface()); + subsurface->m_pPopupParent = pOwner; + subsurface->m_pSubsurface = pSubsurface; + subsurface->m_pSelf = subsurface; + subsurface->m_pWLSurface = CWLSurface::create(); + subsurface->m_pWLSurface->assign(pSubsurface->surface.lock(), subsurface.get()); + subsurface->initSignals(); + subsurface->initExistingSubsurfaces(pSubsurface->surface.lock()); + return subsurface; +} + +void CSubsurface::initSignals() { + if (m_pSubsurface) { + listeners.commitSubsurface = m_pSubsurface->surface->events.commit.registerListener([this](std::any d) { onCommit(); }); + listeners.destroySubsurface = m_pSubsurface->events.destroy.registerListener([this](std::any d) { onDestroy(); }); + listeners.mapSubsurface = m_pSubsurface->surface->events.map.registerListener([this](std::any d) { onMap(); }); + listeners.unmapSubsurface = m_pSubsurface->surface->events.unmap.registerListener([this](std::any d) { onUnmap(); }); + listeners.newSubsurface = + m_pSubsurface->surface->events.newSubsurface.registerListener([this](std::any d) { onNewSubsurface(std::any_cast>(d)); }); + } else { + if (m_pWindowParent) + listeners.newSubsurface = m_pWindowParent->m_pWLSurface->resource()->events.newSubsurface.registerListener( + [this](std::any d) { onNewSubsurface(std::any_cast>(d)); }); + else if (m_pPopupParent) + listeners.newSubsurface = m_pPopupParent->m_pWLSurface->resource()->events.newSubsurface.registerListener( + [this](std::any d) { onNewSubsurface(std::any_cast>(d)); }); + else + ASSERT(false); + } +} + +void CSubsurface::checkSiblingDamage() { + if (!m_pParent) + return; // ?????????? + + const double SCALE = m_pWindowParent.lock() && m_pWindowParent->m_bIsX11 ? 1.0 / m_pWindowParent->m_fX11SurfaceScaledBy : 1.0; + + for (auto const& n : m_pParent->m_vChildren) { + if (n.get() == this) + continue; + + const auto COORDS = n->coordsGlobal(); + g_pHyprRenderer->damageSurface(n->m_pWLSurface->resource(), COORDS.x, COORDS.y, SCALE); + } +} + +void CSubsurface::recheckDamageForSubsurfaces() { + for (auto const& n : m_vChildren) { + const auto COORDS = n->coordsGlobal(); + g_pHyprRenderer->damageSurface(n->m_pWLSurface->resource(), COORDS.x, COORDS.y); + } +} + +void CSubsurface::onCommit() { + // no damaging if it's not visible + if (!m_pWindowParent.expired() && (!m_pWindowParent->m_bIsMapped || !m_pWindowParent->m_pWorkspace->m_bVisible)) { + m_vLastSize = m_pWLSurface->resource()->current.size; + + static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); + if (*PLOGDAMAGE) + Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_pWindowParent.lock()); + return; + } + + const auto COORDS = coordsGlobal(); + + g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y); + + if (m_pPopupParent && !m_pPopupParent->inert() && m_pPopupParent->m_pWLSurface) + m_pPopupParent->recheckTree(); + if (!m_pWindowParent.expired()) // I hate you firefox why are you doing this + m_pWindowParent->m_pPopupHead->recheckTree(); + + // I do not think this is correct, but it solves a lot of issues with some apps (e.g. firefox) + checkSiblingDamage(); + + if (m_vLastSize != m_pWLSurface->resource()->current.size) { + // TODO: fix this + // CBox box{COORDS, m_vLastSize}; + // g_pHyprRenderer->damageBox(box); + // m_vLastSize = m_pWLSurface->resource()->current.size; + // box = {COORDS, m_vLastSize}; + // g_pHyprRenderer->damageBox(box); + + CBox box; + if (m_pPopupParent && !m_pPopupParent->inert() && m_pPopupParent->m_pWLSurface) + box = m_pPopupParent->m_pWLSurface->getSurfaceBoxGlobal().value_or(CBox{}); + else if (m_pWindowParent) + box = m_pWindowParent->getWindowMainSurfaceBox(); + + g_pHyprRenderer->damageBox(box); + } +} + +void CSubsurface::onDestroy() { + // destroy children + m_vChildren.clear(); + + m_bInert = true; + + if (!m_pSubsurface) + return; // dummy node, nothing to do, it's the parent dying + + // kill ourselves + std::erase_if(m_pParent->m_vChildren, [this](const auto& other) { return other.get() == this; }); +} + +void CSubsurface::onNewSubsurface(SP pSubsurface) { + WP PSUBSURFACE; + + if (!m_pWindowParent.expired()) + PSUBSURFACE = m_vChildren.emplace_back(CSubsurface::create(pSubsurface, m_pWindowParent.lock())); + else if (m_pPopupParent) + PSUBSURFACE = m_vChildren.emplace_back(CSubsurface::create(pSubsurface, m_pPopupParent)); + + PSUBSURFACE->m_pSelf = PSUBSURFACE; + + ASSERT(PSUBSURFACE); + + PSUBSURFACE->m_pParent = m_pSelf; +} + +void CSubsurface::onMap() { + m_vLastSize = m_pWLSurface->resource()->current.size; + + const auto COORDS = coordsGlobal(); + CBox box{COORDS, m_vLastSize}; + box.expand(4); + g_pHyprRenderer->damageBox(box); + + if (!m_pWindowParent.expired()) + m_pWindowParent->updateSurfaceScaleTransformDetails(); +} + +void CSubsurface::onUnmap() { + const auto COORDS = coordsGlobal(); + CBox box{COORDS, m_vLastSize}; + box.expand(4); + g_pHyprRenderer->damageBox(box); + + if (m_pWLSurface->resource() == g_pCompositor->m_pLastFocus) + g_pInputManager->releaseAllMouseButtons(); + + g_pInputManager->simulateMouseMovement(); + + // TODO: should this remove children? Currently it won't, only on .destroy +} + +Vector2D CSubsurface::coordsRelativeToParent() { + if (!m_pSubsurface) + return {}; + return m_pSubsurface->posRelativeToParent(); +} + +Vector2D CSubsurface::coordsGlobal() { + Vector2D coords = coordsRelativeToParent(); + + if (!m_pWindowParent.expired()) + coords += m_pWindowParent->m_vRealPosition->value(); + else if (m_pPopupParent) + coords += m_pPopupParent->coordsGlobal(); + + return coords; +} + +void CSubsurface::initExistingSubsurfaces(SP pSurface) { + for (auto const& s : pSurface->subsurfaces) { + if (!s || s->surface->hlSurface /* already assigned */) + continue; + onNewSubsurface(s.lock()); + } +} + +Vector2D CSubsurface::size() { + return m_pWLSurface->resource()->current.size; +} + +bool CSubsurface::visible() { + if (!m_pWindowParent.expired()) + return g_pHyprRenderer->shouldRenderWindow(m_pWindowParent.lock()); + if (m_pPopupParent) + return m_pPopupParent->visible(); + if (m_pParent) + return m_pParent->visible(); + + return false; +} diff --git a/src/desktop/Subsurface.hpp b/src/desktop/Subsurface.hpp new file mode 100644 index 00000000..2983c7c1 --- /dev/null +++ b/src/desktop/Subsurface.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include "../defines.hpp" +#include +#include "WLSurface.hpp" + +class CPopup; +class CWLSubsurfaceResource; + +class CSubsurface { + public: + // root dummy nodes + static UP create(PHLWINDOW pOwner); + static UP create(WP pOwner); + + // real nodes + static UP create(SP pSubsurface, PHLWINDOW pOwner); + static UP create(SP pSubsurface, WP pOwner); + + ~CSubsurface() = default; + + Vector2D coordsRelativeToParent(); + Vector2D coordsGlobal(); + + Vector2D size(); + + void onCommit(); + void onDestroy(); + void onNewSubsurface(SP pSubsurface); + void onMap(); + void onUnmap(); + + bool visible(); + + void recheckDamageForSubsurfaces(); + + WP m_pSelf; + + private: + CSubsurface() = default; + + struct { + CHyprSignalListener destroySubsurface; + CHyprSignalListener commitSubsurface; + CHyprSignalListener mapSubsurface; + CHyprSignalListener unmapSubsurface; + CHyprSignalListener newSubsurface; + } listeners; + + WP m_pSubsurface; + SP m_pWLSurface; + Vector2D m_vLastSize = {}; + + // if nullptr, means it's a dummy node + WP m_pParent; + + PHLWINDOWREF m_pWindowParent; + WP m_pPopupParent; + + std::vector> m_vChildren; + + bool m_bInert = false; + + void initSignals(); + void initExistingSubsurfaces(SP pSurface); + void checkSiblingDamage(); +}; diff --git a/src/desktop/WLSurface.cpp b/src/desktop/WLSurface.cpp new file mode 100644 index 00000000..11da7b68 --- /dev/null +++ b/src/desktop/WLSurface.cpp @@ -0,0 +1,234 @@ +#include "WLSurface.hpp" +#include "LayerSurface.hpp" +#include "../desktop/Window.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../protocols/LayerShell.hpp" +#include "../render/Renderer.hpp" + +void CWLSurface::assign(SP pSurface) { + m_pResource = pSurface; + init(); + m_bInert = false; +} + +void CWLSurface::assign(SP pSurface, PHLWINDOW pOwner) { + m_pWindowOwner = pOwner; + m_pResource = pSurface; + init(); + m_bInert = false; +} + +void CWLSurface::assign(SP pSurface, PHLLS pOwner) { + m_pLayerOwner = pOwner; + m_pResource = pSurface; + init(); + m_bInert = false; +} + +void CWLSurface::assign(SP pSurface, CSubsurface* pOwner) { + m_pSubsurfaceOwner = pOwner; + m_pResource = pSurface; + init(); + m_bInert = false; +} + +void CWLSurface::assign(SP pSurface, CPopup* pOwner) { + m_pPopupOwner = pOwner; + m_pResource = pSurface; + init(); + m_bInert = false; +} + +void CWLSurface::unassign() { + destroy(); +} + +CWLSurface::~CWLSurface() { + destroy(); +} + +bool CWLSurface::exists() const { + return m_pResource; +} + +SP CWLSurface::resource() const { + return m_pResource.lock(); +} + +bool CWLSurface::small() const { + if (!validMapped(m_pWindowOwner) || !exists()) + return false; + + if (!m_pResource->current.texture) + return false; + + const auto O = m_pWindowOwner.lock(); + + return O->m_vReportedSize.x > m_pResource->current.size.x + 1 || O->m_vReportedSize.y > m_pResource->current.size.y + 1; +} + +Vector2D CWLSurface::correctSmallVec() const { + if (!validMapped(m_pWindowOwner) || !exists() || !small() || m_bFillIgnoreSmall) + return {}; + + const auto SIZE = getViewporterCorrectedSize(); + const auto O = m_pWindowOwner.lock(); + + return Vector2D{(O->m_vReportedSize.x - SIZE.x) / 2, (O->m_vReportedSize.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_vRealSize->value() / O->m_vReportedSize); +} + +Vector2D CWLSurface::correctSmallVecBuf() const { + if (!exists() || !small() || m_bFillIgnoreSmall || !m_pResource->current.texture) + return {}; + + const auto SIZE = getViewporterCorrectedSize(); + const auto BS = m_pResource->current.bufferSize; + + return Vector2D{(BS.x - SIZE.x) / 2, (BS.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}); +} + +Vector2D CWLSurface::getViewporterCorrectedSize() const { + if (!exists() || !m_pResource->current.texture) + return {}; + + return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.bufferSize; +} + +CRegion CWLSurface::computeDamage() const { + if (!m_pResource->current.texture) + return {}; + + CRegion damage = m_pResource->current.accumulateBufferDamage(); + damage.transform(wlTransformToHyprutils(m_pResource->current.transform), m_pResource->current.bufferSize.x, m_pResource->current.bufferSize.y); + + const auto BUFSIZE = m_pResource->current.bufferSize; + const auto CORRECTVEC = correctSmallVecBuf(); + + if (m_pResource->current.viewport.hasSource) + damage.intersect(m_pResource->current.viewport.source); + + const auto SCALEDSRCSIZE = m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.bufferSize; + + damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y}); + damage.translate(CORRECTVEC); + + // go from buffer coords in the damage to hl logical + + const auto BOX = getSurfaceBoxGlobal(); + const Vector2D SCALE = BOX.has_value() ? BOX->size() / m_pResource->current.bufferSize : + Vector2D{1.0 / m_pResource->current.scale, 1.0 / m_pResource->current.scale /* Wrong... but we can't really do better */}; + + damage.scale(SCALE); + + if (m_pWindowOwner) + damage.scale(m_pWindowOwner->m_fX11SurfaceScaledBy); // fix xwayland:force_zero_scaling stuff that will be fucked by the above a bit + + return damage; +} + +void CWLSurface::destroy() { + if (!m_pResource) + return; + + events.destroy.emit(); + + m_pConstraint.reset(); + + listeners.destroy.reset(); + m_pResource->hlSurface.reset(); + m_pWindowOwner.reset(); + m_pLayerOwner.reset(); + m_pPopupOwner = nullptr; + m_pSubsurfaceOwner = nullptr; + m_bInert = true; + + if (g_pHyprRenderer && g_pHyprRenderer->m_sLastCursorData.surf && g_pHyprRenderer->m_sLastCursorData.surf->get() == this) + g_pHyprRenderer->m_sLastCursorData.surf.reset(); + + m_pResource.reset(); + + Debug::log(LOG, "CWLSurface {:x} called destroy()", (uintptr_t)this); +} + +void CWLSurface::init() { + if (!m_pResource) + return; + + RASSERT(!m_pResource->hlSurface, "Attempted to duplicate CWLSurface ownership!"); + + m_pResource->hlSurface = self.lock(); + + listeners.destroy = m_pResource->events.destroy.registerListener([this](std::any d) { destroy(); }); + + Debug::log(LOG, "CWLSurface {:x} called init()", (uintptr_t)this); +} + +PHLWINDOW CWLSurface::getWindow() const { + return m_pWindowOwner.lock(); +} + +PHLLS CWLSurface::getLayer() const { + return m_pLayerOwner.lock(); +} + +CPopup* CWLSurface::getPopup() const { + return m_pPopupOwner; +} + +CSubsurface* CWLSurface::getSubsurface() const { + return m_pSubsurfaceOwner; +} + +bool CWLSurface::desktopComponent() const { + return !m_pLayerOwner.expired() || !m_pWindowOwner.expired() || m_pSubsurfaceOwner || m_pPopupOwner; +} + +std::optional CWLSurface::getSurfaceBoxGlobal() const { + if (!desktopComponent()) + return {}; + + if (!m_pWindowOwner.expired()) + return m_pWindowOwner->getWindowMainSurfaceBox(); + if (!m_pLayerOwner.expired()) + return m_pLayerOwner->geometry; + if (m_pPopupOwner) + return CBox{m_pPopupOwner->coordsGlobal(), m_pPopupOwner->size()}; + if (m_pSubsurfaceOwner) + return CBox{m_pSubsurfaceOwner->coordsGlobal(), m_pSubsurfaceOwner->size()}; + + return {}; +} + +void CWLSurface::appendConstraint(WP constraint) { + m_pConstraint = constraint; +} + +SP CWLSurface::constraint() const { + return m_pConstraint.lock(); +} + +bool CWLSurface::visible() { + if (!m_pWindowOwner.expired()) + return g_pHyprRenderer->shouldRenderWindow(m_pWindowOwner.lock()); + if (!m_pLayerOwner.expired()) + return true; + if (m_pPopupOwner) + return m_pPopupOwner->visible(); + if (m_pSubsurfaceOwner) + return m_pSubsurfaceOwner->visible(); + return true; // non-desktop, we don't know much. +} + +SP CWLSurface::fromResource(SP pSurface) { + if (!pSurface) + return nullptr; + return pSurface->hlSurface.lock(); +} + +bool CWLSurface::keyboardFocusable() const { + if (m_pWindowOwner || m_pPopupOwner || m_pSubsurfaceOwner) + return true; + if (m_pLayerOwner && m_pLayerOwner->layerSurface) + return m_pLayerOwner->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; + return false; +} diff --git a/src/desktop/WLSurface.hpp b/src/desktop/WLSurface.hpp new file mode 100644 index 00000000..5f8da715 --- /dev/null +++ b/src/desktop/WLSurface.hpp @@ -0,0 +1,124 @@ +#pragma once + +#include "../defines.hpp" +#include "../helpers/math/Math.hpp" +#include "../helpers/signal/Signal.hpp" + +class CSubsurface; +class CPopup; +class CPointerConstraint; +class CWLSurfaceResource; + +class CWLSurface { + public: + static SP create() { + auto p = SP(new CWLSurface); + p->self = p; + return p; + } + ~CWLSurface(); + + // anonymous surfaces are non-desktop components, e.g. a cursor surface or a DnD + void assign(SP pSurface); + void assign(SP pSurface, PHLWINDOW pOwner); + void assign(SP pSurface, PHLLS pOwner); + void assign(SP pSurface, CSubsurface* pOwner); + void assign(SP pSurface, CPopup* pOwner); + void unassign(); + + CWLSurface(const CWLSurface&) = delete; + CWLSurface(CWLSurface&&) = delete; + CWLSurface& operator=(const CWLSurface&) = delete; + CWLSurface& operator=(CWLSurface&&) = delete; + + SP resource() const; + bool exists() const; + bool small() const; // means surface is smaller than the requested size + Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces + Vector2D correctSmallVecBuf() const; // returns a corrective vector for small() surfaces, in BL coords + Vector2D getViewporterCorrectedSize() const; + CRegion computeDamage() const; // logical coordinates. May be wrong if the surface is unassigned + bool visible(); + bool keyboardFocusable() const; + + // getters for owners. + PHLWINDOW getWindow() const; + PHLLS getLayer() const; + CPopup* getPopup() const; + CSubsurface* getSubsurface() const; + + // desktop components misc utils + std::optional getSurfaceBoxGlobal() const; + void appendConstraint(WP constraint); + SP constraint() const; + + // allow stretching. Useful for plugins. + bool m_bFillIgnoreSmall = false; + + // track surface data and avoid dupes + float m_fLastScale = 0; + int m_iLastScale = 0; + wl_output_transform m_eLastTransform = (wl_output_transform)-1; + + // + CWLSurface& operator=(SP pSurface) { + destroy(); + m_pResource = pSurface; + init(); + + return *this; + } + + bool operator==(const CWLSurface& other) const { + return other.resource() == resource(); + } + + bool operator==(const SP other) const { + return other == resource(); + } + + explicit operator bool() const { + return exists(); + } + + static SP fromResource(SP pSurface); + + // used by the alpha-modifier protocol + float m_fAlphaModifier = 1.F; + + // used by the hyprland-surface protocol + float m_fOverallOpacity = 1.F; + CRegion m_visibleRegion; + + struct { + CSignal destroy; + } events; + + WP self; + + private: + CWLSurface() = default; + + bool m_bInert = true; + + WP m_pResource; + + PHLWINDOWREF m_pWindowOwner; + PHLLSREF m_pLayerOwner; + CPopup* m_pPopupOwner = nullptr; + CSubsurface* m_pSubsurfaceOwner = nullptr; + + // + WP m_pConstraint; + + void destroy(); + void init(); + bool desktopComponent() const; + + struct { + CHyprSignalListener destroy; + } listeners; + + friend class CPointerConstraint; + friend class CXxColorManagerV4; +}; diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp new file mode 100644 index 00000000..2cd8b438 --- /dev/null +++ b/src/desktop/Window.cpp @@ -0,0 +1,1808 @@ +#include +#include + +#include +#include +#include +#include +#include "Window.hpp" +#include "../Compositor.hpp" +#include "../render/decorations/CHyprDropShadowDecoration.hpp" +#include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include "../render/decorations/CHyprBorderDecoration.hpp" +#include "../config/ConfigValue.hpp" +#include "../managers/TokenManager.hpp" +#include "../managers/AnimationManager.hpp" +#include "../managers/ANRManager.hpp" +#include "../protocols/XDGShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../protocols/ContentType.hpp" +#include "../xwayland/XWayland.hpp" +#include "../helpers/Color.hpp" +#include "../events/Events.hpp" +#include "../managers/XWaylandManager.hpp" +#include "../render/Renderer.hpp" +#include "../managers/LayoutManager.hpp" +#include "../managers/HookSystemManager.hpp" +#include "../managers/EventManager.hpp" +#include "../managers/input/InputManager.hpp" + +#include + +using namespace Hyprutils::String; +using namespace Hyprutils::Animation; +using enum NContentType::eContentType; + +PHLWINDOW CWindow::create(SP surface) { + PHLWINDOW pWindow = SP(new CWindow(surface)); + + pWindow->m_pSelf = pWindow; + pWindow->m_bIsX11 = true; + + g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_vRealPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_vRealSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fBorderFadeAnimationProgress, g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_BORDER); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fBorderAngleAnimationProgress, g_pConfigManager->getAnimationPropertyConfig("borderangle"), pWindow, AVARDAMAGE_BORDER); + g_pAnimationManager->createAnimation(1.f, pWindow->m_fAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(1.f, pWindow->m_fActiveInactiveAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(CHyprColor(), pWindow->m_cRealShadowColor, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fDimPercent, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fMovingToWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fMovingFromWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(0.f, pWindow->m_notRespondingTint, g_pConfigManager->getAnimationPropertyConfig("fade"), pWindow, AVARDAMAGE_ENTIRE); + + pWindow->addWindowDeco(makeUnique(pWindow)); + pWindow->addWindowDeco(makeUnique(pWindow)); + + return pWindow; +} + +PHLWINDOW CWindow::create(SP resource) { + PHLWINDOW pWindow = SP(new CWindow(resource)); + + pWindow->m_pSelf = pWindow; + resource->toplevel->window = pWindow; + + g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_vRealPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_vRealSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fBorderFadeAnimationProgress, g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_BORDER); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fBorderAngleAnimationProgress, g_pConfigManager->getAnimationPropertyConfig("borderangle"), pWindow, AVARDAMAGE_BORDER); + g_pAnimationManager->createAnimation(1.f, pWindow->m_fAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(1.f, pWindow->m_fActiveInactiveAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(CHyprColor(), pWindow->m_cRealShadowColor, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fDimPercent, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fMovingToWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(0.f, pWindow->m_fMovingFromWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(0.f, pWindow->m_notRespondingTint, g_pConfigManager->getAnimationPropertyConfig("fade"), pWindow, AVARDAMAGE_ENTIRE); + + pWindow->addWindowDeco(makeUnique(pWindow)); + pWindow->addWindowDeco(makeUnique(pWindow)); + + pWindow->m_pWLSurface->assign(pWindow->m_pXDGSurface->surface.lock(), pWindow); + + return pWindow; +} + +CWindow::CWindow(SP resource) : m_pXDGSurface(resource) { + m_pWLSurface = CWLSurface::create(); + + listeners.map = m_pXDGSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); }); + listeners.ack = m_pXDGSurface->events.ack.registerListener([this](std::any d) { onAck(std::any_cast(d)); }); + listeners.unmap = m_pXDGSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); }); + listeners.destroy = m_pXDGSurface->events.destroy.registerListener([this](std::any d) { Events::listener_destroyWindow(this, nullptr); }); + listeners.commit = m_pXDGSurface->events.commit.registerListener([this](std::any d) { Events::listener_commitWindow(this, nullptr); }); + listeners.updateState = m_pXDGSurface->toplevel->events.stateChanged.registerListener([this](std::any d) { onUpdateState(); }); + listeners.updateMetadata = m_pXDGSurface->toplevel->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); }); +} + +CWindow::CWindow(SP surface) : m_pXWaylandSurface(surface) { + m_pWLSurface = CWLSurface::create(); + + listeners.map = m_pXWaylandSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); }); + listeners.unmap = m_pXWaylandSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); }); + listeners.destroy = m_pXWaylandSurface->events.destroy.registerListener([this](std::any d) { Events::listener_destroyWindow(this, nullptr); }); + listeners.commit = m_pXWaylandSurface->events.commit.registerListener([this](std::any d) { Events::listener_commitWindow(this, nullptr); }); + listeners.configureRequest = m_pXWaylandSurface->events.configureRequest.registerListener([this](std::any d) { onX11ConfigureRequest(std::any_cast(d)); }); + listeners.updateState = m_pXWaylandSurface->events.stateChanged.registerListener([this](std::any d) { onUpdateState(); }); + listeners.updateMetadata = m_pXWaylandSurface->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); }); + listeners.resourceChange = m_pXWaylandSurface->events.resourceChange.registerListener([this](std::any d) { onResourceChangeX11(); }); + listeners.activate = m_pXWaylandSurface->events.activate.registerListener([this](std::any d) { Events::listener_activateX11(this, nullptr); }); + + if (m_pXWaylandSurface->overrideRedirect) + listeners.setGeometry = m_pXWaylandSurface->events.setGeometry.registerListener([this](std::any d) { Events::listener_unmanagedSetGeometry(this, nullptr); }); +} + +CWindow::~CWindow() { + if (g_pCompositor->m_pLastWindow == m_pSelf) { + g_pCompositor->m_pLastFocus.reset(); + g_pCompositor->m_pLastWindow.reset(); + } + + events.destroy.emit(); + + if (!g_pHyprOpenGL) + return; + + g_pHyprRenderer->makeEGLCurrent(); + std::erase_if(g_pHyprOpenGL->m_mWindowFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.get() == this; }); +} + +SBoxExtents CWindow::getFullWindowExtents() { + if (m_bFadingOut) + return m_eOriginalClosedExtents; + + const int BORDERSIZE = getRealBorderSize(); + + if (m_sWindowData.dimAround.valueOrDefault()) { + if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR) + return {{m_vRealPosition->value().x - PMONITOR->vecPosition.x, m_vRealPosition->value().y - PMONITOR->vecPosition.y}, + {PMONITOR->vecSize.x - (m_vRealPosition->value().x - PMONITOR->vecPosition.x), PMONITOR->vecSize.y - (m_vRealPosition->value().y - PMONITOR->vecPosition.y)}}; + } + + SBoxExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}}; + + const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(m_pSelf.lock()); + + if (EXTENTS.topLeft.x > maxExtents.topLeft.x) + maxExtents.topLeft.x = EXTENTS.topLeft.x; + + if (EXTENTS.topLeft.y > maxExtents.topLeft.y) + maxExtents.topLeft.y = EXTENTS.topLeft.y; + + if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x) + maxExtents.bottomRight.x = EXTENTS.bottomRight.x; + + if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y) + maxExtents.bottomRight.y = EXTENTS.bottomRight.y; + + if (m_pWLSurface->exists() && !m_bIsX11 && m_pPopupHead) { + CBox surfaceExtents = {0, 0, 0, 0}; + // TODO: this could be better, perhaps make a getFullWindowRegion? + m_pPopupHead->breadthfirst( + [](WP popup, void* data) { + if (!popup->m_pWLSurface || !popup->m_pWLSurface->resource()) + return; + + CBox* pSurfaceExtents = (CBox*)data; + CBox surf = CBox{popup->coordsRelativeToParent(), popup->size()}; + if (surf.x < pSurfaceExtents->x) + pSurfaceExtents->x = surf.x; + if (surf.y < pSurfaceExtents->y) + pSurfaceExtents->y = surf.y; + if (surf.x + surf.w > pSurfaceExtents->width) + pSurfaceExtents->width = surf.x + surf.w - pSurfaceExtents->x; + if (surf.y + surf.h > pSurfaceExtents->height) + pSurfaceExtents->height = surf.y + surf.h - pSurfaceExtents->y; + }, + &surfaceExtents); + + if (-surfaceExtents.x > maxExtents.topLeft.x) + maxExtents.topLeft.x = -surfaceExtents.x; + + if (-surfaceExtents.y > maxExtents.topLeft.y) + maxExtents.topLeft.y = -surfaceExtents.y; + + if (surfaceExtents.x + surfaceExtents.width > m_pWLSurface->resource()->current.size.x + maxExtents.bottomRight.x) + maxExtents.bottomRight.x = surfaceExtents.x + surfaceExtents.width - m_pWLSurface->resource()->current.size.x; + + if (surfaceExtents.y + surfaceExtents.height > m_pWLSurface->resource()->current.size.y + maxExtents.bottomRight.y) + maxExtents.bottomRight.y = surfaceExtents.y + surfaceExtents.height - m_pWLSurface->resource()->current.size.y; + } + + return maxExtents; +} + +CBox CWindow::getFullWindowBoundingBox() { + if (m_sWindowData.dimAround.valueOrDefault()) { + if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR) + return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; + } + + auto maxExtents = getFullWindowExtents(); + + CBox finalBox = {m_vRealPosition->value().x - maxExtents.topLeft.x, m_vRealPosition->value().y - maxExtents.topLeft.y, + m_vRealSize->value().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_vRealSize->value().y + maxExtents.topLeft.y + maxExtents.bottomRight.y}; + + return finalBox; +} + +CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() { + const auto PMONITOR = m_pMonitor.lock(); + + if (!PMONITOR) + return {m_vPosition, m_vSize}; + + auto POS = m_vPosition; + auto SIZE = m_vSize; + + if (isFullscreen()) { + POS = PMONITOR->vecPosition; + SIZE = PMONITOR->vecSize; + + return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y}; + } + + if (DELTALESSTHAN(POS.y - PMONITOR->vecPosition.y, PMONITOR->vecReservedTopLeft.y, 1)) { + POS.y = PMONITOR->vecPosition.y; + SIZE.y += PMONITOR->vecReservedTopLeft.y; + } + if (DELTALESSTHAN(POS.x - PMONITOR->vecPosition.x, PMONITOR->vecReservedTopLeft.x, 1)) { + POS.x = PMONITOR->vecPosition.x; + SIZE.x += PMONITOR->vecReservedTopLeft.x; + } + if (DELTALESSTHAN(POS.x + SIZE.x - PMONITOR->vecPosition.x, PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x, 1)) { + SIZE.x += PMONITOR->vecReservedBottomRight.x; + } + if (DELTALESSTHAN(POS.y + SIZE.y - PMONITOR->vecPosition.y, PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y, 1)) { + SIZE.y += PMONITOR->vecReservedBottomRight.y; + } + + return CBox{(int)POS.x, (int)POS.y, (int)SIZE.x, (int)SIZE.y}; +} + +CBox CWindow::getWindowBoxUnified(uint64_t properties) { + if (m_sWindowData.dimAround.valueOrDefault()) { + const auto PMONITOR = m_pMonitor.lock(); + if (PMONITOR) + return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; + } + + SBoxExtents EXTENTS = {{0, 0}, {0, 0}}; + if (properties & RESERVED_EXTENTS) + EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationReserved(m_pSelf.lock())); + if (properties & INPUT_EXTENTS) + EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(m_pSelf.lock(), true)); + if (properties & FULL_EXTENTS) + EXTENTS.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(m_pSelf.lock(), false)); + + CBox box = {m_vRealPosition->value().x, m_vRealPosition->value().y, m_vRealSize->value().x, m_vRealSize->value().y}; + box.addExtents(EXTENTS); + + return box; +} + +SBoxExtents CWindow::getFullWindowReservedArea() { + return g_pDecorationPositioner->getWindowDecorationReserved(m_pSelf.lock()); +} + +void CWindow::updateWindowDecos() { + + if (!m_bIsMapped || isHidden()) + return; + + for (auto const& wd : m_vDecosToRemove) { + for (auto it = m_dWindowDecorations.begin(); it != m_dWindowDecorations.end(); it++) { + if (it->get() == wd) { + g_pDecorationPositioner->uncacheDecoration(it->get()); + it = m_dWindowDecorations.erase(it); + if (it == m_dWindowDecorations.end()) + break; + } + } + } + + g_pDecorationPositioner->onWindowUpdate(m_pSelf.lock()); + + m_vDecosToRemove.clear(); + + // make a copy because updateWindow can remove decos. + std::vector decos; + // reserve to avoid reallocations + decos.reserve(m_dWindowDecorations.size()); + + for (auto const& wd : m_dWindowDecorations) { + decos.push_back(wd.get()); + } + + for (auto const& wd : decos) { + if (std::find_if(m_dWindowDecorations.begin(), m_dWindowDecorations.end(), [wd](const auto& other) { return other.get() == wd; }) == m_dWindowDecorations.end()) + continue; + wd->updateWindow(m_pSelf.lock()); + } +} + +void CWindow::addWindowDeco(UP deco) { + m_dWindowDecorations.emplace_back(std::move(deco)); + g_pDecorationPositioner->forceRecalcFor(m_pSelf.lock()); + updateWindowDecos(); + g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_pSelf.lock()); +} + +void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) { + m_vDecosToRemove.push_back(deco); + g_pDecorationPositioner->forceRecalcFor(m_pSelf.lock()); + updateWindowDecos(); + g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_pSelf.lock()); +} + +void CWindow::uncacheWindowDecos() { + for (auto const& wd : m_dWindowDecorations) { + g_pDecorationPositioner->uncacheDecoration(wd.get()); + } +} + +bool CWindow::checkInputOnDecos(const eInputType type, const Vector2D& mouseCoords, std::any data) { + if (type != INPUT_TYPE_DRAG_END && hasPopupAt(mouseCoords)) + return false; + + for (auto const& wd : m_dWindowDecorations) { + if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT)) + continue; + + if (!g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(mouseCoords)) + continue; + + if (wd->onInputOnDeco(type, mouseCoords, data)) + return true; + } + + return false; +} + +pid_t CWindow::getPID() { + pid_t PID = -1; + if (!m_bIsX11) { + if (!m_pXDGSurface || !m_pXDGSurface->owner /* happens at unmap */) + return -1; + + wl_client_get_credentials(m_pXDGSurface->owner->client(), &PID, nullptr, nullptr); + } else { + if (!m_pXWaylandSurface) + return -1; + + PID = m_pXWaylandSurface->pid; + } + + return PID; +} + +IHyprWindowDecoration* CWindow::getDecorationByType(eDecorationType type) { + for (auto const& wd : m_dWindowDecorations) { + if (wd->getDecorationType() == type) + return wd.get(); + } + + return nullptr; +} + +void CWindow::updateToplevel() { + updateSurfaceScaleTransformDetails(); +} + +void CWindow::updateSurfaceScaleTransformDetails(bool force) { + if (!m_bIsMapped || m_bHidden || g_pCompositor->m_bUnsafeState) + return; + + const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_iLastSurfaceMonitorID); + + m_iLastSurfaceMonitorID = monitorID(); + + const auto PNEWMONITOR = m_pMonitor.lock(); + + if (!PNEWMONITOR) + return; + + if (PNEWMONITOR != PLASTMONITOR || force) { + if (PLASTMONITOR && PLASTMONITOR->m_bEnabled && PNEWMONITOR != PLASTMONITOR) + m_pWLSurface->resource()->breadthfirst([PLASTMONITOR](SP s, const Vector2D& offset, void* d) { s->leave(PLASTMONITOR->self.lock()); }, nullptr); + + m_pWLSurface->resource()->breadthfirst([PNEWMONITOR](SP s, const Vector2D& offset, void* d) { s->enter(PNEWMONITOR->self.lock()); }, nullptr); + } + + const auto PMONITOR = m_pMonitor.lock(); + + m_pWLSurface->resource()->breadthfirst( + [PMONITOR](SP s, const Vector2D& offset, void* d) { + const auto PSURFACE = CWLSurface::fromResource(s); + if (PSURFACE && PSURFACE->m_fLastScale == PMONITOR->scale) + return; + + g_pCompositor->setPreferredScaleForSurface(s, PMONITOR->scale); + g_pCompositor->setPreferredTransformForSurface(s, PMONITOR->transform); + }, + nullptr); +} + +void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) { + if (m_pWorkspace == pWorkspace) + return; + + static auto PINITIALWSTRACKING = CConfigValue("misc:initial_workspace_tracking"); + + if (!m_szInitialWorkspaceToken.empty()) { + const auto TOKEN = g_pTokenManager->getToken(m_szInitialWorkspaceToken); + if (TOKEN) { + if (*PINITIALWSTRACKING == 2) { + // persistent + SInitialWorkspaceToken token = std::any_cast(TOKEN->data); + if (token.primaryOwner == m_pSelf) { + token.workspace = pWorkspace->getConfigName(); + TOKEN->data = token; + } + } + } + } + + static auto PCLOSEONLASTSPECIAL = CConfigValue("misc:close_special_on_empty"); + + const auto OLDWORKSPACE = m_pWorkspace; + + if (OLDWORKSPACE->isVisible()) { + m_fMovingToWorkspaceAlpha->setValueAndWarp(1.F); + *m_fMovingToWorkspaceAlpha = 0.F; + m_fMovingToWorkspaceAlpha->setCallbackOnEnd([this](auto) { m_iMonitorMovedFrom = -1; }); + m_iMonitorMovedFrom = OLDWORKSPACE ? OLDWORKSPACE->monitorID() : -1; + } + + m_pWorkspace = pWorkspace; + + setAnimationsToMove(); + + OLDWORKSPACE->updateWindows(); + OLDWORKSPACE->updateWindowData(); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(OLDWORKSPACE->monitorID()); + + pWorkspace->updateWindows(); + pWorkspace->updateWindowData(); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID()); + + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + if (valid(pWorkspace)) { + g_pEventManager->postEvent(SHyprIPCEvent{"movewindow", std::format("{:x},{}", (uintptr_t)this, pWorkspace->m_szName)}); + g_pEventManager->postEvent(SHyprIPCEvent{"movewindowv2", std::format("{:x},{},{}", (uintptr_t)this, pWorkspace->m_iID, pWorkspace->m_szName)}); + EMIT_HOOK_EVENT("moveWindow", (std::vector{m_pSelf.lock(), pWorkspace})); + } + + if (const auto SWALLOWED = m_pSwallowed.lock()) { + if (SWALLOWED->m_bCurrentlySwallowed) { + SWALLOWED->moveToWorkspace(pWorkspace); + SWALLOWED->m_pMonitor = m_pMonitor; + } + } + + if (OLDWORKSPACE && g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE->m_iID) && OLDWORKSPACE->getWindows() == 0 && *PCLOSEONLASTSPECIAL) { + if (const auto PMONITOR = OLDWORKSPACE->m_pMonitor.lock(); PMONITOR) + PMONITOR->setSpecialWorkspace(nullptr); + } +} + +PHLWINDOW CWindow::x11TransientFor() { + if (!m_pXWaylandSurface || !m_pXWaylandSurface->parent) + return nullptr; + + auto s = m_pXWaylandSurface->parent; + std::vector> visited; + while (s) { + // break loops. Some X apps make them, and it seems like it's valid behavior?!?!?! + // TODO: we should reject loops being created in the first place. + if (std::find(visited.begin(), visited.end(), s) != visited.end()) + break; + + visited.emplace_back(s.lock()); + s = s->parent; + } + + if (s == m_pXWaylandSurface) + return nullptr; // dead-ass circle + + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pXWaylandSurface != s) + continue; + return w; + } + + return nullptr; +} + +void CWindow::onUnmap() { + static auto PCLOSEONLASTSPECIAL = CConfigValue("misc:close_special_on_empty"); + static auto PINITIALWSTRACKING = CConfigValue("misc:initial_workspace_tracking"); + + if (!m_szInitialWorkspaceToken.empty()) { + const auto TOKEN = g_pTokenManager->getToken(m_szInitialWorkspaceToken); + if (TOKEN) { + if (*PINITIALWSTRACKING == 2) { + // persistent token, but the first window got removed so the token is gone + SInitialWorkspaceToken token = std::any_cast(TOKEN->data); + if (token.primaryOwner == m_pSelf) + g_pTokenManager->removeToken(TOKEN); + } + } + } + + m_iLastWorkspace = m_pWorkspace->m_iID; + + std::erase_if(g_pCompositor->m_vWindowFocusHistory, [this](const auto& other) { return other.expired() || other == m_pSelf; }); + + if (*PCLOSEONLASTSPECIAL && m_pWorkspace && m_pWorkspace->getWindows() == 0 && onSpecialWorkspace()) { + const auto PMONITOR = m_pMonitor.lock(); + if (PMONITOR && PMONITOR->activeSpecialWorkspace && PMONITOR->activeSpecialWorkspace == m_pWorkspace) + PMONITOR->setSpecialWorkspace(nullptr); + } + + const auto PMONITOR = m_pMonitor.lock(); + + if (PMONITOR && PMONITOR->solitaryClient == m_pSelf) + PMONITOR->solitaryClient.reset(); + + if (m_pWorkspace) { + m_pWorkspace->updateWindows(); + m_pWorkspace->updateWindowData(); + } + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID()); + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + m_pWorkspace.reset(); + + if (m_bIsX11) + return; + + m_pSubsurfaceHead.reset(); + m_pPopupHead.reset(); +} + +void CWindow::onMap() { + // JIC, reset the callbacks. If any are set, we'll make sure they are cleared so we don't accidentally unset them. (In case a window got remapped) + m_vRealPosition->resetAllCallbacks(); + m_vRealSize->resetAllCallbacks(); + m_fBorderFadeAnimationProgress->resetAllCallbacks(); + m_fBorderAngleAnimationProgress->resetAllCallbacks(); + m_fActiveInactiveAlpha->resetAllCallbacks(); + m_fAlpha->resetAllCallbacks(); + m_cRealShadowColor->resetAllCallbacks(); + m_fDimPercent->resetAllCallbacks(); + m_fMovingToWorkspaceAlpha->resetAllCallbacks(); + m_fMovingFromWorkspaceAlpha->resetAllCallbacks(); + + m_fMovingFromWorkspaceAlpha->setValueAndWarp(1.F); + + if (m_fBorderAngleAnimationProgress->enabled()) { + m_fBorderAngleAnimationProgress->setValueAndWarp(0.f); + m_fBorderAngleAnimationProgress->setCallbackOnEnd([&](WP p) { onBorderAngleAnimEnd(p); }, false); + *m_fBorderAngleAnimationProgress = 1.f; + } + + m_vRealSize->setCallbackOnBegin( + [this](auto) { + if (!m_bIsMapped || isX11OverrideRedirect()) + return; + + sendWindowSize(); + }, + false); + + m_fMovingFromWorkspaceAlpha->setValueAndWarp(1.F); + + g_pCompositor->m_vWindowFocusHistory.push_back(m_pSelf); + + m_vReportedSize = m_vPendingReportedSize; + m_bAnimatingIn = true; + + updateSurfaceScaleTransformDetails(true); + + if (m_bIsX11) + return; + + m_pSubsurfaceHead = CSubsurface::create(m_pSelf.lock()); + m_pPopupHead = CPopup::create(m_pSelf.lock()); +} + +void CWindow::onBorderAngleAnimEnd(WP pav) { + const auto PAV = pav.lock(); + if (!PAV) + return; + + if (PAV->getStyle() != "loop" || !PAV->enabled()) + return; + + const auto PANIMVAR = dynamic_cast*>(PAV.get()); + + PANIMVAR->setCallbackOnEnd(nullptr); // we remove the callback here because otherwise setvalueandwarp will recurse this + + PANIMVAR->setValueAndWarp(0); + *PANIMVAR = 1.f; + + PANIMVAR->setCallbackOnEnd([&](WP pav) { onBorderAngleAnimEnd(pav); }, false); +} + +void CWindow::setHidden(bool hidden) { + m_bHidden = hidden; + + if (hidden && g_pCompositor->m_pLastWindow == m_pSelf) + g_pCompositor->m_pLastWindow.reset(); + + setSuspended(hidden); +} + +bool CWindow::isHidden() { + return m_bHidden; +} + +void CWindow::applyDynamicRule(const SP& r) { + const eOverridePriority priority = r->execRule ? PRIORITY_SET_PROP : PRIORITY_WINDOW_RULE; + + switch (r->ruleType) { + case CWindowRule::RULE_TAG: { + CVarList vars{r->szRule, 0, 's', true}; + + if (vars.size() == 2 && vars[0] == "tag") + m_tags.applyTag(vars[1], true); + else + Debug::log(ERR, "Tag rule invalid: {}", r->szRule); + break; + } + case CWindowRule::RULE_OPACITY: { + try { + CVarList vars(r->szRule, 0, ' '); + + int opacityIDX = 0; + + for (auto const& r : vars) { + if (r == "opacity") + continue; + + if (r == "override") { + if (opacityIDX == 1) + m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{m_sWindowData.alpha.value().m_fAlpha, true}, priority); + else if (opacityIDX == 2) + m_sWindowData.alphaInactive = CWindowOverridableVar(SAlphaValue{m_sWindowData.alphaInactive.value().m_fAlpha, true}, priority); + else if (opacityIDX == 3) + m_sWindowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{m_sWindowData.alphaFullscreen.value().m_fAlpha, true}, priority); + } else { + if (opacityIDX == 0) { + m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority); + } else if (opacityIDX == 1) { + m_sWindowData.alphaInactive = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority); + } else if (opacityIDX == 2) { + m_sWindowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{std::stof(r), false}, priority); + } else { + throw std::runtime_error("more than 3 alpha values"); + } + + opacityIDX++; + } + } + + if (opacityIDX == 1) { + m_sWindowData.alphaInactive = m_sWindowData.alpha; + m_sWindowData.alphaFullscreen = m_sWindowData.alpha; + } + } catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r->szRule, e.what()); } + break; + } + case CWindowRule::RULE_ANIMATION: { + auto STYLE = r->szRule.substr(r->szRule.find_first_of(' ') + 1); + m_sWindowData.animationStyle = CWindowOverridableVar(STYLE, priority); + break; + } + case CWindowRule::RULE_BORDERCOLOR: { + try { + // Each vector will only get used if it has at least one color + CGradientValueData activeBorderGradient = {}; + CGradientValueData inactiveBorderGradient = {}; + bool active = true; + CVarList colorsAndAngles = CVarList(trim(r->szRule.substr(r->szRule.find_first_of(' ') + 1)), 0, 's', true); + + // Basic form has only two colors, everything else can be parsed as a gradient + if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) { + m_sWindowData.activeBorderColor = CWindowOverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[0]).value_or(0))), priority); + m_sWindowData.inactiveBorderColor = CWindowOverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[1]).value_or(0))), priority); + return; + } + + for (auto const& token : colorsAndAngles) { + // The first angle, or an explicit "0deg", splits the two gradients + if (active && token.contains("deg")) { + activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); + active = false; + } else if (token.contains("deg")) + inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); + else if (active) + activeBorderGradient.m_vColors.push_back(configStringToInt(token).value_or(0)); + else + inactiveBorderGradient.m_vColors.push_back(configStringToInt(token).value_or(0)); + } + + activeBorderGradient.updateColorsOk(); + + // Includes sanity checks for the number of colors in each gradient + if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10) + Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r->szRule); + else if (activeBorderGradient.m_vColors.empty()) + Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r->szRule); + else if (inactiveBorderGradient.m_vColors.empty()) + m_sWindowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority); + else { + m_sWindowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority); + m_sWindowData.inactiveBorderColor = CWindowOverridableVar(inactiveBorderGradient, priority); + } + } catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r->szRule, e.what()); } + break; + } + case CWindowRule::RULE_IDLEINHIBIT: { + auto IDLERULE = r->szRule.substr(r->szRule.find_first_of(' ') + 1); + + if (IDLERULE == "none") + m_eIdleInhibitMode = IDLEINHIBIT_NONE; + else if (IDLERULE == "always") + m_eIdleInhibitMode = IDLEINHIBIT_ALWAYS; + else if (IDLERULE == "focus") + m_eIdleInhibitMode = IDLEINHIBIT_FOCUS; + else if (IDLERULE == "fullscreen") + m_eIdleInhibitMode = IDLEINHIBIT_FULLSCREEN; + else + Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE); + break; + } + case CWindowRule::RULE_MAXSIZE: { + try { + if (!m_bIsFloating) + return; + const auto VEC = configStringToVector2D(r->szRule.substr(8)); + if (VEC.x < 1 || VEC.y < 1) { + Debug::log(ERR, "Invalid size for maxsize"); + return; + } + + m_sWindowData.maxSize = CWindowOverridableVar(VEC, priority); + clampWindowSize(std::nullopt, m_sWindowData.maxSize.value()); + + } catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", r->szRule, e.what()); } + break; + } + case CWindowRule::RULE_MINSIZE: { + try { + if (!m_bIsFloating) + return; + const auto VEC = configStringToVector2D(r->szRule.substr(8)); + if (VEC.x < 1 || VEC.y < 1) { + Debug::log(ERR, "Invalid size for minsize"); + return; + } + + m_sWindowData.minSize = CWindowOverridableVar(VEC, priority); + clampWindowSize(m_sWindowData.minSize.value(), std::nullopt); + + if (m_sGroupData.pNextWindow.expired()) + setHidden(false); + } catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", r->szRule, e.what()); } + break; + } + case CWindowRule::RULE_RENDERUNFOCUSED: { + m_sWindowData.renderUnfocused = CWindowOverridableVar(true, priority); + g_pHyprRenderer->addWindowToRenderUnfocused(m_pSelf.lock()); + break; + } + case CWindowRule::RULE_PROP: { + const CVarList VARS(r->szRule, 0, ' '); + if (auto search = NWindowProperties::intWindowProperties.find(VARS[1]); search != NWindowProperties::intWindowProperties.end()) { + try { + *(search->second(m_pSelf.lock())) = CWindowOverridableVar(std::stoi(VARS[2]), priority); + } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); } + } else if (auto search = NWindowProperties::floatWindowProperties.find(VARS[1]); search != NWindowProperties::floatWindowProperties.end()) { + try { + *(search->second(m_pSelf.lock())) = CWindowOverridableVar(std::stof(VARS[2]), priority); + } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); } + } else if (auto search = NWindowProperties::boolWindowProperties.find(VARS[1]); search != NWindowProperties::boolWindowProperties.end()) { + try { + *(search->second(m_pSelf.lock())) = CWindowOverridableVar(VARS[2].empty() ? true : (bool)std::stoi(VARS[2]), priority); + } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); } + } + break; + } + case CWindowRule::RULE_PERSISTENTSIZE: { + m_sWindowData.persistentSize = CWindowOverridableVar(true, PRIORITY_WINDOW_RULE); + break; + } + default: break; + } +} + +void CWindow::updateDynamicRules() { + m_sWindowData.alpha.unset(PRIORITY_WINDOW_RULE); + m_sWindowData.alphaInactive.unset(PRIORITY_WINDOW_RULE); + m_sWindowData.alphaFullscreen.unset(PRIORITY_WINDOW_RULE); + + unsetWindowData(PRIORITY_WINDOW_RULE); + + m_sWindowData.animationStyle.unset(PRIORITY_WINDOW_RULE); + m_sWindowData.maxSize.unset(PRIORITY_WINDOW_RULE); + m_sWindowData.minSize.unset(PRIORITY_WINDOW_RULE); + + m_sWindowData.activeBorderColor.unset(PRIORITY_WINDOW_RULE); + m_sWindowData.inactiveBorderColor.unset(PRIORITY_WINDOW_RULE); + + m_sWindowData.renderUnfocused.unset(PRIORITY_WINDOW_RULE); + + m_eIdleInhibitMode = IDLEINHIBIT_NONE; + + m_tags.removeDynamicTags(); + + m_vMatchedRules = g_pConfigManager->getMatchingRules(m_pSelf.lock()); + for (const auto& r : m_vMatchedRules) { + applyDynamicRule(r); + } + + EMIT_HOOK_EVENT("windowUpdateRules", m_pSelf.lock()); + + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID()); +} + +// check if the point is "hidden" under a rounded corner of the window +// it is assumed that the point is within the real window box (m_vRealPosition, m_vRealSize) +// otherwise behaviour is undefined +bool CWindow::isInCurvedCorner(double x, double y) { + const int ROUNDING = rounding(); + const int ROUNDINGPOWER = roundingPower(); + if (getRealBorderSize() >= ROUNDING) + return false; + + // (x0, y0), (x0, y1), ... are the center point of rounding at each corner + double x0 = m_vRealPosition->value().x + ROUNDING; + double y0 = m_vRealPosition->value().y + ROUNDING; + double x1 = m_vRealPosition->value().x + m_vRealSize->value().x - ROUNDING; + double y1 = m_vRealPosition->value().y + m_vRealSize->value().y - ROUNDING; + + if (x < x0 && y < y0) { + return std::pow(x0 - x, ROUNDINGPOWER) + std::pow(y0 - y, ROUNDINGPOWER) > std::pow((double)ROUNDING, ROUNDINGPOWER); + } + if (x > x1 && y < y0) { + return std::pow(x - x1, ROUNDINGPOWER) + std::pow(y0 - y, ROUNDINGPOWER) > std::pow((double)ROUNDING, ROUNDINGPOWER); + } + if (x < x0 && y > y1) { + return std::pow(x0 - x, ROUNDINGPOWER) + std::pow(y - y1, ROUNDINGPOWER) > std::pow((double)ROUNDING, ROUNDINGPOWER); + } + if (x > x1 && y > y1) { + return std::pow(x - x1, ROUNDINGPOWER) + std::pow(y - y1, ROUNDINGPOWER) > std::pow((double)ROUNDING, ROUNDINGPOWER); + } + + return false; +} + +// checks if the wayland window has a popup at pos +bool CWindow::hasPopupAt(const Vector2D& pos) { + if (m_bIsX11) + return false; + + auto popup = m_pPopupHead->at(pos); + + return popup && popup->m_pWLSurface->resource(); +} + +void CWindow::applyGroupRules() { + if ((m_eGroupRules & GROUP_SET && m_bFirstMap) || m_eGroupRules & GROUP_SET_ALWAYS) + createGroup(); + + if (m_sGroupData.pNextWindow.lock() && ((m_eGroupRules & GROUP_LOCK && m_bFirstMap) || m_eGroupRules & GROUP_LOCK_ALWAYS)) + getGroupHead()->m_sGroupData.locked = true; +} + +void CWindow::createGroup() { + if (m_sGroupData.deny) { + Debug::log(LOG, "createGroup: window:{:x},title:{} is denied as a group, ignored", (uintptr_t)this, this->m_szTitle); + return; + } + + if (m_sGroupData.pNextWindow.expired()) { + m_sGroupData.pNextWindow = m_pSelf; + m_sGroupData.head = true; + m_sGroupData.locked = false; + m_sGroupData.deny = false; + + addWindowDeco(makeUnique(m_pSelf.lock())); + + if (m_pWorkspace) { + m_pWorkspace->updateWindows(); + m_pWorkspace->updateWindowData(); + } + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID()); + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + g_pEventManager->postEvent(SHyprIPCEvent{"togglegroup", std::format("1,{:x}", (uintptr_t)this)}); + } +} + +void CWindow::destroyGroup() { + if (m_sGroupData.pNextWindow == m_pSelf) { + if (m_eGroupRules & GROUP_SET_ALWAYS) { + Debug::log(LOG, "destoryGroup: window:{:x},title:{} has rule [group set always], ignored", (uintptr_t)this, this->m_szTitle); + return; + } + m_sGroupData.pNextWindow.reset(); + m_sGroupData.head = false; + updateWindowDecos(); + if (m_pWorkspace) { + m_pWorkspace->updateWindows(); + m_pWorkspace->updateWindowData(); + } + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID()); + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + g_pEventManager->postEvent(SHyprIPCEvent{"togglegroup", std::format("0,{:x}", (uintptr_t)this)}); + return; + } + + std::string addresses; + PHLWINDOW curr = m_pSelf.lock(); + std::vector members; + do { + const auto PLASTWIN = curr; + curr = curr->m_sGroupData.pNextWindow.lock(); + PLASTWIN->m_sGroupData.pNextWindow.reset(); + curr->setHidden(false); + members.push_back(curr); + + addresses += std::format("{:x},", (uintptr_t)curr.get()); + } while (curr.get() != this); + + for (auto const& w : members) { + if (w->m_sGroupData.head) + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(curr); + w->m_sGroupData.head = false; + } + + const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_bGroupsLocked; + g_pKeybindManager->m_bGroupsLocked = true; + for (auto const& w : members) { + g_pLayoutManager->getCurrentLayout()->onWindowCreated(w); + w->updateWindowDecos(); + } + g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV; + + if (m_pWorkspace) { + m_pWorkspace->updateWindows(); + m_pWorkspace->updateWindowData(); + } + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID()); + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + if (!addresses.empty()) + addresses.pop_back(); + g_pEventManager->postEvent(SHyprIPCEvent{"togglegroup", std::format("0,{}", addresses)}); +} + +PHLWINDOW CWindow::getGroupHead() { + PHLWINDOW curr = m_pSelf.lock(); + while (!curr->m_sGroupData.head) + curr = curr->m_sGroupData.pNextWindow.lock(); + return curr; +} + +PHLWINDOW CWindow::getGroupTail() { + PHLWINDOW curr = m_pSelf.lock(); + while (!curr->m_sGroupData.pNextWindow->m_sGroupData.head) + curr = curr->m_sGroupData.pNextWindow.lock(); + return curr; +} + +PHLWINDOW CWindow::getGroupCurrent() { + PHLWINDOW curr = m_pSelf.lock(); + while (curr->isHidden()) + curr = curr->m_sGroupData.pNextWindow.lock(); + return curr; +} + +int CWindow::getGroupSize() { + int size = 1; + PHLWINDOW curr = m_pSelf.lock(); + while (curr->m_sGroupData.pNextWindow != m_pSelf) { + curr = curr->m_sGroupData.pNextWindow.lock(); + size++; + } + return size; +} + +bool CWindow::canBeGroupedInto(PHLWINDOW pWindow) { + static auto ALLOWGROUPMERGE = CConfigValue("group:merge_groups_on_drag"); + bool isGroup = m_sGroupData.pNextWindow; + bool disallowDragIntoGroup = g_pInputManager->m_bWasDraggingWindow && isGroup && !bool(*ALLOWGROUPMERGE); + return !g_pKeybindManager->m_bGroupsLocked // global group lock disengaged + && ((m_eGroupRules & GROUP_INVADE && m_bFirstMap) // window ignore local group locks, or + || (!pWindow->getGroupHead()->m_sGroupData.locked // target unlocked + && !(m_sGroupData.pNextWindow.lock() && getGroupHead()->m_sGroupData.locked))) // source unlocked or isn't group + && !m_sGroupData.deny // source is not denied entry + && !(m_eGroupRules & GROUP_BARRED && m_bFirstMap) // group rule doesn't prevent adding window + && !disallowDragIntoGroup; // config allows groups to be merged +} + +PHLWINDOW CWindow::getGroupWindowByIndex(int index) { + const int SIZE = getGroupSize(); + index = ((index % SIZE) + SIZE) % SIZE; + PHLWINDOW curr = getGroupHead(); + while (index > 0) { + curr = curr->m_sGroupData.pNextWindow.lock(); + index--; + } + return curr; +} + +void CWindow::setGroupCurrent(PHLWINDOW pWindow) { + PHLWINDOW curr = m_sGroupData.pNextWindow.lock(); + bool isMember = false; + while (curr.get() != this) { + if (curr == pWindow) { + isMember = true; + break; + } + curr = curr->m_sGroupData.pNextWindow.lock(); + } + + if (!isMember && pWindow.get() != this) + return; + + const auto PCURRENT = getGroupCurrent(); + const bool FULLSCREEN = PCURRENT->isFullscreen(); + const auto WORKSPACE = PCURRENT->m_pWorkspace; + const auto MODE = PCURRENT->m_sFullscreenState.internal; + + const auto CURRENTISFOCUS = PCURRENT == g_pCompositor->m_pLastWindow.lock(); + + if (FULLSCREEN) + g_pCompositor->setWindowFullscreenInternal(PCURRENT, FSMODE_NONE); + + const auto PWINDOWSIZE = PCURRENT->m_vRealSize->goal(); + const auto PWINDOWPOS = PCURRENT->m_vRealPosition->goal(); + + PCURRENT->setHidden(true); + pWindow->setHidden(false); // can remove m_pLastWindow + + g_pLayoutManager->getCurrentLayout()->replaceWindowDataWith(PCURRENT, pWindow); + + if (PCURRENT->m_bIsFloating) { + pWindow->m_vRealPosition->setValueAndWarp(PWINDOWPOS); + pWindow->m_vRealSize->setValueAndWarp(PWINDOWSIZE); + } + + g_pCompositor->updateAllWindowsAnimatedDecorationValues(); + + if (CURRENTISFOCUS) + g_pCompositor->focusWindow(pWindow); + + if (FULLSCREEN) + g_pCompositor->setWindowFullscreenInternal(pWindow, MODE); + + g_pHyprRenderer->damageWindow(pWindow); + + pWindow->updateWindowDecos(); +} + +void CWindow::insertWindowToGroup(PHLWINDOW pWindow) { + const auto BEGINAT = m_pSelf.lock(); + const auto ENDAT = m_sGroupData.pNextWindow.lock(); + + if (!pWindow->getDecorationByType(DECORATION_GROUPBAR)) + pWindow->addWindowDeco(makeUnique(pWindow)); + + if (!pWindow->m_sGroupData.pNextWindow.lock()) { + BEGINAT->m_sGroupData.pNextWindow = pWindow; + pWindow->m_sGroupData.pNextWindow = ENDAT; + pWindow->m_sGroupData.head = false; + return; + } + + const auto SHEAD = pWindow->getGroupHead(); + const auto STAIL = pWindow->getGroupTail(); + + SHEAD->m_sGroupData.head = false; + BEGINAT->m_sGroupData.pNextWindow = SHEAD; + STAIL->m_sGroupData.pNextWindow = ENDAT; +} + +PHLWINDOW CWindow::getGroupPrevious() { + PHLWINDOW curr = m_sGroupData.pNextWindow.lock(); + + while (curr != m_pSelf && curr->m_sGroupData.pNextWindow != m_pSelf) + curr = curr->m_sGroupData.pNextWindow.lock(); + + return curr; +} + +void CWindow::switchWithWindowInGroup(PHLWINDOW pWindow) { + if (!m_sGroupData.pNextWindow.lock() || !pWindow->m_sGroupData.pNextWindow.lock()) + return; + + if (m_sGroupData.pNextWindow.lock() == pWindow) { // A -> this -> pWindow -> B >> A -> pWindow -> this -> B + getGroupPrevious()->m_sGroupData.pNextWindow = pWindow; + m_sGroupData.pNextWindow = pWindow->m_sGroupData.pNextWindow; + pWindow->m_sGroupData.pNextWindow = m_pSelf; + + } else if (pWindow->m_sGroupData.pNextWindow == m_pSelf) { // A -> pWindow -> this -> B >> A -> this -> pWindow -> B + pWindow->getGroupPrevious()->m_sGroupData.pNextWindow = m_pSelf; + pWindow->m_sGroupData.pNextWindow = m_sGroupData.pNextWindow; + m_sGroupData.pNextWindow = pWindow; + + } else { // A -> this -> B | C -> pWindow -> D >> A -> pWindow -> B | C -> this -> D + std::swap(m_sGroupData.pNextWindow, pWindow->m_sGroupData.pNextWindow); + std::swap(getGroupPrevious()->m_sGroupData.pNextWindow, pWindow->getGroupPrevious()->m_sGroupData.pNextWindow); + } + + std::swap(m_sGroupData.head, pWindow->m_sGroupData.head); + std::swap(m_sGroupData.locked, pWindow->m_sGroupData.locked); +} + +void CWindow::updateGroupOutputs() { + if (m_sGroupData.pNextWindow.expired()) + return; + + PHLWINDOW curr = m_sGroupData.pNextWindow.lock(); + + const auto WS = m_pWorkspace; + + while (curr.get() != this) { + curr->m_pMonitor = m_pMonitor; + curr->moveToWorkspace(WS); + + *curr->m_vRealPosition = m_vRealPosition->goal(); + *curr->m_vRealSize = m_vRealSize->goal(); + + curr = curr->m_sGroupData.pNextWindow.lock(); + } +} + +Vector2D CWindow::middle() { + return m_vRealPosition->goal() + m_vRealSize->goal() / 2.f; +} + +bool CWindow::opaque() { + if (m_fAlpha->value() != 1.f || m_fActiveInactiveAlpha->value() != 1.f) + return false; + + if (m_vRealSize->goal().floor() != m_vReportedSize) + return false; + + const auto PWORKSPACE = m_pWorkspace; + + if (m_pWLSurface->small() && !m_pWLSurface->m_bFillIgnoreSmall) + return false; + + if (PWORKSPACE->m_fAlpha->value() != 1.f) + return false; + + if (m_bIsX11 && m_pXWaylandSurface && m_pXWaylandSurface->surface && m_pXWaylandSurface->surface->current.texture) + return m_pXWaylandSurface->surface->current.texture->m_bOpaque; + + if (!m_pWLSurface->resource() || !m_pWLSurface->resource()->current.texture) + return false; + + // TODO: this is wrong + const auto EXTENTS = m_pXDGSurface->surface->current.opaque.getExtents(); + if (EXTENTS.w >= m_pXDGSurface->surface->current.bufferSize.x && EXTENTS.h >= m_pXDGSurface->surface->current.bufferSize.y) + return true; + + return m_pWLSurface->resource()->current.texture->m_bOpaque; +} + +float CWindow::rounding() { + static auto PROUNDING = CConfigValue("decoration:rounding"); + static auto PROUNDINGPOWER = CConfigValue("decoration:rounding_power"); + + float roundingPower = m_sWindowData.roundingPower.valueOr(*PROUNDINGPOWER); + float rounding = m_sWindowData.rounding.valueOr(*PROUNDING) * (roundingPower / 2.0); /* Make perceived roundness consistent. */ + + return m_sWindowData.noRounding.valueOrDefault() ? 0 : rounding; +} + +float CWindow::roundingPower() { + static auto PROUNDINGPOWER = CConfigValue("decoration:rounding_power"); + + return m_sWindowData.roundingPower.valueOr(*PROUNDINGPOWER); +} + +void CWindow::updateWindowData() { + const auto PWORKSPACE = m_pWorkspace; + const auto WORKSPACERULE = PWORKSPACE ? g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE) : SWorkspaceRule{}; + updateWindowData(WORKSPACERULE); +} + +void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) { + static auto PNOBORDERONFLOATING = CConfigValue("general:no_border_on_floating"); + + if (*PNOBORDERONFLOATING) + m_sWindowData.noBorder = CWindowOverridableVar(m_bIsFloating, PRIORITY_LAYOUT); + else + m_sWindowData.noBorder.unset(PRIORITY_LAYOUT); + + m_sWindowData.borderSize.matchOptional(workspaceRule.borderSize, PRIORITY_WORKSPACE_RULE); + m_sWindowData.decorate.matchOptional(workspaceRule.decorate, PRIORITY_WORKSPACE_RULE); + m_sWindowData.noBorder.matchOptional(workspaceRule.noBorder, PRIORITY_WORKSPACE_RULE); + m_sWindowData.noRounding.matchOptional(workspaceRule.noRounding, PRIORITY_WORKSPACE_RULE); + m_sWindowData.noShadow.matchOptional(workspaceRule.noShadow, PRIORITY_WORKSPACE_RULE); +} + +int CWindow::getRealBorderSize() { + if (m_sWindowData.noBorder.valueOrDefault() || (m_pWorkspace && isEffectiveInternalFSMode(FSMODE_FULLSCREEN))) + return 0; + + static auto PBORDERSIZE = CConfigValue("general:border_size"); + + return m_sWindowData.borderSize.valueOr(*PBORDERSIZE); +} + +float CWindow::getScrollMouse() { + static auto PINPUTSCROLLFACTOR = CConfigValue("input:scroll_factor"); + return m_sWindowData.scrollMouse.valueOr(*PINPUTSCROLLFACTOR); +} + +float CWindow::getScrollTouchpad() { + static auto PTOUCHPADSCROLLFACTOR = CConfigValue("input:touchpad:scroll_factor"); + return m_sWindowData.scrollTouchpad.valueOr(*PTOUCHPADSCROLLFACTOR); +} + +bool CWindow::canBeTorn() { + static auto PTEARING = CConfigValue("general:allow_tearing"); + return m_sWindowData.tearing.valueOr(m_bTearingHint) && *PTEARING; +} + +void CWindow::setSuspended(bool suspend) { + if (suspend == m_bSuspended) + return; + + if (m_bIsX11 || !m_pXDGSurface->toplevel) + return; + + m_pXDGSurface->toplevel->setSuspeneded(suspend); + m_bSuspended = suspend; +} + +bool CWindow::visibleOnMonitor(PHLMONITOR pMonitor) { + CBox wbox = {m_vRealPosition->value(), m_vRealSize->value()}; + + return !wbox.intersection({pMonitor->vecPosition, pMonitor->vecSize}).empty(); +} + +void CWindow::setAnimationsToMove() { + m_vRealPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsMove")); + m_bAnimatingIn = false; +} + +void CWindow::onWorkspaceAnimUpdate() { + // clip box for animated offsets + if (!m_bIsFloating || m_bPinned || isFullscreen() || m_bDraggingTiled) { + m_vFloatingOffset = Vector2D(0, 0); + return; + } + + Vector2D offset; + const auto PWORKSPACE = m_pWorkspace; + if (!PWORKSPACE) + return; + + const auto PWSMON = m_pMonitor.lock(); + if (!PWSMON) + return; + + const auto WINBB = getFullWindowBoundingBox(); + if (PWORKSPACE->m_vRenderOffset->value().x != 0) { + const auto PROGRESS = PWORKSPACE->m_vRenderOffset->value().x / PWSMON->vecSize.x; + + if (WINBB.x < PWSMON->vecPosition.x) + offset.x += (PWSMON->vecPosition.x - WINBB.x) * PROGRESS; + + if (WINBB.x + WINBB.width > PWSMON->vecPosition.x + PWSMON->vecSize.x) + offset.x += (WINBB.x + WINBB.width - PWSMON->vecPosition.x - PWSMON->vecSize.x) * PROGRESS; + } else if (PWORKSPACE->m_vRenderOffset->value().y != 0) { + const auto PROGRESS = PWORKSPACE->m_vRenderOffset->value().y / PWSMON->vecSize.y; + + if (WINBB.y < PWSMON->vecPosition.y) + offset.y += (PWSMON->vecPosition.y - WINBB.y) * PROGRESS; + + if (WINBB.y + WINBB.height > PWSMON->vecPosition.y + PWSMON->vecSize.y) + offset.y += (WINBB.y + WINBB.height - PWSMON->vecPosition.y - PWSMON->vecSize.y) * PROGRESS; + } + + m_vFloatingOffset = offset; +} + +void CWindow::onFocusAnimUpdate() { + // borderangle once + if (m_fBorderAngleAnimationProgress->enabled() && !m_fBorderAngleAnimationProgress->isBeingAnimated()) { + m_fBorderAngleAnimationProgress->setValueAndWarp(0.f); + *m_fBorderAngleAnimationProgress = 1.f; + } +} + +int CWindow::popupsCount() { + if (m_bIsX11) + return 0; + + int no = -1; + m_pPopupHead->breadthfirst([](WP p, void* d) { *((int*)d) += 1; }, &no); + return no; +} + +int CWindow::surfacesCount() { + if (m_bIsX11) + return 1; + + int no = 0; + m_pWLSurface->resource()->breadthfirst([](SP r, const Vector2D& offset, void* d) { *((int*)d) += 1; }, &no); + return no; +} + +void CWindow::clampWindowSize(const std::optional minSize, const std::optional maxSize) { + const Vector2D REALSIZE = m_vRealSize->goal(); + const Vector2D NEWSIZE = REALSIZE.clamp(minSize.value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), maxSize.value_or(Vector2D{INFINITY, INFINITY})); + const Vector2D DELTA = REALSIZE - NEWSIZE; + + *m_vRealPosition = m_vRealPosition->goal() + DELTA / 2.0; + *m_vRealSize = NEWSIZE; +} + +bool CWindow::isFullscreen() { + return m_sFullscreenState.internal != FSMODE_NONE; +} + +bool CWindow::isEffectiveInternalFSMode(const eFullscreenMode MODE) { + return (eFullscreenMode)std::bit_floor((uint8_t)m_sFullscreenState.internal) == MODE; +} + +WORKSPACEID CWindow::workspaceID() { + return m_pWorkspace ? m_pWorkspace->m_iID : m_iLastWorkspace; +} + +MONITORID CWindow::monitorID() { + return m_pMonitor ? m_pMonitor->ID : MONITOR_INVALID; +} + +bool CWindow::onSpecialWorkspace() { + return m_pWorkspace ? m_pWorkspace->m_bIsSpecialWorkspace : g_pCompositor->isWorkspaceSpecial(m_iLastWorkspace); +} + +std::unordered_map CWindow::getEnv() { + + const auto PID = getPID(); + + if (PID <= 1) + return {}; + + std::unordered_map results; + + // + std::string environFile = "/proc/" + std::to_string(PID) + "/environ"; + std::ifstream ifs(environFile, std::ios::binary); + + if (!ifs.good()) + return {}; + + std::vector buffer; + size_t needle = 0; + buffer.resize(512, '\0'); + while (ifs.read(buffer.data() + needle, 512)) { + buffer.resize(buffer.size() + 512, '\0'); + needle += 512; + } + + if (needle <= 1) + return {}; + + std::replace(buffer.begin(), buffer.end() - 1, '\0', '\n'); + + CVarList envs(std::string{buffer.data(), buffer.size() - 1}, 0, '\n', true); + + for (auto const& e : envs) { + if (!e.contains('=')) + continue; + + const auto EQ = e.find_first_of('='); + results[e.substr(0, EQ)] = e.substr(EQ + 1); + } + + return results; +} + +void CWindow::activate(bool force) { + if (g_pCompositor->m_pLastWindow == m_pSelf) + return; + + static auto PFOCUSONACTIVATE = CConfigValue("misc:focus_on_activate"); + + g_pEventManager->postEvent(SHyprIPCEvent{"urgent", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("urgent", m_pSelf.lock()); + + m_bIsUrgent = true; + + if (!force && (!m_sWindowData.focusOnActivate.valueOr(*PFOCUSONACTIVATE) || (m_eSuppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_eSuppressedEvents & SUPPRESS_ACTIVATE))) + return; + + if (!m_bIsMapped) { + Debug::log(LOG, "Ignoring CWindow::activate focus/warp, window is not mapped yet."); + return; + } + + if (m_bIsFloating) + g_pCompositor->changeWindowZOrder(m_pSelf.lock(), true); + + g_pCompositor->focusWindow(m_pSelf.lock()); + warpCursor(); +} + +void CWindow::onUpdateState() { + std::optional requestsFS = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsFullscreen : m_pXWaylandSurface->state.requestsFullscreen; + std::optional requestsID = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsFullscreenMonitor : MONITOR_INVALID; + std::optional requestsMX = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsMaximize : m_pXWaylandSurface->state.requestsMaximize; + + if (requestsFS.has_value() && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) { + if (requestsID.has_value() && (requestsID.value() != MONITOR_INVALID) && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN_OUTPUT)) { + if (m_bIsMapped) { + const auto monitor = g_pCompositor->getMonitorFromID(requestsID.value()); + g_pCompositor->moveWindowToWorkspaceSafe(m_pSelf.lock(), monitor->activeWorkspace); + g_pCompositor->setActiveMonitor(monitor); + } + + if (!m_bIsMapped) + m_iWantsInitialFullscreenMonitor = requestsID.value(); + } + + bool fs = requestsFS.value(); + if (m_bIsMapped) + g_pCompositor->changeWindowFullscreenModeClient(m_pSelf.lock(), FSMODE_FULLSCREEN, requestsFS.value()); + + if (!m_bIsMapped) + m_bWantsInitialFullscreen = fs; + } + + if (requestsMX.has_value() && !(m_eSuppressedEvents & SUPPRESS_MAXIMIZE)) { + if (m_bIsMapped) + g_pCompositor->changeWindowFullscreenModeClient(m_pSelf.lock(), FSMODE_MAXIMIZED, requestsMX.value()); + } +} + +void CWindow::onUpdateMeta() { + const auto NEWTITLE = fetchTitle(); + bool doUpdate = false; + + if (m_szTitle != NEWTITLE) { + m_szTitle = NEWTITLE; + g_pEventManager->postEvent(SHyprIPCEvent{"windowtitle", std::format("{:x}", (uintptr_t)this)}); + g_pEventManager->postEvent(SHyprIPCEvent{"windowtitlev2", std::format("{:x},{}", (uintptr_t)this, m_szTitle)}); + EMIT_HOOK_EVENT("windowTitle", m_pSelf.lock()); + + if (m_pSelf == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", m_szClass + "," + m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); + } + + Debug::log(LOG, "Window {:x} set title to {}", (uintptr_t)this, m_szTitle); + doUpdate = true; + } + + const auto NEWCLASS = fetchClass(); + if (m_szClass != NEWCLASS) { + m_szClass = NEWCLASS; + + if (m_pSelf == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", m_szClass + "," + m_szTitle}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", std::format("{:x}", (uintptr_t)this)}); + EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); + } + + Debug::log(LOG, "Window {:x} set class to {}", (uintptr_t)this, m_szClass); + doUpdate = true; + } + + if (doUpdate) { + updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); + updateToplevel(); + } +} + +std::string CWindow::fetchTitle() { + if (!m_bIsX11) { + if (m_pXDGSurface && m_pXDGSurface->toplevel) + return m_pXDGSurface->toplevel->state.title; + } else { + if (m_pXWaylandSurface) + return m_pXWaylandSurface->state.title; + } + + return ""; +} + +std::string CWindow::fetchClass() { + if (!m_bIsX11) { + if (m_pXDGSurface && m_pXDGSurface->toplevel) + return m_pXDGSurface->toplevel->state.appid; + } else { + if (m_pXWaylandSurface) + return m_pXWaylandSurface->state.appid; + } + + return ""; +} + +void CWindow::onAck(uint32_t serial) { + const auto SERIAL = std::find_if(m_vPendingSizeAcks.rbegin(), m_vPendingSizeAcks.rend(), [serial](const auto& e) { return e.first == serial; }); + + if (SERIAL == m_vPendingSizeAcks.rend()) + return; + + m_pPendingSizeAck = *SERIAL; + std::erase_if(m_vPendingSizeAcks, [&](const auto& el) { return el.first <= SERIAL->first; }); +} + +void CWindow::onResourceChangeX11() { + if (m_pXWaylandSurface->surface && !m_pWLSurface->resource()) + m_pWLSurface->assign(m_pXWaylandSurface->surface.lock(), m_pSelf.lock()); + else if (!m_pXWaylandSurface->surface && m_pWLSurface->resource()) + m_pWLSurface->unassign(); + + // update metadata as well, + // could be first assoc and we need to catch the class + onUpdateMeta(); + + Debug::log(LOG, "xwayland window {:x} -> association to {:x}", (uintptr_t)m_pXWaylandSurface.get(), (uintptr_t)m_pWLSurface->resource().get()); +} + +void CWindow::onX11ConfigureRequest(CBox box) { + + if (!m_pXWaylandSurface->surface || !m_pXWaylandSurface->mapped || !m_bIsMapped) { + m_pXWaylandSurface->configure(box); + m_vPendingReportedSize = box.size(); + m_vReportedSize = box.size(); + m_vReportedPosition = box.pos(); + updateX11SurfaceScale(); + return; + } + + g_pHyprRenderer->damageWindow(m_pSelf.lock()); + + if (!m_bIsFloating || isFullscreen() || g_pInputManager->currentlyDraggedWindow == m_pSelf) { + sendWindowSize(true); + g_pInputManager->refocus(); + g_pHyprRenderer->damageWindow(m_pSelf.lock()); + return; + } + + if (box.size() > Vector2D{1, 1}) + setHidden(false); + else + setHidden(true); + + m_vRealPosition->setValueAndWarp(xwaylandPositionToReal(box.pos())); + m_vRealSize->setValueAndWarp(xwaylandSizeToReal(box.size())); + + m_vPosition = m_vRealPosition->goal(); + m_vSize = m_vRealSize->goal(); + + if (m_vPendingReportedSize != box.size() || m_vReportedPosition != box.pos()) { + m_pXWaylandSurface->configure(box); + m_vReportedSize = box.size(); + m_vPendingReportedSize = box.size(); + m_vReportedPosition = box.pos(); + } + + updateX11SurfaceScale(); + updateWindowDecos(); + + if (!m_pWorkspace || !m_pWorkspace->isVisible()) + return; // further things are only for visible windows + + m_pWorkspace = g_pCompositor->getMonitorFromVector(m_vRealPosition->goal() + m_vRealSize->goal() / 2.f)->activeWorkspace; + + g_pCompositor->changeWindowZOrder(m_pSelf.lock(), true); + + m_bCreatedOverFullscreen = true; + + g_pHyprRenderer->damageWindow(m_pSelf.lock()); +} + +void CWindow::warpCursor(bool force) { + static auto PERSISTENTWARPS = CConfigValue("cursor:persistent_warps"); + const auto coords = m_vRelativeCursorCoordsOnLastWarp; + m_vRelativeCursorCoordsOnLastWarp.x = -1; // reset m_vRelativeCursorCoordsOnLastWarp + + if (*PERSISTENTWARPS && coords.x > 0 && coords.y > 0 && coords < m_vSize) // don't warp cursor outside the window + g_pCompositor->warpCursorTo(m_vPosition + coords, force); + else + g_pCompositor->warpCursorTo(middle(), force); +} + +PHLWINDOW CWindow::getSwallower() { + static auto PSWALLOWREGEX = CConfigValue("misc:swallow_regex"); + static auto PSWALLOWEXREGEX = CConfigValue("misc:swallow_exception_regex"); + static auto PSWALLOW = CConfigValue("misc:enable_swallow"); + + if (!*PSWALLOW || std::string{*PSWALLOWREGEX} == STRVAL_EMPTY || (*PSWALLOWREGEX).empty()) + return nullptr; + + // check parent + std::vector candidates; + pid_t currentPid = getPID(); + // walk up the tree until we find someone, 25 iterations max. + for (size_t i = 0; i < 25; ++i) { + currentPid = getPPIDof(currentPid); + + if (!currentPid) + break; + + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped || w->isHidden()) + continue; + + if (w->getPID() == currentPid) + candidates.push_back(w); + } + } + + if (!(*PSWALLOWREGEX).empty()) + std::erase_if(candidates, [&](const auto& other) { return !RE2::FullMatch(other->m_szClass, *PSWALLOWREGEX); }); + + if (candidates.size() == 0) + return nullptr; + + if (!(*PSWALLOWEXREGEX).empty()) + std::erase_if(candidates, [&](const auto& other) { return RE2::FullMatch(other->m_szTitle, *PSWALLOWEXREGEX); }); + + if (candidates.size() == 0) + return nullptr; + + if (candidates.size() == 1) + return candidates[0]; + + // walk up the focus history and find the last focused + for (auto const& w : g_pCompositor->m_vWindowFocusHistory) { + if (!w) + continue; + + if (std::find(candidates.begin(), candidates.end(), w.lock()) != candidates.end()) + return w.lock(); + } + + // if none are found (??) then just return the first one + return candidates[0]; +} + +void CWindow::unsetWindowData(eOverridePriority priority) { + for (auto const& element : NWindowProperties::boolWindowProperties) { + element.second(m_pSelf.lock())->unset(priority); + } + for (auto const& element : NWindowProperties::intWindowProperties) { + element.second(m_pSelf.lock())->unset(priority); + } + for (auto const& element : NWindowProperties::floatWindowProperties) { + element.second(m_pSelf.lock())->unset(priority); + } +} + +bool CWindow::isX11OverrideRedirect() { + return m_pXWaylandSurface && m_pXWaylandSurface->overrideRedirect; +} + +bool CWindow::isModal() { + return (m_pXWaylandSurface && m_pXWaylandSurface->modal); +} + +Vector2D CWindow::requestedMinSize() { + if ((m_bIsX11 && !m_pXWaylandSurface->sizeHints) || (!m_bIsX11 && !m_pXDGSurface->toplevel)) + return Vector2D(1, 1); + + Vector2D minSize = m_bIsX11 ? Vector2D(m_pXWaylandSurface->sizeHints->min_width, m_pXWaylandSurface->sizeHints->min_height) : m_pXDGSurface->toplevel->layoutMinSize(); + + minSize = minSize.clamp({1, 1}); + + return minSize; +} + +Vector2D CWindow::requestedMaxSize() { + constexpr int NO_MAX_SIZE_LIMIT = 99999; + if (((m_bIsX11 && !m_pXWaylandSurface->sizeHints) || (!m_bIsX11 && !m_pXDGSurface->toplevel) || m_sWindowData.noMaxSize.valueOrDefault())) + return Vector2D(NO_MAX_SIZE_LIMIT, NO_MAX_SIZE_LIMIT); + + Vector2D maxSize = m_bIsX11 ? Vector2D(m_pXWaylandSurface->sizeHints->max_width, m_pXWaylandSurface->sizeHints->max_height) : m_pXDGSurface->toplevel->layoutMaxSize(); + + if (maxSize.x < 5) + maxSize.x = NO_MAX_SIZE_LIMIT; + if (maxSize.y < 5) + maxSize.y = NO_MAX_SIZE_LIMIT; + + return maxSize; +} + +Vector2D CWindow::realToReportSize() { + if (!m_bIsX11) + return m_vRealSize->goal().clamp(Vector2D{0, 0}, Vector2D{std::numeric_limits::infinity(), std::numeric_limits::infinity()}); + + static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); + + const auto REPORTSIZE = m_vRealSize->goal().clamp(Vector2D{1, 1}, Vector2D{std::numeric_limits::infinity(), std::numeric_limits::infinity()}); + const auto PMONITOR = m_pMonitor.lock(); + + if (*PXWLFORCESCALEZERO && PMONITOR) + return REPORTSIZE * PMONITOR->scale; + + return REPORTSIZE; +} + +Vector2D CWindow::realToReportPosition() { + if (!m_bIsX11) + return m_vRealPosition->goal(); + + return g_pXWaylandManager->waylandToXWaylandCoords(m_vRealPosition->goal()); +} + +Vector2D CWindow::xwaylandSizeToReal(Vector2D size) { + static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); + + const auto PMONITOR = m_pMonitor.lock(); + const auto SIZE = size.clamp(Vector2D{1, 1}, Vector2D{std::numeric_limits::infinity(), std::numeric_limits::infinity()}); + const auto SCALE = *PXWLFORCESCALEZERO ? PMONITOR->scale : 1.0f; + + return SIZE / SCALE; +} + +Vector2D CWindow::xwaylandPositionToReal(Vector2D pos) { + return g_pXWaylandManager->xwaylandToWaylandCoords(pos); +} + +void CWindow::updateX11SurfaceScale() { + static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); + + m_fX11SurfaceScaledBy = 1.0f; + if (m_bIsX11 && *PXWLFORCESCALEZERO) { + if (const auto PMONITOR = m_pMonitor.lock(); PMONITOR) + m_fX11SurfaceScaledBy = PMONITOR->scale; + } +} + +void CWindow::sendWindowSize(bool force) { + const auto PMONITOR = m_pMonitor.lock(); + + Debug::log(TRACE, "sendWindowSize: window:{:x},title:{} with real pos {}, real size {} (force: {})", (uintptr_t)this, this->m_szTitle, m_vRealPosition->goal(), + m_vRealSize->goal(), force); + + // TODO: this should be decoupled from setWindowSize IMO + const auto REPORTPOS = realToReportPosition(); + + const auto REPORTSIZE = realToReportSize(); + + if (!force && m_vPendingReportedSize == REPORTSIZE && (m_vReportedPosition == REPORTPOS || !m_bIsX11)) + return; + + m_vReportedPosition = REPORTPOS; + m_vPendingReportedSize = REPORTSIZE; + updateX11SurfaceScale(); + + if (m_bIsX11 && m_pXWaylandSurface) + m_pXWaylandSurface->configure({REPORTPOS, REPORTSIZE}); + else if (m_pXDGSurface && m_pXDGSurface->toplevel) + m_vPendingSizeAcks.emplace_back(m_pXDGSurface->toplevel->setSize(REPORTSIZE), REPORTPOS.floor()); +} + +NContentType::eContentType CWindow::getContentType() { + if (!m_pWLSurface || !m_pWLSurface->resource() || !m_pWLSurface->resource()->contentType.valid()) + return CONTENT_TYPE_NONE; + + return m_pWLSurface->resource()->contentType->value; +} + +void CWindow::setContentType(NContentType::eContentType contentType) { + if (!m_pWLSurface->resource()->contentType.valid()) + m_pWLSurface->resource()->contentType = PROTO::contentType->getContentType(m_pWLSurface->resource()); + // else disallow content type change if proto is used? + + Debug::log(INFO, "ContentType for window {}", (int)contentType); + m_pWLSurface->resource()->contentType->value = contentType; +} + +void CWindow::deactivateGroupMembers() { + auto curr = getGroupHead(); + while (curr) { + if (curr != m_pSelf.lock()) { + if (curr->m_bIsX11) + curr->m_pXWaylandSurface->activate(false); + else if (curr->m_pXDGSurface && curr->m_pXDGSurface->toplevel) + curr->m_pXDGSurface->toplevel->setActive(false); + } + + curr = curr->m_sGroupData.pNextWindow.lock(); + if (curr == getGroupHead()) + break; + } +} + +bool CWindow::isNotResponding() { + return g_pANRManager->isNotResponding(m_pSelf.lock()); +} diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp new file mode 100644 index 00000000..3ed4ce75 --- /dev/null +++ b/src/desktop/Window.hpp @@ -0,0 +1,620 @@ +#pragma once + +#include +#include +#include + +#include "../config/ConfigDataValues.hpp" +#include "../helpers/AnimatedVariable.hpp" +#include "../helpers/math/Math.hpp" +#include "../helpers/signal/Signal.hpp" +#include "../helpers/TagKeeper.hpp" +#include "../macros.hpp" +#include "../managers/XWaylandManager.hpp" +#include "../render/decorations/IHyprWindowDecoration.hpp" +#include "../render/Transformer.hpp" +#include "DesktopTypes.hpp" +#include "Popup.hpp" +#include "Subsurface.hpp" +#include "WLSurface.hpp" +#include "Workspace.hpp" +#include "WindowRule.hpp" +#include "../protocols/types/ContentType.hpp" + +class CXDGSurfaceResource; +class CXWaylandSurface; + +enum eIdleInhibitMode : uint8_t { + IDLEINHIBIT_NONE = 0, + IDLEINHIBIT_ALWAYS, + IDLEINHIBIT_FULLSCREEN, + IDLEINHIBIT_FOCUS +}; + +enum eGroupRules : uint8_t { + // effective only during first map, except for _ALWAYS variant + GROUP_NONE = 0, + GROUP_SET = 1 << 0, // Open as new group or add to focused group + GROUP_SET_ALWAYS = 1 << 1, + GROUP_BARRED = 1 << 2, // Don't insert to focused group. + GROUP_LOCK = 1 << 3, // Lock m_sGroupData.lock + GROUP_LOCK_ALWAYS = 1 << 4, + GROUP_INVADE = 1 << 5, // Force enter a group, event if lock is engaged + GROUP_OVERRIDE = 1 << 6, // Override other rules +}; + +enum eGetWindowProperties : uint8_t { + WINDOW_ONLY = 0, + RESERVED_EXTENTS = 1 << 0, + INPUT_EXTENTS = 1 << 1, + FULL_EXTENTS = 1 << 2, + FLOATING_ONLY = 1 << 3, + ALLOW_FLOATING = 1 << 4, + USE_PROP_TILED = 1 << 5, +}; + +enum eSuppressEvents : uint8_t { + SUPPRESS_NONE = 0, + SUPPRESS_FULLSCREEN = 1 << 0, + SUPPRESS_MAXIMIZE = 1 << 1, + SUPPRESS_ACTIVATE = 1 << 2, + SUPPRESS_ACTIVATE_FOCUSONLY = 1 << 3, + SUPPRESS_FULLSCREEN_OUTPUT = 1 << 4, +}; + +class IWindowTransformer; + +struct SAlphaValue { + float m_fAlpha; + bool m_bOverride; + + float applyAlpha(float alpha) const { + if (m_bOverride) + return m_fAlpha; + else + return m_fAlpha * alpha; + }; +}; + +enum eOverridePriority : uint8_t { + PRIORITY_LAYOUT = 0, + PRIORITY_WORKSPACE_RULE, + PRIORITY_WINDOW_RULE, + PRIORITY_SET_PROP, +}; + +template +class CWindowOverridableVar { + public: + CWindowOverridableVar(T const& value, eOverridePriority priority) { + values[priority] = value; + } + CWindowOverridableVar(T const& value) : defaultValue{value} {} + + CWindowOverridableVar() = default; + ~CWindowOverridableVar() = default; + + CWindowOverridableVar& operator=(CWindowOverridableVar const& other) { + // Self-assignment check + if (this == &other) + return *this; + + for (auto const& value : other.values) { + values[value.first] = value.second; + } + + return *this; + } + + void unset(eOverridePriority priority) { + values.erase(priority); + } + + bool hasValue() { + return !values.empty(); + } + + T value() { + if (!values.empty()) + return std::prev(values.end())->second; + else + throw std::bad_optional_access(); + } + + T valueOr(T const& other) { + if (hasValue()) + return value(); + else + return other; + } + + T valueOrDefault() { + return valueOr(defaultValue); + } + + eOverridePriority getPriority() { + if (!values.empty()) + return std::prev(values.end())->first; + else + throw std::bad_optional_access(); + } + + void matchOptional(std::optional const& optValue, eOverridePriority priority) { + if (optValue.has_value()) + values[priority] = optValue.value(); + else + unset(priority); + } + + operator std::optional() { + if (hasValue()) + return value(); + else + return std::nullopt; + } + + private: + std::map values; + T defaultValue; // used for toggling, so required for bool +}; + +struct SWindowData { + CWindowOverridableVar alpha = SAlphaValue{1.f, false}; + CWindowOverridableVar alphaInactive = SAlphaValue{1.f, false}; + CWindowOverridableVar alphaFullscreen = SAlphaValue{1.f, false}; + + CWindowOverridableVar allowsInput = false; + CWindowOverridableVar dimAround = false; + CWindowOverridableVar decorate = true; + CWindowOverridableVar focusOnActivate = false; + CWindowOverridableVar keepAspectRatio = false; + CWindowOverridableVar nearestNeighbor = false; + CWindowOverridableVar noAnim = false; + CWindowOverridableVar noBorder = false; + CWindowOverridableVar noBlur = false; + CWindowOverridableVar noDim = false; + CWindowOverridableVar noFocus = false; + CWindowOverridableVar noMaxSize = false; + CWindowOverridableVar noRounding = false; + CWindowOverridableVar noShadow = false; + CWindowOverridableVar noShortcutsInhibit = false; + CWindowOverridableVar opaque = false; + CWindowOverridableVar RGBX = false; + CWindowOverridableVar syncFullscreen = true; + CWindowOverridableVar tearing = false; + CWindowOverridableVar xray = false; + CWindowOverridableVar renderUnfocused = false; + + CWindowOverridableVar rounding; + CWindowOverridableVar roundingPower; + CWindowOverridableVar borderSize; + + CWindowOverridableVar scrollMouse; + CWindowOverridableVar scrollTouchpad; + + CWindowOverridableVar animationStyle; + CWindowOverridableVar maxSize; + CWindowOverridableVar minSize; + + CWindowOverridableVar activeBorderColor; + CWindowOverridableVar inactiveBorderColor; + + CWindowOverridableVar persistentSize; +}; + +struct SInitialWorkspaceToken { + PHLWINDOWREF primaryOwner; + std::string workspace; +}; + +struct SFullscreenState { + eFullscreenMode internal = FSMODE_NONE; + eFullscreenMode client = FSMODE_NONE; +}; + +class CWindow { + public: + static PHLWINDOW create(SP); + static PHLWINDOW create(SP); + + private: + CWindow(SP resource); + CWindow(SP surface); + + public: + ~CWindow(); + + SP m_pWLSurface; + + struct { + CSignal destroy; + } events; + + WP m_pXDGSurface; + WP m_pXWaylandSurface; + + // this is the position and size of the "bounding box" + Vector2D m_vPosition = Vector2D(0, 0); + Vector2D m_vSize = Vector2D(0, 0); + + // this is the real position and size used to draw the thing + PHLANIMVAR m_vRealPosition; + PHLANIMVAR m_vRealSize; + + // for not spamming the protocols + Vector2D m_vReportedPosition; + Vector2D m_vReportedSize; + Vector2D m_vPendingReportedSize; + std::optional> m_pPendingSizeAck; + std::vector> m_vPendingSizeAcks; + + // for restoring floating statuses + Vector2D m_vLastFloatingSize; + Vector2D m_vLastFloatingPosition; + + // for floating window offset in workspace animations + Vector2D m_vFloatingOffset = Vector2D(0, 0); + + // this is used for pseudotiling + bool m_bIsPseudotiled = false; + Vector2D m_vPseudoSize = Vector2D(1280, 720); + + // for recovering relative cursor position + Vector2D m_vRelativeCursorCoordsOnLastWarp = Vector2D(-1, -1); + + bool m_bFirstMap = false; // for layouts + bool m_bIsFloating = false; + bool m_bDraggingTiled = false; // for dragging around tiled windows + bool m_bWasMaximized = false; + SFullscreenState m_sFullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE}; + std::string m_szTitle = ""; + std::string m_szClass = ""; + std::string m_szInitialTitle = ""; + std::string m_szInitialClass = ""; + PHLWORKSPACE m_pWorkspace; + PHLMONITORREF m_pMonitor; + + bool m_bIsMapped = false; + + bool m_bRequestsFloat = false; + + // This is for fullscreen apps + bool m_bCreatedOverFullscreen = false; + + // XWayland stuff + bool m_bIsX11 = false; + PHLWINDOWREF m_pX11Parent; + bool m_bX11DoesntWantBorders = false; + bool m_bX11ShouldntFocus = false; + float m_fX11SurfaceScaledBy = 1.f; + // + + // For nofocus + bool m_bNoInitialFocus = false; + + // Fullscreen and Maximize + bool m_bWantsInitialFullscreen = false; + MONITORID m_iWantsInitialFullscreenMonitor = MONITOR_INVALID; + + // bitfield eSuppressEvents + uint64_t m_eSuppressedEvents = SUPPRESS_NONE; + + // desktop components + UP m_pSubsurfaceHead; + UP m_pPopupHead; + + // Animated border + CGradientValueData m_cRealBorderColor = {0}; + CGradientValueData m_cRealBorderColorPrevious = {0}; + PHLANIMVAR m_fBorderFadeAnimationProgress; + PHLANIMVAR m_fBorderAngleAnimationProgress; + + // Fade in-out + PHLANIMVAR m_fAlpha; + bool m_bFadingOut = false; + bool m_bReadyToDelete = false; + Vector2D m_vOriginalClosedPos; // these will be used for calculations later on in + Vector2D m_vOriginalClosedSize; // drawing the closing animations + SBoxExtents m_eOriginalClosedExtents; + bool m_bAnimatingIn = false; + + // For pinned (sticky) windows + bool m_bPinned = false; + + // For preserving pinned state when fullscreening a pinned window + bool m_bPinFullscreened = false; + + // urgency hint + bool m_bIsUrgent = false; + + // for proper cycling. While cycling we can't just move the pointers, so we need to keep track of the last cycled window. + PHLWINDOWREF m_pLastCycledWindow; + + // Window decorations + // TODO: make this a SP. + std::vector> m_dWindowDecorations; + std::vector m_vDecosToRemove; + + // Special render data, rules, etc + SWindowData m_sWindowData; + + // Transformers + std::vector> m_vTransformers; + + // for alpha + PHLANIMVAR m_fActiveInactiveAlpha; + PHLANIMVAR m_fMovingFromWorkspaceAlpha; + + // animated shadow color + PHLANIMVAR m_cRealShadowColor; + + // animated tint + PHLANIMVAR m_fDimPercent; + + // animate moving to an invisible workspace + int m_iMonitorMovedFrom = -1; // -1 means not moving + PHLANIMVAR m_fMovingToWorkspaceAlpha; + + // swallowing + PHLWINDOWREF m_pSwallowed; + bool m_bCurrentlySwallowed = false; + bool m_bGroupSwallowed = false; + + // focus stuff + bool m_bStayFocused = false; + + // for toplevel monitor events + MONITORID m_iLastToplevelMonitorID = -1; + MONITORID m_iLastSurfaceMonitorID = -1; + + // for idle inhibiting windows + eIdleInhibitMode m_eIdleInhibitMode = IDLEINHIBIT_NONE; + + // initial token. Will be unregistered on workspace change or timeout of 2 minutes + std::string m_szInitialWorkspaceToken = ""; + + // for groups + struct SGroupData { + PHLWINDOWREF pNextWindow; // nullptr means no grouping. Self means single group. + bool head = false; + bool locked = false; // per group lock + bool deny = false; // deny window from enter a group or made a group + } m_sGroupData; + uint16_t m_eGroupRules = GROUP_NONE; + + bool m_bTearingHint = false; + + // stores the currently matched window rules + std::vector> m_vMatchedRules; + + // window tags + CTagKeeper m_tags; + + // ANR + PHLANIMVAR m_notRespondingTint; + + // For the list lookup + bool operator==(const CWindow& rhs) const { + return m_pXDGSurface == rhs.m_pXDGSurface && m_pXWaylandSurface == rhs.m_pXWaylandSurface && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && + m_bFadingOut == rhs.m_bFadingOut; + } + + // methods + CBox getFullWindowBoundingBox(); + SBoxExtents getFullWindowExtents(); + CBox getWindowBoxUnified(uint64_t props); + CBox getWindowIdealBoundingBoxIgnoreReserved(); + void addWindowDeco(UP deco); + void updateWindowDecos(); + void removeWindowDeco(IHyprWindowDecoration* deco); + void uncacheWindowDecos(); + bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {}); + pid_t getPID(); + IHyprWindowDecoration* getDecorationByType(eDecorationType); + void updateToplevel(); + void updateSurfaceScaleTransformDetails(bool force = false); + void moveToWorkspace(PHLWORKSPACE); + PHLWINDOW x11TransientFor(); + void onUnmap(); + void onMap(); + void setHidden(bool hidden); + bool isHidden(); + void applyDynamicRule(const SP& r); + void updateDynamicRules(); + SBoxExtents getFullWindowReservedArea(); + Vector2D middle(); + bool opaque(); + float rounding(); + float roundingPower(); + bool canBeTorn(); + void setSuspended(bool suspend); + bool visibleOnMonitor(PHLMONITOR pMonitor); + WORKSPACEID workspaceID(); + MONITORID monitorID(); + bool onSpecialWorkspace(); + void activate(bool force = false); + int surfacesCount(); + void clampWindowSize(const std::optional minSize, const std::optional maxSize); + bool isFullscreen(); + bool isEffectiveInternalFSMode(const eFullscreenMode); + int getRealBorderSize(); + float getScrollMouse(); + float getScrollTouchpad(); + void updateWindowData(); + void updateWindowData(const struct SWorkspaceRule&); + void onBorderAngleAnimEnd(WP pav); + bool isInCurvedCorner(double x, double y); + bool hasPopupAt(const Vector2D& pos); + int popupsCount(); + void applyGroupRules(); + void createGroup(); + void destroyGroup(); + PHLWINDOW getGroupHead(); + PHLWINDOW getGroupTail(); + PHLWINDOW getGroupCurrent(); + PHLWINDOW getGroupPrevious(); + PHLWINDOW getGroupWindowByIndex(int); + int getGroupSize(); + bool canBeGroupedInto(PHLWINDOW pWindow); + void setGroupCurrent(PHLWINDOW pWindow); + void insertWindowToGroup(PHLWINDOW pWindow); + void updateGroupOutputs(); + void switchWithWindowInGroup(PHLWINDOW pWindow); + void setAnimationsToMove(); + void onWorkspaceAnimUpdate(); + void onFocusAnimUpdate(); + void onUpdateState(); + void onUpdateMeta(); + void onX11ConfigureRequest(CBox box); + void onResourceChangeX11(); + std::string fetchTitle(); + std::string fetchClass(); + void warpCursor(bool force = false); + PHLWINDOW getSwallower(); + void unsetWindowData(eOverridePriority priority); + bool isX11OverrideRedirect(); + bool isModal(); + Vector2D requestedMinSize(); + Vector2D requestedMaxSize(); + Vector2D realToReportSize(); + Vector2D realToReportPosition(); + Vector2D xwaylandSizeToReal(Vector2D size); + Vector2D xwaylandPositionToReal(Vector2D size); + void updateX11SurfaceScale(); + void sendWindowSize(bool force = false); + NContentType::eContentType getContentType(); + void setContentType(NContentType::eContentType contentType); + void deactivateGroupMembers(); + bool isNotResponding(); + + CBox getWindowMainSurfaceBox() const { + return {m_vRealPosition->value().x, m_vRealPosition->value().y, m_vRealSize->value().x, m_vRealSize->value().y}; + } + + // listeners + void onAck(uint32_t serial); + + // + std::unordered_map getEnv(); + + // + PHLWINDOWREF m_pSelf; + + // make private once we move listeners to inside CWindow + struct { + CHyprSignalListener map; + CHyprSignalListener ack; + CHyprSignalListener unmap; + CHyprSignalListener commit; + CHyprSignalListener destroy; + CHyprSignalListener activate; + CHyprSignalListener configureRequest; + CHyprSignalListener setGeometry; + CHyprSignalListener updateState; + CHyprSignalListener updateMetadata; + CHyprSignalListener resourceChange; + } listeners; + + private: + // For hidden windows and stuff + bool m_bHidden = false; + bool m_bSuspended = false; + WORKSPACEID m_iLastWorkspace = WORKSPACE_INVALID; +}; + +inline bool valid(PHLWINDOW w) { + return w.get(); +} + +inline bool valid(PHLWINDOWREF w) { + return !w.expired(); +} + +inline bool validMapped(PHLWINDOW w) { + if (!valid(w)) + return false; + return w->m_bIsMapped; +} + +inline bool validMapped(PHLWINDOWREF w) { + if (!valid(w)) + return false; + return w->m_bIsMapped; +} + +namespace NWindowProperties { + static const std::unordered_map*(const PHLWINDOW&)>> boolWindowProperties = { + {"allowsinput", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.allowsInput; }}, + {"dimaround", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.dimAround; }}, + {"decorate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.decorate; }}, + {"focusonactivate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.focusOnActivate; }}, + {"keepaspectratio", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.keepAspectRatio; }}, + {"nearestneighbor", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.nearestNeighbor; }}, + {"noanim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noAnim; }}, + {"noblur", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBlur; }}, + {"noborder", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noBorder; }}, + {"nodim", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noDim; }}, + {"nofocus", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noFocus; }}, + {"nomaxsize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noMaxSize; }}, + {"norounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noRounding; }}, + {"noshadow", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShadow; }}, + {"noshortcutsinhibit", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.noShortcutsInhibit; }}, + {"opaque", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.opaque; }}, + {"forcergbx", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.RGBX; }}, + {"syncfullscreen", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.syncFullscreen; }}, + {"immediate", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.tearing; }}, + {"xray", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.xray; }}, + }; + + const std::unordered_map*(const PHLWINDOW&)>> intWindowProperties = { + {"rounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.rounding; }}, + {"bordersize", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.borderSize; }}, + }; + + const std::unordered_map*(PHLWINDOW)>> floatWindowProperties = { + {"roundingpower", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.roundingPower; }}, + {"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollMouse; }}, + {"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }}, + }; +}; + +/** + format specification + - 'x', only address, equivalent of (uintpr_t)CWindow* + - 'm', with monitor id + - 'w', with workspace id + - 'c', with application class +*/ + +template +struct std::formatter : std::formatter { + bool formatAddressOnly = false; + bool formatWorkspace = false; + bool formatMonitor = false; + bool formatClass = false; + FORMAT_PARSE( // + FORMAT_FLAG('x', formatAddressOnly) // + FORMAT_FLAG('m', formatMonitor) // + FORMAT_FLAG('w', formatWorkspace) // + FORMAT_FLAG('c', formatClass), + PHLWINDOW) + + template + auto format(PHLWINDOW const& w, FormatContext& ctx) const { + auto&& out = ctx.out(); + if (formatAddressOnly) + return std::format_to(out, "{:x}", (uintptr_t)w.get()); + if (!w) + return std::format_to(out, "[Window nullptr]"); + + std::format_to(out, "["); + std::format_to(out, "Window {:x}: title: \"{}\"", (uintptr_t)w.get(), w->m_szTitle); + if (formatWorkspace) + std::format_to(out, ", workspace: {}", w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID); + if (formatMonitor) + std::format_to(out, ", monitor: {}", w->monitorID()); + if (formatClass) + std::format_to(out, ", class: {}", w->m_szClass); + return std::format_to(out, "]"); + } +}; diff --git a/src/desktop/WindowRule.cpp b/src/desktop/WindowRule.cpp new file mode 100644 index 00000000..23269085 --- /dev/null +++ b/src/desktop/WindowRule.cpp @@ -0,0 +1,96 @@ +#include "WindowRule.hpp" +#include +#include +#include +#include "../config/ConfigManager.hpp" + +static const auto RULES = std::unordered_set{ + "float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused", "persistentsize", +}; +static const auto RULES_PREFIX = std::unordered_set{ + "animation", "bordercolor", "bordersize", "center", "content", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", + "monitor", "move", "opacity", "plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad", + "size", "suppressevent", "tag", "workspace", "xray", +}; + +CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : szValue(value), szRule(rule), v2(isV2), execRule(isExecRule) { + const auto VALS = CVarList(rule, 2, ' '); + const bool VALID = RULES.contains(rule) || std::any_of(RULES_PREFIX.begin(), RULES_PREFIX.end(), [&rule](auto prefix) { return rule.starts_with(prefix); }) || + (NWindowProperties::boolWindowProperties.find(VALS[0]) != NWindowProperties::boolWindowProperties.end()) || + (NWindowProperties::intWindowProperties.find(VALS[0]) != NWindowProperties::intWindowProperties.end()) || + (NWindowProperties::floatWindowProperties.find(VALS[0]) != NWindowProperties::floatWindowProperties.end()); + + if (!VALID) + return; + + if (rule == "float") + ruleType = RULE_FLOAT; + else if (rule == "fullscreen") + ruleType = RULE_FULLSCREEN; + else if (rule == "maximize") + ruleType = RULE_MAXIMIZE; + else if (rule == "noinitialfocus") + ruleType = RULE_NOINITIALFOCUS; + else if (rule == "pin") + ruleType = RULE_PIN; + else if (rule == "stayfocused") + ruleType = RULE_STAYFOCUSED; + else if (rule == "tile") + ruleType = RULE_TILE; + else if (rule == "renderunfocused") + ruleType = RULE_RENDERUNFOCUSED; + else if (rule == "persistentsize") + ruleType = RULE_PERSISTENTSIZE; + else if (rule.starts_with("animation")) + ruleType = RULE_ANIMATION; + else if (rule.starts_with("bordercolor")) + ruleType = RULE_BORDERCOLOR; + else if (rule.starts_with("center")) + ruleType = RULE_CENTER; + else if (rule.starts_with("fullscreenstate")) + ruleType = RULE_FULLSCREENSTATE; + else if (rule.starts_with("group")) + ruleType = RULE_GROUP; + else if (rule.starts_with("idleinhibit")) + ruleType = RULE_IDLEINHIBIT; + else if (rule.starts_with("maxsize")) + ruleType = RULE_MAXSIZE; + else if (rule.starts_with("minsize")) + ruleType = RULE_MINSIZE; + else if (rule.starts_with("monitor")) + ruleType = RULE_MONITOR; + else if (rule.starts_with("move")) + ruleType = RULE_MOVE; + else if (rule.starts_with("opacity")) + ruleType = RULE_OPACITY; + else if (rule.starts_with("plugin:")) + ruleType = RULE_PLUGIN; + else if (rule.starts_with("pseudo")) + ruleType = RULE_PSEUDO; + else if (rule.starts_with("size")) + ruleType = RULE_SIZE; + else if (rule.starts_with("suppressevent")) + ruleType = RULE_SUPPRESSEVENT; + else if (rule.starts_with("tag")) + ruleType = RULE_TAG; + else if (rule.starts_with("workspace")) + ruleType = RULE_WORKSPACE; + else if (rule.starts_with("prop")) + ruleType = RULE_PROP; + else if (rule.starts_with("content")) + ruleType = RULE_CONTENT; + else { + // check if this is a prop. + const CVarList VARS(rule, 0, 's', true); + if (NWindowProperties::intWindowProperties.find(VARS[0]) != NWindowProperties::intWindowProperties.end() || + NWindowProperties::boolWindowProperties.find(VARS[0]) != NWindowProperties::boolWindowProperties.end() || + NWindowProperties::floatWindowProperties.find(VARS[0]) != NWindowProperties::floatWindowProperties.end()) { + *const_cast(&szRule) = "prop " + rule; + ruleType = RULE_PROP; + Debug::log(LOG, "CWindowRule: direct prop rule found, rewritten {} -> {}", rule, szRule); + } else { + Debug::log(ERR, "CWindowRule: didn't match a rule that was found valid?!"); + ruleType = RULE_INVALID; + } + } +} diff --git a/src/desktop/WindowRule.hpp b/src/desktop/WindowRule.hpp new file mode 100644 index 00000000..192d8aa7 --- /dev/null +++ b/src/desktop/WindowRule.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include "Rule.hpp" + +class CWindowRule { + public: + CWindowRule(const std::string& rule, const std::string& value, bool isV2 = false, bool isExecRule = false); + + enum eRuleType : uint8_t { + RULE_INVALID = 0, + RULE_FLOAT, + RULE_FULLSCREEN, + RULE_MAXIMIZE, + RULE_NOINITIALFOCUS, + RULE_PIN, + RULE_STAYFOCUSED, + RULE_TILE, + RULE_RENDERUNFOCUSED, + RULE_ANIMATION, + RULE_BORDERCOLOR, + RULE_CENTER, + RULE_FULLSCREENSTATE, + RULE_GROUP, + RULE_IDLEINHIBIT, + RULE_MAXSIZE, + RULE_MINSIZE, + RULE_MONITOR, + RULE_MOVE, + RULE_OPACITY, + RULE_PLUGIN, + RULE_PSEUDO, + RULE_SIZE, + RULE_SUPPRESSEVENT, + RULE_TAG, + RULE_WORKSPACE, + RULE_PROP, + RULE_CONTENT, + RULE_PERSISTENTSIZE, + }; + + eRuleType ruleType = RULE_INVALID; + + const std::string szValue; + const std::string szRule; + const bool v2 = false; + const bool execRule = false; + + std::string szTitle; + std::string szClass; + std::string szInitialTitle; + std::string szInitialClass; + std::string szTag; + int bX11 = -1; // -1 means "ANY" + int bFloating = -1; + int bFullscreen = -1; + int bPinned = -1; + int bFocus = -1; + std::string szFullscreenState = ""; // empty means any + std::string szOnWorkspace = ""; // empty means any + std::string szWorkspace = ""; // empty means any + std::string szContentType = ""; // empty means any + + // precompiled regexes + CRuleRegexContainer rTitle; + CRuleRegexContainer rClass; + CRuleRegexContainer rInitialTitle; + CRuleRegexContainer rInitialClass; + CRuleRegexContainer rV1Regex; +}; \ No newline at end of file diff --git a/src/desktop/Workspace.cpp b/src/desktop/Workspace.cpp index 5df3f087..322c4e33 100644 --- a/src/desktop/Workspace.cpp +++ b/src/desktop/Workspace.cpp @@ -1,13 +1,10 @@ #include "Workspace.hpp" -#include "view/Group.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" #include "config/ConfigManager.hpp" -#include "managers/animation/AnimationManager.hpp" +#include "managers/AnimationManager.hpp" #include "../managers/EventManager.hpp" -#include "../layout/space/Space.hpp" -#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp" -#include "../event/EventBus.hpp" +#include "../managers/HookSystemManager.hpp" #include #include @@ -16,78 +13,220 @@ using namespace Hyprutils::String; PHLWORKSPACE CWorkspace::create(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special, bool isEmpty) { PHLWORKSPACE workspace = makeShared(id, monitor, name, special, isEmpty); workspace->init(workspace); - g_pCompositor->registerWorkspace(workspace); return workspace; } CWorkspace::CWorkspace(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special, bool isEmpty) : - m_id(id), m_name(name), m_monitor(monitor), m_isSpecialWorkspace(special), m_wasCreatedEmpty(isEmpty) { + m_iID(id), m_szName(name), m_pMonitor(monitor), m_bIsSpecialWorkspace(special), m_bWasCreatedEmpty(isEmpty) { ; } void CWorkspace::init(PHLWORKSPACE self) { - m_self = self; + m_pSelf = self; - g_pAnimationManager->createAnimation(Vector2D(0, 0), m_renderOffset, g_pConfigManager->getAnimationPropertyConfig(m_isSpecialWorkspace ? "specialWorkspaceIn" : "workspacesIn"), - self, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(1.f, m_alpha, g_pConfigManager->getAnimationPropertyConfig(m_isSpecialWorkspace ? "specialWorkspaceIn" : "workspacesIn"), self, + g_pAnimationManager->createAnimation(Vector2D(0, 0), m_vRenderOffset, + g_pConfigManager->getAnimationPropertyConfig(m_bIsSpecialWorkspace ? "specialWorkspaceIn" : "workspacesIn"), self, AVARDAMAGE_ENTIRE); + g_pAnimationManager->createAnimation(1.f, m_fAlpha, g_pConfigManager->getAnimationPropertyConfig(m_bIsSpecialWorkspace ? "specialWorkspaceIn" : "workspacesIn"), self, AVARDAMAGE_ENTIRE); const auto RULEFORTHIS = g_pConfigManager->getWorkspaceRuleFor(self); if (RULEFORTHIS.defaultName.has_value()) - m_name = RULEFORTHIS.defaultName.value(); + m_szName = RULEFORTHIS.defaultName.value(); - m_focusedWindowHook = Event::bus()->m_events.window.close.listen([this](PHLWINDOW pWindow) { - if (pWindow == m_lastFocusedWindow.lock()) - m_lastFocusedWindow.reset(); + m_pFocusedWindowHook = g_pHookSystem->hookDynamic("closeWindow", [this](void* self, SCallbackInfo& info, std::any param) { + const auto PWINDOW = std::any_cast(param); + + if (PWINDOW == m_pLastFocusedWindow.lock()) + m_pLastFocusedWindow.reset(); }); - m_space = Layout::CSpace::create(m_self.lock()); - m_space->setAlgorithmProvider(Layout::Supplementary::algoMatcher()->createAlgorithmForWorkspace(m_self.lock())); - - m_inert = false; + m_bInert = false; const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self); - setPersistent(WORKSPACERULE.isPersistent); + m_bPersistent = WORKSPACERULE.isPersistent; - if (self->m_wasCreatedEmpty) + if (self->m_bWasCreatedEmpty) if (auto cmd = WORKSPACERULE.onCreatedEmptyRunCmd) - CKeybindManager::spawnWithRules(*cmd, self); + g_pKeybindManager->spawnWithRules(*cmd, self); - g_pEventManager->postEvent({.event = "createworkspace", .data = m_name}); - g_pEventManager->postEvent({.event = "createworkspacev2", .data = std::format("{},{}", m_id, m_name)}); - Event::bus()->m_events.workspace.created.emit(self); + g_pEventManager->postEvent({"createworkspace", m_szName}); + g_pEventManager->postEvent({"createworkspacev2", std::format("{},{}", m_iID, m_szName)}); + EMIT_HOOK_EVENT("createWorkspace", this); +} + +SWorkspaceIDName CWorkspace::getPrevWorkspaceIDName() const { + return m_sPrevWorkspace; } CWorkspace::~CWorkspace() { - Log::logger->log(Log::DEBUG, "Destroying workspace ID {}", m_id); + Debug::log(LOG, "Destroying workspace ID {}", m_iID); + + // check if g_pHookSystem and g_pEventManager exist, they might be destroyed as in when the compositor is closing. + if (g_pHookSystem) + g_pHookSystem->unhook(m_pFocusedWindowHook); if (g_pEventManager) { - g_pEventManager->postEvent({.event = "destroyworkspace", .data = m_name}); - g_pEventManager->postEvent({.event = "destroyworkspacev2", .data = std::format("{},{}", m_id, m_name)}); + g_pEventManager->postEvent({"destroyworkspace", m_szName}); + g_pEventManager->postEvent({"destroyworkspacev2", std::format("{},{}", m_iID, m_szName)}); + EMIT_HOOK_EVENT("destroyWorkspace", this); + } +} + +void CWorkspace::startAnim(bool in, bool left, bool instant) { + if (!instant) { + const std::string ANIMNAME = std::format("{}{}", m_bIsSpecialWorkspace ? "specialWorkspace" : "workspaces", in ? "In" : "Out"); + + m_fAlpha->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME)); + m_vRenderOffset->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME)); } - Event::bus()->m_events.workspace.removed.emit(m_self); + const auto ANIMSTYLE = m_fAlpha->getStyle(); + static auto PWORKSPACEGAP = CConfigValue("general:gaps_workspaces"); - m_events.destroy.emit(); + // set floating windows offset callbacks + m_vRenderOffset->setUpdateCallback([&](auto) { + for (auto const& w : g_pCompositor->m_vWindows) { + if (!validMapped(w) || w->workspaceID() != m_iID) + continue; + + w->onWorkspaceAnimUpdate(); + }; + }); + + if (ANIMSTYLE.starts_with("slidefade")) { + const auto PMONITOR = m_pMonitor.lock(); + float movePerc = 100.f; + + if (ANIMSTYLE.find('%') != std::string::npos) { + try { + auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ') + 1); + movePerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { Debug::log(ERR, "Error in startAnim: invalid percentage"); } + } + + m_fAlpha->setValueAndWarp(1.f); + m_vRenderOffset->setValueAndWarp(Vector2D(0, 0)); + + if (ANIMSTYLE.starts_with("slidefadevert")) { + if (in) { + m_fAlpha->setValueAndWarp(0.f); + m_vRenderOffset->setValueAndWarp(Vector2D(0.0, (left ? PMONITOR->vecSize.y : -PMONITOR->vecSize.y) * (movePerc / 100.f))); + *m_fAlpha = 1.f; + *m_vRenderOffset = Vector2D(0, 0); + } else { + m_fAlpha->setValueAndWarp(1.f); + *m_fAlpha = 0.f; + *m_vRenderOffset = Vector2D(0.0, (left ? -PMONITOR->vecSize.y : PMONITOR->vecSize.y) * (movePerc / 100.f)); + } + } else { + if (in) { + m_fAlpha->setValueAndWarp(0.f); + m_vRenderOffset->setValueAndWarp(Vector2D((left ? PMONITOR->vecSize.x : -PMONITOR->vecSize.x) * (movePerc / 100.f), 0.0)); + *m_fAlpha = 1.f; + *m_vRenderOffset = Vector2D(0, 0); + } else { + m_fAlpha->setValueAndWarp(1.f); + *m_fAlpha = 0.f; + *m_vRenderOffset = Vector2D((left ? -PMONITOR->vecSize.x : PMONITOR->vecSize.x) * (movePerc / 100.f), 0.0); + } + } + } else if (ANIMSTYLE == "fade") { + m_vRenderOffset->setValueAndWarp(Vector2D(0, 0)); // fix a bug, if switching from slide -> fade. + + if (in) { + m_fAlpha->setValueAndWarp(0.f); + *m_fAlpha = 1.f; + } else { + m_fAlpha->setValueAndWarp(1.f); + *m_fAlpha = 0.f; + } + } else if (ANIMSTYLE == "slidevert") { + // fallback is slide + const auto PMONITOR = m_pMonitor.lock(); + const auto YDISTANCE = PMONITOR->vecSize.y + *PWORKSPACEGAP; + + m_fAlpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. + + if (in) { + m_vRenderOffset->setValueAndWarp(Vector2D(0.0, left ? YDISTANCE : -YDISTANCE)); + *m_vRenderOffset = Vector2D(0, 0); + } else { + *m_vRenderOffset = Vector2D(0.0, left ? -YDISTANCE : YDISTANCE); + } + } else { + // fallback is slide + const auto PMONITOR = m_pMonitor.lock(); + const auto XDISTANCE = PMONITOR->vecSize.x + *PWORKSPACEGAP; + + m_fAlpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. + + if (in) { + m_vRenderOffset->setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0.0)); + *m_vRenderOffset = Vector2D(0, 0); + } else { + *m_vRenderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0.0); + } + } + + if (m_bIsSpecialWorkspace) { + // required for open/close animations + if (in) { + m_fAlpha->setValueAndWarp(0.f); + *m_fAlpha = 1.f; + } else { + m_fAlpha->setValueAndWarp(1.f); + *m_fAlpha = 0.f; + } + } + + if (instant) { + m_vRenderOffset->warp(); + m_fAlpha->warp(); + } +} + +void CWorkspace::setActive(bool on) { + ; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40 +} + +void CWorkspace::moveToMonitor(const MONITORID& id) { + ; // empty until https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/40 } PHLWINDOW CWorkspace::getLastFocusedWindow() { - if (!validMapped(m_lastFocusedWindow) || m_lastFocusedWindow->workspaceID() != m_id) + if (!validMapped(m_pLastFocusedWindow) || m_pLastFocusedWindow->workspaceID() != m_iID) return nullptr; - return m_lastFocusedWindow.lock(); + return m_pLastFocusedWindow.lock(); +} + +void CWorkspace::rememberPrevWorkspace(const PHLWORKSPACE& prev) { + if (!prev) { + m_sPrevWorkspace.id = -1; + m_sPrevWorkspace.name = ""; + return; + } + + if (prev->m_iID == m_iID) { + Debug::log(LOG, "Tried to set prev workspace to the same as current one"); + return; + } + + m_sPrevWorkspace.id = prev->m_iID; + m_sPrevWorkspace.name = prev->m_szName; + + prev->m_pMonitor->addPrevWorkspaceID(prev->m_iID); } std::string CWorkspace::getConfigName() { - if (m_isSpecialWorkspace) { - return m_name; + if (m_bIsSpecialWorkspace) { + return m_szName; } - if (m_id > 0) - return std::to_string(m_id); + if (m_iID > 0) + return std::to_string(m_iID); - return "name:" + m_name; + return "name:" + m_szName; } bool CWorkspace::matchesStaticSelector(const std::string& selector_) { @@ -97,17 +236,17 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { return true; if (isNumber(selector)) { - const auto& [wsid, wsname, isAutoID] = getWorkspaceIDNameFromString(selector); + const auto& [wsid, wsname] = getWorkspaceIDNameFromString(selector); if (wsid == WORKSPACE_INVALID) return false; - return wsid == m_id; + return wsid == m_iID; } else if (selector.starts_with("name:")) { - return m_name == selector.substr(5); + return m_szName == selector.substr(5); } else if (selector.starts_with("special")) { - return m_name == selector; + return m_szName == selector; } else { // parse selector @@ -135,14 +274,14 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { if (cur == 'r') { WORKSPACEID from = 0, to = 0; if (!prop.starts_with("r[") || !prop.ends_with("]")) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } prop = prop.substr(2, prop.length() - 3); if (!prop.contains("-")) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } @@ -150,7 +289,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { const auto LHS = prop.substr(0, DASHPOS), RHS = prop.substr(DASHPOS + 1); if (!isNumber(LHS) || !isNumber(RHS)) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } @@ -158,23 +297,23 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { from = std::stoll(LHS); to = std::stoll(RHS); } catch (std::exception& e) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } if (to < from || to < 1 || from < 1) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } - if (std::clamp(m_id, from, to) != m_id) + if (std::clamp(m_iID, from, to) != m_iID) return false; continue; } if (cur == 's') { if (!prop.starts_with("s[") || !prop.ends_with("]")) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } @@ -182,14 +321,14 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { const auto SHOULDBESPECIAL = configStringToInt(prop); - if (SHOULDBESPECIAL && sc(*SHOULDBESPECIAL) != m_isSpecialWorkspace) + if (SHOULDBESPECIAL && (bool)*SHOULDBESPECIAL != m_bIsSpecialWorkspace) return false; continue; } if (cur == 'm') { if (!prop.starts_with("m[") || !prop.ends_with("]")) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } @@ -197,27 +336,27 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { const auto PMONITOR = g_pCompositor->getMonitorFromString(prop); - if (!(PMONITOR ? PMONITOR == m_monitor : false)) + if (!(PMONITOR ? PMONITOR == m_pMonitor : false)) return false; continue; } if (cur == 'n') { if (!prop.starts_with("n[") || !prop.ends_with("]")) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } prop = prop.substr(2, prop.length() - 3); - if (prop.starts_with("s:") && !m_name.starts_with(prop.substr(2))) + if (prop.starts_with("s:") && !m_szName.starts_with(prop.substr(2))) return false; - if (prop.starts_with("e:") && !m_name.ends_with(prop.substr(2))) + if (prop.starts_with("e:") && !m_szName.ends_with(prop.substr(2))) return false; const auto WANTSNAMED = configStringToInt(prop); - if (WANTSNAMED && *WANTSNAMED != (m_id <= -1337)) + if (WANTSNAMED && *WANTSNAMED != (m_iID <= -1337)) return false; continue; } @@ -225,7 +364,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { if (cur == 'w') { WORKSPACEID from = 0, to = 0; if (!prop.starts_with("w[") || !prop.ends_with("]")) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } @@ -263,24 +402,24 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { // try single if (!isNumber(prop)) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } try { from = std::stoll(prop); } catch (std::exception& e) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } int count; if (wantsCountGroup) - count = getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional(sc(wantsOnlyTiled)), + count = getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional((bool)wantsOnlyTiled), wantsOnlyPinned ? std::optional(wantsOnlyPinned) : std::nullopt, wantsCountVisible ? std::optional(wantsCountVisible) : std::nullopt); else - count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional(sc(wantsOnlyTiled)), + count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional((bool)wantsOnlyTiled), wantsOnlyPinned ? std::optional(wantsOnlyPinned) : std::nullopt, wantsCountVisible ? std::optional(wantsCountVisible) : std::nullopt); @@ -293,7 +432,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { const auto LHS = prop.substr(0, DASHPOS), RHS = prop.substr(DASHPOS + 1); if (!isNumber(LHS) || !isNumber(RHS)) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } @@ -301,22 +440,22 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { from = std::stoll(LHS); to = std::stoll(RHS); } catch (std::exception& e) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } if (to < from || to < 1 || from < 1) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } WORKSPACEID count; if (wantsCountGroup) count = - getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional(sc(wantsOnlyTiled)), + getGroups(wantsOnlyTiled == -1 ? std::nullopt : std::optional((bool)wantsOnlyTiled), wantsOnlyPinned ? std::optional(wantsOnlyPinned) : std::nullopt, wantsCountVisible ? std::optional(wantsCountVisible) : std::nullopt); else - count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional(sc(wantsOnlyTiled)), + count = getWindows(wantsOnlyTiled == -1 ? std::nullopt : std::optional((bool)wantsOnlyTiled), wantsOnlyPinned ? std::optional(wantsOnlyPinned) : std::nullopt, wantsCountVisible ? std::optional(wantsCountVisible) : std::nullopt); @@ -327,7 +466,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { if (cur == 'f') { if (!prop.starts_with("f[") || !prop.ends_with("]")) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } @@ -336,21 +475,21 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { try { FSSTATE = std::stoi(prop); } catch (std::exception& e) { - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } switch (FSSTATE) { case -1: // no fullscreen - if (m_hasFullscreenWindow) + if (m_bHasFullscreenWindow) return false; break; case 0: // fullscreen full - if (!m_hasFullscreenWindow || m_fullscreenMode != FSMODE_FULLSCREEN) + if (!m_bHasFullscreenWindow || m_efFullscreenMode != FSMODE_FULLSCREEN) return false; break; case 1: // maximized - if (!m_hasFullscreenWindow || m_fullscreenMode != FSMODE_MAXIMIZED) + if (!m_bHasFullscreenWindow || m_efFullscreenMode != FSMODE_MAXIMIZED) return false; break; default: break; @@ -358,7 +497,7 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { continue; } - Log::logger->log(Log::DEBUG, "Invalid selector {}", selector); + Debug::log(LOG, "Invalid selector {}", selector); return false; } @@ -370,23 +509,23 @@ bool CWorkspace::matchesStaticSelector(const std::string& selector_) { } void CWorkspace::markInert() { - m_inert = true; - m_id = WORKSPACE_INVALID; - m_visible = false; - m_monitor.reset(); + m_bInert = true; + m_iID = WORKSPACE_INVALID; + m_bVisible = false; + m_pMonitor.reset(); } bool CWorkspace::inert() { - return m_inert; + return m_bInert; } MONITORID CWorkspace::monitorID() { - return m_monitor ? m_monitor->m_id : MONITOR_INVALID; + return m_pMonitor ? m_pMonitor->ID : MONITOR_INVALID; } PHLWINDOW CWorkspace::getFullscreenWindow() { - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace == m_self && w->isFullscreen()) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace == m_pSelf && w->isFullscreen()) return w; } @@ -394,32 +533,27 @@ PHLWINDOW CWorkspace::getFullscreenWindow() { } bool CWorkspace::isVisible() { - return m_visible; + return m_bVisible; } bool CWorkspace::isVisibleNotCovered() { - const auto PMONITOR = m_monitor.lock(); - if (PMONITOR->m_activeSpecialWorkspace) - return PMONITOR->m_activeSpecialWorkspace->m_id == m_id; + const auto PMONITOR = m_pMonitor.lock(); + if (PMONITOR->activeSpecialWorkspace) + return PMONITOR->activeSpecialWorkspace->m_iID == m_iID; - return PMONITOR->m_activeWorkspace->m_id == m_id; + return PMONITOR->activeWorkspace->m_iID == m_iID; } int CWorkspace::getWindows(std::optional onlyTiled, std::optional onlyPinned, std::optional onlyVisible) { int no = 0; - - if (!m_space) - return 0; - - for (auto const& t : m_space->targets()) { - if (!t) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->workspaceID() != m_iID || !w->m_bIsMapped) continue; - - if (onlyTiled.has_value() && t->floating() == onlyTiled.value()) + if (onlyTiled.has_value() && w->m_bIsFloating == onlyTiled.value()) continue; - if (onlyPinned.has_value() && (!t->window() || t->window()->m_pinned != onlyPinned.value())) + if (onlyPinned.has_value() && w->m_bPinned != onlyPinned.value()) continue; - if (onlyVisible.has_value() && (!t->window() || t->window()->isHidden() == onlyVisible.value())) + if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value()) continue; no++; } @@ -429,16 +563,16 @@ int CWorkspace::getWindows(std::optional onlyTiled, std::optional on int CWorkspace::getGroups(std::optional onlyTiled, std::optional onlyPinned, std::optional onlyVisible) { int no = 0; - for (auto const& g : Desktop::View::groups()) { - const auto HEAD = g->head(); - - if (HEAD->workspaceID() != m_id || !HEAD->m_isMapped) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->workspaceID() != m_iID || !w->m_bIsMapped) continue; - if (onlyTiled.has_value() && HEAD->m_isFloating == onlyTiled.value()) + if (!w->m_sGroupData.head) continue; - if (onlyPinned.has_value() && HEAD->m_pinned != onlyPinned.value()) + if (onlyTiled.has_value() && w->m_bIsFloating == onlyTiled.value()) continue; - if (onlyVisible.has_value() && g->current()->isHidden() == onlyVisible.value()) + if (onlyPinned.has_value() && w->m_bPinned != onlyPinned.value()) + continue; + if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value()) continue; no++; } @@ -446,8 +580,8 @@ int CWorkspace::getGroups(std::optional onlyTiled, std::optional onl } PHLWINDOW CWorkspace::getFirstWindow() { - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace == m_self && w->m_isMapped && !w->isHidden()) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace == m_pSelf && w->m_bIsMapped && !w->isHidden()) return w; } @@ -455,27 +589,32 @@ PHLWINDOW CWorkspace::getFirstWindow() { } PHLWINDOW CWorkspace::getTopLeftWindow() { - const auto PMONITOR = m_monitor.lock(); + const auto PMONITOR = m_pMonitor.lock(); - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace != m_self || !w->m_isMapped || w->isHidden()) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace != m_pSelf || !w->m_bIsMapped || w->isHidden()) continue; const auto WINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved(); - if (WINDOWIDEALBB.x <= PMONITOR->m_position.x + 1 && WINDOWIDEALBB.y <= PMONITOR->m_position.y + 1) + if (WINDOWIDEALBB.x <= PMONITOR->vecPosition.x + 1 && WINDOWIDEALBB.y <= PMONITOR->vecPosition.y + 1) return w; } return nullptr; } bool CWorkspace::hasUrgentWindow() { - return std::ranges::any_of(g_pCompositor->m_windows, [this](const auto& w) { return w->m_workspace == m_self && w->m_isMapped && w->m_isUrgent; }); + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace == m_pSelf && w->m_bIsMapped && w->m_bIsUrgent) + return true; + } + + return false; } void CWorkspace::updateWindowDecos() { - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace != m_self) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace != m_pSelf) continue; w->updateWindowDecos(); @@ -483,10 +622,10 @@ void CWorkspace::updateWindowDecos() { } void CWorkspace::updateWindowData() { - const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_self.lock()); + const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_pSelf.lock()); - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace != m_self) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace != m_pSelf) continue; w->updateWindowData(WORKSPACERULE); @@ -494,8 +633,8 @@ void CWorkspace::updateWindowData() { } void CWorkspace::forceReportSizesToWindows() { - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace != m_self || !w->m_isMapped || w->isHidden()) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace != m_pSelf || !w->m_bIsMapped || w->isHidden()) continue; w->sendWindowSize(true); @@ -503,43 +642,28 @@ void CWorkspace::forceReportSizesToWindows() { } void CWorkspace::rename(const std::string& name) { - if (g_pCompositor->isWorkspaceSpecial(m_id)) + if (g_pCompositor->isWorkspaceSpecial(m_iID)) return; - Log::logger->log(Log::DEBUG, "CWorkspace::rename: Renaming workspace {} to '{}'", m_id, name); - m_name = name; + Debug::log(LOG, "CWorkspace::rename: Renaming workspace {} to '{}'", m_iID, name); + m_szName = name; - const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_self.lock()); - setPersistent(WORKSPACERULE.isPersistent); + const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_pSelf.lock()); + m_bPersistent = WORKSPACERULE.isPersistent; if (WORKSPACERULE.isPersistent) - g_pCompositor->ensurePersistentWorkspacesPresent(std::vector{WORKSPACERULE}, m_self.lock()); + g_pCompositor->ensurePersistentWorkspacesPresent(std::vector{WORKSPACERULE}, m_pSelf.lock()); - g_pEventManager->postEvent({.event = "renameworkspace", .data = std::to_string(m_id) + "," + m_name}); - m_events.renamed.emit(); + g_pEventManager->postEvent({"renameworkspace", std::to_string(m_iID) + "," + m_szName}); } void CWorkspace::updateWindows() { - m_hasFullscreenWindow = std::ranges::any_of(m_space->targets(), [](const auto& t) { return t->fullscreenMode() != FSMODE_NONE; }); + m_bHasFullscreenWindow = std::ranges::any_of(g_pCompositor->m_vWindows, [this](const auto& w) { return w->m_bIsMapped && w->m_pWorkspace == m_pSelf && w->isFullscreen(); }); - for (auto const& t : m_space->targets()) { - if (t->window()) - t->window()->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE); + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped || w->m_pWorkspace != m_pSelf) + continue; + + w->updateDynamicRules(); } } - -void CWorkspace::setPersistent(bool persistent) { - if (m_persistent == persistent) - return; - - m_persistent = persistent; - - if (persistent) - m_selfPersistent = m_self.lock(); - else - m_selfPersistent.reset(); -} - -bool CWorkspace::isPersistent() { - return m_persistent; -} diff --git a/src/desktop/Workspace.hpp b/src/desktop/Workspace.hpp index 87d1c2d8..12dbe328 100644 --- a/src/desktop/Workspace.hpp +++ b/src/desktop/Workspace.hpp @@ -2,13 +2,9 @@ #include "../helpers/AnimatedVariable.hpp" #include +#include "../defines.hpp" #include "DesktopTypes.hpp" #include "../helpers/MiscFunctions.hpp" -#include "../helpers/signal/Signal.hpp" - -namespace Layout { - class CSpace; -}; enum eFullscreenMode : int8_t { FSMODE_NONE = 0, @@ -17,6 +13,8 @@ enum eFullscreenMode : int8_t { FSMODE_MAX = (1 << 2) - 1 }; +class CWindow; + class CWorkspace { public: static PHLWORKSPACE create(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special = false, bool isEmpty = true); @@ -24,82 +22,77 @@ class CWorkspace { CWorkspace(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special = false, bool isEmpty = true); ~CWorkspace(); - WP m_self; - - SP m_space; - // Workspaces ID-based have IDs > 0 // and workspaces name-based have IDs starting with -1337 - WORKSPACEID m_id = WORKSPACE_INVALID; - std::string m_name = ""; - PHLMONITORREF m_monitor; + WORKSPACEID m_iID = WORKSPACE_INVALID; + std::string m_szName = ""; + PHLMONITORREF m_pMonitor; - bool m_hasFullscreenWindow = false; - eFullscreenMode m_fullscreenMode = FSMODE_NONE; + bool m_bHasFullscreenWindow = false; + eFullscreenMode m_efFullscreenMode = FSMODE_NONE; wl_array m_wlrCoordinateArr; // for animations - PHLANIMVAR m_renderOffset; - PHLANIMVAR m_alpha; - bool m_forceRendering = false; + PHLANIMVAR m_vRenderOffset; + PHLANIMVAR m_fAlpha; + bool m_bForceRendering = false; // allows damage to propagate. - bool m_visible = false; + bool m_bVisible = false; // "scratchpad" - bool m_isSpecialWorkspace = false; + bool m_bIsSpecialWorkspace = false; // last window - PHLWINDOWREF m_lastFocusedWindow; + PHLWINDOWREF m_pLastFocusedWindow; // user-set - bool m_defaultFloating = false; - bool m_defaultPseudo = false; + bool m_bDefaultFloating = false; + bool m_bDefaultPseudo = false; // last monitor (used on reconnect) - std::string m_lastMonitor = ""; + std::string m_szLastMonitor = ""; - bool m_wasCreatedEmpty = true; + bool m_bWasCreatedEmpty = true; + + bool m_bPersistent = false; // Inert: destroyed and invalid. If this is true, release the ptr you have. - bool inert(); - MONITORID monitorID(); - PHLWINDOW getLastFocusedWindow(); - std::string getConfigName(); - bool matchesStaticSelector(const std::string& selector); - void markInert(); - void updateWindowDecos(); - void updateWindowData(); - int getWindows(std::optional onlyTiled = {}, std::optional onlyPinned = {}, std::optional onlyVisible = {}); - int getGroups(std::optional onlyTiled = {}, std::optional onlyPinned = {}, std::optional onlyVisible = {}); - bool hasUrgentWindow(); - PHLWINDOW getFirstWindow(); - PHLWINDOW getTopLeftWindow(); - PHLWINDOW getFullscreenWindow(); - bool isVisible(); - bool isVisibleNotCovered(); - void rename(const std::string& name = ""); - void forceReportSizesToWindows(); - void updateWindows(); - void setPersistent(bool persistent); - bool isPersistent(); - - struct { - CSignalT<> destroy; - CSignalT<> renamed; - CSignalT<> monitorChanged; - CSignalT<> activeChanged; - } m_events; + bool inert(); + void startAnim(bool in, bool left, bool instant = false); + void setActive(bool on); + void moveToMonitor(const MONITORID&); + MONITORID monitorID(); + PHLWINDOW getLastFocusedWindow(); + void rememberPrevWorkspace(const PHLWORKSPACE& prevWorkspace); + std::string getConfigName(); + bool matchesStaticSelector(const std::string& selector); + void markInert(); + SWorkspaceIDName getPrevWorkspaceIDName() const; + void updateWindowDecos(); + void updateWindowData(); + int getWindows(std::optional onlyTiled = {}, std::optional onlyPinned = {}, std::optional onlyVisible = {}); + int getGroups(std::optional onlyTiled = {}, std::optional onlyPinned = {}, std::optional onlyVisible = {}); + bool hasUrgentWindow(); + PHLWINDOW getFirstWindow(); + PHLWINDOW getTopLeftWindow(); + PHLWINDOW getFullscreenWindow(); + bool isVisible(); + bool isVisibleNotCovered(); + void rename(const std::string& name = ""); + void forceReportSizesToWindows(); + void updateWindows(); private: - void init(PHLWORKSPACE self); + void init(PHLWORKSPACE self); + // Previous workspace ID and name is stored during a workspace change, allowing travel + // to the previous workspace. + SWorkspaceIDName m_sPrevWorkspace; - CHyprSignalListener m_focusedWindowHook; - bool m_inert = true; - - SP m_selfPersistent; // for persistent workspaces. - bool m_persistent = false; + SP m_pFocusedWindowHook; + bool m_bInert = true; + WP m_pSelf; }; inline bool valid(const PHLWORKSPACE& ref) { diff --git a/src/desktop/history/WindowHistoryTracker.cpp b/src/desktop/history/WindowHistoryTracker.cpp deleted file mode 100644 index 1dd32164..00000000 --- a/src/desktop/history/WindowHistoryTracker.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "WindowHistoryTracker.hpp" - -#include "../view/Window.hpp" -#include "../../event/EventBus.hpp" - -using namespace Desktop; -using namespace Desktop::History; - -SP History::windowTracker() { - static SP tracker = makeShared(); - return tracker; -} - -CWindowHistoryTracker::CWindowHistoryTracker() { - static auto P = Event::bus()->m_events.window.openEarly.listen([this](PHLWINDOW pWindow) { - // add a last track - m_history.insert(m_history.begin(), pWindow); - }); - - static auto P1 = Event::bus()->m_events.window.active.listen([this](PHLWINDOW window, uint8_t reason) { track(window); }); -} - -void CWindowHistoryTracker::track(PHLWINDOW w) { - std::erase(m_history, w); - m_history.emplace_back(w); -} - -const std::vector& CWindowHistoryTracker::fullHistory() { - gc(); - return m_history; -} - -std::vector CWindowHistoryTracker::historyForWorkspace(PHLWORKSPACE ws) { - gc(); - std::vector windows; - - for (const auto& w : m_history) { - if (w->m_workspace != ws) - continue; - - windows.emplace_back(w); - } - - return windows; -} - -void CWindowHistoryTracker::gc() { - std::erase_if(m_history, [](const auto& e) { return !e; }); -} diff --git a/src/desktop/history/WindowHistoryTracker.hpp b/src/desktop/history/WindowHistoryTracker.hpp deleted file mode 100644 index 92645683..00000000 --- a/src/desktop/history/WindowHistoryTracker.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "../DesktopTypes.hpp" - -#include - -namespace Desktop::History { - class CWindowHistoryTracker { - public: - CWindowHistoryTracker(); - ~CWindowHistoryTracker() = default; - - CWindowHistoryTracker(const CWindowHistoryTracker&) = delete; - CWindowHistoryTracker(CWindowHistoryTracker&) = delete; - CWindowHistoryTracker(CWindowHistoryTracker&&) = delete; - - // History is ordered old -> new, meaning .front() is oldest, while .back() is newest - - const std::vector& fullHistory(); - std::vector historyForWorkspace(PHLWORKSPACE ws); - - private: - std::vector m_history; - - void track(PHLWINDOW w); - void gc(); - }; - - SP windowTracker(); -}; \ No newline at end of file diff --git a/src/desktop/history/WorkspaceHistoryTracker.cpp b/src/desktop/history/WorkspaceHistoryTracker.cpp deleted file mode 100644 index daa115f8..00000000 --- a/src/desktop/history/WorkspaceHistoryTracker.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "WorkspaceHistoryTracker.hpp" - -#include "../../helpers/Monitor.hpp" -#include "../Workspace.hpp" -#include "../state/FocusState.hpp" -#include "../../managers/eventLoop/EventLoopManager.hpp" -#include "../../event/EventBus.hpp" -#include "../../config/ConfigValue.hpp" - -#include - -using namespace Desktop; -using namespace Desktop::History; - -SP History::workspaceTracker() { - static SP tracker = makeShared(); - return tracker; -} - -CWorkspaceHistoryTracker::CWorkspaceHistoryTracker() { - static auto P = Event::bus()->m_events.workspace.active.listen([this](PHLWORKSPACE workspace) { track(workspace); }); - - static auto P1 = Event::bus()->m_events.monitor.focused.listen([this](PHLMONITOR mon) { - // This sucks ASS, but we have to do this because switching to a workspace on another mon will trigger a workspace event right afterwards and we don't - // want to remember the workspace that was not visible there - // TODO: do something about this - g_pEventLoopManager->doLater([this, mon = PHLMONITORREF{mon}] { - if (mon) - track(mon->m_activeWorkspace); - }); - }); -} - -CWorkspaceHistoryTracker::SWorkspacePreviousData& CWorkspaceHistoryTracker::dataFor(PHLWORKSPACE ws) { - for (auto& ref : m_datas) { - if (ref.workspace != ws) - continue; - - return ref; - } - - return m_datas.emplace_back(SWorkspacePreviousData{ - .workspace = ws, - }); -} - -void CWorkspaceHistoryTracker::track(PHLWORKSPACE w) { - if (!w || !w->m_monitor || w == m_lastWorkspaceData.workspace) - return; - - static auto PALLOWWORKSPACECYCLES = CConfigValue("binds:allow_workspace_cycles"); - - auto& data = dataFor(w); - - Hyprutils::Utils::CScopeGuard x([&] { setLastWorkspaceData(w); }); - - if (m_lastWorkspaceData.workspace == w && !*PALLOWWORKSPACECYCLES) - return; - - data.previous = m_lastWorkspaceData.workspace; - if (m_lastWorkspaceData.workspace) { - data.previousName = m_lastWorkspaceData.workspace->m_name; - data.previousID = m_lastWorkspaceData.workspace->m_id; - data.previousMon = m_lastWorkspaceData.workspace->m_monitor; - } else { - data.previousName = m_lastWorkspaceData.workspaceName; - data.previousID = m_lastWorkspaceData.workspaceID; - data.previousMon = m_lastWorkspaceData.monitor; - } -} - -void CWorkspaceHistoryTracker::gc() { - std::erase_if(m_datas, [](const auto& e) { return !e.workspace; }); -} - -const CWorkspaceHistoryTracker::SWorkspacePreviousData* CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws) { - gc(); - - for (const auto& d : m_datas) { - if (d.workspace != ws) - continue; - return &d; - } - - return &dataFor(ws); -} - -SWorkspaceIDName CWorkspaceHistoryTracker::previousWorkspaceIDName(PHLWORKSPACE ws) { - gc(); - - for (const auto& d : m_datas) { - if (d.workspace != ws) - continue; - return SWorkspaceIDName{.id = d.previousID, .name = d.previousName, .isAutoIDd = d.previousID <= 0}; - } - - auto& d = dataFor(ws); - return SWorkspaceIDName{.id = d.previousID, .name = d.previousName, .isAutoIDd = d.previousID <= 0}; -} - -const CWorkspaceHistoryTracker::SWorkspacePreviousData* CWorkspaceHistoryTracker::previousWorkspace(PHLWORKSPACE ws, PHLMONITOR restrict) { - if (!restrict) - return previousWorkspace(ws); - - auto& data = dataFor(ws); - while (true) { - - // case 1: previous exists - if (data.previous) { - if (data.previous->m_monitor != restrict) { - data = dataFor(data.previous.lock()); - continue; - } - - break; - } - - // case 2: previous doesnt exist, but we have mon - if (data.previousMon) { - if (data.previousMon != restrict) - return nullptr; - - break; - } - - // case 3: no mon and no previous - return nullptr; - } - - return &data; -} - -SWorkspaceIDName CWorkspaceHistoryTracker::previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict) { - const auto DATA = previousWorkspace(ws, restrict); - if (!DATA) - return SWorkspaceIDName{.id = WORKSPACE_INVALID}; - - return SWorkspaceIDName{.id = DATA->previousID, .name = DATA->previousName, .isAutoIDd = DATA->previousID <= 0}; -} - -void CWorkspaceHistoryTracker::setLastWorkspaceData(PHLWORKSPACE w) { - if (!w) { - m_lastWorkspaceData = {}; - return; - } - - m_lastWorkspaceData.workspace = w; - m_lastWorkspaceData.workspaceID = w->m_id; - m_lastWorkspaceData.workspaceName = w->m_name; - m_lastWorkspaceData.monitor = w->m_monitor; -} diff --git a/src/desktop/history/WorkspaceHistoryTracker.hpp b/src/desktop/history/WorkspaceHistoryTracker.hpp deleted file mode 100644 index baecb363..00000000 --- a/src/desktop/history/WorkspaceHistoryTracker.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "../DesktopTypes.hpp" -#include "../../SharedDefs.hpp" -#include "../../macros.hpp" -#include "../../helpers/MiscFunctions.hpp" - -#include - -namespace Desktop::History { - class CWorkspaceHistoryTracker { - public: - CWorkspaceHistoryTracker(); - ~CWorkspaceHistoryTracker() = default; - - CWorkspaceHistoryTracker(const CWorkspaceHistoryTracker&) = delete; - CWorkspaceHistoryTracker(CWorkspaceHistoryTracker&) = delete; - CWorkspaceHistoryTracker(CWorkspaceHistoryTracker&&) = delete; - - struct SWorkspacePreviousData { - PHLWORKSPACEREF workspace; - PHLWORKSPACEREF previous; - PHLMONITORREF previousMon; - std::string previousName = ""; - WORKSPACEID previousID = WORKSPACE_INVALID; - }; - - const SWorkspacePreviousData* previousWorkspace(PHLWORKSPACE ws); - SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws); - - const SWorkspacePreviousData* previousWorkspace(PHLWORKSPACE ws, PHLMONITOR restrict); - SWorkspaceIDName previousWorkspaceIDName(PHLWORKSPACE ws, PHLMONITOR restrict); - - private: - struct SLastWorkspaceData { - PHLMONITORREF monitor; - PHLWORKSPACEREF workspace; - std::string workspaceName = ""; - WORKSPACEID workspaceID = WORKSPACE_INVALID; - } m_lastWorkspaceData; - - std::vector m_datas; - - void track(PHLWORKSPACE w); - void gc(); - void setLastWorkspaceData(PHLWORKSPACE w); - - SWorkspacePreviousData& dataFor(PHLWORKSPACE ws); - }; - - SP workspaceTracker(); -}; \ No newline at end of file diff --git a/src/desktop/reserved/ReservedArea.cpp b/src/desktop/reserved/ReservedArea.cpp deleted file mode 100644 index 8b4956dd..00000000 --- a/src/desktop/reserved/ReservedArea.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "ReservedArea.hpp" -#include "../../macros.hpp" - -using namespace Desktop; - -// fuck me. Writing this at 11pm, and I have an in-class test tomorrow. -// I am failing that bitch - -CReservedArea::CReservedArea(const Vector2D& tl, const Vector2D& br) : m_initialTopLeft(tl.clamp({0, 0})), m_initialBottomRight(br.clamp({0, 0})) { - calculate(); -} - -CReservedArea::CReservedArea(double top, double right, double bottom, double left) : - m_initialTopLeft(std::max(left, 0.0), std::max(top, 0.0)), m_initialBottomRight(std::max(right, 0.0), std::max(bottom, 0.0)) { - calculate(); -} - -CReservedArea::CReservedArea(const CBox& parent, const CBox& child) { - if (parent.empty() || child.empty()) - return; // empty reserved area - - ASSERT(parent.containsPoint(child.pos() + Vector2D{0.0001, 0.0001})); - ASSERT(parent.containsPoint(child.pos() + child.size() - Vector2D{0.0001, 0.0001})); - - m_initialTopLeft = child.pos() - parent.pos(); - m_initialBottomRight = (parent.pos() + parent.size()) - (child.pos() + child.size()); - - calculate(); -} - -void CReservedArea::calculate() { - m_bottomRight = m_initialBottomRight; - m_topLeft = m_initialTopLeft; - - for (const auto& e : m_dynamicReserved) { - m_bottomRight += e.bottomRight; - m_topLeft += e.topLeft; - } -} - -CBox CReservedArea::apply(const CBox& other) const { - auto c = other.copy(); - c.x += m_topLeft.x; - c.y += m_topLeft.y; - c.w -= m_topLeft.x + m_bottomRight.x; - c.h -= m_topLeft.y + m_bottomRight.y; - return c; -} - -void CReservedArea::applyip(CBox& other) const { - other.x += m_topLeft.x; - other.y += m_topLeft.y; - other.w -= m_topLeft.x + m_bottomRight.x; - other.h -= m_topLeft.y + m_bottomRight.y; -} - -bool CReservedArea::operator==(const CReservedArea& other) const { - return other.m_bottomRight == m_bottomRight && other.m_topLeft == m_topLeft; -} - -double CReservedArea::left() const { - return m_topLeft.x; -} - -double CReservedArea::right() const { - return m_bottomRight.x; -} - -double CReservedArea::top() const { - return m_topLeft.y; -} - -double CReservedArea::bottom() const { - return m_bottomRight.y; -} - -void CReservedArea::resetType(eReservedDynamicType t) { - m_dynamicReserved[t] = {}; - calculate(); -} - -void CReservedArea::addType(eReservedDynamicType t, const Vector2D& topLeft, const Vector2D& bottomRight) { - auto& ref = m_dynamicReserved[t]; - ref.topLeft += topLeft; - ref.bottomRight += bottomRight; - ref.topLeft = ref.topLeft.clamp({0, 0}); - ref.bottomRight = ref.bottomRight.clamp({0, 0}); - calculate(); -} - -void CReservedArea::addType(eReservedDynamicType t, const CReservedArea& area) { - addType(t, {area.left(), area.top()}, {area.right(), area.bottom()}); -} diff --git a/src/desktop/reserved/ReservedArea.hpp b/src/desktop/reserved/ReservedArea.hpp deleted file mode 100644 index 2aca595d..00000000 --- a/src/desktop/reserved/ReservedArea.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "../../helpers/math/Math.hpp" -#include - -namespace Desktop { - enum eReservedDynamicType : uint8_t { - RESERVED_DYNAMIC_TYPE_LS = 0, - RESERVED_DYNAMIC_TYPE_ERROR_BAR, - - RESERVED_DYNAMIC_TYPE_END, - }; - - class CReservedArea { - public: - CReservedArea() = default; - CReservedArea(const Vector2D& tl, const Vector2D& br); - CReservedArea(double top, double right, double bottom, double left); - CReservedArea(const CBox& parent, const CBox& child); - ~CReservedArea() = default; - - CBox apply(const CBox& other) const; - void applyip(CBox& other) const; - - void resetType(eReservedDynamicType); - void addType(eReservedDynamicType, const Vector2D& topLeft, const Vector2D& bottomRight); - void addType(eReservedDynamicType, const CReservedArea& area); - - double left() const; - double right() const; - double top() const; - double bottom() const; - - bool operator==(const CReservedArea& other) const; - - private: - void calculate(); - - Vector2D m_topLeft, m_bottomRight; - Vector2D m_initialTopLeft, m_initialBottomRight; - - struct SDynamicData { - Vector2D topLeft, bottomRight; - }; - - std::array m_dynamicReserved; - }; -}; \ No newline at end of file diff --git a/src/desktop/rule/Engine.cpp b/src/desktop/rule/Engine.cpp deleted file mode 100644 index fa0c2e27..00000000 --- a/src/desktop/rule/Engine.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "Engine.hpp" -#include "Rule.hpp" -#include "../view/LayerSurface.hpp" -#include "../../Compositor.hpp" - -using namespace Desktop; -using namespace Desktop::Rule; - -SP Rule::ruleEngine() { - static SP engine = makeShared(); - return engine; -} - -void CRuleEngine::registerRule(SP&& rule) { - m_rules.emplace_back(std::move(rule)); -} - -void CRuleEngine::unregisterRule(const std::string& name) { - if (name.empty()) - return; - - std::erase_if(m_rules, [&name](const auto& el) { return el->name() == name; }); -} - -void CRuleEngine::unregisterRule(const SP& rule) { - std::erase(m_rules, rule); - cleanExecRules(); -} - -void CRuleEngine::cleanExecRules() { - std::erase_if(m_rules, [](const auto& e) { return e->isExecRule() && e->execExpired(); }); -} - -void CRuleEngine::updateAllRules() { - cleanExecRules(); - for (const auto& w : g_pCompositor->m_windows) { - if (!validMapped(w) || w->isHidden()) - continue; - - w->m_ruleApplicator->propertiesChanged(RULE_PROP_ALL); - } - for (const auto& ls : g_pCompositor->m_layers) { - if (!validMapped(ls)) - continue; - - ls->m_ruleApplicator->propertiesChanged(RULE_PROP_ALL); - } -} - -void CRuleEngine::clearAllRules() { - std::erase_if(m_rules, [](const auto& e) { return !e->isExecRule() || e->execExpired(); }); -} - -const std::vector>& CRuleEngine::rules() { - return m_rules; -} diff --git a/src/desktop/rule/Engine.hpp b/src/desktop/rule/Engine.hpp deleted file mode 100644 index b0ea118e..00000000 --- a/src/desktop/rule/Engine.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Rule.hpp" - -namespace Desktop::Rule { - class CRuleEngine { - public: - CRuleEngine() = default; - ~CRuleEngine() = default; - - void registerRule(SP&& rule); - void unregisterRule(const std::string& name); - void unregisterRule(const SP& rule); - void updateAllRules(); - void cleanExecRules(); - void clearAllRules(); - const std::vector>& rules(); - - private: - std::vector> m_rules; - }; - - SP ruleEngine(); -} \ No newline at end of file diff --git a/src/desktop/rule/Rule.cpp b/src/desktop/rule/Rule.cpp deleted file mode 100644 index ab5525e8..00000000 --- a/src/desktop/rule/Rule.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "Rule.hpp" -#include "../../debug/log/Logger.hpp" -#include - -#include "matchEngine/RegexMatchEngine.hpp" -#include "matchEngine/BoolMatchEngine.hpp" -#include "matchEngine/IntMatchEngine.hpp" -#include "matchEngine/WorkspaceMatchEngine.hpp" -#include "matchEngine/TagMatchEngine.hpp" - -using namespace Desktop; -using namespace Desktop::Rule; - -static const std::unordered_map MATCH_PROP_STRINGS = { - {RULE_PROP_CLASS, "class"}, // - {RULE_PROP_TITLE, "title"}, // - {RULE_PROP_INITIAL_CLASS, "initial_class"}, // - {RULE_PROP_INITIAL_TITLE, "initial_title"}, // - {RULE_PROP_FLOATING, "float"}, // - {RULE_PROP_TAG, "tag"}, // - {RULE_PROP_XWAYLAND, "xwayland"}, // - {RULE_PROP_FULLSCREEN, "fullscreen"}, // - {RULE_PROP_PINNED, "pin"}, // - {RULE_PROP_FOCUS, "focus"}, // - {RULE_PROP_GROUP, "group"}, // - {RULE_PROP_MODAL, "modal"}, // - {RULE_PROP_FULLSCREENSTATE_INTERNAL, "fullscreen_state_internal"}, // - {RULE_PROP_FULLSCREENSTATE_CLIENT, "fullscreen_state_client"}, // - {RULE_PROP_ON_WORKSPACE, "workspace"}, // - {RULE_PROP_CONTENT, "content"}, // - {RULE_PROP_XDG_TAG, "xdg_tag"}, // - {RULE_PROP_NAMESPACE, "namespace"}, // -}; - -static const std::unordered_map RULE_ENGINES = { - {RULE_PROP_CLASS, RULE_MATCH_ENGINE_REGEX}, // - {RULE_PROP_TITLE, RULE_MATCH_ENGINE_REGEX}, // - {RULE_PROP_INITIAL_CLASS, RULE_MATCH_ENGINE_REGEX}, // - {RULE_PROP_INITIAL_TITLE, RULE_MATCH_ENGINE_REGEX}, // - {RULE_PROP_FLOATING, RULE_MATCH_ENGINE_BOOL}, // - {RULE_PROP_TAG, RULE_MATCH_ENGINE_TAG}, // - {RULE_PROP_XWAYLAND, RULE_MATCH_ENGINE_BOOL}, // - {RULE_PROP_FULLSCREEN, RULE_MATCH_ENGINE_BOOL}, // - {RULE_PROP_PINNED, RULE_MATCH_ENGINE_BOOL}, // - {RULE_PROP_FOCUS, RULE_MATCH_ENGINE_BOOL}, // - {RULE_PROP_GROUP, RULE_MATCH_ENGINE_BOOL}, // - {RULE_PROP_MODAL, RULE_MATCH_ENGINE_BOOL}, // - {RULE_PROP_FULLSCREENSTATE_INTERNAL, RULE_MATCH_ENGINE_INT}, // - {RULE_PROP_FULLSCREENSTATE_CLIENT, RULE_MATCH_ENGINE_INT}, // - {RULE_PROP_ON_WORKSPACE, RULE_MATCH_ENGINE_WORKSPACE}, // - {RULE_PROP_CONTENT, RULE_MATCH_ENGINE_INT}, // - {RULE_PROP_XDG_TAG, RULE_MATCH_ENGINE_REGEX}, // - {RULE_PROP_NAMESPACE, RULE_MATCH_ENGINE_REGEX}, // - {RULE_PROP_EXEC_TOKEN, RULE_MATCH_ENGINE_REGEX}, // - {RULE_PROP_EXEC_PID, RULE_MATCH_ENGINE_INT}, // -}; - -const std::vector& Rule::allMatchPropStrings() { - static std::vector strings; - static bool once = true; - if (once) { - for (const auto& [k, v] : MATCH_PROP_STRINGS) { - strings.emplace_back(v); - } - once = false; - } - return strings; -} - -std::optional Rule::matchPropFromString(const std::string_view& s) { - const auto IT = std::ranges::find_if(MATCH_PROP_STRINGS, [&s](const auto& el) { return el.second == s; }); - if (IT == MATCH_PROP_STRINGS.end()) - return std::nullopt; - - return IT->first; -} - -std::optional Rule::matchPropFromString(const std::string& s) { - return matchPropFromString(std::string_view{s}); -} - -IRule::IRule(const std::string& name) : m_name(name) { - ; -} - -void IRule::registerMatch(eRuleProperty p, const std::string& s) { - if (!RULE_ENGINES.contains(p)) { - Log::logger->log(Log::ERR, "BUG THIS: IRule: RULE_ENGINES does not contain rule idx {}", sc>(p)); - return; - } - - switch (RULE_ENGINES.at(p)) { - case RULE_MATCH_ENGINE_REGEX: m_matchEngines[p] = makeUnique(s); break; - case RULE_MATCH_ENGINE_BOOL: m_matchEngines[p] = makeUnique(s); break; - case RULE_MATCH_ENGINE_INT: m_matchEngines[p] = makeUnique(s); break; - case RULE_MATCH_ENGINE_WORKSPACE: m_matchEngines[p] = makeUnique(s); break; - case RULE_MATCH_ENGINE_TAG: m_matchEngines[p] = makeUnique(s); break; - } - - m_mask |= p; -} - -std::underlying_type_t IRule::getPropertiesMask() { - return m_mask; -} - -bool IRule::has(eRuleProperty p) { - return m_matchEngines.contains(p); -} - -bool IRule::matches(eRuleProperty p, const std::string& s) { - if (!has(p)) - return false; - - return m_matchEngines[p]->match(s); -} - -bool IRule::matches(eRuleProperty p, bool b) { - if (!has(p)) - return false; - - return m_matchEngines[p]->match(b); -} - -const std::string& IRule::name() { - return m_name; -} - -void IRule::markAsExecRule(const std::string& token, uint64_t pid, bool persistent) { - m_execData.isExecRule = true; - m_execData.isExecPersistent = persistent; - m_execData.token = token; - m_execData.pid = pid; - m_execData.expiresAt = Time::steadyNow() + std::chrono::minutes(1); -} - -bool IRule::isExecRule() { - return m_execData.isExecRule; -} - -bool IRule::isExecPersistent() { - return m_execData.isExecPersistent; -} - -bool IRule::execExpired() { - return Time::steadyNow() > m_execData.expiresAt; -} - -const std::string& IRule::execToken() { - return m_execData.token; -} diff --git a/src/desktop/rule/Rule.hpp b/src/desktop/rule/Rule.hpp deleted file mode 100644 index efd3cb39..00000000 --- a/src/desktop/rule/Rule.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include "matchEngine/MatchEngine.hpp" - -#include "../../helpers/memory/Memory.hpp" -#include "../../helpers/time/Time.hpp" -#include -#include -#include - -namespace Desktop::Rule { - enum eRuleProperty : uint32_t { - RULE_PROP_NONE = 0, - RULE_PROP_CLASS = (1 << 0), - RULE_PROP_TITLE = (1 << 1), - RULE_PROP_INITIAL_CLASS = (1 << 2), - RULE_PROP_INITIAL_TITLE = (1 << 3), - RULE_PROP_FLOATING = (1 << 4), - RULE_PROP_TAG = (1 << 5), - RULE_PROP_XWAYLAND = (1 << 6), - RULE_PROP_FULLSCREEN = (1 << 7), - RULE_PROP_PINNED = (1 << 8), - RULE_PROP_FOCUS = (1 << 9), - RULE_PROP_GROUP = (1 << 10), - RULE_PROP_MODAL = (1 << 11), - RULE_PROP_FULLSCREENSTATE_INTERNAL = (1 << 12), - RULE_PROP_FULLSCREENSTATE_CLIENT = (1 << 13), - RULE_PROP_ON_WORKSPACE = (1 << 14), - RULE_PROP_CONTENT = (1 << 15), - RULE_PROP_XDG_TAG = (1 << 16), - RULE_PROP_NAMESPACE = (1 << 17), - RULE_PROP_EXEC_TOKEN = (1 << 18), - RULE_PROP_EXEC_PID = (1 << 19), - - RULE_PROP_ALL = std::numeric_limits>::max(), - }; - - enum eRuleType : uint8_t { - RULE_TYPE_WINDOW = 0, - RULE_TYPE_LAYER, - }; - - std::optional matchPropFromString(const std::string& s); - std::optional matchPropFromString(const std::string_view& s); - const std::vector& allMatchPropStrings(); - - class IRule { - public: - virtual ~IRule() = default; - - virtual eRuleType type() = 0; - virtual std::underlying_type_t getPropertiesMask(); - - void registerMatch(eRuleProperty, const std::string&); - void markAsExecRule(const std::string& token, uint64_t pid, bool persistent = false); - bool isExecRule(); - bool isExecPersistent(); - bool execExpired(); - const std::string& execToken(); - - const std::string& name(); - - protected: - IRule(const std::string& name = ""); - - bool matches(eRuleProperty, const std::string& s); - bool matches(eRuleProperty, bool b); - bool has(eRuleProperty); - - // - std::unordered_map> m_matchEngines; - - private: - std::underlying_type_t m_mask = 0; - std::string m_name = ""; - - struct { - bool isExecRule = false; - bool isExecPersistent = false; - std::string token; - uint64_t pid = 0; - Time::steady_tp expiresAt; - } m_execData; - }; -} diff --git a/src/desktop/rule/effect/EffectContainer.hpp b/src/desktop/rule/effect/EffectContainer.hpp deleted file mode 100644 index 51cae07e..00000000 --- a/src/desktop/rule/effect/EffectContainer.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Desktop::Rule { - template - class IEffectContainer { - static_assert(std::is_enum_v); - - protected: - const std::string DEFAULT_MISSING_KEY = ""; - - public: - // Make sure we're using at least a uint16_t for dynamic registrations to not overflow. - // 32k should be enough - using storageType = std::conditional_t<(sizeof(std::underlying_type_t) >= 2), std::underlying_type_t, uint16_t>; - - IEffectContainer(std::vector&& defaultKeys) : m_keys(std::move(defaultKeys)), m_originalSize(m_keys.size()) { - ; - } - virtual ~IEffectContainer() = default; - - virtual storageType registerEffect(std::string&& name) { - if (m_keys.size() >= std::numeric_limits::max()) - return 0; - if (auto it = std::ranges::find(m_keys, name); it != m_keys.end()) - return it - m_keys.begin(); - m_keys.emplace_back(std::move(name)); - return m_keys.size() - 1; - } - - virtual void unregisterEffect(storageType id) { - if (id >= m_keys.size()) - return; - - m_keys[id] = DEFAULT_MISSING_KEY; - } - - virtual void unregisterEffect(const std::string& name) { - for (auto& key : m_keys) { - if (key == name) { - key = DEFAULT_MISSING_KEY; - break; - } - } - } - - virtual const std::string& get(storageType idx) { - if (idx >= m_keys.size()) - return DEFAULT_MISSING_KEY; - - return m_keys[idx]; - } - - virtual std::optional get(const std::string_view& s) { - for (storageType i = 0; i < m_keys.size(); ++i) { - if (m_keys[i] == s) - return i; - } - - return std::nullopt; - } - - virtual const std::vector& allEffectStrings() { - return m_keys; - } - - // whether the effect has been added dynamically as opposed to in the ctor. - virtual bool isEffectDynamic(storageType i) { - return i >= m_originalSize; - } - - protected: - std::vector m_keys; - size_t m_originalSize = 0; - }; -}; diff --git a/src/desktop/rule/layerRule/LayerRule.cpp b/src/desktop/rule/layerRule/LayerRule.cpp deleted file mode 100644 index eccd9914..00000000 --- a/src/desktop/rule/layerRule/LayerRule.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "LayerRule.hpp" -#include "../../../debug/log/Logger.hpp" -#include "../../view/LayerSurface.hpp" - -using namespace Desktop; -using namespace Desktop::Rule; - -CLayerRule::CLayerRule(const std::string& name) : IRule(name) { - ; -} - -eRuleType CLayerRule::type() { - return RULE_TYPE_LAYER; -} - -void CLayerRule::addEffect(CLayerRule::storageType e, const std::string& result) { - m_effects.emplace_back(std::make_pair<>(e, result)); -} - -const std::vector>& CLayerRule::effects() { - return m_effects; -} - -bool CLayerRule::matches(PHLLS ls) { - if (m_matchEngines.empty()) - return false; - - for (const auto& [prop, engine] : m_matchEngines) { - switch (prop) { - default: { - Log::logger->log(Log::TRACE, "CLayerRule::matches: skipping prop entry {}", sc>(prop)); - break; - } - - case RULE_PROP_NAMESPACE: - if (!engine->match(ls->m_namespace)) - return false; - break; - } - } - - return true; -} diff --git a/src/desktop/rule/layerRule/LayerRule.hpp b/src/desktop/rule/layerRule/LayerRule.hpp deleted file mode 100644 index 990796c1..00000000 --- a/src/desktop/rule/layerRule/LayerRule.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "../Rule.hpp" -#include "../../DesktopTypes.hpp" -#include "LayerRuleEffectContainer.hpp" - -namespace Desktop::Rule { - class CLayerRule : public IRule { - public: - using storageType = CLayerRuleEffectContainer::storageType; - - CLayerRule(const std::string& name = ""); - virtual ~CLayerRule() = default; - - virtual eRuleType type(); - - void addEffect(storageType e, const std::string& result); - const std::vector>& effects(); - - bool matches(PHLLS w); - - private: - std::vector> m_effects; - }; -}; diff --git a/src/desktop/rule/layerRule/LayerRuleApplicator.cpp b/src/desktop/rule/layerRule/LayerRuleApplicator.cpp deleted file mode 100644 index fec3a5b2..00000000 --- a/src/desktop/rule/layerRule/LayerRuleApplicator.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include "LayerRuleApplicator.hpp" -#include "LayerRule.hpp" -#include "../Engine.hpp" -#include "../../view/LayerSurface.hpp" -#include "../../types/OverridableVar.hpp" -#include "../../../helpers/MiscFunctions.hpp" -#include "../../../event/EventBus.hpp" - -using namespace Desktop; -using namespace Desktop::Rule; - -CLayerRuleApplicator::CLayerRuleApplicator(PHLLS ls) : m_ls(ls) { - ; -} - -void CLayerRuleApplicator::resetProps(std::underlying_type_t props, Types::eOverridePriority prio) { - // TODO: fucking kill me, is there a better way to do this? - -#define UNSET(x) \ - if (m_##x.second & props) { \ - if (prio == Types::PRIORITY_WINDOW_RULE) \ - m_##x.second &= ~props; \ - m_##x.first.unset(prio); \ - } - - UNSET(noanim) - UNSET(blur) - UNSET(blurPopups) - UNSET(dimAround) - UNSET(xray) - UNSET(noScreenShare) - UNSET(order) - UNSET(aboveLock) - UNSET(ignoreAlpha) - UNSET(animationStyle) - -#undef UNSET - - if (prio == Types::PRIORITY_WINDOW_RULE) - std::erase_if(m_otherProps.props, [props](const auto& el) { return !el.second || el.second->propMask & props; }); -} - -void CLayerRuleApplicator::applyDynamicRule(const SP& rule) { - for (const auto& [key, effect] : rule->effects()) { - switch (key) { - default: { - if (key <= LAYER_RULE_EFFECT_LAST_STATIC) { - Log::logger->log(Log::TRACE, "CLayerRuleApplicator::applyDynamicRule: Skipping effect {}, not dynamic", sc>(key)); - break; - } - - // custom type, add to our vec - if (!m_otherProps.props.contains(key)) { - m_otherProps.props.emplace(key, - makeUnique(SCustomPropContainer{ - .idx = key, - .propMask = rule->getPropertiesMask(), - .effect = effect, - })); - } else { - auto& e = m_otherProps.props[key]; - e->propMask |= rule->getPropertiesMask(); - e->effect = effect; - } - - break; - } - case LAYER_RULE_EFFECT_NONE: { - Log::logger->log(Log::ERR, "CLayerRuleApplicator::applyDynamicRule: BUG THIS: LAYER_RULE_EFFECT_NONE??"); - break; - } - case LAYER_RULE_EFFECT_NO_ANIM: { - m_noanim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noanim.second |= rule->getPropertiesMask(); - break; - } - case LAYER_RULE_EFFECT_BLUR: { - m_blur.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_blur.second |= rule->getPropertiesMask(); - break; - } - case LAYER_RULE_EFFECT_BLUR_POPUPS: { - m_blurPopups.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_blurPopups.second |= rule->getPropertiesMask(); - break; - } - case LAYER_RULE_EFFECT_DIM_AROUND: { - m_dimAround.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_dimAround.second |= rule->getPropertiesMask(); - break; - } - case LAYER_RULE_EFFECT_XRAY: { - m_xray.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_xray.second |= rule->getPropertiesMask(); - break; - } - case LAYER_RULE_EFFECT_NO_SCREEN_SHARE: { - m_noScreenShare.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noScreenShare.second |= rule->getPropertiesMask(); - break; - } - case LAYER_RULE_EFFECT_ORDER: { - try { - m_order.first.set(std::stoi(effect), Types::PRIORITY_WINDOW_RULE); - m_order.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); } - break; - } - case LAYER_RULE_EFFECT_ABOVE_LOCK: { - try { - m_aboveLock.first.set(std::clamp(std::stoull(effect), 0ULL, 2ULL), Types::PRIORITY_WINDOW_RULE); - m_aboveLock.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); } - break; - } - case LAYER_RULE_EFFECT_IGNORE_ALPHA: { - try { - m_ignoreAlpha.first.set(std::clamp(std::stof(effect), 0.F, 1.F), Types::PRIORITY_WINDOW_RULE); - m_ignoreAlpha.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); } - break; - } - case LAYER_RULE_EFFECT_ANIMATION: { - m_animationStyle.first.set(effect, Types::PRIORITY_WINDOW_RULE); - m_animationStyle.second |= rule->getPropertiesMask(); - break; - } - } - } -} - -void CLayerRuleApplicator::propertiesChanged(std::underlying_type_t props) { - if (!m_ls) - return; - - resetProps(props); - - // FIXME: this will not update properties correctly if we implement dynamic rules for - // layers, due to effects overlapping on 0 prop intersection. - // See WindowRule.cpp, and ::propertiesChanged there. - - for (const auto& r : ruleEngine()->rules()) { - if (r->type() != RULE_TYPE_LAYER) - continue; - - if (!(r->getPropertiesMask() & props)) - continue; - - auto wr = reinterpretPointerCast(r); - - if (!wr->matches(m_ls.lock())) - continue; - - applyDynamicRule(wr); - } - - // for plugins - Event::bus()->m_events.layer.updateRules.emit(m_ls.lock()); -} diff --git a/src/desktop/rule/layerRule/LayerRuleApplicator.hpp b/src/desktop/rule/layerRule/LayerRuleApplicator.hpp deleted file mode 100644 index 35aa18c5..00000000 --- a/src/desktop/rule/layerRule/LayerRuleApplicator.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "LayerRuleEffectContainer.hpp" -#include "../../DesktopTypes.hpp" -#include "../Rule.hpp" -#include "../../types/OverridableVar.hpp" -#include "../../../helpers/math/Math.hpp" -#include "../../../config/ConfigDataValues.hpp" - -namespace Desktop::Rule { - class CLayerRule; - - class CLayerRuleApplicator { - public: - CLayerRuleApplicator(PHLLS ls); - ~CLayerRuleApplicator() = default; - - CLayerRuleApplicator(const CLayerRuleApplicator&) = delete; - CLayerRuleApplicator(CLayerRuleApplicator&) = delete; - CLayerRuleApplicator(CLayerRuleApplicator&&) = delete; - - void propertiesChanged(std::underlying_type_t props); - void resetProps(std::underlying_type_t props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE); - - struct SCustomPropContainer { - CLayerRuleEffectContainer::storageType idx = LAYER_RULE_EFFECT_NONE; - std::underlying_type_t propMask = RULE_PROP_NONE; - std::string effect; - }; - - // This struct holds props that were dynamically registered. Plugins may read this. - struct { - std::unordered_map> props; - } m_otherProps; - -#define COMMA , -#define DEFINE_PROP(type, name, def) \ - private: \ - std::pair, std::underlying_type_t> m_##name = {def, RULE_PROP_NONE}; \ - \ - public: \ - Types::COverridableVar& name() { \ - return m_##name.first; \ - } \ - void name##Override(const Types::COverridableVar& other) { \ - m_##name.first = other; \ - } - - // dynamic props - DEFINE_PROP(bool, noanim, false) - DEFINE_PROP(bool, blur, false) - DEFINE_PROP(bool, blurPopups, false) - DEFINE_PROP(bool, dimAround, false) - DEFINE_PROP(bool, xray, false) - DEFINE_PROP(bool, noScreenShare, false) - - DEFINE_PROP(Hyprlang::INT, order, 0) - DEFINE_PROP(Hyprlang::INT, aboveLock, 0) - - DEFINE_PROP(Hyprlang::FLOAT, ignoreAlpha, 0.F) - - DEFINE_PROP(std::string, animationStyle, std::string("")) - -#undef COMMA -#undef DEFINE_PROP - - private: - PHLLSREF m_ls; - - void applyDynamicRule(const SP& rule); - }; -}; diff --git a/src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp b/src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp deleted file mode 100644 index 17394239..00000000 --- a/src/desktop/rule/layerRule/LayerRuleEffectContainer.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "LayerRuleEffectContainer.hpp" - -using namespace Desktop; -using namespace Desktop::Rule; - -// -SP Rule::layerEffects() { - static SP container = makeShared(); - return container; -} - -static const std::vector EFFECT_STRINGS = { - "__internal_none", // - "no_anim", // - "blur", // - "blur_popups", // - "ignore_alpha", // - "dim_around", // - "xray", // - "animation", // - "order", // - "above_lock", // - "no_screen_share", // - "__internal_last_static", // -}; - -// This is here so that if we change the rules, we get reminded to update -// the strings. -static_assert(LAYER_RULE_EFFECT_LAST_STATIC == 11); - -CLayerRuleEffectContainer::CLayerRuleEffectContainer() : IEffectContainer(std::vector{EFFECT_STRINGS}) { - ; -} diff --git a/src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp b/src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp deleted file mode 100644 index e3b3d26c..00000000 --- a/src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "../effect/EffectContainer.hpp" -#include "../../../helpers/memory/Memory.hpp" - -#pragma once - -namespace Desktop::Rule { - enum eLayerRuleEffect : uint8_t { - LAYER_RULE_EFFECT_NONE = 0, - - LAYER_RULE_EFFECT_NO_ANIM, - LAYER_RULE_EFFECT_BLUR, - LAYER_RULE_EFFECT_BLUR_POPUPS, - LAYER_RULE_EFFECT_IGNORE_ALPHA, - LAYER_RULE_EFFECT_DIM_AROUND, - LAYER_RULE_EFFECT_XRAY, - LAYER_RULE_EFFECT_ANIMATION, - LAYER_RULE_EFFECT_ORDER, - LAYER_RULE_EFFECT_ABOVE_LOCK, - LAYER_RULE_EFFECT_NO_SCREEN_SHARE, - - LAYER_RULE_EFFECT_LAST_STATIC, - }; - - class CLayerRuleEffectContainer : public IEffectContainer { - public: - CLayerRuleEffectContainer(); - virtual ~CLayerRuleEffectContainer() = default; - }; - - SP layerEffects(); -}; \ No newline at end of file diff --git a/src/desktop/rule/matchEngine/BoolMatchEngine.cpp b/src/desktop/rule/matchEngine/BoolMatchEngine.cpp deleted file mode 100644 index f5c47227..00000000 --- a/src/desktop/rule/matchEngine/BoolMatchEngine.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "BoolMatchEngine.hpp" -#include "../../../helpers/MiscFunctions.hpp" - -using namespace Desktop::Rule; - -CBoolMatchEngine::CBoolMatchEngine(const std::string& s) : m_value(truthy(s)) { - ; -} - -bool CBoolMatchEngine::match(bool other) { - return other == m_value; -} diff --git a/src/desktop/rule/matchEngine/BoolMatchEngine.hpp b/src/desktop/rule/matchEngine/BoolMatchEngine.hpp deleted file mode 100644 index bd162cda..00000000 --- a/src/desktop/rule/matchEngine/BoolMatchEngine.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "MatchEngine.hpp" - -namespace Desktop::Rule { - class CBoolMatchEngine : public IMatchEngine { - public: - CBoolMatchEngine(const std::string&); - virtual ~CBoolMatchEngine() = default; - - virtual bool match(bool other); - - private: - bool m_value = false; - }; -} \ No newline at end of file diff --git a/src/desktop/rule/matchEngine/IntMatchEngine.cpp b/src/desktop/rule/matchEngine/IntMatchEngine.cpp deleted file mode 100644 index c8f3c09e..00000000 --- a/src/desktop/rule/matchEngine/IntMatchEngine.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "IntMatchEngine.hpp" -#include "../../../debug/log/Logger.hpp" - -using namespace Desktop::Rule; - -CIntMatchEngine::CIntMatchEngine(const std::string& s) { - try { - m_value = std::stoi(s); - } catch (...) { Log::logger->log(Log::ERR, "CIntMatchEngine: invalid input {}", s); } -} - -bool CIntMatchEngine::match(int other) { - return m_value == other; -} diff --git a/src/desktop/rule/matchEngine/IntMatchEngine.hpp b/src/desktop/rule/matchEngine/IntMatchEngine.hpp deleted file mode 100644 index 2eda492c..00000000 --- a/src/desktop/rule/matchEngine/IntMatchEngine.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "MatchEngine.hpp" - -namespace Desktop::Rule { - class CIntMatchEngine : public IMatchEngine { - public: - CIntMatchEngine(const std::string&); - virtual ~CIntMatchEngine() = default; - - virtual bool match(int other); - - private: - int m_value = 0; - }; -} \ No newline at end of file diff --git a/src/desktop/rule/matchEngine/MatchEngine.cpp b/src/desktop/rule/matchEngine/MatchEngine.cpp deleted file mode 100644 index 0bc89d7f..00000000 --- a/src/desktop/rule/matchEngine/MatchEngine.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "MatchEngine.hpp" - -using namespace Desktop::Rule; - -bool IMatchEngine::match(const std::string&) { - return false; -} - -bool IMatchEngine::match(bool) { - return false; -} - -bool IMatchEngine::match(int) { - return false; -} - -bool IMatchEngine::match(PHLWORKSPACE) { - return false; -} - -bool IMatchEngine::match(const CTagKeeper& keeper) { - return false; -} diff --git a/src/desktop/rule/matchEngine/MatchEngine.hpp b/src/desktop/rule/matchEngine/MatchEngine.hpp deleted file mode 100644 index 9588ac05..00000000 --- a/src/desktop/rule/matchEngine/MatchEngine.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "../../DesktopTypes.hpp" - -class CTagKeeper; - -namespace Desktop::Rule { - enum eRuleMatchEngine : uint8_t { - RULE_MATCH_ENGINE_REGEX = 0, - RULE_MATCH_ENGINE_BOOL, - RULE_MATCH_ENGINE_INT, - RULE_MATCH_ENGINE_WORKSPACE, - RULE_MATCH_ENGINE_TAG, - }; - - class IMatchEngine { - public: - virtual ~IMatchEngine() = default; - virtual bool match(const std::string&); - virtual bool match(bool); - virtual bool match(int); - virtual bool match(PHLWORKSPACE); - virtual bool match(const CTagKeeper& keeper); - - protected: - IMatchEngine() = default; - }; -}; \ No newline at end of file diff --git a/src/desktop/rule/matchEngine/RegexMatchEngine.cpp b/src/desktop/rule/matchEngine/RegexMatchEngine.cpp deleted file mode 100644 index 14e30af1..00000000 --- a/src/desktop/rule/matchEngine/RegexMatchEngine.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "RegexMatchEngine.hpp" -#include - -using namespace Desktop::Rule; - -CRegexMatchEngine::CRegexMatchEngine(const std::string& regex) { - if (regex.starts_with("negative:")) { - m_negative = true; - m_regex = makeUnique(regex.substr(9)); - return; - } - m_regex = makeUnique(regex); -} - -bool CRegexMatchEngine::match(const std::string& other) { - return re2::RE2::FullMatch(other, *m_regex) != m_negative; -} \ No newline at end of file diff --git a/src/desktop/rule/matchEngine/RegexMatchEngine.hpp b/src/desktop/rule/matchEngine/RegexMatchEngine.hpp deleted file mode 100644 index e980ce70..00000000 --- a/src/desktop/rule/matchEngine/RegexMatchEngine.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "MatchEngine.hpp" -#include "../../../helpers/memory/Memory.hpp" - -//NOLINTNEXTLINE -namespace re2 { - class RE2; -}; - -namespace Desktop::Rule { - class CRegexMatchEngine : public IMatchEngine { - public: - CRegexMatchEngine(const std::string& regex); - virtual ~CRegexMatchEngine() = default; - - virtual bool match(const std::string& other); - - private: - UP m_regex; - bool m_negative = false; - }; -} \ No newline at end of file diff --git a/src/desktop/rule/matchEngine/TagMatchEngine.cpp b/src/desktop/rule/matchEngine/TagMatchEngine.cpp deleted file mode 100644 index 6b38c980..00000000 --- a/src/desktop/rule/matchEngine/TagMatchEngine.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "TagMatchEngine.hpp" -#include "../../../helpers/TagKeeper.hpp" -#include - -using namespace Desktop::Rule; - -CTagMatchEngine::CTagMatchEngine(const std::string& tag) : m_tag(tag) { - ; -} - -bool CTagMatchEngine::match(const CTagKeeper& keeper) { - return keeper.isTagged(m_tag); -} diff --git a/src/desktop/rule/matchEngine/TagMatchEngine.hpp b/src/desktop/rule/matchEngine/TagMatchEngine.hpp deleted file mode 100644 index e5e65c9b..00000000 --- a/src/desktop/rule/matchEngine/TagMatchEngine.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "MatchEngine.hpp" -#include - -namespace Desktop::Rule { - class CTagMatchEngine : public IMatchEngine { - public: - CTagMatchEngine(const std::string& tag); - virtual ~CTagMatchEngine() = default; - - virtual bool match(const CTagKeeper& keeper); - - private: - std::string m_tag; - }; -} diff --git a/src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp b/src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp deleted file mode 100644 index fea5c384..00000000 --- a/src/desktop/rule/matchEngine/WorkspaceMatchEngine.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "WorkspaceMatchEngine.hpp" -#include "../../Workspace.hpp" - -using namespace Desktop::Rule; - -CWorkspaceMatchEngine::CWorkspaceMatchEngine(const std::string& s) : m_value(s) { - ; -} - -bool CWorkspaceMatchEngine::match(PHLWORKSPACE ws) { - return ws && ws->matchesStaticSelector(m_value); -} diff --git a/src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp b/src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp deleted file mode 100644 index dcdf4136..00000000 --- a/src/desktop/rule/matchEngine/WorkspaceMatchEngine.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "MatchEngine.hpp" -#include - -namespace Desktop::Rule { - class CWorkspaceMatchEngine : public IMatchEngine { - public: - CWorkspaceMatchEngine(const std::string&); - virtual ~CWorkspaceMatchEngine() = default; - - virtual bool match(PHLWORKSPACE ws); - - private: - std::string m_value = ""; - }; -} diff --git a/src/desktop/rule/utils/SetUtils.hpp b/src/desktop/rule/utils/SetUtils.hpp deleted file mode 100644 index 75fd4739..00000000 --- a/src/desktop/rule/utils/SetUtils.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -namespace Desktop::Rule { - template - bool setsIntersect(const std::unordered_set& A, const std::unordered_set& B) { - if (A.size() > B.size()) - return setsIntersect(B, A); - - for (const auto& e : A) { - if (B.contains(e)) - return true; - } - return false; - } -}; \ No newline at end of file diff --git a/src/desktop/rule/windowRule/WindowRule.cpp b/src/desktop/rule/windowRule/WindowRule.cpp deleted file mode 100644 index 8028e482..00000000 --- a/src/desktop/rule/windowRule/WindowRule.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "WindowRule.hpp" -#include "../../view/Window.hpp" -#include "../../../helpers/Monitor.hpp" -#include "../../../Compositor.hpp" -#include "../../../managers/TokenManager.hpp" -#include "../../../desktop/state/FocusState.hpp" - -using namespace Desktop; -using namespace Desktop::Rule; - -CWindowRule::CWindowRule(const std::string& name) : IRule(name) { - ; -} - -eRuleType CWindowRule::type() { - return RULE_TYPE_WINDOW; -} - -void CWindowRule::addEffect(CWindowRule::storageType e, const std::string& result) { - m_effects.emplace_back(std::make_pair<>(e, result)); - m_effectSet.emplace(e); -} - -const std::vector>& CWindowRule::effects() { - return m_effects; -} - -bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) { - if (m_matchEngines.empty()) - return false; - - for (const auto& [prop, engine] : m_matchEngines) { - switch (prop) { - default: { - Log::logger->log(Log::TRACE, "CWindowRule::matches: skipping prop entry {}", sc>(prop)); - break; - } - - case RULE_PROP_TITLE: - if (!engine->match(w->m_title)) - return false; - break; - case RULE_PROP_INITIAL_TITLE: - if (!engine->match(w->m_initialTitle)) - return false; - break; - case RULE_PROP_CLASS: - if (!engine->match(w->m_class)) - return false; - break; - case RULE_PROP_INITIAL_CLASS: - if (!engine->match(w->m_initialClass)) - return false; - break; - case RULE_PROP_FLOATING: - if (!engine->match(w->m_isFloating)) - return false; - break; - case RULE_PROP_TAG: - if (!engine->match(w->m_ruleApplicator->m_tagKeeper)) - return false; - break; - case RULE_PROP_XWAYLAND: - if (!engine->match(w->m_isX11)) - return false; - break; - case RULE_PROP_FULLSCREEN: - if (!engine->match(w->m_fullscreenState.internal != 0)) - return false; - break; - case RULE_PROP_PINNED: - if (!engine->match(w->m_pinned)) - return false; - break; - case RULE_PROP_FOCUS: - if (!engine->match(Desktop::focusState()->window() == w)) - return false; - break; - case RULE_PROP_GROUP: - if (!engine->match(!!w->m_group)) - return false; - break; - case RULE_PROP_MODAL: - if (!engine->match(w->isModal())) - return false; - break; - case RULE_PROP_FULLSCREENSTATE_INTERNAL: - if (!engine->match(w->m_fullscreenState.internal)) - return false; - break; - case RULE_PROP_FULLSCREENSTATE_CLIENT: - if (!engine->match(w->m_fullscreenState.client)) - return false; - break; - case RULE_PROP_ON_WORKSPACE: - if (!engine->match(w->m_workspace)) - return false; - break; - case RULE_PROP_CONTENT: - if (!engine->match(w->getContentType()) && !engine->match(NContentType::toString(w->getContentType()))) - return false; - break; - case RULE_PROP_XDG_TAG: - if (!w->xdgTag().has_value() || !engine->match(*w->xdgTag())) - return false; - break; - - case RULE_PROP_EXEC_TOKEN: - if (!allowEnvLookup) - break; - - const auto ENV = w->getEnv(); - bool match = false; - - if (ENV.contains(EXEC_RULE_ENV_NAME)) { - if (engine->match(ENV.at(EXEC_RULE_ENV_NAME))) - match = true; - } else if (m_matchEngines.contains(RULE_PROP_EXEC_PID)) { - if (m_matchEngines.at(RULE_PROP_EXEC_PID)->match(w->getPID())) - match = true; - } - if (!match) - return false; - break; - } - } - - return true; -} - -SP CWindowRule::buildFromExecString(std::string&& s) { - CVarList2 varlist(std::move(s), 0, ';'); - SP wr = makeShared("__exec_rule"); - - for (const auto& el : varlist) { - // split element by space, can't do better - size_t spacePos = el.find(' '); - if (spacePos != std::string::npos) { - // great, split and try to parse - auto LHS = el.substr(0, spacePos); - const auto EFFECT = windowEffects()->get(LHS); - - if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE) - continue; // invalid... - - wr->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)}); - continue; - } - - // assume 1 maybe... - - const auto EFFECT = windowEffects()->get(el); - - if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE) - continue; // invalid... - - wr->addEffect(*EFFECT, std::string{"1"}); - } - - return wr; -} - -const std::unordered_set& CWindowRule::effectsSet() { - return m_effectSet; -} diff --git a/src/desktop/rule/windowRule/WindowRule.hpp b/src/desktop/rule/windowRule/WindowRule.hpp deleted file mode 100644 index f7621828..00000000 --- a/src/desktop/rule/windowRule/WindowRule.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "../Rule.hpp" -#include "../../DesktopTypes.hpp" -#include "WindowRuleEffectContainer.hpp" -#include "../../../helpers/math/Math.hpp" - -#include - -namespace Desktop::Rule { - constexpr const char* EXEC_RULE_ENV_NAME = "HL_EXEC_RULE_TOKEN"; - - class CWindowRule : public IRule { - private: - using storageType = CWindowRuleEffectContainer::storageType; - - public: - CWindowRule(const std::string& name = ""); - virtual ~CWindowRule() = default; - - static SP buildFromExecString(std::string&&); - - virtual eRuleType type(); - - void addEffect(storageType e, const std::string& result); - const std::vector>& effects(); - const std::unordered_set& effectsSet(); - - bool matches(PHLWINDOW w, bool allowEnvLookup = false); - - private: - std::vector> m_effects; - std::unordered_set m_effectSet; - }; -}; diff --git a/src/desktop/rule/windowRule/WindowRuleApplicator.cpp b/src/desktop/rule/windowRule/WindowRuleApplicator.cpp deleted file mode 100644 index 07cb5f64..00000000 --- a/src/desktop/rule/windowRule/WindowRuleApplicator.cpp +++ /dev/null @@ -1,642 +0,0 @@ -#include "WindowRuleApplicator.hpp" -#include "WindowRule.hpp" -#include "../Engine.hpp" -#include "../utils/SetUtils.hpp" -#include "../../view/Window.hpp" -#include "../../types/OverridableVar.hpp" -#include "../../../event/EventBus.hpp" - -#include - -using namespace Hyprutils::String; - -using namespace Desktop; -using namespace Desktop::Rule; - -CWindowRuleApplicator::CWindowRuleApplicator(PHLWINDOW w) : m_window(w) { - ; -} - -std::unordered_set CWindowRuleApplicator::resetProps(std::underlying_type_t props, Types::eOverridePriority prio) { - // TODO: fucking kill me, is there a better way to do this? - - std::unordered_set effectsNuked; - -#define UNSET(x) \ - if (m_##x.second & props) { \ - if (prio == Types::PRIORITY_WINDOW_RULE) { \ - effectsNuked.emplace(x##Effect()); \ - m_##x.second &= ~props; \ - } \ - m_##x.first.unset(prio); \ - } - - UNSET(alpha) - UNSET(alphaInactive) - UNSET(alphaFullscreen) - UNSET(allowsInput) - UNSET(decorate) - UNSET(focusOnActivate) - UNSET(keepAspectRatio) - UNSET(nearestNeighbor) - UNSET(noAnim) - UNSET(noBlur) - UNSET(noDim) - UNSET(noFocus) - UNSET(noMaxSize) - UNSET(noShadow) - UNSET(noShortcutsInhibit) - UNSET(opaque) - UNSET(dimAround) - UNSET(RGBX) - UNSET(syncFullscreen) - UNSET(tearing) - UNSET(xray) - UNSET(renderUnfocused) - UNSET(noFollowMouse) - UNSET(noScreenShare) - UNSET(noVRR) - UNSET(persistentSize) - UNSET(stayFocused) - UNSET(idleInhibitMode) - UNSET(borderSize) - UNSET(rounding) - UNSET(roundingPower) - UNSET(scrollMouse) - UNSET(scrollTouchpad) - UNSET(animationStyle) - UNSET(maxSize) - UNSET(minSize) - UNSET(activeBorderColor) - UNSET(inactiveBorderColor) - -#undef UNSET - - if (prio == Types::PRIORITY_WINDOW_RULE) { - std::erase_if(m_dynamicTags, [props, this](const auto& el) { - const bool REMOVE = el.second & props; - - if (REMOVE) - m_tagKeeper.removeDynamicTag(el.first); - - return REMOVE; - }); - - std::erase_if(m_otherProps.props, [props](const auto& el) { return !el.second || el.second->propMask & props; }); - } - - return effectsNuked; -} - -CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const SP& rule) { - SRuleResult result; - - for (const auto& [key, effect] : rule->effects()) { - switch (key) { - default: { - if (key <= WINDOW_RULE_EFFECT_LAST_STATIC) { - Log::logger->log(Log::TRACE, "CWindowRuleApplicator::applyDynamicRule: Skipping effect {}, not dynamic", sc>(key)); - break; - } - - // custom type, add to our vec - if (!m_otherProps.props.contains(key)) { - m_otherProps.props.emplace(key, - makeUnique(SCustomPropContainer{ - .idx = key, - .propMask = rule->getPropertiesMask(), - .effect = effect, - })); - } else { - auto& e = m_otherProps.props[key]; - e->propMask |= rule->getPropertiesMask(); - e->effect = effect; - } - - break; - } - - case WINDOW_RULE_EFFECT_NONE: { - Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: BUG THIS: WINDOW_RULE_EFFECT_NONE??"); - break; - } - case WINDOW_RULE_EFFECT_ROUNDING: { - try { - m_rounding.first.set(std::stoull(effect), Types::PRIORITY_WINDOW_RULE); - m_rounding.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding {}", effect); } - break; - } - case WINDOW_RULE_EFFECT_ROUNDING_POWER: { - try { - m_roundingPower.first.set(std::clamp(std::stof(effect), 1.F, 10.F), Types::PRIORITY_WINDOW_RULE); - m_roundingPower.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding_power {}", effect); } - break; - } - case WINDOW_RULE_EFFECT_PERSISTENT_SIZE: { - m_persistentSize.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_persistentSize.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_ANIMATION: { - m_animationStyle.first.set(effect, Types::PRIORITY_WINDOW_RULE); - m_animationStyle.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_BORDER_COLOR: { - try { - // Each vector will only get used if it has at least one color - CGradientValueData activeBorderGradient = {}; - CGradientValueData inactiveBorderGradient = {}; - bool active = true; - CVarList colorsAndAngles = CVarList(trim(effect), 0, 's', true); - - // Basic form has only two colors, everything else can be parsed as a gradient - if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) { - m_activeBorderColor.first = - Types::COverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[0]).value_or(0))), Types::PRIORITY_WINDOW_RULE); - m_inactiveBorderColor.first = - Types::COverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[1]).value_or(0))), Types::PRIORITY_WINDOW_RULE); - m_activeBorderColor.second |= rule->getPropertiesMask(); - m_inactiveBorderColor.second |= rule->getPropertiesMask(); - break; - } - - for (auto const& token : colorsAndAngles) { - // The first angle, or an explicit "0deg", splits the two gradients - if (active && token.contains("deg")) { - activeBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); - active = false; - } else if (token.contains("deg")) - inactiveBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); - else if (active) - activeBorderGradient.m_colors.emplace_back(configStringToInt(token).value_or(0)); - else - inactiveBorderGradient.m_colors.emplace_back(configStringToInt(token).value_or(0)); - } - - activeBorderGradient.updateColorsOk(); - - // Includes sanity checks for the number of colors in each gradient - if (activeBorderGradient.m_colors.size() > 10 || inactiveBorderGradient.m_colors.size() > 10) - Log::logger->log(Log::WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", effect); - else if (activeBorderGradient.m_colors.empty()) - Log::logger->log(Log::WARN, "Bordercolor rule \"{}\" has no colors, ignoring", effect); - else if (inactiveBorderGradient.m_colors.empty()) - m_activeBorderColor.first = Types::COverridableVar(activeBorderGradient, Types::PRIORITY_WINDOW_RULE); - else { - m_activeBorderColor.first = Types::COverridableVar(activeBorderGradient, Types::PRIORITY_WINDOW_RULE); - m_inactiveBorderColor.first = Types::COverridableVar(inactiveBorderGradient, Types::PRIORITY_WINDOW_RULE); - } - } catch (std::exception& e) { Log::logger->log(Log::ERR, "BorderColor rule \"{}\" failed with: {}", effect, e.what()); } - m_activeBorderColor.second = rule->getPropertiesMask(); - m_inactiveBorderColor.second = rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_IDLE_INHIBIT: { - if (effect == "none") - m_idleInhibitMode.first.set(IDLEINHIBIT_NONE, Types::PRIORITY_WINDOW_RULE); - else if (effect == "always") - m_idleInhibitMode.first.set(IDLEINHIBIT_ALWAYS, Types::PRIORITY_WINDOW_RULE); - else if (effect == "focus") - m_idleInhibitMode.first.set(IDLEINHIBIT_FOCUS, Types::PRIORITY_WINDOW_RULE); - else if (effect == "fullscreen") - m_idleInhibitMode.first.set(IDLEINHIBIT_FULLSCREEN, Types::PRIORITY_WINDOW_RULE); - else - Log::logger->log(Log::ERR, "Rule idleinhibit: unknown mode {}", effect); - m_idleInhibitMode.second = rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_OPACITY: { - try { - CVarList2 vars(std::string{effect}, 0, ' '); - - int opacityIDX = 0; - - for (const auto& r : vars) { - if (r == "opacity") - continue; - - if (r == "override") { - if (opacityIDX == 1) - m_alpha.first = Types::COverridableVar(Types::SAlphaValue{.alpha = m_alpha.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE); - else if (opacityIDX == 2) - m_alphaInactive.first = - Types::COverridableVar(Types::SAlphaValue{.alpha = m_alphaInactive.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE); - else if (opacityIDX == 3) - m_alphaFullscreen.first = - Types::COverridableVar(Types::SAlphaValue{.alpha = m_alphaFullscreen.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE); - } else { - if (opacityIDX == 0) - m_alpha.first = Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE); - else if (opacityIDX == 1) - m_alphaInactive.first = - Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE); - else if (opacityIDX == 2) - m_alphaFullscreen.first = - Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE); - else - throw std::runtime_error("more than 3 alpha values"); - - opacityIDX++; - } - } - - if (opacityIDX == 1) { - m_alphaInactive.first = m_alpha.first; - m_alphaFullscreen.first = m_alpha.first; - } - } catch (std::exception& e) { Log::logger->log(Log::ERR, "Opacity rule \"{}\" failed with: {}", effect, e.what()); } - m_alpha.second = rule->getPropertiesMask(); - m_alphaInactive.second = rule->getPropertiesMask(); - m_alphaFullscreen.second = rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_TAG: { - m_dynamicTags.emplace_back(std::make_pair<>(effect, rule->getPropertiesMask())); - m_tagKeeper.applyTag(effect, true); - result.tagsChanged = true; - break; - } - case WINDOW_RULE_EFFECT_MAX_SIZE: { - try { - static auto PCLAMP_TILED = CConfigValue("misc:size_limits_tiled"); - - if (!m_window) - break; - - const auto VEC = m_window->calculateExpression(effect); - if (!VEC) { - Log::logger->log(Log::ERR, "failed to parse {} as an expression", effect); - break; - } - if (VEC->x < 1 || VEC->y < 1) { - Log::logger->log(Log::ERR, "Invalid size for maxsize"); - break; - } - - m_maxSize.first = Types::COverridableVar(*VEC, Types::PRIORITY_WINDOW_RULE); - - if (*PCLAMP_TILED || m_window->m_isFloating) - m_window->clampWindowSize(std::nullopt, m_maxSize.first.value()); - } catch (std::exception& e) { Log::logger->log(Log::ERR, "maxsize rule \"{}\" failed with: {}", effect, e.what()); } - m_maxSize.second = rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_MIN_SIZE: { - try { - static auto PCLAMP_TILED = CConfigValue("misc:size_limits_tiled"); - - if (!m_window) - break; - - const auto VEC = m_window->calculateExpression(effect); - if (!VEC) { - Log::logger->log(Log::ERR, "failed to parse {} as an expression", effect); - break; - } - - if (VEC->x < 1 || VEC->y < 1) { - Log::logger->log(Log::ERR, "Invalid size for maxsize"); - break; - } - - m_minSize.first = Types::COverridableVar(*VEC, Types::PRIORITY_WINDOW_RULE); - if (*PCLAMP_TILED || m_window->m_isFloating) - m_window->clampWindowSize(m_minSize.first.value(), std::nullopt); - } catch (std::exception& e) { Log::logger->log(Log::ERR, "minsize rule \"{}\" failed with: {}", effect, e.what()); } - m_minSize.second = rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_BORDER_SIZE: { - try { - auto oldBorderSize = m_borderSize.first.valueOrDefault(); - m_borderSize.first.set(std::stoi(effect), Types::PRIORITY_WINDOW_RULE); - m_borderSize.second |= rule->getPropertiesMask(); - if (oldBorderSize != m_borderSize.first.valueOrDefault()) - result.needsRelayout = true; - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid border_size {}", effect); } - break; - } - case WINDOW_RULE_EFFECT_ALLOWS_INPUT: { - m_allowsInput.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_allowsInput.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_DIM_AROUND: { - m_dimAround.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_dimAround.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_DECORATE: { - m_decorate.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_decorate.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE: { - m_focusOnActivate.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_focusOnActivate.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO: { - m_keepAspectRatio.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_keepAspectRatio.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR: { - m_nearestNeighbor.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_nearestNeighbor.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_ANIM: { - m_noAnim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noAnim.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_BLUR: { - m_noBlur.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noBlur.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_DIM: { - m_noDim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noDim.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_FOCUS: { - m_noFocus.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noFocus.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE: { - m_noFollowMouse.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noFollowMouse.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_MAX_SIZE: { - m_noMaxSize.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noMaxSize.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_SHADOW: { - m_noShadow.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noShadow.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT: { - m_noShortcutsInhibit.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noShortcutsInhibit.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_OPAQUE: { - m_opaque.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_opaque.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_FORCE_RGBX: { - m_RGBX.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_RGBX.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_SYNC_FULLSCREEN: { - m_syncFullscreen.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_syncFullscreen.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_IMMEDIATE: { - m_tearing.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_tearing.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_XRAY: { - m_xray.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_xray.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_RENDER_UNFOCUSED: { - m_renderUnfocused.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_renderUnfocused.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_SCREEN_SHARE: { - m_noScreenShare.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noScreenShare.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_NO_VRR: { - m_noVRR.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_noVRR.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_STAY_FOCUSED: { - m_stayFocused.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE); - m_stayFocused.second |= rule->getPropertiesMask(); - break; - } - case WINDOW_RULE_EFFECT_SCROLL_MOUSE: { - try { - m_scrollMouse.first.set(std::clamp(std::stof(effect), 0.01F, 10.F), Types::PRIORITY_WINDOW_RULE); - m_scrollMouse.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid scroll_mouse {}", effect); } - break; - } - case WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD: { - try { - m_scrollTouchpad.first.set(std::clamp(std::stof(effect), 0.01F, 10.F), Types::PRIORITY_WINDOW_RULE); - m_scrollTouchpad.second |= rule->getPropertiesMask(); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyDynamicRule: invalid scroll_touchpad {}", effect); } - break; - } - } - } - return result; -} - -CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyStaticRule(const SP& rule) { - for (const auto& [key, effect] : rule->effects()) { - switch (key) { - default: { - Log::logger->log(Log::TRACE, "CWindowRuleApplicator::applyStaticRule: Skipping effect {}, not static", sc>(key)); - break; - } - - case WINDOW_RULE_EFFECT_FLOAT: { - static_.floating = truthy(effect); - break; - } - case WINDOW_RULE_EFFECT_TILE: { - static_.floating = !truthy(effect); - break; - } - case WINDOW_RULE_EFFECT_FULLSCREEN: { - static_.fullscreen = truthy(effect); - break; - } - case WINDOW_RULE_EFFECT_MAXIMIZE: { - static_.maximize = truthy(effect); - break; - } - case WINDOW_RULE_EFFECT_FULLSCREENSTATE: { - CVarList2 vars(std::string{effect}, 0, 's'); - try { - static_.fullscreenStateInternal = std::stoi(std::string{vars[0]}); - if (!vars[1].empty()) - static_.fullscreenStateClient = std::stoi(std::string{vars[1]}); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyStaticRule: invalid fullscreen state {}", effect); } - break; - } - case WINDOW_RULE_EFFECT_MOVE: { - static_.center = std::nullopt; - static_.position = effect; - break; - } - case WINDOW_RULE_EFFECT_SIZE: { - static_.size = effect; - break; - } - case WINDOW_RULE_EFFECT_CENTER: { - static_.position.clear(); - static_.center = truthy(effect); - break; - } - case WINDOW_RULE_EFFECT_PSEUDO: { - static_.pseudo = truthy(effect); - break; - } - case WINDOW_RULE_EFFECT_MONITOR: { - static_.monitor = effect; - break; - } - case WINDOW_RULE_EFFECT_WORKSPACE: { - static_.workspace = effect; - break; - } - case WINDOW_RULE_EFFECT_NOINITIALFOCUS: { - static_.noInitialFocus = truthy(effect); - break; - } - case WINDOW_RULE_EFFECT_PIN: { - static_.pin = truthy(effect); - break; - } - case WINDOW_RULE_EFFECT_GROUP: { - static_.group = effect; - break; - } - case WINDOW_RULE_EFFECT_SUPPRESSEVENT: { - CVarList2 varlist(std::string{effect}, 0, 's'); - for (const auto& e : varlist) { - static_.suppressEvent.emplace_back(e); - } - break; - } - case WINDOW_RULE_EFFECT_CONTENT: { - static_.content = NContentType::fromString(effect); - break; - } - case WINDOW_RULE_EFFECT_NOCLOSEFOR: { - try { - static_.noCloseFor = std::stoi(effect); - } catch (...) { Log::logger->log(Log::ERR, "CWindowRuleApplicator::applyStaticRule: invalid no close for {}", effect); } - break; - } - } - } - - return SRuleResult{}; -} - -void CWindowRuleApplicator::readStaticRules(bool preRead) { - if (!m_window) - return; - - static_ = {}; - - std::vector> execRules; - bool tagsWereChanged = false; - - for (const auto& r : ruleEngine()->rules()) { - if (r->type() != RULE_TYPE_WINDOW) - continue; - - auto wr = reinterpretPointerCast(r); - - if (!wr->matches(m_window.lock(), true)) - continue; - - if (wr->isExecRule()) { - execRules.emplace_back(wr); - continue; - } - - applyStaticRule(wr); - const auto RES = applyDynamicRule(wr); - tagsWereChanged = tagsWereChanged || RES.tagsChanged; - } - - // recheck some props people might wanna use for static rules. - std::underlying_type_t propsToRecheck = RULE_PROP_NONE; - if (tagsWereChanged) - propsToRecheck |= RULE_PROP_TAG; - if (static_.content != NContentType::CONTENT_TYPE_NONE) - propsToRecheck |= RULE_PROP_CONTENT; - - if (propsToRecheck != RULE_PROP_NONE) { - for (const auto& r : ruleEngine()->rules()) { - if (r->type() != RULE_TYPE_WINDOW) - continue; - - if (!(r->getPropertiesMask() & propsToRecheck)) - continue; - - auto wr = reinterpretPointerCast(r); - - if (!wr->matches(m_window.lock(), true)) - continue; - - applyStaticRule(wr); - } - } - - for (const auto& wr : execRules) { - applyStaticRule(wr); - applyDynamicRule(wr); - if (!preRead) - ruleEngine()->unregisterRule(wr); - } -} - -void CWindowRuleApplicator::propertiesChanged(std::underlying_type_t props) { - if (!m_window || !m_window->m_isMapped || m_window->isHidden()) - return; - - bool needsRelayout = false; - std::unordered_set effectsNeedingRecheck = resetProps(props); - - for (const auto& r : ruleEngine()->rules()) { - if (r->type() != RULE_TYPE_WINDOW) - continue; - - const auto WR = reinterpretPointerCast(r); - - if (!(WR->getPropertiesMask() & props) && !setsIntersect(WR->effectsSet(), effectsNeedingRecheck)) - continue; - - if (!WR->matches(m_window.lock())) - continue; - - const auto RES = applyDynamicRule(WR); - needsRelayout = needsRelayout || RES.needsRelayout; - } - - m_window->updateWindowData(); - m_window->updateWindowDecos(); - m_window->updateDecorationValues(); - - if (needsRelayout) - g_pDecorationPositioner->forceRecalcFor(m_window.lock()); - - // for plugins - Event::bus()->m_events.window.updateRules.emit(m_window.lock()); -} diff --git a/src/desktop/rule/windowRule/WindowRuleApplicator.hpp b/src/desktop/rule/windowRule/WindowRuleApplicator.hpp deleted file mode 100644 index 5c1d4fd1..00000000 --- a/src/desktop/rule/windowRule/WindowRuleApplicator.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once - -#include -#include - -#include "WindowRuleEffectContainer.hpp" -#include "../../DesktopTypes.hpp" -#include "../Rule.hpp" -#include "../../types/OverridableVar.hpp" -#include "../../../helpers/math/Math.hpp" -#include "../../../helpers/TagKeeper.hpp" -#include "../../../config/ConfigDataValues.hpp" - -namespace Desktop::Rule { - class CWindowRule; - - enum eIdleInhibitMode : uint8_t { - IDLEINHIBIT_NONE = 0, - IDLEINHIBIT_ALWAYS, - IDLEINHIBIT_FULLSCREEN, - IDLEINHIBIT_FOCUS - }; - - class CWindowRuleApplicator { - public: - CWindowRuleApplicator(PHLWINDOW w); - ~CWindowRuleApplicator() = default; - - CWindowRuleApplicator(const CWindowRuleApplicator&) = delete; - CWindowRuleApplicator(CWindowRuleApplicator&) = delete; - CWindowRuleApplicator(CWindowRuleApplicator&&) = delete; - - void propertiesChanged(std::underlying_type_t props); - std::unordered_set resetProps(std::underlying_type_t props, - Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE); - void readStaticRules(bool preRead = false); - - // static props - struct { - std::string monitor, workspace, group; - - std::optional floating; - std::optional fullscreen; - std::optional maximize; - std::optional pseudo; - std::optional pin; - std::optional noInitialFocus; - std::optional center; - - std::optional fullscreenStateClient; - std::optional fullscreenStateInternal; - std::optional content; - std::optional noCloseFor; - - std::string size, position; - - std::vector suppressEvent; - } static_; - - struct SCustomPropContainer { - CWindowRuleEffectContainer::storageType idx = WINDOW_RULE_EFFECT_NONE; - std::underlying_type_t propMask = RULE_PROP_NONE; - std::string effect; - }; - - // This struct holds props that were dynamically registered. Plugins may read this. - struct { - std::unordered_map> props; - } m_otherProps; - -#define COMMA , -#define DEFINE_PROP(type, name, def, eff) \ - private: \ - std::pair, std::underlying_type_t> m_##name = {def, RULE_PROP_NONE}; \ - \ - public: \ - Types::COverridableVar& name() { \ - return m_##name.first; \ - } \ - void name##Override(const Types::COverridableVar& other) { \ - m_##name.first = other; \ - } \ - eWindowRuleEffect name##Effect() { \ - return eff; \ - } - - // dynamic props - DEFINE_PROP(Types::SAlphaValue, alpha, Types::SAlphaValue{}, WINDOW_RULE_EFFECT_OPACITY) - DEFINE_PROP(Types::SAlphaValue, alphaInactive, Types::SAlphaValue{}, WINDOW_RULE_EFFECT_OPACITY) - DEFINE_PROP(Types::SAlphaValue, alphaFullscreen, Types::SAlphaValue{}, WINDOW_RULE_EFFECT_OPACITY) - - DEFINE_PROP(bool, allowsInput, false, WINDOW_RULE_EFFECT_ALLOWS_INPUT) - DEFINE_PROP(bool, decorate, true, WINDOW_RULE_EFFECT_DECORATE) - DEFINE_PROP(bool, focusOnActivate, false, WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE) - DEFINE_PROP(bool, keepAspectRatio, false, WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO) - DEFINE_PROP(bool, nearestNeighbor, false, WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR) - DEFINE_PROP(bool, noAnim, false, WINDOW_RULE_EFFECT_NO_ANIM) - DEFINE_PROP(bool, noBlur, false, WINDOW_RULE_EFFECT_NO_BLUR) - DEFINE_PROP(bool, noDim, false, WINDOW_RULE_EFFECT_NO_DIM) - DEFINE_PROP(bool, noFocus, false, WINDOW_RULE_EFFECT_NO_FOCUS) - DEFINE_PROP(bool, noMaxSize, false, WINDOW_RULE_EFFECT_NO_MAX_SIZE) - DEFINE_PROP(bool, noShadow, false, WINDOW_RULE_EFFECT_NO_SHADOW) - DEFINE_PROP(bool, noShortcutsInhibit, false, WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT) - DEFINE_PROP(bool, opaque, false, WINDOW_RULE_EFFECT_OPAQUE) - DEFINE_PROP(bool, dimAround, false, WINDOW_RULE_EFFECT_DIM_AROUND) - DEFINE_PROP(bool, RGBX, false, WINDOW_RULE_EFFECT_FORCE_RGBX) - DEFINE_PROP(bool, syncFullscreen, true, WINDOW_RULE_EFFECT_SYNC_FULLSCREEN) - DEFINE_PROP(bool, tearing, false, WINDOW_RULE_EFFECT_IMMEDIATE) - DEFINE_PROP(bool, xray, false, WINDOW_RULE_EFFECT_XRAY) - DEFINE_PROP(bool, renderUnfocused, false, WINDOW_RULE_EFFECT_RENDER_UNFOCUSED) - DEFINE_PROP(bool, noFollowMouse, false, WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE) - DEFINE_PROP(bool, noScreenShare, false, WINDOW_RULE_EFFECT_NO_SCREEN_SHARE) - DEFINE_PROP(bool, noVRR, false, WINDOW_RULE_EFFECT_NO_VRR) - DEFINE_PROP(bool, persistentSize, false, WINDOW_RULE_EFFECT_PERSISTENT_SIZE) - DEFINE_PROP(bool, stayFocused, false, WINDOW_RULE_EFFECT_STAY_FOCUSED) - - DEFINE_PROP(int, idleInhibitMode, false, WINDOW_RULE_EFFECT_IDLE_INHIBIT) - - DEFINE_PROP(Hyprlang::INT, borderSize, {std::string("general:border_size") COMMA sc(0) COMMA std::nullopt}, WINDOW_RULE_EFFECT_BORDER_SIZE) - DEFINE_PROP(Hyprlang::INT, rounding, {std::string("decoration:rounding") COMMA sc(0) COMMA std::nullopt}, WINDOW_RULE_EFFECT_ROUNDING) - - DEFINE_PROP(Hyprlang::FLOAT, roundingPower, {std::string("decoration:rounding_power")}, WINDOW_RULE_EFFECT_ROUNDING_POWER) - DEFINE_PROP(Hyprlang::FLOAT, scrollMouse, {std::string("input:scroll_factor")}, WINDOW_RULE_EFFECT_SCROLL_MOUSE) - DEFINE_PROP(Hyprlang::FLOAT, scrollTouchpad, {std::string("input:touchpad:scroll_factor")}, WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD) - - DEFINE_PROP(std::string, animationStyle, std::string(""), WINDOW_RULE_EFFECT_ANIMATION) - - DEFINE_PROP(Vector2D, maxSize, Vector2D{}, WINDOW_RULE_EFFECT_MAX_SIZE) - DEFINE_PROP(Vector2D, minSize, Vector2D{}, WINDOW_RULE_EFFECT_MIN_SIZE) - - DEFINE_PROP(CGradientValueData, activeBorderColor, {}, WINDOW_RULE_EFFECT_BORDER_COLOR) - DEFINE_PROP(CGradientValueData, inactiveBorderColor, {}, WINDOW_RULE_EFFECT_BORDER_COLOR) - - std::vector>> m_dynamicTags; - CTagKeeper m_tagKeeper; - -#undef COMMA -#undef DEFINE_PROP - - private: - PHLWINDOWREF m_window; - - struct SRuleResult { - bool needsRelayout = false; - bool tagsChanged = false; - }; - - SRuleResult applyDynamicRule(const SP& rule); - SRuleResult applyStaticRule(const SP& rule); - }; -}; diff --git a/src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp b/src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp deleted file mode 100644 index 660bf871..00000000 --- a/src/desktop/rule/windowRule/WindowRuleEffectContainer.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "WindowRuleEffectContainer.hpp" - -using namespace Desktop; -using namespace Desktop::Rule; - -// -SP Rule::windowEffects() { - static SP container = makeShared(); - return container; -} - -static const std::vector EFFECT_STRINGS = { - "__internal_none", // - "float", // - "tile", // - "fullscreen", // - "maximize", // - "fullscreen_state", // - "move", // - "size", // - "center", // - "pseudo", // - "monitor", // - "workspace", // - "no_initial_focus", // - "pin", // - "group", // - "suppress_event", // - "content", // - "no_close_for", // - "rounding", // - "rounding_power", // - "persistent_size", // - "animation", // - "border_color", // - "idle_inhibit", // - "opacity", // - "tag", // - "max_size", // - "min_size", // - "border_size", // - "allows_input", // - "dim_around", // - "decorate", // - "focus_on_activate", // - "keep_aspect_ratio", // - "nearest_neighbor", // - "no_anim", // - "no_blur", // - "no_dim", // - "no_focus", // - "no_follow_mouse", // - "no_max_size", // - "no_shadow", // - "no_shortcuts_inhibit", // - "opaque", // - "force_rgbx", // - "sync_fullscreen", // - "immediate", // - "xray", // - "render_unfocused", // - "no_screen_share", // - "no_vrr", // - "scroll_mouse", // - "scroll_touchpad", // - "stay_focused", // - "__internal_last_static", // -}; - -// This is here so that if we change the rules, we get reminded to update -// the strings. -static_assert(WINDOW_RULE_EFFECT_LAST_STATIC == 54); - -CWindowRuleEffectContainer::CWindowRuleEffectContainer() : IEffectContainer(std::vector{EFFECT_STRINGS}) { - ; -} diff --git a/src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp b/src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp deleted file mode 100644 index 0827d462..00000000 --- a/src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "../effect/EffectContainer.hpp" -#include "../../../helpers/memory/Memory.hpp" - -#pragma once - -namespace Desktop::Rule { - enum eWindowRuleEffect : uint8_t { - WINDOW_RULE_EFFECT_NONE = 0, - - // static - WINDOW_RULE_EFFECT_FLOAT, - WINDOW_RULE_EFFECT_TILE, - WINDOW_RULE_EFFECT_FULLSCREEN, - WINDOW_RULE_EFFECT_MAXIMIZE, - WINDOW_RULE_EFFECT_FULLSCREENSTATE, - WINDOW_RULE_EFFECT_MOVE, - WINDOW_RULE_EFFECT_SIZE, - WINDOW_RULE_EFFECT_CENTER, - WINDOW_RULE_EFFECT_PSEUDO, - WINDOW_RULE_EFFECT_MONITOR, - WINDOW_RULE_EFFECT_WORKSPACE, - WINDOW_RULE_EFFECT_NOINITIALFOCUS, - WINDOW_RULE_EFFECT_PIN, - WINDOW_RULE_EFFECT_GROUP, - WINDOW_RULE_EFFECT_SUPPRESSEVENT, - WINDOW_RULE_EFFECT_CONTENT, - WINDOW_RULE_EFFECT_NOCLOSEFOR, - - // dynamic - WINDOW_RULE_EFFECT_ROUNDING, - WINDOW_RULE_EFFECT_ROUNDING_POWER, - WINDOW_RULE_EFFECT_PERSISTENT_SIZE, - WINDOW_RULE_EFFECT_ANIMATION, - WINDOW_RULE_EFFECT_BORDER_COLOR, - WINDOW_RULE_EFFECT_IDLE_INHIBIT, - WINDOW_RULE_EFFECT_OPACITY, - WINDOW_RULE_EFFECT_TAG, - WINDOW_RULE_EFFECT_MAX_SIZE, - WINDOW_RULE_EFFECT_MIN_SIZE, - WINDOW_RULE_EFFECT_BORDER_SIZE, - WINDOW_RULE_EFFECT_ALLOWS_INPUT, - WINDOW_RULE_EFFECT_DIM_AROUND, - WINDOW_RULE_EFFECT_DECORATE, - WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE, - WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO, - WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR, - WINDOW_RULE_EFFECT_NO_ANIM, - WINDOW_RULE_EFFECT_NO_BLUR, - WINDOW_RULE_EFFECT_NO_DIM, - WINDOW_RULE_EFFECT_NO_FOCUS, - WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE, - WINDOW_RULE_EFFECT_NO_MAX_SIZE, - WINDOW_RULE_EFFECT_NO_SHADOW, - WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT, - WINDOW_RULE_EFFECT_OPAQUE, - WINDOW_RULE_EFFECT_FORCE_RGBX, - WINDOW_RULE_EFFECT_SYNC_FULLSCREEN, - WINDOW_RULE_EFFECT_IMMEDIATE, - WINDOW_RULE_EFFECT_XRAY, - WINDOW_RULE_EFFECT_RENDER_UNFOCUSED, - WINDOW_RULE_EFFECT_NO_SCREEN_SHARE, - WINDOW_RULE_EFFECT_NO_VRR, - WINDOW_RULE_EFFECT_SCROLL_MOUSE, - WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD, - WINDOW_RULE_EFFECT_STAY_FOCUSED, - - WINDOW_RULE_EFFECT_LAST_STATIC, - }; - - class CWindowRuleEffectContainer : public IEffectContainer { - public: - CWindowRuleEffectContainer(); - virtual ~CWindowRuleEffectContainer() = default; - }; - - SP windowEffects(); -}; \ No newline at end of file diff --git a/src/desktop/state/FocusState.cpp b/src/desktop/state/FocusState.cpp deleted file mode 100644 index c1298766..00000000 --- a/src/desktop/state/FocusState.cpp +++ /dev/null @@ -1,304 +0,0 @@ -#include "FocusState.hpp" -#include "../view/Window.hpp" -#include "../../Compositor.hpp" -#include "../../protocols/XDGShell.hpp" -#include "../../render/Renderer.hpp" -#include "../../managers/EventManager.hpp" -#include "../../managers/input/InputManager.hpp" -#include "../../managers/SeatManager.hpp" -#include "../../xwayland/XSurface.hpp" -#include "../../protocols/PointerConstraints.hpp" -#include "managers/animation/DesktopAnimationManager.hpp" -#include "../../layout/LayoutManager.hpp" -#include "../../event/EventBus.hpp" - -using namespace Desktop; - -#define COMMA , - -SP Desktop::focusState() { - static SP state = makeShared(); - return state; -} - -Desktop::CFocusState::CFocusState() = default; - -struct SFullscreenWorkspaceFocusResult { - PHLWINDOW overrideFocusWindow = nullptr; -}; - -static SFullscreenWorkspaceFocusResult onFullscreenWorkspaceFocusWindow(PHLWINDOW pWindow, bool forceFSCycle) { - const auto FSWINDOW = pWindow->m_workspace->getFullscreenWindow(); - const auto FSMODE = pWindow->m_workspace->m_fullscreenMode; - - if (pWindow == FSWINDOW) - return {}; // no conflict - - if (pWindow->m_isFloating) { - // if the window is floating, just bring it to the top - pWindow->m_createdOverFullscreen = true; - g_pDesktopAnimationManager->setFullscreenFloatingFade(pWindow, 1.f); - g_pHyprRenderer->damageWindow(pWindow); - return {}; - } - - static auto PONFOCUSUNDERFS = CConfigValue("misc:on_focus_under_fullscreen"); - - switch (*PONFOCUSUNDERFS) { - case 0: - // focus the fullscreen window instead - return {.overrideFocusWindow = FSWINDOW}; - case 2: - // undo fs, unless we force a cycle - if (!forceFSCycle) { - g_pCompositor->setWindowFullscreenInternal(FSWINDOW, FSMODE_NONE); - break; - } - [[fallthrough]]; - case 1: - // replace fullscreen - g_pCompositor->setWindowFullscreenInternal(FSWINDOW, FSMODE_NONE); - g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE); - break; - - default: Log::logger->log(Log::ERR, "Invalid misc:on_focus_under_fullscreen mode: {}", *PONFOCUSUNDERFS); break; - } - - return {}; -} - -void CFocusState::fullWindowFocus(PHLWINDOW pWindow, eFocusReason reason, SP surface, bool forceFSCycle) { - if (pWindow) { - if (!pWindow->m_workspace) - return; - - const auto CURRENT_FS_MODE = pWindow->m_workspace->m_hasFullscreenWindow ? pWindow->m_workspace->m_fullscreenMode : FSMODE_NONE; - if (CURRENT_FS_MODE != FSMODE_NONE) { - const auto RESULT = onFullscreenWorkspaceFocusWindow(pWindow, forceFSCycle); - if (RESULT.overrideFocusWindow) - pWindow = RESULT.overrideFocusWindow; - } - } - - static auto PMODALPARENTBLOCKING = CConfigValue("general:modal_parent_blocking"); - - if (*PMODALPARENTBLOCKING && pWindow && pWindow->m_xdgSurface && pWindow->m_xdgSurface->m_toplevel && pWindow->m_xdgSurface->m_toplevel->anyChildModal()) { - Log::logger->log(Log::DEBUG, "Refusing focus to window shadowed by modal dialog"); - return; - } - - rawWindowFocus(pWindow, reason, surface); -} - -void CFocusState::rawWindowFocus(PHLWINDOW pWindow, eFocusReason reason, SP surface) { - static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); - static auto PSPECIALFALLTHROUGH = CConfigValue("input:special_fallthrough"); - - if (!pWindow || !pWindow->priorityFocus()) { - if (g_pSessionLockManager->isSessionLocked()) { - Log::logger->log(Log::DEBUG, "Refusing a keyboard focus to a window because of a sessionlock"); - return; - } - - if (!g_pInputManager->m_exclusiveLSes.empty()) { - Log::logger->log(Log::DEBUG, "Refusing a keyboard focus to a window because of an exclusive ls"); - return; - } - } - - if (pWindow && pWindow->m_isX11 && pWindow->isX11OverrideRedirect() && !pWindow->m_xwaylandSurface->wantsFocus()) - return; - - // m_target on purpose, this avoids the group - if (pWindow) - g_layoutManager->bringTargetToTop(pWindow->m_target); - - if (!pWindow || !validMapped(pWindow)) { - - if (m_focusWindow.expired() && !pWindow) - return; - - const auto PLASTWINDOW = m_focusWindow.lock(); - m_focusWindow.reset(); - - if (PLASTWINDOW && PLASTWINDOW->m_isMapped) { - PLASTWINDOW->m_ruleApplicator->propertiesChanged(Rule::RULE_PROP_FOCUS); - PLASTWINDOW->updateDecorationValues(); - - g_pXWaylandManager->activateWindow(PLASTWINDOW, false); - } - - g_pSeatManager->setKeyboardFocus(nullptr); - - g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); - g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""}); - - Event::bus()->m_events.window.active.emit(nullptr, reason); - - m_focusSurface.reset(); - - g_pInputManager->recheckIdleInhibitorStatus(); - return; - } - - if (pWindow->m_ruleApplicator->noFocus().valueOrDefault()) { - Log::logger->log(Log::DEBUG, "Ignoring focus to nofocus window!"); - return; - } - - if (m_focusWindow.lock() == pWindow && g_pSeatManager->m_state.keyboardFocus == surface && g_pSeatManager->m_state.keyboardFocus) - return; - - if (pWindow->m_pinned) - pWindow->m_workspace = m_focusMonitor->m_activeWorkspace; - - const auto PMONITOR = pWindow->m_monitor.lock(); - - if (!pWindow->m_workspace || !pWindow->m_workspace->isVisible()) { - const auto PWORKSPACE = pWindow->m_workspace; - // This is to fix incorrect feedback on the focus history. - PWORKSPACE->m_lastFocusedWindow = pWindow; - if (PWORKSPACE->m_isSpecialWorkspace) - m_focusMonitor->changeWorkspace(PWORKSPACE, false, true); // if special ws, open on current monitor - else if (PMONITOR) - PMONITOR->changeWorkspace(PWORKSPACE, false, true); - // changeworkspace already calls focusWindow - return; - } - - const auto PLASTWINDOW = m_focusWindow.lock(); - m_focusWindow = pWindow; - - /* If special fallthrough is enabled, this behavior will be disabled, as I have no better idea of nicely tracking which - window focuses are "via keybinds" and which ones aren't. */ - if (PMONITOR && PMONITOR->m_activeSpecialWorkspace && PMONITOR->m_activeSpecialWorkspace != pWindow->m_workspace && !pWindow->m_pinned && !*PSPECIALFALLTHROUGH) - PMONITOR->setSpecialWorkspace(nullptr); - - // we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window - if (PLASTWINDOW && PLASTWINDOW->m_isMapped) { - PLASTWINDOW->m_ruleApplicator->propertiesChanged(Rule::RULE_PROP_FOCUS); - PLASTWINDOW->updateDecorationValues(); - - if (!pWindow->m_isX11 || !pWindow->isX11OverrideRedirect()) - g_pXWaylandManager->activateWindow(PLASTWINDOW, false); - } - - const auto PWINDOWSURFACE = surface ? surface : pWindow->wlSurface()->resource(); - - rawSurfaceFocus(PWINDOWSURFACE, pWindow); - - g_pXWaylandManager->activateWindow(pWindow, true); // sets the m_pLastWindow - - pWindow->m_ruleApplicator->propertiesChanged(Rule::RULE_PROP_FOCUS); - pWindow->onFocusAnimUpdate(); - pWindow->updateDecorationValues(); - - if (pWindow->m_isUrgent) - pWindow->m_isUrgent = false; - - // Send an event - g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = pWindow->m_class + "," + pWindow->m_title}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = std::format("{:x}", rc(pWindow.get()))}); - - Event::bus()->m_events.window.active.emit(pWindow, reason); - - g_pInputManager->recheckIdleInhibitorStatus(); - - if (*PFOLLOWMOUSE == 0) - g_pInputManager->sendMotionEventsToFocused(); - - if (pWindow->m_group) - pWindow->deactivateGroupMembers(); -} - -void CFocusState::rawSurfaceFocus(SP pSurface, PHLWINDOW pWindowOwner) { - if (g_pSeatManager->m_state.keyboardFocus == pSurface || (pWindowOwner && g_pSeatManager->m_state.keyboardFocus == pWindowOwner->wlSurface()->resource())) - return; // Don't focus when already focused on this. - - if (g_pSessionLockManager->isSessionLocked() && pSurface && !g_pSessionLockManager->isSurfaceSessionLock(pSurface)) - return; - - if (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(pSurface)) { - Log::logger->log(Log::DEBUG, "surface {:x} won't receive kb focus because grab rejected it", rc(pSurface.get())); - return; - } - - const auto PLASTSURF = m_focusSurface.lock(); - - // Unfocus last surface if should - if (m_focusSurface && !pWindowOwner) - g_pXWaylandManager->activateSurface(m_focusSurface.lock(), false); - - if (!pSurface) { - g_pSeatManager->setKeyboardFocus(nullptr); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = ","}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = ""}); - Event::bus()->m_events.input.keyboard.focus.emit(nullptr); - m_focusSurface.reset(); - return; - } - - if (g_pSeatManager->m_keyboard) - g_pSeatManager->setKeyboardFocus(pSurface); - - if (pWindowOwner) - Log::logger->log(Log::DEBUG, "Set keyboard focus to surface {:x}, with {}", rc(pSurface.get()), pWindowOwner); - else - Log::logger->log(Log::DEBUG, "Set keyboard focus to surface {:x}", rc(pSurface.get())); - - g_pXWaylandManager->activateSurface(pSurface, true); - m_focusSurface = pSurface; - - Event::bus()->m_events.input.keyboard.focus.emit(pSurface); - - const auto SURF = Desktop::View::CWLSurface::fromResource(pSurface); - const auto OLDSURF = Desktop::View::CWLSurface::fromResource(PLASTSURF); - - if (OLDSURF && OLDSURF->constraint()) - OLDSURF->constraint()->deactivate(); - - if (SURF && SURF->constraint()) - SURF->constraint()->activate(); -} - -void CFocusState::rawMonitorFocus(PHLMONITOR pMonitor) { - if (m_focusMonitor == pMonitor) - return; - - if (!pMonitor) { - m_focusMonitor.reset(); - return; - } - - const auto PWORKSPACE = pMonitor->m_activeWorkspace; - - const auto WORKSPACE_ID = PWORKSPACE ? std::to_string(PWORKSPACE->m_id) : std::to_string(WORKSPACE_INVALID); - const auto WORKSPACE_NAME = PWORKSPACE ? PWORKSPACE->m_name : "?"; - - g_pEventManager->postEvent(SHyprIPCEvent{.event = "focusedmon", .data = pMonitor->m_name + "," + WORKSPACE_NAME}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "focusedmonv2", .data = pMonitor->m_name + "," + WORKSPACE_ID}); - - Event::bus()->m_events.monitor.focused.emit(pMonitor); - m_focusMonitor = pMonitor; -} - -SP CFocusState::surface() { - return m_focusSurface.lock(); -} - -PHLWINDOW CFocusState::window() { - return m_focusWindow.lock(); -} - -PHLMONITOR CFocusState::monitor() { - return m_focusMonitor.lock(); -} - -void CFocusState::resetWindowFocus() { - m_focusWindow.reset(); - m_focusSurface.reset(); -} - -bool Desktop::isHardInputFocusReason(eFocusReason r) { - return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE; -} diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp deleted file mode 100644 index 71330a3e..00000000 --- a/src/desktop/state/FocusState.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "../DesktopTypes.hpp" -#include "../../helpers/signal/Signal.hpp" - -class CWLSurfaceResource; - -namespace Desktop { - enum eFocusReason : uint8_t { - FOCUS_REASON_UNKNOWN = 0, - FOCUS_REASON_FFM, - FOCUS_REASON_KEYBIND, - FOCUS_REASON_CLICK, - FOCUS_REASON_OTHER, - FOCUS_REASON_DESKTOP_STATE_CHANGE, - FOCUS_REASON_NEW_WINDOW, - FOCUS_REASON_GHOSTS, - }; - - bool isHardInputFocusReason(eFocusReason r); - - class CFocusState { - public: - CFocusState(); - ~CFocusState() = default; - - CFocusState(CFocusState&&) = delete; - CFocusState(CFocusState&) = delete; - CFocusState(const CFocusState&) = delete; - - void fullWindowFocus(PHLWINDOW w, eFocusReason reason, SP surface = nullptr, bool forceFSCycle = false); - void rawWindowFocus(PHLWINDOW w, eFocusReason reason, SP surface = nullptr); - void rawSurfaceFocus(SP s, PHLWINDOW pWindowOwner = nullptr); - void rawMonitorFocus(PHLMONITOR m); - - void resetWindowFocus(); - - SP surface(); - PHLWINDOW window(); - PHLMONITOR monitor(); - - private: - WP m_focusSurface; - PHLWINDOWREF m_focusWindow; - PHLMONITORREF m_focusMonitor; - - CHyprSignalListener m_windowOpen, m_windowClose; - }; - - SP focusState(); -}; diff --git a/src/desktop/types/OverridableVar.hpp b/src/desktop/types/OverridableVar.hpp deleted file mode 100644 index cdf27b89..00000000 --- a/src/desktop/types/OverridableVar.hpp +++ /dev/null @@ -1,159 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "../../config/ConfigValue.hpp" - -namespace Desktop::Types { - - struct SAlphaValue { - float alpha = 1.F; - bool overridden = false; - - float applyAlpha(float a) const { - if (overridden) - return alpha; - else - return alpha * a; - }; - }; - - enum eOverridePriority : uint8_t { - PRIORITY_LAYOUT = 0, - PRIORITY_WORKSPACE_RULE, - PRIORITY_WINDOW_RULE, - PRIORITY_SET_PROP, - - PRIORITY_END, - }; - - template - T clampOptional(T const& value, std::optional const& min, std::optional const& max) { - return std::clamp(value, min.value_or(std::numeric_limits::min()), max.value_or(std::numeric_limits::max())); - } - - template || std::is_same_v || std::is_same_v> - class COverridableVar { - public: - COverridableVar(T const& value, eOverridePriority priority) { - m_values[priority] = value; - } - - COverridableVar(T const& value) : m_defaultValue{value} {} - COverridableVar(T const& value, std::optional const& min, std::optional const& max = std::nullopt) : m_defaultValue{value}, m_minValue{min}, m_maxValue{max} {} - COverridableVar(std::string const& value) - requires(Extended && !std::is_same_v) - : m_configValue(SP>(new CConfigValue(value))) {} - COverridableVar(std::string const& value, std::optional const& min, std::optional const& max = std::nullopt) - requires(Extended && !std::is_same_v) - : m_minValue(min), m_maxValue(max), m_configValue(SP>(new CConfigValue(value))) {} - - COverridableVar() = default; - ~COverridableVar() = default; - - COverridableVar& operator=(COverridableVar const& other) { - // Self-assignment check - if (this == &other) - return *this; - - for (size_t i = 0; i < PRIORITY_END; ++i) { - if constexpr (Extended && !std::is_same_v) - m_values[i] = other.m_values[i].has_value() ? clampOptional(*other.m_values[i], m_minValue, m_maxValue) : other.m_values[i]; - else - m_values[i] = other.m_values[i]; - } - - return *this; - } - - void set(T value, eOverridePriority priority) { - m_values[priority] = value; - } - - void unset(eOverridePriority priority) { - m_values[priority] = std::nullopt; - } - - bool hasValue() const { - return std::ranges::any_of(m_values, [](const auto& e) { return e.has_value(); }); - } - - T value() const { - for (const auto& v : m_values | std::ranges::views::reverse) { - if (v) - return *v; - } - throw std::bad_optional_access(); - } - - T valueOr(T const& other) const { - if (hasValue()) - return value(); - else - return other; - } - - T valueOrDefault() const - requires(Extended && !std::is_same_v) - { - if (hasValue()) - return value(); - else if (m_defaultValue.has_value()) - return m_defaultValue.value(); - else - return **std::any_cast>>(m_configValue); - } - - T valueOrDefault() const - requires(!Extended || std::is_same_v) - { - if (hasValue()) - return value(); - else if (!m_defaultValue.has_value()) - throw std::bad_optional_access(); - else - return m_defaultValue.value(); - } - - eOverridePriority getPriority() const { - for (int i = PRIORITY_END - 1; i >= 0; --i) { - if (m_values[i]) - return sc(i); - } - - throw std::bad_optional_access(); - } - - void increment(T const& other, eOverridePriority priority) { - if constexpr (std::is_same_v) - m_values[priority] = valueOr(false) ^ other; - else - m_values[priority] = clampOptional(valueOrDefault() + other, m_minValue, m_maxValue); - } - - void matchOptional(std::optional const& optValue, eOverridePriority priority) { - if (optValue.has_value()) - m_values[priority] = optValue.value(); - else - unset(priority); - } - - operator std::optional() { - if (hasValue()) - return value(); - else - return std::nullopt; - } - - private: - std::array, PRIORITY_END> m_values; - std::optional m_defaultValue; // used for toggling, so required for bool - std::optional m_minValue; - std::optional m_maxValue; - std::any m_configValue; // only there for select variables - }; - -} \ No newline at end of file diff --git a/src/desktop/view/GlobalViewMethods.cpp b/src/desktop/view/GlobalViewMethods.cpp deleted file mode 100644 index 83513e81..00000000 --- a/src/desktop/view/GlobalViewMethods.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "GlobalViewMethods.hpp" -#include "../../Compositor.hpp" - -#include "LayerSurface.hpp" -#include "Window.hpp" -#include "Popup.hpp" -#include "Subsurface.hpp" -#include "SessionLock.hpp" - -#include "../../protocols/core/Compositor.hpp" -#include "../../protocols/core/Subcompositor.hpp" -#include "../../protocols/SessionLock.hpp" - -using namespace Desktop; -using namespace Desktop::View; - -std::vector> View::getViewsForWorkspace(PHLWORKSPACE ws) { - std::vector> views; - - for (const auto& w : g_pCompositor->m_windows) { - if (!w->aliveAndVisible() || w->m_workspace != ws) - continue; - - views.emplace_back(w); - - w->wlSurface()->resource()->breadthfirst( - [&views](SP s, const Vector2D& pos, void* data) { - auto surf = CWLSurface::fromResource(s); - if (!surf || !s->m_mapped) - return; - - views.emplace_back(surf->view()); - }, - nullptr); - - // xwl windows dont have this - if (w->m_popupHead) { - w->m_popupHead->breadthfirst( - [&views](SP s, void* data) { - auto surf = s->wlSurface(); - if (!surf || !s->aliveAndVisible()) - return; - - views.emplace_back(surf->view()); - }, - nullptr); - } - } - - for (const auto& l : g_pCompositor->m_layers) { - if (!l->aliveAndVisible() || l->m_monitor != ws->m_monitor) - continue; - - views.emplace_back(l); - - l->m_popupHead->breadthfirst( - [&views](SP p, void* data) { - auto surf = p->wlSurface(); - if (!surf || !p->aliveAndVisible()) - return; - - views.emplace_back(surf->view()); - }, - nullptr); - } - - for (const auto& v : g_pCompositor->m_otherViews) { - if (!v->aliveAndVisible() || !v->desktopComponent()) - continue; - - if (v->type() == VIEW_TYPE_LOCK_SCREEN) { - const auto LOCK = Desktop::View::CSessionLock::fromView(v); - if (LOCK->monitor() != ws->m_monitor) - continue; - - views.emplace_back(LOCK); - continue; - } - } - - return views; -} diff --git a/src/desktop/view/GlobalViewMethods.hpp b/src/desktop/view/GlobalViewMethods.hpp deleted file mode 100644 index 551a42da..00000000 --- a/src/desktop/view/GlobalViewMethods.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "View.hpp" - -#include "../Workspace.hpp" - -#include - -namespace Desktop::View { - std::vector> getViewsForWorkspace(PHLWORKSPACE ws); -}; \ No newline at end of file diff --git a/src/desktop/view/Group.cpp b/src/desktop/view/Group.cpp deleted file mode 100644 index 06884cff..00000000 --- a/src/desktop/view/Group.cpp +++ /dev/null @@ -1,351 +0,0 @@ -#include "Group.hpp" -#include "Window.hpp" - -#include "../../render/decorations/CHyprGroupBarDecoration.hpp" -#include "../../layout/target/WindowGroupTarget.hpp" -#include "../../layout/target/WindowTarget.hpp" -#include "../../layout/target/Target.hpp" -#include "../../layout/space/Space.hpp" -#include "../../layout/LayoutManager.hpp" -#include "../../desktop/state/FocusState.hpp" -#include "../../Compositor.hpp" - -#include - -using namespace Desktop; -using namespace Desktop::View; - -std::vector>& View::groups() { - static std::vector> g; - return g; -} - -SP CGroup::create(std::vector&& windows) { - auto x = SP(new CGroup(std::move(windows))); - x->m_self = x; - x->m_target = Layout::CWindowGroupTarget::create(x); - groups().emplace_back(x); - - x->init(); - - return x; -} - -CGroup::CGroup(std::vector&& windows) : m_windows(std::move(windows)) { - ; -} - -void CGroup::init() { - // for proper group logic: - // - add all windows to us - // - replace the first window with our target - // - remove all window targets from layout - // - apply updates - - // FIXME: what if some windows are grouped? For now we only do 1-window but YNK - for (const auto& w : m_windows) { - RASSERT(!w->m_group, "CGroup: windows cannot contain grouped in init, this will explode"); - w->m_group = m_self.lock(); - } - - g_layoutManager->switchTargets(m_windows.at(0)->m_target, m_target); - - for (const auto& w : m_windows) { - w->m_target->setSpaceGhost(m_target->space()); - } - - for (const auto& w : m_windows) { - applyWindowDecosAndUpdates(w.lock()); - } - - updateWindowVisibility(); -} - -void CGroup::destroy() { - while (true) { - if (m_windows.size() == 1) { - remove(m_windows.at(0).lock()); - break; - } - - remove(m_windows.at(0).lock()); - } -} - -CGroup::~CGroup() { - if (m_target->space()) - m_target->assignToSpace(nullptr); - std::erase_if(groups(), [this](const auto& e) { return !e || e == m_self; }); -} - -bool CGroup::has(PHLWINDOW w) const { - return std::ranges::contains(m_windows, w); -} - -void CGroup::add(PHLWINDOW w) { - static auto INSERT_AFTER_CURRENT = CConfigValue("group:insert_after_current"); - - if (w->m_group) { - if (w->m_group == m_self) - return; - - const auto WINDOWS = w->m_group->windows(); - for (const auto& w : WINDOWS) { - w->m_group->remove(w.lock()); - add(w.lock()); - } - - return; - } - - if (w->layoutTarget()->space()) { - // remove the target from a space if it is in one - g_layoutManager->removeTarget(w->layoutTarget()); - } - - w->m_group = m_self.lock(); - w->m_target->setSpaceGhost(m_target->space()); - w->m_target->setFloating(m_target->floating()); - - if (*INSERT_AFTER_CURRENT) { - m_windows.insert(m_windows.begin() + m_current + 1, w); - m_current++; - } else { - m_windows.emplace_back(w); - m_current = m_windows.size() - 1; - } - - applyWindowDecosAndUpdates(w); - updateWindowVisibility(); - m_target->recalc(); -} - -void CGroup::remove(PHLWINDOW w, Math::eDirection dir) { - std::optional idx; - for (size_t i = 0; i < m_windows.size(); ++i) { - if (m_windows.at(i) == w) { - idx = i; - break; - } - } - - if (!idx) - return; - - if ((m_current >= *idx && idx != 0) || (m_current >= m_windows.size() - 1 && m_current > 0)) - m_current--; - - auto g = m_self.lock(); // keep ref to avoid uaf after w->m_group.reset() - - w->m_group.reset(); - removeWindowDecos(w); - - w->setHidden(false); - - const bool REMOVING_GROUP = m_windows.size() <= 1; - - if (REMOVING_GROUP) { - w->m_target->assignToSpace(nullptr); - g_layoutManager->switchTargets(m_target, w->m_target); - } - - // we do it after the above because switchTargets expects this to be a valid group - m_windows.erase(m_windows.begin() + *idx); - - if (!m_windows.empty()) - updateWindowVisibility(); - - // do this here: otherwise the new current is hidden and workspace rules get wrong data - if (!REMOVING_GROUP) { - std::optional focalPoint; - if (dir != Math::DIRECTION_DEFAULT) { - const auto box = m_target->position(); - switch (dir) { - case Math::DIRECTION_RIGHT: focalPoint = Vector2D(box.x + box.w, box.y + box.h / 2.0); break; - case Math::DIRECTION_LEFT: focalPoint = Vector2D(box.x, box.y + box.h / 2.0); break; - case Math::DIRECTION_DOWN: focalPoint = Vector2D(box.x + box.w / 2.0, box.y + box.h); break; - case Math::DIRECTION_UP: focalPoint = Vector2D(box.x + box.w / 2.0, box.y); break; - default: break; - } - } - w->m_target->assignToSpace(m_target->space(), focalPoint); - } -} - -void CGroup::moveCurrent(bool next) { - size_t idx = m_current; - - if (next) { - idx++; - if (idx >= m_windows.size()) - idx = 0; - } else { - if (idx == 0) - idx = m_windows.size() - 1; - else - idx--; - } - - setCurrent(idx); -} - -void CGroup::setCurrent(size_t idx) { - if (idx == m_current) - return; - - const auto FS_STATE = m_target->fullscreenMode(); - const auto WASFOCUS = Desktop::focusState()->window() == current(); - auto oldWindow = m_windows.at(m_current).lock(); - - if (FS_STATE != FSMODE_NONE) - g_pCompositor->setWindowFullscreenInternal(oldWindow, FSMODE_NONE); - - m_current = std::clamp(idx, sc(0), m_windows.size() - 1); - updateWindowVisibility(); - - auto newWindow = m_windows.at(m_current).lock(); - - if (FS_STATE != FSMODE_NONE) { - g_pCompositor->setWindowFullscreenInternal(newWindow, FS_STATE); - newWindow->m_target->warpPositionSize(); - oldWindow->m_target->setPositionGlobal(newWindow->m_target->position()); // TODO: this is a hack and sucks - } - - if (WASFOCUS) - Desktop::focusState()->rawWindowFocus(current(), FOCUS_REASON_DESKTOP_STATE_CHANGE); -} - -void CGroup::setCurrent(PHLWINDOW w) { - if (w == current()) - return; - - for (size_t i = 0; i < m_windows.size(); ++i) { - if (m_windows.at(i) == w) { - setCurrent(i); - return; - } - } -} - -size_t CGroup::getCurrentIdx() const { - return m_current; -} - -PHLWINDOW CGroup::head() const { - return m_windows.front().lock(); -} - -PHLWINDOW CGroup::tail() const { - return m_windows.back().lock(); -} - -PHLWINDOW CGroup::current() const { - return m_windows.at(m_current).lock(); -} - -PHLWINDOW CGroup::next() const { - return (m_current >= m_windows.size() - 1 ? m_windows.front() : m_windows.at(m_current + 1)).lock(); -} - -PHLWINDOW CGroup::fromIndex(size_t idx) const { - if (idx >= m_windows.size()) - return nullptr; - - return m_windows.at(idx).lock(); -} - -const std::vector& CGroup::windows() const { - return m_windows; -} - -void CGroup::applyWindowDecosAndUpdates(PHLWINDOW x) { - x->addWindowDeco(makeUnique(x)); - - x->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE); - x->updateWindowDecos(); - x->updateDecorationValues(); -} - -void CGroup::removeWindowDecos(PHLWINDOW x) { - x->removeWindowDeco(x->getDecorationByType(DECORATION_GROUPBAR)); - - x->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE); - x->updateWindowDecos(); - x->updateDecorationValues(); -} - -void CGroup::updateWindowVisibility() { - for (size_t i = 0; i < m_windows.size(); ++i) { - if (i == m_current) { - auto& x = m_windows.at(i); - x->setHidden(false); - x->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE); - x->updateWindowDecos(); - x->updateDecorationValues(); - } else - m_windows.at(i)->setHidden(true); - } - - m_target->recalc(); - - m_target->damageEntire(); -} - -size_t CGroup::size() const { - return m_windows.size(); -} - -bool CGroup::locked() const { - return m_locked; -} - -void CGroup::setLocked(bool x) { - m_locked = x; -} - -bool CGroup::denied() const { - return m_deny; -} - -void CGroup::setDenied(bool x) { - m_deny = x; -} - -void CGroup::updateWorkspace(PHLWORKSPACE ws) { - if (!ws) - return; - - for (const auto& w : windows()) { - w->m_monitor = ws->m_monitor; - w->moveToWorkspace(ws); - w->updateToplevel(); - w->updateWindowDecos(); - w->m_target->setSpaceGhost(ws->m_space); - } -} - -void CGroup::swapWithNext() { - const bool HAD_FOCUS = Desktop::focusState()->window() == m_windows.at(m_current); - - size_t idx = m_current + 1 >= m_windows.size() ? 0 : m_current + 1; - std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx); - m_current = idx; - - updateWindowVisibility(); - - if (HAD_FOCUS) - Desktop::focusState()->fullWindowFocus(m_windows.at(m_current).lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE); -} - -void CGroup::swapWithLast() { - const bool HAD_FOCUS = Desktop::focusState()->window() == m_windows.at(m_current); - - size_t idx = m_current == 0 ? m_windows.size() - 1 : m_current - 1; - std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx); - m_current = idx; - - updateWindowVisibility(); - - if (HAD_FOCUS) - Desktop::focusState()->fullWindowFocus(m_windows.at(m_current).lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE); -} diff --git a/src/desktop/view/Group.hpp b/src/desktop/view/Group.hpp deleted file mode 100644 index 36c4baae..00000000 --- a/src/desktop/view/Group.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include "../DesktopTypes.hpp" -#include "../../helpers/math/Direction.hpp" - -#include - -namespace Layout { - class CWindowGroupTarget; -}; - -namespace Desktop::View { - class CGroup { - public: - static SP create(std::vector&& windows); - ~CGroup(); - - bool has(PHLWINDOW w) const; - - void add(PHLWINDOW w); - void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT); - void moveCurrent(bool next); - void setCurrent(size_t idx); - void setCurrent(PHLWINDOW w); - size_t getCurrentIdx() const; - size_t size() const; - void destroy(); - void updateWorkspace(PHLWORKSPACE); - - void swapWithNext(); - void swapWithLast(); - - PHLWINDOW head() const; - PHLWINDOW tail() const; - PHLWINDOW current() const; - PHLWINDOW next() const; - - PHLWINDOW fromIndex(size_t idx) const; - - bool locked() const; - void setLocked(bool x); - - bool denied() const; - void setDenied(bool x); - - const std::vector& windows() const; - - SP m_target; - - private: - CGroup(std::vector&& windows); - - void applyWindowDecosAndUpdates(PHLWINDOW x); - void removeWindowDecos(PHLWINDOW x); - void init(); - void updateWindowVisibility(); - - WP m_self; - - std::vector m_windows; - - bool m_locked = false; - bool m_deny = false; - - size_t m_current = 0; - }; - - std::vector>& groups(); -}; \ No newline at end of file diff --git a/src/desktop/view/LayerSurface.cpp b/src/desktop/view/LayerSurface.cpp deleted file mode 100644 index a10c9d4d..00000000 --- a/src/desktop/view/LayerSurface.cpp +++ /dev/null @@ -1,453 +0,0 @@ -#include "LayerSurface.hpp" -#include "../state/FocusState.hpp" -#include "../../Compositor.hpp" -#include "../../protocols/LayerShell.hpp" -#include "../../protocols/core/Compositor.hpp" -#include "../../managers/SeatManager.hpp" -#include "../../managers/animation/AnimationManager.hpp" -#include "../../managers/animation/DesktopAnimationManager.hpp" -#include "../../render/Renderer.hpp" -#include "../../config/ConfigManager.hpp" -#include "../../helpers/Monitor.hpp" -#include "../../managers/input/InputManager.hpp" -#include "../../managers/EventManager.hpp" -#include "../../event/EventBus.hpp" - -using namespace Desktop; -using namespace Desktop::View; - -PHLLS CLayerSurface::create(SP resource) { - PHLLS pLS = SP(new CLayerSurface(resource)); - - auto pMonitor = resource->m_monitor.empty() ? Desktop::focusState()->monitor() : g_pCompositor->getMonitorFromName(resource->m_monitor); - - pLS->m_wlSurface->assign(resource->m_surface.lock(), pLS); - - pLS->m_ruleApplicator = makeUnique(pLS); - pLS->m_self = pLS; - pLS->m_namespace = resource->m_layerNamespace; - pLS->m_layer = std::clamp(resource->m_current.layer, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY); - pLS->m_popupHead = CPopup::create(pLS); - - 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); - - pLS->registerCallbacks(); - - pLS->m_alpha->setValueAndWarp(0.f); - - if (!pMonitor) { - Log::logger->log(Log::DEBUG, "LayerSurface {:x} (namespace {} layer {}) created on NO MONITOR ?!", rc(resource.get()), resource->m_layerNamespace, - sc(pLS->m_layer)); - - return pLS; - } - - if (pMonitor->m_mirrorOf) - pMonitor = g_pCompositor->m_monitors.front(); - - pLS->m_monitor = pMonitor; - pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS); - - Log::logger->log(Log::DEBUG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", rc(resource.get()), resource->m_layerNamespace, - sc(pLS->m_layer), pMonitor->m_name); - - return pLS; -} - -PHLLS CLayerSurface::fromView(SP v) { - if (!v || v->type() != VIEW_TYPE_LAYER_SURFACE) - return nullptr; - return dynamicPointerCast(v); -} - -void CLayerSurface::registerCallbacks() { - m_alpha->setUpdateCallback([this](auto) { - if (m_ruleApplicator->dimAround().valueOrDefault() && m_monitor) - g_pHyprRenderer->damageMonitor(m_monitor.lock()); - }); -} - -CLayerSurface::CLayerSurface(SP resource_) : IView(CWLSurface::create()), 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() { - if (!g_pHyprOpenGL) - return; - - if (m_wlSurface) - m_wlSurface->unassign(); - g_pHyprRenderer->makeEGLCurrent(); - std::erase_if(g_pHyprOpenGL->m_layerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == m_self.lock(); }); - - for (auto const& mon : g_pCompositor->m_realMonitors) { - for (auto& lsl : mon->m_layerSurfaceLayers) { - std::erase_if(lsl, [this](auto& ls) { return ls.expired() || ls.get() == this; }); - } - } -} - -eViewType CLayerSurface::type() const { - return VIEW_TYPE_LAYER_SURFACE; -} - -bool CLayerSurface::visible() const { - return (m_mapped && m_layerSurface && m_layerSurface->m_mapped && m_wlSurface && m_wlSurface->resource()) || (m_fadingOut && m_alpha->value() > 0.F); -} - -std::optional CLayerSurface::logicalBox() const { - return surfaceLogicalBox(); -} - -std::optional CLayerSurface::surfaceLogicalBox() const { - if (!visible()) - return std::nullopt; - - return CBox{m_realPosition->value(), m_realSize->value()}; -} - -bool CLayerSurface::desktopComponent() const { - return true; -} - -void CLayerSurface::onDestroy() { - Log::logger->log(Log::DEBUG, "LayerSurface {:x} destroyed", rc(m_layerSurface.get())); - - const auto PMONITOR = m_monitor.lock(); - - if (!PMONITOR) - Log::logger->log(Log::WARN, "Layersurface destroyed on an invalid monitor (removed?)"); - - if (!m_fadingOut) { - if (m_mapped) { - Log::logger->log(Log::DEBUG, "Forcing an unmap of a LS that did a straight destroy!"); - onUnmap(); - } else { - Log::logger->log(Log::DEBUG, "Removing LayerSurface that wasn't mapped."); - if (m_alpha) - g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT); - m_fadingOut = true; - g_pCompositor->addToFadingOutSafe(m_self.lock()); - } - } - - m_popupHead.reset(); - - m_noProcess = true; - - // rearrange to fix the reserved areas - if (PMONITOR) { - g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); - PMONITOR->m_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}; - g_pHyprRenderer->damageBox(geomFixed); - } - - m_readyToDelete = true; - m_layerSurface.reset(); - if (m_wlSurface) - m_wlSurface->unassign(); - - m_listeners.unmap.reset(); - m_listeners.destroy.reset(); - m_listeners.map.reset(); - m_listeners.commit.reset(); -} - -void CLayerSurface::onMap() { - Log::logger->log(Log::DEBUG, "LayerSurface {:x} mapped", rc(m_layerSurface.get())); - - m_mapped = true; - m_interactivity = m_layerSurface->m_current.interactivity; - m_aboveFullscreen = true; - - m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL); - - m_layerSurface->m_surface->map(); - - // this layer might be re-mapped. - m_fadingOut = false; - g_pCompositor->removeFromFadingOutSafe(m_self.lock()); - - // fix if it changed its mon - const auto PMONITOR = m_monitor.lock(); - - if (!PMONITOR) - return; - - PMONITOR->m_scheduledRecalc = true; - - g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); - - m_wlSurface->resource()->enter(PMONITOR->m_self.lock()); - - const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE; - - if (ISEXCLUSIVE) - g_pInputManager->m_exclusiveLSes.push_back(m_self); - - const bool GRABSFOCUS = ISEXCLUSIVE || - (m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE && - // don't focus if constrained - (g_pSeatManager->m_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_wlSurface->resource())) - g_pSeatManager->setGrab(nullptr); - - g_pInputManager->releaseAllMouseButtons(); - Desktop::focusState()->rawSurfaceFocus(m_wlSurface->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_wlSurface->resource(), LOCAL); - g_pInputManager->m_emptyFocusCursorSet = false; - } - - m_position = Vector2D(m_geometry.x, m_geometry.y); - - CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height}; - g_pHyprRenderer->damageBox(geomFixed); - - g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_IN); - - m_readyToDelete = false; - m_fadingOut = false; - - g_pEventManager->postEvent(SHyprIPCEvent{.event = "openlayer", .data = m_namespace}); - Event::bus()->m_events.layer.opened.emit(m_self.lock()); - - g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), PMONITOR->m_scale); - g_pCompositor->setPreferredTransformForSurface(m_wlSurface->resource(), PMONITOR->m_transform); -} - -void CLayerSurface::onUnmap() { - Log::logger->log(Log::DEBUG, "LayerSurface {:x} unmapped", rc(m_layerSurface.get())); - - g_pEventManager->postEvent(SHyprIPCEvent{.event = "closelayer", .data = m_layerSurface->m_layerNamespace}); - Event::bus()->m_events.layer.closed.emit(m_self.lock()); - - std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other || other == m_self; }); - - if (!m_monitor || g_pCompositor->m_unsafeState) { - Log::logger->log(Log::WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring."); - - g_pCompositor->addToFadingOutSafe(m_self.lock()); - - m_mapped = false; - if (m_layerSurface && m_layerSurface->m_surface) - m_layerSurface->m_surface->unmap(); - - g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT); - 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_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT); - - m_fadingOut = true; - - m_mapped = false; - if (m_layerSurface && m_layerSurface->m_surface) - m_layerSurface->m_surface->unmap(); - - g_pCompositor->addToFadingOutSafe(m_self.lock()); - - const auto PMONITOR = m_monitor.lock(); - - const bool WASLASTFOCUS = g_pSeatManager->m_state.keyboardFocus == m_wlSurface->resource() || g_pSeatManager->m_state.pointerFocus == m_wlSurface->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 || - (Desktop::focusState()->surface() && Desktop::focusState()->surface()->m_hlSurface && !Desktop::focusState()->surface()->m_hlSurface->keyboardFocusable())) { - if (!g_pInputManager->refocusLastWindow(PMONITOR)) - g_pInputManager->refocus(); - } else if (Desktop::focusState()->surface() && Desktop::focusState()->surface() != m_wlSurface->resource()) - g_pSeatManager->setKeyboardFocus(Desktop::focusState()->surface()); - - CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height}; - g_pHyprRenderer->damageBox(geomFixed); - - geomFixed = {m_geometry.x + sc(PMONITOR->m_position.x), m_geometry.y + sc(PMONITOR->m_position.y), sc(m_layerSurface->m_surface->m_current.size.x), - sc(m_layerSurface->m_surface->m_current.size.y)}; - g_pHyprRenderer->damageBox(geomFixed); - - g_pInputManager->simulateMouseMovement(); - - g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); -} - -void CLayerSurface::onCommit() { - if (!m_layerSurface) - return; - - if (!m_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 = {}; - g_pHyprRenderer->arrangeLayersForMonitor(monitorID()); - } - - return; - } - - const auto PMONITOR = m_monitor.lock(); - - if (!PMONITOR) - return; - - if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_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}; - g_pHyprRenderer->damageBox(geomFixed); - - if (m_layerSurface->m_current.committed != 0) { - if (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_LAYER && m_layerSurface->m_current.layer != m_layer) { - - const auto NEW_LAYER = std::clamp(m_layerSurface->m_current.layer, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY); - - for (auto it = PMONITOR->m_layerSurfaceLayers[m_layer].begin(); it != PMONITOR->m_layerSurfaceLayers[m_layer].end(); it++) { - if (*it == m_self) { - PMONITOR->m_layerSurfaceLayers[NEW_LAYER].emplace_back(*it); - PMONITOR->m_layerSurfaceLayers[m_layer].erase(it); - break; - } - } - - m_layer = NEW_LAYER; - m_aboveFullscreen = NEW_LAYER >= ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; - - // if in fullscreen, only overlay can be above. - *m_alpha = PMONITOR->inFullscreenMode() ? (m_layer >= ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ? 1.F : 0.F) : 1.F; - - if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) - g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd - } - - g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); - - PMONITOR->m_scheduledRecalc = true; - } else { - m_position = Vector2D(m_geometry.x, m_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) { - // fractional scaling. Dirty hack. - m_geometry = {m_geometry.pos(), m_layerSurface->m_surface->m_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}; - } - } - - if (m_realPosition->goal() != m_geometry.pos()) { - if (m_realPosition->isBeingAnimated()) - *m_realPosition = m_geometry.pos(); - else - m_realPosition->setValueAndWarp(m_geometry.pos()); - } - if (m_realSize->goal() != m_geometry.size()) { - if (m_realSize->isBeingAnimated()) - *m_realSize = m_geometry.size(); - else - m_realSize->setValueAndWarp(m_geometry.size()); - } - - if (m_mapped && (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_INTERACTIVITY)) { - bool WASLASTFOCUS = false; - m_layerSurface->m_surface->breadthfirst( - [&WASLASTFOCUS](SP surf, const Vector2D& offset, void* data) { WASLASTFOCUS = WASLASTFOCUS || g_pSeatManager->m_state.keyboardFocus == surf; }, - nullptr); - if (!WASLASTFOCUS && m_popupHead) { - m_popupHead->breadthfirst( - [&WASLASTFOCUS](WP popup, void* data) { - WASLASTFOCUS = WASLASTFOCUS || (popup->wlSurface() && g_pSeatManager->m_state.keyboardFocus == popup->wlSurface()->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; - - if (!WASEXCLUSIVE && ISEXCLUSIVE) - g_pInputManager->m_exclusiveLSes.push_back(m_self); - else if (WASEXCLUSIVE && !ISEXCLUSIVE) - std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == m_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) { - // moveMouseUnified won't focus non interactive layers but it won't unfocus them either, - // so unfocus the surface here. - Desktop::focusState()->rawSurfaceFocus(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->simulateMouseMovement(); - } else if (!WASEXCLUSIVE && ISEXCLUSIVE) { - // if now exclusive and not previously - g_pSeatManager->setGrab(nullptr); - g_pInputManager->releaseAllMouseButtons(); - Desktop::focusState()->rawSurfaceFocus(m_wlSurface->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_wlSurface->resource(), LOCAL); - g_pInputManager->m_emptyFocusCursorSet = false; - } - } - - m_interactivity = m_layerSurface->m_current.interactivity; - - g_pHyprRenderer->damageSurface(m_wlSurface->resource(), m_position.x, m_position.y); - - g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), PMONITOR->m_scale); - g_pCompositor->setPreferredTransformForSurface(m_wlSurface->resource(), PMONITOR->m_transform); -} - -bool CLayerSurface::isFadedOut() { - if (!m_fadingOut) - return false; - - return !m_realPosition->isBeingAnimated() && !m_realSize->isBeingAnimated() && !m_alpha->isBeingAnimated(); -} - -int CLayerSurface::popupsCount() { - if (!m_layerSurface || !m_mapped || m_fadingOut) - return 0; - - int no = -1; // we have one dummy - m_popupHead->breadthfirst([](WP p, void* data) { *sc(data) += 1; }, &no); - return no; -} - -MONITORID CLayerSurface::monitorID() { - return m_monitor ? m_monitor->m_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()->client()) - return -1; - - wl_client_get_credentials(m_layerSurface->m_surface->getResource()->client(), &PID, nullptr, nullptr); - - return PID; -} diff --git a/src/desktop/view/LayerSurface.hpp b/src/desktop/view/LayerSurface.hpp deleted file mode 100644 index 5faa9e5a..00000000 --- a/src/desktop/view/LayerSurface.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include -#include "../../defines.hpp" -#include "WLSurface.hpp" -#include "View.hpp" -#include "../rule/layerRule/LayerRuleApplicator.hpp" -#include "../../helpers/AnimatedVariable.hpp" - -class CLayerShellResource; - -namespace Desktop::View { - - class CLayerSurface : public IView { - public: - static PHLLS create(SP); - static PHLLS fromView(SP); - - private: - CLayerSurface(SP); - - public: - virtual ~CLayerSurface(); - - virtual eViewType type() const; - virtual bool visible() const; - virtual std::optional logicalBox() const; - virtual bool desktopComponent() const; - virtual std::optional surfaceLogicalBox() const; - - bool isFadedOut(); - int popupsCount(); - - PHLANIMVAR m_realPosition; - PHLANIMVAR m_realSize; - PHLANIMVAR m_alpha; - - WP m_layerSurface; - - // the header providing the enum type cannot be imported here - int m_interactivity = 0; - - bool m_mapped = false; - uint32_t m_layer = 0; - - PHLMONITORREF m_monitor; - - bool m_fadingOut = false; - bool m_readyToDelete = false; - bool m_noProcess = false; - bool m_aboveFullscreen = true; - - UP m_ruleApplicator; - - PHLLSREF m_self; - - CBox m_geometry = {0, 0, 0, 0}; - Vector2D m_position; - std::string m_namespace = ""; - SP m_popupHead; - - pid_t getPID(); - - void onDestroy(); - void onMap(); - void onUnmap(); - void onCommit(); - MONITORID monitorID(); - - private: - struct { - CHyprSignalListener destroy; - CHyprSignalListener map; - CHyprSignalListener unmap; - CHyprSignalListener commit; - } m_listeners; - - void registerCallbacks(); - - // For the list lookup - bool operator==(const CLayerSurface& rhs) const { - return m_layerSurface == rhs.m_layerSurface && m_monitor == rhs.m_monitor; - } - }; - - inline bool valid(PHLLS l) { - return l; - } - - inline bool valid(PHLLSREF l) { - return l; - } - - inline bool validMapped(PHLLS l) { - if (!valid(l)) - return false; - return l->aliveAndVisible(); - } - - inline bool validMapped(PHLLSREF l) { - if (!valid(l)) - return false; - return l->aliveAndVisible(); - } - -} diff --git a/src/desktop/view/Popup.cpp b/src/desktop/view/Popup.cpp deleted file mode 100644 index f6f681d5..00000000 --- a/src/desktop/view/Popup.cpp +++ /dev/null @@ -1,500 +0,0 @@ -#include "Popup.hpp" -#include "../../config/ConfigValue.hpp" -#include "../../config/ConfigManager.hpp" -#include "../../Compositor.hpp" -#include "../../protocols/LayerShell.hpp" -#include "../../protocols/XDGShell.hpp" -#include "../../protocols/core/Compositor.hpp" -#include "../../managers/SeatManager.hpp" -#include "../../managers/animation/AnimationManager.hpp" -#include "LayerSurface.hpp" -#include "../../managers/input/InputManager.hpp" -#include "../../managers/eventLoop/EventLoopManager.hpp" -#include "../../render/Renderer.hpp" -#include "../../render/OpenGL.hpp" -#include - -using namespace Desktop; -using namespace Desktop::View; - -SP CPopup::create(PHLWINDOW pOwner) { - auto popup = SP(new CPopup()); - popup->m_windowOwner = pOwner; - popup->m_self = popup; - popup->initAllSignals(); - return popup; -} - -SP CPopup::create(PHLLS pOwner) { - auto popup = SP(new CPopup()); - popup->m_layerOwner = pOwner; - popup->m_self = popup; - popup->initAllSignals(); - return popup; -} - -SP CPopup::create(SP resource, WP pOwner) { - auto popup = SP(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->wlSurface()->assign(resource->m_surface->m_surface.lock(), popup); - - popup->m_lastSize = resource->m_surface->m_current.geometry.size(); - popup->reposition(); - - popup->initAllSignals(); - return popup; -} - -SP CPopup::fromView(SP v) { - if (!v || v->type() != VIEW_TYPE_POPUP) - return nullptr; - return dynamicPointerCast(v); -} - -CPopup::CPopup() : IView(CWLSurface::create()) { - ; -} - -CPopup::~CPopup() { - if (m_wlSurface) - m_wlSurface->unassign(); -} - -eViewType CPopup::type() const { - return VIEW_TYPE_POPUP; -} - -bool CPopup::visible() const { - if ((!m_mapped || !m_wlSurface->resource()) && (!m_fadingOut || m_alpha->value() > 0.F)) - return false; - - if (!m_windowOwner.expired()) - return g_pHyprRenderer->shouldRenderWindow(m_windowOwner.lock()); - - if (!m_layerOwner.expired()) - return true; - - if (m_parent) - return m_parent->visible(); - - return false; -} - -std::optional CPopup::logicalBox() const { - return surfaceLogicalBox(); -} - -std::optional CPopup::surfaceLogicalBox() const { - if (!visible()) - return std::nullopt; - - return CBox{coordsGlobal(), size()}; -} - -bool CPopup::desktopComponent() const { - return true; -} - -void CPopup::initAllSignals() { - - g_pAnimationManager->createAnimation(0.f, m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadePopupsIn"), AVARDAMAGE_NONE); - m_alpha->setUpdateCallback([this](auto) { - // - g_pHyprRenderer->damageBox(CBox{coordsGlobal(), size()}); - }); - m_alpha->setCallbackOnEnd( - [this](auto) { - if (inert()) { - g_pEventLoopManager->doLater([p = m_self] { - if (!p) - return; - g_pHyprRenderer->damageBox(CBox{p->coordsGlobal(), p->size()}); - p->fullyDestroy(); - }); - } - }, - false); - - 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); }); - 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_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); }); -} - -void CPopup::onNewPopup(SP popup) { - const auto& POPUP = m_children.emplace_back(CPopup::create(popup, m_self)); - POPUP->m_self = POPUP; - Log::logger->log(Log::DEBUG, "New popup at {:x}", rc(this)); -} - -void CPopup::onDestroy() { - m_inert = true; - - if (!m_parent) - return; // head node - - m_subsurfaceHead.reset(); - m_children.clear(); - m_wlSurface.reset(); - - m_listeners.map.reset(); - m_listeners.unmap.reset(); - m_listeners.commit.reset(); - m_listeners.newPopup.reset(); - - if (m_fadingOut && m_alpha->isBeingAnimated()) { - Log::logger->log(Log::DEBUG, "popup {:x}: skipping full destroy, animating", rc(this)); - return; - } - - fullyDestroy(); -} - -void CPopup::fullyDestroy() { - Log::logger->log(Log::DEBUG, "popup {:x} fully destroying", rc(this)); - - g_pHyprRenderer->makeEGLCurrent(); - std::erase_if(g_pHyprOpenGL->m_popupFramebuffers, [&](const auto& other) { return other.first.expired() || other.first == m_self; }); - - std::erase_if(m_parent->m_children, [this](const auto& other) { return other.get() == this; }); -} - -void CPopup::onMap() { - if (m_mapped) - return; - - m_mapped = true; - m_lastSize = m_resource->m_surface->m_surface->m_current.size; - - const auto COORDS = coordsGlobal(); - const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS); - - CBox box = m_wlSurface->resource()->extends(); - box.translate(COORDS).expand(4); - g_pHyprRenderer->damageBox(box); - - m_lastPos = coordsRelativeToParent(); - - g_pInputManager->simulateMouseMovement(); - - m_subsurfaceHead = CSubsurface::create(m_self); - - //unconstrain(); - sendScale(); - m_wlSurface->resource()->breadthfirst([PMONITOR](SP s, const Vector2D& offset, void* d) { s->enter(PMONITOR->m_self.lock()); }, nullptr); - - if (!m_layerOwner.expired() && m_layerOwner->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP) - g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_layerOwner->m_layer)); - - m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadePopupsIn")); - m_alpha->setValueAndWarp(0.F); - *m_alpha = 1.F; - - Log::logger->log(Log::DEBUG, "popup {:x}: mapped", rc(this)); -} - -void CPopup::onUnmap() { - if (!m_mapped) - return; - - if (!m_resource || !m_resource->m_surface) { - Log::logger->log(Log::ERR, "CPopup: orphaned (no surface/resource) and unmaps??"); - onDestroy(); - return; - } - - Log::logger->log(Log::DEBUG, "popup {:x}: unmapped", rc(this)); - - // 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_lastPos = coordsRelativeToParent(); - - const auto COORDS = coordsGlobal(); - - CBox box = m_wlSurface->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_lastSize = MAX_DAMAGE_SIZE; - - g_pHyprRenderer->makeSnapshot(m_self); - - m_fadingOut = true; - m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadePopupsOut")); - m_alpha->setValueAndWarp(1.F); - *m_alpha = 0.F; - - m_mapped = false; - - 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)); - - // damage all children - breadthfirst( - [](WP p, void* data) { - if (!p->m_resource) - return; - - auto box = CBox{p->coordsGlobal(), p->size()}; - g_pHyprRenderer->damageBox(box); - }, - nullptr); - - // TODO: probably refocus, but without a motion event? - // const bool WASLASTFOCUS = g_pSeatManager->state.keyboardFocus == m_pWLSurface->resource() || g_pSeatManager->state.pointerFocus == m_pWLSurface->resource(); - - // if (WASLASTFOCUS) - // g_pInputManager->simulateMouseMovement(); -} - -void CPopup::onCommit(bool ignoreSiblings) { - if (!m_resource || !m_resource->m_surface) { - Log::logger->log(Log::ERR, "CPopup: orphaned (no surface/resource) and commits??"); - onDestroy(); - return; - } - - if (m_resource->m_surface->m_initialCommit) { - m_resource->m_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; - - static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); - if (*PLOGDAMAGE) - Log::logger->log(Log::DEBUG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowOwner.lock()); - return; - } - - if (!m_resource->m_surface->m_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}; - g_pHyprRenderer->damageBox(box); - m_lastSize = m_resource->m_surface->m_surface->m_current.size; - box = {COORDS, m_lastSize}; - g_pHyprRenderer->damageBox(box); - - m_lastPos = COORDSLOCAL; - } - - if (!ignoreSiblings && m_subsurfaceHead) - m_subsurfaceHead->recheckDamageForSubsurfaces(); - - g_pHyprRenderer->damageSurface(m_wlSurface->resource(), COORDS.x, COORDS.y); - - m_requestedReposition = 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)); -} - -void CPopup::onReposition() { - Log::logger->log(Log::DEBUG, "Popup {:x} requests reposition", rc(this)); - - m_requestedReposition = true; - - m_lastPos = coordsRelativeToParent(); - - reposition(); -} - -void CPopup::reposition() { - const auto COORDS = t1ParentCoords(); - const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS); - - if (!PMONITOR) - return; - - m_resource->applyPositioning(m_windowOwner ? PMONITOR->logicalBoxMinusReserved() : PMONITOR->logicalBox(), COORDS); -} - -SP CPopup::getT1Owner() const { - if (m_windowOwner) - return m_windowOwner->wlSurface(); - else - return m_layerOwner->wlSurface(); -} - -Vector2D CPopup::coordsRelativeToParent() const { - Vector2D offset; - - if (!m_resource) - return m_lastPos; - - WP current = m_self; - offset -= current->m_resource->m_surface->m_current.geometry.pos(); - - while (current->m_parent && current->m_resource) { - - offset += current->wlSurface()->resource()->m_current.offset; - offset += current->m_resource->m_geometry.pos(); - - current = current->m_parent; - } - - return offset; -} - -Vector2D CPopup::coordsGlobal() const { - return localToGlobal(coordsRelativeToParent()); -} - -Vector2D CPopup::localToGlobal(const Vector2D& rel) const { - return t1ParentCoords() + rel; -} - -Vector2D CPopup::t1ParentCoords() const { - if (!m_windowOwner.expired()) - return m_windowOwner->m_realPosition->value(); - if (!m_layerOwner.expired()) - return m_layerOwner->m_realPosition->value(); - - ASSERT(false); - return {}; -} - -void CPopup::recheckTree() { - WP curr = m_self; - while (curr->m_parent) { - curr = curr->m_parent; - } - - curr->recheckChildrenRecursive(); -} - -void CPopup::recheckChildrenRecursive() { - if (m_inert || !m_wlSurface) - return; - - std::vector> cpy; - std::ranges::for_each(m_children, [&cpy](const auto& el) { cpy.emplace_back(el); }); - for (auto const& c : cpy) { - if (!c || !c->visible()) - continue; - - // keep ref, onCommit can call onDestroy - auto x = c.lock(); - - x->onCommit(true); - x->recheckChildrenRecursive(); - } -} - -Vector2D CPopup::size() const { - return m_lastSize; -} - -void CPopup::sendScale() { - if (!m_windowOwner.expired()) - g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_windowOwner->wlSurface()->m_lastScaleFloat); - else if (!m_layerOwner.expired()) - g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_layerOwner->wlSurface()->m_lastScaleFloat); - else - UNREACHABLE(); -} - -void CPopup::bfHelper(std::vector> const& nodes, std::function, void*)> fn, void* data) { - for (auto const& n : nodes) { - fn(n, data); - } - - std::vector> nodes2; - nodes2.reserve(nodes.size() * 2); - - for (auto const& n : nodes) { - if (!n) - continue; - - for (auto const& c : n->m_children) { - nodes2.emplace_back(c->m_self.lock()); - } - } - - if (!nodes2.empty()) - bfHelper(nodes2, fn, data); -} - -void CPopup::breadthfirst(std::function, void*)> fn, void* data) { - if (!m_self) - return; - - std::vector> popups; - popups.emplace_back(m_self.lock()); - bfHelper(popups, fn, data); -} - -SP CPopup::at(const Vector2D& globalCoords, bool allowsInput) { - std::vector> popups; - breadthfirst([&popups](SP popup, void* data) { popups.push_back(popup); }, &popups); - - for (auto const& p : popups | std::views::reverse) { - if (!p->m_resource || !p->m_mapped) - continue; - - if (!allowsInput) { - const bool HASSURFACE = p->m_resource && p->m_resource->m_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(); - - if (size == Vector2D{}) - size = p->size(); - - const auto BOX = CBox{p->coordsGlobal() + offset, size}; - if (BOX.containsPoint(globalCoords)) - return p; - } else { - const auto REGION = CRegion{p->wlSurface()->resource()->m_current.input}.intersect(CBox{{}, p->wlSurface()->resource()->m_current.size}).translate(p->coordsGlobal()); - if (REGION.containsPoint(globalCoords)) - return p; - } - } - - return {}; -} - -bool CPopup::inert() const { - return m_inert; -} - -PHLMONITOR CPopup::getMonitor() const { - if (!m_windowOwner.expired()) - return m_windowOwner->m_monitor.lock(); - if (!m_layerOwner.expired()) - return m_layerOwner->m_monitor.lock(); - return nullptr; -} diff --git a/src/desktop/view/Popup.hpp b/src/desktop/view/Popup.hpp deleted file mode 100644 index 1654fc60..00000000 --- a/src/desktop/view/Popup.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include -#include "Subsurface.hpp" -#include "View.hpp" -#include "../../helpers/signal/Signal.hpp" -#include "../../helpers/memory/Memory.hpp" -#include "../../helpers/AnimatedVariable.hpp" - -class CXDGPopupResource; - -namespace Desktop::View { - - class CPopup : public IView { - public: - // dummy head nodes - static SP create(PHLWINDOW pOwner); - static SP create(PHLLS pOwner); - - // real nodes - static SP create(SP popup, WP pOwner); - - static SP fromView(SP); - - virtual ~CPopup(); - - virtual eViewType type() const; - virtual bool visible() const; - virtual std::optional logicalBox() const; - virtual bool desktopComponent() const; - virtual std::optional surfaceLogicalBox() const; - - SP getT1Owner() const; - Vector2D coordsRelativeToParent() const; - Vector2D coordsGlobal() const; - PHLMONITOR getMonitor() const; - - Vector2D size() const; - - void onNewPopup(SP popup); - void onDestroy(); - void onMap(); - void onUnmap(); - void onCommit(bool ignoreSiblings = false); - void onReposition(); - - void recheckTree(); - - bool inert() const; - - // will also loop over this node - void breadthfirst(std::function, void*)> fn, void* data); - SP at(const Vector2D& globalCoords, bool allowsInput = false); - - // - WP m_self; - bool m_mapped = false; - - // fade in-out - PHLANIMVAR m_alpha; - bool m_fadingOut = false; - - private: - CPopup(); - - // T1 owners, each popup has to have one of these - PHLWINDOWREF m_windowOwner; - PHLLSREF m_layerOwner; - - // T2 owners - WP m_parent; - - WP m_resource; - - Vector2D m_lastSize = {}; - Vector2D m_lastPos = {}; - - bool m_requestedReposition = false; - - bool m_inert = false; - - // - std::vector> m_children; - SP m_subsurfaceHead; - - struct { - CHyprSignalListener newPopup; - CHyprSignalListener destroy; - CHyprSignalListener map; - CHyprSignalListener unmap; - CHyprSignalListener commit; - CHyprSignalListener dismissed; - CHyprSignalListener reposition; - } m_listeners; - - void initAllSignals(); - void reposition(); - void recheckChildrenRecursive(); - void sendScale(); - void fullyDestroy(); - - Vector2D localToGlobal(const Vector2D& rel) const; - Vector2D t1ParentCoords() const; - static void bfHelper(std::vector> const& nodes, std::function, void*)> fn, void* data); - }; -} diff --git a/src/desktop/view/SessionLock.cpp b/src/desktop/view/SessionLock.cpp deleted file mode 100644 index a4a5b78b..00000000 --- a/src/desktop/view/SessionLock.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "SessionLock.hpp" - -#include "../../protocols/SessionLock.hpp" -#include "../../protocols/core/Compositor.hpp" -#include "../../helpers/Monitor.hpp" - -#include "../../Compositor.hpp" - -using namespace Desktop; -using namespace Desktop::View; - -SP View::CSessionLock::create(SP resource) { - auto lock = SP(new CSessionLock()); - lock->m_surface = resource; - lock->m_self = lock; - - lock->init(); - - return lock; -} - -View::CSessionLock::CSessionLock() : IView(CWLSurface::create()) { - ; -} - -View::CSessionLock::~CSessionLock() { - m_wlSurface->unassign(); -} - -void View::CSessionLock::init() { - m_listeners.destroy = m_surface->m_events.destroy.listen([this] { std::erase_if(g_pCompositor->m_otherViews, [this](const auto& e) { return e == m_self; }); }); - - m_wlSurface->assign(m_surface->surface(), m_self.lock()); -} - -SP View::CSessionLock::fromView(SP v) { - if (!v || v->type() != VIEW_TYPE_LOCK_SCREEN) - return nullptr; - return dynamicPointerCast(v); -} - -eViewType View::CSessionLock::type() const { - return VIEW_TYPE_LOCK_SCREEN; -} - -bool View::CSessionLock::visible() const { - return m_wlSurface && m_wlSurface->resource() && m_wlSurface->resource()->m_mapped; -} - -std::optional View::CSessionLock::logicalBox() const { - return surfaceLogicalBox(); -} - -std::optional View::CSessionLock::surfaceLogicalBox() const { - if (!visible()) - return std::nullopt; - - const auto MON = m_surface->monitor(); - - if (!MON) - return std::nullopt; - - return MON->logicalBox(); -} - -bool View::CSessionLock::desktopComponent() const { - return true; -} - -PHLMONITOR View::CSessionLock::monitor() const { - if (m_surface) - return m_surface->monitor(); - return nullptr; -} diff --git a/src/desktop/view/SessionLock.hpp b/src/desktop/view/SessionLock.hpp deleted file mode 100644 index c6141fb2..00000000 --- a/src/desktop/view/SessionLock.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "../../defines.hpp" -#include -#include "WLSurface.hpp" -#include "View.hpp" - -class CSessionLockSurface; - -namespace Desktop::View { - class CSessionLock : public IView { - public: - static SP create(SP resource); - - static SP fromView(SP); - - virtual ~CSessionLock(); - - virtual eViewType type() const; - virtual bool visible() const; - virtual std::optional logicalBox() const; - virtual bool desktopComponent() const; - virtual std::optional surfaceLogicalBox() const; - - PHLMONITOR monitor() const; - - WP m_self; - - private: - CSessionLock(); - - void init(); - - struct { - CHyprSignalListener destroy; - } m_listeners; - - WP m_surface; - }; -} diff --git a/src/desktop/view/Subsurface.cpp b/src/desktop/view/Subsurface.cpp deleted file mode 100644 index c601c00e..00000000 --- a/src/desktop/view/Subsurface.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include "Subsurface.hpp" -#include "../state/FocusState.hpp" -#include "Window.hpp" -#include "../../config/ConfigValue.hpp" -#include "../../protocols/core/Compositor.hpp" -#include "../../protocols/core/Subcompositor.hpp" -#include "../../render/Renderer.hpp" -#include "../../managers/input/InputManager.hpp" - -using namespace Desktop; -using namespace Desktop::View; - -SP CSubsurface::create(PHLWINDOW pOwner) { - auto subsurface = SP(new CSubsurface()); - subsurface->m_windowParent = pOwner; - subsurface->m_self = subsurface; - - subsurface->initSignals(); - subsurface->initExistingSubsurfaces(pOwner->wlSurface()->resource()); - return subsurface; -} - -SP CSubsurface::create(WP pOwner) { - auto subsurface = SP(new CSubsurface()); - subsurface->m_popupParent = pOwner; - subsurface->m_self = subsurface; - subsurface->initSignals(); - subsurface->initExistingSubsurfaces(pOwner->wlSurface()->resource()); - return subsurface; -} - -SP CSubsurface::create(SP pSubsurface, PHLWINDOW pOwner) { - auto subsurface = SP(new CSubsurface()); - subsurface->m_windowParent = pOwner; - subsurface->m_subsurface = pSubsurface; - subsurface->m_self = subsurface; - subsurface->wlSurface() = CWLSurface::create(); - subsurface->wlSurface()->assign(pSubsurface->m_surface.lock(), subsurface); - subsurface->initSignals(); - subsurface->initExistingSubsurfaces(pSubsurface->m_surface.lock()); - return subsurface; -} - -SP CSubsurface::create(SP pSubsurface, WP pOwner) { - auto subsurface = SP(new CSubsurface()); - subsurface->m_popupParent = pOwner; - subsurface->m_subsurface = pSubsurface; - subsurface->m_self = subsurface; - subsurface->wlSurface() = CWLSurface::create(); - subsurface->wlSurface()->assign(pSubsurface->m_surface.lock(), subsurface); - subsurface->initSignals(); - subsurface->initExistingSubsurfaces(pSubsurface->m_surface.lock()); - return subsurface; -} - -SP CSubsurface::fromView(SP v) { - if (!v || v->type() != VIEW_TYPE_SUBSURFACE) - return nullptr; - return dynamicPointerCast(v); -} - -CSubsurface::CSubsurface() : IView(CWLSurface::create()) { - ; -} - -eViewType CSubsurface::type() const { - return VIEW_TYPE_SUBSURFACE; -} - -bool CSubsurface::visible() const { - if (!m_wlSurface || !m_wlSurface->resource() || !m_wlSurface->resource()->m_mapped) - return false; - - if (!m_windowParent.expired()) - return g_pHyprRenderer->shouldRenderWindow(m_windowParent.lock()); - if (m_popupParent) - return m_popupParent->visible(); - if (m_parent) - return m_parent->visible(); - - return false; -} - -bool CSubsurface::desktopComponent() const { - return true; -} - -std::optional CSubsurface::logicalBox() const { - return surfaceLogicalBox(); -} - -std::optional CSubsurface::surfaceLogicalBox() const { - if (!visible()) - return std::nullopt; - - return CBox{coordsGlobal(), m_lastSize}; -} - -void CSubsurface::initSignals() { - if (m_subsurface) { - m_listeners.commitSubsurface = m_subsurface->m_surface->m_events.commit.listen([this] { onCommit(); }); - m_listeners.destroySubsurface = m_subsurface->m_events.destroy.listen([this] { onDestroy(); }); - m_listeners.mapSubsurface = m_subsurface->m_surface->m_events.map.listen([this] { onMap(); }); - m_listeners.unmapSubsurface = m_subsurface->m_surface->m_events.unmap.listen([this] { onUnmap(); }); - m_listeners.newSubsurface = m_subsurface->m_surface->m_events.newSubsurface.listen([this](const auto& resource) { onNewSubsurface(resource); }); - } else { - if (m_windowParent) - m_listeners.newSubsurface = m_windowParent->wlSurface()->resource()->m_events.newSubsurface.listen([this](const auto& resource) { onNewSubsurface(resource); }); - else if (m_popupParent) - m_listeners.newSubsurface = m_popupParent->wlSurface()->resource()->m_events.newSubsurface.listen([this](const auto& resource) { onNewSubsurface(resource); }); - else - ASSERT(false); - } -} - -void CSubsurface::checkSiblingDamage() { - if (!m_parent) - return; // ?????????? - - const double SCALE = m_windowParent.lock() && m_windowParent->m_isX11 ? 1.0 / m_windowParent->m_X11SurfaceScaledBy : 1.0; - - for (auto const& n : m_parent->m_children) { - if (n.get() == this) - continue; - - const auto COORDS = n->coordsGlobal(); - g_pHyprRenderer->damageSurface(n->wlSurface()->resource(), COORDS.x, COORDS.y, SCALE); - } -} - -void CSubsurface::recheckDamageForSubsurfaces() { - for (auto const& n : m_children) { - const auto COORDS = n->coordsGlobal(); - g_pHyprRenderer->damageSurface(n->wlSurface()->resource(), COORDS.x, COORDS.y); - } -} - -void CSubsurface::onCommit() { - // no damaging if it's not visible - if (!m_windowParent.expired() && (!m_windowParent->m_isMapped || !m_windowParent->m_workspace->m_visible)) { - m_lastSize = m_wlSurface->resource()->m_current.size; - - static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); - if (*PLOGDAMAGE) - Log::logger->log(Log::DEBUG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowParent.lock()); - return; - } - - const auto COORDS = coordsGlobal(); - - g_pHyprRenderer->damageSurface(m_wlSurface->resource(), COORDS.x, COORDS.y); - - if (m_popupParent && !m_popupParent->inert() && m_popupParent->wlSurface()) - m_popupParent->recheckTree(); - if (!m_windowParent.expired()) // I hate you firefox why are you doing this - m_windowParent->m_popupHead->recheckTree(); - - // I do not think this is correct, but it solves a lot of issues with some apps (e.g. firefox) - checkSiblingDamage(); - - if (m_lastSize != m_wlSurface->resource()->m_current.size || m_lastPosition != m_subsurface->m_position) { - damageLastArea(); - m_lastSize = m_wlSurface->resource()->m_current.size; - m_lastPosition = m_subsurface->m_position; - } -} - -void CSubsurface::onDestroy() { - // destroy children - m_children.clear(); - - m_inert = true; - - if (!m_subsurface) - return; // dummy node, nothing to do, it's the parent dying - - // kill ourselves - std::erase_if(m_parent->m_children, [this](const auto& other) { return other.get() == this; }); -} - -void CSubsurface::onNewSubsurface(SP pSubsurface) { - WP PSUBSURFACE; - - if (!m_windowParent.expired()) - PSUBSURFACE = m_children.emplace_back(CSubsurface::create(pSubsurface, m_windowParent.lock())); - else if (m_popupParent) - PSUBSURFACE = m_children.emplace_back(CSubsurface::create(pSubsurface, m_popupParent)); - - PSUBSURFACE->m_self = PSUBSURFACE; - - ASSERT(PSUBSURFACE); - - PSUBSURFACE->m_parent = m_self; -} - -void CSubsurface::onMap() { - m_lastSize = m_wlSurface->resource()->m_current.size; - m_lastPosition = m_subsurface->m_position; - - const auto COORDS = coordsGlobal(); - CBox box{COORDS, m_lastSize}; - box.expand(4); - g_pHyprRenderer->damageBox(box); - - if (!m_windowParent.expired()) - m_windowParent->updateSurfaceScaleTransformDetails(); -} - -void CSubsurface::onUnmap() { - damageLastArea(); - - if (m_wlSurface->resource() == Desktop::focusState()->surface()) - g_pInputManager->releaseAllMouseButtons(); - - g_pInputManager->simulateMouseMovement(); - - // TODO: should this remove children? Currently it won't, only on .destroy -} - -void CSubsurface::damageLastArea() { - const auto COORDS = coordsGlobal() + m_lastPosition - m_subsurface->m_position; - - const Vector2D MAX_DAMAGE_SIZE = m_wlSurface && m_wlSurface->resource() ? - Vector2D{ - std::max(m_lastSize.x, m_wlSurface->resource()->m_current.size.x), - std::max(m_lastSize.y, m_wlSurface->resource()->m_current.size.y), - } : - m_lastSize; - - CBox box{COORDS, m_lastSize}; - box.expand(4); - g_pHyprRenderer->damageBox(box); -} - -Vector2D CSubsurface::coordsRelativeToParent() const { - if (!m_subsurface) - return {}; - return m_subsurface->posRelativeToParent(); -} - -Vector2D CSubsurface::coordsGlobal() const { - Vector2D coords = coordsRelativeToParent(); - - if (!m_windowParent.expired()) - coords += m_windowParent->m_realPosition->value(); - else if (m_popupParent) - coords += m_popupParent->coordsGlobal(); - - return coords; -} - -void CSubsurface::initExistingSubsurfaces(SP pSurface) { - for (auto const& s : pSurface->m_subsurfaces) { - if (!s || s->m_surface->m_hlSurface /* already assigned */) - continue; - onNewSubsurface(s.lock()); - } -} - -Vector2D CSubsurface::size() { - return m_wlSurface->resource()->m_current.size; -} diff --git a/src/desktop/view/Subsurface.hpp b/src/desktop/view/Subsurface.hpp deleted file mode 100644 index ab74f48c..00000000 --- a/src/desktop/view/Subsurface.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include "../../defines.hpp" -#include -#include "WLSurface.hpp" -#include "View.hpp" - -class CWLSubsurfaceResource; - -namespace Desktop::View { - class CPopup; - class CSubsurface : public IView { - public: - // root dummy nodes - static SP create(PHLWINDOW pOwner); - static SP create(WP pOwner); - - // real nodes - static SP create(SP pSubsurface, PHLWINDOW pOwner); - static SP create(SP pSubsurface, WP pOwner); - - static SP fromView(SP); - - virtual ~CSubsurface() = default; - - virtual eViewType type() const; - virtual bool visible() const; - virtual std::optional logicalBox() const; - virtual bool desktopComponent() const; - virtual std::optional surfaceLogicalBox() const; - - Vector2D coordsRelativeToParent() const; - Vector2D coordsGlobal() const; - - Vector2D size(); - - void onCommit(); - void onDestroy(); - void onNewSubsurface(SP pSubsurface); - void onMap(); - void onUnmap(); - - void recheckDamageForSubsurfaces(); - - WP m_self; - - private: - CSubsurface(); - - struct { - CHyprSignalListener destroySubsurface; - CHyprSignalListener commitSubsurface; - CHyprSignalListener mapSubsurface; - CHyprSignalListener unmapSubsurface; - CHyprSignalListener newSubsurface; - } m_listeners; - - WP m_subsurface; - Vector2D m_lastSize = {}; - Vector2D m_lastPosition = {}; - - // if nullptr, means it's a dummy node - WP m_parent; - - PHLWINDOWREF m_windowParent; - WP m_popupParent; - - std::vector> m_children; - - bool m_inert = false; - - void initSignals(); - void initExistingSubsurfaces(SP pSurface); - void checkSiblingDamage(); - void damageLastArea(); - }; -} diff --git a/src/desktop/view/View.cpp b/src/desktop/view/View.cpp deleted file mode 100644 index 17a10c64..00000000 --- a/src/desktop/view/View.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "View.hpp" -#include "../../protocols/core/Compositor.hpp" - -using namespace Desktop; -using namespace Desktop::View; - -SP IView::wlSurface() const { - return m_wlSurface; -} - -IView::IView(SP pWlSurface) : m_wlSurface(pWlSurface) { - ; -} - -SP IView::resource() const { - return m_wlSurface ? m_wlSurface->resource() : nullptr; -} - -bool IView::aliveAndVisible() const { - auto res = resource(); - if (!res) - return false; - - if (!res->m_mapped) - return false; - - return visible(); -} diff --git a/src/desktop/view/View.hpp b/src/desktop/view/View.hpp deleted file mode 100644 index 4d777c36..00000000 --- a/src/desktop/view/View.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "WLSurface.hpp" -#include "../../helpers/math/Math.hpp" - -namespace Desktop::View { - enum eViewType : uint8_t { - VIEW_TYPE_WINDOW = 0, - VIEW_TYPE_SUBSURFACE, - VIEW_TYPE_POPUP, - VIEW_TYPE_LAYER_SURFACE, - VIEW_TYPE_LOCK_SCREEN, - }; - - class IView { - public: - virtual ~IView() = default; - - virtual SP wlSurface() const; - virtual SP resource() const; - virtual bool aliveAndVisible() const; - virtual eViewType type() const = 0; - virtual bool visible() const = 0; - virtual bool desktopComponent() const = 0; - virtual std::optional logicalBox() const = 0; - virtual std::optional surfaceLogicalBox() const = 0; - - protected: - IView(SP pWlSurface); - - SP m_wlSurface; - }; -}; \ No newline at end of file diff --git a/src/desktop/view/WLSurface.cpp b/src/desktop/view/WLSurface.cpp deleted file mode 100644 index ae8a22e2..00000000 --- a/src/desktop/view/WLSurface.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include "WLSurface.hpp" -#include "LayerSurface.hpp" -#include "Window.hpp" -#include "../../protocols/core/Compositor.hpp" -#include "../../protocols/LayerShell.hpp" -#include "../../render/Renderer.hpp" - -using namespace Desktop; -using namespace Desktop::View; - -void CWLSurface::assign(SP pSurface) { - m_resource = pSurface; - init(); - m_inert = false; -} - -void CWLSurface::assign(SP pSurface, SP pOwner) { - m_view = pOwner; - m_resource = pSurface; - init(); - m_inert = false; -} - -void CWLSurface::unassign() { - destroy(); -} - -CWLSurface::~CWLSurface() { - destroy(); -} - -bool CWLSurface::exists() const { - return m_resource; -} - -SP CWLSurface::resource() const { - return m_resource.lock(); -} - -bool CWLSurface::small() const { - if (!m_view || !m_view->aliveAndVisible() || m_view->type() != VIEW_TYPE_WINDOW || !exists()) - return false; - - if (!m_resource->m_current.texture) - return false; - - const auto O = dynamicPointerCast(m_view.lock()); - const auto REPORTED_SIZE = O->getReportedSize(); - - return REPORTED_SIZE.x > m_resource->m_current.size.x + 1 || REPORTED_SIZE.y > m_resource->m_current.size.y + 1; -} - -Vector2D CWLSurface::correctSmallVec() const { - if (!m_view || !m_view->aliveAndVisible() || m_view->type() != VIEW_TYPE_WINDOW || !exists() || !small() || !m_fillIgnoreSmall) - return {}; - - const auto SIZE = getViewporterCorrectedSize(); - const auto O = dynamicPointerCast(m_view.lock()); - const auto REP = O->getReportedSize(); - - return Vector2D{(REP.x - SIZE.x) / 2, (REP.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}) * (O->m_realSize->value() / REP); -} - -Vector2D CWLSurface::correctSmallVecBuf() const { - if (!exists() || !small() || m_fillIgnoreSmall || !m_resource->m_current.texture) - return {}; - - const auto SIZE = getViewporterCorrectedSize(); - const auto BS = m_resource->m_current.bufferSize; - - return Vector2D{(BS.x - SIZE.x) / 2, (BS.y - SIZE.y) / 2}.clamp({}, {INFINITY, INFINITY}); -} - -Vector2D CWLSurface::getViewporterCorrectedSize() const { - if (!exists() || !m_resource->m_current.texture) - return {}; - - return m_resource->m_current.viewport.hasDestination ? m_resource->m_current.viewport.destination : m_resource->m_current.bufferSize; -} - -CRegion CWLSurface::computeDamage() const { - if (!m_resource->m_current.texture) - return {}; - - CRegion damage = m_resource->m_current.accumulateBufferDamage(); - damage.transform(Math::wlTransformToHyprutils(m_resource->m_current.transform), m_resource->m_current.bufferSize.x, m_resource->m_current.bufferSize.y); - - const auto BUFSIZE = m_resource->m_current.bufferSize; - const auto CORRECTVEC = correctSmallVecBuf(); - - if (m_resource->m_current.viewport.hasSource) - damage.intersect(m_resource->m_current.viewport.source); - - const auto SCALEDSRCSIZE = - m_resource->m_current.viewport.hasSource ? m_resource->m_current.viewport.source.size() * m_resource->m_current.scale : m_resource->m_current.bufferSize; - - damage.scale({BUFSIZE.x / SCALEDSRCSIZE.x, BUFSIZE.y / SCALEDSRCSIZE.y}); - damage.translate(CORRECTVEC); - - // go from buffer coords in the damage to hl logical - - const auto BOX = getSurfaceBoxGlobal(); - const auto SURFSIZE = m_resource->m_current.size; - const Vector2D SCALE = SURFSIZE / m_resource->m_current.bufferSize; - - damage.scale(SCALE); - if (BOX.has_value()) { - if (m_view->type() == VIEW_TYPE_WINDOW) - damage.intersect(CBox{{}, BOX->size() * dynamicPointerCast(m_view.lock())->m_X11SurfaceScaledBy}); - else - damage.intersect(CBox{{}, BOX->size()}); - } - - return damage; -} - -void CWLSurface::destroy() { - if (!m_resource) - return; - - m_events.destroy.emit(); - - m_constraint.reset(); - - m_listeners.destroy.reset(); - m_resource->m_hlSurface.reset(); - m_view.reset(); - m_inert = true; - - if (g_pHyprRenderer && g_pHyprRenderer->m_lastCursorData.surf && g_pHyprRenderer->m_lastCursorData.surf->get() == this) - g_pHyprRenderer->m_lastCursorData.surf.reset(); - - m_resource.reset(); - - Log::logger->log(Log::DEBUG, "CWLSurface {:x} called destroy()", rc(this)); -} - -void CWLSurface::init() { - if (!m_resource) - return; - - RASSERT(!m_resource->m_hlSurface, "Attempted to duplicate CWLSurface ownership!"); - - m_resource->m_hlSurface = m_self.lock(); - - m_listeners.destroy = m_resource->m_events.destroy.listen([this] { destroy(); }); - - Log::logger->log(Log::DEBUG, "CWLSurface {:x} called init()", rc(this)); -} - -SP CWLSurface::view() const { - return m_view.lock(); -} - -bool CWLSurface::desktopComponent() const { - return m_view && m_view->visible(); -} - -std::optional CWLSurface::getSurfaceBoxGlobal() const { - if (!desktopComponent()) - return {}; - - return m_view->surfaceLogicalBox(); -} - -void CWLSurface::appendConstraint(WP constraint) { - m_constraint = constraint; -} - -SP CWLSurface::constraint() const { - return m_constraint.lock(); -} - -SP CWLSurface::fromResource(SP pSurface) { - if (!pSurface) - return nullptr; - return pSurface->m_hlSurface.lock(); -} - -bool CWLSurface::keyboardFocusable() const { - if (!m_view) - return false; - if (m_view->type() == VIEW_TYPE_WINDOW || m_view->type() == VIEW_TYPE_SUBSURFACE || m_view->type() == VIEW_TYPE_POPUP) - return true; - if (const auto LS = CLayerSurface::fromView(m_view.lock()); LS && LS->m_layerSurface) - return LS->m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; - return false; -} diff --git a/src/desktop/view/WLSurface.hpp b/src/desktop/view/WLSurface.hpp deleted file mode 100644 index 3c5e3a38..00000000 --- a/src/desktop/view/WLSurface.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include "../../defines.hpp" -#include "../../helpers/math/Math.hpp" -#include "../../helpers/signal/Signal.hpp" - -class CPointerConstraint; -class CWLSurfaceResource; - -namespace Desktop::View { - class CSubsurface; - class CPopup; - class IView; - - class CWLSurface { - public: - static SP create() { - auto p = SP(new CWLSurface); - p->m_self = p; - return p; - } - ~CWLSurface(); - - // anonymous surfaces are non-desktop components, e.g. a cursor surface or a DnD - void assign(SP pSurface); - void assign(SP pSurface, SP pOwner); - void unassign(); - - CWLSurface(const CWLSurface&) = delete; - CWLSurface(CWLSurface&&) = delete; - CWLSurface& operator=(const CWLSurface&) = delete; - CWLSurface& operator=(CWLSurface&&) = delete; - - SP resource() const; - bool exists() const; - bool small() const; // means surface is smaller than the requested size - Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces - Vector2D correctSmallVecBuf() const; // returns a corrective vector for small() surfaces, in BL coords - Vector2D getViewporterCorrectedSize() const; - CRegion computeDamage() const; // logical coordinates. May be wrong if the surface is unassigned - bool keyboardFocusable() const; - - SP view() const; - - // desktop components misc utils - std::optional getSurfaceBoxGlobal() const; - void appendConstraint(WP constraint); - SP constraint() const; - - // allow stretching. Useful for plugins. - bool m_fillIgnoreSmall = false; - - // track surface data and avoid dupes - float m_lastScaleFloat = 0; - int m_lastScaleInt = 0; - wl_output_transform m_lastTransform = sc(-1); - - // - CWLSurface& operator=(SP pSurface) { - destroy(); - m_resource = pSurface; - init(); - - return *this; - } - - bool operator==(const CWLSurface& other) const { - return other.resource() == resource(); - } - - bool operator==(const SP other) const { - return other == resource(); - } - - explicit operator bool() const { - return exists(); - } - - static SP fromResource(SP pSurface); - - // used by the alpha-modifier protocol - float m_alphaModifier = 1.F; - - // used by the hyprland-surface protocol - float m_overallOpacity = 1.F; - CRegion m_visibleRegion; - - struct { - CSignalT<> destroy; - } m_events; - - WP m_self; - - private: - CWLSurface() = default; - - bool m_inert = true; - - WP m_resource; - - WP m_view; - - // - WP m_constraint; - - void destroy(); - void init(); - bool desktopComponent() const; - - struct { - CHyprSignalListener destroy; - } m_listeners; - - friend class ::CPointerConstraint; - }; -} diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp deleted file mode 100644 index abf4da95..00000000 --- a/src/desktop/view/Window.cpp +++ /dev/null @@ -1,2508 +0,0 @@ -#include -#include -#include -#include - -#include "Group.hpp" - -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) -#include -#include -#endif - -#include -#include -#include -#include -#include "Window.hpp" -#include "LayerSurface.hpp" -#include "../state/FocusState.hpp" -#include "../history/WindowHistoryTracker.hpp" -#include "../../Compositor.hpp" -#include "../../render/decorations/CHyprDropShadowDecoration.hpp" -#include "../../render/decorations/CHyprGroupBarDecoration.hpp" -#include "../../render/decorations/CHyprBorderDecoration.hpp" -#include "../../config/ConfigValue.hpp" -#include "../../config/ConfigManager.hpp" -#include "../../managers/TokenManager.hpp" -#include "../../managers/animation/AnimationManager.hpp" -#include "../../managers/ANRManager.hpp" -#include "../../managers/eventLoop/EventLoopManager.hpp" -#include "../../protocols/XDGShell.hpp" -#include "../../protocols/core/Compositor.hpp" -#include "../../protocols/core/Subcompositor.hpp" -#include "../../protocols/ContentType.hpp" -#include "../../protocols/FractionalScale.hpp" -#include "../../protocols/LayerShell.hpp" -#include "../../xwayland/XWayland.hpp" -#include "../../helpers/Color.hpp" -#include "../../helpers/math/Expression.hpp" -#include "../../managers/XWaylandManager.hpp" -#include "../../render/Renderer.hpp" -#include "../../managers/EventManager.hpp" -#include "../../managers/input/InputManager.hpp" -#include "../../managers/PointerManager.hpp" -#include "../../managers/animation/DesktopAnimationManager.hpp" -#include "../../layout/space/Space.hpp" -#include "../../layout/LayoutManager.hpp" -#include "../../layout/target/WindowTarget.hpp" -#include "../../layout/target/WindowGroupTarget.hpp" -#include "../../event/EventBus.hpp" - -#include - -using namespace Hyprutils::String; -using namespace Hyprutils::Animation; -using enum NContentType::eContentType; - -using namespace Desktop; -using namespace Desktop::View; - -// I wish I had an elven wife instead of a windowIDCounter -static uint64_t windowIDCounter = 0x18000000; - -// -#define COMMA , -// - -PHLWINDOW CWindow::create(SP surface) { - PHLWINDOW pWindow = SP(new CWindow(surface)); - - pWindow->m_self = pWindow; - pWindow->m_isX11 = true; - pWindow->m_ruleApplicator = makeUnique(pWindow); - - g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.f, pWindow->m_borderFadeAnimationProgress, g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_BORDER); - g_pAnimationManager->createAnimation(0.f, pWindow->m_borderAngleAnimationProgress, g_pConfigManager->getAnimationPropertyConfig("borderangle"), pWindow, AVARDAMAGE_BORDER); - g_pAnimationManager->createAnimation(1.f, pWindow->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(1.f, pWindow->m_activeInactiveAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(CHyprColor(), pWindow->m_realShadowColor, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); - g_pAnimationManager->createAnimation(0.f, pWindow->m_dimPercent, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.f, pWindow->m_movingToWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.f, pWindow->m_movingFromWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.f, pWindow->m_notRespondingTint, g_pConfigManager->getAnimationPropertyConfig("fade"), pWindow, AVARDAMAGE_ENTIRE); - - pWindow->addWindowDeco(makeUnique(pWindow)); - pWindow->addWindowDeco(makeUnique(pWindow)); - - pWindow->m_target = Layout::CWindowTarget::create(pWindow); - - return pWindow; -} - -PHLWINDOW CWindow::create(SP resource) { - PHLWINDOW pWindow = SP(new CWindow(resource)); - - pWindow->m_self = pWindow; - resource->m_toplevel->m_window = pWindow; - pWindow->m_ruleApplicator = makeUnique(pWindow); - - g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.f, pWindow->m_borderFadeAnimationProgress, g_pConfigManager->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_BORDER); - g_pAnimationManager->createAnimation(0.f, pWindow->m_borderAngleAnimationProgress, g_pConfigManager->getAnimationPropertyConfig("borderangle"), pWindow, AVARDAMAGE_BORDER); - g_pAnimationManager->createAnimation(1.f, pWindow->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(1.f, pWindow->m_activeInactiveAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(CHyprColor(), pWindow->m_realShadowColor, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), pWindow, AVARDAMAGE_SHADOW); - g_pAnimationManager->createAnimation(0.f, pWindow->m_dimPercent, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.f, pWindow->m_movingToWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeOut"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.f, pWindow->m_movingFromWorkspaceAlpha, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), pWindow, AVARDAMAGE_ENTIRE); - g_pAnimationManager->createAnimation(0.f, pWindow->m_notRespondingTint, g_pConfigManager->getAnimationPropertyConfig("fade"), pWindow, AVARDAMAGE_ENTIRE); - - pWindow->addWindowDeco(makeUnique(pWindow)); - pWindow->addWindowDeco(makeUnique(pWindow)); - - pWindow->m_target = Layout::CWindowTarget::create(pWindow); - - pWindow->wlSurface()->assign(pWindow->m_xdgSurface->m_surface.lock(), pWindow); - - return pWindow; -} - -CWindow::CWindow(SP resource) : IView(CWLSurface::create()), m_xdgSurface(resource), m_stableID(windowIDCounter++) { - m_listeners.map = m_xdgSurface->m_events.map.listen([this] { mapWindow(); }); - m_listeners.ack = m_xdgSurface->m_events.ack.listen([this](uint32_t d) { onAck(d); }); - m_listeners.unmap = m_xdgSurface->m_events.unmap.listen([this] { unmapWindow(); }); - m_listeners.destroy = m_xdgSurface->m_events.destroy.listen([this] { destroyWindow(); }); - m_listeners.commit = m_xdgSurface->m_events.commit.listen([this] { commitWindow(); }); - m_listeners.updateState = m_xdgSurface->m_toplevel->m_events.stateChanged.listen([this] { onUpdateState(); }); - m_listeners.updateMetadata = m_xdgSurface->m_toplevel->m_events.metadataChanged.listen([this] { onUpdateMeta(); }); -} - -CWindow::CWindow(SP surface) : IView(CWLSurface::create()), m_xwaylandSurface(surface), m_stableID(windowIDCounter++) { - m_listeners.map = m_xwaylandSurface->m_events.map.listen([this] { mapWindow(); }); - m_listeners.unmap = m_xwaylandSurface->m_events.unmap.listen([this] { unmapWindow(); }); - m_listeners.destroy = m_xwaylandSurface->m_events.destroy.listen([this] { destroyWindow(); }); - m_listeners.commit = m_xwaylandSurface->m_events.commit.listen([this] { commitWindow(); }); - m_listeners.configureRequest = m_xwaylandSurface->m_events.configureRequest.listen([this](const CBox& box) { onX11ConfigureRequest(box); }); - m_listeners.updateState = m_xwaylandSurface->m_events.stateChanged.listen([this] { onUpdateState(); }); - m_listeners.updateMetadata = m_xwaylandSurface->m_events.metadataChanged.listen([this] { onUpdateMeta(); }); - m_listeners.resourceChange = m_xwaylandSurface->m_events.resourceChange.listen([this] { onResourceChangeX11(); }); - m_listeners.activate = m_xwaylandSurface->m_events.activate.listen([this] { activateX11(); }); - - if (m_xwaylandSurface->m_overrideRedirect) - m_listeners.setGeometry = m_xwaylandSurface->m_events.setGeometry.listen([this] { unmanagedSetGeometry(); }); -} - -SP CWindow::fromView(SP v) { - if (!v || v->type() != VIEW_TYPE_WINDOW) - return nullptr; - return dynamicPointerCast(v); -} - -CWindow::~CWindow() { - if (Desktop::focusState()->window() == m_self) { - Desktop::focusState()->surface().reset(); - Desktop::focusState()->window().reset(); - } - - m_events.destroy.emit(); - - if (!g_pHyprOpenGL) - return; - - g_pHyprRenderer->makeEGLCurrent(); - std::erase_if(g_pHyprOpenGL->m_windowFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.get() == this; }); -} - -eViewType CWindow::type() const { - return VIEW_TYPE_WINDOW; -} - -bool CWindow::visible() const { - return !m_hidden && ((m_isMapped && m_wlSurface && m_wlSurface->resource()) || (m_fadingOut && m_alpha->value() != 0.F)); -} - -std::optional CWindow::logicalBox() const { - return getFullWindowBoundingBox(); -} - -bool CWindow::desktopComponent() const { - return true; -} - -std::optional CWindow::surfaceLogicalBox() const { - return getWindowMainSurfaceBox(); -} - -SBoxExtents CWindow::getFullWindowExtents() const { - if (m_fadingOut) - return m_originalClosedExtents; - - const int BORDERSIZE = getRealBorderSize(); - - if (m_ruleApplicator->dimAround().valueOrDefault()) { - if (const auto PMONITOR = m_monitor.lock(); PMONITOR) - return {.topLeft = {m_realPosition->value().x - PMONITOR->m_position.x, m_realPosition->value().y - PMONITOR->m_position.y}, - .bottomRight = {PMONITOR->m_size.x - (m_realPosition->value().x - PMONITOR->m_position.x), - PMONITOR->m_size.y - (m_realPosition->value().y - PMONITOR->m_position.y)}}; - } - - SBoxExtents maxExtents = {.topLeft = {BORDERSIZE + 2, BORDERSIZE + 2}, .bottomRight = {BORDERSIZE + 2, BORDERSIZE + 2}}; - - const auto EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(m_self); - - maxExtents.topLeft.x = std::max(EXTENTS.topLeft.x, maxExtents.topLeft.x); - - maxExtents.topLeft.y = std::max(EXTENTS.topLeft.y, maxExtents.topLeft.y); - - maxExtents.bottomRight.x = std::max(EXTENTS.bottomRight.x, maxExtents.bottomRight.x); - - maxExtents.bottomRight.y = std::max(EXTENTS.bottomRight.y, maxExtents.bottomRight.y); - - if (m_wlSurface->exists() && !m_isX11 && m_popupHead) { - CBox surfaceExtents = {0, 0, 0, 0}; - // TODO: this could be better, perhaps make a getFullWindowRegion? - m_popupHead->breadthfirst( - [](WP popup, void* data) { - if (!popup->wlSurface() || !popup->wlSurface()->resource()) - return; - - CBox* pSurfaceExtents = sc(data); - CBox surf = CBox{popup->coordsRelativeToParent(), popup->size()}; - pSurfaceExtents->x = std::min(surf.x, pSurfaceExtents->x); - pSurfaceExtents->y = std::min(surf.y, pSurfaceExtents->y); - if (surf.x + surf.w > pSurfaceExtents->width) - pSurfaceExtents->width = surf.x + surf.w - pSurfaceExtents->x; - if (surf.y + surf.h > pSurfaceExtents->height) - pSurfaceExtents->height = surf.y + surf.h - pSurfaceExtents->y; - }, - &surfaceExtents); - - maxExtents.topLeft.x = std::max(-surfaceExtents.x, maxExtents.topLeft.x); - - maxExtents.topLeft.y = std::max(-surfaceExtents.y, maxExtents.topLeft.y); - - if (surfaceExtents.x + surfaceExtents.width > m_wlSurface->resource()->m_current.size.x + maxExtents.bottomRight.x) - maxExtents.bottomRight.x = surfaceExtents.x + surfaceExtents.width - m_wlSurface->resource()->m_current.size.x; - - if (surfaceExtents.y + surfaceExtents.height > m_wlSurface->resource()->m_current.size.y + maxExtents.bottomRight.y) - maxExtents.bottomRight.y = surfaceExtents.y + surfaceExtents.height - m_wlSurface->resource()->m_current.size.y; - } - - return maxExtents; -} - -CBox CWindow::getFullWindowBoundingBox() const { - if (m_ruleApplicator->dimAround().valueOrDefault()) { - if (const auto PMONITOR = m_monitor.lock(); PMONITOR) - return {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y}; - } - - auto maxExtents = getFullWindowExtents(); - - CBox finalBox = {m_realPosition->value().x - maxExtents.topLeft.x, m_realPosition->value().y - maxExtents.topLeft.y, - m_realSize->value().x + maxExtents.topLeft.x + maxExtents.bottomRight.x, m_realSize->value().y + maxExtents.topLeft.y + maxExtents.bottomRight.y}; - - return finalBox; -} - -CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() { - const auto PMONITOR = m_monitor.lock(); - - if (!PMONITOR || !m_workspace) - return {m_position, m_size}; - - auto POS = m_position; - auto SIZE = m_size; - - if (isFullscreen()) { - POS = PMONITOR->m_position; - SIZE = PMONITOR->m_size; - - return CBox{sc(POS.x), sc(POS.y), sc(SIZE.x), sc(SIZE.y)}; - } - - // fucker fucking fuck - const auto WORKAREA = m_workspace->m_space->workArea(); - const auto& RESERVED = CReservedArea(PMONITOR->logicalBox(), WORKAREA); - - if (DELTALESSTHAN(POS.x, WORKAREA.x, 1)) { - POS.x -= RESERVED.left(); - SIZE.x += RESERVED.left(); - } - - if (DELTALESSTHAN(POS.y, WORKAREA.y, 1)) { - POS.y -= RESERVED.top(); - SIZE.y += RESERVED.top(); - } - - if (DELTALESSTHAN(POS.x + SIZE.x, WORKAREA.x + WORKAREA.width, 1)) - SIZE.x += RESERVED.right(); - - if (DELTALESSTHAN(POS.y + SIZE.y, WORKAREA.y + WORKAREA.height, 1)) - SIZE.y += RESERVED.bottom(); - - return CBox{sc(POS.x), sc(POS.y), sc(SIZE.x), sc(SIZE.y)}; -} - -SBoxExtents CWindow::getWindowExtentsUnified(uint64_t properties) { - SBoxExtents extents = {.topLeft = {0, 0}, .bottomRight = {0, 0}}; - if (properties & Desktop::View::RESERVED_EXTENTS) - extents.addExtents(g_pDecorationPositioner->getWindowDecorationReserved(m_self)); - if (properties & Desktop::View::INPUT_EXTENTS) - extents.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(m_self, true)); - if (properties & FULL_EXTENTS) - extents.addExtents(g_pDecorationPositioner->getWindowDecorationExtents(m_self, false)); - - return extents; -} - -CBox CWindow::getWindowBoxUnified(uint64_t properties) { - if (m_ruleApplicator->dimAround().valueOrDefault()) { - const auto PMONITOR = m_monitor.lock(); - if (PMONITOR) - return {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y}; - } - - const auto POS = m_realPosition->value(); - const auto SIZE = m_realSize->value(); - - CBox box{POS, SIZE}; - box.addExtents(getWindowExtentsUnified(properties)); - - return box; -} - -SBoxExtents CWindow::getFullWindowReservedArea() { - return g_pDecorationPositioner->getWindowDecorationReserved(m_self); -} - -void CWindow::updateWindowDecos() { - - if (!m_isMapped || isHidden()) - return; - - for (auto const& wd : m_decosToRemove) { - for (auto it = m_windowDecorations.begin(); it != m_windowDecorations.end(); it++) { - if (it->get() == wd) { - g_pDecorationPositioner->uncacheDecoration(it->get()); - it = m_windowDecorations.erase(it); - if (it == m_windowDecorations.end()) - break; - } - } - } - - g_pDecorationPositioner->onWindowUpdate(m_self.lock()); - - m_decosToRemove.clear(); - - // make a copy because updateWindow can remove decos. - std::vector decos; - // reserve to avoid reallocations - decos.reserve(m_windowDecorations.size()); - - for (auto const& wd : m_windowDecorations) { - decos.push_back(wd.get()); - } - - for (auto const& wd : decos) { - if (std::ranges::find_if(m_windowDecorations, [wd](const auto& other) { return other.get() == wd; }) == m_windowDecorations.end()) - continue; - wd->updateWindow(m_self.lock()); - } -} - -void CWindow::addWindowDeco(UP deco) { - m_windowDecorations.emplace_back(std::move(deco)); - g_pDecorationPositioner->forceRecalcFor(m_self.lock()); - updateWindowDecos(); - - if (layoutTarget()) - layoutTarget()->recalc(); -} - -void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) { - m_decosToRemove.push_back(deco); - g_pDecorationPositioner->forceRecalcFor(m_self.lock()); - updateWindowDecos(); - - if (layoutTarget()) - layoutTarget()->recalc(); -} - -void CWindow::uncacheWindowDecos() { - for (auto const& wd : m_windowDecorations) { - g_pDecorationPositioner->uncacheDecoration(wd.get()); - } -} - -bool CWindow::checkInputOnDecos(const eInputType type, const Vector2D& mouseCoords, std::any data) { - if (type != INPUT_TYPE_DRAG_END && hasPopupAt(mouseCoords)) - return false; - - for (auto const& wd : m_windowDecorations) { - if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT)) - continue; - - if (!g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(mouseCoords)) - continue; - - if (wd->onInputOnDeco(type, mouseCoords, data)) - return true; - } - - return false; -} - -pid_t CWindow::getPID() { - pid_t PID = -1; - if (!m_isX11) { - if (!m_xdgSurface || !m_xdgSurface->m_owner /* happens at unmap */) - return -1; - - wl_client_get_credentials(m_xdgSurface->m_owner->client(), &PID, nullptr, nullptr); - } else { - if (!m_xwaylandSurface) - return -1; - - PID = m_xwaylandSurface->m_pid; - } - - return PID; -} - -IHyprWindowDecoration* CWindow::getDecorationByType(eDecorationType type) { - for (auto const& wd : m_windowDecorations) { - if (wd->getDecorationType() == type) - return wd.get(); - } - - return nullptr; -} - -void CWindow::updateToplevel() { - updateSurfaceScaleTransformDetails(); -} - -void CWindow::updateSurfaceScaleTransformDetails(bool force) { - if (!m_isMapped || m_hidden || g_pCompositor->m_unsafeState) - return; - - const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_lastSurfaceMonitorID); - - m_lastSurfaceMonitorID = monitorID(); - - const auto PNEWMONITOR = m_monitor.lock(); - - if (!PNEWMONITOR) - return; - - if (PNEWMONITOR != PLASTMONITOR || force) { - if (PLASTMONITOR && PLASTMONITOR->m_enabled && PNEWMONITOR != PLASTMONITOR) - m_wlSurface->resource()->breadthfirst([PLASTMONITOR](SP s, const Vector2D& offset, void* d) { s->leave(PLASTMONITOR->m_self.lock()); }, nullptr); - - m_wlSurface->resource()->breadthfirst([PNEWMONITOR](SP s, const Vector2D& offset, void* d) { s->enter(PNEWMONITOR->m_self.lock()); }, nullptr); - } - - const auto PMONITOR = m_monitor.lock(); - - m_wlSurface->resource()->breadthfirst( - [PMONITOR](SP s, const Vector2D& offset, void* d) { - const auto PSURFACE = CWLSurface::fromResource(s); - if (PSURFACE && PSURFACE->m_lastScaleFloat == PMONITOR->m_scale) - return; - - PROTO::fractional->sendScale(s, PMONITOR->m_scale); - g_pCompositor->setPreferredScaleForSurface(s, PMONITOR->m_scale); - g_pCompositor->setPreferredTransformForSurface(s, PMONITOR->m_transform); - }, - nullptr); -} - -void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) { - if (m_workspace == pWorkspace) - return; - - static auto PINITIALWSTRACKING = CConfigValue("misc:initial_workspace_tracking"); - - if (!m_initialWorkspaceToken.empty()) { - const auto TOKEN = g_pTokenManager->getToken(m_initialWorkspaceToken); - if (TOKEN) { - if (*PINITIALWSTRACKING == 2) { - // persistent - try { - SInitialWorkspaceToken token = std::any_cast(TOKEN->m_data); - if (token.primaryOwner == m_self) { - token.workspace = pWorkspace->getConfigName(); - TOKEN->m_data = token; - } - } catch (const std::bad_any_cast& e) { ; } - } - } - } - - static auto PCLOSEONLASTSPECIAL = CConfigValue("misc:close_special_on_empty"); - - const auto OLDWORKSPACE = m_workspace; - - if (OLDWORKSPACE->isVisible()) { - m_movingToWorkspaceAlpha->setValueAndWarp(1.F); - *m_movingToWorkspaceAlpha = 0.F; - m_movingToWorkspaceAlpha->setCallbackOnEnd([this](auto) { m_monitorMovedFrom = -1; }); - m_monitorMovedFrom = OLDWORKSPACE ? OLDWORKSPACE->monitorID() : -1; - } - - m_workspace = pWorkspace; - - setAnimationsToMove(); - - g_pCompositor->updateAllWindowsAnimatedDecorationValues(); - - if (valid(pWorkspace)) { - g_pEventManager->postEvent(SHyprIPCEvent{.event = "movewindow", .data = std::format("{:x},{}", rc(this), pWorkspace->m_name)}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "movewindowv2", .data = std::format("{:x},{},{}", rc(this), pWorkspace->m_id, pWorkspace->m_name)}); - Event::bus()->m_events.window.moveToWorkspace.emit(m_self.lock(), pWorkspace); - } - - if (const auto SWALLOWED = m_swallowed.lock()) { - if (SWALLOWED->m_currentlySwallowed) { - SWALLOWED->moveToWorkspace(pWorkspace); - SWALLOWED->m_monitor = m_monitor; - } - } - - if (OLDWORKSPACE && g_pCompositor->isWorkspaceSpecial(OLDWORKSPACE->m_id) && OLDWORKSPACE->getWindows() == 0 && *PCLOSEONLASTSPECIAL) { - if (const auto PMONITOR = OLDWORKSPACE->m_monitor.lock(); PMONITOR) - PMONITOR->setSpecialWorkspace(nullptr); - } -} - -PHLWINDOW CWindow::x11TransientFor() { - if (!m_xwaylandSurface || !m_xwaylandSurface->m_parent) - return nullptr; - - auto s = m_xwaylandSurface->m_parent; - std::vector> visited; - while (s) { - // break loops. Some X apps make them, and it seems like it's valid behavior?!?!?! - // TODO: we should reject loops being created in the first place. - if (std::ranges::find(visited.begin(), visited.end(), s) != visited.end()) - break; - - visited.emplace_back(s.lock()); - s = s->m_parent; - } - - if (s == m_xwaylandSurface) - return nullptr; // dead-ass circle - - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_xwaylandSurface != s) - continue; - return w; - } - - return nullptr; -} - -void CWindow::onUnmap() { - static auto PCLOSEONLASTSPECIAL = CConfigValue("misc:close_special_on_empty"); - static auto PINITIALWSTRACKING = CConfigValue("misc:initial_workspace_tracking"); - - if (!m_initialWorkspaceToken.empty()) { - const auto TOKEN = g_pTokenManager->getToken(m_initialWorkspaceToken); - if (TOKEN) { - if (*PINITIALWSTRACKING == 2) { - // persistent token, but the first window got removed so the token is gone - try { - SInitialWorkspaceToken token = std::any_cast(TOKEN->m_data); - if (token.primaryOwner == m_self) - g_pTokenManager->removeToken(TOKEN); - } catch (const std::bad_any_cast& e) { g_pTokenManager->removeToken(TOKEN); } - } - } - } - - m_lastWorkspace = m_workspace->m_id; - - // if the special workspace now has 0 windows, it will be closed, and this - // window will no longer pass render checks, cuz the workspace will be nuked. - // throw it into the main one for the fadeout. - if (m_workspace->m_isSpecialWorkspace && m_workspace->getWindows() == 0) - m_lastWorkspace = m_monitor->activeWorkspaceID(); - - if (*PCLOSEONLASTSPECIAL && m_workspace && m_workspace->getWindows() == 0 && onSpecialWorkspace()) { - const auto PMONITOR = m_monitor.lock(); - if (PMONITOR && PMONITOR->m_activeSpecialWorkspace && PMONITOR->m_activeSpecialWorkspace == m_workspace) - PMONITOR->setSpecialWorkspace(nullptr); - } - - const auto PMONITOR = m_monitor.lock(); - - if (PMONITOR && PMONITOR->m_solitaryClient == m_self) - PMONITOR->m_solitaryClient.reset(); - - if (m_workspace) { - m_workspace->updateWindows(); - m_workspace->updateWindowData(); - } - - g_pCompositor->updateAllWindowsAnimatedDecorationValues(); - - m_workspace.reset(); - - if (m_isX11) - return; - - m_subsurfaceHead.reset(); - m_popupHead.reset(); -} - -void CWindow::onMap() { - // JIC, reset the callbacks. If any are set, we'll make sure they are cleared so we don't accidentally unset them. (In case a window got remapped) - m_realPosition->resetAllCallbacks(); - m_realSize->resetAllCallbacks(); - m_borderFadeAnimationProgress->resetAllCallbacks(); - m_borderAngleAnimationProgress->resetAllCallbacks(); - m_activeInactiveAlpha->resetAllCallbacks(); - m_alpha->resetAllCallbacks(); - m_realShadowColor->resetAllCallbacks(); - m_dimPercent->resetAllCallbacks(); - m_movingToWorkspaceAlpha->resetAllCallbacks(); - m_movingFromWorkspaceAlpha->resetAllCallbacks(); - - m_movingFromWorkspaceAlpha->setValueAndWarp(1.F); - - if (m_borderAngleAnimationProgress->enabled()) { - m_borderAngleAnimationProgress->setValueAndWarp(0.f); - m_borderAngleAnimationProgress->setCallbackOnEnd([&](WP p) { onBorderAngleAnimEnd(p); }, false); - *m_borderAngleAnimationProgress = 1.f; - } - - m_realSize->setCallbackOnBegin( - [this](auto) { - if (!m_isMapped || isX11OverrideRedirect()) - return; - - g_pEventLoopManager->doLater([this, self = m_self] { - if (!self) - return; - - sendWindowSize(); - }); - }, - false); - - m_realSize->setUpdateCallback([this](auto) { - if (m_isMapped) - m_events.resize.emit(); - }); - - m_realPosition->setUpdateCallback([this](auto) { - if (m_isMapped && m_monitor != m_prevMonitor) { - m_prevMonitor = m_monitor; - m_events.monitorChanged.emit(); - } - }); - - m_movingFromWorkspaceAlpha->setValueAndWarp(1.F); - - m_reportedSize = m_pendingReportedSize; - m_animatingIn = true; - - updateSurfaceScaleTransformDetails(true); - - if (m_isX11) - return; - - m_subsurfaceHead = CSubsurface::create(m_self.lock()); - m_popupHead = CPopup::create(m_self.lock()); -} - -void CWindow::onBorderAngleAnimEnd(WP pav) { - if (!pav) - return; - - if (pav->getStyle() != "loop" || !pav->enabled()) - return; - - const auto PANIMVAR = dc*>(pav.get()); - - PANIMVAR->setCallbackOnEnd(nullptr); // we remove the callback here because otherwise setvalueandwarp will recurse this - - PANIMVAR->setValueAndWarp(0); - *PANIMVAR = 1.f; - - PANIMVAR->setCallbackOnEnd([&](WP pav) { onBorderAngleAnimEnd(pav); }, false); -} - -void CWindow::setHidden(bool hidden) { - m_hidden = hidden; - - if (hidden) - m_events.hide.emit(); - - if (hidden && Desktop::focusState()->window() == m_self) - Desktop::focusState()->window().reset(); - - setSuspended(hidden); -} - -bool CWindow::isHidden() { - return m_hidden; -} - -// check if the point is "hidden" under a rounded corner of the window -// it is assumed that the point is within the real window box (m_vRealPosition, m_vRealSize) -// otherwise behaviour is undefined -bool CWindow::isInCurvedCorner(double x, double y) { - const int ROUNDING = rounding(); - const int ROUNDINGPOWER = roundingPower(); - if (getRealBorderSize() >= ROUNDING) - return false; - - // (x0, y0), (x0, y1), ... are the center point of rounding at each corner - double x0 = m_realPosition->value().x + ROUNDING; - double y0 = m_realPosition->value().y + ROUNDING; - double x1 = m_realPosition->value().x + m_realSize->value().x - ROUNDING; - double y1 = m_realPosition->value().y + m_realSize->value().y - ROUNDING; - - if (x < x0 && y < y0) { - return std::pow(x0 - x, ROUNDINGPOWER) + std::pow(y0 - y, ROUNDINGPOWER) > std::pow(sc(ROUNDING), ROUNDINGPOWER); - } - if (x > x1 && y < y0) { - return std::pow(x - x1, ROUNDINGPOWER) + std::pow(y0 - y, ROUNDINGPOWER) > std::pow(sc(ROUNDING), ROUNDINGPOWER); - } - if (x < x0 && y > y1) { - return std::pow(x0 - x, ROUNDINGPOWER) + std::pow(y - y1, ROUNDINGPOWER) > std::pow(sc(ROUNDING), ROUNDINGPOWER); - } - if (x > x1 && y > y1) { - return std::pow(x - x1, ROUNDINGPOWER) + std::pow(y - y1, ROUNDINGPOWER) > std::pow(sc(ROUNDING), ROUNDINGPOWER); - } - - return false; -} - -// checks if the wayland window has a popup at pos -bool CWindow::hasPopupAt(const Vector2D& pos) { - if (m_isX11) - return false; - - auto popup = m_popupHead->at(pos); - - return popup && popup->wlSurface()->resource(); -} - -Vector2D CWindow::middle() { - return m_realPosition->goal() + m_realSize->goal() / 2.f; -} - -bool CWindow::opaque() { - if (m_alpha->value() != 1.f || m_activeInactiveAlpha->value() != 1.f) - return false; - - const auto PWORKSPACE = m_workspace; - - if (m_wlSurface->small() && !m_wlSurface->m_fillIgnoreSmall) - return false; - - if (PWORKSPACE && PWORKSPACE->m_alpha->value() != 1.f) - return false; - - if (m_isX11 && m_xwaylandSurface && m_xwaylandSurface->m_surface && m_xwaylandSurface->m_surface->m_current.texture) - return m_xwaylandSurface->m_surface->m_current.texture->m_opaque; - - auto solitaryResource = getSolitaryResource(); - if (!solitaryResource || !solitaryResource->m_current.texture) - return false; - - // TODO: this is wrong - const auto EXTENTS = m_xdgSurface->m_surface->m_current.opaque.getExtents(); - if (EXTENTS.w >= m_xdgSurface->m_surface->m_current.bufferSize.x && EXTENTS.h >= m_xdgSurface->m_surface->m_current.bufferSize.y) - return true; - - return solitaryResource->m_current.texture->m_opaque; -} - -float CWindow::rounding() { - static auto PROUNDING = CConfigValue("decoration:rounding"); - static auto PROUNDINGPOWER = CConfigValue("decoration:rounding_power"); - - float roundingPower = m_ruleApplicator->roundingPower().valueOr(*PROUNDINGPOWER); - float rounding = m_ruleApplicator->rounding().valueOr(*PROUNDING) * (roundingPower / 2.0); /* Make perceived roundness consistent. */ - - return rounding; -} - -float CWindow::roundingPower() { - static auto PROUNDINGPOWER = CConfigValue("decoration:rounding_power"); - - return m_ruleApplicator->roundingPower().valueOr(std::clamp(*PROUNDINGPOWER, 1.F, 10.F)); -} - -void CWindow::updateWindowData() { - const auto PWORKSPACE = m_workspace; - const auto WORKSPACERULE = PWORKSPACE ? g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE) : SWorkspaceRule{}; - updateWindowData(WORKSPACERULE); -} - -void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) { - if (workspaceRule.noBorder.value_or(false)) - m_ruleApplicator->borderSize().matchOptional(std::optional(0), Desktop::Types::PRIORITY_WORKSPACE_RULE); - else if (workspaceRule.borderSize) - m_ruleApplicator->borderSize().matchOptional(workspaceRule.borderSize, Desktop::Types::PRIORITY_WORKSPACE_RULE); - else - m_ruleApplicator->borderSize().matchOptional(std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE); - m_ruleApplicator->decorate().matchOptional(workspaceRule.decorate, Desktop::Types::PRIORITY_WORKSPACE_RULE); - m_ruleApplicator->rounding().matchOptional(workspaceRule.noRounding.value_or(false) ? std::optional(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE); - m_ruleApplicator->noShadow().matchOptional(workspaceRule.noShadow, Desktop::Types::PRIORITY_WORKSPACE_RULE); -} - -int CWindow::getRealBorderSize() const { - if ((m_workspace && isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) || !m_ruleApplicator->decorate().valueOrDefault()) - return 0; - - static auto PBORDERSIZE = CConfigValue("general:border_size"); - - return m_ruleApplicator->borderSize().valueOr(*PBORDERSIZE); -} - -float CWindow::getScrollMouse() { - static auto PINPUTSCROLLFACTOR = CConfigValue("input:scroll_factor"); - return m_ruleApplicator->scrollMouse().valueOr(*PINPUTSCROLLFACTOR); -} - -float CWindow::getScrollTouchpad() { - static auto PTOUCHPADSCROLLFACTOR = CConfigValue("input:touchpad:scroll_factor"); - return m_ruleApplicator->scrollTouchpad().valueOr(*PTOUCHPADSCROLLFACTOR); -} - -bool CWindow::isScrollMouseOverridden() { - return m_ruleApplicator->scrollMouse().hasValue(); -} - -bool CWindow::isScrollTouchpadOverridden() { - return m_ruleApplicator->scrollTouchpad().hasValue(); -} - -bool CWindow::canBeTorn() { - static auto PTEARING = CConfigValue("general:allow_tearing"); - return m_ruleApplicator->tearing().valueOr(m_tearingHint) && *PTEARING; -} - -void CWindow::setSuspended(bool suspend) { - if (suspend == m_suspended) - return; - - if (m_isX11 || !m_xdgSurface || !m_xdgSurface->m_toplevel) - return; - - m_xdgSurface->m_toplevel->setSuspeneded(suspend); - m_suspended = suspend; -} - -bool CWindow::visibleOnMonitor(PHLMONITOR pMonitor) { - CBox wbox = {m_realPosition->value(), m_realSize->value()}; - - if (m_isFloating) - wbox = getFullWindowBoundingBox(); - - return !wbox.intersection({pMonitor->m_position, pMonitor->m_size}).empty(); -} - -void CWindow::setAnimationsToMove() { - m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsMove")); - m_animatingIn = false; -} - -void CWindow::onWorkspaceAnimUpdate() { - // clip box for animated offsets - if (!m_isFloating || m_pinned || isFullscreen()) { - m_floatingOffset = Vector2D(0, 0); - return; - } - - Vector2D offset; - const auto PWORKSPACE = m_workspace; - if (!PWORKSPACE) - return; - - const auto PWSMON = m_monitor.lock(); - if (!PWSMON) - return; - - const auto WINBB = getFullWindowBoundingBox(); - if (PWORKSPACE->m_renderOffset->value().x != 0) { - const auto PROGRESS = PWORKSPACE->m_renderOffset->value().x / PWSMON->m_size.x; - - if (WINBB.x < PWSMON->m_position.x) - offset.x += (PWSMON->m_position.x - WINBB.x) * PROGRESS; - - if (WINBB.x + WINBB.width > PWSMON->m_position.x + PWSMON->m_size.x) - offset.x += (WINBB.x + WINBB.width - PWSMON->m_position.x - PWSMON->m_size.x) * PROGRESS; - } else if (PWORKSPACE->m_renderOffset->value().y != 0) { - const auto PROGRESS = PWORKSPACE->m_renderOffset->value().y / PWSMON->m_size.y; - - if (WINBB.y < PWSMON->m_position.y) - offset.y += (PWSMON->m_position.y - WINBB.y) * PROGRESS; - - if (WINBB.y + WINBB.height > PWSMON->m_position.y + PWSMON->m_size.y) - offset.y += (WINBB.y + WINBB.height - PWSMON->m_position.y - PWSMON->m_size.y) * PROGRESS; - } - - m_floatingOffset = offset; -} - -void CWindow::onFocusAnimUpdate() { - // borderangle once - if (m_borderAngleAnimationProgress->enabled() && !m_borderAngleAnimationProgress->isBeingAnimated()) { - m_borderAngleAnimationProgress->setValueAndWarp(0.f); - *m_borderAngleAnimationProgress = 1.f; - } -} - -int CWindow::popupsCount() { - if (m_isX11 || !m_popupHead) - return 0; - - int no = -1; - m_popupHead->breadthfirst([](WP p, void* d) { *sc(d) += 1; }, &no); - return no; -} - -int CWindow::surfacesCount() { - if (m_isX11) - return 1; - - int no = 0; - m_wlSurface->resource()->breadthfirst([](SP r, const Vector2D& offset, void* d) { *sc(d) += 1; }, &no); - return no; -} - -bool CWindow::clampWindowSize(const std::optional minSize, const std::optional maxSize) { - const Vector2D REALSIZE = m_realSize->goal(); - const Vector2D MAX = isFullscreen() ? Vector2D{INFINITY, INFINITY} : maxSize.value_or(Vector2D{INFINITY, INFINITY}); - const Vector2D NEWSIZE = REALSIZE.clamp(minSize.value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), MAX); - const bool changed = !(NEWSIZE == REALSIZE); - - if (changed) { - const Vector2D DELTA = REALSIZE - NEWSIZE; - *m_realPosition = m_realPosition->goal() + DELTA / 2.0; - *m_realSize = NEWSIZE; - } - - return changed; -} - -bool CWindow::isFullscreen() { - return m_fullscreenState.internal != FSMODE_NONE; -} - -bool CWindow::isEffectiveInternalFSMode(const eFullscreenMode MODE) const { - return sc(std::bit_floor(sc(m_fullscreenState.internal))) == MODE; -} - -WORKSPACEID CWindow::workspaceID() { - return m_workspace ? m_workspace->m_id : m_lastWorkspace; -} - -MONITORID CWindow::monitorID() { - return m_monitor ? m_monitor->m_id : MONITOR_INVALID; -} - -bool CWindow::onSpecialWorkspace() { - return m_workspace ? m_workspace->m_isSpecialWorkspace : g_pCompositor->isWorkspaceSpecial(m_lastWorkspace); -} - -std::unordered_map CWindow::getEnv() { - - const auto PID = getPID(); - - if (PID <= 1) - return {}; - - std::unordered_map results; - - std::vector buffer; - size_t needle = 0; - -#if defined(__linux__) - // - std::string environFile = "/proc/" + std::to_string(PID) + "/environ"; - std::ifstream ifs(environFile, std::ios::binary); - - if (!ifs.good()) - return {}; - - buffer.resize(512, '\0'); - while (ifs.read(buffer.data() + needle, 512)) { - buffer.resize(buffer.size() + 512, '\0'); - needle += 512; - } -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ENV, static_cast(PID)}; - size_t len = 0; - - if (sysctl(mib, 4, nullptr, &len, nullptr, 0) < 0 || len == 0) - return {}; - - buffer.resize(len, '\0'); - - if (sysctl(mib, 4, buffer.data(), &len, nullptr, 0) < 0) - return {}; - - needle = len; -#endif - - if (needle <= 1) - return {}; - - std::replace(buffer.begin(), buffer.end() - 1, '\0', '\n'); - - CVarList envs(std::string{buffer.data(), buffer.size() - 1}, 0, '\n', true); - - for (auto const& e : envs) { - if (!e.contains('=')) - continue; - - const auto EQ = e.find_first_of('='); - results[e.substr(0, EQ)] = e.substr(EQ + 1); - } - - return results; -} - -void CWindow::activate(bool force) { - if (Desktop::focusState()->window() == m_self) - return; - - static auto PFOCUSONACTIVATE = CConfigValue("misc:focus_on_activate"); - - m_isUrgent = true; - - g_pEventManager->postEvent(SHyprIPCEvent{.event = "urgent", .data = std::format("{:x}", rc(this))}); - Event::bus()->m_events.window.urgent.emit(m_self.lock()); - - if (!force && - (!m_ruleApplicator->focusOnActivate().valueOr(*PFOCUSONACTIVATE) || (m_suppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_suppressedEvents & SUPPRESS_ACTIVATE))) - return; - - if (!m_isMapped) { - Log::logger->log(Log::DEBUG, "Ignoring CWindow::activate focus/warp, window is not mapped yet."); - return; - } - - if (m_isFloating) - g_pCompositor->changeWindowZOrder(m_self.lock(), true); - - Desktop::focusState()->fullWindowFocus(m_self.lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE); - warpCursor(); -} - -void CWindow::onUpdateState() { - std::optional requestsFS = m_xdgSurface ? m_xdgSurface->m_toplevel->m_state.requestsFullscreen : m_xwaylandSurface->m_state.requestsFullscreen; - std::optional requestsID = m_xdgSurface ? m_xdgSurface->m_toplevel->m_state.requestsFullscreenMonitor : MONITOR_INVALID; - std::optional requestsMX = m_xdgSurface ? m_xdgSurface->m_toplevel->m_state.requestsMaximize : m_xwaylandSurface->m_state.requestsMaximize; - - if (requestsFS.has_value() && !(m_suppressedEvents & SUPPRESS_FULLSCREEN)) { - if (requestsID.has_value() && (requestsID.value() != MONITOR_INVALID) && !(m_suppressedEvents & SUPPRESS_FULLSCREEN_OUTPUT)) { - if (m_isMapped) { - const auto monitor = g_pCompositor->getMonitorFromID(requestsID.value()); - g_pCompositor->moveWindowToWorkspaceSafe(m_self.lock(), monitor->m_activeWorkspace); - Desktop::focusState()->rawMonitorFocus(monitor); - } - - if (!m_isMapped) - m_wantsInitialFullscreenMonitor = requestsID.value(); - } - - bool fs = requestsFS.value(); - if (m_isMapped) - g_pCompositor->changeWindowFullscreenModeClient(m_self.lock(), FSMODE_FULLSCREEN, requestsFS.value()); - - if (!m_isMapped) - m_wantsInitialFullscreen = fs; - } - - if (requestsMX.has_value() && !(m_suppressedEvents & SUPPRESS_MAXIMIZE)) { - if (m_isMapped) { - auto window = m_self.lock(); - auto state = sc(window->m_fullscreenState.client); - bool maximized = (state & sc(FSMODE_MAXIMIZED)) != 0; - g_pCompositor->changeWindowFullscreenModeClient(window, FSMODE_MAXIMIZED, !maximized); - } - } -} - -void CWindow::onUpdateMeta() { - const auto NEWTITLE = fetchTitle(); - bool doUpdate = false; - - if (m_title != NEWTITLE) { - m_title = NEWTITLE; - g_pEventManager->postEvent(SHyprIPCEvent{.event = "windowtitle", .data = std::format("{:x}", rc(this))}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "windowtitlev2", .data = std::format("{:x},{}", rc(this), m_title)}); - Event::bus()->m_events.window.title.emit(m_self.lock()); - - if (m_self == Desktop::focusState()->window()) { // if it's the active, let's post an event to update others - g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = m_class + "," + m_title}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = std::format("{:x}", rc(this))}); - - // no need for a hook event - } - - Log::logger->log(Log::DEBUG, "Window {:x} set title to {}", rc(this), m_title); - doUpdate = true; - } - - const auto NEWCLASS = fetchClass(); - if (m_class != NEWCLASS) { - m_class = NEWCLASS; - - Event::bus()->m_events.window.class_.emit(m_self.lock()); - - if (m_self == Desktop::focusState()->window()) { // if it's the active, let's post an event to update others - g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = m_class + "," + m_title}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = std::format("{:x}", rc(this))}); - - // no need for a hook event - } - - Log::logger->log(Log::DEBUG, "Window {:x} set class to {}", rc(this), m_class); - doUpdate = true; - } - - if (doUpdate) { - m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TITLE | Desktop::Rule::RULE_PROP_CLASS); - updateToplevel(); - } -} - -std::string CWindow::fetchTitle() { - if (!m_isX11) { - if (m_xdgSurface && m_xdgSurface->m_toplevel) - return m_xdgSurface->m_toplevel->m_state.title; - } else { - if (m_xwaylandSurface) - return m_xwaylandSurface->m_state.title; - } - - return ""; -} - -std::string CWindow::fetchClass() { - if (!m_isX11) { - if (m_xdgSurface && m_xdgSurface->m_toplevel) - return m_xdgSurface->m_toplevel->m_state.appid; - } else { - if (m_xwaylandSurface) - return m_xwaylandSurface->m_state.appid; - } - - return ""; -} - -void CWindow::onAck(uint32_t serial) { - const auto SERIAL = std::ranges::find_if(m_pendingSizeAcks | std::views::reverse, [serial](const auto& e) { return e.first <= serial; }); - - if (SERIAL == m_pendingSizeAcks.rend()) - return; - - m_pendingSizeAck = *SERIAL; - std::erase_if(m_pendingSizeAcks, [&](const auto& el) { return el.first <= SERIAL->first; }); - - if (m_isX11) - return; - - m_wlSurface->resource()->m_pending.ackedSize = m_pendingSizeAck->second; // apply pending size. We pinged, the window ponged. - m_wlSurface->resource()->m_pending.updated.bits.acked = true; - m_pendingSizeAck.reset(); -} - -void CWindow::onResourceChangeX11() { - if (m_xwaylandSurface->m_surface && !m_wlSurface->resource()) - m_wlSurface->assign(m_xwaylandSurface->m_surface.lock(), m_self.lock()); - else if (!m_xwaylandSurface->m_surface && m_wlSurface->resource()) - m_wlSurface->unassign(); - - // update metadata as well, - // could be first assoc and we need to catch the class - onUpdateMeta(); - - Log::logger->log(Log::DEBUG, "xwayland window {:x} -> association to {:x}", rc(m_xwaylandSurface.get()), rc(m_wlSurface->resource().get())); -} - -void CWindow::onX11ConfigureRequest(CBox box) { - - if (!m_xwaylandSurface->m_surface || !m_xwaylandSurface->m_mapped || !m_isMapped) { - m_xwaylandSurface->configure(box); - m_pendingReportedSize = box.size(); - m_reportedSize = box.size(); - m_reportedPosition = box.pos(); - updateX11SurfaceScale(); - return; - } - - g_pHyprRenderer->damageWindow(m_self.lock()); - - if (!m_isFloating || isFullscreen() || g_layoutManager->dragController()->target() == m_self) { - sendWindowSize(true); - g_pInputManager->refocus(); - g_pHyprRenderer->damageWindow(m_self.lock()); - return; - } - - if (box.size() > Vector2D{1, 1}) - setHidden(false); - else - setHidden(true); - - m_realPosition->setValueAndWarp(xwaylandPositionToReal(box.pos())); - m_realSize->setValueAndWarp(xwaylandSizeToReal(box.size())); - - m_position = m_realPosition->goal(); - m_size = m_realSize->goal(); - - if (m_pendingReportedSize != box.size() || m_reportedPosition != box.pos()) { - m_xwaylandSurface->configure(box); - m_reportedSize = box.size(); - m_pendingReportedSize = box.size(); - m_reportedPosition = box.pos(); - } - - updateX11SurfaceScale(); - updateWindowDecos(); - - if (!m_workspace || !m_workspace->isVisible()) - return; // further things are only for visible windows - - const auto monitorByRequestedPosition = g_pCompositor->getMonitorFromVector(m_realPosition->goal() + m_realSize->goal() / 2.f); - const auto currentMonitor = m_workspace->m_monitor.lock(); - - Log::logger->log( - Log::DEBUG, - "onX11ConfigureRequest: window '{}' ({:#x}) - workspace '{}' (special={}), currentMonitor='{}', monitorByRequestedPosition='{}', pos={:.0f},{:.0f}, size={:.0f},{:.0f}", - m_title, (uintptr_t)this, m_workspace->m_name, m_workspace->m_isSpecialWorkspace, currentMonitor ? currentMonitor->m_name : "null", - monitorByRequestedPosition ? monitorByRequestedPosition->m_name : "null", m_realPosition->goal().x, m_realPosition->goal().y, m_realSize->goal().x, m_realSize->goal().y); - - // Reassign workspace only when moving to a different monitor and not on a special workspace - // X11 apps send configure requests with positions based on XWayland's monitor layout, such as "0,0", - // which would incorrectly move windows off special workspaces - if (monitorByRequestedPosition && monitorByRequestedPosition != currentMonitor && !m_workspace->m_isSpecialWorkspace) { - Log::logger->log(Log::DEBUG, "onX11ConfigureRequest: reassigning workspace from '{}' to '{}'", m_workspace->m_name, monitorByRequestedPosition->m_activeWorkspace->m_name); - m_workspace = monitorByRequestedPosition->m_activeWorkspace; - } - - g_pCompositor->changeWindowZOrder(m_self.lock(), true); - - m_createdOverFullscreen = true; - - g_pHyprRenderer->damageWindow(m_self.lock()); -} - -void CWindow::warpCursor(bool force) { - static auto PERSISTENTWARPS = CConfigValue("cursor:persistent_warps"); - const auto coords = m_relativeCursorCoordsOnLastWarp; - m_relativeCursorCoordsOnLastWarp.x = -1; // reset m_vRelativeCursorCoordsOnLastWarp - - if (*PERSISTENTWARPS && coords.x > 0 && coords.y > 0 && coords < m_size) // don't warp cursor outside the window - g_pCompositor->warpCursorTo(m_position + coords, force); - else - g_pCompositor->warpCursorTo(middle(), force); -} - -PHLWINDOW CWindow::getSwallower() { - static auto PSWALLOWREGEX = CConfigValue("misc:swallow_regex"); - static auto PSWALLOWEXREGEX = CConfigValue("misc:swallow_exception_regex"); - static auto PSWALLOW = CConfigValue("misc:enable_swallow"); - - if (!*PSWALLOW || std::string{*PSWALLOWREGEX} == STRVAL_EMPTY || (*PSWALLOWREGEX).empty()) - return nullptr; - - // check parent - std::vector candidates; - pid_t currentPid = getPID(); - // walk up the tree until we find someone, 25 iterations max. - for (size_t i = 0; i < 25; ++i) { - currentPid = getPPIDof(currentPid); - - if (!currentPid) - break; - - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_isMapped || w->isHidden()) - continue; - - if (w->getPID() == currentPid) - candidates.push_back(w); - } - } - - if (!(*PSWALLOWREGEX).empty()) - std::erase_if(candidates, [&](const auto& other) { return !RE2::FullMatch(other->m_class, *PSWALLOWREGEX); }); - - if (candidates.empty()) - return nullptr; - - if (!(*PSWALLOWEXREGEX).empty()) - std::erase_if(candidates, [&](const auto& other) { return RE2::FullMatch(other->m_title, *PSWALLOWEXREGEX); }); - - if (candidates.empty()) - return nullptr; - - if (candidates.size() == 1) - return candidates[0]; - - // walk up the focus history and find the last focused - for (auto const& w : Desktop::History::windowTracker()->fullHistory() | std::views::reverse) { - if (!w) - continue; - - if (std::ranges::find(candidates.begin(), candidates.end(), w.lock()) != candidates.end()) - return w.lock(); - } - - // if none are found (??) then just return the first one - return candidates[0]; -} - -bool CWindow::isX11OverrideRedirect() { - return m_xwaylandSurface && m_xwaylandSurface->m_overrideRedirect; -} - -bool CWindow::isModal() { - return (m_xwaylandSurface && m_xwaylandSurface->m_modal); -} - -Vector2D CWindow::realToReportSize() { - if (!m_isX11) - return m_realSize->goal().clamp(Vector2D{0, 0}, Math::VECTOR2D_MAX); - - static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - - const auto REPORTSIZE = m_realSize->goal().clamp(Vector2D{1, 1}, Math::VECTOR2D_MAX); - const auto PMONITOR = m_monitor.lock(); - - if (*PXWLFORCESCALEZERO && PMONITOR) - // Keep X11 configure sizes integral to avoid truncation (e.g. 2879.999 -> 2879) later in xcb. - return (REPORTSIZE * PMONITOR->m_scale).round(); - - return REPORTSIZE; -} - -Vector2D CWindow::realToReportPosition() { - if (!m_isX11) - return m_realPosition->goal(); - - return g_pXWaylandManager->waylandToXWaylandCoords(m_realPosition->goal()); -} - -Vector2D CWindow::xwaylandSizeToReal(Vector2D size) { - static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - - const auto PMONITOR = m_monitor.lock(); - const auto SIZE = size.clamp(Vector2D{1, 1}, Math::VECTOR2D_MAX); - const auto SCALE = *PXWLFORCESCALEZERO ? PMONITOR->m_scale : 1.0f; - - return SIZE / SCALE; -} - -Vector2D CWindow::xwaylandPositionToReal(Vector2D pos) { - return g_pXWaylandManager->xwaylandToWaylandCoords(pos); -} - -void CWindow::updateX11SurfaceScale() { - static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - - m_X11SurfaceScaledBy = 1.0f; - if (m_isX11 && *PXWLFORCESCALEZERO) { - if (const auto PMONITOR = m_monitor.lock(); PMONITOR) - m_X11SurfaceScaledBy = PMONITOR->m_scale; - } -} - -void CWindow::sendWindowSize(bool force) { - const auto PMONITOR = m_monitor.lock(); - - Log::logger->log(Log::TRACE, "sendWindowSize: window:{:x},title:{} with real pos {}, real size {} (force: {})", rc(this), this->m_title, m_realPosition->goal(), - m_realSize->goal(), force); - - // TODO: this should be decoupled from setWindowSize IMO - const auto REPORTPOS = realToReportPosition(); - - const auto REPORTSIZE = realToReportSize(); - - if (!force && m_pendingReportedSize == REPORTSIZE && (m_reportedPosition == REPORTPOS || !m_isX11)) - return; - - m_reportedPosition = REPORTPOS; - m_pendingReportedSize = REPORTSIZE; - updateX11SurfaceScale(); - - if (m_isX11 && m_xwaylandSurface) - m_xwaylandSurface->configure({REPORTPOS, REPORTSIZE}); - else if (m_xdgSurface && m_xdgSurface->m_toplevel) - m_pendingSizeAcks.emplace_back(m_xdgSurface->m_toplevel->setSize(REPORTSIZE), REPORTSIZE.floor()); -} - -NContentType::eContentType CWindow::getContentType() { - if (!m_wlSurface || !m_wlSurface->resource() || !m_wlSurface->resource()->m_contentType.valid()) - return CONTENT_TYPE_NONE; - - return m_wlSurface->resource()->m_contentType->m_value; -} - -void CWindow::setContentType(NContentType::eContentType contentType) { - if (!m_wlSurface->resource()->m_contentType.valid()) - m_wlSurface->resource()->m_contentType = PROTO::contentType->getContentType(m_wlSurface->resource()); - // else disallow content type change if proto is used? - - Log::logger->log(Log::INFO, "ContentType for window {}", sc(contentType)); - m_wlSurface->resource()->m_contentType->m_value = contentType; -} - -void CWindow::deactivateGroupMembers() { - if (!m_group) - return; - for (const auto& w : m_group->windows()) { - if (w != m_self.lock()) { - // we don't want to deactivate unfocused xwayland windows - // because X is weird, keep the behavior for wayland windows - // also its not really needed for xwayland windows - // ref: #9760 #9294 - if (!w->m_isX11 && w->m_xdgSurface && w->m_xdgSurface->m_toplevel) - w->m_xdgSurface->m_toplevel->setActive(false); - } - } -} - -bool CWindow::isNotResponding() { - return g_pANRManager->isNotResponding(m_self.lock()); -} - -std::optional CWindow::xdgTag() { - if (!m_xdgSurface || !m_xdgSurface->m_toplevel) - return std::nullopt; - - return m_xdgSurface->m_toplevel->m_toplevelTag; -} - -std::optional CWindow::xdgDescription() { - if (!m_xdgSurface || !m_xdgSurface->m_toplevel) - return std::nullopt; - - return m_xdgSurface->m_toplevel->m_toplevelDescription; -} - -PHLWINDOW CWindow::parent() { - if (m_isX11) { - auto t = x11TransientFor(); - - // don't return a parent that's not mapped - if (!validMapped(t)) - return nullptr; - - return t; - } - - if (!m_xdgSurface || !m_xdgSurface->m_toplevel || !m_xdgSurface->m_toplevel->m_parent) - return nullptr; - - // don't return a parent that's not mapped - if (!m_xdgSurface->m_toplevel->m_parent->m_window || !validMapped(m_xdgSurface->m_toplevel->m_parent->m_window)) - return nullptr; - - return m_xdgSurface->m_toplevel->m_parent->m_window.lock(); -} - -bool CWindow::priorityFocus() { - return !m_isX11 && CAsyncDialogBox::isPriorityDialogBox(getPID()); -} - -SP CWindow::getSolitaryResource() { - if (!m_wlSurface || !m_wlSurface->resource()) - return nullptr; - - auto res = m_wlSurface->resource(); - if (m_isX11) - return res; - - if (popupsCount()) - return nullptr; - - if (res->m_subsurfaces.size() == 0) - return res; - - if (res->m_subsurfaces.size() >= 1) { - if (!res->hasVisibleSubsurface()) - return res; - - if (res->m_subsurfaces.size() == 1) { - if (res->m_subsurfaces[0].expired() || res->m_subsurfaces[0]->m_surface.expired()) - return nullptr; - auto surf = res->m_subsurfaces[0]->m_surface.lock(); - if (!surf || surf->m_subsurfaces.size() != 0 || surf->extends() != res->extends() || !surf->m_current.texture || !surf->m_current.texture->m_opaque) - return nullptr; - return surf; - } - } - - return nullptr; -} - -Vector2D CWindow::getReportedSize() { - if (m_isX11) - return m_reportedSize; - if (m_wlSurface && m_wlSurface->resource()) - return m_wlSurface->resource()->m_current.ackedSize; - return m_reportedSize; -} - -void CWindow::updateDecorationValues() { - static auto PACTIVECOL = CConfigValue("general:col.active_border"); - static auto PINACTIVECOL = CConfigValue("general:col.inactive_border"); - static auto PNOGROUPACTIVECOL = CConfigValue("general:col.nogroup_border_active"); - static auto PNOGROUPINACTIVECOL = CConfigValue("general:col.nogroup_border"); - static auto PGROUPACTIVECOL = CConfigValue("group:col.border_active"); - static auto PGROUPINACTIVECOL = CConfigValue("group:col.border_inactive"); - static auto PGROUPACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_active"); - static auto PGROUPINACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_inactive"); - static auto PINACTIVEALPHA = CConfigValue("decoration:inactive_opacity"); - static auto PACTIVEALPHA = CConfigValue("decoration:active_opacity"); - static auto PFULLSCREENALPHA = CConfigValue("decoration:fullscreen_opacity"); - static auto PSHADOWCOL = CConfigValue("decoration:shadow:color"); - static auto PSHADOWCOLINACTIVE = CConfigValue("decoration:shadow:color_inactive"); - static auto PDIMSTRENGTH = CConfigValue("decoration:dim_strength"); - static auto PDIMENABLED = CConfigValue("decoration:dim_inactive"); - static auto PDIMMODAL = CConfigValue("decoration:dim_modal"); - - auto* const ACTIVECOL = sc((PACTIVECOL.ptr())->getData()); - auto* const INACTIVECOL = sc((PINACTIVECOL.ptr())->getData()); - auto* const NOGROUPACTIVECOL = sc((PNOGROUPACTIVECOL.ptr())->getData()); - auto* const NOGROUPINACTIVECOL = sc((PNOGROUPINACTIVECOL.ptr())->getData()); - auto* const GROUPACTIVECOL = sc((PGROUPACTIVECOL.ptr())->getData()); - auto* const GROUPINACTIVECOL = sc((PGROUPINACTIVECOL.ptr())->getData()); - auto* const GROUPACTIVELOCKEDCOL = sc((PGROUPACTIVELOCKEDCOL.ptr())->getData()); - auto* const GROUPINACTIVELOCKEDCOL = sc((PGROUPINACTIVELOCKEDCOL.ptr())->getData()); - - auto setBorderColor = [&](CGradientValueData grad) -> void { - if (grad == m_realBorderColor) - return; - - m_realBorderColorPrevious = m_realBorderColor; - m_realBorderColor = grad; - m_borderFadeAnimationProgress->setValueAndWarp(0.f); - *m_borderFadeAnimationProgress = 1.f; - }; - - const bool IS_SHADOWED_BY_MODAL = m_xdgSurface && m_xdgSurface->m_toplevel && m_xdgSurface->m_toplevel->anyChildModal(); - - const bool GROUPLOCKED = m_group ? m_group->locked() : false; - if (m_self == Desktop::focusState()->window()) { - const auto* const ACTIVECOLOR = !m_group ? (!(m_groupRules & GROUP_DENY) ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL); - setBorderColor(m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR)); - } else { - const auto* const INACTIVECOLOR = !m_group ? (!(m_groupRules & GROUP_DENY) ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL); - setBorderColor(m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR)); - } - - // opacity - const auto PWORKSPACE = m_workspace; - if (isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) { - *m_activeInactiveAlpha = m_ruleApplicator->alphaFullscreen().valueOrDefault().applyAlpha(*PFULLSCREENALPHA); - } else { - if (m_self == Desktop::focusState()->window()) - *m_activeInactiveAlpha = m_ruleApplicator->alpha().valueOrDefault().applyAlpha(*PACTIVEALPHA); - else - *m_activeInactiveAlpha = m_ruleApplicator->alphaInactive().valueOrDefault().applyAlpha(*PINACTIVEALPHA); - } - - // dim - float goalDim = 1.F; - if (m_self == Desktop::focusState()->window() || m_ruleApplicator->noDim().valueOrDefault() || !*PDIMENABLED) - goalDim = 0; - else - goalDim = *PDIMSTRENGTH; - - if (IS_SHADOWED_BY_MODAL && *PDIMMODAL) - goalDim += (1.F - goalDim) / 2.F; - - *m_dimPercent = goalDim; - - // shadow - if (!isX11OverrideRedirect() && !m_X11DoesntWantBorders) { - if (m_self == Desktop::focusState()->window()) - *m_realShadowColor = CHyprColor(*PSHADOWCOL); - else - *m_realShadowColor = CHyprColor(*PSHADOWCOLINACTIVE != -1 ? *PSHADOWCOLINACTIVE : *PSHADOWCOL); - } else - m_realShadowColor->setValueAndWarp(CHyprColor(0, 0, 0, 0)); // no shadow - - updateWindowDecos(); -} - -std::optional CWindow::calculateSingleExpr(const std::string& s) { - const auto PMONITOR = m_monitor ? m_monitor : Desktop::focusState()->monitor(); - const auto CURSOR_LOCAL = g_pInputManager->getMouseCoordsInternal() - (PMONITOR ? PMONITOR->m_position : Vector2D{}); - - Math::CExpression expr; - expr.addVariable("window_w", m_realSize->goal().x); - expr.addVariable("window_h", m_realSize->goal().y); - expr.addVariable("window_x", m_realPosition->goal().x - (PMONITOR ? PMONITOR->m_position.x : 0)); - expr.addVariable("window_y", m_realPosition->goal().y - (PMONITOR ? PMONITOR->m_position.y : 0)); - - expr.addVariable("monitor_w", PMONITOR ? PMONITOR->m_size.x : 1920); - expr.addVariable("monitor_h", PMONITOR ? PMONITOR->m_size.y : 1080); - - expr.addVariable("cursor_x", CURSOR_LOCAL.x); - expr.addVariable("cursor_y", CURSOR_LOCAL.y); - - return expr.compute(s); -} - -std::optional CWindow::calculateExpression(const std::string& s) { - auto spacePos = s.find(' '); - if (spacePos == std::string::npos) - return std::nullopt; - - const auto LHS = calculateSingleExpr(s.substr(0, spacePos)); - const auto RHS = calculateSingleExpr(s.substr(spacePos + 1)); - - if (!LHS || !RHS) - return std::nullopt; - - return Vector2D{*LHS, *RHS}; -} - -static void setVector2DAnimToMove(WP pav) { - if (!pav) - return; - - CAnimatedVariable* animvar = dc*>(pav.get()); - animvar->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsMove")); - - if (animvar->m_Context.pWindow) - animvar->m_Context.pWindow->m_animatingIn = false; -} - -void CWindow::mapWindow() { - static auto PINACTIVEALPHA = CConfigValue("decoration:inactive_opacity"); - static auto PACTIVEALPHA = CConfigValue("decoration:active_opacity"); - static auto PDIMSTRENGTH = CConfigValue("decoration:dim_strength"); - static auto PNEWTAKESOVERFS = CConfigValue("misc:on_focus_under_fullscreen"); - static auto PINITIALWSTRACKING = CConfigValue("misc:initial_workspace_tracking"); - static auto PAUTOGROUP = CConfigValue("group:auto_group"); - - const auto LAST_FOCUS_WINDOW = Desktop::focusState()->window(); - const bool IS_LAST_IN_FS = LAST_FOCUS_WINDOW ? LAST_FOCUS_WINDOW->m_fullscreenState.internal != FSMODE_NONE : false; - const auto LAST_FS_MODE = LAST_FOCUS_WINDOW ? LAST_FOCUS_WINDOW->m_fullscreenState.internal : FSMODE_NONE; - - auto PMONITOR = Desktop::focusState()->monitor(); - if (!Desktop::focusState()->monitor()) { - Desktop::focusState()->rawMonitorFocus(g_pCompositor->getMonitorFromVector({})); - PMONITOR = Desktop::focusState()->monitor(); - } - auto PWORKSPACE = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace; - m_monitor = PMONITOR; - m_workspace = PWORKSPACE; - m_isMapped = true; - m_readyToDelete = false; - m_fadingOut = false; - m_title = fetchTitle(); - m_firstMap = true; - m_initialTitle = m_title; - m_initialClass = fetchClass(); - - // check for token - std::string requestedWorkspace = ""; - bool workspaceSilent = false; - - if (*PINITIALWSTRACKING) { - const auto WINDOWENV = getEnv(); - if (WINDOWENV.contains("HL_INITIAL_WORKSPACE_TOKEN")) { - const auto SZTOKEN = WINDOWENV.at("HL_INITIAL_WORKSPACE_TOKEN"); - Log::logger->log(Log::DEBUG, "New window contains HL_INITIAL_WORKSPACE_TOKEN: {}", SZTOKEN); - const auto TOKEN = g_pTokenManager->getToken(SZTOKEN); - if (TOKEN) { - // find workspace and use it - Desktop::View::SInitialWorkspaceToken WS = std::any_cast(TOKEN->m_data); - - Log::logger->log(Log::DEBUG, "HL_INITIAL_WORKSPACE_TOKEN {} -> {}", SZTOKEN, WS.workspace); - - if (g_pCompositor->getWorkspaceByString(WS.workspace) != m_workspace) { - requestedWorkspace = WS.workspace; - workspaceSilent = true; - } - - if (*PINITIALWSTRACKING == 1) // one-shot token - g_pTokenManager->removeToken(TOKEN); - else if (*PINITIALWSTRACKING == 2) { // persistent - if (WS.primaryOwner.expired()) { - WS.primaryOwner = m_self.lock(); - TOKEN->m_data = WS; - } - - m_initialWorkspaceToken = SZTOKEN; - } - } - } - } - - if (g_pInputManager->m_lastFocusOnLS) // waybar fix - g_pInputManager->releaseAllMouseButtons(); - - // checks if the window wants borders and sets the appropriate flag - g_pXWaylandManager->checkBorders(m_self.lock()); - - // registers the animated vars and stuff - onMap(); - - if (g_pXWaylandManager->shouldBeFloated(m_self.lock())) { - m_isFloating = true; - m_requestsFloat = true; - } - - m_X11ShouldntFocus = m_X11ShouldntFocus || (m_isX11 && isX11OverrideRedirect() && !m_xwaylandSurface->wantsFocus()); - - // window rules - std::optional requestedInternalFSMode, requestedClientFSMode; - std::optional requestedFSState; - if (m_wantsInitialFullscreen || (m_isX11 && m_xwaylandSurface->m_fullscreen)) - requestedClientFSMode = FSMODE_FULLSCREEN; - MONITORID requestedFSMonitor = m_wantsInitialFullscreenMonitor; - - m_ruleApplicator->readStaticRules(); - { - if (!m_ruleApplicator->static_.monitor.empty()) { - const auto& MONITORSTR = m_ruleApplicator->static_.monitor; - if (MONITORSTR == "unset") - m_monitor = PMONITOR; - else { - const auto MONITOR = g_pCompositor->getMonitorFromString(MONITORSTR); - - if (MONITOR) { - m_monitor = MONITOR; - - const auto PMONITORFROMID = m_monitor.lock(); - - if (m_monitor != PMONITOR) { - g_pKeybindManager->m_dispatchers["focusmonitor"](std::to_string(monitorID())); - PMONITOR = PMONITORFROMID; - } - m_workspace = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace; - PWORKSPACE = m_workspace; - - Log::logger->log(Log::DEBUG, "Rule monitor, applying to {:mw}", m_self.lock()); - requestedFSMonitor = MONITOR_INVALID; - } else - Log::logger->log(Log::ERR, "No monitor in monitor {} rule", MONITORSTR); - } - } - - if (!m_ruleApplicator->static_.workspace.empty()) { - const auto WORKSPACERQ = m_ruleApplicator->static_.workspace; - - if (WORKSPACERQ == "unset") - requestedWorkspace = ""; - else - requestedWorkspace = WORKSPACERQ; - - const auto JUSTWORKSPACE = WORKSPACERQ.contains(' ') ? WORKSPACERQ.substr(0, WORKSPACERQ.find_first_of(' ')) : WORKSPACERQ; - - if (JUSTWORKSPACE == PWORKSPACE->m_name || JUSTWORKSPACE == "name:" + PWORKSPACE->m_name) - requestedWorkspace = ""; - - Log::logger->log(Log::DEBUG, "Rule workspace matched by {}, {} applied.", m_self.lock(), m_ruleApplicator->static_.workspace); - requestedFSMonitor = MONITOR_INVALID; - } - - m_isFloating = m_ruleApplicator->static_.floating.value_or(m_isFloating); - m_target->setPseudo(m_ruleApplicator->static_.pseudo.value_or(m_target->isPseudo())); - m_noInitialFocus = m_ruleApplicator->static_.noInitialFocus.value_or(m_noInitialFocus); - m_pinned = m_ruleApplicator->static_.pin.value_or(m_pinned); - - if (m_ruleApplicator->static_.fullscreenStateClient || m_ruleApplicator->static_.fullscreenStateInternal) { - requestedFSState = Desktop::View::SFullscreenState{ - .internal = sc(m_ruleApplicator->static_.fullscreenStateInternal.value_or(0)), - .client = sc(m_ruleApplicator->static_.fullscreenStateClient.value_or(0)), - }; - } - - if (!m_ruleApplicator->static_.suppressEvent.empty()) { - for (const auto& var : m_ruleApplicator->static_.suppressEvent) { - if (var == "fullscreen") - m_suppressedEvents |= Desktop::View::SUPPRESS_FULLSCREEN; - else if (var == "maximize") - m_suppressedEvents |= Desktop::View::SUPPRESS_MAXIMIZE; - else if (var == "activate") - m_suppressedEvents |= Desktop::View::SUPPRESS_ACTIVATE; - else if (var == "activatefocus") - m_suppressedEvents |= Desktop::View::SUPPRESS_ACTIVATE_FOCUSONLY; - else if (var == "fullscreenoutput") - m_suppressedEvents |= Desktop::View::SUPPRESS_FULLSCREEN_OUTPUT; - else - Log::logger->log(Log::ERR, "Error while parsing suppressevent windowrule: unknown event type {}", var); - } - } - - if (m_ruleApplicator->static_.fullscreen.value_or(false)) - requestedInternalFSMode = FSMODE_FULLSCREEN; - - if (m_ruleApplicator->static_.maximize.value_or(false)) - requestedInternalFSMode = FSMODE_MAXIMIZED; - - if (!m_ruleApplicator->static_.group.empty()) { - if (!(m_groupRules & Desktop::View::GROUP_OVERRIDE) && trim(m_ruleApplicator->static_.group) != "group") { - CVarList2 vars(std::string{m_ruleApplicator->static_.group}, 0, 's'); - std::string vPrev = ""; - - for (auto const& v : vars) { - if (v == "group") - continue; - - if (v == "set") { - m_groupRules |= Desktop::View::GROUP_SET; - } else if (v == "new") { - // shorthand for `group barred set` - m_groupRules |= (Desktop::View::GROUP_SET | Desktop::View::GROUP_BARRED); - } else if (v == "lock") { - m_groupRules |= Desktop::View::GROUP_LOCK; - } else if (v == "invade") { - m_groupRules |= Desktop::View::GROUP_INVADE; - } else if (v == "barred") { - m_groupRules |= Desktop::View::GROUP_BARRED; - } else if (v == "deny") { - m_groupRules |= Desktop::View::GROUP_DENY; - } else if (v == "override") { - // Clear existing rules - m_groupRules = Desktop::View::GROUP_OVERRIDE; - } else if (v == "unset") { - // Clear existing rules and stop processing - m_groupRules = Desktop::View::GROUP_OVERRIDE; - break; - } else if (v == "always") { - if (vPrev == "set" || vPrev == "group") - m_groupRules |= Desktop::View::GROUP_SET_ALWAYS; - else if (vPrev == "lock") - m_groupRules |= Desktop::View::GROUP_LOCK_ALWAYS; - else - Log::logger->log(Log::ERR, "windowrule `group` does not support `{} always`", vPrev); - } - vPrev = v; - } - } - } - - if (m_ruleApplicator->static_.content) - setContentType(sc(m_ruleApplicator->static_.content.value())); - - if (m_ruleApplicator->static_.noCloseFor) - m_closeableSince = Time::steadyNow() + std::chrono::milliseconds(m_ruleApplicator->static_.noCloseFor.value()); - } - - // make it uncloseable if it's a Hyprland dialog - // TODO: make some closeable? - if (CAsyncDialogBox::isAsyncDialogBox(getPID())) - m_closeableSince = Time::steadyNow() + std::chrono::years(10 /* Should be enough, no? */); - - // disallow tiled pinned - if (m_pinned && !m_isFloating) - m_pinned = false; - - CVarList2 WORKSPACEARGS = CVarList2(std::move(requestedWorkspace), 0, ' ', false, false); - - if (!WORKSPACEARGS[0].empty()) { - WORKSPACEID requestedWorkspaceID; - std::string requestedWorkspaceName; - if (WORKSPACEARGS.contains("silent")) - workspaceSilent = true; - - auto joined = WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0); - if (joined.starts_with("empty") && PWORKSPACE->getWindows() == 0) { - requestedWorkspaceID = PWORKSPACE->m_id; - requestedWorkspaceName = PWORKSPACE->m_name; - } else { - auto result = getWorkspaceIDNameFromString(joined); - requestedWorkspaceID = result.id; - requestedWorkspaceName = result.name; - } - - if (requestedWorkspaceID != WORKSPACE_INVALID) { - auto pWorkspace = g_pCompositor->getWorkspaceByID(requestedWorkspaceID); - - if (!pWorkspace) - pWorkspace = g_pCompositor->createNewWorkspace(requestedWorkspaceID, monitorID(), requestedWorkspaceName, false); - - PWORKSPACE = pWorkspace; - - m_workspace = pWorkspace; - m_monitor = pWorkspace->m_monitor; - - if (m_monitor.lock()->m_activeSpecialWorkspace && !pWorkspace->m_isSpecialWorkspace) - workspaceSilent = true; - - if (!workspaceSilent) { - if (pWorkspace->m_isSpecialWorkspace) - pWorkspace->m_monitor->setSpecialWorkspace(pWorkspace); - else if (PMONITOR->activeWorkspaceID() != requestedWorkspaceID && !m_noInitialFocus) - g_pKeybindManager->m_dispatchers["workspace"](requestedWorkspaceName); - - PMONITOR = Desktop::focusState()->monitor(); - } - - requestedFSMonitor = MONITOR_INVALID; - } else - workspaceSilent = false; - } - - if (m_suppressedEvents & Desktop::View::SUPPRESS_FULLSCREEN_OUTPUT) - requestedFSMonitor = MONITOR_INVALID; - else if (requestedFSMonitor != MONITOR_INVALID) { - if (const auto PM = g_pCompositor->getMonitorFromID(requestedFSMonitor); PM) - m_monitor = PM; - - const auto PMONITORFROMID = m_monitor.lock(); - - if (m_monitor != PMONITOR) { - g_pKeybindManager->m_dispatchers["focusmonitor"](std::to_string(monitorID())); - PMONITOR = PMONITORFROMID; - } - m_workspace = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace; - PWORKSPACE = m_workspace; - - Log::logger->log(Log::DEBUG, "Requested monitor, applying to {:mw}", m_self.lock()); - } - - if (PWORKSPACE->m_defaultFloating) - m_isFloating = true; - - if (PWORKSPACE->m_defaultPseudo) { - CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(m_self.lock()); - m_target->setPseudoSize(Vector2D{desiredGeometry.width, desiredGeometry.height}); - m_target->setPseudo(true); - } - - updateWindowData(); - - // Verify window swallowing. Get the swallower before calling onWindowCreated(m_self.lock()) because getSwallower() wouldn't get it after if m_self.lock() gets auto grouped. - const auto SWALLOWER = getSwallower(); - m_swallowed = SWALLOWER; - if (m_swallowed) - m_swallowed->m_currentlySwallowed = true; - - // emit the IPC event before the layout might focus the window to avoid a focus event first - g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", m_self.lock(), PWORKSPACE->m_name, m_class, m_title)}); - Event::bus()->m_events.window.openEarly.emit(m_self.lock()); - - if (*PAUTOGROUP // auto_group enabled - && Desktop::focusState()->window() // focused window exists - && canBeGroupedInto(Desktop::focusState()->window()->m_group) // we can group - && Desktop::focusState()->window()->m_workspace == m_workspace // workspaces match, we're not opening on another ws - && !g_pXWaylandManager->shouldBeFloated(m_self.lock()) && !isX11OverrideRedirect() // not a window that should float or X11 - && !(m_isFloating && !Desktop::focusState()->window()->m_isFloating) // do not auto-group a floated window into a tiled group - && !isModal() // no modal grouping - ) { - // add to group if we are focused on one - Desktop::focusState()->window()->m_group->add(m_self.lock()); - } else - g_layoutManager->newTarget(m_target, m_workspace->m_space); - - if (!m_group && (m_groupRules & GROUP_SET)) - m_group = CGroup::create({m_self}); - - if (m_isFloating) { - m_createdOverFullscreen = true; - - // set the pseudo size to the GOAL of our current size - // because the windows are animated on RealSize - m_target->setPseudoSize(m_realSize->goal()); - - g_pCompositor->changeWindowZOrder(m_self.lock(), true); - } else { - bool setPseudo = false; - - if (!m_ruleApplicator->static_.size.empty()) { - const auto COMPUTED = calculateExpression(m_ruleApplicator->static_.size); - if (!COMPUTED) - Log::logger->log(Log::ERR, "failed to parse {} as an expression", m_ruleApplicator->static_.size); - else { - setPseudo = true; - m_target->setPseudoSize(*COMPUTED); - setHidden(false); - } - } - - if (!setPseudo) - m_target->setPseudoSize(m_realSize->goal() - Vector2D(10, 10)); - } - - const auto PFOCUSEDWINDOWPREV = Desktop::focusState()->window(); - - if (m_ruleApplicator->allowsInput().valueOrDefault()) { // if default value wasn't set to false getPriority() would throw an exception - m_ruleApplicator->noFocusOverride(Desktop::Types::COverridableVar(false, m_ruleApplicator->allowsInput().getPriority())); - m_noInitialFocus = false; - m_X11ShouldntFocus = false; - } - - // check LS focus grab - const auto PFORCEFOCUS = g_pCompositor->getForceFocus(); - const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(Desktop::focusState()->surface()); - if (PLSFROMFOCUS && PLSFROMFOCUS->m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) - m_noInitialFocus = true; - - if (m_workspace->m_hasFullscreenWindow && !requestedInternalFSMode.has_value() && !requestedClientFSMode.has_value() && !m_isFloating) { - if (*PNEWTAKESOVERFS == 0) - m_noInitialFocus = true; - else if (*PNEWTAKESOVERFS == 1) - requestedInternalFSMode = m_workspace->m_fullscreenMode; - else if (*PNEWTAKESOVERFS == 2) - g_pCompositor->setWindowFullscreenInternal(m_workspace->getFullscreenWindow(), FSMODE_NONE); - } - - if (!m_ruleApplicator->noFocus().valueOrDefault() && !m_noInitialFocus && (!isX11OverrideRedirect() || (m_isX11 && m_xwaylandSurface->wantsFocus())) && !workspaceSilent && - (!PFORCEFOCUS || PFORCEFOCUS == m_self.lock()) && !g_pInputManager->isConstrained()) { - - // this window should gain focus: if it's grouped, preserve fullscreen state. - const bool SAME_GROUP = m_group && m_group->has(LAST_FOCUS_WINDOW); - - if (IS_LAST_IN_FS && SAME_GROUP) { - Desktop::focusState()->rawWindowFocus(m_self.lock(), FOCUS_REASON_NEW_WINDOW); - g_pCompositor->setWindowFullscreenInternal(m_self.lock(), LAST_FS_MODE); - } else - Desktop::focusState()->fullWindowFocus(m_self.lock(), FOCUS_REASON_NEW_WINDOW); - - m_activeInactiveAlpha->setValueAndWarp(*PACTIVEALPHA); - m_dimPercent->setValueAndWarp(m_ruleApplicator->noDim().valueOrDefault() ? 0.f : *PDIMSTRENGTH); - } else { - m_activeInactiveAlpha->setValueAndWarp(*PINACTIVEALPHA); - m_dimPercent->setValueAndWarp(0); - } - - if (requestedClientFSMode.has_value() && (m_suppressedEvents & Desktop::View::SUPPRESS_FULLSCREEN)) - requestedClientFSMode = sc(sc(requestedClientFSMode.value_or(FSMODE_NONE)) & ~sc(FSMODE_FULLSCREEN)); - if (requestedClientFSMode.has_value() && (m_suppressedEvents & Desktop::View::SUPPRESS_MAXIMIZE)) - requestedClientFSMode = sc(sc(requestedClientFSMode.value_or(FSMODE_NONE)) & ~sc(FSMODE_MAXIMIZED)); - - if (!m_noInitialFocus && (requestedInternalFSMode.has_value() || requestedClientFSMode.has_value() || requestedFSState.has_value())) { - // fix fullscreen on requested (basically do a switcheroo) - if (m_workspace->m_hasFullscreenWindow) - g_pCompositor->setWindowFullscreenInternal(m_workspace->getFullscreenWindow(), FSMODE_NONE); - - m_realPosition->warp(); - m_realSize->warp(); - if (requestedFSState.has_value()) { - m_ruleApplicator->syncFullscreenOverride(Desktop::Types::COverridableVar(false, Desktop::Types::PRIORITY_WINDOW_RULE)); - g_pCompositor->setWindowFullscreenState(m_self.lock(), requestedFSState.value()); - } else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !m_ruleApplicator->syncFullscreen().valueOrDefault()) - g_pCompositor->setWindowFullscreenState(m_self.lock(), - Desktop::View::SFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()}); - else if (requestedInternalFSMode.has_value()) - g_pCompositor->setWindowFullscreenInternal(m_self.lock(), requestedInternalFSMode.value()); - else if (requestedClientFSMode.has_value()) - g_pCompositor->setWindowFullscreenClient(m_self.lock(), requestedClientFSMode.value()); - } - - // recheck idle inhibitors - g_pInputManager->recheckIdleInhibitorStatus(); - - updateToplevel(); - m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL); - - if (workspaceSilent) { - if (validMapped(PFOCUSEDWINDOWPREV)) { - Desktop::focusState()->rawWindowFocus(PFOCUSEDWINDOWPREV, FOCUS_REASON_NEW_WINDOW); - PFOCUSEDWINDOWPREV->updateWindowDecos(); // need to for some reason i cba to find out why - } else if (!PFOCUSEDWINDOWPREV) - Desktop::focusState()->rawWindowFocus(nullptr, FOCUS_REASON_NEW_WINDOW); - } - - // swallow - if (SWALLOWER) { - g_layoutManager->removeTarget(SWALLOWER->layoutTarget()); - SWALLOWER->setHidden(true); - } - - m_firstMap = false; - - Log::logger->log(Log::DEBUG, "Map request dispatched, monitor {}, window pos: {:5j}, window size: {:5j}", PMONITOR->m_name, m_realPosition->goal(), m_realSize->goal()); - - // emit the hook event here after basic stuff has been initialized - Event::bus()->m_events.window.open.emit(m_self.lock()); - - // apply data from default decos. Borders, shadows. - g_pDecorationPositioner->forceRecalcFor(m_self.lock()); - updateWindowDecos(); - layoutTarget()->recalc(); - - // do animations - g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_IN); - - m_realPosition->setCallbackOnEnd(setVector2DAnimToMove); - m_realSize->setCallbackOnEnd(setVector2DAnimToMove); - - // recalc the values for this window - updateDecorationValues(); - // avoid this window being visible - if (PWORKSPACE->m_hasFullscreenWindow && !isFullscreen() && !m_isFloating) - m_alpha->setValueAndWarp(0.f); - - g_pCompositor->setPreferredScaleForSurface(wlSurface()->resource(), PMONITOR->m_scale); - g_pCompositor->setPreferredTransformForSurface(wlSurface()->resource(), PMONITOR->m_transform); - - if (g_pSeatManager->m_mouse.expired() || !g_pInputManager->isConstrained()) - g_pInputManager->sendMotionEventsToFocused(); - - // fix some xwayland apps that don't behave nicely - m_reportedSize = m_pendingReportedSize; - - if (m_workspace) - m_workspace->updateWindows(); - - if (PMONITOR && isX11OverrideRedirect()) { - static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - if (*PXWLFORCESCALEZERO) - m_X11SurfaceScaledBy = PMONITOR->m_scale; - } -} - -void CWindow::unmapWindow() { - Log::logger->log(Log::DEBUG, "{:c} unmapped", m_self.lock()); - - static auto PEXITRETAINSFS = CConfigValue("misc:exit_window_retains_fullscreen"); - - const auto CURRENTWINDOWFSSTATE = isFullscreen(); - const auto CURRENTFSMODE = m_fullscreenState.internal; - - if (!wlSurface()->exists() || !m_isMapped) { - Log::logger->log(Log::WARN, "{} unmapped without being mapped??", m_self.lock()); - m_fadingOut = false; - return; - } - - const auto PMONITOR = m_monitor.lock(); - if (PMONITOR) { - m_originalClosedPos = m_realPosition->value() - PMONITOR->m_position; - m_originalClosedSize = m_realSize->value(); - m_originalClosedExtents = getFullWindowExtents(); - } - - m_events.unmap.emit(); - g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", m_self.lock())}); - Event::bus()->m_events.window.close.emit(m_self.lock()); - - if (m_isFloating && !m_isX11 && m_ruleApplicator->persistentSize().valueOrDefault()) { - Log::logger->log(Log::DEBUG, "storing floating size {}x{} for window {}::{} on close", m_realSize->value().x, m_realSize->value().y, m_class, m_title); - g_pConfigManager->storeFloatingSize(m_self.lock(), m_realSize->value()); - } - - if (isFullscreen()) - g_pCompositor->setWindowFullscreenInternal(m_self.lock(), FSMODE_NONE); - - // Allow the renderer to catch the last frame. - if (g_pHyprRenderer->shouldRenderWindow(m_self.lock())) - g_pHyprRenderer->makeSnapshot(m_self.lock()); - - // swallowing - if (valid(m_swallowed)) { - if (m_swallowed->m_currentlySwallowed) { - m_swallowed->m_currentlySwallowed = false; - m_swallowed->setHidden(false); - - if (m_group) - m_swallowed->m_groupSwallowed = true; // flag for the swallowed window to be created into the group where it belongs when auto_group = false. - - g_layoutManager->newTarget(m_swallowed->layoutTarget(), m_workspace->m_space); - } - - m_swallowed->m_groupSwallowed = false; - m_swallowed.reset(); - } - - bool wasLastWindow = false; - PHLWINDOW nextInGroup = [this] -> PHLWINDOW { - if (!m_group) - return nullptr; - - // walk the history to find a suitable window - const auto HISTORY = Desktop::History::windowTracker()->fullHistory(); - for (const auto& w : HISTORY | std::views::reverse) { - if (!w || !w->m_isMapped || w == m_self) - continue; - - if (!m_group->has(w.lock())) - continue; - - return w.lock(); - } - - return nullptr; - }(); - - if (m_self.lock() == Desktop::focusState()->window()) { - wasLastWindow = true; - Desktop::focusState()->resetWindowFocus(); - - g_pInputManager->releaseAllMouseButtons(); - } - - if (m_self.lock() == g_layoutManager->dragController()->target()) - CKeybindManager::changeMouseBindMode(MBIND_INVALID); - - // remove the fullscreen window status from workspace if we closed it - const auto PWORKSPACE = m_workspace; - - if (PWORKSPACE->m_hasFullscreenWindow && isFullscreen()) - PWORKSPACE->m_hasFullscreenWindow = false; - - if (m_group) - m_group->remove(m_self.lock()); - - g_layoutManager->removeTarget(m_target); - - g_pHyprRenderer->damageWindow(m_self.lock()); - - // do this after onWindowRemoved because otherwise it'll think the window is invalid - m_isMapped = false; - - // refocus on a new window if needed - if (wasLastWindow) { - static auto FOCUSONCLOSE = CConfigValue("input:focus_on_close"); - PHLWINDOW candidate = nextInGroup; - - if (!candidate) { - if (*FOCUSONCLOSE) - candidate = (g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), - Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING)); - else { - const auto CAND = g_layoutManager->getNextCandidate(m_workspace->m_space, layoutTarget()); - if (CAND) - candidate = CAND->window(); - } - } - - Log::logger->log(Log::DEBUG, "On closed window, new focused candidate is {}", candidate); - - if (candidate != Desktop::focusState()->window() && candidate) { - if (candidate == nextInGroup) - Desktop::focusState()->rawWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE); - else - Desktop::focusState()->fullWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE); - - if ((*PEXITRETAINSFS || candidate == nextInGroup) && CURRENTWINDOWFSSTATE) - g_pCompositor->setWindowFullscreenInternal(candidate, CURRENTFSMODE); - } - - if (!candidate && m_workspace && m_workspace->getWindows() == 0) - g_pInputManager->refocus(); - - g_pInputManager->sendMotionEventsToFocused(); - - // CWindow::onUnmap will remove this window's active status, but we can't really do it above. - if (m_self.lock() == Desktop::focusState()->window() || !Desktop::focusState()->window()) { - g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); - g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""}); - - Event::bus()->m_events.window.active.emit(m_self.lock(), FOCUS_REASON_OTHER); - } - } else { - Log::logger->log(Log::DEBUG, "Unmapped was not focused, ignoring a refocus."); - } - - m_fadingOut = true; - - g_pCompositor->addToFadingOutSafe(m_self.lock()); - - if (!m_X11DoesntWantBorders) // don't animate out if they weren't animated in. - *m_realPosition = m_realPosition->value() + Vector2D(0.01f, 0.01f); // it has to be animated, otherwise CesktopAnimationManager will ignore it - - // anims - g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT); - - // recheck idle inhibitors - g_pInputManager->recheckIdleInhibitorStatus(); - - // force report all sizes (QT sometimes has an issue with this) - if (m_workspace) - m_workspace->forceReportSizesToWindows(); - - // update lastwindow after focus - onUnmap(); -} - -void CWindow::commitWindow() { - if (!m_isX11 && m_xdgSurface->m_initialCommit) { - // try to calculate static rules already for any floats - m_ruleApplicator->readStaticRules(true); - - const Vector2D predSize = !m_ruleApplicator->static_.floating.value_or(false) // no float rule - && !m_isFloating // not floating - && !parent() // no parents - && !g_pXWaylandManager->shouldBeFloated(m_self.lock(), true) // should not be floated - ? - g_layoutManager->predictSizeForNewTiledTarget().value_or(Vector2D{}) : - Vector2D{}; - - Log::logger->log(Log::DEBUG, "Layout predicts size {} for {}", predSize, m_self.lock()); - - m_xdgSurface->m_toplevel->setSize(predSize); - return; - } - - if (!m_isMapped || isHidden()) - return; - - if (m_isX11) - m_reportedSize = m_pendingReportedSize; - - if (!m_isX11 && !isFullscreen() && m_isFloating) { - const auto MINSIZE = m_xdgSurface->m_toplevel->layoutMinSize(); - const auto MAXSIZE = m_xdgSurface->m_toplevel->layoutMaxSize(); - - if (clampWindowSize(MINSIZE, MAXSIZE > Vector2D{1, 1} ? std::optional{MAXSIZE} : std::nullopt)) - g_pHyprRenderer->damageWindow(m_self.lock()); - } - - if (!m_workspace->m_visible) - return; - - const auto PMONITOR = m_monitor.lock(); - - g_pHyprRenderer->damageSurface(wlSurface()->resource(), m_realPosition->goal().x, m_realPosition->goal().y, m_isX11 ? 1.0 / m_X11SurfaceScaledBy : 1.0); - - if (!m_isX11) { - m_subsurfaceHead->recheckDamageForSubsurfaces(); - m_popupHead->recheckTree(); - } - - // tearing: if solitary, redraw it. This still might be a single surface window - if (PMONITOR && PMONITOR->m_solitaryClient.lock() == m_self.lock() && canBeTorn() && PMONITOR->m_tearingState.canTear && wlSurface()->resource()->m_current.texture) { - CRegion damageBox{wlSurface()->resource()->m_current.accumulateBufferDamage()}; - - if (!damageBox.empty()) { - if (PMONITOR->m_tearingState.busy) { - PMONITOR->m_tearingState.frameScheduledWhileBusy = true; - } else { - PMONITOR->m_tearingState.nextRenderTorn = true; - g_pHyprRenderer->renderMonitor(PMONITOR); - } - } - } -} - -void CWindow::destroyWindow() { - Log::logger->log(Log::DEBUG, "{:c} destroyed, queueing.", m_self.lock()); - - if (m_self.lock() == Desktop::focusState()->window()) { - Desktop::focusState()->window().reset(); - Desktop::focusState()->surface().reset(); - } - - wlSurface()->unassign(); - - m_listeners = {}; - - g_layoutManager->removeTarget(m_target); - - m_readyToDelete = true; - - m_xdgSurface.reset(); - - m_listeners.unmap.reset(); - m_listeners.destroy.reset(); - m_listeners.map.reset(); - m_listeners.commit.reset(); - - if (!m_fadingOut) { - Log::logger->log(Log::DEBUG, "Unmapped {} removed instantly", m_self.lock()); - g_pCompositor->removeWindowFromVectorSafe(m_self.lock()); // most likely X11 unmanaged or sumn - } -} - -void CWindow::activateX11() { - Log::logger->log(Log::DEBUG, "X11 Activate request for window {}", m_self.lock()); - - if (isX11OverrideRedirect()) { - - Log::logger->log(Log::DEBUG, "Unmanaged X11 {} requests activate", m_self.lock()); - - if (Desktop::focusState()->window() && Desktop::focusState()->window()->getPID() != getPID()) - return; - - if (!m_xwaylandSurface->wantsFocus()) - return; - - Desktop::focusState()->fullWindowFocus(m_self.lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE); - return; - } - - if (m_self.lock() == Desktop::focusState()->window() || (m_suppressedEvents & Desktop::View::SUPPRESS_ACTIVATE)) - return; - - activate(); -} - -void CWindow::unmanagedSetGeometry() { - if (!m_isMapped || !m_xwaylandSurface || !m_xwaylandSurface->m_overrideRedirect) - return; - - const auto POS = m_realPosition->goal(); - const auto SIZ = m_realSize->goal(); - - if (m_xwaylandSurface->m_geometry.size() > Vector2D{1, 1}) - setHidden(false); - else - setHidden(true); - - if (isFullscreen() || !m_isFloating) { - sendWindowSize(true); - g_pHyprRenderer->damageWindow(m_self.lock()); - return; - } - - static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - - const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords(m_xwaylandSurface->m_geometry.pos()); - - const auto PMONITOR = m_monitor.lock(); - const auto XWLSCALE = (*PXWLFORCESCALEZERO && PMONITOR) ? PMONITOR->m_scale : 1.0; - const auto LOGICALGEOSIZE = m_xwaylandSurface->m_geometry.size() / XWLSCALE; - - if (abs(std::floor(POS.x) - LOGICALPOS.x) > 2 || abs(std::floor(POS.y) - LOGICALPOS.y) > 2 || abs(std::floor(SIZ.x) - LOGICALGEOSIZE.x) > 2 || - abs(std::floor(SIZ.y) - LOGICALGEOSIZE.y) > 2) { - Log::logger->log(Log::DEBUG, "Unmanaged window {} requests geometry update to {:j} {:j}", m_self.lock(), LOGICALPOS, m_xwaylandSurface->m_geometry.size()); - - g_pHyprRenderer->damageWindow(m_self.lock()); - m_realPosition->setValueAndWarp(Vector2D(LOGICALPOS.x, LOGICALPOS.y)); - - if (abs(std::floor(SIZ.x) - LOGICALGEOSIZE.x) > 2 || abs(std::floor(SIZ.y) - LOGICALGEOSIZE.y) > 2) - m_realSize->setValueAndWarp(LOGICALGEOSIZE); - - m_position = m_realPosition->goal(); - m_size = m_realSize->goal(); - - m_workspace = g_pCompositor->getMonitorFromVector(m_realPosition->value() + m_realSize->value() / 2.f)->m_activeWorkspace; - - g_pCompositor->changeWindowZOrder(m_self.lock(), true); - updateWindowDecos(); - g_pHyprRenderer->damageWindow(m_self.lock()); - - m_reportedPosition = m_realPosition->goal(); - m_pendingReportedSize = m_realSize->goal(); - } -} - -std::optional CWindow::minSize() { - // first check for overrides - if (m_ruleApplicator->minSize().hasValue()) - return m_ruleApplicator->minSize().value(); - - // then check if we have any proto overrides - bool hasSizeHints = m_xwaylandSurface ? m_xwaylandSurface->m_sizeHints : false; - bool hasTopLevel = m_xdgSurface ? m_xdgSurface->m_toplevel : false; - if ((m_isX11 && !hasSizeHints) || (!m_isX11 && !hasTopLevel)) - return std::nullopt; - - Vector2D minSize = m_isX11 ? Vector2D(m_xwaylandSurface->m_sizeHints->min_width, m_xwaylandSurface->m_sizeHints->min_height) : m_xdgSurface->m_toplevel->layoutMinSize(); - - minSize = minSize.clamp({1, 1}); - - return minSize; -} - -std::optional CWindow::maxSize() { - // first check for overrides - if (m_ruleApplicator->maxSize().hasValue()) - return m_ruleApplicator->maxSize().value(); - - // then check if we have any proto overrides - if (((m_isX11 && !m_xwaylandSurface->m_sizeHints) || (!m_isX11 && (!m_xdgSurface || !m_xdgSurface->m_toplevel)) || m_ruleApplicator->noMaxSize().valueOrDefault())) - return std::nullopt; - - constexpr const double NO_MAX_SIZE_LIMIT = std::numeric_limits::max(); - - Vector2D maxSize = m_isX11 ? Vector2D(m_xwaylandSurface->m_sizeHints->max_width, m_xwaylandSurface->m_sizeHints->max_height) : m_xdgSurface->m_toplevel->layoutMaxSize(); - - if (maxSize.x < 5) - maxSize.x = NO_MAX_SIZE_LIMIT; - if (maxSize.y < 5) - maxSize.y = NO_MAX_SIZE_LIMIT; - - return maxSize; -} - -SP CWindow::layoutTarget() { - return m_group ? m_group->m_target : m_target; -} - -bool CWindow::canBeGroupedInto(SP group) { - if (!group) - return false; - - if (isX11OverrideRedirect()) - return false; - - static auto ALLOWGROUPMERGE = CConfigValue("group:merge_groups_on_drag"); - bool isGroup = m_group; - bool disallowDragIntoGroup = g_layoutManager->dragController()->wasDraggingWindow() && isGroup && !sc(*ALLOWGROUPMERGE); - return !g_pKeybindManager->m_groupsLocked // global group lock disengaged - && ((m_groupRules & GROUP_INVADE && m_firstMap) // window ignore local group locks, or - || (!group->locked() // target unlocked - && !(m_group && m_group->locked()))) // source unlocked or isn't group - && !(m_groupRules & GROUP_DENY) // source is not denied entry - && !(m_groupRules & GROUP_BARRED && m_firstMap) // group rule doesn't prevent adding window - && !disallowDragIntoGroup; // config allows groups to be merged -} diff --git a/src/desktop/view/Window.hpp b/src/desktop/view/Window.hpp deleted file mode 100644 index d689ae3f..00000000 --- a/src/desktop/view/Window.hpp +++ /dev/null @@ -1,453 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "View.hpp" -#include "../../config/ConfigDataValues.hpp" -#include "../../helpers/AnimatedVariable.hpp" -#include "../../helpers/TagKeeper.hpp" -#include "../../macros.hpp" -#include "../../managers/XWaylandManager.hpp" -#include "../../render/decorations/IHyprWindowDecoration.hpp" -#include "../../render/Transformer.hpp" -#include "../DesktopTypes.hpp" -#include "Popup.hpp" -#include "Subsurface.hpp" -#include "WLSurface.hpp" -#include "../Workspace.hpp" -#include "../rule/windowRule/WindowRuleApplicator.hpp" -#include "../../protocols/types/ContentType.hpp" - -class CXDGSurfaceResource; -class CXWaylandSurface; -struct SWorkspaceRule; - -class IWindowTransformer; - -namespace Layout { - class ITarget; - class CWindowTarget; -} - -namespace Desktop { - enum eFocusReason : uint8_t; -} - -namespace Desktop::View { - - class CGroup; - - enum eGroupRules : uint8_t { - // effective only during first map, except for _ALWAYS variant - GROUP_NONE = 0, - GROUP_SET = 1 << 0, // Open as new group or add to focused group - GROUP_SET_ALWAYS = 1 << 1, - GROUP_BARRED = 1 << 2, // Don't insert to focused group. - GROUP_LOCK = 1 << 3, // Lock m_sGroupData.lock - GROUP_LOCK_ALWAYS = 1 << 4, - GROUP_INVADE = 1 << 5, // Force enter a group, event if lock is engaged - GROUP_OVERRIDE = 1 << 6, // Override other rules - GROUP_DENY = 1 << 7, // deny - }; - - enum eGetWindowProperties : uint8_t { - WINDOW_ONLY = 0, - RESERVED_EXTENTS = 1 << 0, - INPUT_EXTENTS = 1 << 1, - FULL_EXTENTS = 1 << 2, - FLOATING_ONLY = 1 << 3, - ALLOW_FLOATING = 1 << 4, - USE_PROP_TILED = 1 << 5, - SKIP_FULLSCREEN_PRIORITY = 1 << 6, - FOCUS_PRIORITY = 1 << 7, - }; - - enum eSuppressEvents : uint8_t { - SUPPRESS_NONE = 0, - SUPPRESS_FULLSCREEN = 1 << 0, - SUPPRESS_MAXIMIZE = 1 << 1, - SUPPRESS_ACTIVATE = 1 << 2, - SUPPRESS_ACTIVATE_FOCUSONLY = 1 << 3, - SUPPRESS_FULLSCREEN_OUTPUT = 1 << 4, - }; - - struct SWindowActiveEvent { - PHLWINDOW window = nullptr; - eFocusReason reason = sc(0) /* unknown */; - }; - - struct SInitialWorkspaceToken { - PHLWINDOWREF primaryOwner; - std::string workspace; - }; - - struct SFullscreenState { - eFullscreenMode internal = FSMODE_NONE; - eFullscreenMode client = FSMODE_NONE; - }; - - class CWindow : public IView { - public: - static PHLWINDOW create(SP); - static PHLWINDOW create(SP); - static PHLWINDOW fromView(SP); - - private: - CWindow(SP resource); - CWindow(SP surface); - - public: - virtual ~CWindow(); - - virtual eViewType type() const; - virtual bool visible() const; - virtual std::optional logicalBox() const; - virtual bool desktopComponent() const; - virtual std::optional surfaceLogicalBox() const; - - struct { - CSignalT<> destroy; - CSignalT<> unmap; - CSignalT<> hide; - CSignalT<> resize; - CSignalT<> monitorChanged; - } m_events; - - WP m_xdgSurface; - WP m_xwaylandSurface; - - SP m_target; - - // this is the position and size of the "bounding box" - Vector2D m_position = Vector2D(0, 0); - Vector2D m_size = Vector2D(0, 0); - - // this is the real position and size used to draw the thing - PHLANIMVAR m_realPosition; - PHLANIMVAR m_realSize; - - // for not spamming the protocols - Vector2D m_reportedPosition; - Vector2D m_reportedSize; - Vector2D m_pendingReportedSize; - std::optional> m_pendingSizeAck; - std::vector> m_pendingSizeAcks; - - // for floating window offset in workspace animations - Vector2D m_floatingOffset = Vector2D(0, 0); - - // for recovering relative cursor position - Vector2D m_relativeCursorCoordsOnLastWarp = Vector2D(-1, -1); - - bool m_firstMap = false; // for layouts - bool m_isFloating = false; - SFullscreenState m_fullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE}; - std::string m_title = ""; - std::string m_class = ""; - std::string m_initialTitle = ""; - std::string m_initialClass = ""; - PHLWORKSPACE m_workspace; - PHLMONITORREF m_monitor, m_prevMonitor; - - bool m_isMapped = false; - - bool m_requestsFloat = false; - - // This is for fullscreen apps - bool m_createdOverFullscreen = false; - - // XWayland stuff - bool m_isX11 = false; - bool m_X11DoesntWantBorders = false; - bool m_X11ShouldntFocus = false; - float m_X11SurfaceScaledBy = 1.f; - // - - // For nofocus - bool m_noInitialFocus = false; - - // Fullscreen and Maximize - bool m_wantsInitialFullscreen = false; - MONITORID m_wantsInitialFullscreenMonitor = MONITOR_INVALID; - - // bitfield suppressEvents - uint64_t m_suppressedEvents = SUPPRESS_NONE; - - // desktop components - SP m_subsurfaceHead; - SP m_popupHead; - - // Animated border - CGradientValueData m_realBorderColor = {0}; - CGradientValueData m_realBorderColorPrevious = {0}; - PHLANIMVAR m_borderFadeAnimationProgress; - PHLANIMVAR m_borderAngleAnimationProgress; - - // Fade in-out - PHLANIMVAR m_alpha; - bool m_fadingOut = false; - bool m_readyToDelete = false; - Vector2D m_originalClosedPos; // these will be used for calculations later on in - Vector2D m_originalClosedSize; // drawing the closing animations - SBoxExtents m_originalClosedExtents; - bool m_animatingIn = false; - - // For pinned (sticky) windows - bool m_pinned = false; - - // For preserving pinned state when fullscreening a pinned window - bool m_pinFullscreened = false; - - // urgency hint - bool m_isUrgent = false; - - // for proper cycling. While cycling we can't just move the pointers, so we need to keep track of the last cycled window. - PHLWINDOWREF m_lastCycledWindow; - - // Window decorations - // TODO: make this a SP. - std::vector> m_windowDecorations; - std::vector m_decosToRemove; - - // Special render data, rules, etc - UP m_ruleApplicator; - - // Transformers - std::vector> m_transformers; - - // for alpha - PHLANIMVAR m_activeInactiveAlpha; - PHLANIMVAR m_movingFromWorkspaceAlpha; - - // animated shadow color - PHLANIMVAR m_realShadowColor; - - // animated tint - PHLANIMVAR m_dimPercent; - - // animate moving to an invisible workspace - int m_monitorMovedFrom = -1; // -1 means not moving - PHLANIMVAR m_movingToWorkspaceAlpha; - - // swallowing - PHLWINDOWREF m_swallowed; - bool m_currentlySwallowed = false; - bool m_groupSwallowed = false; - - // for toplevel monitor events - MONITORID m_lastSurfaceMonitorID = -1; - - // initial token. Will be unregistered on workspace change or timeout of 2 minutes - std::string m_initialWorkspaceToken = ""; - - // for groups - SP m_group; - uint16_t m_groupRules = Desktop::View::GROUP_NONE; - - bool m_tearingHint = false; - - // Stable ID for ext_foreign_toplevel_list - const uint64_t m_stableID = 0x2137; - - // ANR - PHLANIMVAR m_notRespondingTint; - - // For the noclosefor windowrule - Time::steady_tp m_closeableSince = Time::steadyNow(); - - // For the list lookup - bool operator==(const CWindow& rhs) const { - return m_xdgSurface == rhs.m_xdgSurface && m_xwaylandSurface == rhs.m_xwaylandSurface && m_position == rhs.m_position && m_size == rhs.m_size && - m_fadingOut == rhs.m_fadingOut; - } - - // methods - CBox getFullWindowBoundingBox() const; - SBoxExtents getFullWindowExtents() const; - CBox getWindowBoxUnified(uint64_t props); - SBoxExtents getWindowExtentsUnified(uint64_t props); - CBox getWindowIdealBoundingBoxIgnoreReserved(); - void addWindowDeco(UP deco); - void updateWindowDecos(); - void removeWindowDeco(IHyprWindowDecoration* deco); - void uncacheWindowDecos(); - bool checkInputOnDecos(const eInputType, const Vector2D&, std::any = {}); - pid_t getPID(); - IHyprWindowDecoration* getDecorationByType(eDecorationType); - void updateToplevel(); - void updateSurfaceScaleTransformDetails(bool force = false); - void moveToWorkspace(PHLWORKSPACE); - PHLWINDOW x11TransientFor(); - void onUnmap(); - void onMap(); - void setHidden(bool hidden); - bool isHidden(); - void updateDecorationValues(); - SBoxExtents getFullWindowReservedArea(); - Vector2D middle(); - bool opaque(); - float rounding(); - float roundingPower(); - bool canBeTorn(); - void setSuspended(bool suspend); - bool visibleOnMonitor(PHLMONITOR pMonitor); - WORKSPACEID workspaceID(); - MONITORID monitorID(); - bool onSpecialWorkspace(); - void activate(bool force = false); - int surfacesCount(); - bool clampWindowSize(const std::optional minSize, const std::optional maxSize); - bool isFullscreen(); - bool isEffectiveInternalFSMode(const eFullscreenMode) const; - int getRealBorderSize() const; - float getScrollMouse(); - float getScrollTouchpad(); - bool isScrollMouseOverridden(); - bool isScrollTouchpadOverridden(); - void updateWindowData(); - void updateWindowData(const SWorkspaceRule&); - void onBorderAngleAnimEnd(WP pav); - bool isInCurvedCorner(double x, double y); - bool hasPopupAt(const Vector2D& pos); - int popupsCount(); - void setAnimationsToMove(); - void onWorkspaceAnimUpdate(); - void onFocusAnimUpdate(); - void onUpdateState(); - void onUpdateMeta(); - void onX11ConfigureRequest(CBox box); - void onResourceChangeX11(); - std::string fetchTitle(); - std::string fetchClass(); - void warpCursor(bool force = false); - PHLWINDOW getSwallower(); - bool isX11OverrideRedirect(); - bool isModal(); - Vector2D realToReportSize(); - Vector2D realToReportPosition(); - Vector2D xwaylandSizeToReal(Vector2D size); - Vector2D xwaylandPositionToReal(Vector2D size); - void updateX11SurfaceScale(); - void sendWindowSize(bool force = false); - NContentType::eContentType getContentType(); - void setContentType(NContentType::eContentType contentType); - void deactivateGroupMembers(); - bool isNotResponding(); - std::optional xdgTag(); - std::optional xdgDescription(); - PHLWINDOW parent(); - bool priorityFocus(); - SP getSolitaryResource(); - Vector2D getReportedSize(); - std::optional calculateExpression(const std::string& s); - std::optional minSize(); - std::optional maxSize(); - SP layoutTarget(); - bool canBeGroupedInto(SP group); - - CBox getWindowMainSurfaceBox() const { - return {m_realPosition->value().x, m_realPosition->value().y, m_realSize->value().x, m_realSize->value().y}; - } - - // listeners - void onAck(uint32_t serial); - - // - std::unordered_map getEnv(); - - // - PHLWINDOWREF m_self; - - // make private once we move listeners to inside CWindow - struct { - CHyprSignalListener map; - CHyprSignalListener ack; - CHyprSignalListener unmap; - CHyprSignalListener commit; - CHyprSignalListener destroy; - CHyprSignalListener activate; - CHyprSignalListener configureRequest; - CHyprSignalListener setGeometry; - CHyprSignalListener updateState; - CHyprSignalListener updateMetadata; - CHyprSignalListener resourceChange; - } m_listeners; - - private: - std::optional calculateSingleExpr(const std::string& s); - void mapWindow(); - void unmapWindow(); - void commitWindow(); - void destroyWindow(); - void activateX11(); - void unmanagedSetGeometry(); - - // For hidden windows and stuff - bool m_hidden = false; - bool m_suspended = false; - WORKSPACEID m_lastWorkspace = WORKSPACE_INVALID; - }; - - inline bool valid(PHLWINDOW w) { - return w.get(); - } - - inline bool valid(PHLWINDOWREF w) { - return !w.expired(); - } - - inline bool validMapped(PHLWINDOW w) { - if (!valid(w)) - return false; - return w->m_isMapped; - } - - inline bool validMapped(PHLWINDOWREF w) { - if (!valid(w)) - return false; - return w->m_isMapped; - } -} - -/** - format specification - - 'x', only address, equivalent of (uintpr_t)CWindow* - - 'm', with monitor id - - 'w', with workspace id - - 'c', with application class -*/ - -template -struct std::formatter : std::formatter { - bool formatAddressOnly = false; - bool formatWorkspace = false; - bool formatMonitor = false; - bool formatClass = false; - FORMAT_PARSE( // - FORMAT_FLAG('x', formatAddressOnly) // - FORMAT_FLAG('m', formatMonitor) // - FORMAT_FLAG('w', formatWorkspace) // - FORMAT_FLAG('c', formatClass), - PHLWINDOW) - - template - auto format(PHLWINDOW const& w, FormatContext& ctx) const { - auto&& out = ctx.out(); - if (formatAddressOnly) - return std::format_to(out, "{:x}", rc(w.get())); - if (!w) - return std::format_to(out, "[Window nullptr]"); - - std::format_to(out, "["); - std::format_to(out, "Window {:x}: title: \"{}\"", rc(w.get()), w->m_title); - if (formatWorkspace) - std::format_to(out, ", workspace: {}", w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID); - if (formatMonitor) - std::format_to(out, ", monitor: {}", w->monitorID()); - if (formatClass) - std::format_to(out, ", class: {}", w->m_class); - return std::format_to(out, "]"); - } -}; diff --git a/src/devices/IHID.hpp b/src/devices/IHID.hpp index c57778ff..b9cc4212 100644 --- a/src/devices/IHID.hpp +++ b/src/devices/IHID.hpp @@ -33,9 +33,8 @@ class IHID { virtual eHIDType getType(); struct { - CSignalT<> destroy; - } m_events; + CSignal destroy; + } events; - std::string m_deviceName; - std::string m_hlName; -}; + std::string deviceName, hlName; +}; \ No newline at end of file diff --git a/src/devices/IKeyboard.cpp b/src/devices/IKeyboard.cpp index ae6df1f5..89891ebd 100644 --- a/src/devices/IKeyboard.cpp +++ b/src/devices/IKeyboard.cpp @@ -28,39 +28,38 @@ eHIDType IKeyboard::getType() { } IKeyboard::~IKeyboard() { - m_events.destroy.emit(); + events.destroy.emit(); clearManuallyAllocd(); } void IKeyboard::clearManuallyAllocd() { - if (m_xkbStaticState) - xkb_state_unref(m_xkbStaticState); + if (xkbStaticState) + xkb_state_unref(xkbStaticState); - if (m_xkbState) - xkb_state_unref(m_xkbState); + if (xkbState) + xkb_state_unref(xkbState); - if (m_xkbKeymap) - xkb_keymap_unref(m_xkbKeymap); + if (xkbKeymap) + xkb_keymap_unref(xkbKeymap); - if (m_xkbSymState) - xkb_state_unref(m_xkbSymState); + if (xkbSymState) + xkb_state_unref(xkbSymState); - m_xkbSymState = nullptr; - m_xkbKeymap = nullptr; - m_xkbState = nullptr; - m_xkbStaticState = nullptr; - m_xkbKeymapFD.reset(); - m_xkbKeymapV1FD.reset(); + xkbSymState = nullptr; + xkbKeymap = nullptr; + xkbState = nullptr; + xkbStaticState = nullptr; + xkbKeymapFD.reset(); } void IKeyboard::setKeymap(const SStringRuleNames& rules) { - if (m_keymapOverridden) { - Log::logger->log(Log::DEBUG, "Ignoring setKeymap: keymap is overridden"); + if (keymapOverridden) { + Debug::log(LOG, "Ignoring setKeymap: keymap is overridden"); return; } - m_currentRules = rules; + currentRules = rules; xkb_rule_names XKBRULES = { .rules = rules.rules.c_str(), .model = rules.model.c_str(), @@ -72,69 +71,69 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) { const auto CONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!CONTEXT) { - Log::logger->log(Log::ERR, "setKeymap: CONTEXT null??"); + Debug::log(ERR, "setKeymap: CONTEXT null??"); return; } clearManuallyAllocd(); - Log::logger->log(Log::DEBUG, "Attempting to create a keymap for layout {} with variant {} (rules: {}, model: {}, options: {})", rules.layout, rules.variant, rules.rules, - rules.model, rules.options); + Debug::log(LOG, "Attempting to create a keymap for layout {} with variant {} (rules: {}, model: {}, options: {})", rules.layout, rules.variant, rules.rules, rules.model, + rules.options); - if (!m_xkbFilePath.empty()) { - auto path = absolutePath(m_xkbFilePath, g_pConfigManager->m_configCurrentPath); + if (!xkbFilePath.empty()) { + auto path = absolutePath(xkbFilePath, g_pConfigManager->configCurrentPath); if (FILE* const KEYMAPFILE = fopen(path.c_str(), "r"); !KEYMAPFILE) - Log::logger->log(Log::ERR, "Cannot open input:kb_file= file for reading"); + Debug::log(ERR, "Cannot open input:kb_file= file for reading"); else { - m_xkbKeymap = xkb_keymap_new_from_file(CONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + xkbKeymap = xkb_keymap_new_from_file(CONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); fclose(KEYMAPFILE); } } - if (!m_xkbKeymap) - m_xkbKeymap = xkb_keymap_new_from_names2(CONTEXT, &XKBRULES, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!xkbKeymap) + xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (!m_xkbKeymap) { + if (!xkbKeymap) { g_pConfigManager->addParseError("Invalid keyboard layout passed. ( rules: " + rules.rules + ", model: " + rules.model + ", variant: " + rules.variant + ", options: " + rules.options + ", layout: " + rules.layout + " )"); - Log::logger->log(Log::ERR, "Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded.", rules.layout, rules.variant, rules.rules, - rules.model, rules.options); + Debug::log(ERR, "Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded.", rules.layout, rules.variant, rules.rules, rules.model, + rules.options); memset(&XKBRULES, 0, sizeof(XKBRULES)); - m_currentRules.rules = ""; - m_currentRules.model = ""; - m_currentRules.variant = ""; - m_currentRules.options = ""; - m_currentRules.layout = "us"; + currentRules.rules = ""; + currentRules.model = ""; + currentRules.variant = ""; + currentRules.options = ""; + currentRules.layout = "us"; - m_xkbKeymap = xkb_keymap_new_from_names2(CONTEXT, &XKBRULES, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS); } - updateXKBTranslationState(m_xkbKeymap); + updateXKBTranslationState(xkbKeymap); - const auto NUMLOCKON = g_pConfigManager->getDeviceInt(m_hlName, "numlock_by_default", "input:numlock_by_default"); + const auto NUMLOCKON = g_pConfigManager->getDeviceInt(hlName, "numlock_by_default", "input:numlock_by_default"); if (NUMLOCKON == 1) { // lock numlock - const auto IDX = xkb_map_mod_get_index(m_xkbKeymap, XKB_MOD_NAME_NUM); + const auto IDX = xkb_map_mod_get_index(xkbKeymap, XKB_MOD_NAME_NUM); if (IDX != XKB_MOD_INVALID) - m_modifiersState.locked |= sc(1) << IDX; + modifiersState.locked |= (uint32_t)1 << IDX; // 0 to avoid mods getting stuck if depressed during reload - updateModifiers(0, 0, m_modifiersState.locked, m_modifiersState.group); + updateModifiers(0, 0, modifiersState.locked, modifiersState.group); } - for (size_t i = 0; i < std::min(LEDNAMES.size(), m_ledIndexes.size()); ++i) { - m_ledIndexes[i] = xkb_map_led_get_index(m_xkbKeymap, LEDNAMES[i]); - Log::logger->log(Log::DEBUG, "xkb: LED index {} (name {}) got index {}", i, LEDNAMES[i], m_ledIndexes[i]); + for (size_t i = 0; i < std::min(LEDNAMES.size(), ledIndexes.size()); ++i) { + ledIndexes[i] = xkb_map_led_get_index(xkbKeymap, LEDNAMES[i]); + Debug::log(LOG, "xkb: LED index {} (name {}) got index {}", i, LEDNAMES[i], ledIndexes[i]); } - for (size_t i = 0; i < std::min(MODNAMES.size(), m_modIndexes.size()); ++i) { - m_modIndexes[i] = xkb_map_mod_get_index(m_xkbKeymap, MODNAMES[i]); - Log::logger->log(Log::DEBUG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES[i], m_modIndexes[i]); + for (size_t i = 0; i < std::min(MODNAMES.size(), modIndexes.size()); ++i) { + modIndexes[i] = xkb_map_mod_get_index(xkbKeymap, MODNAMES[i]); + Debug::log(LOG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES[i], modIndexes[i]); } updateKeymapFD(); @@ -145,87 +144,70 @@ void IKeyboard::setKeymap(const SStringRuleNames& rules) { } void IKeyboard::updateKeymapFD() { - Log::logger->log(Log::DEBUG, "Updating keymap fd for keyboard {}", m_deviceName); + Debug::log(LOG, "Updating keymap fd for keyboard {}", deviceName); - if (m_xkbKeymapFD.isValid()) - m_xkbKeymapFD.reset(); + if (xkbKeymapFD.isValid()) + xkbKeymapFD.reset(); - if (m_xkbKeymapV1FD.isValid()) - m_xkbKeymapV1FD.reset(); + auto cKeymapStr = xkb_keymap_get_as_string(xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1); + xkbKeymapString = cKeymapStr; + free(cKeymapStr); - auto cKeymapStr = xkb_keymap_get_as_string(m_xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V2); - m_xkbKeymapString = cKeymapStr; - free(cKeymapStr); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors) - auto cKeymapV1Str = xkb_keymap_get_as_string(m_xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1); - m_xkbKeymapV1String = cKeymapV1Str; - free(cKeymapV1Str); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors) - - CFileDescriptor rw, ro, rwV1, roV1; - if (!allocateSHMFilePair(m_xkbKeymapString.length() + 1, rw, ro)) - Log::logger->log(Log::ERR, "IKeyboard: failed to allocate shm pair for the keymap"); - else if (!allocateSHMFilePair(m_xkbKeymapV1String.length() + 1, rwV1, roV1)) { - ro.reset(); + CFileDescriptor rw, ro; + if (!allocateSHMFilePair(xkbKeymapString.length() + 1, rw, ro)) + Debug::log(ERR, "IKeyboard: failed to allocate shm pair for the keymap"); + else { + auto keymapFDDest = mmap(nullptr, xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw.get(), 0); rw.reset(); - Log::logger->log(Log::ERR, "IKeyboard: failed to allocate shm pair for keymap V1"); - } else { - auto keymapFDDest = mmap(nullptr, m_xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw.get(), 0); - auto keymapV1FDDest = mmap(nullptr, m_xkbKeymapV1String.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rwV1.get(), 0); - rw.reset(); - rwV1.reset(); - - if (keymapFDDest == MAP_FAILED || keymapV1FDDest == MAP_FAILED) { - Log::logger->log(Log::ERR, "IKeyboard: failed to mmap a shm pair for the keymap"); + if (keymapFDDest == MAP_FAILED) { + Debug::log(ERR, "IKeyboard: failed to mmap a shm pair for the keymap"); ro.reset(); - roV1.reset(); } else { - memcpy(keymapFDDest, m_xkbKeymapString.c_str(), m_xkbKeymapString.length()); - munmap(keymapFDDest, m_xkbKeymapString.length() + 1); - m_xkbKeymapFD = std::move(ro); - memcpy(keymapV1FDDest, m_xkbKeymapV1String.c_str(), m_xkbKeymapV1String.length()); - munmap(keymapV1FDDest, m_xkbKeymapV1String.length() + 1); - m_xkbKeymapV1FD = std::move(roV1); + memcpy(keymapFDDest, xkbKeymapString.c_str(), xkbKeymapString.length()); + munmap(keymapFDDest, xkbKeymapString.length() + 1); + xkbKeymapFD = std::move(ro); } } - Log::logger->log(Log::DEBUG, "Updated keymap fd to {}, keymap V1 to: {}", m_xkbKeymapFD.get(), m_xkbKeymapV1FD.get()); + Debug::log(LOG, "Updated keymap fd to {}", xkbKeymapFD.get()); } void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) { - if (m_xkbStaticState) - xkb_state_unref(m_xkbStaticState); + if (xkbStaticState) + xkb_state_unref(xkbStaticState); - if (m_xkbState) - xkb_state_unref(m_xkbState); + if (xkbState) + xkb_state_unref(xkbState); - if (m_xkbSymState) - xkb_state_unref(m_xkbSymState); + if (xkbSymState) + xkb_state_unref(xkbSymState); - m_xkbState = nullptr; - m_xkbStaticState = nullptr; - m_xkbSymState = nullptr; + xkbState = nullptr; + xkbStaticState = nullptr; + xkbSymState = nullptr; if (keymap) { - Log::logger->log(Log::DEBUG, "Updating keyboard {:x}'s translation state from a provided keymap", rc(this)); - m_xkbStaticState = xkb_state_new(keymap); - m_xkbState = xkb_state_new(keymap); - m_xkbSymState = xkb_state_new(keymap); + Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this); + xkbStaticState = xkb_state_new(keymap); + xkbState = xkb_state_new(keymap); + xkbSymState = xkb_state_new(keymap); return; } - const auto KEYMAP = m_xkbKeymap; - const auto STATE = m_xkbState; + const auto KEYMAP = xkbKeymap; + const auto STATE = xkbState; const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP); const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); for (uint32_t i = 0; i < LAYOUTSNUM; ++i) { if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE) == 1) { - Log::logger->log(Log::DEBUG, "Updating keyboard {:x}'s translation state from an active index {}", rc(this), i); + Debug::log(LOG, "Updating keyboard {:x}'s translation state from an active index {}", (uintptr_t)this, i); - CVarList keyboardLayouts(m_currentRules.layout, 0, ','); - CVarList keyboardModels(m_currentRules.model, 0, ','); - CVarList keyboardVariants(m_currentRules.variant, 0, ','); + CVarList keyboardLayouts(currentRules.layout, 0, ','); + CVarList keyboardModels(currentRules.model, 0, ','); + CVarList keyboardVariants(currentRules.variant, 0, ','); xkb_rule_names rules = {.rules = "", .model = "", .layout = "", .variant = "", .options = ""}; @@ -238,24 +220,24 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) { rules.model = model.c_str(); rules.variant = variant.c_str(); - auto KEYMAP = xkb_keymap_new_from_names2(PCONTEXT, &rules, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + auto KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!KEYMAP) { - Log::logger->log(Log::ERR, "updateXKBTranslationState: keymap failed 1, fallback without model/variant"); + Debug::log(ERR, "updateXKBTranslationState: keymap failed 1, fallback without model/variant"); rules.model = ""; rules.variant = ""; - KEYMAP = xkb_keymap_new_from_names2(PCONTEXT, &rules, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); } if (!KEYMAP) { - Log::logger->log(Log::ERR, "updateXKBTranslationState: keymap failed 2, fallback to us"); + Debug::log(ERR, "updateXKBTranslationState: keymap failed 2, fallback to us"); rules.layout = "us"; - KEYMAP = xkb_keymap_new_from_names2(PCONTEXT, &rules, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); } - m_xkbState = xkb_state_new(KEYMAP); - m_xkbStaticState = xkb_state_new(KEYMAP); - m_xkbSymState = xkb_state_new(KEYMAP); + xkbState = xkb_state_new(KEYMAP); + xkbStaticState = xkb_state_new(KEYMAP); + xkbSymState = xkb_state_new(KEYMAP); xkb_keymap_unref(KEYMAP); xkb_context_unref(PCONTEXT); @@ -264,42 +246,29 @@ void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) { } } - Log::logger->log(Log::DEBUG, "Updating keyboard {:x}'s translation state from an unknown index", rc(this)); + Debug::log(LOG, "Updating keyboard {:x}'s translation state from an unknown index", (uintptr_t)this); xkb_rule_names rules = { - .rules = m_currentRules.rules.c_str(), - .model = m_currentRules.model.c_str(), - .layout = m_currentRules.layout.c_str(), - .variant = m_currentRules.variant.c_str(), - .options = m_currentRules.options.c_str(), + .rules = currentRules.rules.c_str(), + .model = currentRules.model.c_str(), + .layout = currentRules.layout.c_str(), + .variant = currentRules.variant.c_str(), + .options = currentRules.options.c_str(), }; - const auto NEWKEYMAP = xkb_keymap_new_from_names2(PCONTEXT, &rules, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + const auto NEWKEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - m_xkbState = xkb_state_new(NEWKEYMAP); - m_xkbStaticState = xkb_state_new(NEWKEYMAP); - m_xkbSymState = xkb_state_new(NEWKEYMAP); + xkbState = xkb_state_new(NEWKEYMAP); + xkbStaticState = xkb_state_new(NEWKEYMAP); + xkbSymState = xkb_state_new(NEWKEYMAP); xkb_keymap_unref(NEWKEYMAP); xkb_context_unref(PCONTEXT); } -std::optional IKeyboard::getActiveLayoutIndex() { - const auto KEYMAP = m_xkbKeymap; - const auto STATE = m_xkbState; - const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP); - - for (xkb_layout_index_t i = 0; i < LAYOUTSNUM; ++i) { - if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE) == 1) - return i; - } - - return {}; -} - std::string IKeyboard::getActiveLayout() { - const auto KEYMAP = m_xkbKeymap; - const auto STATE = m_xkbState; + const auto KEYMAP = xkbKeymap; + const auto STATE = xkbState; const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP); for (uint32_t i = 0; i < LAYOUTSNUM; ++i) { @@ -316,12 +285,12 @@ std::string IKeyboard::getActiveLayout() { } std::optional IKeyboard::getLEDs() { - if (m_xkbState == nullptr) + if (xkbState == nullptr) return {}; uint32_t leds = 0; - for (uint32_t i = 0; i < std::min(sc(LED_COUNT), m_ledIndexes.size()); ++i) { - if (xkb_state_led_index_is_active(m_xkbState, m_ledIndexes[i])) + for (uint32_t i = 0; i < std::min((size_t)LED_COUNT, ledIndexes.size()); ++i) { + if (xkb_state_led_index_is_active(xkbState, ledIndexes[i])) leds |= (1 << i); } @@ -338,10 +307,10 @@ void IKeyboard::updateLEDs() { } void IKeyboard::updateLEDs(uint32_t leds) { - if (!m_xkbState) + if (!xkbState) return; - if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(m_self.lock())) + if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock())) return; if (!aq()) @@ -351,13 +320,13 @@ void IKeyboard::updateLEDs(uint32_t leds) { } uint32_t IKeyboard::getModifiers() { - uint32_t modMask = m_modifiersState.depressed | m_modifiersState.latched; + uint32_t modMask = modifiersState.depressed | modifiersState.latched; uint32_t mods = 0; - for (size_t i = 0; i < m_modIndexes.size(); ++i) { - if (m_modIndexes[i] == XKB_MOD_INVALID) + for (size_t i = 0; i < modIndexes.size(); ++i) { + if (modIndexes[i] == XKB_MOD_INVALID) continue; - if (!(modMask & (1 << m_modIndexes[i]))) + if (!(modMask & (1 << modIndexes[i]))) continue; mods |= (1 << i); @@ -367,88 +336,72 @@ uint32_t IKeyboard::getModifiers() { } void IKeyboard::updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { - if (!m_xkbState) + if (!xkbState) return; - xkb_state_update_mask(m_xkbState, depressed, latched, locked, 0, 0, group); + xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0, group); - if (m_xkbSymState) - xkb_state_update_mask(m_xkbSymState, 0, 0, 0, 0, 0, group); + if (xkbSymState) + xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, group); if (!updateModifiersState()) return; - m_keyboardEvents.modifiers.emit(SModifiersEvent{ - .depressed = m_modifiersState.depressed, - .latched = m_modifiersState.latched, - .locked = m_modifiersState.locked, - .group = m_modifiersState.group, + keyboardEvents.modifiers.emit(SModifiersEvent{ + .depressed = modifiersState.depressed, + .latched = modifiersState.latched, + .locked = modifiersState.locked, + .group = modifiersState.group, }); updateLEDs(); } bool IKeyboard::updateModifiersState() { - if (!m_xkbState) + if (!xkbState) return false; - auto depressed = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_DEPRESSED); - auto latched = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LATCHED); - auto locked = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LOCKED); - auto group = xkb_state_serialize_layout(m_xkbState, XKB_STATE_LAYOUT_EFFECTIVE); + auto depressed = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_DEPRESSED); + auto latched = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED); + auto locked = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LOCKED); + auto group = xkb_state_serialize_layout(xkbState, XKB_STATE_LAYOUT_EFFECTIVE); - if (depressed == m_modifiersState.depressed && latched == m_modifiersState.latched && locked == m_modifiersState.locked && group == m_modifiersState.group) + if (depressed == modifiersState.depressed && latched == modifiersState.latched && locked == modifiersState.locked && group == modifiersState.group) return false; - m_modifiersState.depressed = depressed; - m_modifiersState.latched = latched; - m_modifiersState.locked = locked; - m_modifiersState.group = group; + modifiersState.depressed = depressed; + modifiersState.latched = latched; + modifiersState.locked = locked; + modifiersState.group = group; return true; } void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) { - xkb_state_update_key(m_xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); + + const auto contains = std::find(pressedXKB.begin(), pressedXKB.end(), xkbKey) != pressedXKB.end(); + + if (contains && pressed) + return; + if (!contains && !pressed) + return; + + if (contains) + std::erase(pressedXKB, xkbKey); + else + pressedXKB.emplace_back(xkbKey); + + xkb_state_update_key(xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); if (updateModifiersState()) { - if (m_xkbSymState) - xkb_state_update_mask(m_xkbSymState, 0, 0, 0, 0, 0, m_modifiersState.group); + if (xkbSymState) + xkb_state_update_mask(xkbSymState, 0, 0, 0, 0, 0, modifiersState.group); - m_keyboardEvents.modifiers.emit(SModifiersEvent{ - .depressed = m_modifiersState.depressed, - .latched = m_modifiersState.latched, - .locked = m_modifiersState.locked, - .group = m_modifiersState.group, + keyboardEvents.modifiers.emit(SModifiersEvent{ + .depressed = modifiersState.depressed, + .latched = modifiersState.latched, + .locked = modifiersState.locked, + .group = modifiersState.group, }); } } - -bool IKeyboard::updatePressed(uint32_t key, bool pressed) { - const auto contains = getPressed(key); - - if (contains && pressed) - return false; - if (!contains && !pressed) - return false; - - if (contains) - std::erase(m_pressed, key); - else - m_pressed.emplace_back(key); - - return true; -} - -bool IKeyboard::getPressed(uint32_t key) { - return std::ranges::contains(m_pressed, key); -} - -bool IKeyboard::shareStates() { - return m_shareStates; -} - -void IKeyboard::setShareStatesAuto(bool shareStates) { - if (m_shareStatesAuto) - m_shareStates = shareStates; -} diff --git a/src/devices/IKeyboard.hpp b/src/devices/IKeyboard.hpp index 6f34063a..aabf4cc2 100644 --- a/src/devices/IKeyboard.hpp +++ b/src/devices/IKeyboard.hpp @@ -24,13 +24,10 @@ enum eKeyboardModifiers { class IKeyboard : public IHID { public: virtual ~IKeyboard(); - virtual uint32_t getCapabilities(); - virtual eHIDType getType(); - virtual bool isVirtual() = 0; - virtual wl_client* getClient() { - return nullptr; - }; - virtual SP aq() = 0; + virtual uint32_t getCapabilities(); + virtual eHIDType getType(); + virtual bool isVirtual() = 0; + virtual SP aq() = 0; struct SKeyEvent { uint32_t timeMs = 0; @@ -51,11 +48,11 @@ class IKeyboard : public IHID { }; struct { - CSignalT key; - CSignalT modifiers; - CSignalT keymap; - CSignalT<> repeatInfo; - } m_keyboardEvents; + CSignal key; + CSignal modifiers; + CSignal keymap; + CSignal repeatInfo; + } keyboardEvents; struct SStringRuleNames { std::string layout = ""; @@ -65,73 +62,53 @@ class IKeyboard : public IHID { std::string rules = ""; }; - void setKeymap(const SStringRuleNames& rules); - void updateXKBTranslationState(xkb_keymap* const keymap = nullptr); - std::optional getActiveLayoutIndex(); - std::string getActiveLayout(); - std::optional getLEDs(); - void updateLEDs(); - void updateLEDs(uint32_t leds); - uint32_t getModifiers(); - void updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); - bool updateModifiersState(); // rets whether changed - void updateXkbStateWithKey(uint32_t xkbKey, bool pressed); - void updateKeymapFD(); - bool getPressed(uint32_t key); - bool shareStates(); - void setShareStatesAuto(bool shareStates); + void setKeymap(const SStringRuleNames& rules); + void updateXKBTranslationState(xkb_keymap* const keymap = nullptr); + std::string getActiveLayout(); + std::optional getLEDs(); + void updateLEDs(); + void updateLEDs(uint32_t leds); + uint32_t getModifiers(); + void updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); + bool updateModifiersState(); // rets whether changed + void updateXkbStateWithKey(uint32_t xkbKey, bool pressed); + void updateKeymapFD(); - bool m_active = false; - bool m_enabled = true; - bool m_allowBinds = true; - - // permission flag: whether this keyboard is allowed to be processed - bool m_allowed = true; + bool active = false; + bool enabled = true; // if the keymap is overridden by the implementation, // don't try to set keyboard rules anymore, to avoid overwriting the requested one. // e.g. Virtual keyboards with custom maps. - bool m_keymapOverridden = false; + bool keymapOverridden = false; - xkb_layout_index_t m_activeLayout = 0; - xkb_state* m_xkbState = nullptr; - - // never gets modifiers or layout changes sent, used for keybinds - xkb_state* m_xkbStaticState = nullptr; - xkb_state* m_xkbSymState = nullptr; // same as static but gets layouts - - xkb_keymap* m_xkbKeymap = nullptr; + xkb_layout_index_t activeLayout = 0; + xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr, + *xkbSymState = nullptr /* Same as static but gets layouts */; + xkb_keymap* xkbKeymap = nullptr; struct { uint32_t depressed = 0, latched = 0, locked = 0, group = 0; - } m_modifiersState; + } modifiersState; - std::array m_ledIndexes = {XKB_MOD_INVALID}; - std::array m_modIndexes = {XKB_MOD_INVALID}; - uint32_t m_leds = 0; + std::array ledIndexes = {XKB_MOD_INVALID}; + std::array modIndexes = {XKB_MOD_INVALID}; + uint32_t leds = 0; - std::string m_xkbFilePath = ""; - std::string m_xkbKeymapString = ""; - Hyprutils::OS::CFileDescriptor m_xkbKeymapFD; + std::string xkbFilePath = ""; + std::string xkbKeymapString = ""; + Hyprutils::OS::CFileDescriptor xkbKeymapFD; - std::string m_xkbKeymapV1String = ""; - Hyprutils::OS::CFileDescriptor m_xkbKeymapV1FD; + SStringRuleNames currentRules; + int repeatRate = 0; + int repeatDelay = 0; + int numlockOn = -1; + bool resolveBindsBySym = false; - SStringRuleNames m_currentRules; - int m_repeatRate = 0; - int m_repeatDelay = 0; - int m_numlockOn = -1; - bool m_resolveBindsBySym = false; - - WP m_self; + WP self; private: void clearManuallyAllocd(); - std::vector m_pressed; - - protected: - bool updatePressed(uint32_t key, bool pressed); - bool m_shareStates = true; - bool m_shareStatesAuto = true; + std::vector pressedXKB; }; diff --git a/src/devices/IPointer.hpp b/src/devices/IPointer.hpp index 197ba123..d5541222 100644 --- a/src/devices/IPointer.hpp +++ b/src/devices/IPointer.hpp @@ -90,30 +90,29 @@ class IPointer : public IHID { }; struct { - CSignalT motion; - CSignalT motionAbsolute; - CSignalT button; - CSignalT axis; - CSignalT<> frame; + CSignal motion; + CSignal motionAbsolute; + CSignal button; + CSignal axis; + CSignal frame; - CSignalT swipeBegin; - CSignalT swipeEnd; - CSignalT swipeUpdate; + CSignal swipeBegin; + CSignal swipeEnd; + CSignal swipeUpdate; - CSignalT pinchBegin; - CSignalT pinchEnd; - CSignalT pinchUpdate; + CSignal pinchBegin; + CSignal pinchEnd; + CSignal pinchUpdate; - CSignalT holdBegin; - CSignalT holdEnd; - } m_pointerEvents; + CSignal holdBegin; + CSignal holdEnd; + } pointerEvents; - bool m_connected = false; // means connected to the cursor - std::string m_boundOutput = ""; - bool m_flipX = false; // decide to invert horizontal movement - bool m_flipY = false; // decide to invert vertical movement - bool m_isTouchpad = false; - std::optional m_scrollFactor = {}; + bool connected = false; // means connected to the cursor + std::string boundOutput = ""; + bool flipX = false; // decide to invert horizontal movement + bool flipY = false; // decide to invert vertical movement + bool isTouchpad = false; - WP m_self; + WP self; }; diff --git a/src/devices/ITouch.hpp b/src/devices/ITouch.hpp index 14803011..766c0510 100644 --- a/src/devices/ITouch.hpp +++ b/src/devices/ITouch.hpp @@ -37,14 +37,14 @@ class ITouch : public IHID { }; struct { - CSignalT down; - CSignalT up; - CSignalT motion; - CSignalT cancel; - CSignalT<> frame; - } m_touchEvents; + CSignal down; + CSignal up; + CSignal motion; + CSignal cancel; + CSignal frame; + } touchEvents; - std::string m_boundOutput = ""; + std::string boundOutput = ""; - WP m_self; -}; + WP self; +}; \ No newline at end of file diff --git a/src/devices/Keyboard.cpp b/src/devices/Keyboard.cpp index 80f76c17..191cc6b2 100644 --- a/src/devices/Keyboard.cpp +++ b/src/devices/Keyboard.cpp @@ -6,7 +6,7 @@ SP CKeyboard::create(SP keeb) { SP pKeeb = SP(new CKeyboard(keeb)); - pKeeb->m_self = pKeeb; + pKeeb->self = pKeeb; return pKeeb; } @@ -16,41 +16,40 @@ bool CKeyboard::isVirtual() { } SP CKeyboard::aq() { - return m_keyboard.lock(); + return keyboard.lock(); } -CKeyboard::CKeyboard(SP keeb) : m_keyboard(keeb) { +CKeyboard::CKeyboard(SP keeb) : keyboard(keeb) { if (!keeb) return; - m_listeners.destroy = keeb->events.destroy.listen([this] { - m_keyboard.reset(); - m_events.destroy.emit(); + listeners.destroy = keeb->events.destroy.registerListener([this](std::any d) { + keyboard.reset(); + events.destroy.emit(); }); - m_listeners.key = keeb->events.key.listen([this](const Aquamarine::IKeyboard::SKeyEvent& event) { - const auto UPDATED = updatePressed(event.key, event.pressed); + listeners.key = keeb->events.key.registerListener([this](std::any d) { + auto E = std::any_cast(d); - m_keyboardEvents.key.emit(SKeyEvent{ - .timeMs = event.timeMs, - .keycode = event.key, - .state = event.pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, + keyboardEvents.key.emit(SKeyEvent{ + .timeMs = E.timeMs, + .keycode = E.key, + .state = E.pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, }); - if (UPDATED) - updateXkbStateWithKey(event.key + 8, event.pressed); + updateXkbStateWithKey(E.key + 8, E.pressed); }); - m_listeners.modifiers = keeb->events.modifiers.listen([this] { + listeners.modifiers = keeb->events.modifiers.registerListener([this](std::any d) { updateModifiersState(); - m_keyboardEvents.modifiers.emit(SModifiersEvent{ - .depressed = m_modifiersState.depressed, - .latched = m_modifiersState.latched, - .locked = m_modifiersState.locked, - .group = m_modifiersState.group, + keyboardEvents.modifiers.emit(SModifiersEvent{ + .depressed = modifiersState.depressed, + .latched = modifiersState.latched, + .locked = modifiersState.locked, + .group = modifiersState.group, }); }); - m_deviceName = keeb->getName(); + deviceName = keeb->getName(); } diff --git a/src/devices/Keyboard.hpp b/src/devices/Keyboard.hpp index 2ba4626d..f6de43cb 100644 --- a/src/devices/Keyboard.hpp +++ b/src/devices/Keyboard.hpp @@ -12,11 +12,11 @@ class CKeyboard : public IKeyboard { private: CKeyboard(SP keeb); - WP m_keyboard; + WP keyboard; struct { CHyprSignalListener destroy; CHyprSignalListener key; CHyprSignalListener modifiers; - } m_listeners; + } listeners; }; \ No newline at end of file diff --git a/src/devices/Mouse.cpp b/src/devices/Mouse.cpp index 403b0895..600ae5a8 100644 --- a/src/devices/Mouse.cpp +++ b/src/devices/Mouse.cpp @@ -5,127 +5,151 @@ SP CMouse::create(SP mouse) { SP pMouse = SP(new CMouse(mouse)); - pMouse->m_self = pMouse; + pMouse->self = pMouse; return pMouse; } -CMouse::CMouse(SP mouse_) : m_mouse(mouse_) { - if (!m_mouse) +CMouse::CMouse(SP mouse_) : mouse(mouse_) { + if (!mouse) return; - if (auto handle = m_mouse->getLibinputHandle()) { + if (auto handle = mouse->getLibinputHandle()) { double w = 0, h = 0; - m_isTouchpad = libinput_device_has_capability(handle, LIBINPUT_DEVICE_CAP_POINTER) && libinput_device_get_size(handle, &w, &h) == 0; + isTouchpad = libinput_device_has_capability(handle, LIBINPUT_DEVICE_CAP_POINTER) && libinput_device_get_size(handle, &w, &h) == 0; } - m_listeners.destroy = m_mouse->events.destroy.listen([this] { - m_mouse.reset(); - m_events.destroy.emit(); + listeners.destroy = mouse->events.destroy.registerListener([this](std::any d) { + mouse.reset(); + events.destroy.emit(); }); - m_listeners.motion = m_mouse->events.move.listen([this](const Aquamarine::IPointer::SMoveEvent& event) { - m_pointerEvents.motion.emit(SMotionEvent{ - .timeMs = event.timeMs, - .delta = event.delta, - .unaccel = event.unaccel, + listeners.motion = mouse->events.move.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.motion.emit(SMotionEvent{ + .timeMs = E.timeMs, + .delta = E.delta, + .unaccel = E.unaccel, .mouse = true, - .device = m_self.lock(), + .device = self.lock(), }); }); - m_listeners.motionAbsolute = m_mouse->events.warp.listen([this](const Aquamarine::IPointer::SWarpEvent& event) { - m_pointerEvents.motionAbsolute.emit(SMotionAbsoluteEvent{ - .timeMs = event.timeMs, - .absolute = event.absolute, - .device = m_self.lock(), + listeners.motionAbsolute = mouse->events.warp.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.motionAbsolute.emit(SMotionAbsoluteEvent{ + .timeMs = E.timeMs, + .absolute = E.absolute, + .device = self.lock(), }); }); - m_listeners.button = m_mouse->events.button.listen([this](const Aquamarine::IPointer::SButtonEvent& event) { - m_pointerEvents.button.emit(SButtonEvent{ - .timeMs = event.timeMs, - .button = event.button, - .state = event.pressed ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED, + listeners.button = mouse->events.button.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.button.emit(SButtonEvent{ + .timeMs = E.timeMs, + .button = E.button, + .state = E.pressed ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED, .mouse = true, }); }); - m_listeners.axis = m_mouse->events.axis.listen([this](const Aquamarine::IPointer::SAxisEvent& event) { - m_pointerEvents.axis.emit(SAxisEvent{ - .timeMs = event.timeMs, - .source = sc(event.source), - .axis = sc(event.axis), - .relativeDirection = sc(event.direction), - .delta = event.delta, - .deltaDiscrete = event.discrete, + listeners.axis = mouse->events.axis.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.axis.emit(SAxisEvent{ + .timeMs = E.timeMs, + .source = (wl_pointer_axis_source)E.source, + .axis = (wl_pointer_axis)E.axis, + .relativeDirection = (wl_pointer_axis_relative_direction)E.direction, + .delta = E.delta, + .deltaDiscrete = E.discrete, .mouse = true, }); }); - m_listeners.frame = m_mouse->events.frame.listen([this] { m_pointerEvents.frame.emit(); }); + listeners.frame = mouse->events.frame.registerListener([this](std::any d) { pointerEvents.frame.emit(); }); - m_listeners.swipeBegin = m_mouse->events.swipeBegin.listen([this](const Aquamarine::IPointer::SSwipeBeginEvent& event) { - m_pointerEvents.swipeBegin.emit(SSwipeBeginEvent{ - .timeMs = event.timeMs, - .fingers = event.fingers, + listeners.swipeBegin = mouse->events.swipeBegin.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.swipeBegin.emit(SSwipeBeginEvent{ + .timeMs = E.timeMs, + .fingers = E.fingers, }); }); - m_listeners.swipeEnd = m_mouse->events.swipeEnd.listen([this](const Aquamarine::IPointer::SSwipeEndEvent& event) { - m_pointerEvents.swipeEnd.emit(SSwipeEndEvent{ - .timeMs = event.timeMs, - .cancelled = event.cancelled, + listeners.swipeEnd = mouse->events.swipeEnd.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.swipeEnd.emit(SSwipeEndEvent{ + .timeMs = E.timeMs, + .cancelled = E.cancelled, }); }); - m_listeners.swipeUpdate = m_mouse->events.swipeUpdate.listen([this](const Aquamarine::IPointer::SSwipeUpdateEvent& event) { - m_pointerEvents.swipeUpdate.emit(SSwipeUpdateEvent{ - .timeMs = event.timeMs, - .fingers = event.fingers, - .delta = event.delta, + listeners.swipeUpdate = mouse->events.swipeUpdate.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.swipeUpdate.emit(SSwipeUpdateEvent{ + .timeMs = E.timeMs, + .fingers = E.fingers, + .delta = E.delta, }); }); - m_listeners.pinchBegin = m_mouse->events.pinchBegin.listen([this](const Aquamarine::IPointer::SPinchBeginEvent& event) { - m_pointerEvents.pinchBegin.emit(SPinchBeginEvent{ - .timeMs = event.timeMs, - .fingers = event.fingers, + listeners.pinchBegin = mouse->events.pinchBegin.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.pinchBegin.emit(SPinchBeginEvent{ + .timeMs = E.timeMs, + .fingers = E.fingers, }); }); - m_listeners.pinchEnd = m_mouse->events.pinchEnd.listen([this](const Aquamarine::IPointer::SPinchEndEvent& event) { - m_pointerEvents.pinchEnd.emit(SPinchEndEvent{ - .timeMs = event.timeMs, - .cancelled = event.cancelled, + listeners.pinchEnd = mouse->events.pinchEnd.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.pinchEnd.emit(SPinchEndEvent{ + .timeMs = E.timeMs, + .cancelled = E.cancelled, }); }); - m_listeners.pinchUpdate = m_mouse->events.pinchUpdate.listen([this](const Aquamarine::IPointer::SPinchUpdateEvent& event) { - m_pointerEvents.pinchUpdate.emit(SPinchUpdateEvent{ - .timeMs = event.timeMs, - .fingers = event.fingers, - .delta = event.delta, - .scale = event.scale, - .rotation = event.rotation, + listeners.pinchUpdate = mouse->events.pinchUpdate.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.pinchUpdate.emit(SPinchUpdateEvent{ + .timeMs = E.timeMs, + .fingers = E.fingers, + .delta = E.delta, + .scale = E.scale, + .rotation = E.rotation, }); }); - m_listeners.holdBegin = m_mouse->events.holdBegin.listen([this](const Aquamarine::IPointer::SHoldBeginEvent& event) { - m_pointerEvents.holdBegin.emit(SHoldBeginEvent{ - .timeMs = event.timeMs, - .fingers = event.fingers, + listeners.holdBegin = mouse->events.holdBegin.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.holdBegin.emit(SHoldBeginEvent{ + .timeMs = E.timeMs, + .fingers = E.fingers, }); }); - m_listeners.holdEnd = m_mouse->events.holdEnd.listen([this](const Aquamarine::IPointer::SHoldEndEvent& event) { - m_pointerEvents.holdEnd.emit(SHoldEndEvent{ - .timeMs = event.timeMs, - .cancelled = event.cancelled, + listeners.holdEnd = mouse->events.holdEnd.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + pointerEvents.holdEnd.emit(SHoldEndEvent{ + .timeMs = E.timeMs, + .cancelled = E.cancelled, }); }); - m_deviceName = m_mouse->getName(); + deviceName = mouse->getName(); } bool CMouse::isVirtual() { @@ -133,5 +157,5 @@ bool CMouse::isVirtual() { } SP CMouse::aq() { - return m_mouse.lock(); + return mouse.lock(); } diff --git a/src/devices/Mouse.hpp b/src/devices/Mouse.hpp index f99b663e..2b51fbe9 100644 --- a/src/devices/Mouse.hpp +++ b/src/devices/Mouse.hpp @@ -12,7 +12,7 @@ class CMouse : public IPointer { private: CMouse(SP mouse); - WP m_mouse; + WP mouse; struct { CHyprSignalListener destroy; @@ -33,5 +33,5 @@ class CMouse : public IPointer { CHyprSignalListener holdBegin; CHyprSignalListener holdEnd; - } m_listeners; + } listeners; }; diff --git a/src/devices/Tablet.cpp b/src/devices/Tablet.cpp index cbda1a71..99e436fb 100644 --- a/src/devices/Tablet.cpp +++ b/src/devices/Tablet.cpp @@ -7,7 +7,7 @@ SP CTablet::create(SP tablet) { SP pTab = SP(new CTablet(tablet)); - pTab->m_self = pTab; + pTab->self = pTab; PROTO::tablet->registerDevice(pTab); @@ -17,7 +17,7 @@ SP CTablet::create(SP tablet) { SP CTabletTool::create(SP tablet) { SP pTab = SP(new CTabletTool(tablet)); - pTab->m_self = pTab; + pTab->self = pTab; PROTO::tablet->registerDevice(pTab); @@ -27,7 +27,7 @@ SP CTabletTool::create(SP tablet) { SP CTabletPad::create(SP tablet) { SP pTab = SP(new CTabletPad(tablet)); - pTab->m_self = pTab; + pTab->self = pTab; PROTO::tablet->registerDevice(pTab); @@ -62,66 +62,74 @@ uint32_t CTablet::getCapabilities() { } SP CTablet::aq() { - return m_tablet.lock(); + return tablet.lock(); } -CTablet::CTablet(SP tablet_) : m_tablet(tablet_) { - if (!m_tablet) +CTablet::CTablet(SP tablet_) : tablet(tablet_) { + if (!tablet) return; - m_listeners.destroy = m_tablet->events.destroy.listen([this] { - m_tablet.reset(); - m_events.destroy.emit(); + listeners.destroy = tablet->events.destroy.registerListener([this](std::any d) { + tablet.reset(); + events.destroy.emit(); }); - m_listeners.axis = m_tablet->events.axis.listen([this](const Aquamarine::ITablet::SAxisEvent& event) { - m_tabletEvents.axis.emit(SAxisEvent{ - .tool = event.tool, - .tablet = m_self.lock(), - .timeMs = event.timeMs, - .updatedAxes = aqUpdateToHl(event.updatedAxes), - .axis = event.absolute, - .axisDelta = event.delta, - .tilt = event.tilt, - .pressure = event.pressure, - .distance = event.distance, - .rotation = event.rotation, - .slider = event.slider, - .wheelDelta = event.wheelDelta, + listeners.axis = tablet->events.axis.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + tabletEvents.axis.emit(SAxisEvent{ + .tool = E.tool, + .tablet = self.lock(), + .timeMs = E.timeMs, + .updatedAxes = aqUpdateToHl(E.updatedAxes), + .axis = E.absolute, + .axisDelta = E.delta, + .tilt = E.tilt, + .pressure = E.pressure, + .distance = E.distance, + .rotation = E.rotation, + .slider = E.slider, + .wheelDelta = E.wheelDelta, }); }); - m_listeners.proximity = m_tablet->events.proximity.listen([this](const Aquamarine::ITablet::SProximityEvent& event) { - m_tabletEvents.proximity.emit(SProximityEvent{ - .tool = event.tool, - .tablet = m_self.lock(), - .timeMs = event.timeMs, - .proximity = event.absolute, - .in = event.in, + listeners.proximity = tablet->events.proximity.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + tabletEvents.proximity.emit(SProximityEvent{ + .tool = E.tool, + .tablet = self.lock(), + .timeMs = E.timeMs, + .proximity = E.absolute, + .in = E.in, }); }); - m_listeners.tip = m_tablet->events.tip.listen([this](const Aquamarine::ITablet::STipEvent& event) { - m_tabletEvents.tip.emit(STipEvent{ - .tool = event.tool, - .tablet = m_self.lock(), - .timeMs = event.timeMs, - .tip = event.absolute, - .in = event.down, + listeners.tip = tablet->events.tip.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + tabletEvents.tip.emit(STipEvent{ + .tool = E.tool, + .tablet = self.lock(), + .timeMs = E.timeMs, + .tip = E.absolute, + .in = E.down, }); }); - m_listeners.button = m_tablet->events.button.listen([this](const Aquamarine::ITablet::SButtonEvent& event) { - m_tabletEvents.button.emit(SButtonEvent{ - .tool = event.tool, - .tablet = m_self.lock(), - .timeMs = event.timeMs, - .button = event.button, - .down = event.down, + listeners.button = tablet->events.button.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + tabletEvents.button.emit(SButtonEvent{ + .tool = E.tool, + .tablet = self.lock(), + .timeMs = E.timeMs, + .button = E.button, + .down = E.down, }); }); - m_deviceName = m_tablet->getName(); + deviceName = tablet->getName(); } CTablet::~CTablet() { @@ -137,57 +145,63 @@ uint32_t CTabletPad::getCapabilities() { } SP CTabletPad::aq() { - return m_pad.lock(); + return pad.lock(); } eHIDType CTabletPad::getType() { return HID_TYPE_TABLET_PAD; } -CTabletPad::CTabletPad(SP pad_) : m_pad(pad_) { - if (!m_pad) +CTabletPad::CTabletPad(SP pad_) : pad(pad_) { + if (!pad) return; - m_listeners.destroy = m_pad->events.destroy.listen([this] { - m_pad.reset(); - m_events.destroy.emit(); + listeners.destroy = pad->events.destroy.registerListener([this](std::any d) { + pad.reset(); + events.destroy.emit(); }); - m_listeners.button = m_pad->events.button.listen([this](const Aquamarine::ITabletPad::SButtonEvent& event) { - m_padEvents.button.emit(SButtonEvent{ - .timeMs = event.timeMs, - .button = event.button, - .down = event.down, - .mode = event.mode, - .group = event.group, + listeners.button = pad->events.button.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + padEvents.button.emit(SButtonEvent{ + .timeMs = E.timeMs, + .button = E.button, + .down = E.down, + .mode = E.mode, + .group = E.group, }); }); - m_listeners.ring = m_pad->events.ring.listen([this](const Aquamarine::ITabletPad::SRingEvent& event) { - m_padEvents.ring.emit(SRingEvent{ - .timeMs = event.timeMs, - .finger = event.source == Aquamarine::ITabletPad::AQ_TABLET_PAD_RING_SOURCE_FINGER, - .ring = event.ring, - .position = event.pos, - .mode = event.mode, + listeners.ring = pad->events.ring.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + padEvents.ring.emit(SRingEvent{ + .timeMs = E.timeMs, + .finger = E.source == Aquamarine::ITabletPad::AQ_TABLET_PAD_RING_SOURCE_FINGER, + .ring = E.ring, + .position = E.pos, + .mode = E.mode, }); }); - m_listeners.strip = m_pad->events.strip.listen([this](const Aquamarine::ITabletPad::SStripEvent& event) { - m_padEvents.strip.emit(SStripEvent{ - .timeMs = event.timeMs, - .finger = event.source == Aquamarine::ITabletPad::AQ_TABLET_PAD_STRIP_SOURCE_FINGER, - .strip = event.strip, - .position = event.pos, - .mode = event.mode, + listeners.strip = pad->events.strip.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + padEvents.strip.emit(SStripEvent{ + .timeMs = E.timeMs, + .finger = E.source == Aquamarine::ITabletPad::AQ_TABLET_PAD_STRIP_SOURCE_FINGER, + .strip = E.strip, + .position = E.pos, + .mode = E.mode, }); }); - m_listeners.attach = m_pad->events.attach.listen([] { + listeners.attach = pad->events.attach.registerListener([](std::any d) { ; // TODO: this doesn't do anything in aq atm }); - m_deviceName = m_pad->getName(); + deviceName = pad->getName(); } CTabletPad::~CTabletPad() { @@ -199,36 +213,36 @@ uint32_t CTabletTool::getCapabilities() { } SP CTabletTool::aq() { - return m_tool.lock(); + return tool.lock(); } eHIDType CTabletTool::getType() { return HID_TYPE_TABLET_TOOL; } -CTabletTool::CTabletTool(SP tool_) : m_tool(tool_) { - if (!m_tool) +CTabletTool::CTabletTool(SP tool_) : tool(tool_) { + if (!tool) return; - m_listeners.destroyTool = m_tool->events.destroy.listen([this] { - m_tool.reset(); - m_events.destroy.emit(); + listeners.destroyTool = tool->events.destroy.registerListener([this](std::any d) { + tool.reset(); + events.destroy.emit(); }); - if (m_tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_TILT) - m_toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_TILT; - if (m_tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_PRESSURE) - m_toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_PRESSURE; - if (m_tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_DISTANCE) - m_toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_DISTANCE; - if (m_tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_ROTATION) - m_toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_ROTATION; - if (m_tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_SLIDER) - m_toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_SLIDER; - if (m_tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_WHEEL) - m_toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_WHEEL; + if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_TILT) + toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_TILT; + if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_PRESSURE) + toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_PRESSURE; + if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_DISTANCE) + toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_DISTANCE; + if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_ROTATION) + toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_ROTATION; + if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_SLIDER) + toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_SLIDER; + if (tool->capabilities & Aquamarine::ITabletTool::AQ_TABLET_TOOL_CAPABILITY_WHEEL) + toolCapabilities |= HID_TABLET_TOOL_CAPABILITY_WHEEL; - m_deviceName = std::format("{:x}-{:x}", m_tool->serial, m_tool->id); + deviceName = std::format("{:x}-{:x}", tool->serial, tool->id); } CTabletTool::~CTabletTool() { @@ -236,25 +250,25 @@ CTabletTool::~CTabletTool() { } SP CTabletTool::getSurface() { - return m_surface.lock(); + return pSurface.lock(); } void CTabletTool::setSurface(SP surf) { - if (surf == m_surface) + if (surf == pSurface) return; - if (m_surface) { - m_listeners.destroySurface.reset(); - m_surface.reset(); + if (pSurface) { + listeners.destroySurface.reset(); + pSurface.reset(); } - m_surface = surf; + pSurface = surf; if (surf) { - m_listeners.destroySurface = surf->m_events.destroy.listen([this] { - PROTO::tablet->proximityOut(m_self.lock()); - m_surface.reset(); - m_listeners.destroySurface.reset(); + listeners.destroySurface = surf->events.destroy.registerListener([this](std::any d) { + PROTO::tablet->proximityOut(self.lock()); + pSurface.reset(); + listeners.destroySurface.reset(); }); } } diff --git a/src/devices/Tablet.hpp b/src/devices/Tablet.hpp index b5e55c19..c3a9a2c9 100644 --- a/src/devices/Tablet.hpp +++ b/src/devices/Tablet.hpp @@ -82,24 +82,24 @@ class CTablet : public IHID { }; struct { - CSignalT axis; - CSignalT proximity; - CSignalT tip; - CSignalT button; - } m_tabletEvents; + CSignal axis; + CSignal proximity; + CSignal tip; + CSignal button; + } tabletEvents; - WP m_self; + WP self; - bool m_relativeInput = false; - bool m_absolutePos = false; - std::string m_boundOutput = ""; - CBox m_activeArea; - CBox m_boundBox; + bool relativeInput = false; + bool absolutePos = false; + std::string boundOutput = ""; + CBox activeArea; + CBox boundBox; private: CTablet(SP tablet); - WP m_tablet; + WP tablet; struct { CHyprSignalListener destroy; @@ -107,7 +107,7 @@ class CTablet : public IHID { CHyprSignalListener proximity; CHyprSignalListener tip; CHyprSignalListener button; - } m_listeners; + } listeners; }; class CTabletPad : public IHID { @@ -144,19 +144,19 @@ class CTabletPad : public IHID { }; struct { - CSignalT button; - CSignalT ring; - CSignalT strip; - CSignalT> attach; - } m_padEvents; + CSignal button; + CSignal ring; + CSignal strip; + CSignal attach; + } padEvents; - WP m_self; - WP m_parent; + WP self; + WP parent; private: CTabletPad(SP pad); - WP m_pad; + WP pad; struct { CHyprSignalListener destroy; @@ -164,7 +164,7 @@ class CTabletPad : public IHID { CHyprSignalListener strip; CHyprSignalListener button; CHyprSignalListener attach; - } m_listeners; + } listeners; }; class CTabletTool : public IHID { @@ -198,23 +198,23 @@ class CTabletTool : public IHID { SP getSurface(); void setSurface(SP); - WP m_self; - Vector2D m_tilt; - bool m_active = false; // true if in proximity - uint32_t m_toolCapabilities = 0; + WP self; + Vector2D tilt; + bool active = false; // true if in proximity + uint32_t toolCapabilities = 0; - bool m_isDown = false; - std::vector m_buttonsDown; - Vector2D m_absolutePos; // last known absolute position. + bool isDown = false; + std::vector buttonsDown; + Vector2D absolutePos; // last known absolute position. private: CTabletTool(SP tool); - WP m_surface; - WP m_tool; + WP pSurface; + WP tool; struct { CHyprSignalListener destroySurface; CHyprSignalListener destroyTool; - } m_listeners; -}; + } listeners; +}; \ No newline at end of file diff --git a/src/devices/TouchDevice.cpp b/src/devices/TouchDevice.cpp index 527cb02a..25b7b503 100644 --- a/src/devices/TouchDevice.cpp +++ b/src/devices/TouchDevice.cpp @@ -5,54 +5,62 @@ SP CTouchDevice::create(SP touch) { SP pTouch = SP(new CTouchDevice(touch)); - pTouch->m_self = pTouch; + pTouch->self = pTouch; return pTouch; } -CTouchDevice::CTouchDevice(SP touch_) : m_touch(touch_) { - if (!m_touch) +CTouchDevice::CTouchDevice(SP touch_) : touch(touch_) { + if (!touch) return; - m_listeners.destroy = m_touch->events.destroy.listen([this] { - m_events.destroy.emit(); - m_touch.reset(); + listeners.destroy = touch->events.destroy.registerListener([this](std::any d) { + events.destroy.emit(); + touch.reset(); }); - m_listeners.down = m_touch->events.down.listen([this](const Aquamarine::ITouch::SDownEvent& event) { - m_touchEvents.down.emit(SDownEvent{ - .timeMs = event.timeMs, - .touchID = event.touchID, - .pos = event.pos, - .device = m_self.lock(), + listeners.down = touch->events.down.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + touchEvents.down.emit(SDownEvent{ + .timeMs = E.timeMs, + .touchID = E.touchID, + .pos = E.pos, + .device = self.lock(), }); }); - m_listeners.up = m_touch->events.up.listen([this](const Aquamarine::ITouch::SUpEvent& event) { - m_touchEvents.up.emit(SUpEvent{ - .timeMs = event.timeMs, - .touchID = event.touchID, + listeners.up = touch->events.up.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + touchEvents.up.emit(SUpEvent{ + .timeMs = E.timeMs, + .touchID = E.touchID, }); }); - m_listeners.motion = m_touch->events.move.listen([this](const Aquamarine::ITouch::SMotionEvent& event) { - m_touchEvents.motion.emit(SMotionEvent{ - .timeMs = event.timeMs, - .touchID = event.touchID, - .pos = event.pos, + listeners.motion = touch->events.move.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + touchEvents.motion.emit(SMotionEvent{ + .timeMs = E.timeMs, + .touchID = E.touchID, + .pos = E.pos, }); }); - m_listeners.cancel = m_touch->events.cancel.listen([this](const Aquamarine::ITouch::SCancelEvent& event) { - m_touchEvents.cancel.emit(SCancelEvent{ - .timeMs = event.timeMs, - .touchID = event.touchID, + listeners.cancel = touch->events.cancel.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + touchEvents.cancel.emit(SCancelEvent{ + .timeMs = E.timeMs, + .touchID = E.touchID, }); }); - m_listeners.frame = m_touch->events.frame.listen([this] { m_touchEvents.frame.emit(); }); + listeners.frame = touch->events.frame.registerListener([this](std::any d) { touchEvents.frame.emit(); }); - m_deviceName = m_touch->getName(); + deviceName = touch->getName(); } bool CTouchDevice::isVirtual() { @@ -60,5 +68,5 @@ bool CTouchDevice::isVirtual() { } SP CTouchDevice::aq() { - return m_touch.lock(); + return touch.lock(); } diff --git a/src/devices/TouchDevice.hpp b/src/devices/TouchDevice.hpp index 9b32077d..b18df2d0 100644 --- a/src/devices/TouchDevice.hpp +++ b/src/devices/TouchDevice.hpp @@ -12,7 +12,7 @@ class CTouchDevice : public ITouch { private: CTouchDevice(SP touch); - WP m_touch; + WP touch; struct { CHyprSignalListener destroy; @@ -21,5 +21,5 @@ class CTouchDevice : public ITouch { CHyprSignalListener motion; CHyprSignalListener cancel; CHyprSignalListener frame; - } m_listeners; + } listeners; }; \ No newline at end of file diff --git a/src/devices/VirtualKeyboard.cpp b/src/devices/VirtualKeyboard.cpp index 2951f36a..548711c2 100644 --- a/src/devices/VirtualKeyboard.cpp +++ b/src/devices/VirtualKeyboard.cpp @@ -1,54 +1,47 @@ #include "VirtualKeyboard.hpp" #include "../defines.hpp" #include "../protocols/VirtualKeyboard.hpp" -#include "../config/ConfigManager.hpp" -#include SP CVirtualKeyboard::create(SP keeb) { SP pKeeb = SP(new CVirtualKeyboard(keeb)); - pKeeb->m_self = pKeeb; + pKeeb->self = pKeeb; return pKeeb; } -CVirtualKeyboard::CVirtualKeyboard(SP keeb_) : m_keyboard(keeb_) { +CVirtualKeyboard::CVirtualKeyboard(SP keeb_) : keyboard(keeb_) { if (!keeb_) return; - m_listeners.destroy = keeb_->m_events.destroy.listen([this] { - m_keyboard.reset(); - m_events.destroy.emit(); + listeners.destroy = keeb_->events.destroy.registerListener([this](std::any d) { + keyboard.reset(); + events.destroy.emit(); }); - m_listeners.key = keeb_->m_events.key.listen([this](const auto& event) { - updatePressed(event.keycode, event.state == WL_KEYBOARD_KEY_STATE_PRESSED); - m_keyboardEvents.key.emit(event); - }); - m_listeners.modifiers = keeb_->m_events.modifiers.listen([this](const SModifiersEvent& event) { - updateModifiers(event.depressed, event.latched, event.locked, event.group); - m_keyboardEvents.modifiers.emit(SModifiersEvent{ - .depressed = m_modifiersState.depressed, - .latched = m_modifiersState.latched, - .locked = m_modifiersState.locked, - .group = m_modifiersState.group, + listeners.key = keeb_->events.key.registerListener([this](std::any d) { keyboardEvents.key.emit(d); }); + listeners.modifiers = keeb_->events.modifiers.registerListener([this](std::any d) { + auto E = std::any_cast(d); + updateModifiers(E.depressed, E.latched, E.locked, E.group); + keyboardEvents.modifiers.emit(SModifiersEvent{ + .depressed = modifiersState.depressed, + .latched = modifiersState.latched, + .locked = modifiersState.locked, + .group = modifiersState.group, }); }); - m_listeners.keymap = keeb_->m_events.keymap.listen([this](const SKeymapEvent& event) { - if (m_xkbKeymap) - xkb_keymap_unref(m_xkbKeymap); - m_xkbKeymap = xkb_keymap_ref(event.keymap); - m_keymapOverridden = true; - updateXKBTranslationState(m_xkbKeymap); + listeners.keymap = keeb_->events.keymap.registerListener([this](std::any d) { + auto E = std::any_cast(d); + if (xkbKeymap) + xkb_keymap_unref(xkbKeymap); + xkbKeymap = xkb_keymap_ref(E.keymap); + keymapOverridden = true; + updateXKBTranslationState(xkbKeymap); updateKeymapFD(); - m_keyboardEvents.keymap.emit(event); + keyboardEvents.keymap.emit(d); }); - m_deviceName = keeb_->m_name; - - const auto SHARESTATES = g_pConfigManager->getDeviceInt(m_deviceName, "share_states", "input:virtualkeyboard:share_states"); - m_shareStates = SHARESTATES != 0; - m_shareStatesAuto = SHARESTATES == 2; + deviceName = keeb_->name; } bool CVirtualKeyboard::isVirtual() { @@ -60,7 +53,7 @@ SP CVirtualKeyboard::aq() { } wl_client* CVirtualKeyboard::getClient() { - if (m_keyboard.expired()) + if (keyboard.expired()) return nullptr; - return m_keyboard->client(); + return keyboard->client(); } diff --git a/src/devices/VirtualKeyboard.hpp b/src/devices/VirtualKeyboard.hpp index a0783d93..12a3907c 100644 --- a/src/devices/VirtualKeyboard.hpp +++ b/src/devices/VirtualKeyboard.hpp @@ -11,17 +11,17 @@ class CVirtualKeyboard : public IKeyboard { virtual bool isVirtual(); virtual SP aq(); - virtual wl_client* getClient(); + wl_client* getClient(); private: CVirtualKeyboard(SP keeb); - WP m_keyboard; + WP keyboard; struct { CHyprSignalListener destroy; CHyprSignalListener key; CHyprSignalListener modifiers; CHyprSignalListener keymap; - } m_listeners; + } listeners; }; diff --git a/src/devices/VirtualPointer.cpp b/src/devices/VirtualPointer.cpp index 23d860bf..e98acd04 100644 --- a/src/devices/VirtualPointer.cpp +++ b/src/devices/VirtualPointer.cpp @@ -5,44 +5,46 @@ SP CVirtualPointer::create(SP resource) { SP pPointer = SP(new CVirtualPointer(resource)); - pPointer->m_self = pPointer; + pPointer->self = pPointer; return pPointer; } -CVirtualPointer::CVirtualPointer(SP resource) : m_pointer(resource) { +CVirtualPointer::CVirtualPointer(SP resource) : pointer(resource) { if UNLIKELY (!resource->good()) return; - m_listeners.destroy = m_pointer->m_events.destroy.listen([this] { - m_pointer.reset(); - m_events.destroy.emit(); + listeners.destroy = pointer->events.destroy.registerListener([this](std::any d) { + pointer.reset(); + events.destroy.emit(); }); - m_listeners.motion = m_pointer->m_events.move.listen([this](SMotionEvent event) { - event.device = m_self.lock(); - m_pointerEvents.motion.emit(event); + listeners.motion = pointer->events.move.registerListener([this](std::any d) { + auto E = std::any_cast(d); + E.device = self.lock(); + pointerEvents.motion.emit(E); }); - m_listeners.motionAbsolute = m_pointer->m_events.warp.listen([this](SMotionAbsoluteEvent event) { + listeners.motionAbsolute = pointer->events.warp.registerListener([this](std::any d) { // we need to unpack the event and add our device here because it's required to calculate the position correctly - event.device = m_self.lock(); - m_pointerEvents.motionAbsolute.emit(event); + auto E = std::any_cast(d); + E.device = self.lock(); + pointerEvents.motionAbsolute.emit(E); }); - m_listeners.button = m_pointer->m_events.button.forward(m_pointerEvents.button); - m_listeners.axis = m_pointer->m_events.axis.forward(m_pointerEvents.axis); - m_listeners.frame = m_pointer->m_events.frame.forward(m_pointerEvents.frame); - m_listeners.swipeBegin = m_pointer->m_events.swipeBegin.forward(m_pointerEvents.swipeBegin); - m_listeners.swipeEnd = m_pointer->m_events.swipeEnd.forward(m_pointerEvents.swipeEnd); - m_listeners.swipeUpdate = m_pointer->m_events.swipeUpdate.forward(m_pointerEvents.swipeUpdate); - m_listeners.pinchBegin = m_pointer->m_events.pinchBegin.forward(m_pointerEvents.pinchBegin); - m_listeners.pinchEnd = m_pointer->m_events.pinchEnd.forward(m_pointerEvents.pinchEnd); - m_listeners.pinchUpdate = m_pointer->m_events.pinchUpdate.forward(m_pointerEvents.pinchUpdate); - m_listeners.holdBegin = m_pointer->m_events.holdBegin.forward(m_pointerEvents.holdBegin); - m_listeners.holdEnd = m_pointer->m_events.holdEnd.forward(m_pointerEvents.holdEnd); + listeners.button = pointer->events.button.registerListener([this](std::any d) { pointerEvents.button.emit(d); }); + listeners.axis = pointer->events.axis.registerListener([this](std::any d) { pointerEvents.axis.emit(d); }); + listeners.frame = pointer->events.frame.registerListener([this](std::any d) { pointerEvents.frame.emit(); }); + listeners.swipeBegin = pointer->events.swipeBegin.registerListener([this](std::any d) { pointerEvents.swipeBegin.emit(d); }); + listeners.swipeEnd = pointer->events.swipeEnd.registerListener([this](std::any d) { pointerEvents.swipeEnd.emit(d); }); + listeners.swipeUpdate = pointer->events.swipeUpdate.registerListener([this](std::any d) { pointerEvents.swipeUpdate.emit(d); }); + listeners.pinchBegin = pointer->events.pinchBegin.registerListener([this](std::any d) { pointerEvents.pinchBegin.emit(d); }); + listeners.pinchEnd = pointer->events.pinchEnd.registerListener([this](std::any d) { pointerEvents.pinchEnd.emit(d); }); + listeners.pinchUpdate = pointer->events.pinchUpdate.registerListener([this](std::any d) { pointerEvents.pinchUpdate.emit(d); }); + listeners.holdBegin = pointer->events.holdBegin.registerListener([this](std::any d) { pointerEvents.holdBegin.emit(d); }); + listeners.holdEnd = pointer->events.holdEnd.registerListener([this](std::any d) { pointerEvents.holdEnd.emit(d); }); - m_boundOutput = resource->m_boundOutput ? resource->m_boundOutput->m_name : ""; + boundOutput = resource->boundOutput ? resource->boundOutput->szName : ""; - m_deviceName = m_pointer->m_name; + deviceName = pointer->name; } bool CVirtualPointer::isVirtual() { diff --git a/src/devices/VirtualPointer.hpp b/src/devices/VirtualPointer.hpp index 9eacbd5f..1ecfd842 100644 --- a/src/devices/VirtualPointer.hpp +++ b/src/devices/VirtualPointer.hpp @@ -14,7 +14,7 @@ class CVirtualPointer : public IPointer { private: CVirtualPointer(SP); - WP m_pointer; + WP pointer; struct { CHyprSignalListener destroy; @@ -35,5 +35,5 @@ class CVirtualPointer : public IPointer { CHyprSignalListener holdBegin; CHyprSignalListener holdEnd; - } m_listeners; + } listeners; }; \ No newline at end of file diff --git a/src/event/EventBus.cpp b/src/event/EventBus.cpp deleted file mode 100644 index f06c3984..00000000 --- a/src/event/EventBus.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "EventBus.hpp" - -using namespace Event; - -UP& Event::bus() { - static UP p = makeUnique(); - return p; -} diff --git a/src/event/EventBus.hpp b/src/event/EventBus.hpp deleted file mode 100644 index a30288f0..00000000 --- a/src/event/EventBus.hpp +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include "../helpers/memory/Memory.hpp" -#include "../helpers/signal/Signal.hpp" -#include "../helpers/math/Math.hpp" - -#include "../devices/IPointer.hpp" -#include "../devices/IKeyboard.hpp" -#include "../devices/Tablet.hpp" -#include "../devices/ITouch.hpp" - -#include "../desktop/DesktopTypes.hpp" - -#include "../SharedDefs.hpp" - -namespace Desktop { - enum eFocusReason : uint8_t; -} -namespace Event { - struct SCallbackInfo { - bool cancelled = false; /* on cancellable events, will cancel the event. */ - }; - - class CEventBus { - public: - CEventBus() = default; - ~CEventBus() = default; - - template - using Event = CSignalT; - - template - using Cancellable = CSignalT; - - struct { - Event<> ready; - Event<> tick; - - struct { - Event open; - Event openEarly; - Event destroy; - Event close; - Event kill; - Event active; - Event urgent; - Event title; - Event class_; - Event pin; - Event fullscreen; - Event updateRules; - Event moveToWorkspace; - } window; - - struct { - Event opened; - Event closed; - Event updateRules; - } layer; - - struct { - struct { - Cancellable move; - Cancellable button; - Cancellable axis; - } mouse; - - struct { - Cancellable key; - Event, const std::string&> layout; - Event> focus; - } keyboard; - - struct { - Cancellable axis; - Cancellable button; - Cancellable proximity; - Cancellable tip; - } tablet; - - struct { - Cancellable cancel; - Cancellable down; - Cancellable up; - Cancellable motion; - } touch; - } input; - - struct { - Event pre; - Event stage; - } render; - - struct { - Event state; - } screenshare; - - struct { - struct { - Cancellable begin; - Cancellable end; - Cancellable update; - } swipe; - - struct { - Cancellable begin; - Cancellable end; - Cancellable update; - } pinch; - } gesture; - - struct { - Event newMon; - Event preAdded; - Event added; - Event preRemoved; - Event removed; - Event preCommit; - Event focused; - - Event<> layoutChanged; - } monitor; - - struct { - Event moveToMonitor; - Event active; - Event created; - Event removed; - } workspace; - - struct { - Event<> preReload; - Event<> reloaded; - } config; - - struct { - Event submap; - } keybinds; - - } m_events; - }; - - UP& bus(); -}; diff --git a/src/events/Events.hpp b/src/events/Events.hpp new file mode 100644 index 00000000..2e564944 --- /dev/null +++ b/src/events/Events.hpp @@ -0,0 +1,22 @@ +#pragma once +#include "../defines.hpp" + +// NOLINTNEXTLINE(readability-identifier-naming) +namespace Events { + // Window events + DYNLISTENFUNC(commitWindow); + DYNLISTENFUNC(mapWindow); + DYNLISTENFUNC(unmapWindow); + DYNLISTENFUNC(destroyWindow); + DYNLISTENFUNC(setTitleWindow); + DYNLISTENFUNC(fullscreenWindow); + DYNLISTENFUNC(activateX11); + DYNLISTENFUNC(configureX11); + DYNLISTENFUNC(unmanagedSetGeometry); + DYNLISTENFUNC(requestMove); + DYNLISTENFUNC(requestResize); + DYNLISTENFUNC(requestMinimize); + DYNLISTENFUNC(requestMaximize); + DYNLISTENFUNC(setOverrideRedirect); + DYNLISTENFUNC(ackConfigure); +}; diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp new file mode 100644 index 00000000..adc0c762 --- /dev/null +++ b/src/events/Windows.cpp @@ -0,0 +1,1004 @@ +#include "Events.hpp" + +#include "../Compositor.hpp" +#include "../helpers/WLClasses.hpp" +#include "../managers/input/InputManager.hpp" +#include "../managers/TokenManager.hpp" +#include "../managers/SeatManager.hpp" +#include "../render/Renderer.hpp" +#include "../config/ConfigValue.hpp" +#include "../protocols/LayerShell.hpp" +#include "../protocols/XDGShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../protocols/ToplevelExport.hpp" +#include "../protocols/types/ContentType.hpp" +#include "../xwayland/XSurface.hpp" +#include "managers/AnimationManager.hpp" +#include "managers/PointerManager.hpp" +#include "../desktop/LayerSurface.hpp" +#include "../managers/LayoutManager.hpp" +#include "../managers/EventManager.hpp" +#include "../managers/AnimationManager.hpp" + +#include +using namespace Hyprutils::String; +using namespace Hyprutils::Animation; + +// ------------------------------------------------------------ // +// __ _______ _ _ _____ ______ _______ // +// \ \ / /_ _| \ | | __ \ / __ \ \ / / ____| // +// \ \ /\ / / | | | \| | | | | | | \ \ /\ / / (___ // +// \ \/ \/ / | | | . ` | | | | | | |\ \/ \/ / \___ \ // +// \ /\ / _| |_| |\ | |__| | |__| | \ /\ / ____) | // +// \/ \/ |_____|_| \_|_____/ \____/ \/ \/ |_____/ // +// // +// ------------------------------------------------------------ // + +static void setVector2DAnimToMove(WP pav) { + const auto PAV = pav.lock(); + if (!PAV) + return; + + CAnimatedVariable* animvar = dynamic_cast*>(PAV.get()); + animvar->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsMove")); + + const auto PHLWINDOW = animvar->m_Context.pWindow.lock(); + if (PHLWINDOW) + PHLWINDOW->m_bAnimatingIn = false; +} + +void Events::listener_mapWindow(void* owner, void* data) { + PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); + + static auto PINACTIVEALPHA = CConfigValue("decoration:inactive_opacity"); + static auto PACTIVEALPHA = CConfigValue("decoration:active_opacity"); + static auto PDIMSTRENGTH = CConfigValue("decoration:dim_strength"); + static auto PNEWTAKESOVERFS = CConfigValue("misc:new_window_takes_over_fullscreen"); + static auto PINITIALWSTRACKING = CConfigValue("misc:initial_workspace_tracking"); + + auto PMONITOR = g_pCompositor->m_pLastMonitor.lock(); + if (!g_pCompositor->m_pLastMonitor) { + g_pCompositor->setActiveMonitor(g_pCompositor->getMonitorFromVector({})); + PMONITOR = g_pCompositor->m_pLastMonitor.lock(); + } + auto PWORKSPACE = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace; + PWINDOW->m_pMonitor = PMONITOR; + PWINDOW->m_pWorkspace = PWORKSPACE; + PWINDOW->m_bIsMapped = true; + PWINDOW->m_bReadyToDelete = false; + PWINDOW->m_bFadingOut = false; + PWINDOW->m_szTitle = PWINDOW->fetchTitle(); + PWINDOW->m_bFirstMap = true; + PWINDOW->m_szInitialTitle = PWINDOW->m_szTitle; + PWINDOW->m_szInitialClass = PWINDOW->fetchClass(); + + // check for token + std::string requestedWorkspace = ""; + bool workspaceSilent = false; + + if (*PINITIALWSTRACKING) { + const auto WINDOWENV = PWINDOW->getEnv(); + if (WINDOWENV.contains("HL_INITIAL_WORKSPACE_TOKEN")) { + const auto SZTOKEN = WINDOWENV.at("HL_INITIAL_WORKSPACE_TOKEN"); + Debug::log(LOG, "New window contains HL_INITIAL_WORKSPACE_TOKEN: {}", SZTOKEN); + const auto TOKEN = g_pTokenManager->getToken(SZTOKEN); + if (TOKEN) { + // find workspace and use it + SInitialWorkspaceToken WS = std::any_cast(TOKEN->data); + + Debug::log(LOG, "HL_INITIAL_WORKSPACE_TOKEN {} -> {}", SZTOKEN, WS.workspace); + + if (g_pCompositor->getWorkspaceByString(WS.workspace) != PWINDOW->m_pWorkspace) { + requestedWorkspace = WS.workspace; + workspaceSilent = true; + } + + if (*PINITIALWSTRACKING == 1) // one-shot token + g_pTokenManager->removeToken(TOKEN); + else if (*PINITIALWSTRACKING == 2) { // persistent + if (WS.primaryOwner.expired()) { + WS.primaryOwner = PWINDOW; + TOKEN->data = WS; + } + + PWINDOW->m_szInitialWorkspaceToken = SZTOKEN; + } + } + } + } + + if (g_pInputManager->m_bLastFocusOnLS) // waybar fix + g_pInputManager->releaseAllMouseButtons(); + + // checks if the window wants borders and sets the appropriate flag + g_pXWaylandManager->checkBorders(PWINDOW); + + // registers the animated vars and stuff + PWINDOW->onMap(); + + const auto PWINDOWSURFACE = PWINDOW->m_pWLSurface->resource(); + + if (!PWINDOWSURFACE) { + g_pCompositor->removeWindowFromVectorSafe(PWINDOW); + return; + } + + if (g_pXWaylandManager->shouldBeFloated(PWINDOW)) { + PWINDOW->m_bIsFloating = true; + PWINDOW->m_bRequestsFloat = true; + } + + PWINDOW->m_bX11ShouldntFocus = PWINDOW->m_bX11ShouldntFocus || (PWINDOW->m_bIsX11 && PWINDOW->isX11OverrideRedirect() && !PWINDOW->m_pXWaylandSurface->wantsFocus()); + + // window rules + PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false); + std::optional requestedInternalFSMode, requestedClientFSMode; + std::optional requestedFSState; + if (PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen)) + requestedClientFSMode = FSMODE_FULLSCREEN; + MONITORID requestedFSMonitor = PWINDOW->m_iWantsInitialFullscreenMonitor; + + for (auto const& r : PWINDOW->m_vMatchedRules) { + switch (r->ruleType) { + case CWindowRule::RULE_MONITOR: { + try { + const auto MONITORSTR = trim(r->szRule.substr(r->szRule.find(' '))); + + if (MONITORSTR == "unset") { + PWINDOW->m_pMonitor = PMONITOR; + } else { + if (isNumber(MONITORSTR)) { + const MONITORID MONITOR = std::stoi(MONITORSTR); + if (const auto PM = g_pCompositor->getMonitorFromID(MONITOR); PM) + PWINDOW->m_pMonitor = PM; + else + PWINDOW->m_pMonitor = g_pCompositor->m_vMonitors.at(0); + } else { + const auto PMONITOR = g_pCompositor->getMonitorFromName(MONITORSTR); + if (PMONITOR) + PWINDOW->m_pMonitor = PMONITOR; + else { + Debug::log(ERR, "No monitor in monitor {} rule", MONITORSTR); + continue; + } + } + } + + const auto PMONITORFROMID = PWINDOW->m_pMonitor.lock(); + + if (PWINDOW->m_pMonitor != PMONITOR) { + g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->monitorID())); + PMONITOR = PMONITORFROMID; + } + PWINDOW->m_pWorkspace = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace; + PWORKSPACE = PWINDOW->m_pWorkspace; + + Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW); + requestedFSMonitor = MONITOR_INVALID; + } catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r->szRule, r->szValue, e.what()); } + break; + } + case CWindowRule::RULE_WORKSPACE: { + // check if it isnt unset + const auto WORKSPACERQ = r->szRule.substr(r->szRule.find_first_of(' ') + 1); + + if (WORKSPACERQ == "unset") + requestedWorkspace = ""; + else + requestedWorkspace = WORKSPACERQ; + + const auto JUSTWORKSPACE = WORKSPACERQ.contains(' ') ? WORKSPACERQ.substr(0, WORKSPACERQ.find_first_of(' ')) : WORKSPACERQ; + + if (JUSTWORKSPACE == PWORKSPACE->m_szName || JUSTWORKSPACE == "name:" + PWORKSPACE->m_szName) + requestedWorkspace = ""; + + Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r->szValue); + requestedFSMonitor = MONITOR_INVALID; + break; + } + case CWindowRule::RULE_FLOAT: { + PWINDOW->m_bIsFloating = true; + break; + } + case CWindowRule::RULE_TILE: { + PWINDOW->m_bIsFloating = false; + break; + } + case CWindowRule::RULE_PSEUDO: { + PWINDOW->m_bIsPseudotiled = true; + break; + } + case CWindowRule::RULE_NOINITIALFOCUS: { + PWINDOW->m_bNoInitialFocus = true; + break; + } + case CWindowRule::RULE_FULLSCREENSTATE: { + const auto ARGS = CVarList(r->szRule.substr(r->szRule.find_first_of(' ') + 1), 2, ' '); + int internalMode, clientMode; + try { + internalMode = std::stoi(ARGS[0]); + } catch (std::exception& e) { internalMode = 0; } + try { + clientMode = std::stoi(ARGS[1]); + } catch (std::exception& e) { clientMode = 0; } + requestedFSState = SFullscreenState{.internal = (eFullscreenMode)internalMode, .client = (eFullscreenMode)clientMode}; + break; + } + case CWindowRule::RULE_SUPPRESSEVENT: { + CVarList vars(r->szRule, 0, 's', true); + for (size_t i = 1; i < vars.size(); ++i) { + if (vars[i] == "fullscreen") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_FULLSCREEN; + else if (vars[i] == "maximize") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_MAXIMIZE; + else if (vars[i] == "activate") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE; + else if (vars[i] == "activatefocus") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY; + else if (vars[i] == "fullscreenoutput") + PWINDOW->m_eSuppressedEvents |= SUPPRESS_FULLSCREEN_OUTPUT; + else + Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]); + } + break; + } + case CWindowRule::RULE_PIN: { + PWINDOW->m_bPinned = true; + break; + } + case CWindowRule::RULE_FULLSCREEN: { + requestedInternalFSMode = FSMODE_FULLSCREEN; + break; + } + case CWindowRule::RULE_MAXIMIZE: { + requestedInternalFSMode = FSMODE_MAXIMIZED; + break; + } + case CWindowRule::RULE_STAYFOCUSED: { + PWINDOW->m_bStayFocused = true; + break; + } + case CWindowRule::RULE_GROUP: { + if (PWINDOW->m_eGroupRules & GROUP_OVERRIDE) + continue; + + // `group` is a shorthand of `group set` + if (trim(r->szRule) == "group") { + PWINDOW->m_eGroupRules |= GROUP_SET; + continue; + } + + CVarList vars(r->szRule, 0, 's'); + std::string vPrev = ""; + + for (auto const& v : vars) { + if (v == "group") + continue; + + if (v == "set") { + PWINDOW->m_eGroupRules |= GROUP_SET; + } else if (v == "new") { + // shorthand for `group barred set` + PWINDOW->m_eGroupRules |= (GROUP_SET | GROUP_BARRED); + } else if (v == "lock") { + PWINDOW->m_eGroupRules |= GROUP_LOCK; + } else if (v == "invade") { + PWINDOW->m_eGroupRules |= GROUP_INVADE; + } else if (v == "barred") { + PWINDOW->m_eGroupRules |= GROUP_BARRED; + } else if (v == "deny") { + PWINDOW->m_sGroupData.deny = true; + } else if (v == "override") { + // Clear existing rules + PWINDOW->m_eGroupRules = GROUP_OVERRIDE; + } else if (v == "unset") { + // Clear existing rules and stop processing + PWINDOW->m_eGroupRules = GROUP_OVERRIDE; + break; + } else if (v == "always") { + if (vPrev == "set" || vPrev == "group") + PWINDOW->m_eGroupRules |= GROUP_SET_ALWAYS; + else if (vPrev == "lock") + PWINDOW->m_eGroupRules |= GROUP_LOCK_ALWAYS; + else + Debug::log(ERR, "windowrule `group` does not support `{} always`", vPrev); + } + vPrev = v; + } + break; + } + case CWindowRule::RULE_CONTENT: { + const CVarList VARS(r->szRule, 0, ' '); + try { + PWINDOW->setContentType(NContentType::fromString(VARS[1])); + } catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->szRule, e.what()); } + break; + } + default: break; + } + + PWINDOW->applyDynamicRule(r); + } + + // disallow tiled pinned + if (PWINDOW->m_bPinned && !PWINDOW->m_bIsFloating) + PWINDOW->m_bPinned = false; + + const CVarList WORKSPACEARGS = CVarList(requestedWorkspace, 0, ' '); + + if (!WORKSPACEARGS[0].empty()) { + if (WORKSPACEARGS[WORKSPACEARGS.size() - 1].starts_with("silent")) + workspaceSilent = true; + + const auto& [REQUESTEDWORKSPACEID, requestedWorkspaceName] = getWorkspaceIDNameFromString(WORKSPACEARGS.join(" ", 0, workspaceSilent ? WORKSPACEARGS.size() - 1 : 0)); + + if (REQUESTEDWORKSPACEID != WORKSPACE_INVALID) { + auto pWorkspace = g_pCompositor->getWorkspaceByID(REQUESTEDWORKSPACEID); + + if (!pWorkspace) + pWorkspace = g_pCompositor->createNewWorkspace(REQUESTEDWORKSPACEID, PWINDOW->monitorID(), requestedWorkspaceName, false); + + PWORKSPACE = pWorkspace; + + PWINDOW->m_pWorkspace = pWorkspace; + PWINDOW->m_pMonitor = pWorkspace->m_pMonitor; + + if (PWINDOW->m_pMonitor.lock()->activeSpecialWorkspace && !pWorkspace->m_bIsSpecialWorkspace) + workspaceSilent = true; + + if (!workspaceSilent) { + if (pWorkspace->m_bIsSpecialWorkspace) + pWorkspace->m_pMonitor->setSpecialWorkspace(pWorkspace); + else if (PMONITOR->activeWorkspaceID() != REQUESTEDWORKSPACEID && !PWINDOW->m_bNoInitialFocus) + g_pKeybindManager->m_mDispatchers["workspace"](requestedWorkspaceName); + + PMONITOR = g_pCompositor->m_pLastMonitor.lock(); + } + + requestedFSMonitor = MONITOR_INVALID; + } else + workspaceSilent = false; + } + + if (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN_OUTPUT) + requestedFSMonitor = MONITOR_INVALID; + else if (requestedFSMonitor != MONITOR_INVALID) { + if (const auto PM = g_pCompositor->getMonitorFromID(requestedFSMonitor); PM) + PWINDOW->m_pMonitor = PM; + + const auto PMONITORFROMID = PWINDOW->m_pMonitor.lock(); + + if (PWINDOW->m_pMonitor != PMONITOR) { + g_pKeybindManager->m_mDispatchers["focusmonitor"](std::to_string(PWINDOW->monitorID())); + PMONITOR = PMONITORFROMID; + } + PWINDOW->m_pWorkspace = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace; + PWORKSPACE = PWINDOW->m_pWorkspace; + + Debug::log(LOG, "Requested monitor, applying to {:mw}", PWINDOW); + } + + if (PWORKSPACE->m_bDefaultFloating) + PWINDOW->m_bIsFloating = true; + + if (PWORKSPACE->m_bDefaultPseudo) { + PWINDOW->m_bIsPseudotiled = true; + CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(PWINDOW); + PWINDOW->m_vPseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height); + } + + PWINDOW->updateWindowData(); + + // Verify window swallowing. Get the swallower before calling onWindowCreated(PWINDOW) because getSwallower() wouldn't get it after if PWINDOW gets auto grouped. + const auto SWALLOWER = PWINDOW->getSwallower(); + PWINDOW->m_pSwallowed = SWALLOWER; + if (PWINDOW->m_pSwallowed) + PWINDOW->m_pSwallowed->m_bCurrentlySwallowed = true; + + if (PWINDOW->m_bIsFloating) { + g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW); + PWINDOW->m_bCreatedOverFullscreen = true; + + // size and move rules + for (auto const& r : PWINDOW->m_vMatchedRules) { + switch (r->ruleType) { + case CWindowRule::RULE_SIZE: { + try { + auto stringToFloatClamp = [](const std::string& VALUE, const float CURR, const float REL) { + if (VALUE.starts_with('<')) + return std::min(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL)); + else if (VALUE.starts_with('>')) + return std::max(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL)); + + return stringToPercentage(VALUE, REL); + }; + + const auto VALUE = r->szRule.substr(r->szRule.find(' ') + 1); + const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' ')); + const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1); + + const auto MAXSIZE = PWINDOW->requestedMaxSize(); + + const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->vecSize.x) : + stringToFloatClamp(SIZEXSTR, PWINDOW->m_vRealSize->goal().x, PMONITOR->vecSize.x); + + const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->vecSize.y) : + stringToFloatClamp(SIZEYSTR, PWINDOW->m_vRealSize->goal().y, PMONITOR->vecSize.y); + + Debug::log(LOG, "Rule size, applying to {}", PWINDOW); + + PWINDOW->clampWindowSize(Vector2D{SIZEXSTR.starts_with("<") ? 0 : SIZEX, SIZEYSTR.starts_with("<") ? 0 : SIZEY}, Vector2D{SIZEX, SIZEY}); + + PWINDOW->setHidden(false); + } catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->szRule, r->szValue); } + break; + } + case CWindowRule::RULE_MOVE: { + try { + auto value = r->szRule.substr(r->szRule.find(' ') + 1); + + const bool ONSCREEN = value.starts_with("onscreen"); + + if (ONSCREEN) + value = value.substr(value.find_first_of(' ') + 1); + + const bool CURSOR = value.starts_with("cursor"); + + if (CURSOR) + value = value.substr(value.find_first_of(' ') + 1); + + const auto POSXSTR = value.substr(0, value.find(' ')); + const auto POSYSTR = value.substr(value.find(' ') + 1); + + int posX = 0; + int posY = 0; + + if (POSXSTR.starts_with("100%-")) { + const bool subtractWindow = POSXSTR.starts_with("100%-w-"); + const auto POSXRAW = (subtractWindow) ? POSXSTR.substr(7) : POSXSTR.substr(5); + posX = PMONITOR->vecSize.x - + (!POSXRAW.contains('%') ? std::stoi(POSXRAW) : std::stof(POSXRAW.substr(0, POSXRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.x); + + if (subtractWindow) + posX -= PWINDOW->m_vRealSize->goal().x; + + if (CURSOR) + Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!"); + } else if (!CURSOR) { + posX = !POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.x; + } else { + // cursor + if (POSXSTR == "cursor") { + posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->vecPosition.x; + } else { + posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->vecPosition.x + + (!POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PWINDOW->m_vRealSize->goal().x); + } + } + + if (POSYSTR.starts_with("100%-")) { + const bool subtractWindow = POSYSTR.starts_with("100%-w-"); + const auto POSYRAW = (subtractWindow) ? POSYSTR.substr(7) : POSYSTR.substr(5); + posY = PMONITOR->vecSize.y - + (!POSYRAW.contains('%') ? std::stoi(POSYRAW) : std::stof(POSYRAW.substr(0, POSYRAW.length() - 1)) * 0.01 * PMONITOR->vecSize.y); + + if (subtractWindow) + posY -= PWINDOW->m_vRealSize->goal().y; + + if (CURSOR) + Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!"); + } else if (!CURSOR) { + posY = !POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.y; + } else { + // cursor + if (POSYSTR == "cursor") { + posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->vecPosition.y; + } else { + posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->vecPosition.y + + (!POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PWINDOW->m_vRealSize->goal().y); + } + } + + if (ONSCREEN) { + int borderSize = PWINDOW->getRealBorderSize(); + + posX = std::clamp(posX, (int)(PMONITOR->vecReservedTopLeft.x + borderSize), + (int)(PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PWINDOW->m_vRealSize->goal().x - borderSize)); + + posY = std::clamp(posY, (int)(PMONITOR->vecReservedTopLeft.y + borderSize), + (int)(PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PWINDOW->m_vRealSize->goal().y - borderSize)); + } + + Debug::log(LOG, "Rule move, applying to {}", PWINDOW); + + *PWINDOW->m_vRealPosition = Vector2D(posX, posY) + PMONITOR->vecPosition; + + PWINDOW->setHidden(false); + } catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r->szRule, r->szValue); } + break; + } + case CWindowRule::RULE_CENTER: { + auto RESERVEDOFFSET = Vector2D(); + const auto ARGS = CVarList(r->szRule, 2, ' '); + if (ARGS[1] == "1") + RESERVEDOFFSET = (PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight) / 2.f; + + *PWINDOW->m_vRealPosition = PMONITOR->middle() - PWINDOW->m_vRealSize->goal() / 2.f + RESERVEDOFFSET; + break; + } + + default: break; + } + } + + // set the pseudo size to the GOAL of our current size + // because the windows are animated on RealSize + PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize->goal(); + + g_pCompositor->changeWindowZOrder(PWINDOW, true); + } else { + g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW); + + bool setPseudo = false; + + for (auto const& r : PWINDOW->m_vMatchedRules) { + if (r->ruleType != CWindowRule::RULE_SIZE) + continue; + + try { + const auto VALUE = r->szRule.substr(r->szRule.find(' ') + 1); + const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' ')); + const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1); + + const auto MAXSIZE = PWINDOW->requestedMaxSize(); + + const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->vecSize.x) : stringToPercentage(SIZEXSTR, PMONITOR->vecSize.x); + + const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->vecSize.y) : stringToPercentage(SIZEYSTR, PMONITOR->vecSize.y); + + Debug::log(LOG, "Rule size (tiled), applying to {}", PWINDOW); + + setPseudo = true; + PWINDOW->m_vPseudoSize = Vector2D(SIZEX, SIZEY); + + PWINDOW->setHidden(false); + } catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->szRule, r->szValue); } + } + + if (!setPseudo) + PWINDOW->m_vPseudoSize = PWINDOW->m_vRealSize->goal() - Vector2D(10, 10); + } + + const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_pLastWindow.lock(); + + if (PWINDOW->m_sWindowData.allowsInput.valueOrDefault()) { // if default value wasn't set to false getPriority() would throw an exception + PWINDOW->m_sWindowData.noFocus = CWindowOverridableVar(false, PWINDOW->m_sWindowData.allowsInput.getPriority()); + PWINDOW->m_bNoInitialFocus = false; + PWINDOW->m_bX11ShouldntFocus = false; + } + + // check LS focus grab + const auto PFORCEFOCUS = g_pCompositor->getForceFocus(); + const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus.lock()); + if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) + PWINDOW->m_bNoInitialFocus = true; + + if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow && !requestedInternalFSMode.has_value() && !requestedClientFSMode.has_value() && !PWINDOW->m_bIsFloating) { + if (*PNEWTAKESOVERFS == 0) + PWINDOW->m_bNoInitialFocus = true; + else if (*PNEWTAKESOVERFS == 1) + requestedInternalFSMode = PWINDOW->m_pWorkspace->m_efFullscreenMode; + else if (*PNEWTAKESOVERFS == 2) + g_pCompositor->setWindowFullscreenInternal(PWINDOW->m_pWorkspace->getFullscreenWindow(), FSMODE_NONE); + } + + if (!PWINDOW->m_sWindowData.noFocus.valueOrDefault() && !PWINDOW->m_bNoInitialFocus && + (!PWINDOW->isX11OverrideRedirect() || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->wantsFocus())) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) && + !g_pInputManager->isConstrained()) { + g_pCompositor->focusWindow(PWINDOW); + PWINDOW->m_fActiveInactiveAlpha->setValueAndWarp(*PACTIVEALPHA); + PWINDOW->m_fDimPercent->setValueAndWarp(PWINDOW->m_sWindowData.noDim.valueOrDefault() ? 0.f : *PDIMSTRENGTH); + } else { + PWINDOW->m_fActiveInactiveAlpha->setValueAndWarp(*PINACTIVEALPHA); + PWINDOW->m_fDimPercent->setValueAndWarp(0); + } + + if (requestedClientFSMode.has_value() && (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) + requestedClientFSMode = (eFullscreenMode)((uint8_t)requestedClientFSMode.value_or(FSMODE_NONE) & ~(uint8_t)FSMODE_FULLSCREEN); + if (requestedClientFSMode.has_value() && (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE)) + requestedClientFSMode = (eFullscreenMode)((uint8_t)requestedClientFSMode.value_or(FSMODE_NONE) & ~(uint8_t)FSMODE_MAXIMIZED); + + if (!PWINDOW->m_bNoInitialFocus && (requestedInternalFSMode.has_value() || requestedClientFSMode.has_value() || requestedFSState.has_value())) { + // fix fullscreen on requested (basically do a switcheroo) + if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow) + g_pCompositor->setWindowFullscreenInternal(PWINDOW->m_pWorkspace->getFullscreenWindow(), FSMODE_NONE); + + PWINDOW->m_vRealPosition->warp(); + PWINDOW->m_vRealSize->warp(); + if (requestedFSState.has_value()) { + PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_WINDOW_RULE); + g_pCompositor->setWindowFullscreenState(PWINDOW, requestedFSState.value()); + } else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !PWINDOW->m_sWindowData.syncFullscreen.valueOrDefault()) + g_pCompositor->setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()}); + else if (requestedInternalFSMode.has_value()) + g_pCompositor->setWindowFullscreenInternal(PWINDOW, requestedInternalFSMode.value()); + else if (requestedClientFSMode.has_value()) + g_pCompositor->setWindowFullscreenClient(PWINDOW, requestedClientFSMode.value()); + } + + // recheck idle inhibitors + g_pInputManager->recheckIdleInhibitorStatus(); + + PWINDOW->updateToplevel(); + + if (workspaceSilent) { + if (validMapped(PFOCUSEDWINDOWPREV)) { + g_pCompositor->focusWindow(PFOCUSEDWINDOWPREV); + PFOCUSEDWINDOWPREV->updateWindowDecos(); // need to for some reason i cba to find out why + } else if (!PFOCUSEDWINDOWPREV) + g_pCompositor->focusWindow(nullptr); + } + + // swallow + if (SWALLOWER) { + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(SWALLOWER); + g_pHyprRenderer->damageWindow(SWALLOWER); + SWALLOWER->setHidden(true); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID()); + } + + PWINDOW->m_bFirstMap = false; + + Debug::log(LOG, "Map request dispatched, monitor {}, window pos: {:5j}, window size: {:5j}", PMONITOR->szName, PWINDOW->m_vRealPosition->goal(), PWINDOW->m_vRealSize->goal()); + + auto workspaceID = requestedWorkspace != "" ? requestedWorkspace : PWORKSPACE->m_szName; + g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", PWINDOW, workspaceID, PWINDOW->m_szClass, PWINDOW->m_szTitle)}); + EMIT_HOOK_EVENT("openWindow", PWINDOW); + + // apply data from default decos. Borders, shadows. + g_pDecorationPositioner->forceRecalcFor(PWINDOW); + PWINDOW->updateWindowDecos(); + g_pLayoutManager->getCurrentLayout()->recalculateWindow(PWINDOW); + + // do animations + g_pAnimationManager->onWindowPostCreateClose(PWINDOW, false); + PWINDOW->m_fAlpha->setValueAndWarp(0.f); + *PWINDOW->m_fAlpha = 1.f; + + PWINDOW->m_vRealPosition->setCallbackOnEnd(setVector2DAnimToMove); + PWINDOW->m_vRealSize->setCallbackOnEnd(setVector2DAnimToMove); + + // recalc the values for this window + g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); + // avoid this window being visible + if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->isFullscreen() && !PWINDOW->m_bIsFloating) + PWINDOW->m_fAlpha->setValueAndWarp(0.f); + + g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface->resource(), PMONITOR->scale); + g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface->resource(), PMONITOR->transform); + + if (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) + g_pInputManager->sendMotionEventsToFocused(); + + // fix some xwayland apps that don't behave nicely + PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; + + if (PWINDOW->m_pWorkspace) + PWINDOW->m_pWorkspace->updateWindows(); + + if (PMONITOR && PWINDOW->isX11OverrideRedirect()) + PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale; +} + +void Events::listener_unmapWindow(void* owner, void* data) { + PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); + + Debug::log(LOG, "{:c} unmapped", PWINDOW); + + static auto PEXITRETAINSFS = CConfigValue("misc:exit_window_retains_fullscreen"); + + const auto CURRENTWINDOWFSSTATE = PWINDOW->isFullscreen(); + const auto CURRENTFSMODE = PWINDOW->m_sFullscreenState.internal; + + if (!PWINDOW->m_pWLSurface->exists() || !PWINDOW->m_bIsMapped) { + Debug::log(WARN, "{} unmapped without being mapped??", PWINDOW); + PWINDOW->m_bFadingOut = false; + return; + } + + const auto PMONITOR = PWINDOW->m_pMonitor.lock(); + if (PMONITOR) { + PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition->value() - PMONITOR->vecPosition; + PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize->value(); + PWINDOW->m_eOriginalClosedExtents = PWINDOW->getFullWindowExtents(); + } + + g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)}); + EMIT_HOOK_EVENT("closeWindow", PWINDOW); + + if (PWINDOW->m_bIsFloating && !PWINDOW->m_bIsX11 && + std::any_of(PWINDOW->m_vMatchedRules.begin(), PWINDOW->m_vMatchedRules.end(), [](const auto& r) { return r->ruleType == CWindowRule::RULE_PERSISTENTSIZE; })) { + Debug::log(LOG, "storing floating size {}x{} for window {}::{} on close", PWINDOW->m_vRealSize->value().x, PWINDOW->m_vRealSize->value().y, PWINDOW->m_szClass, + PWINDOW->m_szTitle); + g_pConfigManager->storeFloatingSize(PWINDOW, PWINDOW->m_vRealSize->value()); + } + + PROTO::toplevelExport->onWindowUnmap(PWINDOW); + + if (PWINDOW->isFullscreen()) + g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); + + // Allow the renderer to catch the last frame. + g_pHyprRenderer->makeWindowSnapshot(PWINDOW); + + // swallowing + if (valid(PWINDOW->m_pSwallowed)) { + if (PWINDOW->m_pSwallowed->m_bCurrentlySwallowed) { + PWINDOW->m_pSwallowed->m_bCurrentlySwallowed = false; + PWINDOW->m_pSwallowed->setHidden(false); + + if (PWINDOW->m_sGroupData.pNextWindow.lock()) + PWINDOW->m_pSwallowed->m_bGroupSwallowed = true; // flag for the swallowed window to be created into the group where it belongs when auto_group = false. + + g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW->m_pSwallowed.lock()); + } + + PWINDOW->m_pSwallowed->m_bGroupSwallowed = false; + PWINDOW->m_pSwallowed.reset(); + } + + bool wasLastWindow = false; + + if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) { + wasLastWindow = true; + g_pCompositor->m_pLastWindow.reset(); + g_pCompositor->m_pLastFocus.reset(); + + g_pInputManager->releaseAllMouseButtons(); + } + + if (PWINDOW == g_pInputManager->currentlyDraggedWindow.lock()) + g_pKeybindManager->changeMouseBindMode(MBIND_INVALID); + + // remove the fullscreen window status from workspace if we closed it + const auto PWORKSPACE = PWINDOW->m_pWorkspace; + + if (PWORKSPACE->m_bHasFullscreenWindow && PWINDOW->isFullscreen()) + PWORKSPACE->m_bHasFullscreenWindow = false; + + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW); + + g_pHyprRenderer->damageWindow(PWINDOW); + + // do this after onWindowRemoved because otherwise it'll think the window is invalid + PWINDOW->m_bIsMapped = false; + + // refocus on a new window if needed + if (wasLastWindow) { + static auto FOCUSONCLOSE = CConfigValue("input:focus_on_close"); + PHLWINDOW PWINDOWCANDIDATE = nullptr; + if (*FOCUSONCLOSE) + PWINDOWCANDIDATE = (g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING)); + else + PWINDOWCANDIDATE = g_pLayoutManager->getCurrentLayout()->getNextWindowCandidate(PWINDOW); + + Debug::log(LOG, "On closed window, new focused candidate is {}", PWINDOWCANDIDATE); + + if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow.lock() && PWINDOWCANDIDATE) { + g_pCompositor->focusWindow(PWINDOWCANDIDATE); + if (*PEXITRETAINSFS && CURRENTWINDOWFSSTATE) + g_pCompositor->setWindowFullscreenInternal(PWINDOWCANDIDATE, CURRENTFSMODE); + } + + if (!PWINDOWCANDIDATE && PWINDOW->m_pWorkspace && PWINDOW->m_pWorkspace->getWindows() == 0) + g_pInputManager->refocus(); + + g_pInputManager->sendMotionEventsToFocused(); + + // CWindow::onUnmap will remove this window's active status, but we can't really do it above. + if (PWINDOW == g_pCompositor->m_pLastWindow.lock() || !g_pCompositor->m_pLastWindow.lock()) { + g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); + g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""}); + EMIT_HOOK_EVENT("activeWindow", (PHLWINDOW) nullptr); + } + } else { + Debug::log(LOG, "Unmapped was not focused, ignoring a refocus."); + } + + PWINDOW->m_bFadingOut = true; + + g_pCompositor->addToFadingOutSafe(PWINDOW); + + if (!PWINDOW->m_bX11DoesntWantBorders) // don't animate out if they weren't animated in. + *PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition->value() + Vector2D(0.01f, 0.01f); // it has to be animated, otherwise onWindowPostCreateClose will ignore it + + // anims + g_pAnimationManager->onWindowPostCreateClose(PWINDOW, true); + *PWINDOW->m_fAlpha = 0.f; + + // recheck idle inhibitors + g_pInputManager->recheckIdleInhibitorStatus(); + + // force report all sizes (QT sometimes has an issue with this) + if (PWINDOW->m_pWorkspace) + PWINDOW->m_pWorkspace->forceReportSizesToWindows(); + + // update lastwindow after focus + PWINDOW->onUnmap(); +} + +void Events::listener_commitWindow(void* owner, void* data) { + PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); + + if (!PWINDOW->m_bIsX11 && PWINDOW->m_pXDGSurface->initialCommit) { + Vector2D predSize = g_pLayoutManager->getCurrentLayout()->predictSizeForNewWindow(PWINDOW); + + Debug::log(LOG, "Layout predicts size {} for {}", predSize, PWINDOW); + + PWINDOW->m_pXDGSurface->toplevel->setSize(predSize); + return; + } + + if (!PWINDOW->m_bIsMapped || PWINDOW->isHidden()) + return; + + PWINDOW->m_vReportedSize = PWINDOW->m_vPendingReportedSize; // apply pending size. We pinged, the window ponged. + + if (!PWINDOW->m_bIsX11 && !PWINDOW->isFullscreen() && PWINDOW->m_bIsFloating) { + const auto MINSIZE = PWINDOW->m_pXDGSurface->toplevel->layoutMinSize(); + const auto MAXSIZE = PWINDOW->m_pXDGSurface->toplevel->layoutMaxSize(); + + PWINDOW->clampWindowSize(MINSIZE, MAXSIZE > Vector2D{1, 1} ? std::optional{MAXSIZE} : std::nullopt); + g_pHyprRenderer->damageWindow(PWINDOW); + } + + if (!PWINDOW->m_pWorkspace->m_bVisible) + return; + + const auto PMONITOR = PWINDOW->m_pMonitor.lock(); + + if (PMONITOR) + PMONITOR->debugLastPresentation(g_pSeatManager->isPointerFrameCommit ? "listener_commitWindow skip" : "listener_commitWindow"); + + if (g_pSeatManager->isPointerFrameCommit) { + g_pSeatManager->isPointerFrameSkipped = false; + g_pSeatManager->isPointerFrameCommit = false; + } else + g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface->resource(), PWINDOW->m_vRealPosition->goal().x, PWINDOW->m_vRealPosition->goal().y, + PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0); + + if (g_pSeatManager->isPointerFrameSkipped) { + g_pPointerManager->sendStoredMovement(); + g_pSeatManager->sendPointerFrame(); + g_pSeatManager->isPointerFrameCommit = true; + } + + if (!PWINDOW->m_bIsX11) { + PWINDOW->m_pSubsurfaceHead->recheckDamageForSubsurfaces(); + PWINDOW->m_pPopupHead->recheckTree(); + } + + // tearing: if solitary, redraw it. This still might be a single surface window + if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.texture) { + CRegion damageBox{PWINDOW->m_pWLSurface->resource()->current.accumulateBufferDamage()}; + + if (!damageBox.empty()) { + if (PMONITOR->tearingState.busy) { + PMONITOR->tearingState.frameScheduledWhileBusy = true; + } else { + PMONITOR->tearingState.nextRenderTorn = true; + g_pHyprRenderer->renderMonitor(PMONITOR); + } + } + } +} + +void Events::listener_destroyWindow(void* owner, void* data) { + PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); + + Debug::log(LOG, "{:c} destroyed, queueing.", PWINDOW); + + if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) { + g_pCompositor->m_pLastWindow.reset(); + g_pCompositor->m_pLastFocus.reset(); + } + + PWINDOW->m_pWLSurface->unassign(); + + PWINDOW->listeners = {}; + + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW); + + PWINDOW->m_bReadyToDelete = true; + + PWINDOW->m_pXDGSurface.reset(); + + if (!PWINDOW->m_bFadingOut) { + Debug::log(LOG, "Unmapped {} removed instantly", PWINDOW); + g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn + } + + PWINDOW->listeners.unmap.reset(); + PWINDOW->listeners.destroy.reset(); + PWINDOW->listeners.map.reset(); + PWINDOW->listeners.commit.reset(); +} + +void Events::listener_activateX11(void* owner, void* data) { + PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); + + Debug::log(LOG, "X11 Activate request for window {}", PWINDOW); + + if (PWINDOW->isX11OverrideRedirect()) { + + Debug::log(LOG, "Unmanaged X11 {} requests activate", PWINDOW); + + if (g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->getPID() != PWINDOW->getPID()) + return; + + if (!PWINDOW->m_pXWaylandSurface->wantsFocus()) + return; + + g_pCompositor->focusWindow(PWINDOW); + return; + } + + if (PWINDOW == g_pCompositor->m_pLastWindow.lock() || (PWINDOW->m_eSuppressedEvents & SUPPRESS_ACTIVATE)) + return; + + PWINDOW->activate(); +} + +void Events::listener_unmanagedSetGeometry(void* owner, void* data) { + PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); + + if (!PWINDOW->m_bIsMapped || !PWINDOW->m_pXWaylandSurface || !PWINDOW->m_pXWaylandSurface->overrideRedirect) + return; + + const auto POS = PWINDOW->m_vRealPosition->goal(); + const auto SIZ = PWINDOW->m_vRealSize->goal(); + + if (PWINDOW->m_pXWaylandSurface->geometry.size() > Vector2D{1, 1}) + PWINDOW->setHidden(false); + else + PWINDOW->setHidden(true); + + if (PWINDOW->isFullscreen() || !PWINDOW->m_bIsFloating) { + PWINDOW->sendWindowSize(true); + g_pHyprRenderer->damageWindow(PWINDOW); + return; + } + + static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); + + const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords(PWINDOW->m_pXWaylandSurface->geometry.pos()); + + if (abs(std::floor(POS.x) - LOGICALPOS.x) > 2 || abs(std::floor(POS.y) - LOGICALPOS.y) > 2 || abs(std::floor(SIZ.x) - PWINDOW->m_pXWaylandSurface->geometry.width) > 2 || + abs(std::floor(SIZ.y) - PWINDOW->m_pXWaylandSurface->geometry.height) > 2) { + Debug::log(LOG, "Unmanaged window {} requests geometry update to {:j} {:j}", PWINDOW, LOGICALPOS, PWINDOW->m_pXWaylandSurface->geometry.size()); + + g_pHyprRenderer->damageWindow(PWINDOW); + PWINDOW->m_vRealPosition->setValueAndWarp(Vector2D(LOGICALPOS.x, LOGICALPOS.y)); + + if (abs(std::floor(SIZ.x) - PWINDOW->m_pXWaylandSurface->geometry.w) > 2 || abs(std::floor(SIZ.y) - PWINDOW->m_pXWaylandSurface->geometry.h) > 2) + PWINDOW->m_vRealSize->setValueAndWarp(PWINDOW->m_pXWaylandSurface->geometry.size()); + + if (*PXWLFORCESCALEZERO) { + if (const auto PMONITOR = PWINDOW->m_pMonitor.lock(); PMONITOR) { + PWINDOW->m_vRealSize->setValueAndWarp(PWINDOW->m_vRealSize->goal() / PMONITOR->scale); + } + } + + PWINDOW->m_vPosition = PWINDOW->m_vRealPosition->goal(); + PWINDOW->m_vSize = PWINDOW->m_vRealSize->goal(); + + PWINDOW->m_pWorkspace = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition->value() + PWINDOW->m_vRealSize->value() / 2.f)->activeWorkspace; + + g_pCompositor->changeWindowZOrder(PWINDOW, true); + PWINDOW->updateWindowDecos(); + g_pHyprRenderer->damageWindow(PWINDOW); + + PWINDOW->m_vReportedPosition = PWINDOW->m_vRealPosition->goal(); + PWINDOW->m_vPendingReportedSize = PWINDOW->m_vRealSize->goal(); + } +} diff --git a/src/helpers/AnimatedVariable.hpp b/src/helpers/AnimatedVariable.hpp index f0bdc5a8..e7d5fd8c 100644 --- a/src/helpers/AnimatedVariable.hpp +++ b/src/helpers/AnimatedVariable.hpp @@ -67,7 +67,7 @@ template using CAnimatedVariable = Hyprutils::Animation::CGenericAnimatedVariable; template -using PHLANIMVAR = UP>; +using PHLANIMVAR = SP>; template using PHLANIMVARREF = WP>; diff --git a/src/helpers/AsyncDialogBox.cpp b/src/helpers/AsyncDialogBox.cpp deleted file mode 100644 index 8c2c7cd7..00000000 --- a/src/helpers/AsyncDialogBox.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "AsyncDialogBox.hpp" -#include "./fs/FsUtils.hpp" -#include -#include -#include -#include "../managers/eventLoop/EventLoopManager.hpp" -#include "../desktop/rule/windowRule/WindowRule.hpp" -#include "../desktop/rule/Engine.hpp" - -using namespace Hyprutils::OS; - -static std::vector>> asyncDialogBoxes; - -// -SP CAsyncDialogBox::create(const std::string& title, const std::string& description, std::vector buttons) { - if (!NFsUtils::executableExistsInPath("hyprland-dialog")) { - Log::logger->log(Log::ERR, "CAsyncDialogBox: cannot create, no hyprland-dialog"); - return nullptr; - } - - auto dialog = SP(new CAsyncDialogBox(title, description, buttons)); - - dialog->m_selfWeakReference = dialog; - - return dialog; -} - -bool CAsyncDialogBox::isAsyncDialogBox(pid_t pid) { - return std::ranges::find_if(asyncDialogBoxes, [pid](const auto& e) { return e.first == pid; }) != asyncDialogBoxes.end(); -} - -bool CAsyncDialogBox::isPriorityDialogBox(pid_t pid) { - for (const auto& [p, db] : asyncDialogBoxes) { - if (p != pid) - continue; - - return db && db->m_priority; - } - - return false; -} - -CAsyncDialogBox::CAsyncDialogBox(const std::string& title, const std::string& description, std::vector buttons) : - m_title(title), m_description(description), m_buttons(buttons) { - ; -} - -static int onFdWrite(int fd, uint32_t mask, void* data) { - auto box = sc(data); - - // lock the box to prevent a UAF - auto lock = box->lockSelf(); - - box->onWrite(fd, mask); - - return 0; -} - -void CAsyncDialogBox::onWrite(int fd, uint32_t mask) { - if (mask & WL_EVENT_READABLE) { - std::array buf; - int ret = 0; - - // make the FD nonblock for a moment - // TODO: can we avoid this without risking a blocking read()? - int fdFlags = fcntl(fd, F_GETFL, 0); - if (fcntl(fd, F_SETFL, fdFlags | O_NONBLOCK) < 0) { - Log::logger->log(Log::ERR, "CAsyncDialogBox::onWrite: fcntl 1 failed!"); - return; - } - - while ((ret = read(m_pipeReadFd.get(), buf.data(), 1023)) > 0) { - m_stdout += std::string_view{(buf.data()), sc(ret)}; - } - - // restore the flags (otherwise libwayland won't give us a hangup) - if (fcntl(fd, F_SETFL, fdFlags) < 0) { - Log::logger->log(Log::ERR, "CAsyncDialogBox::onWrite: fcntl 2 failed!"); - return; - } - } - - if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { - Log::logger->log(Log::DEBUG, "CAsyncDialogBox: dialog {:x} hung up, closed."); - - m_promiseResolver->resolve(m_stdout); - std::erase_if(asyncDialogBoxes, [this](const auto& e) { return e.first == m_dialogPid; }); - - wl_event_source_remove(m_readEventSource); - m_selfReference.reset(); - return; - } -} - -SP> CAsyncDialogBox::open() { - std::string buttonsString = ""; - for (auto& b : m_buttons) { - buttonsString += b + ";"; - } - if (!buttonsString.empty()) - buttonsString.pop_back(); - - CProcess proc("hyprland-dialog", std::vector{"--title", m_title, "--text", m_description, "--buttons", buttonsString}); - - int outPipe[2]; - if (pipe(outPipe)) { - Log::logger->log(Log::ERR, "CAsyncDialogBox::open: failed to pipe()"); - return nullptr; - } - - m_pipeReadFd = CFileDescriptor(outPipe[0]); - - proc.setStdoutFD(outPipe[1]); - - m_readEventSource = wl_event_loop_add_fd(g_pEventLoopManager->m_wayland.loop, m_pipeReadFd.get(), WL_EVENT_READABLE, ::onFdWrite, this); - - if (!m_readEventSource) { - Log::logger->log(Log::ERR, "CAsyncDialogBox::open: failed to add read fd to loop"); - return nullptr; - } - - m_selfReference = m_selfWeakReference.lock(); - - if (!m_execRuleToken.empty()) - proc.addEnv(Desktop::Rule::EXEC_RULE_ENV_NAME, m_execRuleToken); - - if (!proc.runAsync()) { - Log::logger->log(Log::ERR, "CAsyncDialogBox::open: failed to run async"); - wl_event_source_remove(m_readEventSource); - return nullptr; - } - - m_dialogPid = proc.pid(); - asyncDialogBoxes.emplace_back(std::make_pair<>(m_dialogPid, m_selfWeakReference)); - - // close the write fd, only the dialog owns it now - close(outPipe[1]); - - auto promise = CPromise::make([this](SP> r) { m_promiseResolver = r; }); - - return promise; -} - -void CAsyncDialogBox::kill() { - if (m_dialogPid <= 0) - return; - - ::kill(m_dialogPid, SIGKILL); -} - -bool CAsyncDialogBox::isRunning() const { - return m_readEventSource; -} - -pid_t CAsyncDialogBox::getPID() const { - return m_dialogPid; -} - -SP CAsyncDialogBox::lockSelf() { - return m_selfWeakReference.lock(); -} - -void CAsyncDialogBox::setExecRule(std::string&& s) { - auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(s)); - m_execRuleToken = rule->execToken(); - Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); -} diff --git a/src/helpers/AsyncDialogBox.hpp b/src/helpers/AsyncDialogBox.hpp deleted file mode 100644 index 1bdeba14..00000000 --- a/src/helpers/AsyncDialogBox.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "../macros.hpp" -#include "./memory/Memory.hpp" -#include "./defer/Promise.hpp" - -#include -#include - -#include -#include - -struct wl_event_source; - -class CAsyncDialogBox { - public: - static SP create(const std::string& title, const std::string& description, std::vector buttons); - static bool isAsyncDialogBox(pid_t pid); - static bool isPriorityDialogBox(pid_t pid); - - CAsyncDialogBox(const CAsyncDialogBox&) = delete; - CAsyncDialogBox(CAsyncDialogBox&&) = delete; - CAsyncDialogBox& operator=(const CAsyncDialogBox&) = delete; - CAsyncDialogBox& operator=(CAsyncDialogBox&&) = delete; - - SP> open(); - void kill(); - bool isRunning() const; - pid_t getPID() const; - void setExecRule(std::string&& s); - - SP lockSelf(); - - // focus priority, only permission popups - bool m_priority = false; - - void onWrite(int fd, uint32_t mask); - - private: - CAsyncDialogBox(const std::string& title, const std::string& description, std::vector buttons); - - pid_t m_dialogPid = 0; - wl_event_source* m_readEventSource = nullptr; - Hyprutils::OS::CFileDescriptor m_pipeReadFd; - std::string m_stdout = ""; - std::string m_execRuleToken = ""; - - const std::string m_title; - const std::string m_description; - const std::vector m_buttons; - - SP> m_promiseResolver; - - // WARNING: cyclic reference. This will be removed once the event source is removed to avoid dangling pointers - SP m_selfReference; - WP m_selfWeakReference; -}; diff --git a/src/helpers/CMType.cpp b/src/helpers/CMType.cpp deleted file mode 100644 index db2b7317..00000000 --- a/src/helpers/CMType.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "CMType.hpp" -#include -#include -#include - -static std::unordered_map const table = {{"auto", NCMType::CM_AUTO}, {"srgb", NCMType::CM_SRGB}, {"wide", NCMType::CM_WIDE}, - {"edid", NCMType::CM_EDID}, {"hdr", NCMType::CM_HDR}, {"hdredid", NCMType::CM_HDR_EDID}, - {"dcip3", NCMType::CM_DCIP3}, {"dp3", NCMType::CM_DP3}, {"adobe", NCMType::CM_ADOBE}}; - -std::optional NCMType::fromString(const std::string cmType) { - auto it = table.find(cmType); - if (it == table.end()) - return std::nullopt; - return it->second; -} - -std::string NCMType::toString(eCMType cmType) { - for (const auto& [key, value] : table) { - if (value == cmType) - return key; - } - return ""; -} diff --git a/src/helpers/CMType.hpp b/src/helpers/CMType.hpp deleted file mode 100644 index 8802cca8..00000000 --- a/src/helpers/CMType.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include - -namespace NCMType { - enum eCMType : uint8_t { - CM_AUTO = 0, // subject to change. srgb for 8bpc, wide for 10bpc if supported - CM_SRGB, // default, sRGB primaries - CM_WIDE, // wide color gamut, BT2020 primaries - CM_EDID, // primaries from edid (known to be inaccurate) - CM_HDR, // wide color gamut and HDR PQ transfer function - CM_HDR_EDID, // same as CM_HDR with edid primaries - CM_DCIP3, // movie theatre with greenish white point - CM_DP3, // applle P3 variant with blueish white point - CM_ADOBE, // adobe colorspace - }; - - std::optional fromString(const std::string cmType); - std::string toString(eCMType cmType); -} diff --git a/src/helpers/Color.cpp b/src/helpers/Color.cpp index d8f3b02b..8dfefbbd 100644 --- a/src/helpers/Color.cpp +++ b/src/helpers/Color.cpp @@ -8,11 +8,11 @@ CHyprColor::CHyprColor() = default; CHyprColor::CHyprColor(float r_, float g_, float b_, float a_) : r(r_), g(g_), b(b_), a(a_) { - m_okLab = Hyprgraphics::CColor(Hyprgraphics::CColor::SSRGB{r, g, b}).asOkLab(); + okLab = Hyprgraphics::CColor(Hyprgraphics::CColor::SSRGB{r, g, b}).asOkLab(); } CHyprColor::CHyprColor(uint64_t hex) : r(RED(hex)), g(GREEN(hex)), b(BLUE(hex)), a(ALPHA(hex)) { - m_okLab = Hyprgraphics::CColor(Hyprgraphics::CColor::SSRGB{r, g, b}).asOkLab(); + okLab = Hyprgraphics::CColor(Hyprgraphics::CColor::SSRGB{r, g, b}).asOkLab(); } CHyprColor::CHyprColor(const Hyprgraphics::CColor& color, float a_) : a(a_) { @@ -21,11 +21,11 @@ CHyprColor::CHyprColor(const Hyprgraphics::CColor& color, float a_) : a(a_) { g = SRGB.g; b = SRGB.b; - m_okLab = color.asOkLab(); + okLab = color.asOkLab(); } uint32_t CHyprColor::getAsHex() const { - return sc(a * 255.f) * 0x1000000 + sc(r * 255.f) * 0x10000 + sc(g * 255.f) * 0x100 + sc(b * 255.f) * 0x1; + return (uint32_t)(a * 255.f) * 0x1000000 + (uint32_t)(r * 255.f) * 0x10000 + (uint32_t)(g * 255.f) * 0x100 + (uint32_t)(b * 255.f) * 0x1; } Hyprgraphics::CColor::SSRGB CHyprColor::asRGB() const { @@ -33,11 +33,11 @@ Hyprgraphics::CColor::SSRGB CHyprColor::asRGB() const { } Hyprgraphics::CColor::SOkLab CHyprColor::asOkLab() const { - return m_okLab; + return okLab; } Hyprgraphics::CColor::SHSL CHyprColor::asHSL() const { - return Hyprgraphics::CColor(m_okLab).asHSL(); + return Hyprgraphics::CColor(okLab).asHSL(); } CHyprColor CHyprColor::stripA() const { diff --git a/src/helpers/Color.hpp b/src/helpers/Color.hpp index e72fcd58..8050bebd 100644 --- a/src/helpers/Color.hpp +++ b/src/helpers/Color.hpp @@ -43,7 +43,7 @@ class CHyprColor { double r = 0, g = 0, b = 0, a = 0; private: - Hyprgraphics::CColor::SOkLab m_okLab; // cache for the OkLab representation + Hyprgraphics::CColor::SOkLab okLab; // cache for the OkLab representation }; //NOLINTNEXTLINE diff --git a/src/helpers/CursorShapes.hpp b/src/helpers/CursorShapes.hpp index 3882c593..6f3c8a0e 100644 --- a/src/helpers/CursorShapes.hpp +++ b/src/helpers/CursorShapes.hpp @@ -3,7 +3,7 @@ #include // clang-format off -constexpr std::array CURSOR_SHAPE_NAMES = { +constexpr std::array CURSOR_SHAPE_NAMES = { "invalid", "default", "context-menu", @@ -39,7 +39,5 @@ constexpr std::array CURSOR_SHAPE_NAMES = { "all-scroll", "zoom-in", "zoom-out", - "dnd-ask", - "all-resize" }; -// clang-format on +// clang-format on \ No newline at end of file diff --git a/src/helpers/DamageRing.cpp b/src/helpers/DamageRing.cpp index 82c8894b..093e7ca6 100644 --- a/src/helpers/DamageRing.cpp +++ b/src/helpers/DamageRing.cpp @@ -1,43 +1,43 @@ #include "DamageRing.hpp" void CDamageRing::setSize(const Vector2D& size_) { - if (size_ == m_size) + if (size_ == size) return; - m_size = size_; + size = size_; damageEntire(); } bool CDamageRing::damage(const CRegion& rg) { - CRegion clipped = rg.copy().intersect(CBox{{}, m_size}); + CRegion clipped = rg.copy().intersect(CBox{{}, size}); if (clipped.empty()) return false; - m_current.add(clipped); + current.add(clipped); return true; } void CDamageRing::damageEntire() { - damage(CBox{{}, m_size}); + damage(CBox{{}, size}); } void CDamageRing::rotate() { - m_previousIdx = (m_previousIdx + DAMAGE_RING_PREVIOUS_LEN - 1) % DAMAGE_RING_PREVIOUS_LEN; + previousIdx = (previousIdx + DAMAGE_RING_PREVIOUS_LEN - 1) % DAMAGE_RING_PREVIOUS_LEN; - m_previous[m_previousIdx] = m_current; - m_current.clear(); + previous[previousIdx] = current; + current.clear(); } CRegion CDamageRing::getBufferDamage(int age) { if (age <= 0 || age > DAMAGE_RING_PREVIOUS_LEN + 1) - return CBox{{}, m_size}; + return CBox{{}, size}; - CRegion damage = m_current; + CRegion damage = current; for (int i = 0; i < age - 1; ++i) { - int j = (m_previousIdx + i) % DAMAGE_RING_PREVIOUS_LEN; - damage.add(m_previous.at(j)); + int j = (previousIdx + i) % DAMAGE_RING_PREVIOUS_LEN; + damage.add(previous.at(j)); } // don't return a ludicrous amount of rects @@ -48,5 +48,5 @@ CRegion CDamageRing::getBufferDamage(int age) { } bool CDamageRing::hasChanged() { - return !m_current.empty(); + return !current.empty(); } diff --git a/src/helpers/DamageRing.hpp b/src/helpers/DamageRing.hpp index 4750648b..ae85c453 100644 --- a/src/helpers/DamageRing.hpp +++ b/src/helpers/DamageRing.hpp @@ -3,7 +3,7 @@ #include "./math/Math.hpp" #include -constexpr static int DAMAGE_RING_PREVIOUS_LEN = 3; +constexpr static int DAMAGE_RING_PREVIOUS_LEN = 2; class CDamageRing { public: @@ -15,8 +15,8 @@ class CDamageRing { bool hasChanged(); private: - Vector2D m_size; - CRegion m_current; - std::array m_previous; - size_t m_previousIdx = 0; + Vector2D size; + CRegion current; + std::array previous; + size_t previousIdx = 0; }; diff --git a/src/helpers/Drm.cpp b/src/helpers/Drm.cpp deleted file mode 100644 index 207b5e3d..00000000 --- a/src/helpers/Drm.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include "Drm.hpp" - -bool DRM::sameGpu(int fd1, int fd2) { - drmDevice* devA = nullptr; - drmDevice* devB = nullptr; - - if (drmGetDevice2(fd1, 0, &devA) != 0) - return false; - if (drmGetDevice2(fd2, 0, &devB) != 0) { - drmFreeDevice(&devA); - return false; - } - - bool same = drmDevicesEqual(devA, devB); - - drmFreeDevice(&devA); - drmFreeDevice(&devB); - return same; -} diff --git a/src/helpers/Drm.hpp b/src/helpers/Drm.hpp deleted file mode 100644 index bc56b1ee..00000000 --- a/src/helpers/Drm.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -namespace DRM { - bool sameGpu(int fd1, int fd2); -} diff --git a/src/helpers/Format.cpp b/src/helpers/Format.cpp index 7660934e..6679e869 100644 --- a/src/helpers/Format.cpp +++ b/src/helpers/Format.cpp @@ -1,191 +1,203 @@ #include "Format.hpp" #include #include "../includes.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #include "../macros.hpp" #include #include +/* + DRM formats are LE, while OGL is BE. The two primary formats + will be flipped, so we will set flipRB which will later use swizzle + to flip the red and blue channels. + This will not work on GLES2, but I want to drop support for it one day anyways. +*/ inline const std::vector GLES3_FORMATS = { { - .drmFormat = DRM_FORMAT_ARGB8888, - .glInternalFormat = GL_RGBA8, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = true, - .alphaStripped = DRM_FORMAT_XRGB8888, - .bytesPerBlock = 4, - .swizzle = {SWIZZLE_BGRA}, + .drmFormat = DRM_FORMAT_ARGB8888, + .flipRB = true, +#ifndef GLES2 + .glFormat = GL_RGBA, +#else + .glFormat = GL_BGRA_EXT, +#endif + .glType = GL_UNSIGNED_BYTE, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XRGB8888, + .bytesPerBlock = 4, }, { - .drmFormat = DRM_FORMAT_XRGB8888, - .glInternalFormat = GL_RGBA8, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_XRGB8888, - .bytesPerBlock = 4, - .swizzle = {SWIZZLE_BGR1}, + .drmFormat = DRM_FORMAT_XRGB8888, + .flipRB = true, +#ifndef GLES2 + .glFormat = GL_RGBA, +#else + .glFormat = GL_BGRA_EXT, +#endif + .glType = GL_UNSIGNED_BYTE, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XRGB8888, + .bytesPerBlock = 4, }, { - .drmFormat = DRM_FORMAT_XBGR8888, - .glInternalFormat = GL_RGBA8, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_XBGR8888, - .bytesPerBlock = 4, - .swizzle = {SWIZZLE_RGB1}, + .drmFormat = DRM_FORMAT_XBGR8888, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_BYTE, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XBGR8888, + .bytesPerBlock = 4, }, { - .drmFormat = DRM_FORMAT_ABGR8888, - .glInternalFormat = GL_RGBA8, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = true, - .alphaStripped = DRM_FORMAT_XBGR8888, - .bytesPerBlock = 4, - .swizzle = {SWIZZLE_RGBA}, + .drmFormat = DRM_FORMAT_ABGR8888, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_BYTE, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XBGR8888, + .bytesPerBlock = 4, }, { - .drmFormat = DRM_FORMAT_BGR888, - .glInternalFormat = GL_RGB8, - .glFormat = GL_RGB, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_BGR888, - .bytesPerBlock = 3, - .swizzle = {SWIZZLE_RGB1}, + .drmFormat = DRM_FORMAT_BGR888, + .glFormat = GL_RGB, + .glType = GL_UNSIGNED_BYTE, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_BGR888, + .bytesPerBlock = 3, }, { - .drmFormat = DRM_FORMAT_RGBX4444, - .glInternalFormat = GL_RGBA4, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT_4_4_4_4, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_RGBX4444, - .bytesPerBlock = 2, - .swizzle = {SWIZZLE_RGB1}, + .drmFormat = DRM_FORMAT_RGBX4444, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT_4_4_4_4, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_RGBX4444, + .bytesPerBlock = 2, }, { - .drmFormat = DRM_FORMAT_RGBA4444, - .glInternalFormat = GL_RGBA4, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT_4_4_4_4, - .withAlpha = true, - .alphaStripped = DRM_FORMAT_RGBX4444, - .bytesPerBlock = 2, - .swizzle = {SWIZZLE_RGBA}, + .drmFormat = DRM_FORMAT_RGBA4444, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT_4_4_4_4, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_RGBX4444, + .bytesPerBlock = 2, }, { - .drmFormat = DRM_FORMAT_RGBX5551, - .glInternalFormat = GL_RGB5_A1, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT_5_5_5_1, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_RGBX5551, - .bytesPerBlock = 2, - .swizzle = {SWIZZLE_RGB1}, + .drmFormat = DRM_FORMAT_RGBX5551, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT_5_5_5_1, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_RGBX5551, + .bytesPerBlock = 2, }, { - .drmFormat = DRM_FORMAT_RGBA5551, - .glInternalFormat = GL_RGB5_A1, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT_5_5_5_1, - .withAlpha = true, - .alphaStripped = DRM_FORMAT_RGBX5551, - .bytesPerBlock = 2, - .swizzle = {SWIZZLE_RGBA}, + .drmFormat = DRM_FORMAT_RGBA5551, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT_5_5_5_1, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_RGBX5551, + .bytesPerBlock = 2, }, { - .drmFormat = DRM_FORMAT_RGB565, - .glInternalFormat = GL_RGB565, - .glFormat = GL_RGB, - .glType = GL_UNSIGNED_SHORT_5_6_5, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_RGB565, - .bytesPerBlock = 2, - .swizzle = {SWIZZLE_RGB1}, + .drmFormat = DRM_FORMAT_RGB565, + .glFormat = GL_RGB, + .glType = GL_UNSIGNED_SHORT_5_6_5, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_RGB565, + .bytesPerBlock = 2, }, { - .drmFormat = DRM_FORMAT_XBGR2101010, - .glInternalFormat = GL_RGB10_A2, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_INT_2_10_10_10_REV, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_XBGR2101010, - .bytesPerBlock = 4, - .swizzle = {SWIZZLE_RGB1}, + .drmFormat = DRM_FORMAT_XBGR2101010, + .glFormat = GL_RGBA, +#ifndef GLES2 + .glType = GL_UNSIGNED_INT_2_10_10_10_REV, +#else + .glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, +#endif + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XBGR2101010, + .bytesPerBlock = 4, }, { - .drmFormat = DRM_FORMAT_ABGR2101010, - .glInternalFormat = GL_RGB10_A2, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_INT_2_10_10_10_REV, - .withAlpha = true, - .alphaStripped = DRM_FORMAT_XBGR2101010, - .bytesPerBlock = 4, - .swizzle = {SWIZZLE_RGBA}, + .drmFormat = DRM_FORMAT_ABGR2101010, + .glFormat = GL_RGBA, +#ifndef GLES2 + .glType = GL_UNSIGNED_INT_2_10_10_10_REV, +#else + .glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, +#endif + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XBGR2101010, + .bytesPerBlock = 4, }, { - .drmFormat = DRM_FORMAT_XRGB2101010, - .glInternalFormat = GL_RGB10_A2, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_INT_2_10_10_10_REV, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_XRGB2101010, - .bytesPerBlock = 4, - .swizzle = {SWIZZLE_BGR1}, + .drmFormat = DRM_FORMAT_XRGB2101010, + .glFormat = GL_RGBA, +#ifndef GLES2 + .glType = GL_UNSIGNED_INT_2_10_10_10_REV, +#else + .glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, +#endif + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XRGB2101010, + .bytesPerBlock = 4, }, { - .drmFormat = DRM_FORMAT_ARGB2101010, - .glInternalFormat = GL_RGB10_A2, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_INT_2_10_10_10_REV, - .withAlpha = true, - .alphaStripped = DRM_FORMAT_XRGB2101010, - .bytesPerBlock = 4, - .swizzle = {SWIZZLE_BGRA}, + .drmFormat = DRM_FORMAT_ARGB2101010, + .glFormat = GL_RGBA, +#ifndef GLES2 + .glType = GL_UNSIGNED_INT_2_10_10_10_REV, +#else + .glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, +#endif + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XRGB2101010, + .bytesPerBlock = 4, }, { - .drmFormat = DRM_FORMAT_XBGR16161616F, - .glInternalFormat = GL_RGBA16F, - .glFormat = GL_RGBA, - .glType = GL_HALF_FLOAT, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_XBGR16161616F, - .bytesPerBlock = 8, - .swizzle = {SWIZZLE_RGB1}, + .drmFormat = DRM_FORMAT_XBGR16161616F, + .glFormat = GL_RGBA, +#ifndef GLES2 + .glType = GL_HALF_FLOAT, +#else + .glType = GL_HALF_FLOAT_OES, +#endif + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XBGR16161616F, + .bytesPerBlock = 8, }, { - .drmFormat = DRM_FORMAT_ABGR16161616F, - .glInternalFormat = GL_RGBA16F, - .glFormat = GL_RGBA, - .glType = GL_HALF_FLOAT, - .withAlpha = true, - .alphaStripped = DRM_FORMAT_XBGR16161616F, - .bytesPerBlock = 8, - .swizzle = {SWIZZLE_RGBA}, + .drmFormat = DRM_FORMAT_ABGR16161616F, + .glFormat = GL_RGBA, +#ifndef GLES2 + .glType = GL_HALF_FLOAT, +#else + .glType = GL_HALF_FLOAT_OES, +#endif + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XBGR16161616F, + .bytesPerBlock = 8, }, { - .drmFormat = DRM_FORMAT_XBGR16161616, - .glInternalFormat = GL_RGBA16UI, - .glFormat = GL_RGBA_INTEGER, - .glType = GL_UNSIGNED_SHORT, - .withAlpha = false, - .alphaStripped = DRM_FORMAT_XBGR16161616, - .bytesPerBlock = 8, - .swizzle = {SWIZZLE_RGBA}, + .drmFormat = DRM_FORMAT_XBGR16161616, +#ifndef GLES2 + .glFormat = GL_RGBA16UI, +#else + .glFormat = GL_RGBA16_EXT, +#endif + .glType = GL_UNSIGNED_SHORT, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XBGR16161616, + .bytesPerBlock = 8, }, { - .drmFormat = DRM_FORMAT_ABGR16161616, - .glInternalFormat = GL_RGBA16UI, - .glFormat = GL_RGBA_INTEGER, - .glType = GL_UNSIGNED_SHORT, - .withAlpha = true, - .alphaStripped = DRM_FORMAT_XBGR16161616, - .bytesPerBlock = 8, - .swizzle = {SWIZZLE_RGBA}, + .drmFormat = DRM_FORMAT_ABGR16161616, +#ifndef GLES2 + .glFormat = GL_RGBA16UI, +#else + .glFormat = GL_RGBA16_EXT, +#endif + .glType = GL_UNSIGNED_SHORT, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XBGR16161616, + .bytesPerBlock = 8, }, { .drmFormat = DRM_FORMAT_YVYU, @@ -198,28 +210,24 @@ inline const std::vector GLES3_FORMATS = { .blockSize = {2, 1}, }, { - .drmFormat = DRM_FORMAT_R8, - .glInternalFormat = GL_R8, - .glFormat = GL_RED, - .glType = GL_UNSIGNED_BYTE, - .bytesPerBlock = 1, - .swizzle = {SWIZZLE_R001}, + .drmFormat = DRM_FORMAT_R8, + .bytesPerBlock = 1, }, { - .drmFormat = DRM_FORMAT_GR88, - .glInternalFormat = GL_RG8, - .glFormat = GL_RG, - .glType = GL_UNSIGNED_BYTE, - .bytesPerBlock = 2, - .swizzle = {SWIZZLE_RG01}, + .drmFormat = DRM_FORMAT_GR88, + .bytesPerBlock = 2, }, { - .drmFormat = DRM_FORMAT_RGB888, - .glInternalFormat = GL_RGB8, - .glFormat = GL_RGB, - .glType = GL_UNSIGNED_BYTE, - .bytesPerBlock = 3, - .swizzle = {SWIZZLE_BGR1}, + .drmFormat = DRM_FORMAT_RGB888, + .bytesPerBlock = 3, + }, + { + .drmFormat = DRM_FORMAT_BGR888, + .bytesPerBlock = 3, + }, + { + .drmFormat = DRM_FORMAT_RGBX4444, + .bytesPerBlock = 2, }, }; @@ -254,33 +262,13 @@ const SPixelFormat* NFormatUtils::getPixelFormatFromDRM(DRMFormat drm) { const SPixelFormat* NFormatUtils::getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha) { for (auto const& fmt : GLES3_FORMATS) { - if (fmt.glFormat == sc(glFormat) && fmt.glType == sc(glType) && fmt.withAlpha == alpha) + if (fmt.glFormat == (int)glFormat && fmt.glType == (int)glType && fmt.withAlpha == alpha) return &fmt; } return nullptr; } -bool NFormatUtils::isFormatYUV(uint32_t drmFormat) { - switch (drmFormat) { - case DRM_FORMAT_YUYV: - case DRM_FORMAT_YVYU: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - case DRM_FORMAT_AYUV: - case DRM_FORMAT_NV12: - case DRM_FORMAT_NV21: - case DRM_FORMAT_NV16: - case DRM_FORMAT_NV61: - case DRM_FORMAT_YUV410: - case DRM_FORMAT_YUV411: - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YUV422: - case DRM_FORMAT_YUV444: return true; - default: return false; - } -} - bool NFormatUtils::isFormatOpaque(DRMFormat drm) { const auto FMT = NFormatUtils::getPixelFormatFromDRM(drm); if (!FMT) @@ -297,38 +285,43 @@ int NFormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) { return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt)); } +uint32_t NFormatUtils::drmFormatToGL(DRMFormat drm) { + switch (drm) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case. + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: +#ifdef GLES2 + return GL_RGB10_A2_EXT; +#else + return GL_RGB10_A2; +#endif + default: return GL_RGBA; + } + UNREACHABLE(); + return GL_RGBA; +} + +uint32_t NFormatUtils::glFormatToType(uint32_t gl) { + return gl != GL_RGBA ? +#ifdef GLES2 + GL_UNSIGNED_INT_2_10_10_10_REV_EXT : +#else + GL_UNSIGNED_INT_2_10_10_10_REV : +#endif + GL_UNSIGNED_BYTE; +} + std::string NFormatUtils::drmFormatName(DRMFormat drm) { - auto n = drmGetFormatName(drm); - - if (!n) - return "unknown"; - + auto n = drmGetFormatName(drm); std::string name = n; - free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors) + free(n); return name; } std::string NFormatUtils::drmModifierName(uint64_t mod) { - auto n = drmGetFormatModifierName(mod); - - if (!n) - return "unknown"; - + auto n = drmGetFormatModifierName(mod); std::string name = n; - free(n); // NOLINT(cppcoreguidelines-no-malloc,-warnings-as-errors) + free(n); return name; } - -DRMFormat NFormatUtils::alphaFormat(DRMFormat prevFormat) { - switch (prevFormat) { - case DRM_FORMAT_XRGB8888: return DRM_FORMAT_ARGB8888; - case DRM_FORMAT_XBGR8888: return DRM_FORMAT_ABGR8888; - case DRM_FORMAT_BGRX8888: return DRM_FORMAT_BGRA8888; - case DRM_FORMAT_RGBX8888: return DRM_FORMAT_RGBA8888; - case DRM_FORMAT_XRGB2101010: return DRM_FORMAT_ARGB2101010; - case DRM_FORMAT_XBGR2101010: return DRM_FORMAT_ABGR2101010; - case DRM_FORMAT_RGBX1010102: return DRM_FORMAT_RGBA1010102; - case DRM_FORMAT_BGRX1010102: return DRM_FORMAT_BGRA1010102; - default: return 0; - } -} diff --git a/src/helpers/Format.hpp b/src/helpers/Format.hpp index 02925e22..b1353dc7 100644 --- a/src/helpers/Format.hpp +++ b/src/helpers/Format.hpp @@ -2,46 +2,25 @@ #include #include -#include #include "math/Math.hpp" #include -using DRMFormat = uint32_t; -using SHMFormat = uint32_t; - -#define SWIZZLE_A1GB {GL_ALPHA, GL_ONE, GL_GREEN, GL_BLUE} -#define SWIZZLE_ABG1 {GL_ALPHA, GL_BLUE, GL_GREEN, GL_ONE} -#define SWIZZLE_ABGR {GL_ALPHA, GL_BLUE, GL_GREEN, GL_RED} -#define SWIZZLE_ARGB {GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE} -#define SWIZZLE_B1RG {GL_BLUE, GL_ONE, GL_RED, GL_GREEN} -#define SWIZZLE_BARG {GL_BLUE, GL_ALPHA, GL_RED, GL_GREEN} -#define SWIZZLE_BGR1 {GL_BLUE, GL_GREEN, GL_RED, GL_ONE} -#define SWIZZLE_BGRA {GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA} -#define SWIZZLE_G1AB {GL_GREEN, GL_ONE, GL_ALPHA, GL_BLUE} -#define SWIZZLE_GBA1 {GL_GREEN, GL_BLUE, GL_ALPHA, GL_ONE} -#define SWIZZLE_GBAR {GL_GREEN, GL_BLUE, GL_ALPHA, GL_RED} -#define SWIZZLE_GRAB {GL_GREEN, GL_RED, GL_ALPHA, GL_BLUE} -#define SWIZZLE_R001 {GL_RED, GL_ZERO, GL_ZERO, GL_ONE} -#define SWIZZLE_R1BG {GL_RED, GL_ONE, GL_BLUE, GL_GREEN} -#define SWIZZLE_RABG {GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN} -#define SWIZZLE_RG01 {GL_RED, GL_GREEN, GL_ZERO, GL_ONE} -#define SWIZZLE_GR01 {GL_GREEN, GL_RED, GL_ZERO, GL_ONE} -#define SWIZZLE_RGB1 {GL_RED, GL_GREEN, GL_BLUE, GL_ONE} -#define SWIZZLE_RGBA {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA} +typedef uint32_t DRMFormat; +typedef uint32_t SHMFormat; struct SPixelFormat { - DRMFormat drmFormat = 0; /* DRM_FORMAT_INVALID */ - int glInternalFormat = 0; - int glFormat = 0; - int glType = 0; - bool withAlpha = true; - DRMFormat alphaStripped = 0; /* DRM_FORMAT_INVALID */ - uint32_t bytesPerBlock = 0; - Vector2D blockSize; - std::optional> swizzle = std::nullopt; + DRMFormat drmFormat = 0; /* DRM_FORMAT_INVALID */ + bool flipRB = false; + int glInternalFormat = 0; + int glFormat = 0; + int glType = 0; + bool withAlpha = true; + DRMFormat alphaStripped = 0; /* DRM_FORMAT_INVALID */ + uint32_t bytesPerBlock = 0; + Vector2D blockSize; }; -using SDRMFormat = Aquamarine::SDRMFormat; +typedef Aquamarine::SDRMFormat SDRMFormat; namespace NFormatUtils { SHMFormat drmToShm(DRMFormat drm); @@ -49,11 +28,11 @@ namespace NFormatUtils { const SPixelFormat* getPixelFormatFromDRM(DRMFormat drm); const SPixelFormat* getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha); - bool isFormatYUV(uint32_t drmFormat); bool isFormatOpaque(DRMFormat drm); int pixelsPerBlock(const SPixelFormat* const fmt); int minStride(const SPixelFormat* const fmt, int32_t width); + uint32_t drmFormatToGL(DRMFormat drm); + uint32_t glFormatToType(uint32_t gl); std::string drmFormatName(DRMFormat drm); std::string drmModifierName(uint64_t mod); - DRMFormat alphaFormat(DRMFormat prevFormat); }; diff --git a/src/helpers/MainLoopExecutor.cpp b/src/helpers/MainLoopExecutor.cpp deleted file mode 100644 index c7b5f910..00000000 --- a/src/helpers/MainLoopExecutor.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "MainLoopExecutor.hpp" -#include "../managers/eventLoop/EventLoopManager.hpp" -#include "../macros.hpp" -#include - -static int onDataRead(int fd, uint32_t mask, void* data) { - ((CMainLoopExecutor*)data)->onFired(); - return 0; -} - -CMainLoopExecutor::CMainLoopExecutor(std::function&& callback) : m_fn(std::move(callback)) { - - int fds[2]; - RASSERT(pipe(fds) == 0, "CMainLoopExecutor: failed to open a pipe"); - - m_event = wl_event_loop_add_fd(g_pEventLoopManager->m_wayland.loop, fds[0], WL_EVENT_READABLE, ::onDataRead, this); - - m_readFd = Hyprutils::OS::CFileDescriptor(fds[0]); - m_writeFd = Hyprutils::OS::CFileDescriptor(fds[1]); -} - -CMainLoopExecutor::~CMainLoopExecutor() { - if (m_event) // FIXME: potential race in case of a weird destroy on a worker thread - wl_event_source_remove(m_event); -} - -void CMainLoopExecutor::signal() { - const char* amogus = "h"; - write(m_writeFd.get(), amogus, 1); -} - -void CMainLoopExecutor::onFired() { - if (!m_fn) - return; - - m_fn(); - m_fn = nullptr; - - // we need to remove the event here because we're on the main thread - wl_event_source_remove(m_event); - m_event = nullptr; - - m_readFd.reset(); - m_writeFd.reset(); -} diff --git a/src/helpers/MainLoopExecutor.hpp b/src/helpers/MainLoopExecutor.hpp deleted file mode 100644 index b33148f0..00000000 --- a/src/helpers/MainLoopExecutor.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -class CMainLoopExecutor { - public: - /* - MainLoopExecutor - - Executes a function on the main thread once the writeFd() has some data written to it, - then destroys itself. - - Needs to be kept owned, otherwise will die and kill the fds. - */ - - CMainLoopExecutor(std::function&& callback); - ~CMainLoopExecutor(); - - // Call from your worker thread: signals to the main thread. Destroy afterwards. - void signal(); - - // do not call - void onFired(); - - private: - Hyprutils::OS::CFileDescriptor m_readFd, m_writeFd; - wl_event_source* m_event = nullptr; - std::function m_fn; -}; diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 34b06c2e..9e398fd9 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -3,14 +3,11 @@ #include #include "../Compositor.hpp" #include "../managers/TokenManager.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../desktop/history/WorkspaceHistoryTracker.hpp" #include "Monitor.hpp" #include "../config/ConfigManager.hpp" #include "fs/FsUtils.hpp" #include #include -#include #include #include #include @@ -20,14 +17,11 @@ #include #include #include -#include #ifdef HAS_EXECINFO #include #endif #include #include -#include "../version.h" - using namespace Hyprutils::String; using namespace Hyprutils::OS; @@ -92,7 +86,7 @@ std::string escapeJSONStrings(const std::string& str) { case '\t': oss << "\\t"; break; default: if ('\x00' <= c && c <= '\x1f') { - oss << "\\u" << std::hex << std::setw(4) << std::setfill('0') << sc(c); + oss << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(c); } else { oss << c; } @@ -105,7 +99,7 @@ std::optional getPlusMinusKeywordResult(std::string source, float relativ try { return relative + stof(source); } catch (...) { - Log::logger->log(Log::ERR, "Invalid arg \"{}\" in getPlusMinusKeywordResult!", source); + Debug::log(ERR, "Invalid arg \"{}\" in getPlusMinusKeywordResult!", source); return {}; } } @@ -118,10 +112,6 @@ bool isDirection(const char& arg) { return arg == 'l' || arg == 'r' || arg == 'u' || arg == 'd' || arg == 't' || arg == 'b'; } -static bool isAutoIDdWorkspace(WORKSPACEID id) { - return id < WORKSPACE_INVALID; -} - SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { SWorkspaceIDName result = {WORKSPACE_INVALID, ""}; @@ -132,7 +122,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { const auto NAME = in.substr(8); const auto WS = g_pCompositor->getWorkspaceByName("special:" + NAME); - return {WS ? WS->m_id : g_pCompositor->getNewSpecialID(), "special:" + NAME}; + return {WS ? WS->m_iID : g_pCompositor->getNewSpecialID(), "special:" + NAME}; } result.id = SPECIAL_WORKSPACE_START; @@ -143,27 +133,27 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { if (!WORKSPACE) { result.id = g_pCompositor->getNextAvailableNamedWorkspace(); } else { - result.id = WORKSPACE->m_id; + result.id = WORKSPACE->m_iID; } result.name = WORKSPACENAME; } else if (in.starts_with("empty")) { const bool same_mon = in.substr(5).contains("m"); const bool next = in.substr(5).contains("n"); - if ((same_mon || next) && !Desktop::focusState()->monitor()) { - Log::logger->log(Log::ERR, "Empty monitor workspace on monitor null!"); + if ((same_mon || next) && !g_pCompositor->m_pLastMonitor) { + Debug::log(ERR, "Empty monitor workspace on monitor null!"); return {WORKSPACE_INVALID}; } std::set invalidWSes; if (same_mon) { for (auto const& rule : g_pConfigManager->getAllWorkspaceRules()) { - const auto PMONITOR = g_pCompositor->getMonitorFromString(rule.monitor); - if (PMONITOR && (PMONITOR->m_id != Desktop::focusState()->monitor()->m_id)) + const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor); + if (PMONITOR && (PMONITOR->ID != g_pCompositor->m_pLastMonitor->ID)) invalidWSes.insert(rule.workspaceId); } } - WORKSPACEID id = next ? Desktop::focusState()->monitor()->activeWorkspaceID() : 0; + WORKSPACEID id = next ? g_pCompositor->m_pLastMonitor->activeWorkspaceID() : 0; while (++id < LONG_MAX) { const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(id); if (!invalidWSes.contains(id) && (!PWORKSPACE || PWORKSPACE->getWindows() == 0)) { @@ -172,48 +162,25 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { } } } else if (in.starts_with("prev")) { - if (!Desktop::focusState()->monitor()) + if (!g_pCompositor->m_pLastMonitor) return {WORKSPACE_INVALID}; - const auto PWORKSPACE = Desktop::focusState()->monitor()->m_activeWorkspace; + const auto PWORKSPACE = g_pCompositor->m_pLastMonitor->activeWorkspace; if (!valid(PWORKSPACE)) return {WORKSPACE_INVALID}; - const auto PREVWORKSPACEIDNAME = Desktop::History::workspaceTracker()->previousWorkspaceIDName(PWORKSPACE); + const auto PLASTWORKSPACE = g_pCompositor->getWorkspaceByID(PWORKSPACE->getPrevWorkspaceIDName().id); - if (PREVWORKSPACEIDNAME.id == -1) + if (!PLASTWORKSPACE) return {WORKSPACE_INVALID}; - const auto PLASTWORKSPACE = g_pCompositor->getWorkspaceByID(PREVWORKSPACEIDNAME.id); - - if (!PLASTWORKSPACE) { - Log::logger->log(Log::DEBUG, "previous workspace {} doesn't exist yet", PREVWORKSPACEIDNAME.id); - return {PREVWORKSPACEIDNAME.id, PREVWORKSPACEIDNAME.name}; - } - - return {PLASTWORKSPACE->m_id, PLASTWORKSPACE->m_name}; - } else if (in == "next") { - if (!Desktop::focusState()->monitor() || !Desktop::focusState()->monitor()->m_activeWorkspace) { - Log::logger->log(Log::ERR, "no active monitor or workspace for 'next'"); - return {WORKSPACE_INVALID}; - } - - auto PCURRENTWORKSPACE = Desktop::focusState()->monitor()->m_activeWorkspace; - - WORKSPACEID nextId = PCURRENTWORKSPACE->m_id + 1; - - if (nextId <= 0) - return {WORKSPACE_INVALID}; - - result.id = nextId; - result.name = std::to_string(nextId); - return result; + return {PLASTWORKSPACE->m_iID, PLASTWORKSPACE->m_szName}; } else { if (in[0] == 'r' && (in[1] == '-' || in[1] == '+' || in[1] == '~') && isNumber(in.substr(2))) { bool absolute = in[1] == '~'; - if (!Desktop::focusState()->monitor()) { - Log::logger->log(Log::ERR, "Relative monitor workspace on monitor null!"); + if (!g_pCompositor->m_pLastMonitor) { + Debug::log(ERR, "Relative monitor workspace on monitor null!"); return {WORKSPACE_INVALID}; } @@ -222,22 +189,22 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { if (!PLUSMINUSRESULT.has_value()) return {WORKSPACE_INVALID}; - result.id = sc(PLUSMINUSRESULT.value()); + result.id = (int)PLUSMINUSRESULT.value(); WORKSPACEID remains = result.id; std::set invalidWSes; // Collect all the workspaces we can't jump to. - for (auto const& ws : g_pCompositor->getWorkspaces()) { - if (ws->m_isSpecialWorkspace || (ws->m_monitor != Desktop::focusState()->monitor())) { + for (auto const& ws : g_pCompositor->m_vWorkspaces) { + if (ws->m_bIsSpecialWorkspace || (ws->m_pMonitor != g_pCompositor->m_pLastMonitor)) { // Can't jump to this workspace - invalidWSes.insert(ws->m_id); + invalidWSes.insert(ws->m_iID); } } for (auto const& rule : g_pConfigManager->getAllWorkspaceRules()) { - const auto PMONITOR = g_pCompositor->getMonitorFromString(rule.monitor); - if (!PMONITOR || PMONITOR->m_id == Desktop::focusState()->monitor()->m_id) { + const auto PMONITOR = g_pCompositor->getMonitorFromName(rule.monitor); + if (!PMONITOR || PMONITOR->ID == g_pCompositor->m_pLastMonitor->ID) { // Can't be invalid continue; } @@ -247,20 +214,20 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { // Prepare all named workspaces in case when we need them std::vector namedWSes; - for (auto const& ws : g_pCompositor->getWorkspaces()) { - if (ws->m_isSpecialWorkspace || (ws->m_monitor != Desktop::focusState()->monitor()) || ws->m_id >= 0) + for (auto const& ws : g_pCompositor->m_vWorkspaces) { + if (ws->m_bIsSpecialWorkspace || (ws->m_pMonitor != g_pCompositor->m_pLastMonitor) || ws->m_iID >= 0) continue; - namedWSes.push_back(ws->m_id); + namedWSes.push_back(ws->m_iID); } - std::ranges::sort(namedWSes); + std::sort(namedWSes.begin(), namedWSes.end()); if (absolute) { // 1-index remains -= 1; // traverse valid workspaces until we reach the remains - if (sc(remains) < namedWSes.size()) { + if ((size_t)remains < namedWSes.size()) { result.id = namedWSes[remains]; } else { remains -= namedWSes.size(); @@ -275,13 +242,13 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { } else { // Just take a blind guess at where we'll probably end up - WORKSPACEID activeWSID = Desktop::focusState()->monitor()->m_activeWorkspace ? Desktop::focusState()->monitor()->m_activeWorkspace->m_id : 1; + WORKSPACEID activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1; WORKSPACEID predictedWSID = activeWSID + remains; int remainingWSes = 0; char walkDir = in[1]; // sanitize. 0 means invalid oob in - - predictedWSID = std::max(predictedWSID, sc(0)); + predictedWSID = std::max(predictedWSID, static_cast(0)); // Count how many invalidWSes are in between (how bad the prediction was) WORKSPACEID beginID = in[1] == '+' ? activeWSID + 1 : predictedWSID; @@ -304,7 +271,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { } currentItem += remains; - currentItem = std::max(currentItem, sc(0)); + currentItem = std::max(currentItem, static_cast(0)); if (currentItem >= namedWSes.size()) { // At the seam between namedWSes and normal WSes. Behave like r+[diff] at imaginary ws 0 size_t diff = currentItem - (namedWSes.size() - 1); @@ -336,12 +303,12 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { finalWSID = curID; } if (finalWSID <= 0 || invalidWSes.contains(finalWSID)) { - if (!namedWSes.empty()) { + if (namedWSes.size()) { // Go to the named workspaces // Need remainingWSes more auto namedWSIdx = namedWSes.size() - remainingWSes; // Sanitze - namedWSIdx = std::clamp(namedWSIdx, sc(0), namedWSes.size() - sc(1)); + namedWSIdx = std::clamp(namedWSIdx, static_cast(0), namedWSes.size() - static_cast(1)); finalWSID = namedWSes[namedWSIdx]; } else { // Couldn't find valid workspace in negative direction, search last first one back up positive direction @@ -366,7 +333,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(result.id); if (PWORKSPACE) - result.name = g_pCompositor->getWorkspaceByID(result.id)->m_name; + result.name = g_pCompositor->getWorkspaceByID(result.id)->m_szName; else result.name = std::to_string(result.id); @@ -374,8 +341,8 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { bool onAllMonitors = in[0] == 'e'; bool absolute = in[1] == '~'; - if (!Desktop::focusState()->monitor()) { - Log::logger->log(Log::ERR, "Relative monitor workspace on monitor null!"); + if (!g_pCompositor->m_pLastMonitor) { + Debug::log(ERR, "Relative monitor workspace on monitor null!"); return {WORKSPACE_INVALID}; } @@ -385,20 +352,20 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { if (!PLUSMINUSRESULT.has_value()) return {WORKSPACE_INVALID}; - result.id = sc(PLUSMINUSRESULT.value()); + result.id = (int)PLUSMINUSRESULT.value(); // result now has +/- what we should move on mon - int remains = sc(result.id); + int remains = (int)result.id; std::vector validWSes; - for (auto const& ws : g_pCompositor->getWorkspaces()) { - if (ws->m_isSpecialWorkspace || (ws->m_monitor != Desktop::focusState()->monitor() && !onAllMonitors)) + for (auto const& ws : g_pCompositor->m_vWorkspaces) { + if (ws->m_bIsSpecialWorkspace || (ws->m_pMonitor != g_pCompositor->m_pLastMonitor && !onAllMonitors)) continue; - validWSes.push_back(ws->m_id); + validWSes.push_back(ws->m_iID); } - std::ranges::sort(validWSes); + std::sort(validWSes.begin(), validWSes.end()); ssize_t currentItem = -1; @@ -409,7 +376,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { // clamp if (currentItem < 0) { currentItem = 0; - } else if (currentItem >= sc(validWSes.size())) { + } else if (currentItem >= (ssize_t)validWSes.size()) { currentItem = validWSes.size() - 1; } } else { @@ -417,8 +384,8 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { remains = remains < 0 ? -((-remains) % validWSes.size()) : remains % validWSes.size(); // get the current item - WORKSPACEID activeWSID = Desktop::focusState()->monitor()->m_activeWorkspace ? Desktop::focusState()->monitor()->m_activeWorkspace->m_id : 1; - for (ssize_t i = 0; i < sc(validWSes.size()); i++) { + WORKSPACEID activeWSID = g_pCompositor->m_pLastMonitor->activeWorkspace ? g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID : 1; + for (ssize_t i = 0; i < (ssize_t)validWSes.size(); i++) { if (validWSes[i] == activeWSID) { currentItem = i; break; @@ -429,7 +396,7 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { currentItem += remains; // sanitize - if (currentItem >= sc(validWSes.size())) { + if (currentItem >= (ssize_t)validWSes.size()) { currentItem = currentItem % validWSes.size(); } else if (currentItem < 0) { currentItem = validWSes.size() + currentItem; @@ -437,17 +404,17 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { } result.id = validWSes[currentItem]; - result.name = g_pCompositor->getWorkspaceByID(validWSes[currentItem])->m_name; + result.name = g_pCompositor->getWorkspaceByID(validWSes[currentItem])->m_szName; } else { if (in[0] == '+' || in[0] == '-') { - if (Desktop::focusState()->monitor()) { - const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in, Desktop::focusState()->monitor()->activeWorkspaceID()); + if (g_pCompositor->m_pLastMonitor) { + const auto PLUSMINUSRESULT = getPlusMinusKeywordResult(in, g_pCompositor->m_pLastMonitor->activeWorkspaceID()); if (!PLUSMINUSRESULT.has_value()) return {WORKSPACE_INVALID}; - result.id = std::max(sc(PLUSMINUSRESULT.value()), 1); + result.id = std::max((int)PLUSMINUSRESULT.value(), 1); } else { - Log::logger->log(Log::ERR, "Relative workspace on no mon!"); + Debug::log(ERR, "Relative workspace on no mon!"); return {WORKSPACE_INVALID}; } } else if (isNumber(in)) @@ -456,15 +423,13 @@ SWorkspaceIDName getWorkspaceIDNameFromString(const std::string& in) { // maybe name const auto PWORKSPACE = g_pCompositor->getWorkspaceByName(in); if (PWORKSPACE) - result.id = PWORKSPACE->m_id; + result.id = PWORKSPACE->m_iID; } result.name = std::to_string(result.id); } } - result.isAutoIDd = isAutoIDdWorkspace(result.id); - return result; } @@ -526,12 +491,12 @@ void logSystemInfo() { uname(&unameInfo); - Log::logger->log(Log::DEBUG, "System name: {}", std::string{unameInfo.sysname}); - Log::logger->log(Log::DEBUG, "Node name: {}", std::string{unameInfo.nodename}); - Log::logger->log(Log::DEBUG, "Release: {}", std::string{unameInfo.release}); - Log::logger->log(Log::DEBUG, "Version: {}", std::string{unameInfo.version}); + Debug::log(LOG, "System name: {}", std::string{unameInfo.sysname}); + Debug::log(LOG, "Node name: {}", std::string{unameInfo.nodename}); + Debug::log(LOG, "Release: {}", std::string{unameInfo.release}); + Debug::log(LOG, "Version: {}", std::string{unameInfo.version}); - Log::logger->log(Log::DEBUG, "\n"); + Debug::log(NONE, "\n"); #if defined(__DragonFly__) || defined(__FreeBSD__) const std::string GPUINFO = execAndGet("pciconf -lv | grep -F -A4 vga"); @@ -557,16 +522,16 @@ void logSystemInfo() { #else const std::string GPUINFO = execAndGet("lspci -vnn | grep -E '(VGA|Display|3D)'"); #endif - Log::logger->log(Log::DEBUG, "GPU information:\n{}\n", GPUINFO); + Debug::log(LOG, "GPU information:\n{}\n", GPUINFO); if (GPUINFO.contains("NVIDIA")) { - Log::logger->log(Log::WARN, "Warning: you're using an NVIDIA GPU. Make sure you follow the instructions on the wiki if anything is amiss.\n"); + Debug::log(WARN, "Warning: you're using an NVIDIA GPU. Make sure you follow the instructions on the wiki if anything is amiss.\n"); } // log etc - Log::logger->log(Log::DEBUG, "os-release:"); + Debug::log(LOG, "os-release:"); - Log::logger->log(Log::DEBUG, "{}", NFsUtils::readFileAsString("/etc/os-release").value_or("error")); + Debug::log(NONE, "{}", NFsUtils::readFileAsString("/etc/os-release").value_or("error")); } int64_t getPPIDof(int64_t pid) { @@ -610,7 +575,7 @@ int64_t getPPIDof(int64_t pid) { fclose(infile); if (line) - free(line); // NOLINT(cppcoreguidelines-no-malloc) + free(line); try { return std::stoll(pidstr); @@ -635,7 +600,7 @@ std::expected configStringToInt(const std::string& VALUE) const auto VALUEWITHOUTFUNC = trim(VALUE.substr(5, VALUE.length() - 6)); // try doing it the comma way first - if (std::ranges::count(VALUEWITHOUTFUNC, ',') == 3) { + if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 3) { // cool std::string rolling = VALUEWITHOUTFUNC; auto r = configStringToInt(trim(rolling.substr(0, rolling.find(',')))); @@ -653,7 +618,7 @@ std::expected configStringToInt(const std::string& VALUE) a = std::round(std::stof(trim(rolling.substr(0, rolling.find(',')))) * 255.f); } catch (std::exception& e) { return std::unexpected("failed parsing " + VALUEWITHOUTFUNC); } - return a * sc(0x1000000) + *r * sc(0x10000) + *g * sc(0x100) + *b; + return a * (Hyprlang::INT)0x1000000 + *r * (Hyprlang::INT)0x10000 + *g * (Hyprlang::INT)0x100 + *b; } else if (VALUEWITHOUTFUNC.length() == 8) { const auto RGBA = parseHex(VALUEWITHOUTFUNC); @@ -669,7 +634,7 @@ std::expected configStringToInt(const std::string& VALUE) const auto VALUEWITHOUTFUNC = trim(VALUE.substr(4, VALUE.length() - 5)); // try doing it the comma way first - if (std::ranges::count(VALUEWITHOUTFUNC, ',') == 2) { + if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 2) { // cool std::string rolling = VALUEWITHOUTFUNC; auto r = configStringToInt(trim(rolling.substr(0, rolling.find(',')))); @@ -681,7 +646,7 @@ std::expected configStringToInt(const std::string& VALUE) if (!r || !g || !b) return std::unexpected("failed parsing " + VALUEWITHOUTFUNC); - return sc(0xFF000000) + *r * sc(0x10000) + *g * sc(0x100) + *b; + return (Hyprlang::INT)0xFF000000 + *r * (Hyprlang::INT)0x10000 + *g * (Hyprlang::INT)0x100 + *b; } else if (VALUEWITHOUTFUNC.length() == 6) { auto r = parseHex(VALUEWITHOUTFUNC); return r ? *r + 0xFF000000 : r; @@ -728,7 +693,7 @@ Vector2D configStringToVector2D(const std::string& VALUE) { if (std::getline(iss, token)) throw std::invalid_argument("Invalid string format"); - return Vector2D(sc(x), sc(y)); + return Vector2D((double)x, (double)y); } double normalizeAngleRad(double ang) { @@ -769,10 +734,17 @@ std::vector getBacktrace() { } void throwError(const std::string& err) { - Log::logger->log(Log::CRIT, "Critical error thrown: {}", err); + Debug::log(CRIT, "Critical error thrown: {}", err); throw std::runtime_error(err); } +bool envEnabled(const std::string& env) { + const auto ENV = getenv(env.c_str()); + if (!ENV) + return false; + return std::string(ENV) == "1"; +} + std::pair openExclusiveShm() { // Only absolute paths can be shared across different shm_open() calls std::string name = "/" + g_pTokenManager->getRandomUUID(); @@ -845,139 +817,3 @@ float stringToPercentage(const std::string& VALUE, const float REL) { else return std::stof(VALUE); } - -// Checks if Nvidia driver major version is at least given version. -// Useful for explicit_sync_kms and ctm_animation as they only work -// past certain driver versions. -bool isNvidiaDriverVersionAtLeast(int threshold) { - static int driverMajor = 0; - static bool once = true; - - if (once) { - once = false; - - std::error_code ec; - if (std::filesystem::exists("/sys/module/nvidia_drm/version", ec) && !ec) { - std::ifstream ifs("/sys/module/nvidia_drm/version"); - if (ifs.good()) { - try { - std::string driverInfo((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); - - size_t firstDot = driverInfo.find('.'); - if (firstDot != std::string::npos) - driverMajor = std::stoi(driverInfo.substr(0, firstDot)); - - Log::logger->log(Log::DEBUG, "Parsed NVIDIA major version: {}", driverMajor); - - } catch (std::exception& e) { - driverMajor = 0; // Default to 0 if parsing fails - } - - ifs.close(); - } - } - } - - return driverMajor >= threshold; -} - -std::expected binaryNameForWlClient(wl_client* client) { - if (!client) - return std::unexpected("client unknown"); - - pid_t pid = 0; - wl_client_get_credentials(client, &pid, nullptr, nullptr); - - return binaryNameForPid(pid); -} - -std::expected binaryNameForPid(pid_t pid) { - if (pid <= 0) - return std::unexpected("No pid for client"); - -#if defined(KERN_PROC_PATHNAME) - int mib[] = { - CTL_KERN, -#if defined(__NetBSD__) - KERN_PROC_ARGS, - pid, - KERN_PROC_PATHNAME, -#else - KERN_PROC, - KERN_PROC_PATHNAME, - pid, -#endif - }; - u_int miblen = sizeof(mib) / sizeof(mib[0]); - char exe[PATH_MAX] = "/nonexistent"; - size_t sz = sizeof(exe); - sysctl(mib, miblen, &exe, &sz, NULL, 0); - std::string path = exe; -#else - std::string path = std::format("/proc/{}/exe", sc(pid)); -#endif - std::error_code ec; - - std::string fullPath = std::filesystem::canonical(path, ec); - - if (ec) - return std::unexpected("canonical failed"); - - return fullPath; -} - -std::string deviceNameToInternalString(std::string in) { - std::ranges::replace(in, ' ', '-'); - std::ranges::replace(in, '\n', '-'); - std::ranges::replace(in, ',', '-'); - std::ranges::transform(in, in.begin(), ::tolower); - return in; -} - -static const std::vector PKGCONF_PATHS = {"/usr/lib/pkgconfig", "/usr/local/lib/pkgconfig"}; - -// -std::string getSystemLibraryVersion(const std::string& name) { - for (const auto& pkgconf : PKGCONF_PATHS) { - std::error_code ec; - const std::string PATH = std::string{pkgconf} + "/" + name + ".pc"; - if (!std::filesystem::exists(PATH, ec)) - continue; - - const auto DATA = NFsUtils::readFileAsString(PATH); - - if (!DATA) - continue; - - size_t versionAt = DATA->find("\nVersion: "); - size_t versionAtEnd = DATA->find("\n", versionAt + 11); - - if (versionAt == std::string::npos) - continue; - - versionAt += 10; - - return DATA->substr(versionAt, versionAtEnd == std::string::npos ? std::string::npos : versionAtEnd - versionAt); - } - return "unknown"; -} - -std::string getBuiltSystemLibraryNames() { - std::string result = "Libraries:\n"; - result += std::format("Hyprgraphics: built against {}, system has {}\n", HYPRGRAPHICS_VERSION, getSystemLibraryVersion("hyprgraphics")); - result += std::format("Hyprutils: built against {}, system has {}\n", HYPRUTILS_VERSION, getSystemLibraryVersion("hyprutils")); - result += std::format("Hyprcursor: built against {}, system has {}\n", HYPRCURSOR_VERSION, getSystemLibraryVersion("hyprcursor")); - result += std::format("Hyprlang: built against {}, system has {}\n", HYPRLANG_VERSION, getSystemLibraryVersion("hyprlang")); - result += std::format("Aquamarine: built against {}, system has {}\n", AQUAMARINE_VERSION, getSystemLibraryVersion("aquamarine")); - return result; -} - -bool truthy(const std::string& str) { - if (str == "1") - return true; - - std::string cpy = str; - std::ranges::transform(cpy, cpy.begin(), ::tolower); - - return cpy.starts_with("true") || cpy.starts_with("yes") || cpy.starts_with("on"); -} diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp index 437cfcb4..bd288638 100644 --- a/src/helpers/MiscFunctions.hpp +++ b/src/helpers/MiscFunctions.hpp @@ -17,35 +17,28 @@ struct SCallstackFrameInfo { struct SWorkspaceIDName { WORKSPACEID id = WORKSPACE_INVALID; std::string name; - bool isAutoIDd = false; }; -std::string absolutePath(const std::string&, const std::string&); -std::string escapeJSONStrings(const std::string& str); -bool isDirection(const std::string&); -bool isDirection(const char&); -SWorkspaceIDName getWorkspaceIDNameFromString(const std::string&); -std::optional cleanCmdForWorkspace(const std::string&, std::string); -float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2); -void logSystemInfo(); -std::string execAndGet(const char*); -int64_t getPPIDof(int64_t pid); -std::expected configStringToInt(const std::string&); -Vector2D configStringToVector2D(const std::string&); -std::optional getPlusMinusKeywordResult(std::string in, float relative); -double normalizeAngleRad(double ang); -std::vector getBacktrace(); -void throwError(const std::string& err); -Hyprutils::OS::CFileDescriptor allocateSHMFile(size_t len); -bool allocateSHMFilePair(size_t size, Hyprutils::OS::CFileDescriptor& rw_fd_ptr, Hyprutils::OS::CFileDescriptor& ro_fd_ptr); -float stringToPercentage(const std::string& VALUE, const float REL); -bool isNvidiaDriverVersionAtLeast(int threshold); -std::expected binaryNameForWlClient(wl_client* client); -std::expected binaryNameForPid(pid_t pid); -std::string deviceNameToInternalString(std::string in); -std::string getSystemLibraryVersion(const std::string& name); -std::string getBuiltSystemLibraryNames(); -bool truthy(const std::string& str); +std::string absolutePath(const std::string&, const std::string&); +std::string escapeJSONStrings(const std::string& str); +bool isDirection(const std::string&); +bool isDirection(const char&); +SWorkspaceIDName getWorkspaceIDNameFromString(const std::string&); +std::optional cleanCmdForWorkspace(const std::string&, std::string); +float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2); +void logSystemInfo(); +std::string execAndGet(const char*); +int64_t getPPIDof(int64_t pid); +std::expected configStringToInt(const std::string&); +Vector2D configStringToVector2D(const std::string&); +std::optional getPlusMinusKeywordResult(std::string in, float relative); +double normalizeAngleRad(double ang); +std::vector getBacktrace(); +void throwError(const std::string& err); +bool envEnabled(const std::string& env); +Hyprutils::OS::CFileDescriptor allocateSHMFile(size_t len); +bool allocateSHMFilePair(size_t size, Hyprutils::OS::CFileDescriptor& rw_fd_ptr, Hyprutils::OS::CFileDescriptor& ro_fd_ptr); +float stringToPercentage(const std::string& VALUE, const float REL); template [[deprecated("use std::format instead")]] std::string getFormat(std::format_string fmt, Args&&... args) { diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 07156ff1..75c0d8b9 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1,13 +1,11 @@ #include "Monitor.hpp" #include "MiscFunctions.hpp" #include "../macros.hpp" -#include "SharedDefs.hpp" -#include "../helpers/TransferFunction.hpp" #include "math/Math.hpp" #include "../protocols/ColorManagement.hpp" +#include "sync/SyncReleaser.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" -#include "../config/ConfigManager.hpp" #include "../protocols/GammaControl.hpp" #include "../devices/ITouch.hpp" #include "../protocols/LayerShell.hpp" @@ -20,238 +18,147 @@ #include "../managers/PointerManager.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" #include "../protocols/core/Compositor.hpp" -#include "../protocols/core/DataDevice.hpp" #include "../render/Renderer.hpp" #include "../managers/EventManager.hpp" -#include "../managers/screenshare/ScreenshareManager.hpp" -#include "../managers/animation/AnimationManager.hpp" -#include "../managers/animation/DesktopAnimationManager.hpp" +#include "../managers/LayoutManager.hpp" #include "../managers/input/InputManager.hpp" -#include "../hyprerror/HyprError.hpp" -#include "../layout/LayoutManager.hpp" -#include "../i18n/Engine.hpp" -#include "../helpers/cm/ColorManagement.hpp" #include "sync/SyncTimeline.hpp" -#include "time/Time.hpp" -#include "../desktop/view/LayerSurface.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../event/EventBus.hpp" -#include "Drm.hpp" +#include "../desktop/LayerSurface.hpp" #include -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #include "debug/HyprNotificationOverlay.hpp" -#include "MonitorFrameScheduler.hpp" #include #include #include -#include #include -#include -#include - using namespace Hyprutils::String; using namespace Hyprutils::Utils; using namespace Hyprutils::OS; using enum NContentType::eContentType; -using namespace NColorManagement; -CMonitor::CMonitor(SP output_) : m_state(this), m_output(output_), m_imageDescription(DEFAULT_IMAGE_DESCRIPTION) { - g_pAnimationManager->createAnimation(0.f, m_specialFade, g_pConfigManager->getAnimationPropertyConfig("specialWorkspaceIn"), AVARDAMAGE_NONE); - m_specialFade->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); - static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); - g_pAnimationManager->createAnimation(*PZOOMFACTOR, m_cursorZoom, g_pConfigManager->getAnimationPropertyConfig("zoomFactor"), AVARDAMAGE_NONE); - m_cursorZoom->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); - g_pAnimationManager->createAnimation(0.F, m_zoomAnimProgress, g_pConfigManager->getAnimationPropertyConfig("monitorAdded"), AVARDAMAGE_NONE); - m_zoomAnimProgress->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); - g_pAnimationManager->createAnimation(0.F, m_backgroundOpacity, g_pConfigManager->getAnimationPropertyConfig("monitorAdded"), AVARDAMAGE_NONE); - m_backgroundOpacity->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); - g_pAnimationManager->createAnimation(0.F, m_dpmsBlackOpacity, g_pConfigManager->getAnimationPropertyConfig("fadeDpms"), AVARDAMAGE_NONE); - m_dpmsBlackOpacity->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); }); +static int ratHandler(void* data) { + g_pHyprRenderer->renderMonitor(((CMonitor*)data)->self.lock()); + + return 1; +} + +CMonitor::CMonitor(SP output_) : state(this), output(output_) { + ; } CMonitor::~CMonitor() { - m_events.destroy.emit(); - if (g_pHyprOpenGL) - g_pHyprOpenGL->destroyMonitorResources(m_self); + events.destroy.emit(); } void CMonitor::onConnect(bool noRule) { - Event::bus()->m_events.monitor.preAdded.emit(m_self.lock()); + EMIT_HOOK_EVENT("preMonitorAdded", self.lock()); CScopeGuard x = {[]() { g_pCompositor->arrangeMonitors(); }}; - m_zoomAnimProgress->setValueAndWarp(0.F); - m_zoomAnimFrameCounter = 0; + g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); }); - g_pEventLoopManager->doLater([] { - g_pConfigManager->ensurePersistentWorkspacesPresent(); - g_pCompositor->ensureWorkspacesOnAssignedMonitors(); - }); + if (output->supportsExplicit) { + inTimeline = CSyncTimeline::create(output->getBackend()->drmFD()); + } - m_listeners.frame = m_output->events.frame.listen([this] { - if (m_frameScheduler) - m_frameScheduler->onFrame(); - }); - m_listeners.commit = m_output->events.commit.listen([this] { - m_events.commit.emit(); - - // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER - if (true && Screenshare::mgr()) - Screenshare::mgr()->onOutputCommit(m_self.lock()); - }); - m_listeners.needsFrame = m_output->events.needsFrame.listen([this] { g_pCompositor->scheduleFrameForMonitor(m_self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); }); - - m_listeners.presented = m_output->events.present.listen([this](const Aquamarine::IOutput::SPresentEvent& event) { - if (m_pendingDpmsAnimation) { - m_pendingDpmsAnimationCounter++; - // we give ourselves 5 frames of a buffer. The first presentation event still doesn't usually say that we actually - // are scanning out to the CRTC, and it could still be modesetting. - // this is not ideal (some CRTCs will just eat frames) but it's better than nothing - - m_dpmsBlackOpacity->setValueAndWarp(1.F); - - if (m_pendingDpmsAnimationCounter == 5) { - *m_dpmsBlackOpacity = 0.F; - m_pendingDpmsAnimation = false; - } + listeners.frame = output->events.frame.registerListener([this](std::any d) { onMonitorFrame(); }); + listeners.commit = output->events.commit.registerListener([this](std::any d) { + if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER + PROTO::screencopy->onOutputCommit(self.lock()); + PROTO::toplevelExport->onOutputCommit(self.lock()); } + }); + listeners.needsFrame = + output->events.needsFrame.registerListener([this](std::any d) { g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); }); - timespec* ts = event.when; - - if (ts && ts->tv_sec <= 2) { - // drop this timestamp, it's not valid. Likely drm is cringe. We can't push it further because - // a) it's wrong, b) our translations aren't 100% accurate and risk underflows - ts = nullptr; - } - - if (!ts) { - timespec mono{}; - clock_gettime(CLOCK_MONOTONIC, &mono); - PROTO::presentation->onPresented(m_self.lock(), mono, event.refresh, event.seq, event.flags & ~Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK); - } else - PROTO::presentation->onPresented(m_self.lock(), *ts, event.refresh, event.seq, event.flags); - - if (m_zoomAnimFrameCounter < 5) { - m_zoomAnimFrameCounter++; - - // we give ourselves 5 frames of a buffer. The first presentation event still doesn't usually say that we actually - // are scanning out to the CRTC, and it could still be modesetting. - // this is not ideal (some CRTCs will just eat frames) but it's better than nothing - m_zoomAnimProgress->setValueAndWarp(0.F); - if (m_zoomAnimFrameCounter == 5) { - // start the animation for realzies - *m_zoomAnimProgress = 1.F; - } - - // damage the entire display to force a frame immediately - g_pEventLoopManager->doLater([self = m_self] { - if (!self) - return; - - g_pHyprRenderer->damageMonitor(self.lock()); - }); - } - - m_frameScheduler->onPresented(); - - m_events.presented.emit(); + listeners.presented = output->events.present.registerListener([this](std::any d) { + auto E = std::any_cast(d); + PROTO::presentation->onPresented(self.lock(), E.when, E.refresh, E.seq, E.flags); }); - m_listeners.destroy = m_output->events.destroy.listen([this] { - Log::logger->log(Log::DEBUG, "Destroy called for monitor {}", m_name); + listeners.destroy = output->events.destroy.registerListener([this](std::any d) { + Debug::log(LOG, "Destroy called for monitor {}", szName); onDisconnect(true); - m_output = nullptr; - m_renderingInitPassed = false; + output = nullptr; + m_bRenderingInitPassed = false; - Log::logger->log(Log::DEBUG, "Removing monitor {} from realMonitors", m_name); + Debug::log(LOG, "Removing monitor {} from realMonitors", szName); - std::erase_if(g_pCompositor->m_realMonitors, [&](PHLMONITOR& el) { return el.get() == this; }); + std::erase_if(g_pCompositor->m_vRealMonitors, [&](PHLMONITOR& el) { return el.get() == this; }); }); - m_listeners.state = m_output->events.state.listen([this](const Aquamarine::IOutput::SStateEvent& event) { - if (event.size == Vector2D{}) { + listeners.state = output->events.state.registerListener([this](std::any d) { + auto E = std::any_cast(d); + + if (E.size == Vector2D{}) { // an indication to re-set state // we can't do much for createdByUser displays I think - if (m_createdByUser) + if (createdByUser) return; - Log::logger->log(Log::DEBUG, "Reapplying monitor rule for {} from a state request", m_name); - applyMonitorRule(&m_activeMonitorRule, true); + Debug::log(LOG, "Reapplying monitor rule for {} from a state request", szName); + applyMonitorRule(&activeMonitorRule, true); return; } - if (!m_createdByUser) + if (!createdByUser) return; - const auto SIZE = event.size; + const auto SIZE = E.size; - m_forceSize = SIZE; + forceSize = SIZE; - SMonitorRule rule = m_activeMonitorRule; - - if (SIZE == rule.resolution) - return; - - rule.resolution = SIZE; + SMonitorRule rule = activeMonitorRule; + rule.resolution = SIZE; applyMonitorRule(&rule); }); - m_frameScheduler = makeUnique(m_self.lock()); - m_frameScheduler->m_self = WP(m_frameScheduler); + tearingState.canTear = output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM; - m_tearingState.canTear = m_output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM; - - m_name = m_output->name; - - m_description = m_output->description; - // remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description - std::erase(m_description, ','); - - // field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix - m_shortDescription = trim(std::format("{} {} {}", m_output->make, m_output->model, m_output->serial)); - std::erase(m_shortDescription, ','); - - if (m_output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM) - m_createdByUser = true; // should be true. WL and Headless backends should be addable / removable - - // get monitor rule that matches - SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(m_self.lock()); - - if (m_enabled && !monitorRule.disabled) { - applyMonitorRule(&monitorRule, m_pixelSize == Vector2D{}); - - m_output->state->resetExplicitFences(); - m_output->state->setEnabled(true); - m_state.commit(); + if (m_bEnabled) { + output->state->resetExplicitFences(); + output->state->setEnabled(true); + state.commit(); return; } + szName = output->name; + + szDescription = output->description; + // remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description + std::erase(szDescription, ','); + + // field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix + szShortDescription = trim(std::format("{} {} {}", output->make, output->model, output->serial)); + std::erase(szShortDescription, ','); + + if (output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM) + createdByUser = true; // should be true. WL and Headless backends should be addable / removable + + // get monitor rule that matches + SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(self.lock()); + // if it's disabled, disable and ignore if (monitorRule.disabled) { - m_output->state->resetExplicitFences(); - m_output->state->setEnabled(false); + output->state->resetExplicitFences(); + output->state->setEnabled(false); - if (!m_state.commit()) - Log::logger->log(Log::ERR, "Couldn't commit disabled state on output {}", m_output->name); + if (!state.commit()) + Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name); - m_enabled = false; + m_bEnabled = false; - m_listeners.frame.reset(); + listeners.frame.reset(); return; } - if (m_output->nonDesktop) { - Log::logger->log(Log::DEBUG, "Not configuring non-desktop output"); - - for (auto& [name, lease] : PROTO::lease) { - if (!lease || m_output->getBackend() != lease->getBackend()) - continue; - - lease->offer(m_self.lock()); - } + if (output->nonDesktop) { + Debug::log(LOG, "Not configuring non-desktop output"); + if (PROTO::lease) + PROTO::lease->offer(self.lock()); return; } @@ -259,8 +166,8 @@ void CMonitor::onConnect(bool noRule) { PHLMONITOR* thisWrapper = nullptr; // find the wrap - for (auto& m : g_pCompositor->m_realMonitors) { - if (m->m_id == m_id) { + for (auto& m : g_pCompositor->m_vRealMonitors) { + if (m->ID == ID) { thisWrapper = &m; break; } @@ -268,130 +175,107 @@ void CMonitor::onConnect(bool noRule) { RASSERT(thisWrapper->get(), "CMonitor::onConnect: Had no wrapper???"); - if (std::ranges::find_if(g_pCompositor->m_monitors, [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_monitors.end()) - g_pCompositor->m_monitors.push_back(*thisWrapper); + if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end()) + g_pCompositor->m_vMonitors.push_back(*thisWrapper); - m_enabled = true; + m_bEnabled = true; - m_output->state->resetExplicitFences(); - m_output->state->setEnabled(true); + output->state->resetExplicitFences(); + output->state->setEnabled(true); // set mode, also applies if (!noRule) applyMonitorRule(&monitorRule, true); - if (!m_state.commit()) - Log::logger->log(Log::WARN, "state.commit() failed in CMonitor::onCommit"); + if (!state.commit()) + Debug::log(WARN, "state.commit() failed in CMonitor::onCommit"); - m_damage.setSize(m_transformedSize); + damage.setSize(vecTransformedSize); - Log::logger->log(Log::DEBUG, "Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x}", m_output->name, m_position, m_pixelSize, rc(m_output.get())); + Debug::log(LOG, "Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x}", output->name, vecPosition, vecPixelSize, (uintptr_t)output.get()); setupDefaultWS(monitorRule); - for (auto const& ws : g_pCompositor->getWorkspacesCopy()) { + for (auto const& ws : g_pCompositor->m_vWorkspaces) { if (!valid(ws)) continue; - const auto CURRENTMON = ws->m_monitor.lock(); - const bool ORPHANED = !CURRENTMON || std::ranges::none_of(g_pCompositor->m_monitors, [&](const auto& mon) { return mon == CURRENTMON; }); - const bool RETURNING = ws->m_lastMonitor == m_name; - const bool RECOVERY = g_pCompositor->m_monitors.size() == 1 && ORPHANED; // temporarily recover orphaned workspaces - - if (RETURNING || RECOVERY) { - g_pCompositor->moveWorkspaceToMonitor(ws, m_self.lock()); - g_pDesktopAnimationManager->startAnimation(ws, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); - if (RETURNING) - ws->m_lastMonitor = ""; + if (ws->m_szLastMonitor == szName || g_pCompositor->m_vMonitors.size() == 1 /* avoid lost workspaces on recover */) { + g_pCompositor->moveWorkspaceToMonitor(ws, self.lock()); + ws->startAnim(true, true, true); + ws->m_szLastMonitor = ""; } } - m_scale = monitorRule.scale; - if (m_scale < 0.1) - m_scale = getDefaultScale(); + scale = monitorRule.scale; + if (scale < 0.1) + scale = getDefaultScale(); - m_forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering. + forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering. // - if (!m_activeMonitorRule.mirrorOf.empty()) - setMirror(m_activeMonitorRule.mirrorOf); + if (!activeMonitorRule.mirrorOf.empty()) + setMirror(activeMonitorRule.mirrorOf); - if (!Desktop::focusState()->monitor()) // set the last monitor if it isn't set yet - Desktop::focusState()->rawMonitorFocus(m_self.lock()); + if (!g_pCompositor->m_pLastMonitor) // set the last monitor if it isnt set yet + g_pCompositor->setActiveMonitor(self.lock()); - g_pHyprRenderer->arrangeLayersForMonitor(m_id); - g_layoutManager->recalculateMonitor(m_self.lock()); + g_pHyprRenderer->arrangeLayersForMonitor(ID); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID); // ensure VRR (will enable if necessary) - g_pConfigManager->ensureVRR(m_self.lock()); + g_pConfigManager->ensureVRR(self.lock()); // verify last mon valid bool found = false; - for (auto const& m : g_pCompositor->m_monitors) { - if (m == Desktop::focusState()->monitor()) { + for (auto const& m : g_pCompositor->m_vMonitors) { + if (m == g_pCompositor->m_pLastMonitor) { found = true; break; } } - Log::logger->log(Log::DEBUG, "checking if we have seen this monitor before: {}", m_name); - // if we saw this monitor before, set it to the workspace it was on - if (g_pCompositor->m_seenMonitorWorkspaceMap.contains(m_name)) { - auto workspaceID = g_pCompositor->m_seenMonitorWorkspaceMap[m_name]; - Log::logger->log(Log::DEBUG, "Monitor {} was on workspace {}, setting it to that", m_name, workspaceID); - auto ws = g_pCompositor->getWorkspaceByID(workspaceID); - if (ws) { - g_pCompositor->moveWorkspaceToMonitor(ws, m_self.lock()); - changeWorkspace(ws, true, false, false); - } - } else - Log::logger->log(Log::DEBUG, "Monitor {} was not on any workspace", m_name); - if (!found) - Desktop::focusState()->rawMonitorFocus(m_self.lock()); + g_pCompositor->setActiveMonitor(self.lock()); - g_pCompositor->scheduleFrameForMonitor(m_self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEW_MONITOR); + renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this); - PROTO::gamma->applyGammaToState(m_self.lock()); + g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEW_MONITOR); - m_events.connect.emit(); + PROTO::gamma->applyGammaToState(self.lock()); - g_pEventManager->postEvent(SHyprIPCEvent{"monitoradded", m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{"monitoraddedv2", std::format("{},{},{}", m_id, m_name, m_shortDescription)}); - Event::bus()->m_events.monitor.added.emit(m_self.lock()); + events.connect.emit(); + + g_pEventManager->postEvent(SHyprIPCEvent{"monitoradded", szName}); + g_pEventManager->postEvent(SHyprIPCEvent{"monitoraddedv2", std::format("{},{},{}", ID, szName, szShortDescription)}); + EMIT_HOOK_EVENT("monitorAdded", self.lock()); } void CMonitor::onDisconnect(bool destroy) { - Event::bus()->m_events.monitor.preRemoved.emit(m_self.lock()); + EMIT_HOOK_EVENT("preMonitorRemoved", self.lock()); CScopeGuard x = {[this]() { - if (g_pCompositor->m_isShuttingDown) + if (g_pCompositor->m_bIsShuttingDown) return; - g_pEventManager->postEvent(SHyprIPCEvent{"monitorremoved", m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{"monitorremovedv2", std::format("{},{},{}", m_id, m_name, m_shortDescription)}); - Event::bus()->m_events.monitor.removed.emit(m_self.lock()); - g_pCompositor->scheduleMonitorStateRecheck(); + g_pEventManager->postEvent(SHyprIPCEvent{"monitorremoved", szName}); + EMIT_HOOK_EVENT("monitorRemoved", self.lock()); + g_pCompositor->arrangeMonitors(); }}; - m_frameScheduler.reset(); + if (renderTimer) { + wl_event_source_remove(renderTimer); + renderTimer = nullptr; + } - if (!m_enabled || g_pCompositor->m_isShuttingDown) + if (!m_bEnabled || g_pCompositor->m_bIsShuttingDown) return; - Log::logger->log(Log::DEBUG, "onDisconnect called for {}", m_output->name); + Debug::log(LOG, "onDisconnect called for {}", output->name); - m_events.disconnect.emit(); - if (g_pHyprOpenGL) - g_pHyprOpenGL->destroyMonitorResources(m_self); - - // record what workspace this monitor was on - if (m_activeWorkspace) { - Log::logger->log(Log::DEBUG, "Disconnecting Monitor {} was on workspace {}", m_name, m_activeWorkspace->m_id); - g_pCompositor->m_seenMonitorWorkspaceMap[m_name] = m_activeWorkspace->m_id; - } + events.disconnect.emit(); // Cleanup everything. Move windows back, snap cursor, shit. PHLMONITOR BACKUPMON = nullptr; - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { if (m.get() != this) { BACKUPMON = m; break; @@ -399,269 +283,139 @@ void CMonitor::onDisconnect(bool destroy) { } // remove mirror - if (m_mirrorOf) { - m_mirrorOf->m_mirrors.erase(std::ranges::find_if(m_mirrorOf->m_mirrors, [&](const auto& other) { return other == m_self; })); + if (pMirrorOf) { + pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == self; })); // unlock software for mirrored monitor - g_pPointerManager->unlockSoftwareForMonitor(m_mirrorOf.lock()); - m_mirrorOf.reset(); + g_pPointerManager->unlockSoftwareForMonitor(pMirrorOf.lock()); + pMirrorOf.reset(); } - if (!m_mirrors.empty()) { - for (auto const& m : m_mirrors) { + if (!mirrors.empty()) { + for (auto const& m : mirrors) { m->setMirror(""); } - g_pConfigManager->m_wantsMonitorReload = true; + g_pConfigManager->m_bWantsMonitorReload = true; } - m_listeners.frame.reset(); - m_listeners.presented.reset(); - m_listeners.needsFrame.reset(); - m_listeners.commit.reset(); + listeners.frame.reset(); + listeners.presented.reset(); + listeners.needsFrame.reset(); + listeners.commit.reset(); for (size_t i = 0; i < 4; ++i) { - for (auto const& ls : m_layerSurfaceLayers[i]) { - if (ls->m_layerSurface && !ls->m_fadingOut) - ls->m_layerSurface->sendClosed(); + for (auto const& ls : m_aLayerSurfaceLayers[i]) { + if (ls->layerSurface && !ls->fadingOut) + ls->layerSurface->sendClosed(); } - m_layerSurfaceLayers[i].clear(); + m_aLayerSurfaceLayers[i].clear(); } - Log::logger->log(Log::DEBUG, "Removed monitor {}!", m_name); + Debug::log(LOG, "Removed monitor {}!", szName); if (!BACKUPMON) { - Log::logger->log(Log::WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend."); + Debug::log(WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend."); g_pCompositor->enterUnsafeState(); } - m_enabled = false; - m_renderingInitPassed = false; - - std::vector wspToMove; - for (auto const& w : g_pCompositor->getWorkspaces()) { - if (w->m_monitor == m_self || !w->m_monitor) - wspToMove.emplace_back(w.lock()); - } - - // Preserve ownership across cascaded monitor disconnects. - // The first disconnected monitor "owns" where a workspace should return. - for (auto const& w : wspToMove) { - if (w && w->m_lastMonitor.empty()) - w->m_lastMonitor = m_name; - } + m_bEnabled = false; + m_bRenderingInitPassed = false; if (BACKUPMON) { // snap cursor - g_pCompositor->warpCursorTo(BACKUPMON->m_position + BACKUPMON->m_transformedSize / 2.F, true); + g_pCompositor->warpCursorTo(BACKUPMON->vecPosition + BACKUPMON->vecTransformedSize / 2.F, true); + + // move workspaces + std::vector wspToMove; + for (auto const& w : g_pCompositor->m_vWorkspaces) { + if (w->m_pMonitor == self || !w->m_pMonitor) + wspToMove.push_back(w); + } for (auto const& w : wspToMove) { + w->m_szLastMonitor = szName; g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON); - g_pDesktopAnimationManager->startAnimation(w, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); + w->startAnim(true, true, true); } } else { - Desktop::focusState()->surface().reset(); - Desktop::focusState()->window().reset(); - Desktop::focusState()->monitor().reset(); + g_pCompositor->m_pLastFocus.reset(); + g_pCompositor->m_pLastWindow.reset(); + g_pCompositor->m_pLastMonitor.reset(); } - if (m_activeWorkspace) - m_activeWorkspace->m_visible = false; - m_activeWorkspace.reset(); + if (activeWorkspace) + activeWorkspace->m_bVisible = false; + activeWorkspace.reset(); - m_output->state->resetExplicitFences(); - m_output->state->setAdaptiveSync(false); - m_output->state->setEnabled(false); + output->state->resetExplicitFences(); + output->state->setAdaptiveSync(false); + output->state->setEnabled(false); - if (!m_state.commit()) - Log::logger->log(Log::WARN, "state.commit() failed in CMonitor::onDisconnect"); + if (!state.commit()) + Debug::log(WARN, "state.commit() failed in CMonitor::onDisconnect"); - if (Desktop::focusState()->monitor() == m_self) - Desktop::focusState()->rawMonitorFocus(BACKUPMON ? BACKUPMON : g_pCompositor->m_unsafeOutput.lock()); + if (g_pCompositor->m_pLastMonitor == self) + g_pCompositor->setActiveMonitor(BACKUPMON ? BACKUPMON : g_pCompositor->m_pUnsafeOutput.lock()); - if (g_pHyprRenderer->m_mostHzMonitor == m_self) { + if (g_pHyprRenderer->m_pMostHzMonitor == self) { int mostHz = 0; PHLMONITOR pMonitorMostHz = nullptr; - for (auto const& m : g_pCompositor->m_monitors) { - if (m->m_refreshRate > mostHz && m != m_self) { + for (auto const& m : g_pCompositor->m_vMonitors) { + if (m->refreshRate > mostHz && m != self) { pMonitorMostHz = m; - mostHz = m->m_refreshRate; + mostHz = m->refreshRate; } } - g_pHyprRenderer->m_mostHzMonitor = pMonitorMostHz; - } - - std::erase_if(g_pCompositor->m_monitors, [&](PHLMONITOR& el) { return el.get() == this; }); -} - -static NColorManagement::eTransferFunction chooseTF(NTransferFunction::eTF tf) { - const auto sdrEOTF = NTransferFunction::fromConfig(); - - switch (tf) { - case NTransferFunction::TF_DEFAULT: - case NTransferFunction::TF_GAMMA22: - case NTransferFunction::TF_FORCED_GAMMA22: return NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; - case NTransferFunction::TF_SRGB: return NColorManagement::CM_TRANSFER_FUNCTION_SRGB; - - case NTransferFunction::TF_AUTO: // use global setting - switch (sdrEOTF) { - case NTransferFunction::TF_AUTO: return NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; - default: return chooseTF(sdrEOTF); - } - - default: UNREACHABLE(); - } -} - -void CMonitor::applyCMType(NCMType::eCMType cmType, NTransferFunction::eTF cmSdrEotf) { - auto oldImageDescription = m_imageDescription; - const auto chosenSdrEotf = chooseTF(cmSdrEotf); - - const auto masteringPrimaries = getMasteringPrimaries(); - const NColorManagement::SImageDescription::SPCMasteringLuminances masteringLuminances = getMasteringLuminances(); - - const auto maxFALL = this->maxFALL(); - const auto maxCLL = this->maxCLL(); - - switch (cmType) { - case NCMType::CM_SRGB: - m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf, - .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB)}); - break; // assumes SImageDescription defaults to sRGB - case NCMType::CM_WIDE: - m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), - .masteringPrimaries = masteringPrimaries, - .masteringLuminances = masteringLuminances, - .maxCLL = maxCLL, - .maxFALL = maxFALL}); - break; - case NCMType::CM_DCIP3: - m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_DCI_P3, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DCI_P3), - .masteringPrimaries = masteringPrimaries, - .masteringLuminances = masteringLuminances, - .maxCLL = maxCLL, - .maxFALL = maxFALL}); - break; - case NCMType::CM_DP3: - m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_DISPLAY_P3, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DISPLAY_P3), - .masteringPrimaries = masteringPrimaries, - .masteringLuminances = masteringLuminances, - .maxCLL = maxCLL, - .maxFALL = maxFALL}); - break; - case NCMType::CM_ADOBE: - m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_ADOBE_RGB, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_ADOBE_RGB), - .masteringPrimaries = masteringPrimaries, - .masteringLuminances = masteringLuminances, - .maxCLL = maxCLL, - .maxFALL = maxFALL}); - break; - case NCMType::CM_EDID: - m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf, - .primariesNameSet = false, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = masteringPrimaries, - .masteringPrimaries = masteringPrimaries, - .masteringLuminances = masteringLuminances, - .maxCLL = maxCLL, - .maxFALL = maxFALL}); - break; - case NCMType::CM_HDR: m_imageDescription = DEFAULT_HDR_IMAGE_DESCRIPTION; break; - case NCMType::CM_HDR_EDID: - m_imageDescription = CImageDescription::from( - {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, - .primariesNameSet = false, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = m_output->parsedEDID.chromaticityCoords.has_value() ? masteringPrimaries : NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), - .masteringPrimaries = masteringPrimaries, - .luminances = {.min = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFMinLuminance(), - .max = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFMaxLuminance(), - .reference = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFRefLuminance()}, - .masteringLuminances = masteringLuminances, - .maxCLL = maxCLL, - .maxFALL = maxFALL}); - - break; - default: UNREACHABLE(); - } - if ((m_minLuminance >= 0 || m_maxLuminance >= 0 || m_maxAvgLuminance >= 0) && (cmType == NCMType::CM_HDR || cmType == NCMType::CM_HDR_EDID)) - m_imageDescription = m_imageDescription->with({ - .min = m_minLuminance >= 0 ? m_minLuminance : m_imageDescription->value().luminances.min, // - .max = m_maxLuminance >= 0 ? m_maxLuminance : m_imageDescription->value().luminances.max, // - .reference = m_imageDescription->value().luminances.reference // - }); - - if (oldImageDescription != m_imageDescription) { - if (PROTO::colorManagement) - PROTO::colorManagement->onMonitorImageDescriptionChanged(m_self); + g_pHyprRenderer->m_pMostHzMonitor = pMonitorMostHz; } + std::erase_if(g_pCompositor->m_vMonitors, [&](PHLMONITOR& el) { return el.get() == this; }); } bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { static auto PDISABLESCALECHECKS = CConfigValue("debug:disable_scale_checks"); - Log::logger->log(Log::DEBUG, "Applying monitor rule for {}", m_name); + Debug::log(LOG, "Applying monitor rule for {}", szName); - m_activeMonitorRule = *pMonitorRule; + activeMonitorRule = *pMonitorRule; - if (m_forceSize.has_value()) - m_activeMonitorRule.resolution = m_forceSize.value(); + if (forceSize.has_value()) + activeMonitorRule.resolution = forceSize.value(); - const auto RULE = &m_activeMonitorRule; + const auto RULE = &activeMonitorRule; // if it's disabled, disable and ignore if (RULE->disabled) { - if (m_enabled) + if (m_bEnabled) onDisconnect(); - m_events.modeChanged.emit(); + events.modeChanged.emit(); return true; } // don't touch VR headsets - if (m_output->nonDesktop) + if (output->nonDesktop) return true; - if (!m_enabled) { + if (!m_bEnabled) { onConnect(true); // enable it. - Log::logger->log(Log::DEBUG, "Monitor {} is disabled but is requested to be enabled", m_name); + Debug::log(LOG, "Monitor {} is disabled but is requested to be enabled", szName); force = true; } // Check if the rule isn't already applied // TODO: clean this up lol - if (!force && DELTALESSTHAN(m_pixelSize.x, RULE->resolution.x, 1) /* ↓ */ - && DELTALESSTHAN(m_pixelSize.y, RULE->resolution.y, 1) /* Resolution is the same */ - && m_pixelSize.x > 1 && m_pixelSize.y > 1 /* Active resolution is not invalid */ - && DELTALESSTHAN(m_refreshRate, RULE->refreshRate, 1) /* Refresh rate is the same */ - && m_setScale == RULE->scale /* Scale is the same */ - && m_autoDir == RULE->autoDir /* Auto direction is the same */ - /* position is set correctly */ - && ((DELTALESSTHAN(m_position.x, RULE->offset.x, 1) && DELTALESSTHAN(m_position.y, RULE->offset.y, 1)) || RULE->offset == Vector2D(-INT32_MAX, -INT32_MAX)) - /* other properties hadn't changed */ - && m_transform == RULE->transform && RULE->enable10bit == m_enabled10bit && RULE->cmType == m_cmType && RULE->sdrSaturation == m_sdrSaturation && - RULE->sdrBrightness == m_sdrBrightness && RULE->sdrMinLuminance == m_minLuminance && RULE->sdrMaxLuminance == m_maxLuminance && - RULE->supportsWideColor == m_supportsWideColor && RULE->supportsHDR == m_supportsHDR && RULE->minLuminance == m_minLuminance && RULE->maxLuminance == m_maxLuminance && - RULE->maxAvgLuminance == m_maxAvgLuminance && !std::memcmp(&m_customDrmMode, &RULE->drmMode, sizeof(m_customDrmMode)) && m_reservedArea == RULE->reservedArea) { + if (!force && DELTALESSTHAN(vecPixelSize.x, RULE->resolution.x, 1) && DELTALESSTHAN(vecPixelSize.y, RULE->resolution.y, 1) && + DELTALESSTHAN(refreshRate, RULE->refreshRate, 1) && setScale == RULE->scale && + ((DELTALESSTHAN(vecPosition.x, RULE->offset.x, 1) && DELTALESSTHAN(vecPosition.y, RULE->offset.y, 1)) || RULE->offset == Vector2D(-INT32_MAX, -INT32_MAX)) && + transform == RULE->transform && RULE->enable10bit == enabled10bit && RULE->cmType == cmType && RULE->sdrSaturation == sdrSaturation && + RULE->sdrBrightness == sdrBrightness && !std::memcmp(&customDrmMode, &RULE->drmMode, sizeof(customDrmMode))) { - Log::logger->log(Log::DEBUG, "Not applying a new rule to {} because it's already applied!", m_name); + Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", szName); setMirror(RULE->mirrorOf); @@ -670,18 +424,16 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { bool autoScale = false; - if (RULE->scale > 0.1) - m_scale = RULE->scale; - else { + if (RULE->scale > 0.1) { + scale = RULE->scale; + } else { autoScale = true; const auto DEFAULTSCALE = getDefaultScale(); - m_scale = DEFAULTSCALE; + scale = DEFAULTSCALE; } - m_setScale = m_scale; - m_transform = RULE->transform; - m_autoDir = RULE->autoDir; - m_reservedArea = RULE->reservedArea; + setScale = scale; + transform = RULE->transform; // accumulate requested modes in reverse order (cause inesrting at front is inefficient) std::vector> requestedModes; @@ -689,30 +441,30 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { // use sortFunc, add best 3 to requestedModes in reverse, since we test in reverse auto addBest3Modes = [&](auto const& sortFunc) { - auto sortedModes = m_output->modes; + auto sortedModes = output->modes; std::ranges::sort(sortedModes, sortFunc); if (sortedModes.size() > 3) sortedModes.erase(sortedModes.begin() + 3, sortedModes.end()); - requestedModes.insert_range(requestedModes.end(), sortedModes | std::views::reverse); + requestedModes.insert(requestedModes.end(), sortedModes.rbegin(), sortedModes.rend()); }; // last fallback is always preferred mode - if (!m_output->preferredMode()) - Log::logger->log(Log::ERR, "Monitor {} has NO PREFERRED MODE", m_output->name); + if (!output->preferredMode()) + Debug::log(ERR, "Monitor {} has NO PREFERRED MODE", output->name); else - requestedModes.push_back(m_output->preferredMode()); + requestedModes.push_back(output->preferredMode()); if (RULE->resolution == Vector2D()) { requestedStr = "preferred"; // fallback to first 3 modes if preferred fails/doesn't exist - requestedModes = m_output->modes; + requestedModes = output->modes; if (requestedModes.size() > 3) requestedModes.erase(requestedModes.begin() + 3, requestedModes.end()); std::ranges::reverse(requestedModes.begin(), requestedModes.end()); - if (m_output->preferredMode()) - requestedModes.push_back(m_output->preferredMode()); + if (output->preferredMode()) + requestedModes.push_back(output->preferredMode()); } else if (RULE->resolution == Vector2D(-1, -1)) { requestedStr = "highrr"; @@ -720,14 +472,14 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { addBest3Modes([](auto const& a, auto const& b) { if (std::round(a->refreshRate) > std::round(b->refreshRate)) return true; - else if (DELTALESSTHAN(sc(a->refreshRate), sc(b->refreshRate), 1.F) && a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y) + else if (DELTALESSTHAN((float)a->refreshRate, (float)b->refreshRate, 1.F) && a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y) return true; return false; }); } else if (RULE->resolution == Vector2D(-1, -2)) { requestedStr = "highres"; - // sort prioritizing resolution 1st and refresh rate 2nd, then add best 3 + // sort prioritizing resultion 1st and refresh rate 2nd, then add best 3 addBest3Modes([](auto const& a, auto const& b) { if (a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y) return true; @@ -736,17 +488,6 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { return true; return false; }); - } else if (RULE->resolution == Vector2D(-1, -3)) { - requestedStr = "maxwidth"; - - // sort prioritizing widest resolution 1st and refresh rate 2nd, then add best 3 - addBest3Modes([](auto const& a, auto const& b) { - if (a->pixelSize.x > b->pixelSize.x) - return true; - if (a->pixelSize.x == b->pixelSize.x && std::round(a->refreshRate) > std::round(b->refreshRate)) - return true; - return false; - }); } else if (RULE->resolution != Vector2D()) { // user requested mode requestedStr = std::format("{:X0}@{:.2f}Hz", RULE->resolution, RULE->refreshRate); @@ -762,7 +503,7 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { return false; }); - // if the best mode isn't close to requested, then try requested as custom mode first + // if the best mode isnt close to requested, then try requested as custom mode first if (!requestedModes.empty()) { auto bestMode = requestedModes.back(); if (!DELTALESSTHAN(bestMode->pixelSize.x, RULE->resolution.x, 1) || !DELTALESSTHAN(bestMode->pixelSize.y, RULE->resolution.y, 1) || @@ -772,34 +513,34 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { // then if requested is custom, try custom mode first if (RULE->drmMode.type == DRM_MODE_TYPE_USERDEF) { - if (m_output->getBackend()->type() != Aquamarine::eBackendType::AQ_BACKEND_DRM) - Log::logger->log(Log::ERR, "Tried to set custom modeline on non-DRM output"); + if (output->getBackend()->type() != Aquamarine::eBackendType::AQ_BACKEND_DRM) + Debug::log(ERR, "Tried to set custom modeline on non-DRM output"); else requestedModes.push_back(makeShared( Aquamarine::SOutputMode{.pixelSize = {RULE->drmMode.hdisplay, RULE->drmMode.vdisplay}, .refreshRate = RULE->drmMode.vrefresh, .modeInfo = RULE->drmMode})); } } - const auto WAS10B = m_enabled10bit; - const auto OLDRES = m_pixelSize; + const auto WAS10B = enabled10bit; + const auto OLDRES = vecPixelSize; bool success = false; // Needed in case we are switching from a custom modeline to a standard mode - m_customDrmMode = {}; - m_currentMode = nullptr; + customDrmMode = {}; + currentMode = nullptr; - m_output->state->setFormat(DRM_FORMAT_XRGB8888); - m_prevDrmFormat = m_drmFormat; - m_drmFormat = DRM_FORMAT_XRGB8888; - m_output->state->resetExplicitFences(); + output->state->setFormat(DRM_FORMAT_XRGB8888); + prevDrmFormat = drmFormat; + drmFormat = DRM_FORMAT_XRGB8888; + output->state->resetExplicitFences(); - if (Env::isTrace()) { - Log::logger->log(Log::TRACE, "Monitor {} requested modes:", m_name); + if (Debug::trace) { + Debug::log(TRACE, "Monitor {} requested modes:", szName); if (requestedModes.empty()) - Log::logger->log(Log::TRACE, "| None"); + Debug::log(TRACE, "| None"); else { for (auto const& mode : requestedModes | std::views::reverse) { - Log::logger->log(Log::TRACE, "| {:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f); + Debug::log(TRACE, "| {:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f); } } } @@ -808,81 +549,81 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { std::string modeStr = std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f); if (mode->modeInfo.has_value() && mode->modeInfo->type == DRM_MODE_TYPE_USERDEF) { - m_output->state->setCustomMode(mode); + output->state->setCustomMode(mode); - if (!m_state.test()) { - Log::logger->log(Log::ERR, "Monitor {}: REJECTED custom mode {}!", m_name, modeStr); + if (!state.test()) { + Debug::log(ERR, "Monitor {}: REJECTED custom mode {}!", szName, modeStr); continue; } - m_customDrmMode = mode->modeInfo.value(); + customDrmMode = mode->modeInfo.value(); } else { - m_output->state->setMode(mode); + output->state->setMode(mode); - if (!m_state.test()) { - Log::logger->log(Log::ERR, "Monitor {}: REJECTED available mode {}!", m_name, modeStr); + if (!state.test()) { + Debug::log(ERR, "Monitor {}: REJECTED available mode {}!", szName, modeStr); if (mode->preferred) - Log::logger->log(Log::ERR, "Monitor {}: REJECTED preferred mode!!!", m_name); + Debug::log(ERR, "Monitor {}: REJECTED preferred mode!!!", szName); continue; } - m_customDrmMode = {}; + customDrmMode = {}; } - m_refreshRate = mode->refreshRate / 1000.f; - m_size = mode->pixelSize; - m_currentMode = mode; + refreshRate = mode->refreshRate / 1000.f; + vecSize = mode->pixelSize; + currentMode = mode; success = true; if (mode->preferred) - Log::logger->log(Log::DEBUG, "Monitor {}: requested {}, using preferred mode {}", m_name, requestedStr, modeStr); + Debug::log(LOG, "Monitor {}: requested {}, using preferred mode {}", szName, requestedStr, modeStr); else if (mode->modeInfo.has_value() && mode->modeInfo->type == DRM_MODE_TYPE_USERDEF) - Log::logger->log(Log::DEBUG, "Monitor {}: requested {}, using custom mode {}", m_name, requestedStr, modeStr); + Debug::log(LOG, "Monitor {}: requested {}, using custom mode {}", szName, requestedStr, modeStr); else - Log::logger->log(Log::DEBUG, "Monitor {}: requested {}, using available mode {}", m_name, requestedStr, modeStr); + Debug::log(LOG, "Monitor {}: requested {}, using available mode {}", szName, requestedStr, modeStr); break; } // try requested as custom mode jic it works if (!success && RULE->resolution != Vector2D() && RULE->resolution != Vector2D(-1, -1) && RULE->resolution != Vector2D(-1, -2)) { - auto refreshRate = m_output->getBackend()->type() == Aquamarine::eBackendType::AQ_BACKEND_DRM ? RULE->refreshRate * 1000 : 0; + auto refreshRate = output->getBackend()->type() == Aquamarine::eBackendType::AQ_BACKEND_DRM ? RULE->refreshRate * 1000 : 0; auto mode = makeShared(Aquamarine::SOutputMode{.pixelSize = RULE->resolution, .refreshRate = refreshRate}); std::string modeStr = std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f); - m_output->state->setCustomMode(mode); + output->state->setCustomMode(mode); - if (m_state.test()) { - Log::logger->log(Log::DEBUG, "Monitor {}: requested {}, using custom mode {}", m_name, requestedStr, modeStr); + if (state.test()) { + Debug::log(LOG, "Monitor {}: requested {}, using custom mode {}", szName, requestedStr, modeStr); - refreshRate = mode->refreshRate / 1000.f; - m_size = mode->pixelSize; - m_currentMode = mode; - m_customDrmMode = {}; + refreshRate = mode->refreshRate / 1000.f; + vecSize = mode->pixelSize; + currentMode = mode; + customDrmMode = {}; success = true; } else - Log::logger->log(Log::ERR, "Monitor {}: REJECTED custom mode {}!", m_name, modeStr); + Debug::log(ERR, "Monitor {}: REJECTED custom mode {}!", szName, modeStr); } // try any of the modes if none of the above work if (!success) { - for (auto const& mode : m_output->modes) { - m_output->state->setMode(mode); + for (auto const& mode : output->modes) { + output->state->setMode(mode); - if (!m_state.test()) + if (!state.test()) continue; - auto errorMessage = I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_MONITOR_MODE_FAIL, - {{"name", m_name}, {"mode", std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f)}}); - Log::logger->log(Log::WARN, errorMessage); + auto errorMessage = + std::format("Monitor {} failed to set any requested modes, falling back to mode {:X0}@{:.2f}Hz", szName, mode->pixelSize, mode->refreshRate / 1000.f); + Debug::log(WARN, errorMessage); g_pHyprNotificationOverlay->addNotification(errorMessage, CHyprColor(0xff0000ff), 5000, ICON_WARNING); - m_refreshRate = mode->refreshRate / 1000.f; - m_size = mode->pixelSize; - m_currentMode = mode; - m_customDrmMode = {}; + refreshRate = mode->refreshRate / 1000.f; + vecSize = mode->pixelSize; + currentMode = mode; + customDrmMode = {}; success = true; @@ -891,14 +632,14 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { } if (!success) { - Log::logger->log(Log::ERR, "Monitor {} has NO FALLBACK MODES, and an INVALID one was requested: {:X0}@{:.2f}Hz", m_name, RULE->resolution, RULE->refreshRate); + Debug::log(ERR, "Monitor {} has NO FALLBACK MODES, and an INVALID one was requested: {:X0}@{:.2f}Hz", szName, RULE->resolution, RULE->refreshRate); return true; } - m_vrrActive = m_output->state->state().adaptiveSync // disabled here, will be tested in CConfigManager::ensureVRR() - || m_createdByUser; // wayland backend doesn't allow for disabling adaptive_sync + vrrActive = output->state->state().adaptiveSync // disabled here, will be tested in CConfigManager::ensureVRR() + || createdByUser; // wayland backend doesn't allow for disabling adaptive_sync - m_pixelSize = m_size; + vecPixelSize = vecSize; // clang-format off static const std::array>, 2> formats{ @@ -913,86 +654,103 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { bool set10bit = false; - for (auto const& fmt : formats[sc(!RULE->enable10bit)]) { - m_output->state->setFormat(fmt.second); - m_prevDrmFormat = m_drmFormat; - m_drmFormat = fmt.second; + for (auto const& fmt : formats[(int)!RULE->enable10bit]) { + output->state->setFormat(fmt.second); + prevDrmFormat = drmFormat; + drmFormat = fmt.second; - if (!m_state.test()) { - Log::logger->log(Log::ERR, "output {} failed basic test on format {}", m_name, fmt.first); + if (!state.test()) { + Debug::log(ERR, "output {} failed basic test on format {}", szName, fmt.first); } else { - Log::logger->log(Log::DEBUG, "output {} succeeded basic test on format {}", m_name, fmt.first); + Debug::log(LOG, "output {} succeeded basic test on format {}", szName, fmt.first); if (RULE->enable10bit && fmt.first.contains("101010")) set10bit = true; break; } } - m_enabled10bit = set10bit; + enabled10bit = set10bit; - m_supportsWideColor = RULE->supportsHDR; - m_supportsHDR = RULE->supportsHDR; - - if (RULE->iccFile.empty()) { - // only apply explicit cm settings if we have no icc file - - m_cmType = RULE->cmType; - switch (m_cmType) { - case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break; - case NCMType::CM_EDID: m_cmType = m_output->parsedEDID.chromaticityCoords.has_value() ? NCMType::CM_EDID : NCMType::CM_SRGB; break; - case NCMType::CM_HDR: - case NCMType::CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : NCMType::CM_SRGB; break; - default: break; - } - - m_sdrEotf = RULE->sdrEotf; - - m_sdrMinLuminance = RULE->sdrMinLuminance; - m_sdrMaxLuminance = RULE->sdrMaxLuminance; - - m_minLuminance = RULE->minLuminance; - m_maxLuminance = RULE->maxLuminance; - m_maxAvgLuminance = RULE->maxAvgLuminance; - - applyCMType(m_cmType, m_sdrEotf); - - m_sdrSaturation = RULE->sdrSaturation; - m_sdrBrightness = RULE->sdrBrightness; - } else { - auto image = NColorManagement::SImageDescription::fromICC(RULE->iccFile); - if (!image) { - Log::logger->log(Log::ERR, "icc for {} ({}) failed: {}", m_name, RULE->iccFile, image.error()); - g_pConfigManager->addParseError(std::format("failed to apply icc {} to {}: {}", RULE->iccFile, m_name, image.error())); - } else { - m_imageDescription = CImageDescription::from(*image); - if (!m_imageDescription) { - Log::logger->log(Log::ERR, "icc for {} ({}) failed 2: {}", m_name, RULE->iccFile, image.error()); - g_pConfigManager->addParseError(std::format("failed to apply icc {} to {}: {}", RULE->iccFile, m_name, image.error())); - m_imageDescription = CImageDescription::from(SImageDescription{}); - } - } + auto oldImageDescription = imageDescription; + cmType = RULE->cmType; + switch (cmType) { + case CM_AUTO: cmType = enabled10bit && output->parsedEDID.supportsBT2020 ? CM_WIDE : CM_SRGB; break; + case CM_EDID: cmType = output->parsedEDID.chromaticityCoords.has_value() ? CM_EDID : CM_SRGB; break; + case CM_HDR: + case CM_HDR_EDID: + cmType = output->parsedEDID.supportsBT2020 && output->parsedEDID.hdrMetadata.has_value() && output->parsedEDID.hdrMetadata->supportsPQ ? cmType : CM_SRGB; + break; + default: break; } + switch (cmType) { + case CM_SRGB: imageDescription = {}; break; // assumes SImageDescirption defaults to sRGB + case CM_WIDE: + imageDescription = {.primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020)}; + break; + case CM_EDID: + imageDescription = {.primariesNameSet = false, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = { + .red = {.x = output->parsedEDID.chromaticityCoords->red.x, .y = output->parsedEDID.chromaticityCoords->red.y}, + .green = {.x = output->parsedEDID.chromaticityCoords->green.x, .y = output->parsedEDID.chromaticityCoords->green.y}, + .blue = {.x = output->parsedEDID.chromaticityCoords->blue.x, .y = output->parsedEDID.chromaticityCoords->blue.y}, + .white = {.x = output->parsedEDID.chromaticityCoords->white.x, .y = output->parsedEDID.chromaticityCoords->white.y}, + }}; + break; + case CM_HDR: + imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, + .primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), + .luminances = {.min = 0, .max = 10000, .reference = 203}}; + break; + case CM_HDR_EDID: + imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, + .primariesNameSet = false, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = output->parsedEDID.chromaticityCoords.has_value() ? + NColorManagement::SPCPRimaries{ + .red = {.x = output->parsedEDID.chromaticityCoords->red.x, .y = output->parsedEDID.chromaticityCoords->red.y}, + .green = {.x = output->parsedEDID.chromaticityCoords->green.x, .y = output->parsedEDID.chromaticityCoords->green.y}, + .blue = {.x = output->parsedEDID.chromaticityCoords->blue.x, .y = output->parsedEDID.chromaticityCoords->blue.y}, + .white = {.x = output->parsedEDID.chromaticityCoords->white.x, .y = output->parsedEDID.chromaticityCoords->white.y}, + } : + NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), + .luminances = {.min = output->parsedEDID.hdrMetadata->desiredContentMinLuminance, + .max = output->parsedEDID.hdrMetadata->desiredContentMaxLuminance, + .reference = output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}}; - Vector2D logicalSize = m_pixelSize / m_scale; + break; + default: UNREACHABLE(); + } + if (oldImageDescription != imageDescription) + PROTO::colorManagement->onMonitorImageDescriptionChanged(self); + + sdrSaturation = RULE->sdrSaturation; + sdrBrightness = RULE->sdrBrightness; + + Vector2D logicalSize = vecPixelSize / scale; if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) { // invalid scale, will produce fractional pixels. // find the nearest valid. - float searchScale = std::round(m_scale * 120.0); + float searchScale = std::round(scale * 120.0); bool found = false; double scaleZero = searchScale / 120.0; - Vector2D logicalZero = m_pixelSize / scaleZero; + Vector2D logicalZero = vecPixelSize / scaleZero; if (logicalZero == logicalZero.round()) - m_scale = scaleZero; + scale = scaleZero; else { for (size_t i = 1; i < 90; ++i) { double scaleUp = (searchScale + i) / 120.0; double scaleDown = (searchScale - i) / 120.0; - Vector2D logicalUp = m_pixelSize / scaleUp; - Vector2D logicalDown = m_pixelSize / scaleDown; + Vector2D logicalUp = vecPixelSize / scaleUp; + Vector2D logicalDown = vecPixelSize / scaleDown; if (logicalUp == logicalUp.round()) { found = true; @@ -1008,83 +766,79 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { if (!found) { if (autoScale) - m_scale = std::round(scaleZero); + scale = std::round(scaleZero); else { - Log::logger->log(Log::ERR, "Invalid scale passed to monitor, {} failed to find a clean divisor", m_scale); - g_pConfigManager->addParseError("Invalid scale passed to monitor " + m_name + ", failed to find a clean divisor"); - m_scale = getDefaultScale(); + Debug::log(ERR, "Invalid scale passed to monitor, {} failed to find a clean divisor", scale); + g_pConfigManager->addParseError("Invalid scale passed to monitor " + szName + ", failed to find a clean divisor"); + scale = getDefaultScale(); } } else { if (!autoScale) { - Log::logger->log(Log::ERR, "Invalid scale passed to monitor, {} found suggestion {}", m_scale, searchScale); - static auto PDISABLENOTIFICATION = CConfigValue("misc:disable_scale_notification"); - if (!*PDISABLENOTIFICATION) - g_pHyprNotificationOverlay->addNotification( - I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - {{"name", m_name}, {"scale", std::format("{:.2f}", m_scale)}, {"fixed_scale", std::format("{:.2f}", searchScale)}}), - CHyprColor(1.0, 0.0, 0.0, 1.0), 5000, ICON_WARNING); - } - m_scale = searchScale; + Debug::log(ERR, "Invalid scale passed to monitor, {} found suggestion {}", scale, searchScale); + g_pConfigManager->addParseError( + std::format("Invalid scale passed to monitor {}, failed to find a clean divisor. Suggested nearest scale: {:5f}", szName, searchScale)); + scale = getDefaultScale(); + } else + scale = searchScale; } } } - m_output->scheduleFrame(); + output->scheduleFrame(); - if (!m_state.commit()) - Log::logger->log(Log::ERR, "Couldn't commit output named {}", m_output->name); + if (!state.commit()) + Debug::log(ERR, "Couldn't commit output named {}", output->name); - Vector2D xfmd = m_transform % 2 == 1 ? Vector2D{m_pixelSize.y, m_pixelSize.x} : m_pixelSize; - m_size = (xfmd / m_scale).round(); - m_transformedSize = xfmd; + Vector2D xfmd = transform % 2 == 1 ? Vector2D{vecPixelSize.y, vecPixelSize.x} : vecPixelSize; + vecSize = (xfmd / scale).round(); + vecTransformedSize = xfmd; - if (m_createdByUser) { - CBox transformedBox = {0, 0, m_transformedSize.x, m_transformedSize.y}; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_transform)), m_transformedSize.x, m_transformedSize.y); + if (createdByUser) { + CBox transformedBox = {0, 0, vecTransformedSize.x, vecTransformedSize.y}; + transformedBox.transform(wlTransformToHyprutils(invertTransform(transform)), vecTransformedSize.x, vecTransformedSize.y); - m_pixelSize = Vector2D(transformedBox.width, transformedBox.height); + vecPixelSize = Vector2D(transformedBox.width, transformedBox.height); } updateMatrix(); - if (WAS10B != m_enabled10bit || OLDRES != m_pixelSize) - g_pHyprOpenGL->destroyMonitorResources(m_self); + if (WAS10B != enabled10bit || OLDRES != vecPixelSize) + g_pHyprOpenGL->destroyMonitorResources(self.lock()); - g_pCompositor->scheduleMonitorStateRecheck(); + g_pCompositor->arrangeMonitors(); - m_damage.setSize(m_transformedSize); - - updateVCGTRamps(); + damage.setSize(vecTransformedSize); // Set scale for all surfaces on this monitor, needed for some clients // but not on unsafe state to avoid crashes - if (!g_pCompositor->m_unsafeState) { - for (auto const& w : g_pCompositor->m_windows) { + if (!g_pCompositor->m_bUnsafeState) { + for (auto const& w : g_pCompositor->m_vWindows) { w->updateSurfaceScaleTransformDetails(); } } // updato us - g_pHyprRenderer->arrangeLayersForMonitor(m_id); + g_pHyprRenderer->arrangeLayersForMonitor(ID); // reload to fix mirrors - g_pConfigManager->m_wantsMonitorReload = true; + g_pConfigManager->m_bWantsMonitorReload = true; - Log::logger->log(Log::DEBUG, "Monitor {} data dump: res {:X}@{:.2f}Hz, scale {:.2f}, transform {}, pos {:X}, 10b {}", m_name, m_pixelSize, m_refreshRate, m_scale, - sc(m_transform), m_position, sc(m_enabled10bit)); + Debug::log(LOG, "Monitor {} data dump: res {:X}@{:.2f}Hz, scale {:.2f}, transform {}, pos {:X}, 10b {}", szName, vecPixelSize, refreshRate, scale, (int)transform, vecPosition, + (int)enabled10bit); - Event::bus()->m_events.monitor.layoutChanged.emit(); + EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr); - m_events.modeChanged.emit(); + events.modeChanged.emit(); return true; } void CMonitor::addDamage(const pixman_region32_t* rg) { - if (m_cursorZoom->value() != 1.f && g_pCompositor->getMonitorFromCursor() == m_self) { - m_damage.damageEntire(); - g_pCompositor->scheduleFrameForMonitor(m_self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); - } else if (m_damage.damage(rg)) - g_pCompositor->scheduleFrameForMonitor(m_self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); + static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); + if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == self) { + damage.damageEntire(); + g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); + } else if (damage.damage(rg)) + g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); } void CMonitor::addDamage(const CRegion& rg) { @@ -1092,13 +846,14 @@ void CMonitor::addDamage(const CRegion& rg) { } void CMonitor::addDamage(const CBox& box) { - if (m_cursorZoom->value() != 1.f && g_pCompositor->getMonitorFromCursor() == m_self) { - m_damage.damageEntire(); - g_pCompositor->scheduleFrameForMonitor(m_self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); + static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); + if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == self) { + damage.damageEntire(); + g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); } - if (m_damage.damage(box)) - g_pCompositor->scheduleFrameForMonitor(m_self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); + if (damage.damage(box)) + g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); } bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() { @@ -1106,15 +861,13 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() { static auto PMINRR = CConfigValue("cursor:min_refresh_rate"); // skip scheduling extra frames for fullsreen apps with vrr - const auto FS_WINDOW = getFullscreenWindow(); - const bool shouldRenderCursor = g_pHyprRenderer->shouldRenderCursor(); - const bool noBreak = FS_WINDOW && (*PNOBREAK == 1 || (*PNOBREAK == 2 && FS_WINDOW->getContentType() == CONTENT_TYPE_GAME)); - const bool shouldSkip = (!shouldRenderCursor || noBreak) && m_output->state->state().adaptiveSync; + const bool shouldSkip = activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN && + (*PNOBREAK == 1 || (*PNOBREAK == 2 && activeWorkspace->getFullscreenWindow()->getContentType() == CONTENT_TYPE_GAME)) && output->state->state().adaptiveSync; // keep requested minimum refresh rate - if (shouldSkip && *PMINRR && m_lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) { + if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) { // damage whole screen because some previous cursor box damages were skipped - m_damage.damageEntire(); + damage.damageEntire(); return false; } @@ -1122,18 +875,18 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() { } bool CMonitor::isMirror() { - return m_mirrorOf != nullptr; + return pMirrorOf != nullptr; } bool CMonitor::matchesStaticSelector(const std::string& selector) const { if (selector.starts_with("desc:")) { // match by description - const auto DESCRIPTIONSELECTOR = trim(selector.substr(5)); + const auto DESCRIPTIONSELECTOR = selector.substr(5); - return m_description.starts_with(DESCRIPTIONSELECTOR) || m_shortDescription.starts_with(DESCRIPTIONSELECTOR); + return szDescription.starts_with(DESCRIPTIONSELECTOR) || szShortDescription.starts_with(DESCRIPTIONSELECTOR); } else { // match by selector - return m_name == selector; + return szName == selector; } } @@ -1142,7 +895,7 @@ WORKSPACEID CMonitor::findAvailableDefaultWS() { if (g_pCompositor->getWorkspaceByID(i)) continue; - if (const auto BOUND = g_pConfigManager->getBoundMonitorStringForWS(std::to_string(i)); !BOUND.empty() && BOUND != m_name) + if (const auto BOUND = g_pConfigManager->getBoundMonitorStringForWS(std::to_string(i)); !BOUND.empty() && BOUND != szName) continue; return i; @@ -1155,85 +908,85 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) { // Workspace std::string newDefaultWorkspaceName = ""; int64_t wsID = WORKSPACE_INVALID; - if (g_pConfigManager->getDefaultWorkspaceFor(m_name).empty()) + if (g_pConfigManager->getDefaultWorkspaceFor(szName).empty()) wsID = findAvailableDefaultWS(); else { - const auto ws = getWorkspaceIDNameFromString(g_pConfigManager->getDefaultWorkspaceFor(m_name)); + const auto ws = getWorkspaceIDNameFromString(g_pConfigManager->getDefaultWorkspaceFor(szName)); wsID = ws.id; newDefaultWorkspaceName = ws.name; } if (wsID == WORKSPACE_INVALID || (wsID >= SPECIAL_WORKSPACE_START && wsID <= -2)) { - wsID = std::ranges::distance(g_pCompositor->getWorkspaces()) + 1; + wsID = g_pCompositor->m_vWorkspaces.size() + 1; newDefaultWorkspaceName = std::to_string(wsID); - Log::logger->log(Log::DEBUG, "Invalid workspace= directive name in monitor parsing, workspace name \"{}\" is invalid.", g_pConfigManager->getDefaultWorkspaceFor(m_name)); + Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"{}\" is invalid.", g_pConfigManager->getDefaultWorkspaceFor(szName)); } auto PNEWWORKSPACE = g_pCompositor->getWorkspaceByID(wsID); - Log::logger->log(Log::DEBUG, "New monitor: WORKSPACEID {}, exists: {}", wsID, sc(PNEWWORKSPACE != nullptr)); + Debug::log(LOG, "New monitor: WORKSPACEID {}, exists: {}", wsID, (int)(PNEWWORKSPACE != nullptr)); if (PNEWWORKSPACE) { // workspace exists, move it to the newly connected monitor - g_pCompositor->moveWorkspaceToMonitor(PNEWWORKSPACE, m_self.lock()); - m_activeWorkspace = PNEWWORKSPACE; - g_layoutManager->recalculateMonitor(m_self.lock()); - g_pDesktopAnimationManager->startAnimation(PNEWWORKSPACE, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); + g_pCompositor->moveWorkspaceToMonitor(PNEWWORKSPACE, self.lock()); + activeWorkspace = PNEWWORKSPACE; + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID); + PNEWWORKSPACE->startAnim(true, true, true); } else { - if (newDefaultWorkspaceName.empty()) + if (newDefaultWorkspaceName == "") newDefaultWorkspaceName = std::to_string(wsID); - PNEWWORKSPACE = CWorkspace::create(wsID, m_self.lock(), newDefaultWorkspaceName); + PNEWWORKSPACE = g_pCompositor->m_vWorkspaces.emplace_back(CWorkspace::create(wsID, self.lock(), newDefaultWorkspaceName)); } - m_activeWorkspace = PNEWWORKSPACE; + activeWorkspace = PNEWWORKSPACE; - PNEWWORKSPACE->m_events.activeChanged.emit(); - PNEWWORKSPACE->m_visible = true; - PNEWWORKSPACE->m_lastMonitor = ""; + PNEWWORKSPACE->setActive(true); + PNEWWORKSPACE->m_bVisible = true; + PNEWWORKSPACE->m_szLastMonitor = ""; } void CMonitor::setMirror(const std::string& mirrorOf) { const auto PMIRRORMON = g_pCompositor->getMonitorFromString(mirrorOf); - if (PMIRRORMON == m_mirrorOf) + if (PMIRRORMON == pMirrorOf) return; if (PMIRRORMON && PMIRRORMON->isMirror()) { - Log::logger->log(Log::ERR, "Cannot mirror a mirror!"); + Debug::log(ERR, "Cannot mirror a mirror!"); return; } - if (PMIRRORMON == m_self) { - Log::logger->log(Log::ERR, "Cannot mirror self!"); + if (PMIRRORMON == self) { + Debug::log(ERR, "Cannot mirror self!"); return; } if (!PMIRRORMON) { // disable mirroring - if (m_mirrorOf) { - m_mirrorOf->m_mirrors.erase(std::ranges::find_if(m_mirrorOf->m_mirrors, [&](const auto& other) { return other == m_self; })); + if (pMirrorOf) { + pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == self; })); // unlock software for mirrored monitor - g_pPointerManager->unlockSoftwareForMonitor(m_mirrorOf.lock()); + g_pPointerManager->unlockSoftwareForMonitor(pMirrorOf.lock()); } - m_mirrorOf.reset(); + pMirrorOf.reset(); // set rule - const auto RULE = g_pConfigManager->getMonitorRuleFor(m_self.lock()); + const auto RULE = g_pConfigManager->getMonitorRuleFor(self.lock()); - m_position = RULE.offset; + vecPosition = RULE.offset; // push to mvmonitors PHLMONITOR* thisWrapper = nullptr; // find the wrap - for (auto& m : g_pCompositor->m_realMonitors) { - if (m->m_id == m_id) { + for (auto& m : g_pCompositor->m_vRealMonitors) { + if (m->ID == ID) { thisWrapper = &m; break; } @@ -1241,16 +994,17 @@ void CMonitor::setMirror(const std::string& mirrorOf) { RASSERT(thisWrapper->get(), "CMonitor::setMirror: Had no wrapper???"); - if (std::ranges::find_if(g_pCompositor->m_monitors, [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_monitors.end()) { - g_pCompositor->m_monitors.push_back(*thisWrapper); + if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == + g_pCompositor->m_vMonitors.end()) { + g_pCompositor->m_vMonitors.push_back(*thisWrapper); } setupDefaultWS(RULE); - applyMonitorRule(const_cast(&RULE), true); // will apply the offset and stuff + applyMonitorRule((SMonitorRule*)&RULE, true); // will apply the offset and stuff } else { PHLMONITOR BACKUPMON = nullptr; - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { if (m.get() != this) { BACKUPMON = m; break; @@ -1259,46 +1013,48 @@ void CMonitor::setMirror(const std::string& mirrorOf) { // move all the WS std::vector wspToMove; - for (auto const& w : g_pCompositor->getWorkspaces()) { - if (w->m_monitor == m_self || !w->m_monitor) - wspToMove.emplace_back(w.lock()); + for (auto const& w : g_pCompositor->m_vWorkspaces) { + if (w->m_pMonitor == self || !w->m_pMonitor) + wspToMove.push_back(w); } for (auto const& w : wspToMove) { g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON); - g_pDesktopAnimationManager->startAnimation(w, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true); + w->startAnim(true, true, true); } - m_activeWorkspace.reset(); + activeWorkspace.reset(); - m_position = PMIRRORMON->m_position; + vecPosition = PMIRRORMON->vecPosition; - m_mirrorOf = PMIRRORMON; + pMirrorOf = PMIRRORMON; - m_mirrorOf->m_mirrors.push_back(m_self); + pMirrorOf->mirrors.push_back(self); // remove from mvmonitors - std::erase_if(g_pCompositor->m_monitors, [&](const auto& other) { return other == m_self; }); + std::erase_if(g_pCompositor->m_vMonitors, [&](const auto& other) { return other == self; }); - g_pCompositor->scheduleMonitorStateRecheck(); + g_pCompositor->arrangeMonitors(); - Desktop::focusState()->rawMonitorFocus(g_pCompositor->m_monitors.front()); + g_pCompositor->setActiveMonitor(g_pCompositor->m_vMonitors.front()); + + g_pCompositor->sanityCheckWorkspaces(); // Software lock mirrored monitor g_pPointerManager->lockSoftwareForMonitor(PMIRRORMON); } - m_events.modeChanged.emit(); + events.modeChanged.emit(); } float CMonitor::getDefaultScale() { - if (!m_enabled) + if (!m_bEnabled) return 1; static constexpr double MMPERINCH = 25.4; - const auto DIAGONALPX = sqrt(pow(m_pixelSize.x, 2) + pow(m_pixelSize.y, 2)); - const auto DIAGONALIN = sqrt(pow(m_output->physicalSize.x / MMPERINCH, 2) + pow(m_output->physicalSize.y / MMPERINCH, 2)); + const auto DIAGONALPX = sqrt(pow(vecPixelSize.x, 2) + pow(vecPixelSize.y, 2)); + const auto DIAGONALIN = sqrt(pow(output->physicalSize.x / MMPERINCH, 2) + pow(output->physicalSize.y / MMPERINCH, 2)); const auto PPI = DIAGONALPX / DIAGONALIN; @@ -1309,71 +1065,48 @@ float CMonitor::getDefaultScale() { return 1; } -static bool shouldWraparound(const WORKSPACEID id1, const WORKSPACEID id2) { - static auto PWORKSPACEWRAPAROUND = CConfigValue("animations:workspace_wraparound"); - - if (!*PWORKSPACEWRAPAROUND) - return false; - - WORKSPACEID lowestID = INT64_MAX; - WORKSPACEID highestID = INT64_MIN; - - for (auto const& w : g_pCompositor->getWorkspaces()) { - if (w->m_id < 0 || w->m_isSpecialWorkspace) - continue; - lowestID = std::min(w->m_id, lowestID); - highestID = std::max(w->m_id, highestID); - } - - return std::min(id1, id2) == lowestID && std::max(id1, id2) == highestID; -} - void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bool noMouseMove, bool noFocus) { if (!pWorkspace) return; - if (pWorkspace->m_isSpecialWorkspace) { - if (m_activeSpecialWorkspace != pWorkspace) { - Log::logger->log(Log::DEBUG, "changeworkspace on special, togglespecialworkspace to id {}", pWorkspace->m_id); + if (pWorkspace->m_bIsSpecialWorkspace) { + if (activeSpecialWorkspace != pWorkspace) { + Debug::log(LOG, "changeworkspace on special, togglespecialworkspace to id {}", pWorkspace->m_iID); setSpecialWorkspace(pWorkspace); } return; } - if (pWorkspace == m_activeWorkspace) + if (pWorkspace == activeWorkspace) return; - const auto POLDWORKSPACE = m_activeWorkspace; - m_activeWorkspace = pWorkspace; + const auto POLDWORKSPACE = activeWorkspace; + if (POLDWORKSPACE) + POLDWORKSPACE->m_bVisible = false; + pWorkspace->m_bVisible = true; - if (POLDWORKSPACE) { - POLDWORKSPACE->m_visible = false; - POLDWORKSPACE->m_events.activeChanged.emit(); - } - - pWorkspace->m_visible = true; + activeWorkspace = pWorkspace; if (!internal) { - const auto ANIMTOLEFT = POLDWORKSPACE && (shouldWraparound(pWorkspace->m_id, POLDWORKSPACE->m_id) ^ (pWorkspace->m_id > POLDWORKSPACE->m_id)); + const auto ANIMTOLEFT = POLDWORKSPACE && pWorkspace->m_iID > POLDWORKSPACE->m_iID; if (POLDWORKSPACE) - g_pDesktopAnimationManager->startAnimation(POLDWORKSPACE, CDesktopAnimationManager::ANIMATION_TYPE_OUT, ANIMTOLEFT); - g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, ANIMTOLEFT); + POLDWORKSPACE->startAnim(false, ANIMTOLEFT); + pWorkspace->startAnim(true, ANIMTOLEFT); // move pinned windows - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace == POLDWORKSPACE && w->m_pinned) - w->layoutTarget()->assignToSpace(pWorkspace->m_space); + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace == POLDWORKSPACE && w->m_bPinned) + w->moveToWorkspace(pWorkspace); } - if (!noFocus && !Desktop::focusState()->monitor()->m_activeSpecialWorkspace && - !(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) { + if (!noFocus && !g_pCompositor->m_pLastMonitor->activeSpecialWorkspace && + !(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_pMonitor == self)) { static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); - auto pWindow = pWorkspace->m_hasFullscreenWindow ? pWorkspace->getFullscreenWindow() : pWorkspace->getLastFocusedWindow(); + auto pWindow = pWorkspace->m_bHasFullscreenWindow ? pWorkspace->getFullscreenWindow() : pWorkspace->getLastFocusedWindow(); if (!pWindow) { if (*PFOLLOWMOUSE == 1) - pWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), - Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); + pWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (!pWindow) pWindow = pWorkspace->getTopLeftWindow(); @@ -1382,39 +1115,29 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo pWindow = pWorkspace->getFirstWindow(); } - Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->focusWindow(pWindow); } if (!noMouseMove) g_pInputManager->simulateMouseMovement(); - g_layoutManager->recalculateMonitor(m_self.lock()); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID); - g_pEventManager->postEvent(SHyprIPCEvent{"workspace", pWorkspace->m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{"workspacev2", std::format("{},{}", pWorkspace->m_id, pWorkspace->m_name)}); - Event::bus()->m_events.workspace.active.emit(pWorkspace); + g_pEventManager->postEvent(SHyprIPCEvent{"workspace", pWorkspace->m_szName}); + g_pEventManager->postEvent(SHyprIPCEvent{"workspacev2", std::format("{},{}", pWorkspace->m_iID, pWorkspace->m_szName)}); + EMIT_HOOK_EVENT("workspace", pWorkspace); } - // set all LSes as not above fullscreen on workspace changes - for (auto const& ls : g_pCompositor->m_layers) { - if (ls->m_monitor == m_self) - ls->m_aboveFullscreen = false; - } + g_pHyprRenderer->damageMonitor(self.lock()); - pWorkspace->m_events.activeChanged.emit(); + g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace); - g_pHyprRenderer->damageMonitor(m_self.lock()); - - g_pDesktopAnimationManager->setFullscreenFadeAnimation( - pWorkspace, pWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); - - g_pConfigManager->ensureVRR(m_self.lock()); + g_pConfigManager->ensureVRR(self.lock()); g_pCompositor->updateSuspendedStates(); - if (m_activeSpecialWorkspace) - g_pDesktopAnimationManager->setFullscreenFadeAnimation( - m_activeSpecialWorkspace, m_activeSpecialWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); + if (activeSpecialWorkspace) + g_pCompositor->updateFullscreenFadeOnWorkspace(activeSpecialWorkspace); } void CMonitor::changeWorkspace(const WORKSPACEID& id, bool internal, bool noMouseMove, bool noFocus) { @@ -1422,147 +1145,107 @@ void CMonitor::changeWorkspace(const WORKSPACEID& id, bool internal, bool noMous } void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { - if (m_activeSpecialWorkspace == pWorkspace) + if (activeSpecialWorkspace == pWorkspace) return; - const auto POLDSPECIAL = m_activeSpecialWorkspace; - - m_specialFade->setConfig(g_pConfigManager->getAnimationPropertyConfig(pWorkspace ? "specialWorkspaceIn" : "specialWorkspaceOut")); - *m_specialFade = pWorkspace ? 1.F : 0.F; - - g_pHyprRenderer->damageMonitor(m_self.lock()); + g_pHyprRenderer->damageMonitor(self.lock()); if (!pWorkspace) { // remove special if exists - if (m_activeSpecialWorkspace) { - m_activeSpecialWorkspace->m_visible = false; - g_pDesktopAnimationManager->startAnimation(m_activeSpecialWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_OUT, false); - g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + m_name}); - - // Reset layer surface state when closing special workspace - for (auto const& ls : g_pCompositor->m_layers) { - if (ls->m_monitor == m_self) - ls->m_aboveFullscreen = false; - } + if (activeSpecialWorkspace) { + activeSpecialWorkspace->m_bVisible = false; + activeSpecialWorkspace->startAnim(false, false); + g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + szName}); + g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + szName}); } - m_activeSpecialWorkspace.reset(); + activeSpecialWorkspace.reset(); - if (POLDSPECIAL) - POLDSPECIAL->m_events.activeChanged.emit(); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID); - g_layoutManager->recalculateMonitor(m_self.lock()); - - if (!(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) { - if (const auto PLAST = m_activeWorkspace->getLastFocusedWindow(); PLAST) - Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND); + if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_pMonitor == self)) { + if (const auto PLAST = activeWorkspace->getLastFocusedWindow(); PLAST) + g_pCompositor->focusWindow(PLAST); else g_pInputManager->refocus(); } - g_pDesktopAnimationManager->setFullscreenFadeAnimation( - m_activeWorkspace, m_activeWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); + g_pCompositor->updateFullscreenFadeOnWorkspace(activeWorkspace); - g_pConfigManager->ensureVRR(m_self.lock()); + g_pConfigManager->ensureVRR(self.lock()); g_pCompositor->updateSuspendedStates(); return; } - if (m_activeSpecialWorkspace) { - m_activeSpecialWorkspace->m_visible = false; - g_pDesktopAnimationManager->startAnimation(m_activeSpecialWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_OUT, false); + if (activeSpecialWorkspace) { + activeSpecialWorkspace->m_bVisible = false; + activeSpecialWorkspace->startAnim(false, false); } - bool wasActive = false; + bool animate = true; //close if open elsewhere - const auto PMONITORWORKSPACEOWNER = pWorkspace->m_monitor.lock(); - if (const auto PMWSOWNER = pWorkspace->m_monitor.lock(); PMWSOWNER && PMWSOWNER->m_activeSpecialWorkspace == pWorkspace) { - PMWSOWNER->m_activeSpecialWorkspace.reset(); - g_layoutManager->recalculateMonitor(PMWSOWNER); - g_pHyprRenderer->damageMonitor(PMWSOWNER); - g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMWSOWNER->m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMWSOWNER->m_name}); + const auto PMONITORWORKSPACEOWNER = pWorkspace->m_pMonitor.lock(); + if (const auto PMWSOWNER = pWorkspace->m_pMonitor.lock(); PMWSOWNER && PMWSOWNER->activeSpecialWorkspace == pWorkspace) { + PMWSOWNER->activeSpecialWorkspace.reset(); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMWSOWNER->ID); + g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMWSOWNER->szName}); + g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMWSOWNER->szName}); - // Reset layer surfaces on the old monitor when special workspace is stolen - for (auto const& ls : g_pCompositor->m_layers) { - if (ls->m_monitor == PMWSOWNER) - ls->m_aboveFullscreen = false; - } + const auto PACTIVEWORKSPACE = PMWSOWNER->activeWorkspace; + g_pCompositor->updateFullscreenFadeOnWorkspace(PACTIVEWORKSPACE); - const auto PACTIVEWORKSPACE = PMWSOWNER->m_activeWorkspace; - g_pDesktopAnimationManager->setFullscreenFadeAnimation(PACTIVEWORKSPACE, - PACTIVEWORKSPACE && PACTIVEWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : - CDesktopAnimationManager::ANIMATION_TYPE_OUT); - - wasActive = true; + animate = false; } // open special - pWorkspace->m_monitor = m_self; - m_activeSpecialWorkspace = pWorkspace; - m_activeSpecialWorkspace->m_visible = true; + pWorkspace->m_pMonitor = self; + activeSpecialWorkspace = pWorkspace; + activeSpecialWorkspace->m_bVisible = true; + if (animate) + pWorkspace->startAnim(true, true); - // Reset layer surface state when opening special workspace - for (auto const& ls : g_pCompositor->m_layers) { - if (ls->m_monitor == m_self) - ls->m_aboveFullscreen = false; - } - - if (POLDSPECIAL) - POLDSPECIAL->m_events.activeChanged.emit(); - - if (PMONITORWORKSPACEOWNER != m_self) - pWorkspace->m_events.monitorChanged.emit(); - - if (!wasActive) - pWorkspace->m_events.activeChanged.emit(); - - if (!wasActive) - g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, true); - - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace == pWorkspace) { - w->m_monitor = m_self; + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace == pWorkspace) { + w->m_pMonitor = self; w->updateSurfaceScaleTransformDetails(); w->setAnimationsToMove(); const auto MIDDLE = w->middle(); - if (w->m_isFloating && VECNOTINRECT(MIDDLE, m_position.x, m_position.y, m_position.x + m_size.x, m_position.y + m_size.y) && !w->isX11OverrideRedirect()) { - // if it's floating and the middle isn't on the current mon, move it to the center + if (w->m_bIsFloating && !VECINRECT(MIDDLE, vecPosition.x, vecPosition.y, vecPosition.x + vecSize.x, vecPosition.y + vecSize.y) && !w->isX11OverrideRedirect()) { + // if it's floating and the middle isnt on the current mon, move it to the center const auto PMONFROMMIDDLE = g_pCompositor->getMonitorFromVector(MIDDLE); - Vector2D pos = w->m_realPosition->goal(); - if (VECNOTINRECT(MIDDLE, PMONFROMMIDDLE->m_position.x, PMONFROMMIDDLE->m_position.y, PMONFROMMIDDLE->m_position.x + PMONFROMMIDDLE->m_size.x, - PMONFROMMIDDLE->m_position.y + PMONFROMMIDDLE->m_size.y)) { + Vector2D pos = w->m_vRealPosition->goal(); + if (!VECINRECT(MIDDLE, PMONFROMMIDDLE->vecPosition.x, PMONFROMMIDDLE->vecPosition.y, PMONFROMMIDDLE->vecPosition.x + PMONFROMMIDDLE->vecSize.x, + PMONFROMMIDDLE->vecPosition.y + PMONFROMMIDDLE->vecSize.y)) { // not on any monitor, center - pos = middle() / 2.f - w->m_realSize->goal() / 2.f; + pos = middle() / 2.f - w->m_vRealSize->goal() / 2.f; } else - pos = pos - PMONFROMMIDDLE->m_position + m_position; + pos = pos - PMONFROMMIDDLE->vecPosition + vecPosition; - w->layoutTarget()->setPositionGlobal(CBox{pos, w->layoutTarget()->position().size()}); + *w->m_vRealPosition = pos; + w->m_vPosition = pos; } } } - g_layoutManager->recalculateMonitor(m_self.lock()); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID); - if (!(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) { + if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_pMonitor == self)) { if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST) - Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->focusWindow(PLAST); else g_pInputManager->refocus(); } - g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_name + "," + m_name}); - g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", std::to_string(pWorkspace->m_id) + "," + pWorkspace->m_name + "," + m_name}); + g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName}); + g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", std::to_string(pWorkspace->m_iID) + "," + pWorkspace->m_szName + "," + szName}); - g_pHyprRenderer->damageMonitor(m_self.lock()); + g_pHyprRenderer->damageMonitor(self.lock()); - g_pDesktopAnimationManager->setFullscreenFadeAnimation( - pWorkspace, pWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT); + g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace); - g_pConfigManager->ensureVRR(m_self.lock()); + g_pConfigManager->ensureVRR(self.lock()); g_pCompositor->updateSuspendedStates(); } @@ -1572,369 +1255,123 @@ void CMonitor::setSpecialWorkspace(const WORKSPACEID& id) { } void CMonitor::moveTo(const Vector2D& pos) { - m_position = pos; + vecPosition = pos; +} + +SWorkspaceIDName CMonitor::getPrevWorkspaceIDName(const WORKSPACEID id) { + while (!prevWorkSpaces.empty()) { + const int PREVID = prevWorkSpaces.top(); + prevWorkSpaces.pop(); + if (PREVID == id) // skip same workspace + continue; + + // recheck if previous workspace's was moved to another monitor + const auto ws = g_pCompositor->getWorkspaceByID(PREVID); + if (ws && ws->monitorID() == ID) + return {.id = PREVID, .name = ws->m_szName}; + } + + return {.id = WORKSPACE_INVALID}; +} + +void CMonitor::addPrevWorkspaceID(const WORKSPACEID id) { + if (!prevWorkSpaces.empty() && prevWorkSpaces.top() == id) + return; + + prevWorkSpaces.emplace(id); } Vector2D CMonitor::middle() { - return m_position + m_size / 2.f; -} - -const Mat3x3& CMonitor::getTransformMatrix() { - return m_projMatrix; -} - -const Mat3x3& CMonitor::getScaleMatrix() { - return m_projOutputMatrix; + return vecPosition + vecSize / 2.f; } void CMonitor::updateMatrix() { - m_projMatrix = Mat3x3::identity(); - if (m_transform != WL_OUTPUT_TRANSFORM_NORMAL) - m_projMatrix.translate(m_pixelSize / 2.0).transform(Math::wlTransformToHyprutils(m_transform)).translate(-m_transformedSize / 2.0); - - m_projOutputMatrix = Mat3x3::outputProjection(m_pixelSize, HYPRUTILS_TRANSFORM_NORMAL); + projMatrix = Mat3x3::identity(); + if (transform != WL_OUTPUT_TRANSFORM_NORMAL) + projMatrix.translate(vecPixelSize / 2.0).transform(wlTransformToHyprutils(transform)).translate(-vecTransformedSize / 2.0); } WORKSPACEID CMonitor::activeWorkspaceID() { - return m_activeWorkspace ? m_activeWorkspace->m_id : 0; + return activeWorkspace ? activeWorkspace->m_iID : 0; } WORKSPACEID CMonitor::activeSpecialWorkspaceID() { - return m_activeSpecialWorkspace ? m_activeSpecialWorkspace->m_id : 0; + return activeSpecialWorkspace ? activeSpecialWorkspace->m_iID : 0; } CBox CMonitor::logicalBox() { - return {m_position, m_size}; -} - -CBox CMonitor::logicalBoxMinusReserved() { - return m_reservedArea.apply(logicalBox()); + return {vecPosition, vecSize}; } void CMonitor::scheduleDone() { - if (m_doneScheduled) + if (doneScheduled) return; - m_doneScheduled = true; + doneScheduled = true; - g_pEventLoopManager->doLater([M = m_self] { + g_pEventLoopManager->doLater([M = self] { if (!M) // if M is gone, we got destroyed, doesn't matter. return; - if (!PROTO::outputs.contains(M->m_name)) + if (!PROTO::outputs.contains(M->szName)) return; - PROTO::outputs.at(M->m_name)->sendDone(); - M->m_doneScheduled = false; + PROTO::outputs.at(M->szName)->sendDone(); + M->doneScheduled = false; }); } void CMonitor::setCTM(const Mat3x3& ctm_) { - m_ctm = ctm_; - m_ctmUpdated = true; - g_pCompositor->scheduleFrameForMonitor(m_self.lock(), Aquamarine::IOutput::scheduleFrameReason::AQ_SCHEDULE_NEEDS_FRAME); -} - -uint32_t CMonitor::isSolitaryBlocked(bool full) { - uint32_t reasons = 0; - - if (g_pHyprNotificationOverlay->hasAny()) { - reasons |= SC_NOTIFICATION; - if (!full) - return reasons; - } - - if (g_pHyprError->active() && Desktop::focusState()->monitor() == m_self) { - reasons |= SC_ERRORBAR; - if (!full) - return reasons; - } - - if (g_pSessionLockManager->isSessionLocked()) { - reasons |= SC_LOCK; - if (!full) - return reasons; - } - - const auto PWORKSPACE = m_activeWorkspace; - if (!PWORKSPACE) { - reasons |= SC_WORKSPACE; - return reasons; - } - - if (!PWORKSPACE->m_hasFullscreenWindow) { - reasons |= SC_WINDOWED; - if (!full) - return reasons; - } - - if (PROTO::data->dndActive()) { - reasons |= SC_DND; - if (!full) - return reasons; - } - - if (m_activeSpecialWorkspace) { - reasons |= SC_SPECIAL; - if (!full) - return reasons; - } - - if (PWORKSPACE->m_alpha->value() != 1.f) { - reasons |= SC_ALPHA; - if (!full) - return reasons; - } - - if (PWORKSPACE->m_renderOffset->value() != Vector2D{}) { - reasons |= SC_OFFSET; - if (!full) - return reasons; - } - - const auto PCANDIDATE = PWORKSPACE->getFullscreenWindow(); - - if (!PCANDIDATE) { - reasons |= SC_CANDIDATE; - return reasons; - } - - if (!PCANDIDATE->opaque()) { - reasons |= SC_OPAQUE; - if (!full) - return reasons; - } - - if (PCANDIDATE->m_realSize->value() != m_size || PCANDIDATE->m_realPosition->value() != m_position || PCANDIDATE->m_realPosition->isBeingAnimated() || - PCANDIDATE->m_realSize->isBeingAnimated()) { - reasons |= SC_TRANSFORM; - if (!full) - return reasons; - } - - if (!m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY].empty()) { - reasons |= SC_OVERLAYS; - if (!full) - return reasons; - } - - for (auto const& topls : m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { - if (topls->m_alpha->value() != 0.f) { - reasons |= SC_OVERLAYS; - if (!full) - return reasons; - } - } - - for (auto const& w : g_pCompositor->m_windows) { - if (w == PCANDIDATE || (!w->m_isMapped && !w->m_fadingOut) || w->isHidden()) - continue; - - if (w->workspaceID() == PCANDIDATE->workspaceID() && w->m_isFloating && w->m_createdOverFullscreen && w->visibleOnMonitor(m_self.lock())) { - reasons |= SC_FLOAT; - if (!full) - return reasons; - } - } - - for (auto const& ws : g_pCompositor->getWorkspaces()) { - if (ws->m_alpha->value() <= 0.F || !ws->m_isSpecialWorkspace || ws->m_monitor != m_self) - continue; - - reasons |= SC_WORKSPACES; - if (!full) - return reasons; - } - - // check if it did not open any subsurfaces or shit - if (!PCANDIDATE->getSolitaryResource()) - reasons |= SC_SURFACES; - - return reasons; -} - -void CMonitor::recheckSolitary() { - m_solitaryClient.reset(); // reset it, if we find one it will be set. - if (isSolitaryBlocked()) - return; - - m_solitaryClient = m_activeWorkspace->getFullscreenWindow(); -} - -uint8_t CMonitor::isTearingBlocked(bool full) { - uint8_t reasons = 0; - - static auto PTEARINGENABLED = CConfigValue("general:allow_tearing"); - - if (!m_tearingState.nextRenderTorn) { - reasons |= TC_NOT_TORN; - if (!full) - return reasons; - } - - if (!*PTEARINGENABLED) { - reasons |= TC_USER; - if (!full) { - Log::logger->log(Log::WARN, "Tearing commit requested but the master switch general:allow_tearing is off, ignoring"); - return reasons; - } - } - - if (g_pHyprOpenGL->m_renderData.mouseZoomFactor != 1.0) { - reasons |= TC_ZOOM; - if (!full) { - Log::logger->log(Log::WARN, "Tearing commit requested but scale factor is not 1, ignoring"); - return reasons; - } - } - - if (!m_tearingState.canTear) { - reasons |= TC_SUPPORT; - if (!full) { - Log::logger->log(Log::WARN, "Tearing commit requested but monitor doesn't support it, ignoring"); - return reasons; - } - } - - // TODO: remove this when kernel allows tearing + hw cursor updated - if (g_pPointerManager->hasVisibleHWCursor(m_self.lock())) - reasons |= TC_HW_CURSOR; - - if (m_solitaryClient.expired()) { - reasons |= TC_CANDIDATE; - return reasons; - } - - if (!m_solitaryClient->canBeTorn()) - reasons |= TC_WINDOW; - - return reasons; -} - -bool CMonitor::updateTearing() { - m_tearingState.activelyTearing = !isTearingBlocked(); - m_tearingState.nextRenderTorn = false; - return m_tearingState.activelyTearing; -} - -uint16_t CMonitor::isDSBlocked(bool full) { - uint16_t reasons = 0; - static auto PDIRECTSCANOUT = CConfigValue("render:direct_scanout"); - static auto PPASS = CConfigValue("render:cm_fs_passthrough"); - static auto PNONSHADER = CConfigValue("render:non_shader_cm"); - - if (*PDIRECTSCANOUT == 0) { - reasons |= DS_BLOCK_USER; - if (!full) - return reasons; - } - - if (*PDIRECTSCANOUT == 2) { - if (!m_activeWorkspace || !m_activeWorkspace->m_hasFullscreenWindow || m_activeWorkspace->m_fullscreenMode != FSMODE_FULLSCREEN) { - reasons |= DS_BLOCK_WINDOWED; - if (!full) - return reasons; - } else if (m_activeWorkspace->getFullscreenWindow()->getContentType() != CONTENT_TYPE_GAME) { - reasons |= DS_BLOCK_CONTENT; - if (!full) - return reasons; - } - } - - if (!m_mirrors.empty() || isMirror()) { - reasons |= DS_BLOCK_MIRROR; - if (!full) - return reasons; - } - - if (g_pHyprRenderer->m_directScanoutBlocked) { - reasons |= DS_BLOCK_RECORD; - if (!full) - return reasons; - } - - if (g_pPointerManager->softwareLockedFor(m_self.lock())) { - reasons |= DS_BLOCK_SW; - if (!full) - return reasons; - } - - const auto PCANDIDATE = m_solitaryClient.lock(); - if (!PCANDIDATE) { - reasons |= DS_BLOCK_CANDIDATE; - return reasons; - } - - const auto PSURFACE = PCANDIDATE->getSolitaryResource(); - if (!PSURFACE || !PSURFACE->m_current.texture || !PSURFACE->m_current.buffer) { - reasons |= DS_BLOCK_SURFACE; - return reasons; - } - - if (PSURFACE->m_current.bufferSize != m_pixelSize || PSURFACE->m_current.transform != m_transform) { - reasons |= DS_BLOCK_TRANSFORM; - if (!full) - return reasons; - } - - // we can't scanout shm buffers. - const auto params = PSURFACE->m_current.buffer->dmabuf(); - if (!params.success || !PSURFACE->m_current.texture->isDMA() /* dmabuf */) { - reasons |= DS_BLOCK_DMA; - if (!full) - return reasons; - } - - const bool surfaceIsHDR = PSURFACE->m_colorManagement.valid() && PSURFACE->m_colorManagement->isHDR(); - const bool surfaceIsScRGB = surfaceIsHDR && PSURFACE->m_colorManagement->isWindowsScRGB(); - - if (needsCM() && (*PNONSHADER != CM_NS_IGNORE || surfaceIsScRGB) && !canNoShaderCM() && - ((inHDR() && (*PPASS == 0 || !surfaceIsHDR || surfaceIsScRGB)) || (!inHDR() && (*PPASS != 1 || surfaceIsHDR)))) - reasons |= DS_BLOCK_CM; - - return reasons; + ctm = ctm_; + ctmUpdated = true; + g_pCompositor->scheduleFrameForMonitor(self.lock(), Aquamarine::IOutput::scheduleFrameReason::AQ_SCHEDULE_NEEDS_FRAME); } bool CMonitor::attemptDirectScanout() { - static const auto PSAME = CConfigValue("debug:ds_handle_same_buffer"); - static const auto PSAMEFIFO = CConfigValue("debug:ds_handle_same_buffer_fifo"); + if (!mirrors.empty() || isMirror() || g_pHyprRenderer->m_bDirectScanoutBlocked) + return false; // do not DS if this monitor is being mirrored. Will break the functionality. - const auto blockedReason = isDSBlocked(); - if (blockedReason) + if (g_pPointerManager->softwareLockedFor(self.lock())) return false; - const auto PCANDIDATE = m_solitaryClient.lock(); - const auto PSURFACE = PCANDIDATE->getSolitaryResource(); - const auto params = PSURFACE->m_current.buffer->dmabuf(); + const auto PCANDIDATE = solitaryClient.lock(); - Log::logger->log(Log::TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {} fmt: {} -> {} (mod {})", rc(PSURFACE.get()), - rc(PSURFACE->m_current.buffer.m_buffer.get()), m_drmFormat, params.format, params.modifier); + if (!PCANDIDATE) + return false; - auto PBUFFER = PSURFACE->m_current.buffer.m_buffer; + const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE); - // #TODO this entire bit needs figuring out, vrr goes down the drain without it - if (PBUFFER == m_output->state->state().buffer && *PSAME) { - PSURFACE->presentFeedback(Time::steadyNow(), m_self.lock()); + if (!PSURFACE || !PSURFACE->current.texture || !PSURFACE->current.buffer) + return false; - if (m_scanoutNeedsCursorUpdate) { - if (!m_state.test()) { - Log::logger->log(Log::TRACE, "attemptDirectScanout: failed basic test on cursor update"); + if (PSURFACE->current.bufferSize != vecPixelSize || PSURFACE->current.transform != transform) + return false; + + // we can't scanout shm buffers. + const auto params = PSURFACE->current.buffer->buffer->dmabuf(); + if (!params.success || !PSURFACE->current.texture->m_pEglImage /* dmabuf */) + return false; + + Debug::log(TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {}", (uintptr_t)PSURFACE.get(), (uintptr_t)PSURFACE->current.buffer->buffer.get()); + + auto PBUFFER = PSURFACE->current.buffer->buffer; + + if (PBUFFER == output->state->state().buffer) { + if (scanoutNeedsCursorUpdate) { + if (!state.test()) { + Debug::log(TRACE, "attemptDirectScanout: failed basic test"); return false; } - if (!m_output->commit()) { - Log::logger->log(Log::TRACE, "attemptDirectScanout: failed to commit cursor update"); - m_lastScanout.reset(); + if (!output->commit()) { + Debug::log(TRACE, "attemptDirectScanout: failed to commit cursor update"); + lastScanout.reset(); return false; } - m_scanoutNeedsCursorUpdate = false; + scanoutNeedsCursorUpdate = false; } - //#TODO this entire bit is bootleg deluxe, above bit is to not make vrr go down the drain, returning early here means fifo gets forever locked. - if (PSURFACE->m_fifo && !m_tearingState.activelyTearing && *PSAMEFIFO) - PSURFACE->m_stateQueue.unlockFirst(LOCK_REASON_FIFO); - return true; } @@ -1942,145 +1379,150 @@ bool CMonitor::attemptDirectScanout() { // and comes from the appropriate device. This may implode on multi-gpu!! // entering into scanout, so save monitor format - if (m_lastScanout.expired()) - m_prevDrmFormat = m_drmFormat; + if (lastScanout.expired()) + prevDrmFormat = drmFormat; - if (m_drmFormat != params.format) { - m_output->state->setFormat(params.format); - m_drmFormat = params.format; + if (drmFormat != params.format) { + output->state->setFormat(params.format); + drmFormat = params.format; } - m_output->state->setBuffer(PBUFFER); - Log::logger->log(Log::TRACE, "attemptDirectScanout: setting presentation mode"); - m_output->state->setPresentationMode(m_tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE : - Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC); + output->state->setBuffer(PBUFFER); + output->state->setPresentationMode(tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE : + Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC); - if (!m_state.test()) { - Log::logger->log(Log::TRACE, "attemptDirectScanout: failed basic test"); + if (!state.test()) { + Debug::log(TRACE, "attemptDirectScanout: failed basic test"); return false; } - PSURFACE->presentFeedback(Time::steadyNow(), m_self.lock()); + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + PSURFACE->presentFeedback(&now, self.lock()); - m_output->state->addDamage(PSURFACE->m_current.accumulateBufferDamage()); + output->state->addDamage(PSURFACE->current.accumulateBufferDamage()); + output->state->resetExplicitFences(); - // multigpu needs a fence to trigger fence syncing blits and also committing with the recreated dgpu fence - if (!DRM::sameGpu(m_output->getBackend()->preferredAllocator()->drmFD(), g_pCompositor->m_drm.fd) && g_pHyprOpenGL->explicitSyncSupported()) { - auto sync = CEGLSync::create(); + auto cleanup = CScopeGuard([this]() { output->state->resetExplicitFences(); }); - if (sync->fd().isValid()) { - m_inFence = sync->takeFd(); - m_output->state->setExplicitInFence(m_inFence.get()); - } else - m_output->state->resetExplicitFences(); // good luck. - } else - m_output->state->resetExplicitFences(); + auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(output); - // no need to do explicit sync here as surface current can only ever be ready to read + bool DOEXPLICIT = PSURFACE->syncobj && PSURFACE->current.buffer && PSURFACE->current.buffer->acquire && explicitOptions.explicitKMSEnabled; + if (DOEXPLICIT) { + // wait for surface's explicit fence if present + inFence = PSURFACE->current.buffer->acquire->exportAsFD(); + if (inFence.isValid()) { + Debug::log(TRACE, "attemptDirectScanout: setting IN_FENCE for aq to {}", inFence.get()); + output->state->setExplicitInFence(inFence.get()); + } else { + Debug::log(TRACE, "attemptDirectScanout: failed to acquire an sync file fd for aq IN_FENCE"); + DOEXPLICIT = false; + } + } - bool ok = m_output->commit(); + bool ok = output->commit(); + + if (!ok && DOEXPLICIT) { + Debug::log(TRACE, "attemptDirectScanout: EXPLICIT SYNC FAILED: commit() returned false. Resetting fences and retrying, might result in glitches."); + output->state->resetExplicitFences(); + + ok = output->commit(); + } if (!ok) { - Log::logger->log(Log::TRACE, "attemptDirectScanout: failed to scanout surface"); - m_lastScanout.reset(); + Debug::log(TRACE, "attemptDirectScanout: failed to scanout surface"); + lastScanout.reset(); return false; } - if (m_lastScanout.expired()) { - m_lastScanout = PCANDIDATE; - Log::logger->log(Log::DEBUG, "Entered a direct scanout to {:x}: \"{}\"", rc(PCANDIDATE.get()), PCANDIDATE->m_title); + if (lastScanout.expired()) { + lastScanout = PCANDIDATE; + Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle); } - m_scanoutNeedsCursorUpdate = false; + scanoutNeedsCursorUpdate = false; - if (!PBUFFER->lockedByBackend || PBUFFER->m_hlEvents.backendRelease) + if (!PBUFFER->lockedByBackend || PBUFFER->hlEvents.backendRelease) return true; // lock buffer while DRM/KMS is using it, then release it when page flip happens since DRM/KMS should be done by then // btw buffer's syncReleaser will take care of signaling release point, so we don't do that here PBUFFER->lock(); - PBUFFER->onBackendRelease([wb = WP{PBUFFER}] { - if (wb) - wb->unlock(); - }); + PBUFFER->onBackendRelease([PBUFFER]() { PBUFFER->unlock(); }); return true; } -void CMonitor::setDPMS(bool on) { - // Don't trigger animation if the target state is the same - if (m_dpmsStatus == on) - return; - - m_dpmsStatus = on; - m_events.dpmsChanged.emit(); - - if (on) { - // enable the monitor. Wait for the frame to be presented, then begin animation - m_dpmsBlackOpacity->setCallbackOnEnd(nullptr); - m_dpmsBlackOpacity->setValueAndWarp(1.F); - m_pendingDpmsAnimation = true; - m_pendingDpmsAnimationCounter = 0; - commitDPMSState(true); - } else { - // disable the monitor. Begin the animation, then do dpms on its end. - m_dpmsBlackOpacity->setCallbackOnEnd(nullptr); - m_dpmsBlackOpacity->setValueAndWarp(0.F); - *m_dpmsBlackOpacity = 1.F; - m_dpmsBlackOpacity->setCallbackOnEnd( - [this, self = m_self](auto) { - if (!self) - return; - - // commit DPMS to disable the monitor, it's fully black now - commitDPMSState(false); - }, - true); - } -} - -void CMonitor::commitDPMSState(bool state) { - m_output->state->resetExplicitFences(); - m_output->state->setEnabled(state); - - if (!m_state.commit()) { - Log::logger->log(Log::ERR, "Couldn't commit output {} for DPMS = {}, will retry.", m_name, state); - - // retry in 2 frames. This could happen when the DRM backend rejects our commit - // because disable + enable were sent almost instantly - - m_dpmsRetryTimer = makeShared( - std::chrono::milliseconds(2000 / sc(m_refreshRate)), - [this, self = m_self](SP s, void* d) { - if (!self) - return; - - m_output->state->resetExplicitFences(); - m_output->state->setEnabled(m_dpmsStatus); - if (!m_state.commit()) { - Log::logger->log(Log::ERR, "Couldn't retry committing output {} for DPMS = {}", m_name, m_dpmsStatus); - return; - } - - m_dpmsRetryTimer.reset(); - }, - nullptr); - g_pEventLoopManager->addTimer(m_dpmsRetryTimer); - - return; - } - - if (state) - g_pHyprRenderer->damageMonitor(m_self.lock()); -} - void CMonitor::debugLastPresentation(const std::string& message) { - Log::logger->log(Log::TRACE, "{} (last presentation {} - {} fps)", message, m_lastPresentationTimer.getMillis(), - m_lastPresentationTimer.getMillis() > 0 ? 1000.0f / m_lastPresentationTimer.getMillis() : 0.0f); + Debug::log(TRACE, "{} (last presentation {} - {} fps)", message, lastPresentationTimer.getMillis(), + lastPresentationTimer.getMillis() > 0 ? 1000.0f / lastPresentationTimer.getMillis() : 0.0f); +} + +void CMonitor::onMonitorFrame() { + if ((g_pCompositor->m_pAqBackend->hasSession() && !g_pCompositor->m_pAqBackend->session->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) { + Debug::log(WARN, "Attempted to render frame on inactive session!"); + + if (g_pCompositor->m_bUnsafeState && std::ranges::any_of(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& m) { + return m->output != g_pCompositor->m_pUnsafeOutput->output; + })) { + // restore from unsafe state + g_pCompositor->leaveUnsafeState(); + } + + return; // cannot draw on session inactive (different tty) + } + + if (!m_bEnabled) + return; + + g_pHyprRenderer->recheckSolitaryForMonitor(self.lock()); + + tearingState.busy = false; + + if (tearingState.activelyTearing && solitaryClient.lock() /* can be invalidated by a recheck */) { + + if (!tearingState.frameScheduledWhileBusy) + return; // we did not schedule a frame yet to be displayed, but we are tearing. Why render? + + tearingState.nextRenderTorn = true; + tearingState.frameScheduledWhileBusy = false; + } + + static auto PENABLERAT = CConfigValue("misc:render_ahead_of_time"); + static auto PRATSAFE = CConfigValue("misc:render_ahead_safezone"); + + lastPresentationTimer.reset(); + + if (*PENABLERAT && !tearingState.nextRenderTorn) { + if (!RATScheduled) { + // render + g_pHyprRenderer->renderMonitor(self.lock()); + } + + RATScheduled = false; + + const auto& [avg, max, min] = g_pHyprRenderer->getRenderTimes(self.lock()); + + if (max + *PRATSAFE > 1000.0 / refreshRate) + return; + + const auto MSLEFT = 1000.0 / refreshRate - lastPresentationTimer.getMillis(); + + RATScheduled = true; + + const auto ESTRENDERTIME = std::ceil(avg + *PRATSAFE); + const auto TIMETOSLEEP = std::floor(MSLEFT - ESTRENDERTIME); + + if (MSLEFT < 1 || MSLEFT < ESTRENDERTIME || TIMETOSLEEP < 1) + g_pHyprRenderer->renderMonitor(self.lock()); + else + wl_event_source_timer_update(renderTimer, TIMETOSLEEP); + } else + g_pHyprRenderer->renderMonitor(self.lock()); } void CMonitor::onCursorMovedOnMonitor() { - if (!m_tearingState.activelyTearing || !m_solitaryClient || !g_pHyprRenderer->shouldRenderCursor()) + if (!tearingState.activelyTearing || !solitaryClient || !g_pHyprRenderer->shouldRenderCursor()) return; // submit a frame immediately. This will only update the cursor pos. @@ -2088,251 +1530,47 @@ void CMonitor::onCursorMovedOnMonitor() { // output->state->addDamage(CRegion{}); // output->state->setPresentationMode(Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE); // if (!output->commit()) - // Log::logger->log(Log::ERR, "onCursorMovedOnMonitor: tearing and wanted to update cursor, failed."); + // Debug::log(ERR, "onCursorMovedOnMonitor: tearing and wanted to update cursor, failed."); // FIXME: try to do the above. We currently can't just render because drm is a fucking bitch // and throws a "nO pRoP cAn Be ChAnGeD dUrInG AsYnC fLiP" on crtc_x // this will throw too but fix it if we use sw cursors - m_tearingState.frameScheduledWhileBusy = true; + tearingState.frameScheduledWhileBusy = true; } -bool CMonitor::supportsWideColor() { - switch (m_supportsWideColor) { - case -1: return false; - case 1: return true; - default: return m_output->parsedEDID.supportsBT2020; - } -} - -bool CMonitor::supportsHDR() { - if (!supportsWideColor()) - return false; - - switch (m_supportsHDR) { - case -1: return false; - case 1: return true; - default: return m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->supportsPQ : false; - } -} - -float CMonitor::minLuminance(float defaultValue) { - return m_minLuminance >= 0 ? m_minLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance : defaultValue); -} - -int CMonitor::maxLuminance(int defaultValue) { - return m_maxLuminance >= 0 ? m_maxLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance : defaultValue); -} - -int CMonitor::maxAvgLuminance(int defaultValue) { - return m_maxAvgLuminance >= 0 ? m_maxAvgLuminance : - (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance : defaultValue); -} - -float CMonitor::maxFALL() { - return m_maxAvgLuminance >= 0 ? m_maxAvgLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance : 0); -} - -float CMonitor::maxCLL() { - return m_maxLuminance >= 0 ? m_maxLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance : 0); -} - -bool CMonitor::wantsWideColor() { - return supportsWideColor() && (wantsHDR() || m_imageDescription->value().primariesNamed == CM_PRIMARIES_BT2020); -} - -bool CMonitor::wantsHDR() { - return supportsHDR() && inHDR(); -} - -bool CMonitor::inHDR() { - return m_output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2; -} - -bool CMonitor::inFullscreenMode() { - // Check special workspace first since it renders on top of regular workspaces - if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) - return true; - return m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN; -} - -PHLWINDOW CMonitor::getFullscreenWindow() { - // Check special workspace first since it renders on top of regular workspaces - if (m_activeSpecialWorkspace && m_activeSpecialWorkspace->m_hasFullscreenWindow && m_activeSpecialWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) - return m_activeSpecialWorkspace->getFullscreenWindow(); - if (m_activeWorkspace && m_activeWorkspace->m_hasFullscreenWindow && m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) - return m_activeWorkspace->getFullscreenWindow(); - return nullptr; -} - -std::optional CMonitor::getFSImageDescription() { - if (!inFullscreenMode()) - return {}; - - const auto FS_WINDOW = getFullscreenWindow(); - if (!FS_WINDOW) - return {}; - - const auto ROOT_SURF = FS_WINDOW->wlSurface()->resource(); - const auto SURF = ROOT_SURF->findWithCM(); - return SURF ? NColorManagement::CImageDescription::from(SURF->m_colorManagement->imageDescription()) : DEFAULT_IMAGE_DESCRIPTION; -} - -NColorManagement::SPCPRimaries CMonitor::getMasteringPrimaries() { - return m_output->parsedEDID.chromaticityCoords.has_value() ? - NColorManagement::SPCPRimaries{ - .red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y}, - .green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y}, - .blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y}, - .white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y}, - } : - NColorManagement::SPCPRimaries{}; -} - -NColorManagement::SImageDescription::SPCMasteringLuminances CMonitor::getMasteringLuminances() { - return { - .min = m_minLuminance >= 0 ? m_minLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance : 0), - .max = m_maxLuminance >= 0 ? m_maxLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance : 0), - }; -} - -bool CMonitor::needsCM() { - const auto SRC_DESC = getFSImageDescription(); - return SRC_DESC.has_value() && SRC_DESC.value() != m_imageDescription; -} - -// TODO support more drm properties -bool CMonitor::canNoShaderCM() { - static auto PNONSHADER = CConfigValue("render:non_shader_cm"); - if (*PNONSHADER == CM_NS_DISABLE) - return false; - - const auto SRC_DESC = getFSImageDescription(); - if (!SRC_DESC.has_value()) - return false; - - if (SRC_DESC.value() == m_imageDescription) - return true; // no CM needed - - const auto SRC_DESC_VALUE = SRC_DESC.value()->value(); - - if (m_imageDescription->value().icc.present) - return false; - - const auto sdrEOTF = NTransferFunction::fromConfig(); - // only primaries differ - return ( - (SRC_DESC_VALUE.transferFunction == m_imageDescription->value().transferFunction || - (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && SRC_DESC_VALUE.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB && - m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22)) && - SRC_DESC_VALUE.transferFunctionPower == m_imageDescription->value().transferFunctionPower && - (!inHDR() || SRC_DESC_VALUE.luminances == m_imageDescription->value().luminances) - // not used by shaders atm - // && SRC_DESC_VALUE.masteringLuminances == m_imageDescription->value().masteringLuminances && SRC_DESC_VALUE.maxCLL == m_imageDescription->value().maxCLL && SRC_DESC_VALUE.maxFALL == m_imageDescription->value().maxFALL - ); -} - -bool CMonitor::doesNoShaderCM() { - return m_noShaderCTM; -} - -static std::vector resampleInterleavedToKms(const SVCGTTable16& t, size_t gammaSize) { - std::vector out; - out.resize(gammaSize * 3); - - // - auto sample = [&](int c, float x) -> uint16_t { - const float maxX = t.entries - 1; - x = std::clamp(x, 0.F, maxX); - - const size_t i0 = (size_t)std::floor(x); - const size_t i1 = std::min(i0 + 1, (size_t)t.entries - 1); - const float f = x - sc(i0); - - const float v0 = sc(t.ch[c][i0]); - const float v1 = sc(t.ch[c][i1]); - const float v = v0 + ((v1 - v0) * f); - - int64_t vi = std::round(v); - vi = std::clamp(vi, sc(0), sc(65535)); - return sc(vi); - }; - - for (size_t i = 0; i < gammaSize; ++i) { - float x = sc(i) * sc(t.entries - 1) / sc(gammaSize - 1); - - const uint16_t r = sample(0, x); - const uint16_t g = sample(1, x); - const uint16_t b = sample(2, x); - - out[i * 3 + 0] = r; - out[i * 3 + 1] = g; - out[i * 3 + 2] = b; - } - - return out; -} - -void CMonitor::updateVCGTRamps() { - auto gammaSize = m_output->getGammaSize(); - - if (gammaSize <= 10) { - Log::logger->log(Log::DEBUG, "CMonitor::updateVCGTRamps: skipping, no gamma ramp for output"); - return; - } - - if (!m_imageDescription->value().icc.vcgt) { - if (m_vcgtRampsSet) - m_output->state->setGammaLut({}); - - m_vcgtRampsSet = false; - return; - } - - // build table - auto table = resampleInterleavedToKms(*m_imageDescription->value().icc.vcgt, gammaSize); - - m_output->state->setGammaLut(table); - - m_vcgtRampsSet = true; -} - -bool CMonitor::gammaRampsInUse() { - return m_vcgtRampsSet; -} - -CMonitorState::CMonitorState(CMonitor* owner) : m_owner(owner) { +CMonitorState::CMonitorState(CMonitor* owner) : m_pOwner(owner) { ; } void CMonitorState::ensureBufferPresent() { - const auto STATE = m_owner->m_output->state->state(); + const auto STATE = m_pOwner->output->state->state(); if (!STATE.enabled) { - Log::logger->log(Log::TRACE, "CMonitorState::ensureBufferPresent: Ignoring, monitor is not enabled"); + Debug::log(TRACE, "CMonitorState::ensureBufferPresent: Ignoring, monitor is not enabled"); return; } if (STATE.buffer) { - if (const auto params = STATE.buffer->dmabuf(); params.success && params.format == m_owner->m_drmFormat) + if (const auto params = STATE.buffer->dmabuf(); params.success && params.format == m_pOwner->drmFormat) return; } // this is required for modesetting being possible and might be missing in case of first tests in the renderer // where we test modes and buffers - Log::logger->log(Log::DEBUG, "CMonitorState::ensureBufferPresent: no buffer or mismatched format, attaching one from the swapchain for modeset being possible"); - m_owner->m_output->state->setBuffer(m_owner->m_output->swapchain->next(nullptr)); - m_owner->m_output->swapchain->rollback(); // restore the counter, don't advance the swapchain + Debug::log(LOG, "CMonitorState::ensureBufferPresent: no buffer or mismatched format, attaching one from the swapchain for modeset being possible"); + m_pOwner->output->state->setBuffer(m_pOwner->output->swapchain->next(nullptr)); + m_pOwner->output->swapchain->rollback(); // restore the counter, don't advance the swapchain } bool CMonitorState::commit() { if (!updateSwapchain()) return false; - Event::bus()->m_events.monitor.preCommit.emit(m_owner->m_self.lock()); + EMIT_HOOK_EVENT("preMonitorCommit", m_pOwner->self.lock()); ensureBufferPresent(); - bool ret = m_owner->m_output->commit(); + bool ret = m_pOwner->output->commit(); return ret; } @@ -2342,20 +1580,20 @@ bool CMonitorState::test() { ensureBufferPresent(); - return m_owner->m_output->test(); + return m_pOwner->output->test(); } bool CMonitorState::updateSwapchain() { - auto options = m_owner->m_output->swapchain->currentOptions(); - const auto& STATE = m_owner->m_output->state->state(); + auto options = m_pOwner->output->swapchain->currentOptions(); + const auto& STATE = m_pOwner->output->state->state(); const auto& MODE = STATE.mode ? STATE.mode : STATE.customMode; if (!MODE) { - Log::logger->log(Log::WARN, "updateSwapchain: No mode?"); + Debug::log(WARN, "updateSwapchain: No mode?"); return true; } - options.format = m_owner->m_drmFormat; + options.format = m_pOwner->drmFormat; options.scanout = true; - options.length = 3; + options.length = 2; options.size = MODE->pixelSize; - return m_owner->m_output->swapchain->reconfigure(options); + return m_pOwner->output->swapchain->reconfigure(options); } diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 7467467a..a7d97c8d 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -7,75 +7,57 @@ #include "MiscFunctions.hpp" #include "WLClasses.hpp" #include -#include "AnimatedVariable.hpp" -#include "CMType.hpp" #include -#include "MonitorZoomController.hpp" -#include "time/Timer.hpp" +#include "Timer.hpp" #include "math/Math.hpp" -#include "../desktop/reserved/ReservedArea.hpp" #include -#include "cm/ColorManagement.hpp" +#include "../protocols/types/ColorManagement.hpp" #include "signal/Signal.hpp" #include "DamageRing.hpp" #include #include #include -#include "../helpers/TransferFunction.hpp" - -class CMonitorFrameScheduler; - // Enum for the different types of auto directions, e.g. auto-left, auto-up. enum eAutoDirs : uint8_t { DIR_AUTO_NONE = 0, /* None will be treated as right. */ DIR_AUTO_UP, DIR_AUTO_DOWN, DIR_AUTO_LEFT, - DIR_AUTO_RIGHT, - DIR_AUTO_CENTER_UP, - DIR_AUTO_CENTER_DOWN, - DIR_AUTO_CENTER_LEFT, - DIR_AUTO_CENTER_RIGHT + DIR_AUTO_RIGHT +}; + +enum eCMType : uint8_t { + CM_AUTO = 0, // subject to change. srgb for 8bpc, wide for 10bpc if supported + CM_SRGB, // default, sRGB primaries + CM_WIDE, // wide color gamut, BT2020 primaries + CM_EDID, // primaries from edid (known to be inaccurate) + CM_HDR, // wide color gamut and HDR PQ transfer function + CM_HDR_EDID, // same as CM_HDR with edid primaries }; struct SMonitorRule { - eAutoDirs autoDir = DIR_AUTO_NONE; - std::string name = ""; - Vector2D resolution = Vector2D(1280, 720); - Vector2D offset = Vector2D(0, 0); - float scale = 1; - float refreshRate = 60; // Hz - bool disabled = false; - wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; - std::string mirrorOf = ""; - bool enable10bit = false; - NCMType::eCMType cmType = NCMType::CM_SRGB; - NTransferFunction::eTF sdrEotf = NTransferFunction::TF_DEFAULT; - float sdrSaturation = 1.0f; // SDR -> HDR - float sdrBrightness = 1.0f; // SDR -> HDR - Desktop::CReservedArea reservedArea; - std::string iccFile; - - int supportsWideColor = 0; // 0 - auto, 1 - force enable, -1 - force disable - int supportsHDR = 0; // 0 - auto, 1 - force enable, -1 - force disable - float sdrMinLuminance = 0.2f; // SDR -> HDR - int sdrMaxLuminance = 80; // SDR -> HDR - - // Incorrect values will result in reduced luminance range or incorrect tonemapping. Shouldn't damage the HW. Use with care in case of a faulty monitor firmware. - float minLuminance = -1.0f; // >= 0 overrides EDID - int maxLuminance = -1; // >= 0 overrides EDID - int maxAvgLuminance = -1; // >= 0 overrides EDID - - drmModeModeInfo drmMode = {}; - std::optional vrr; + eAutoDirs autoDir = DIR_AUTO_NONE; + std::string name = ""; + Vector2D resolution = Vector2D(1280, 720); + Vector2D offset = Vector2D(0, 0); + float scale = 1; + float refreshRate = 60; // Hz + bool disabled = false; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + std::string mirrorOf = ""; + bool enable10bit = false; + eCMType cmType = CM_SRGB; + float sdrSaturation = 1.0f; // SDR -> HDR + float sdrBrightness = 1.0f; // SDR -> HDR + drmModeModeInfo drmMode = {}; + std::optional vrr; }; class CMonitor; class CSyncTimeline; class CEGLSync; -class CEventLoopTimer; class CMonitorState { public: @@ -89,7 +71,7 @@ class CMonitorState { private: void ensureBufferPresent(); - CMonitor* m_owner = nullptr; + CMonitor* m_pOwner = nullptr; }; class CMonitor { @@ -97,109 +79,87 @@ class CMonitor { CMonitor(SP output); ~CMonitor(); - Vector2D m_position = Vector2D(-1, -1); // means unset - Vector2D m_xwaylandPosition = Vector2D(-1, -1); // means unset - eAutoDirs m_autoDir = DIR_AUTO_NONE; - Vector2D m_size = Vector2D(0, 0); - Vector2D m_pixelSize = Vector2D(0, 0); - Vector2D m_transformedSize = Vector2D(0, 0); + Vector2D vecPosition = Vector2D(-1, -1); // means unset + Vector2D vecXWaylandPosition = Vector2D(-1, -1); // means unset + Vector2D vecSize = Vector2D(0, 0); + Vector2D vecPixelSize = Vector2D(0, 0); + Vector2D vecTransformedSize = Vector2D(0, 0); - MONITORID m_id = MONITOR_INVALID; - PHLWORKSPACE m_activeWorkspace = nullptr; - PHLWORKSPACE m_activeSpecialWorkspace = nullptr; - float m_setScale = 1; // scale set by cfg - float m_scale = 1; // real scale + bool primary = false; - std::string m_name = ""; - std::string m_description = ""; - std::string m_shortDescription = ""; + MONITORID ID = MONITOR_INVALID; + PHLWORKSPACE activeWorkspace = nullptr; + PHLWORKSPACE activeSpecialWorkspace = nullptr; + float setScale = 1; // scale set by cfg + float scale = 1; // real scale - drmModeModeInfo m_customDrmMode = {}; + std::string szName = ""; + std::string szDescription = ""; + std::string szShortDescription = ""; - Desktop::CReservedArea m_reservedArea; + Vector2D vecReservedTopLeft = Vector2D(0, 0); + Vector2D vecReservedBottomRight = Vector2D(0, 0); - CMonitorState m_state; - CDamageRing m_damage; + drmModeModeInfo customDrmMode = {}; - SP m_output; - float m_refreshRate = 60; // Hz - int m_forceFullFrames = 0; - bool m_scheduledRecalc = false; - wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL; - float m_xwaylandScale = 1.f; + CMonitorState state; + CDamageRing damage; - std::optional m_forceSize; - SP m_currentMode; - SP m_cursorSwapchain; - uint32_t m_drmFormat = DRM_FORMAT_INVALID; - uint32_t m_prevDrmFormat = DRM_FORMAT_INVALID; + SP output; + float refreshRate = 60; // Hz + int forceFullFrames = 0; + bool scheduledRecalc = false; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + float xwaylandScale = 1.f; + Mat3x3 projMatrix; + std::optional forceSize; + SP currentMode; + SP cursorSwapchain; + uint32_t drmFormat = DRM_FORMAT_INVALID; + uint32_t prevDrmFormat = DRM_FORMAT_INVALID; - CMonitorZoomController m_zoomController; + bool dpmsStatus = true; + bool vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. + bool enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. + eCMType cmType = CM_SRGB; + float sdrSaturation = 1.0f; + float sdrBrightness = 1.0f; + bool createdByUser = false; + bool isUnsafeFallback = false; - bool m_dpmsStatus = true; - bool m_vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. - bool m_enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. - NCMType::eCMType m_cmType = NCMType::CM_SRGB; - NTransferFunction::eTF m_sdrEotf = NTransferFunction::TF_DEFAULT; - float m_sdrSaturation = 1.0f; - float m_sdrBrightness = 1.0f; - float m_sdrMinLuminance = 0.2f; - int m_sdrMaxLuminance = 80; - bool m_createdByUser = false; - bool m_isUnsafeFallback = false; + bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after + bool renderingActive = false; - SP m_dpmsRetryTimer; + wl_event_source* renderTimer = nullptr; // for RAT + bool RATScheduled = false; + CTimer lastPresentationTimer; - bool m_pendingFrame = false; // if we schedule a frame during rendering, reschedule it after - bool m_renderingActive = false; + bool isBeingLeased = false; - bool m_ratsScheduled = false; - CTimer m_lastPresentationTimer; - - bool m_isBeingLeased = false; - - SMonitorRule m_activeMonitorRule; + SMonitorRule activeMonitorRule; // explicit sync - Hyprutils::OS::CFileDescriptor m_inFence; // TODO: remove when aq uses CFileDescriptor + SP inTimeline; + Hyprutils::OS::CFileDescriptor inFence; + SP eglSync; + uint64_t inTimelinePoint = 0; - PHLMONITORREF m_self; - - UP m_frameScheduler; + PHLMONITORREF self; // mirroring - PHLMONITORREF m_mirrorOf; - std::vector m_mirrors; + PHLMONITORREF pMirrorOf; + std::vector mirrors; // ctm - Mat3x3 m_ctm = Mat3x3::identity(); - bool m_ctmUpdated = false; + Mat3x3 ctm = Mat3x3::identity(); + bool ctmUpdated = false; // for tearing - PHLWINDOWREF m_solitaryClient; + PHLWINDOWREF solitaryClient; // for direct scanout - PHLWINDOWREF m_lastScanout; - bool m_directScanoutIsActive = false; // for cleanup logic. m_lastScanout.expired() can become true before the DS cleanup if client crashes/exits while DS is active. - bool m_scanoutNeedsCursorUpdate = false; - - // for special fade/blur - PHLANIMVAR m_specialFade; - - // for dpms off anim - PHLANIMVAR m_dpmsBlackOpacity; - bool m_pendingDpmsAnimation = false; - int m_pendingDpmsAnimationCounter = 0; - - PHLANIMVAR m_cursorZoom; - - // for fading in the wallpaper because it doesn't happen instantly (it's loaded async) - PHLANIMVAR m_backgroundOpacity; - - // for initial zoom anim - PHLANIMVAR m_zoomAnimProgress; - CTimer m_newMonitorAnimTimer; - int m_zoomAnimFrameCounter = 0; + PHLWINDOWREF lastScanout; + bool scanoutNeedsCursorUpdate = false; struct { bool canTear = false; @@ -208,179 +168,69 @@ class CMonitor { bool busy = false; bool frameScheduledWhileBusy = false; - } m_tearingState; + } tearingState; struct { - CSignalT<> commit; - CSignalT<> destroy; - CSignalT<> connect; - CSignalT<> disconnect; - CSignalT<> dpmsChanged; - CSignalT<> modeChanged; - CSignalT<> presented; - } m_events; + CSignal destroy; + CSignal connect; + CSignal disconnect; + CSignal dpmsChanged; + CSignal modeChanged; + } events; - std::array, 4> m_layerSurfaceLayers; - - // keep in sync with HyprCtl - enum eDSBlockReason : uint16_t { - DS_OK = 0, - - DS_BLOCK_UNKNOWN = (1 << 0), - DS_BLOCK_USER = (1 << 1), - DS_BLOCK_WINDOWED = (1 << 2), - DS_BLOCK_CONTENT = (1 << 3), - DS_BLOCK_MIRROR = (1 << 4), - DS_BLOCK_RECORD = (1 << 5), - DS_BLOCK_SW = (1 << 6), - DS_BLOCK_CANDIDATE = (1 << 7), - DS_BLOCK_SURFACE = (1 << 8), - DS_BLOCK_TRANSFORM = (1 << 9), - DS_BLOCK_DMA = (1 << 10), - DS_BLOCK_FAILED = (1 << 11), - DS_BLOCK_CM = (1 << 12), - - DS_CHECKS_COUNT = 14, - }; - - // keep in sync with HyprCtl - enum eSolitaryCheck : uint32_t { - SC_OK = 0, - - SC_UNKNOWN = (1 << 0), - SC_NOTIFICATION = (1 << 1), - SC_LOCK = (1 << 2), - SC_WORKSPACE = (1 << 3), - SC_WINDOWED = (1 << 4), - SC_DND = (1 << 5), - SC_SPECIAL = (1 << 6), - SC_ALPHA = (1 << 7), - SC_OFFSET = (1 << 8), - SC_CANDIDATE = (1 << 9), - SC_OPAQUE = (1 << 10), - SC_TRANSFORM = (1 << 11), - SC_OVERLAYS = (1 << 12), - SC_FLOAT = (1 << 13), - SC_WORKSPACES = (1 << 14), - SC_SURFACES = (1 << 15), - SC_ERRORBAR = (1 << 16), - - SC_CHECKS_COUNT = 17, - }; - - // keep in sync with HyprCtl - enum eTearingCheck : uint8_t { - TC_OK = 0, - - TC_UNKNOWN = (1 << 0), - TC_NOT_TORN = (1 << 1), - TC_USER = (1 << 2), - TC_ZOOM = (1 << 3), - TC_SUPPORT = (1 << 4), - TC_CANDIDATE = (1 << 5), - TC_WINDOW = (1 << 6), - TC_HW_CURSOR = (1 << 7), - - TC_CHECKS_COUNT = 8, - }; + std::array, 4> m_aLayerSurfaceLayers; // methods - void onConnect(bool noRule); - void onDisconnect(bool destroy = false); - void applyCMType(NCMType::eCMType cmType, NTransferFunction::eTF cmSdrEotf); - bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false); - void addDamage(const pixman_region32_t* rg); - void addDamage(const CRegion& rg); - void addDamage(const CBox& box); - bool shouldSkipScheduleFrameOnMouseEvent(); - void setMirror(const std::string&); - bool isMirror(); - bool matchesStaticSelector(const std::string& selector) const; - float getDefaultScale(); - void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false); - void changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false); - void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace); - void setSpecialWorkspace(const WORKSPACEID& id); - void moveTo(const Vector2D& pos); - Vector2D middle(); - WORKSPACEID activeWorkspaceID(); - WORKSPACEID activeSpecialWorkspaceID(); - CBox logicalBox(); - CBox logicalBoxMinusReserved(); - void scheduleDone(); - uint32_t isSolitaryBlocked(bool full = false); - void recheckSolitary(); - uint8_t isTearingBlocked(bool full = false); - bool updateTearing(); - uint16_t isDSBlocked(bool full = false); - bool attemptDirectScanout(); - void setCTM(const Mat3x3& ctm); - void onCursorMovedOnMonitor(); - void setDPMS(bool on); + void onConnect(bool noRule); + void onDisconnect(bool destroy = false); + bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false); + void addDamage(const pixman_region32_t* rg); + void addDamage(const CRegion& rg); + void addDamage(const CBox& box); + bool shouldSkipScheduleFrameOnMouseEvent(); + void setMirror(const std::string&); + bool isMirror(); + bool matchesStaticSelector(const std::string& selector) const; + float getDefaultScale(); + void changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false); + void changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false); + void setSpecialWorkspace(const PHLWORKSPACE& pWorkspace); + void setSpecialWorkspace(const WORKSPACEID& id); + void moveTo(const Vector2D& pos); + Vector2D middle(); + void updateMatrix(); + WORKSPACEID activeWorkspaceID(); + WORKSPACEID activeSpecialWorkspaceID(); + CBox logicalBox(); + void scheduleDone(); + bool attemptDirectScanout(); + void setCTM(const Mat3x3& ctm); + void onCursorMovedOnMonitor(); - void debugLastPresentation(const std::string& message); + void debugLastPresentation(const std::string& message); + void onMonitorFrame(); - bool supportsWideColor(); - bool supportsHDR(); - float minLuminance(float defaultValue = 0); - int maxLuminance(int defaultValue = 80); - int maxAvgLuminance(int defaultValue = 80); - float maxFALL(); - float maxCLL(); - - bool wantsWideColor(); - bool wantsHDR(); - - bool inHDR(); - bool gammaRampsInUse(); - - // - const Mat3x3& getTransformMatrix(); - const Mat3x3& getScaleMatrix(); - - /// Has an active workspace with a real fullscreen window (includes special workspace) - bool inFullscreenMode(); - /// Get fullscreen window from active or special workspace - PHLWINDOW getFullscreenWindow(); - std::optional getFSImageDescription(); - - NColorManagement::SPCPRimaries getMasteringPrimaries(); - NColorManagement::SImageDescription::SPCMasteringLuminances getMasteringLuminances(); - - bool needsCM(); - /// Can do CM without shader - bool canNoShaderCM(); - bool doesNoShaderCM(); - - bool m_enabled = false; - bool m_renderingInitPassed = false; - - PHLWINDOWREF m_previousFSWindow; - bool m_needsHDRupdate = false; - - NColorManagement::PImageDescription m_imageDescription = NColorManagement::CImageDescription::from(NColorManagement::SImageDescription{}); - bool m_noShaderCTM = false; // sets drm CTM, restore needed + bool m_bEnabled = false; + bool m_bRenderingInitPassed = false; + WP m_previousFSWindow; + NColorManagement::SImageDescription imageDescription; // For the list lookup bool operator==(const CMonitor& rhs) { - return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name; + return vecPosition == rhs.vecPosition && vecSize == rhs.vecSize && szName == rhs.szName; } - Mat3x3 m_projMatrix; + // workspace previous per monitor functionality + SWorkspaceIDName getPrevWorkspaceIDName(const WORKSPACEID id); + void addPrevWorkspaceID(const WORKSPACEID id); private: - void updateMatrix(); - Mat3x3 m_projOutputMatrix; - void setupDefaultWS(const SMonitorRule&); WORKSPACEID findAvailableDefaultWS(); - void commitDPMSState(bool state); - void updateVCGTRamps(); - bool m_doneScheduled = false; - bool m_vcgtRampsSet = false; - std::stack m_prevWorkSpaces; + bool doneScheduled = false; + std::stack prevWorkSpaces; struct { CHyprSignalListener frame; @@ -389,11 +239,5 @@ class CMonitor { CHyprSignalListener needsFrame; CHyprSignalListener presented; CHyprSignalListener commit; - } m_listeners; - - int m_supportsWideColor = 0; - int m_supportsHDR = 0; - float m_minLuminance = -1.0f; - int m_maxLuminance = -1; - int m_maxAvgLuminance = -1; + } listeners; }; diff --git a/src/helpers/MonitorFrameScheduler.cpp b/src/helpers/MonitorFrameScheduler.cpp deleted file mode 100644 index 648e6dec..00000000 --- a/src/helpers/MonitorFrameScheduler.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "MonitorFrameScheduler.hpp" -#include "../config/ConfigValue.hpp" -#include "../Compositor.hpp" -#include "../render/Renderer.hpp" -#include "../managers/eventLoop/EventLoopManager.hpp" - -CMonitorFrameScheduler::CMonitorFrameScheduler(PHLMONITOR m) : m_monitor(m) { - ; -} - -bool CMonitorFrameScheduler::newSchedulingEnabled() { - static auto PENABLENEW = CConfigValue("render:new_render_scheduling"); - - return *PENABLENEW && g_pHyprOpenGL->explicitSyncSupported() && m_monitor && !m_monitor->m_directScanoutIsActive; -} - -void CMonitorFrameScheduler::onSyncFired() { - - if (!newSchedulingEnabled()) - return; - - // Sync fired: reset submitted state, set as rendered. Check the last render time. If we are running - // late, we will instantly render here. - - if (std::chrono::duration_cast(hrc::now() - m_lastRenderBegun).count() / 1000.F < 1000.F / m_monitor->m_refreshRate) { - // we are in. Frame is valid. We can just render as normal. - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onSyncFired, didn't miss.", m_monitor->m_name); - m_renderAtFrame = true; - return; - } - - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onSyncFired, missed.", m_monitor->m_name); - - // we are out. The frame is taking too long to render. Begin rendering immediately, but don't commit yet. - m_pendingThird = true; - m_renderAtFrame = false; // block frame rendering, we already scheduled - - m_lastRenderBegun = hrc::now(); - - // get a ref to ourselves. renderMonitor can destroy this scheduler if it decides to perform a monitor reload - // FIXME: this is horrible. "renderMonitor" should not be able to do that. - auto self = m_self; - - g_pHyprRenderer->renderMonitor(m_monitor.lock(), false); - - if (!self) - return; - - onFinishRender(); -} - -void CMonitorFrameScheduler::onPresented() { - if (!newSchedulingEnabled()) - return; - - if (!m_pendingThird) - return; - - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onPresented, missed, committing pending.", m_monitor->m_name); - - m_pendingThird = false; - - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> onPresented, missed, committing pending at the earliest convenience.", m_monitor->m_name); - - m_pendingThird = false; - - g_pEventLoopManager->doLater([m = m_monitor.lock()] { - if (!m) - return; - g_pHyprRenderer->commitPendingAndDoExplicitSync(m); // commit the pending frame. If it didn't fire yet (is not rendered) it doesn't matter. Syncs will wait. - - // schedule a frame: we might have some missed damage, which got cleared due to the above commit. - // TODO: this is not always necessary, but doesn't hurt in general. We likely won't hit this if nothing's happening anyways. - if (m->m_damage.hasChanged()) - g_pCompositor->scheduleFrameForMonitor(m); - }); -} - -void CMonitorFrameScheduler::onFrame() { - if (!canRender()) - return; - - m_monitor->recheckSolitary(); - - m_monitor->m_tearingState.busy = false; - - if (m_monitor->m_tearingState.activelyTearing && m_monitor->m_solitaryClient.lock() /* can be invalidated by a recheck */) { - - if (!m_monitor->m_tearingState.frameScheduledWhileBusy) - return; // we did not schedule a frame yet to be displayed, but we are tearing. Why render? - - m_monitor->m_tearingState.nextRenderTorn = true; - m_monitor->m_tearingState.frameScheduledWhileBusy = false; - } - - if (!newSchedulingEnabled()) { - m_monitor->m_lastPresentationTimer.reset(); - - g_pHyprRenderer->renderMonitor(m_monitor.lock()); - return; - } - - if (!m_renderAtFrame) { - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> frame event, but m_renderAtFrame = false.", m_monitor->m_name); - return; - } - - Log::logger->log(Log::TRACE, "CMonitorFrameScheduler: {} -> frame event, render = true, rendering normally.", m_monitor->m_name); - - m_lastRenderBegun = hrc::now(); - - // get a ref to ourselves. renderMonitor can destroy this scheduler if it decides to perform a monitor reload - // FIXME: this is horrible. "renderMonitor" should not be able to do that. - auto self = m_self; - - g_pHyprRenderer->renderMonitor(m_monitor.lock()); - - if (!self) - return; - - onFinishRender(); -} - -void CMonitorFrameScheduler::onFinishRender() { - m_sync = CEGLSync::create(); // this destroys the old sync - g_pEventLoopManager->doOnReadable(m_sync->fd().duplicate(), [this, self = m_self] { - if (!self) // might've gotten destroyed - return; - onSyncFired(); - }); -} - -bool CMonitorFrameScheduler::canRender() { - if ((g_pCompositor->m_aqBackend->hasSession() && !g_pCompositor->m_aqBackend->session->active) || !g_pCompositor->m_sessionActive || g_pCompositor->m_unsafeState) { - Log::logger->log(Log::WARN, "Attempted to render frame on inactive session!"); - - if (g_pCompositor->m_unsafeState && std::ranges::any_of(g_pCompositor->m_monitors.begin(), g_pCompositor->m_monitors.end(), [&](auto& m) { - return m->m_output != g_pCompositor->m_unsafeOutput->m_output; - })) { - // restore from unsafe state - g_pCompositor->leaveUnsafeState(); - } - - return false; // cannot draw on session inactive (different tty) - } - - if (!m_monitor->m_enabled) - return false; - - return true; -} diff --git a/src/helpers/MonitorFrameScheduler.hpp b/src/helpers/MonitorFrameScheduler.hpp deleted file mode 100644 index c9b45a64..00000000 --- a/src/helpers/MonitorFrameScheduler.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "Monitor.hpp" - -#include - -class CEGLSync; - -class CMonitorFrameScheduler { - public: - using hrc = std::chrono::high_resolution_clock; - - CMonitorFrameScheduler(PHLMONITOR m); - - CMonitorFrameScheduler(const CMonitorFrameScheduler&) = delete; - CMonitorFrameScheduler(CMonitorFrameScheduler&&) = delete; - CMonitorFrameScheduler& operator=(const CMonitorFrameScheduler&) = delete; - CMonitorFrameScheduler& operator=(CMonitorFrameScheduler&&) = delete; - - void onSyncFired(); - void onPresented(); - void onFrame(); - - private: - bool canRender(); - void onFinishRender(); - bool newSchedulingEnabled(); - - bool m_renderAtFrame = true; - bool m_pendingThird = false; - hrc::time_point m_lastRenderBegun; - - PHLMONITORREF m_monitor; - - UP m_sync; - - WP m_self; - - friend class CMonitor; -}; diff --git a/src/helpers/MonitorZoomController.cpp b/src/helpers/MonitorZoomController.cpp deleted file mode 100644 index d90f416f..00000000 --- a/src/helpers/MonitorZoomController.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "MonitorZoomController.hpp" - -#include -#include "../config/ConfigValue.hpp" -#include "../managers/input/InputManager.hpp" -#include "../render/OpenGL.hpp" -#include "desktop/DesktopTypes.hpp" -#include "render/Renderer.hpp" - -void CMonitorZoomController::zoomWithDetachedCamera(CBox& result, const SCurrentRenderData& m_renderData) { - const auto m = m_renderData.pMonitor; - auto monbox = CBox(0, 0, m->m_size.x, m->m_size.y); - const auto ZOOM = m_renderData.mouseZoomFactor; - const auto MOUSE = g_pInputManager->getMouseCoordsInternal() - m->m_position; - - if (m_lastZoomLevel != ZOOM) { - if (m_resetCameraState) { - m_resetCameraState = false; - m_camera = CBox(0, 0, m->m_size.x, m->m_size.y); - m_lastZoomLevel = 1.0f; - } - const CBox old = m_camera; - - // mouse normalized inside screen (0..1) - const float mx = MOUSE.x / m->m_size.x; - const float my = MOUSE.y / m->m_size.y; - // world-space point under the cursor before zoom - const float mouseWorldX = old.x + (mx * old.w); - const float mouseWorldY = old.y + (my * old.h); - - const auto CAMERAW = monbox.w / ZOOM; - const auto CAMERAH = monbox.h / ZOOM; - - // compute new top-left so the same world point stays under the cursor - const float newX = mouseWorldX - (mx * CAMERAW); - const float newY = mouseWorldY - (my * CAMERAH); - - m_camera = CBox(newX, newY, CAMERAW, CAMERAH); - // Detect if this zoom would've caused jerk to keep mouse in view and disable edges if so - if (!m_camera.copy().scaleFromCenter(.9).containsPoint(MOUSE)) - m_padCamEdges = false; - m_lastZoomLevel = ZOOM; - } - - // Keep mouse inside cameraview - auto smallerbox = m_camera; - // Prevent zoom step from causing us to jerk to keep mouse in padded camera view, - // but let us switch to the padded camera once the mouse moves into the safe area - if (!m_padCamEdges) - if (smallerbox.copy().scaleFromCenter(.9).containsPoint(MOUSE)) - m_padCamEdges = true; - if (m_padCamEdges) - smallerbox.scaleFromCenter(.9); - if (!smallerbox.containsPoint(MOUSE)) { - if (MOUSE.x < smallerbox.x) - m_camera.x -= smallerbox.x - MOUSE.x; - if (MOUSE.y < smallerbox.y) - m_camera.y -= smallerbox.y - MOUSE.y; - if (MOUSE.y > smallerbox.y + smallerbox.h) - m_camera.y += MOUSE.y - (smallerbox.y + smallerbox.h); - if (MOUSE.x > smallerbox.x + smallerbox.w) - m_camera.x += MOUSE.x - (smallerbox.x + smallerbox.w); - } - - auto z = ZOOM * m->m_scale; - monbox.scale(z).translate(-m_camera.pos() * z); - - result = monbox; -} - -void CMonitorZoomController::applyZoomTransform(CBox& monbox, const SCurrentRenderData& m_renderData) { - static auto PZOOMRIGID = CConfigValue("cursor:zoom_rigid"); - static auto PZOOMDETACHEDCAMERA = CConfigValue("cursor:zoom_detached_camera"); - const auto ZOOM = m_renderData.mouseZoomFactor; - - if (ZOOM == 1.0f) - return; - - const auto m = m_renderData.pMonitor; - const auto ORIGINAL = monbox; - const auto INITANIM = m->m_zoomAnimProgress->value() != 1.0; - - if (*PZOOMDETACHEDCAMERA && !INITANIM) - zoomWithDetachedCamera(monbox, m_renderData); - else { - const auto ZOOMCENTER = m_renderData.mouseZoomUseMouse ? (g_pInputManager->getMouseCoordsInternal() - m->m_position) * m->m_scale : m->m_transformedSize / 2.f; - - monbox.translate(-ZOOMCENTER).scale(ZOOM).translate(*PZOOMRIGID ? m->m_transformedSize / 2.0 : ZOOMCENTER); - } - - monbox.x = std::min(monbox.x, 0.0); - monbox.y = std::min(monbox.y, 0.0); - if (monbox.x + monbox.width < ORIGINAL.w) - monbox.x = ORIGINAL.w - monbox.width; - if (monbox.y + monbox.height < ORIGINAL.h) - monbox.y = ORIGINAL.h - monbox.height; -} diff --git a/src/helpers/MonitorZoomController.hpp b/src/helpers/MonitorZoomController.hpp deleted file mode 100644 index 4f7c9d7a..00000000 --- a/src/helpers/MonitorZoomController.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "./math/Math.hpp" - -struct SCurrentRenderData; - -class CMonitorZoomController { - public: - bool m_resetCameraState = true; - - void applyZoomTransform(CBox& monbox, const SCurrentRenderData& m_renderData); - - private: - void zoomWithDetachedCamera(CBox& result, const SCurrentRenderData& m_renderData); - - CBox m_camera; - float m_lastZoomLevel = 1.0f; - bool m_padCamEdges = true; -}; diff --git a/src/helpers/SdDaemon.cpp b/src/helpers/SdDaemon.cpp index b6c207d8..1cf15741 100644 --- a/src/helpers/SdDaemon.cpp +++ b/src/helpers/SdDaemon.cpp @@ -38,16 +38,16 @@ int NSystemd::sdNotify(int unsetEnvironment, const char* state) { if (!addr) return 0; - struct sockaddr_un unixAddr = {0}; - - size_t addrLen = strnlen(addr, sizeof(unixAddr.sun_path) - 1); + // address length must be at most this; see man 7 unix + size_t addrLen = strnlen(addr, 107); + struct sockaddr_un unixAddr; unixAddr.sun_family = AF_UNIX; strncpy(unixAddr.sun_path, addr, addrLen); if (unixAddr.sun_path[0] == '@') unixAddr.sun_path[0] = '\0'; - if (connect(fd, rc(&unixAddr), sizeof(struct sockaddr_un)) < 0) + if (connect(fd, (const sockaddr*)&unixAddr, sizeof(struct sockaddr_un)) < 0) return -errno; // arbitrary value which seems to be enough for s-d messages diff --git a/src/helpers/Splashes.hpp b/src/helpers/Splashes.hpp index 4bc2814b..5c996a29 100644 --- a/src/helpers/Splashes.hpp +++ b/src/helpers/Splashes.hpp @@ -49,7 +49,7 @@ namespace NSplashes { "By dt, do you mean damage tracking or distrotube?", "Made in Poland", "\"I use Arch, btw\" - John Cena", - R"("Hyper".replace("e", ""))", + "\"Hyper\".replace(\"e\", \"\")", "\"my win11 install runs hyprland that is true\" - raf", "\"stop playing league loser\" - hyprBot", "\"If it ain't broke, don't fix it\" - Lucascito_03", @@ -59,7 +59,6 @@ namespace NSplashes { "The AUR packages always work, except for the times they don't.", "Funny animation compositor woo", "3 years!", - "Beauty will save the world", // 4th ricing comp winner - zacoons' choice // music reference / quote section "J'remue le ciel, le jour, la nuit.", "aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi, aezakmi!", diff --git a/src/helpers/TagKeeper.cpp b/src/helpers/TagKeeper.cpp index 7f377657..f960accb 100644 --- a/src/helpers/TagKeeper.cpp +++ b/src/helpers/TagKeeper.cpp @@ -1,10 +1,7 @@ #include "TagKeeper.hpp" -bool CTagKeeper::isTagged(const std::string& tag, bool strict) const { - const bool NEGATIVE = tag.starts_with("negative"); - const auto MATCH = NEGATIVE ? tag.substr(9) : tag; - const bool TAGGED = m_tags.contains(MATCH) || (!strict && m_tags.contains(MATCH + "*")); - return NEGATIVE ? !TAGGED : TAGGED; +bool CTagKeeper::isTagged(const std::string& tag, bool strict) { + return m_tags.contains(tag) || (!strict && m_tags.contains(tag + "*")); } bool CTagKeeper::applyTag(const std::string& tag, bool dynamic) { @@ -38,6 +35,6 @@ bool CTagKeeper::applyTag(const std::string& tag, bool dynamic) { return true; } -bool CTagKeeper::removeDynamicTag(const std::string& s) { - return std::erase_if(m_tags, [&s](const auto& tag) { return tag == s + "*"; }); +bool CTagKeeper::removeDynamicTags() { + return std::erase_if(m_tags, [](const auto& tag) { return tag.ends_with("*"); }); } diff --git a/src/helpers/TagKeeper.hpp b/src/helpers/TagKeeper.hpp index d18a0d29..f4732005 100644 --- a/src/helpers/TagKeeper.hpp +++ b/src/helpers/TagKeeper.hpp @@ -5,9 +5,9 @@ class CTagKeeper { public: - bool isTagged(const std::string& tag, bool strict = false) const; + bool isTagged(const std::string& tag, bool strict = false); bool applyTag(const std::string& tag, bool dynamic = false); - bool removeDynamicTag(const std::string& tag); + bool removeDynamicTags(); const auto& getTags() const { return m_tags; diff --git a/src/helpers/Timer.cpp b/src/helpers/Timer.cpp new file mode 100644 index 00000000..e00c5918 --- /dev/null +++ b/src/helpers/Timer.cpp @@ -0,0 +1,22 @@ +#include "Timer.hpp" +#include + +void CTimer::reset() { + m_tpLastReset = std::chrono::steady_clock::now(); +} + +std::chrono::steady_clock::duration CTimer::getDuration() { + return std::chrono::steady_clock::now() - m_tpLastReset; +} + +float CTimer::getMillis() { + return std::chrono::duration_cast(getDuration()).count() / 1000.f; +} + +float CTimer::getSeconds() { + return std::chrono::duration_cast(getDuration()).count() / 1000.f; +} + +const std::chrono::steady_clock::time_point& CTimer::chrono() const { + return m_tpLastReset; +} \ No newline at end of file diff --git a/src/helpers/Timer.hpp b/src/helpers/Timer.hpp new file mode 100644 index 00000000..a58225d0 --- /dev/null +++ b/src/helpers/Timer.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +class CTimer { + public: + void reset(); + float getSeconds(); + float getMillis(); + const std::chrono::steady_clock::time_point& chrono() const; + + private: + std::chrono::steady_clock::time_point m_tpLastReset; + + std::chrono::steady_clock::duration getDuration(); +}; \ No newline at end of file diff --git a/src/helpers/TransferFunction.cpp b/src/helpers/TransferFunction.cpp deleted file mode 100644 index 074f4b19..00000000 --- a/src/helpers/TransferFunction.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "TransferFunction.hpp" -#include "../config/ConfigValue.hpp" -#include "../event/EventBus.hpp" -#include -#include -#include - -using namespace NTransferFunction; - -static std::unordered_map const table = {{"default", TF_DEFAULT}, {"0", TF_DEFAULT}, {"auto", TF_AUTO}, {"srgb", TF_SRGB}, - {"3", TF_SRGB}, {"gamma22", TF_GAMMA22}, {"1", TF_GAMMA22}, {"gamma22force", TF_FORCED_GAMMA22}, - {"2", TF_FORCED_GAMMA22}}; - -eTF NTransferFunction::fromString(const std::string tfName) { - auto it = table.find(tfName); - if (it == table.end()) - return TF_DEFAULT; - return it->second; -} - -std::string NTransferFunction::toString(eTF tf) { - for (const auto& [key, value] : table) { - if (value == tf) - return key; - } - return ""; -} - -eTF NTransferFunction::fromConfig(bool useICC) { - if (useICC) - return TF_SRGB; - - static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); - static auto sdrEOTF = NTransferFunction::fromString(*PSDREOTF); - static auto P = Event::bus()->m_events.config.reloaded.listen([]() { sdrEOTF = NTransferFunction::fromString(*PSDREOTF); }); - - return sdrEOTF; -} diff --git a/src/helpers/TransferFunction.hpp b/src/helpers/TransferFunction.hpp deleted file mode 100644 index ae575158..00000000 --- a/src/helpers/TransferFunction.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -namespace NTransferFunction { - enum eTF : uint8_t { - TF_DEFAULT = 0, - TF_AUTO = 1, - TF_SRGB = 2, - TF_GAMMA22 = 3, - TF_FORCED_GAMMA22 = 4, - }; - - eTF fromString(const std::string tfName); - std::string toString(eTF tf); - - eTF fromConfig(bool useICC = false); -} diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index ec073787..bedea065 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -1,9 +1,9 @@ #pragma once #include "../defines.hpp" -#include "../desktop/view/Subsurface.hpp" -#include "../desktop/view/Popup.hpp" -#include "../desktop/view/WLSurface.hpp" +#include "../desktop/Subsurface.hpp" +#include "../desktop/Popup.hpp" +#include "../desktop/WLSurface.hpp" #include "../macros.hpp" #include "../desktop/DesktopTypes.hpp" #include "memory/Memory.hpp" @@ -16,6 +16,19 @@ class CWLSurfaceResource; AQUAMARINE_FORWARD(ISwitch); +struct SSwipeGesture { + PHLWORKSPACE pWorkspaceBegin = nullptr; + + double delta = 0; + + int initialDirection = 0; + float avgSpeed = 0; + int speedPoints = 0; + int touch_id = 0; + + PHLMONITORREF pMonitor; +}; + struct SSwitchDevice { WP pDevice; diff --git a/src/helpers/Watchdog.cpp b/src/helpers/Watchdog.cpp new file mode 100644 index 00000000..0956eed5 --- /dev/null +++ b/src/helpers/Watchdog.cpp @@ -0,0 +1,58 @@ +#include "Watchdog.hpp" +#include +#include "config/ConfigManager.hpp" +#include "../config/ConfigValue.hpp" + +CWatchdog::~CWatchdog() { + m_bExitThread = true; + m_bNotified = true; + m_cvWatchdogCondition.notify_all(); + + if (m_pWatchdog && m_pWatchdog->joinable()) + m_pWatchdog->join(); +} + +CWatchdog::CWatchdog() : m_iMainThreadPID(pthread_self()) { + + m_pWatchdog = makeUnique([this] { + static auto PTIMEOUT = CConfigValue("debug:watchdog_timeout"); + + m_bWatchdogInitialized = true; + while (!m_bExitThread) { + std::unique_lock lk(m_mWatchdogMutex); + + if (!m_bWillWatch) + m_cvWatchdogCondition.wait(lk, [this] { return m_bNotified || m_bExitThread; }); + else if (!m_cvWatchdogCondition.wait_for(lk, std::chrono::milliseconds((int)(*PTIMEOUT * 1000.0)), [this] { return m_bNotified || m_bExitThread; })) + pthread_kill(m_iMainThreadPID, SIGUSR1); + + if (m_bExitThread) + break; + + m_bWatching = false; + m_bNotified = false; + } + }); +} + +void CWatchdog::startWatching() { + static auto PTIMEOUT = CConfigValue("debug:watchdog_timeout"); + + if (*PTIMEOUT == 0) + return; + + m_tTriggered = std::chrono::high_resolution_clock::now(); + m_bWillWatch = true; + m_bWatching = true; + + m_bNotified = true; + m_cvWatchdogCondition.notify_all(); +} + +void CWatchdog::endWatching() { + m_bWatching = false; + m_bWillWatch = false; + + m_bNotified = true; + m_cvWatchdogCondition.notify_all(); +} \ No newline at end of file diff --git a/src/helpers/Watchdog.hpp b/src/helpers/Watchdog.hpp new file mode 100644 index 00000000..51b71d13 --- /dev/null +++ b/src/helpers/Watchdog.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "memory/Memory.hpp" +#include +#include +#include + +class CWatchdog { + public: + // must be called from the main thread + CWatchdog(); + ~CWatchdog(); + + void startWatching(); + void endWatching(); + + std::atomic m_bWatchdogInitialized{false}; + + private: + std::chrono::high_resolution_clock::time_point m_tTriggered; + + pthread_t m_iMainThreadPID = 0; + + std::atomic m_bWatching = false; + std::atomic m_bWillWatch = false; + + UP m_pWatchdog; + std::mutex m_mWatchdogMutex; + std::atomic m_bNotified = false; + std::atomic m_bExitThread = false; + std::condition_variable m_cvWatchdogCondition; +}; + +inline UP g_pWatchdog; \ No newline at end of file diff --git a/src/helpers/cm/ColorManagement.cpp b/src/helpers/cm/ColorManagement.cpp deleted file mode 100644 index bac9f25a..00000000 --- a/src/helpers/cm/ColorManagement.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "ColorManagement.hpp" -#include "../../macros.hpp" -#include -#include -#include - -using namespace NColorManagement; - -namespace NColorManagement { - // expected to be small - static std::vector> knownPrimaries; - static std::vector> knownDescriptions; - static std::map, Hyprgraphics::CMatrix3> primariesConversion; -} - -const SPCPRimaries& NColorManagement::getPrimaries(ePrimaries name) { - switch (name) { - case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709; - case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020; - case CM_PRIMARIES_PAL_M: return NColorPrimaries::PAL_M; - case CM_PRIMARIES_PAL: return NColorPrimaries::PAL; - case CM_PRIMARIES_NTSC: return NColorPrimaries::NTSC; - case CM_PRIMARIES_GENERIC_FILM: return NColorPrimaries::GENERIC_FILM; - case CM_PRIMARIES_CIE1931_XYZ: return NColorPrimaries::CIE1931_XYZ; - case CM_PRIMARIES_DCI_P3: return NColorPrimaries::DCI_P3; - case CM_PRIMARIES_DISPLAY_P3: return NColorPrimaries::DISPLAY_P3; - case CM_PRIMARIES_ADOBE_RGB: return NColorPrimaries::ADOBE_RGB; - default: return NColorPrimaries::DEFAULT_PRIMARIES; - } -} - -CPrimaries::CPrimaries(const SPCPRimaries& primaries, const uint32_t primariesId) : m_id(primariesId), m_primaries(primaries) { - m_primaries2XYZ = m_primaries.toXYZ(); -} - -WP CPrimaries::from(const SPCPRimaries& primaries) { - for (const auto& known : knownPrimaries) { - if (known->value() == primaries) - return known; - } - - knownPrimaries.emplace_back(CUniquePointer(new CPrimaries(primaries, knownPrimaries.size() + 1))); - return knownPrimaries.back(); -} - -WP CPrimaries::from(const ePrimaries name) { - return from(getPrimaries(name)); -} - -WP CPrimaries::from(const uint32_t primariesId) { - ASSERT(primariesId <= knownPrimaries.size()); - return knownPrimaries[primariesId - 1]; -} - -const SPCPRimaries& CPrimaries::value() const { - return m_primaries; -} - -uint CPrimaries::id() const { - return m_id; -} - -const Hyprgraphics::CMatrix3& CPrimaries::toXYZ() const { - return m_primaries2XYZ; -} - -const Hyprgraphics::CMatrix3& CPrimaries::convertMatrix(const WP dst) const { - const auto cacheKey = std::make_pair(m_id, dst->m_id); - if (!primariesConversion.contains(cacheKey)) - primariesConversion.insert(std::make_pair(cacheKey, m_primaries.convertMatrix(dst->m_primaries))); - - return primariesConversion[cacheKey]; -} - -CImageDescription::CImageDescription(const SImageDescription& imageDescription, const uint32_t imageDescriptionId) : - m_id(imageDescriptionId), m_imageDescription(imageDescription) { - m_primariesId = CPrimaries::from(m_imageDescription.getPrimaries())->id(); -} - -PImageDescription CImageDescription::from(const SImageDescription& imageDescription) { - for (const auto& known : knownDescriptions) { - if (known->value() == imageDescription) - return known; - } - - knownDescriptions.emplace_back(UP(new CImageDescription(imageDescription, knownDescriptions.size() + 1))); - return knownDescriptions.back(); -} - -PImageDescription CImageDescription::from(const uint32_t imageDescriptionId) { - ASSERT(imageDescriptionId <= knownDescriptions.size()); - return knownDescriptions[imageDescriptionId - 1]; -} - -PImageDescription CImageDescription::with(const SImageDescription::SPCLuminances& luminances) const { - auto desc = m_imageDescription; - desc.luminances = luminances; - return CImageDescription::from(desc); -} - -const SImageDescription& CImageDescription::value() const { - return m_imageDescription; -} - -uint CImageDescription::id() const { - return m_id; -} - -WP CImageDescription::getPrimaries() const { - return CPrimaries::from(m_primariesId); -} - -static Mat3x3 diag3(const std::array& s) { - return Mat3x3{std::array{s[0], 0, 0, 0, s[1], 0, 0, 0, s[2]}}; -} - -static std::optional invertMat3(const Mat3x3& m) { - const auto ARR = m.getMatrix(); - const double a = ARR[0], b = ARR[1], c = ARR[2]; - const double d = ARR[3], e = ARR[4], f = ARR[5]; - const double g = ARR[6], h = ARR[7], i = ARR[8]; - - const double A = (e * i - f * h); - const double B = -(d * i - f * g); - const double C = (d * h - e * g); - const double D = -(b * i - c * h); - const double E = (a * i - c * g); - const double F = -(a * h - b * g); - const double G = (b * f - c * e); - const double H = -(a * f - c * d); - const double I = (a * e - b * d); - - const double det = a * A + b * B + c * C; - if (std::abs(det) < 1e-18) - return std::nullopt; - - const double invDet = 1.0 / det; - Mat3x3 inv{std::array{ - A * invDet, - D * invDet, - G * invDet, // - B * invDet, - E * invDet, - H * invDet, // - C * invDet, - F * invDet, - I * invDet, // - }}; - return inv; -} - -static std::array matByVec(const Mat3x3& M, const std::array& v) { - const auto ARR = M.getMatrix(); - return {ARR[0] * v[0] + ARR[1] * v[1] + ARR[2] * v[2], ARR[3] * v[0] + ARR[4] * v[1] + ARR[5] * v[2], ARR[6] * v[0] + ARR[7] * v[1] + ARR[8] * v[2]}; -} - -std::optional NColorManagement::rgbToXYZFromPrimaries(SPCPRimaries pr) { - const auto R = Hyprgraphics::xy2xyz(pr.red); - const auto G = Hyprgraphics::xy2xyz(pr.green); - const auto B = Hyprgraphics::xy2xyz(pr.blue); - const auto W = Hyprgraphics::xy2xyz(pr.white); - - // P has columns R,G,B - Mat3x3 P{std::array{R.x, G.x, B.x, R.y, G.y, B.y, R.z, G.z, B.z}}; - - auto invP = invertMat3(P); - if (!invP) - return std::nullopt; - - const auto S = matByVec(*invP, {W.x, W.y, W.z}); - - P.multiply(diag3(S)); // RGB->XYZ - - return P; -} - -Mat3x3 NColorManagement::adaptBradford(Hyprgraphics::CColor::xy srcW, Hyprgraphics::CColor::xy dstW) { - static const Mat3x3 Bradford{std::array{0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f, 0.0367f, 0.0389f, -0.0685f, 1.0296f}}; - static const Mat3x3 BradfordInv = invertMat3(Bradford).value(); - - const auto srcXYZ = Hyprgraphics::xy2xyz(srcW); - const auto dstXYZ = Hyprgraphics::xy2xyz(dstW); - - const auto srcLMS = matByVec(Bradford, {srcXYZ.x, srcXYZ.y, srcXYZ.z}); - const auto dstLMS = matByVec(Bradford, {dstXYZ.x, dstXYZ.y, dstXYZ.z}); - - const std::array scale{dstLMS[0] / srcLMS[0], dstLMS[1] / srcLMS[1], dstLMS[2] / srcLMS[2]}; - - Mat3x3 result = BradfordInv; - result.multiply(diag3(scale)).multiply(Bradford); - - return result; -} \ No newline at end of file diff --git a/src/helpers/cm/ColorManagement.hpp b/src/helpers/cm/ColorManagement.hpp deleted file mode 100644 index 0103e2a4..00000000 --- a/src/helpers/cm/ColorManagement.hpp +++ /dev/null @@ -1,351 +0,0 @@ -#pragma once - -#include "color-management-v1.hpp" -#include -#include "../../helpers/memory/Memory.hpp" -#include "../../helpers/math/Math.hpp" - -#include -#include -#include - -#define SDR_MIN_LUMINANCE 0.2 -#define SDR_MAX_LUMINANCE 80.0 -#define SDR_REF_LUMINANCE 80.0 -#define HDR_MIN_LUMINANCE 0.005 -#define HDR_MAX_LUMINANCE 10000.0 -#define HDR_REF_LUMINANCE 203.0 -#define HLG_MAX_LUMINANCE 1000.0 - -class ITexture; - -namespace NColorManagement { - enum eNoShader : uint8_t { - CM_NS_DISABLE = 0, - CM_NS_ALWAYS = 1, - CM_NS_ONDEMAND = 2, - CM_NS_IGNORE = 3, - }; - - enum ePrimaries : uint8_t { - CM_PRIMARIES_SRGB = 1, - CM_PRIMARIES_PAL_M = 2, - CM_PRIMARIES_PAL = 3, - CM_PRIMARIES_NTSC = 4, - CM_PRIMARIES_GENERIC_FILM = 5, - CM_PRIMARIES_BT2020 = 6, - CM_PRIMARIES_CIE1931_XYZ = 7, - CM_PRIMARIES_DCI_P3 = 8, - CM_PRIMARIES_DISPLAY_P3 = 9, - CM_PRIMARIES_ADOBE_RGB = 10, - }; - - enum eTransferFunction : uint8_t { - CM_TRANSFER_FUNCTION_BT1886 = 1, - CM_TRANSFER_FUNCTION_GAMMA22 = 2, - CM_TRANSFER_FUNCTION_GAMMA28 = 3, - CM_TRANSFER_FUNCTION_ST240 = 4, - CM_TRANSFER_FUNCTION_EXT_LINEAR = 5, - CM_TRANSFER_FUNCTION_LOG_100 = 6, - CM_TRANSFER_FUNCTION_LOG_316 = 7, - CM_TRANSFER_FUNCTION_XVYCC = 8, - CM_TRANSFER_FUNCTION_SRGB = 9, - CM_TRANSFER_FUNCTION_EXT_SRGB = 10, - CM_TRANSFER_FUNCTION_ST2084_PQ = 11, - CM_TRANSFER_FUNCTION_ST428 = 12, - CM_TRANSFER_FUNCTION_HLG = 13, - }; - - // NOTE should be ok this way. unsupported primaries/tfs must be rejected earlier. supported enum values should be in sync with proto. - // might need a proper switch-case and additional INVALID enum value. - inline wpColorManagerV1Primaries convertPrimaries(ePrimaries primaries) { - return sc(primaries); - } - inline ePrimaries convertPrimaries(wpColorManagerV1Primaries primaries) { - return sc(primaries); - } - inline wpColorManagerV1TransferFunction convertTransferFunction(eTransferFunction tf) { - return sc(tf); - } - inline eTransferFunction convertTransferFunction(wpColorManagerV1TransferFunction tf) { - return sc(tf); - } - - using SPCPRimaries = Hyprgraphics::SPCPRimaries; - - namespace NColorPrimaries { - - static const auto BT709 = SPCPRimaries{ - .red = {.x = 0.64, .y = 0.33}, - .green = {.x = 0.30, .y = 0.60}, - .blue = {.x = 0.15, .y = 0.06}, - .white = {.x = 0.3127, .y = 0.3290}, - }; - - static const auto DEFAULT_PRIMARIES = BT709; - - static const auto PAL_M = SPCPRimaries{ - .red = {.x = 0.67, .y = 0.33}, - .green = {.x = 0.21, .y = 0.71}, - .blue = {.x = 0.14, .y = 0.08}, - .white = {.x = 0.310, .y = 0.316}, - }; - - static const auto PAL = SPCPRimaries{ - .red = {.x = 0.640, .y = 0.330}, - .green = {.x = 0.290, .y = 0.600}, - .blue = {.x = 0.150, .y = 0.060}, - .white = {.x = 0.3127, .y = 0.3290}, - }; - - static const auto NTSC = SPCPRimaries{ - .red = {.x = 0.630, .y = 0.340}, - .green = {.x = 0.310, .y = 0.595}, - .blue = {.x = 0.155, .y = 0.070}, - .white = {.x = 0.3127, .y = 0.3290}, - }; - - static const auto GENERIC_FILM = SPCPRimaries{ - .red = {.x = 0.243, .y = 0.692}, - .green = {.x = 0.145, .y = 0.049}, - .blue = {.x = 0.681, .y = 0.319}, // NOLINT(modernize-use-std-numbers) - .white = {.x = 0.310, .y = 0.316}, - }; - - static const auto BT2020 = SPCPRimaries{ - .red = {.x = 0.708, .y = 0.292}, - .green = {.x = 0.170, .y = 0.797}, - .blue = {.x = 0.131, .y = 0.046}, - .white = {.x = 0.3127, .y = 0.3290}, - }; - - static const auto CIE1931_XYZ = SPCPRimaries{ - .red = {.x = 1.0, .y = 0.0}, - .green = {.x = 0.0, .y = 1.0}, - .blue = {.x = 0.0, .y = 0.0}, - .white = {.x = 1.0 / 3.0, .y = 1.0 / 3.0}, - }; - - static const auto DCI_P3 = SPCPRimaries{ - .red = {.x = 0.680, .y = 0.320}, - .green = {.x = 0.265, .y = 0.690}, - .blue = {.x = 0.150, .y = 0.060}, - .white = {.x = 0.314, .y = 0.351}, - }; - - static const auto DISPLAY_P3 = SPCPRimaries{ - .red = {.x = 0.680, .y = 0.320}, - .green = {.x = 0.265, .y = 0.690}, - .blue = {.x = 0.150, .y = 0.060}, - .white = {.x = 0.3127, .y = 0.3290}, - }; - - static const auto ADOBE_RGB = SPCPRimaries{ - .red = {.x = 0.6400, .y = 0.3300}, - .green = {.x = 0.2100, .y = 0.7100}, - .blue = {.x = 0.1500, .y = 0.0600}, - .white = {.x = 0.3127, .y = 0.3290}, - }; - } - - struct SVCGTTable16 { - uint16_t channels = 0; - uint16_t entries = 0; - uint16_t entrySize = 0; - std::array, 3> ch; - }; - - const SPCPRimaries& getPrimaries(ePrimaries name); - std::optional rgbToXYZFromPrimaries(SPCPRimaries pr); - Mat3x3 adaptBradford(Hyprgraphics::CColor::xy srcW, Hyprgraphics::CColor::xy dstW); - - class CPrimaries { - public: - static WP from(const SPCPRimaries& primaries); - static WP from(const ePrimaries name); - static WP from(const uint primariesId); - - const SPCPRimaries& value() const; - uint id() const; - - const Hyprgraphics::CMatrix3& toXYZ() const; // toXYZ() * rgb -> xyz - const Hyprgraphics::CMatrix3& convertMatrix(const WP dst) const; // convertMatrix(dst) * rgb with "this" primaries -> rgb with dst primaries - - private: - CPrimaries(const SPCPRimaries& primaries, const uint primariesId); - uint m_id; - SPCPRimaries m_primaries; - - Hyprgraphics::CMatrix3 m_primaries2XYZ; - }; - - struct SImageDescription { - static std::expected fromICC(const std::filesystem::path& file); - - // - std::vector rawICC; - - eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_GAMMA22; - float transferFunctionPower = 1.0f; - bool windowsScRGB = false; - - bool primariesNameSet = false; - ePrimaries primariesNamed = CM_PRIMARIES_SRGB; - // primaries are stored as FP values with the same scale as standard defines (0.0 - 1.0) - // wayland protocol expects int32_t values multiplied by 1000000 - // drm expects uint16_t values multiplied by 50000 - SPCPRimaries primaries, masteringPrimaries; - - // luminances in cd/m² - // protos and drm expect min * 10000 - struct SPCLuminances { - float min = 0.2; // 0.2 cd/m² - uint32_t max = 80; // 80 cd/m² - uint32_t reference = 80; // 80 cd/m² - bool operator==(const SPCLuminances& l2) const { - return min == l2.min && max == l2.max && reference == l2.reference; - } - } luminances; - struct SPCMasteringLuminances { - float min = 0; - uint32_t max = 0; - bool operator==(const SPCMasteringLuminances& l2) const { - return min == l2.min && max == l2.max; - } - } masteringLuminances; - - // Matrix data from ICC - struct SICCData { - bool present = false; - size_t lutSize = 33; - std::vector lutDataPacked; - SP lutTexture; - std::optional vcgt; - } icc; - - uint32_t maxCLL = 0; - uint32_t maxFALL = 0; - - bool operator==(const SImageDescription& d2) const { - if (icc.present || d2.icc.present) - return false; - - return windowsScRGB == d2.windowsScRGB && transferFunction == d2.transferFunction && transferFunctionPower == d2.transferFunctionPower && - (primariesNameSet == d2.primariesNameSet && (primariesNameSet ? primariesNamed == d2.primariesNamed : primaries == d2.primaries)) && - masteringPrimaries == d2.masteringPrimaries && luminances == d2.luminances && masteringLuminances == d2.masteringLuminances && maxCLL == d2.maxCLL && - maxFALL == d2.maxFALL; - } - - const SPCPRimaries& getPrimaries() const { - if (primariesNameSet || primaries == SPCPRimaries{}) - return NColorManagement::getPrimaries(primariesNamed); - return primaries; - } - - float getTFMinLuminance(float sdrMinLuminance = -1.0f) const { - switch (transferFunction) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: return 0; - case CM_TRANSFER_FUNCTION_ST2084_PQ: - case CM_TRANSFER_FUNCTION_HLG: return HDR_MIN_LUMINANCE; - case CM_TRANSFER_FUNCTION_BT1886: return 0.01; - case CM_TRANSFER_FUNCTION_GAMMA22: - case CM_TRANSFER_FUNCTION_GAMMA28: - case CM_TRANSFER_FUNCTION_ST240: - case CM_TRANSFER_FUNCTION_LOG_100: - case CM_TRANSFER_FUNCTION_LOG_316: - case CM_TRANSFER_FUNCTION_XVYCC: - case CM_TRANSFER_FUNCTION_EXT_SRGB: - case CM_TRANSFER_FUNCTION_ST428: - case CM_TRANSFER_FUNCTION_SRGB: - default: return sdrMinLuminance >= 0 ? sdrMinLuminance : SDR_MIN_LUMINANCE; - } - }; - - float getTFMaxLuminance(int sdrMaxLuminance = -1) const { - switch (transferFunction) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: - return SDR_MAX_LUMINANCE; // assume Windows scRGB. white color range 1.0 - 125.0 maps to SDR_MAX_LUMINANCE (80) - HDR_MAX_LUMINANCE (10000) - case CM_TRANSFER_FUNCTION_ST2084_PQ: return HDR_MAX_LUMINANCE; - case CM_TRANSFER_FUNCTION_HLG: return HLG_MAX_LUMINANCE; - case CM_TRANSFER_FUNCTION_BT1886: return 100; - case CM_TRANSFER_FUNCTION_GAMMA22: - case CM_TRANSFER_FUNCTION_GAMMA28: - case CM_TRANSFER_FUNCTION_ST240: - case CM_TRANSFER_FUNCTION_LOG_100: - case CM_TRANSFER_FUNCTION_LOG_316: - case CM_TRANSFER_FUNCTION_XVYCC: - case CM_TRANSFER_FUNCTION_EXT_SRGB: - case CM_TRANSFER_FUNCTION_ST428: - case CM_TRANSFER_FUNCTION_SRGB: - default: return sdrMaxLuminance >= 0 ? sdrMaxLuminance : SDR_MAX_LUMINANCE; - } - }; - - float getTFRefLuminance(int sdrRefLuminance = -1) const { - switch (transferFunction) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: - case CM_TRANSFER_FUNCTION_ST2084_PQ: - case CM_TRANSFER_FUNCTION_HLG: return HDR_REF_LUMINANCE; - case CM_TRANSFER_FUNCTION_BT1886: return 100; - case CM_TRANSFER_FUNCTION_GAMMA22: - case CM_TRANSFER_FUNCTION_GAMMA28: - case CM_TRANSFER_FUNCTION_ST240: - case CM_TRANSFER_FUNCTION_LOG_100: - case CM_TRANSFER_FUNCTION_LOG_316: - case CM_TRANSFER_FUNCTION_XVYCC: - case CM_TRANSFER_FUNCTION_EXT_SRGB: - case CM_TRANSFER_FUNCTION_ST428: - case CM_TRANSFER_FUNCTION_SRGB: - default: return sdrRefLuminance >= 0 ? sdrRefLuminance : SDR_REF_LUMINANCE; - } - }; - }; - - class CImageDescription { - public: - static WP from(const SImageDescription& imageDescription); - static WP from(const uint32_t imageDescriptionId); - - WP with(const SImageDescription::SPCLuminances& luminances) const; - - const SImageDescription& value() const; - uint32_t id() const; - - WP getPrimaries() const; - - private: - CImageDescription(const SImageDescription& imageDescription, const uint imageDescriptionId); - uint32_t m_id = 0; - uint32_t m_primariesId = 0; - SImageDescription m_imageDescription; - }; - - using PImageDescription = WP; - - static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB), - .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}, - }); - - static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), - .luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203}, - }); - - static const auto SCRGB_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ - .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR, - .windowsScRGB = true, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, - .primaries = NColorPrimaries::BT709, - .luminances = {.reference = 203}, - }); - - static const auto LINEAR_IMAGE_DESCRIPTION = SCRGB_IMAGE_DESCRIPTION; // TODO any reason to use something different? -} diff --git a/src/helpers/cm/ICC.cpp b/src/helpers/cm/ICC.cpp deleted file mode 100644 index 00140c62..00000000 --- a/src/helpers/cm/ICC.cpp +++ /dev/null @@ -1,278 +0,0 @@ -#include "ColorManagement.hpp" -#include "../math/Math.hpp" -#include -#include - -#include "../../debug/log/Logger.hpp" -#include "../../render/Texture.hpp" -#include "../../render/Renderer.hpp" - -#include -using namespace Hyprutils::Utils; - -#include - -using namespace NColorManagement; - -static std::vector readBinary(const std::filesystem::path& file) { - std::ifstream ifs(file, std::ios::binary); - if (!ifs.good()) - return {}; - - ifs.seekg(0, std::ios::end); - size_t len = ifs.tellg(); - ifs.seekg(0, std::ios::beg); - - if (len <= 0) - return {}; - - std::vector buf; - buf.resize(len); - ifs.read(reinterpret_cast(buf.data()), len); - - return buf; -} - -static uint16_t bigEndianU16(const uint8_t* p) { - return (uint16_t)((uint16_t)p[0] << 8 | (uint16_t)p[1]); -} - -static uint32_t bigEndianU32(const uint8_t* p) { - return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | (uint32_t)p[3]; -} - -static constexpr cmsTagSignature makeSig(char a, char b, char c, char d) { - return sc(sc(a) << 24 | sc(b) << 16 | sc(c) << 8 | sc(d)); -} - -static constexpr cmsTagSignature VCGT_SIG = makeSig('v', 'c', 'g', 't'); - -// - -static std::expected, std::string> readVCGT16(cmsHPROFILE prof) { - if (!cmsIsTag(prof, VCGT_SIG)) - return std::nullopt; - - cmsUInt32Number n = cmsReadRawTag(prof, VCGT_SIG, nullptr, 0); - if (n < 8 + 4 + 2 + 2 + 2 + 2) // header + type + table header - return std::unexpected("Malformed vcgt tag"); - - std::vector raw(n); - if (cmsReadRawTag(prof, VCGT_SIG, raw.data(), n) != n) - return std::unexpected("Malformed vcgt tag"); - - // raw layout: - // 0 ... 3: 'vcgt' - // 4 ... 7: reserved - // 8 ... 11: gammaType (0 = table) - uint32_t gammaType = bigEndianU32(raw.data() + 8); - if (gammaType != 0) - return std::unexpected("VCGT formula type is not supported by Hyprland"); - - SVCGTTable16 table; - table.channels = bigEndianU16(raw.data() + 12); - table.entries = bigEndianU16(raw.data() + 14); - table.entrySize = bigEndianU16(raw.data() + 16); - // raw+18: reserved u16 - - Log::logger->log(Log::DEBUG, "readVCGT16: table has {} channels, {} entries, and entry size of {}", table.channels, table.entries, table.entrySize); - - if (table.channels != 3 || table.entrySize != 2 || table.entries == 0) - return std::unexpected("invalid vcgt table size"); - - size_t tableBytes = (size_t)table.channels * (size_t)table.entries * (size_t)table.entrySize; - - // VCGT is a piece of shit and some absolute fucking mongoloid idiots - // decided it'd be great to have both 18 and 20 - // FUCK YOU - size_t tableOff = 20; - - auto readTable = [&] -> void { - for (int c = 0; c < 3; ++c) { - table.ch[c].resize(table.entries); - for (uint16_t i = 0; i < table.entries; ++i) { - const uint8_t* p = raw.data() + tableOff + static_cast((c * table.entries + i) * 2); - table.ch[c][i] = bigEndianU16(p); // 0 ... 65535 - } - } - }; - - if (raw.size() < tableOff + tableBytes) { - tableOff = 18; - - if (raw.size() < tableOff + tableBytes) { - Log::logger->log(Log::ERR, "readVCGT16: table is too short, tag is invalid"); - return std::unexpected("table is too short"); - } - - Log::logger->log(Log::DEBUG, "readVCGT16: table is too short, but off = 18 fits. Attempting offset = 18"); - - readTable(); - } else { - readTable(); - - // if the table's last entry is suspiciously low, we more than likely read an 18 as a 20. - if (table.ch[0][table.entries - 1] < 30000) { - Log::logger->log(Log::DEBUG, "readVCGT16: table is likely offset 18 not 20, re-reading"); - - tableOff = 18; - - readTable(); - } - } - - if (table.ch[0][table.entries - 1] < 30000) { - Log::logger->log(Log::ERR, "readVCGT16: table is malformed, last value of a gamma ramp can't be {}", table.ch[0][table.entries - 1]); - return std::unexpected("invalid table values"); - } - - Log::logger->log(Log::DEBUG, "readVCGT16: red channel: [{}, {}, ... {}, {}]", table.ch[0][0], table.ch[0][1], table.ch[0][table.entries - 2], table.ch[0][table.entries - 1]); - Log::logger->log(Log::DEBUG, "readVCGT16: green channel: [{}, {}, ... {}, {}]", table.ch[1][0], table.ch[1][1], table.ch[1][table.entries - 2], table.ch[1][table.entries - 1]); - Log::logger->log(Log::DEBUG, "readVCGT16: blue channel: [{}, {}, ... {}, {}]", table.ch[2][0], table.ch[2][1], table.ch[2][table.entries - 2], table.ch[2][table.entries - 1]); - - return table; -} - -struct CmsProfileDeleter { - void operator()(cmsHPROFILE p) const { - if (p) - cmsCloseProfile(p); - } -}; -struct CmsTransformDeleter { - void operator()(cmsHTRANSFORM t) const { - if (t) - cmsDeleteTransform(t); - } -}; - -using UniqueProfile = std::unique_ptr, CmsProfileDeleter>; -using UniqueTransform = std::unique_ptr, CmsTransformDeleter>; - -static UniqueProfile createLinearSRGBProfile() { - cmsCIExyYTRIPLE prim{}; - // sRGB / Rec.709 primaries - prim.Red.x = 0.6400; - prim.Red.y = 0.3300; - prim.Red.Y = 1.0; - prim.Green.x = 0.3000; - prim.Green.y = 0.6000; - prim.Green.Y = 1.0; - prim.Blue.x = 0.1500; - prim.Blue.y = 0.0600; - prim.Blue.Y = 1.0; - - cmsCIExyY wp{}; - wp.x = 0.3127; - wp.y = 0.3290; - wp.Y = 1.0; // D65 - - cmsToneCurve* lin = cmsBuildGamma(nullptr, 1.0); - cmsToneCurve* curves[3] = {lin, lin, lin}; - - cmsHPROFILE p = cmsCreateRGBProfile(&wp, &prim, curves); - - cmsFreeToneCurve(lin); - return UniqueProfile{p}; -} - -static std::expected buildIcc3DLut(cmsHPROFILE profile, SImageDescription& image) { - UniqueProfile src = createLinearSRGBProfile(); - if (!src) - return std::unexpected("Failed to create linear sRGB profile"); - - // Rendering intent: RELATIVE_COLORIMETRIC is common for displays; add BPC to be safe. - const int intent = INTENT_RELATIVE_COLORIMETRIC; - const cmsUInt32Number flags = cmsFLAGS_BLACKPOINTCOMPENSATION | cmsFLAGS_HIGHRESPRECALC; // good quality precalc in LCMS - - // float->float transform (linear input, encoded output in dst device space) - UniqueTransform xform{cmsCreateTransform(src.get(), TYPE_RGB_FLT, profile, TYPE_RGB_FLT, intent, flags)}; - if (!xform) - return std::unexpected("Failed to create ICC transform"); - - Log::logger->log(Log::DEBUG, "Building a {}³ 3D LUT", image.icc.lutSize); - - image.icc.present = true; - image.icc.lutDataPacked.resize(image.icc.lutSize * image.icc.lutSize * image.icc.lutSize * 3); - - auto idx = [&image](int r, int g, int b) -> size_t { - // - return ((size_t)b * image.icc.lutSize * image.icc.lutSize + (size_t)g * image.icc.lutSize + (size_t)r) * 3; - }; - - for (size_t bz = 0; bz < image.icc.lutSize; ++bz) { - for (size_t gy = 0; gy < image.icc.lutSize; ++gy) { - for (size_t rx = 0; rx < image.icc.lutSize; ++rx) { - float in[3] = { - rx / float(image.icc.lutSize - 1), - gy / float(image.icc.lutSize - 1), - bz / float(image.icc.lutSize - 1), - }; - float outRGB[3]; - cmsDoTransform(xform.get(), in, outRGB, 1); - - outRGB[0] = std::clamp(outRGB[0], 0.F, 1.F); - outRGB[1] = std::clamp(outRGB[1], 0.F, 1.F); - outRGB[2] = std::clamp(outRGB[2], 0.F, 1.F); - - const size_t o = idx(rx, gy, bz); - image.icc.lutDataPacked[o + 0] = outRGB[0]; - image.icc.lutDataPacked[o + 1] = outRGB[1]; - image.icc.lutDataPacked[o + 2] = outRGB[2]; - } - } - } - - Log::logger->log(Log::DEBUG, "3D LUT constructed, size {}", image.icc.lutDataPacked.size()); - - // upload - image.icc.lutTexture = g_pHyprRenderer->createTexture(image.icc.lutDataPacked, image.icc.lutSize); - - return {}; -} - -std::expected SImageDescription::fromICC(const std::filesystem::path& file) { - static auto PVCGTENABLED = CConfigValue("render:icc_vcgt_enabled"); - - std::error_code ec; - if (!std::filesystem::exists(file, ec) || ec) - return std::unexpected("Invalid file"); - - SImageDescription image; - image.rawICC = readBinary(file); - - if (image.rawICC.empty()) - return std::unexpected("Failed to read file"); - - cmsHPROFILE prof = cmsOpenProfileFromFile(file.string().c_str(), "r"); - if (!prof) - return std::unexpected("CMS failed to open icc file"); - - CScopeGuard x([&prof] { cmsCloseProfile(prof); }); - - // only handle RGB (typical display profiles) - if (cmsGetColorSpace(prof) != cmsSigRgbData) - return std::unexpected("Only RGB display profiles are supported"); - - Log::logger->log(Log::DEBUG, "============= Begin ICC load ============="); - Log::logger->log(Log::DEBUG, "ICC size: {} bytes", image.rawICC.size()); - - if (const auto RET = buildIcc3DLut(prof, image); !RET) - return std::unexpected(RET.error()); - - if (*PVCGTENABLED) { - auto vcgtRes = readVCGT16(prof); - if (!vcgtRes) - return std::unexpected(vcgtRes.error()); - - image.icc.vcgt = *vcgtRes; - - if (!*vcgtRes) - Log::logger->log(Log::DEBUG, "ICC profile has no VCGT data"); - } else - Log::logger->log(Log::DEBUG, "Skipping VCGT load, disabled by config"); - - Log::logger->log(Log::DEBUG, "============= End ICC load ============="); - - return image; -} \ No newline at end of file diff --git a/src/helpers/defer/Promise.hpp b/src/helpers/defer/Promise.hpp deleted file mode 100644 index 53399bc2..00000000 --- a/src/helpers/defer/Promise.hpp +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -#include -#include -#include "../memory/Memory.hpp" - -// TODO: move into hyprutils - -template -class CPromise; -template -class CPromiseResult; - -template -class CPromiseResolver { - public: - CPromiseResolver(const CPromiseResolver&) = delete; - CPromiseResolver(CPromiseResolver&&) = delete; - CPromiseResolver& operator=(const CPromiseResolver&) = delete; - CPromiseResolver& operator=(CPromiseResolver&&) = delete; - - void resolve(T value) { - if (m_promise->m_result) - return; - - m_promise->m_result = CPromiseResult::result(value); - - if (!m_promise->m_then) - return; - - m_promise->m_then(m_promise->m_result); - } - - void reject(const std::string& reason) { - if (m_promise->m_result) - return; - - m_promise->m_result = CPromiseResult::err(reason); - - if (!m_promise->m_then) - return; - - m_promise->m_then(m_promise->m_result); - } - - private: - CPromiseResolver(SP> promise) : m_promise(promise) {} - - SP> m_promise; - - friend class CPromise; -}; - -template -class CPromiseResult { - public: - bool hasError() { - return m_hasError; - } - - T result() { - return m_result; - } - - std::string error() { - return m_error; - } - - private: - static SP> result(T result) { - auto p = SP>(new CPromiseResult()); - p->m_result = result; - return p; - } - - static SP> err(std::string reason) { - auto p = SP>(new CPromiseResult()); - p->m_error = reason; - p->m_hasError = true; - return p; - } - - T m_result = {}; - std::string m_error = {}; - bool m_hasError = false; - - friend class CPromiseResolver; -}; - -template -class CPromise { - public: - CPromise(const CPromise&) = delete; - CPromise(CPromise&&) = delete; - CPromise& operator=(const CPromise&) = delete; - CPromise& operator=(CPromise&&) = delete; - - static SP make(const std::function>)>& fn) { - auto sp = SP>(new CPromise()); - fn(SP>(new CPromiseResolver(sp))); - return sp; - } - - void then(std::function>)>&& fn) { - m_then = std::move(fn); - if (m_result) - m_then(m_result); - } - - private: - CPromise() = default; - - const std::function>)> m_fn; - std::function>)> m_then; - SP> m_result; - - friend class CPromiseResult; - friend class CPromiseResolver; -}; \ No newline at end of file diff --git a/src/helpers/env/Env.cpp b/src/helpers/env/Env.cpp deleted file mode 100644 index 606d5f72..00000000 --- a/src/helpers/env/Env.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "Env.hpp" - -#include -#include - -bool Env::envEnabled(const std::string& env) { - auto ret = getenv(env.c_str()); - if (!ret) - return false; - - const std::string_view sv = ret; - - return !sv.empty() && sv != "0"; -} - -bool Env::isTrace() { - static bool TRACE = envEnabled("HYPRLAND_TRACE"); - return TRACE; -} diff --git a/src/helpers/env/Env.hpp b/src/helpers/env/Env.hpp deleted file mode 100644 index 030fe736..00000000 --- a/src/helpers/env/Env.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -namespace Env { - bool envEnabled(const std::string& env); - bool isTrace(); -} diff --git a/src/helpers/fs/FsUtils.cpp b/src/helpers/fs/FsUtils.cpp index 60af7d44..0bc2e685 100644 --- a/src/helpers/fs/FsUtils.cpp +++ b/src/helpers/fs/FsUtils.cpp @@ -1,9 +1,8 @@ #include "FsUtils.hpp" -#include "../../debug/log/Logger.hpp" +#include "../../debug/Log.hpp" #include #include -#include #include #include @@ -18,7 +17,7 @@ std::optional NFsUtils::getDataHome() { const auto HOME = getenv("HOME"); if (!HOME) { - Log::logger->log(Log::ERR, "FsUtils::getDataHome: can't get data home: no $HOME or $XDG_DATA_HOME"); + Debug::log(ERR, "FsUtils::getDataHome: can't get data home: no $HOME or $XDG_DATA_HOME"); return std::nullopt; } @@ -28,26 +27,26 @@ std::optional NFsUtils::getDataHome() { std::error_code ec; if (!std::filesystem::exists(dataRoot, ec) || ec) { - Log::logger->log(Log::ERR, "FsUtils::getDataHome: can't get data home: inaccessible / missing"); + Debug::log(ERR, "FsUtils::getDataHome: can't get data home: inaccessible / missing"); return std::nullopt; } dataRoot += "hyprland/"; if (!std::filesystem::exists(dataRoot, ec) || ec) { - Log::logger->log(Log::DEBUG, "FsUtils::getDataHome: no hyprland data home, creating."); + Debug::log(LOG, "FsUtils::getDataHome: no hyprland data home, creating."); std::filesystem::create_directory(dataRoot, ec); if (ec) { - Log::logger->log(Log::ERR, "FsUtils::getDataHome: can't create new data home for hyprland"); + Debug::log(ERR, "FsUtils::getDataHome: can't create new data home for hyprland"); return std::nullopt; } std::filesystem::permissions(dataRoot, std::filesystem::perms::owner_read | std::filesystem::perms::owner_write | std::filesystem::perms::owner_exec, ec); if (ec) - Log::logger->log(Log::WARN, "FsUtils::getDataHome: couldn't set perms on hyprland data store. Proceeding anyways."); + Debug::log(WARN, "FsUtils::getDataHome: couldn't set perms on hyprland data store. Proceeding anyways."); } if (!std::filesystem::exists(dataRoot, ec) || ec) { - Log::logger->log(Log::ERR, "FsUtils::getDataHome: no hyprland data home, failed to create."); + Debug::log(ERR, "FsUtils::getDataHome: no hyprland data home, failed to create."); return std::nullopt; } @@ -70,7 +69,7 @@ std::optional NFsUtils::readFileAsString(const std::string& path) { bool NFsUtils::writeToFile(const std::string& path, const std::string& content) { std::ofstream of(path, std::ios::trunc); if (!of.good()) { - Log::logger->log(Log::ERR, "CVersionKeeperManager: couldn't open an ofstream for writing the version file."); + Debug::log(ERR, "CVersionKeeperManager: couldn't open an ofstream for writing the version file."); return false; } diff --git a/src/helpers/math/Direction.cpp b/src/helpers/math/Direction.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/helpers/math/Direction.hpp b/src/helpers/math/Direction.hpp deleted file mode 100644 index 9905db4f..00000000 --- a/src/helpers/math/Direction.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -namespace Math { - enum eDirection : int8_t { - DIRECTION_DEFAULT = -1, - DIRECTION_UP, - DIRECTION_RIGHT, - DIRECTION_DOWN, - DIRECTION_LEFT - }; - - inline eDirection fromChar(char x) { - switch (x) { - case 'r': return DIRECTION_RIGHT; - case 'l': return DIRECTION_LEFT; - case 't': - case 'u': return DIRECTION_UP; - case 'b': - case 'd': return DIRECTION_DOWN; - default: return DIRECTION_DEFAULT; - } - } - - inline const char* toString(eDirection d) { - switch (d) { - case DIRECTION_UP: return "up"; - case DIRECTION_DOWN: return "down"; - case DIRECTION_LEFT: return "left"; - case DIRECTION_RIGHT: return "right"; - default: return "default"; - } - } -}; \ No newline at end of file diff --git a/src/helpers/math/Expression.cpp b/src/helpers/math/Expression.cpp deleted file mode 100644 index 3c0bee91..00000000 --- a/src/helpers/math/Expression.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "Expression.hpp" -#include "muParser.h" -#include "../../debug/log/Logger.hpp" - -using namespace Math; - -CExpression::CExpression() : m_parser(makeUnique()) { - ; -} - -void CExpression::addVariable(const std::string& name, double val) { - m_parser->DefineConst(name, val); -} - -std::optional CExpression::compute(const std::string& expr) { - try { - m_parser->SetExpr(expr); - return m_parser->Eval(); - } catch (mu::Parser::exception_type& e) { Log::logger->log(Log::ERR, "CExpression::compute: mu threw: {}", e.GetMsg()); } - - return std::nullopt; -} diff --git a/src/helpers/math/Expression.hpp b/src/helpers/math/Expression.hpp deleted file mode 100644 index 1780e3ee..00000000 --- a/src/helpers/math/Expression.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "../memory/Memory.hpp" -#include -#include - -namespace mu { - class Parser; -}; - -namespace Math { - class CExpression { - public: - CExpression(); - ~CExpression() = default; - - CExpression(const CExpression&) = delete; - CExpression(CExpression&) = delete; - CExpression(CExpression&&) = delete; - - void addVariable(const std::string& name, double val); - - std::optional compute(const std::string& expr); - - private: - UP m_parser; - }; -}; \ No newline at end of file diff --git a/src/helpers/math/Math.cpp b/src/helpers/math/Math.cpp index d10997b5..d111690e 100644 --- a/src/helpers/math/Math.cpp +++ b/src/helpers/math/Math.cpp @@ -1,86 +1,23 @@ #include "Math.hpp" -#include "../memory/Memory.hpp" -#include "../../macros.hpp" -#include -#include - -using namespace Math; - -// FIXME: expose in hu -static std::unordered_map transforms = { - {HYPRUTILS_TRANSFORM_NORMAL, std::array{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, - {HYPRUTILS_TRANSFORM_90, std::array{0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, - {HYPRUTILS_TRANSFORM_180, std::array{-1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, - {HYPRUTILS_TRANSFORM_270, std::array{0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, - {HYPRUTILS_TRANSFORM_FLIPPED, std::array{-1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, - {HYPRUTILS_TRANSFORM_FLIPPED_90, std::array{0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, - {HYPRUTILS_TRANSFORM_FLIPPED_180, std::array{1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, - {HYPRUTILS_TRANSFORM_FLIPPED_270, std::array{0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}}, -}; - -eTransform Math::wlTransformToHyprutils(wl_output_transform t) { +Hyprutils::Math::eTransform wlTransformToHyprutils(wl_output_transform t) { switch (t) { - case WL_OUTPUT_TRANSFORM_NORMAL: return eTransform::HYPRUTILS_TRANSFORM_NORMAL; - case WL_OUTPUT_TRANSFORM_180: return eTransform::HYPRUTILS_TRANSFORM_180; - case WL_OUTPUT_TRANSFORM_90: return eTransform::HYPRUTILS_TRANSFORM_90; - case WL_OUTPUT_TRANSFORM_270: return eTransform::HYPRUTILS_TRANSFORM_270; - case WL_OUTPUT_TRANSFORM_FLIPPED: return eTransform::HYPRUTILS_TRANSFORM_FLIPPED; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: return eTransform::HYPRUTILS_TRANSFORM_FLIPPED_180; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: return eTransform::HYPRUTILS_TRANSFORM_FLIPPED_270; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: return eTransform::HYPRUTILS_TRANSFORM_FLIPPED_90; + case WL_OUTPUT_TRANSFORM_NORMAL: return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_NORMAL; + case WL_OUTPUT_TRANSFORM_180: return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_180; + case WL_OUTPUT_TRANSFORM_90: return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_90; + case WL_OUTPUT_TRANSFORM_270: return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_270; + case WL_OUTPUT_TRANSFORM_FLIPPED: return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_FLIPPED; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_FLIPPED_180; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_FLIPPED_270; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_FLIPPED_90; default: break; } - return eTransform::HYPRUTILS_TRANSFORM_NORMAL; + return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_NORMAL; } -wl_output_transform Math::invertTransform(wl_output_transform tr) { +wl_output_transform invertTransform(wl_output_transform tr) { if ((tr & WL_OUTPUT_TRANSFORM_90) && !(tr & WL_OUTPUT_TRANSFORM_FLIPPED)) - tr = sc(tr ^ sc(WL_OUTPUT_TRANSFORM_180)); + tr = (wl_output_transform)(tr ^ (int)WL_OUTPUT_TRANSFORM_180); return tr; } - -static bool matEq(const Mat3x3& a, const Mat3x3& b) { - for (size_t i = 0; i < 9; ++i) { - const float Δ = std::fabs(a.getMatrix()[i] - b.getMatrix()[i]); - if (Δ > 1e-6) // eps - return false; - } - return true; -} - -static eTransform composeInternal(eTransform a, eTransform b) { - const auto& A = transforms.at(a); - const auto& B = transforms.at(b); - const auto RESULT = Mat3x3{A}.multiply(B); - - for (const auto& [t, M] : transforms) { - if (matEq(M, RESULT)) - return t; - } - - return eTransform::HYPRUTILS_TRANSFORM_NORMAL; -} - -eTransform Math::composeTransform(eTransform a, eTransform b) { - static std::array, 8> lookup; - static bool once = true; - - if (once) { - once = false; - - // bake the composition table - static_assert(HYPRUTILS_TRANSFORM_FLIPPED_270 == 7); - for (size_t i = 0; i <= HYPRUTILS_TRANSFORM_FLIPPED_270 /* 7 */; ++i) { - for (size_t j = 0; j <= HYPRUTILS_TRANSFORM_FLIPPED_270 /* 7 */; ++j) { - lookup[i][j] = composeInternal(sc(i), sc(j)); - } - } - } - - RASSERT(a >= HYPRUTILS_TRANSFORM_NORMAL && a <= HYPRUTILS_TRANSFORM_FLIPPED_270, "Invalid transform a in composeTransform"); - RASSERT(b >= HYPRUTILS_TRANSFORM_NORMAL && b <= HYPRUTILS_TRANSFORM_FLIPPED_270, "Invalid transform b in composeTransform"); - - return lookup[a][b]; -} diff --git a/src/helpers/math/Math.hpp b/src/helpers/math/Math.hpp index cc181434..367d8190 100644 --- a/src/helpers/math/Math.hpp +++ b/src/helpers/math/Math.hpp @@ -9,10 +9,5 @@ // NOLINTNEXTLINE using namespace Hyprutils::Math; -namespace Math { - constexpr const Vector2D VECTOR2D_MAX = {std::numeric_limits::max(), std::numeric_limits::max()}; - - eTransform wlTransformToHyprutils(wl_output_transform t); - wl_output_transform invertTransform(wl_output_transform tr); - eTransform composeTransform(eTransform a, eTransform b); -} \ No newline at end of file +eTransform wlTransformToHyprutils(wl_output_transform t); +wl_output_transform invertTransform(wl_output_transform tr); diff --git a/src/helpers/memory/Memory.hpp b/src/helpers/memory/Memory.hpp index 66ba2c1f..e67c2c81 100644 --- a/src/helpers/memory/Memory.hpp +++ b/src/helpers/memory/Memory.hpp @@ -1,15 +1,10 @@ #pragma once #include -#include +//NOLINTNEXTLINE using namespace Hyprutils::Memory; -template -using SP = Hyprutils::Memory::CSharedPointer; -template -using WP = Hyprutils::Memory::CWeakPointer; -template -using UP = Hyprutils::Memory::CUniquePointer; -template -using ASP = Hyprutils::Memory::CAtomicSharedPointer; +#define SP Hyprutils::Memory::CSharedPointer +#define WP Hyprutils::Memory::CWeakPointer +#define UP Hyprutils::Memory::CUniquePointer diff --git a/src/helpers/sync/SyncReleaser.cpp b/src/helpers/sync/SyncReleaser.cpp index fbc585d0..fca7d7c2 100644 --- a/src/helpers/sync/SyncReleaser.cpp +++ b/src/helpers/sync/SyncReleaser.cpp @@ -25,7 +25,7 @@ CSyncReleaser::CSyncReleaser(SP timeline, uint64_t point) : m_tim CSyncReleaser::~CSyncReleaser() { if (!m_timeline) { - Log::logger->log(Log::ERR, "CSyncReleaser destructing without a timeline"); + Debug::log(ERR, "CSyncReleaser destructing without a timeline"); return; } @@ -35,10 +35,7 @@ CSyncReleaser::~CSyncReleaser() { m_timeline->signal(m_point); } -static CFileDescriptor mergeSyncFds(const CFileDescriptor& fd1, const CFileDescriptor& fd2) { - // combines the fences of both sync_fds into a dma_fence_array (https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_fence_array_create) - // with the signal_on_any param set to false, so the new sync_fd will "signal when all fences in the array signal." - +CFileDescriptor CSyncReleaser::mergeSyncFds(const CFileDescriptor& fd1, const CFileDescriptor& fd2) { struct sync_merge_data data{ .name = "merged release fence", .fd2 = fd2.get(), @@ -54,11 +51,13 @@ static CFileDescriptor mergeSyncFds(const CFileDescriptor& fd1, const CFileDescr return CFileDescriptor(data.fence); } -void CSyncReleaser::addSyncFileFd(const Hyprutils::OS::CFileDescriptor& syncFd) { +void CSyncReleaser::addReleaseSync(SP sync) { if (m_fd.isValid()) - m_fd = mergeSyncFds(m_fd, syncFd); + m_fd = mergeSyncFds(m_fd, sync->takeFD()); else - m_fd = syncFd.duplicate(); + m_fd = sync->fd().duplicate(); + + m_sync = sync; } void CSyncReleaser::drop() { diff --git a/src/helpers/sync/SyncReleaser.hpp b/src/helpers/sync/SyncReleaser.hpp index 0063171d..f04e85c1 100644 --- a/src/helpers/sync/SyncReleaser.hpp +++ b/src/helpers/sync/SyncReleaser.hpp @@ -12,6 +12,7 @@ */ class CSyncTimeline; +class CEGLSync; class CSyncReleaser { public: @@ -21,11 +22,13 @@ class CSyncReleaser { // drops the releaser, will never signal anymore void drop(); - // wait for this sync_fd to signal before releasing - void addSyncFileFd(const Hyprutils::OS::CFileDescriptor& syncFd); + // wait for this gpu job to finish before releasing + Hyprutils::OS::CFileDescriptor mergeSyncFds(const Hyprutils::OS::CFileDescriptor& fd1, const Hyprutils::OS::CFileDescriptor& fd2); + void addReleaseSync(SP sync); private: SP m_timeline; uint64_t m_point = 0; Hyprutils::OS::CFileDescriptor m_fd; + SP m_sync; }; diff --git a/src/helpers/sync/SyncTimeline.cpp b/src/helpers/sync/SyncTimeline.cpp index 5a233e48..224b2c0f 100644 --- a/src/helpers/sync/SyncTimeline.cpp +++ b/src/helpers/sync/SyncTimeline.cpp @@ -1,22 +1,18 @@ #include "SyncTimeline.hpp" #include "../../defines.hpp" #include "../../managers/eventLoop/EventLoopManager.hpp" -#include "../../Compositor.hpp" #include #include using namespace Hyprutils::OS; SP CSyncTimeline::create(int drmFD_) { - if (!g_pCompositor->supportsDrmSyncobjTimeline()) - return nullptr; + auto timeline = SP(new CSyncTimeline); + timeline->drmFD = drmFD_; + timeline->self = timeline; - auto timeline = SP(new CSyncTimeline); - timeline->m_drmFD = drmFD_; - timeline->m_self = timeline; - - if (drmSyncobjCreate(drmFD_, 0, &timeline->m_handle)) { - Log::logger->log(Log::ERR, "CSyncTimeline: failed to create a drm syncobj??"); + if (drmSyncobjCreate(drmFD_, 0, &timeline->handle)) { + Debug::log(ERR, "CSyncTimeline: failed to create a drm syncobj??"); return nullptr; } @@ -24,16 +20,13 @@ SP CSyncTimeline::create(int drmFD_) { } SP CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobjFD) { - if (!g_pCompositor->supportsDrmSyncobjTimeline()) - return nullptr; + auto timeline = SP(new CSyncTimeline); + timeline->drmFD = drmFD_; + timeline->syncobjFd = std::move(drmSyncobjFD); + timeline->self = timeline; - auto timeline = SP(new CSyncTimeline); - timeline->m_drmFD = drmFD_; - timeline->m_syncobjFD = std::move(drmSyncobjFD); - timeline->m_self = timeline; - - if (drmSyncobjFDToHandle(drmFD_, timeline->m_syncobjFD.get(), &timeline->m_handle)) { - Log::logger->log(Log::ERR, "CSyncTimeline: failed to create a drm syncobj from fd??"); + if (drmSyncobjFDToHandle(drmFD_, timeline->syncobjFd.get(), &timeline->handle)) { + Debug::log(ERR, "CSyncTimeline: failed to create a drm syncobj from fd??"); return nullptr; } @@ -41,10 +34,17 @@ SP CSyncTimeline::create(int drmFD_, CFileDescriptor&& drmSyncobj } CSyncTimeline::~CSyncTimeline() { - if (m_handle == 0) + for (auto& w : waiters) { + if (w->source) { + wl_event_source_remove(w->source); + w->source = nullptr; + } + } + + if (handle == 0) return; - drmSyncobjDestroy(m_drmFD, m_handle); + drmSyncobjDestroy(drmFD, handle); } std::optional CSyncTimeline::check(uint64_t point, uint32_t flags) { @@ -55,90 +55,151 @@ std::optional CSyncTimeline::check(uint64_t point, uint32_t flags) { #endif uint32_t signaled = 0; - int ret = drmSyncobjTimelineWait(m_drmFD, &m_handle, &point, 1, 0, flags, &signaled); + int ret = drmSyncobjTimelineWait(drmFD, &handle, &point, 1, 0, flags, &signaled); if (ret != 0 && ret != -ETIME_ERR) { - Log::logger->log(Log::ERR, "CSyncTimeline::check: drmSyncobjTimelineWait failed"); + Debug::log(ERR, "CSyncTimeline::check: drmSyncobjTimelineWait failed"); return std::nullopt; } return ret == 0; } -bool CSyncTimeline::addWaiter(std::function&& waiter, uint64_t point, uint32_t flags) { - auto eventFd = CFileDescriptor(eventfd(0, EFD_CLOEXEC)); +static int handleWaiterFD(int fd, uint32_t mask, void* data) { + auto waiter = (CSyncTimeline::SWaiter*)data; - if (!eventFd.isValid()) { - Log::logger->log(Log::ERR, "CSyncTimeline::addWaiter: failed to acquire an eventfd"); + if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { + Debug::log(ERR, "handleWaiterFD: eventfd error"); + return 0; + } + + if (mask & WL_EVENT_READABLE) { + uint64_t value = 0; + if (read(fd, &value, sizeof(value)) <= 0) + Debug::log(ERR, "handleWaiterFD: failed to read from eventfd"); + } + + wl_event_source_remove(waiter->source); + waiter->source = nullptr; + + if (waiter->fn) + waiter->fn(); + + if (waiter->timeline) + waiter->timeline->removeWaiter(waiter); + + return 0; +} + +bool CSyncTimeline::addWaiter(const std::function& waiter, uint64_t point, uint32_t flags) { + auto w = makeShared(); + w->fn = waiter; + w->timeline = self; + w->eventFd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)}; + + if (!w->eventFd.isValid()) { + Debug::log(ERR, "CSyncTimeline::addWaiter: failed to acquire an eventfd"); return false; } - if (drmSyncobjEventfd(m_drmFD, m_handle, point, eventFd.get(), flags)) { - Log::logger->log(Log::ERR, "CSyncTimeline::addWaiter: drmSyncobjEventfd failed"); + drm_syncobj_eventfd syncobjEventFD = { + .handle = handle, + .flags = flags, + .point = point, + .fd = w->eventFd.get(), + }; + + if (drmIoctl(drmFD, DRM_IOCTL_SYNCOBJ_EVENTFD, &syncobjEventFD) != 0) { + Debug::log(ERR, "CSyncTimeline::addWaiter: drmIoctl failed"); return false; } - g_pEventLoopManager->doOnReadable(std::move(eventFd), std::move(waiter)); + w->source = wl_event_loop_add_fd(g_pEventLoopManager->m_sWayland.loop, w->eventFd.get(), WL_EVENT_READABLE, ::handleWaiterFD, w.get()); + if (!w->source) { + Debug::log(ERR, "CSyncTimeline::addWaiter: wl_event_loop_add_fd failed"); + return false; + } + + waiters.emplace_back(w); return true; } +void CSyncTimeline::removeWaiter(SWaiter* w) { + if (w->source) { + wl_event_source_remove(w->source); + w->source = nullptr; + } + std::erase_if(waiters, [w](const auto& e) { return e.get() == w; }); +} + +void CSyncTimeline::removeAllWaiters() { + for (auto& w : waiters) { + if (w->source) { + wl_event_source_remove(w->source); + w->source = nullptr; + } + } + + waiters.clear(); +} + CFileDescriptor CSyncTimeline::exportAsSyncFileFD(uint64_t src) { int sync = -1; uint32_t syncHandle = 0; - if (drmSyncobjCreate(m_drmFD, 0, &syncHandle)) { - Log::logger->log(Log::ERR, "exportAsSyncFileFD: drmSyncobjCreate failed"); + if (drmSyncobjCreate(drmFD, 0, &syncHandle)) { + Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjCreate failed"); return {}; } - if (drmSyncobjTransfer(m_drmFD, syncHandle, 0, m_handle, src, 0)) { - Log::logger->log(Log::ERR, "exportAsSyncFileFD: drmSyncobjTransfer failed"); - drmSyncobjDestroy(m_drmFD, syncHandle); + if (drmSyncobjTransfer(drmFD, syncHandle, 0, handle, src, 0)) { + Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjTransfer failed"); + drmSyncobjDestroy(drmFD, syncHandle); return {}; } - if (drmSyncobjExportSyncFile(m_drmFD, syncHandle, &sync)) { - Log::logger->log(Log::ERR, "exportAsSyncFileFD: drmSyncobjExportSyncFile failed"); - drmSyncobjDestroy(m_drmFD, syncHandle); + if (drmSyncobjExportSyncFile(drmFD, syncHandle, &sync)) { + Debug::log(ERR, "exportAsSyncFileFD: drmSyncobjExportSyncFile failed"); + drmSyncobjDestroy(drmFD, syncHandle); return {}; } - drmSyncobjDestroy(m_drmFD, syncHandle); + drmSyncobjDestroy(drmFD, syncHandle); return CFileDescriptor{sync}; } bool CSyncTimeline::importFromSyncFileFD(uint64_t dst, CFileDescriptor& fd) { uint32_t syncHandle = 0; - if (drmSyncobjCreate(m_drmFD, 0, &syncHandle)) { - Log::logger->log(Log::ERR, "importFromSyncFileFD: drmSyncobjCreate failed"); + if (drmSyncobjCreate(drmFD, 0, &syncHandle)) { + Debug::log(ERR, "importFromSyncFileFD: drmSyncobjCreate failed"); return false; } - if (drmSyncobjImportSyncFile(m_drmFD, syncHandle, fd.get())) { - Log::logger->log(Log::ERR, "importFromSyncFileFD: drmSyncobjImportSyncFile failed"); - drmSyncobjDestroy(m_drmFD, syncHandle); + if (drmSyncobjImportSyncFile(drmFD, syncHandle, fd.get())) { + Debug::log(ERR, "importFromSyncFileFD: drmSyncobjImportSyncFile failed"); + drmSyncobjDestroy(drmFD, syncHandle); return false; } - if (drmSyncobjTransfer(m_drmFD, m_handle, dst, syncHandle, 0, 0)) { - Log::logger->log(Log::ERR, "importFromSyncFileFD: drmSyncobjTransfer failed"); - drmSyncobjDestroy(m_drmFD, syncHandle); + if (drmSyncobjTransfer(drmFD, handle, dst, syncHandle, 0, 0)) { + Debug::log(ERR, "importFromSyncFileFD: drmSyncobjTransfer failed"); + drmSyncobjDestroy(drmFD, syncHandle); return false; } - drmSyncobjDestroy(m_drmFD, syncHandle); + drmSyncobjDestroy(drmFD, syncHandle); return true; } bool CSyncTimeline::transfer(SP from, uint64_t fromPoint, uint64_t toPoint) { - if (m_drmFD != from->m_drmFD) { - Log::logger->log(Log::ERR, "CSyncTimeline::transfer: cannot transfer timelines between gpus, {} -> {}", from->m_drmFD, m_drmFD); + if (drmFD != from->drmFD) { + Debug::log(ERR, "CSyncTimeline::transfer: cannot transfer timelines between gpus, {} -> {}", from->drmFD, drmFD); return false; } - if (drmSyncobjTransfer(m_drmFD, m_handle, toPoint, from->m_handle, fromPoint, 0)) { - Log::logger->log(Log::ERR, "CSyncTimeline::transfer: drmSyncobjTransfer failed"); + if (drmSyncobjTransfer(drmFD, handle, toPoint, from->handle, fromPoint, 0)) { + Debug::log(ERR, "CSyncTimeline::transfer: drmSyncobjTransfer failed"); return false; } @@ -146,6 +207,6 @@ bool CSyncTimeline::transfer(SP from, uint64_t fromPoint, uint64_ } void CSyncTimeline::signal(uint64_t point) { - if (drmSyncobjTimelineSignal(m_drmFD, &m_handle, &point, 1)) - Log::logger->log(Log::ERR, "CSyncTimeline::signal: drmSyncobjTimelineSignal failed"); + if (drmSyncobjTimelineSignal(drmFD, &handle, &point, 1)) + Debug::log(ERR, "CSyncTimeline::signal: drmSyncobjTimelineSignal failed"); } diff --git a/src/helpers/sync/SyncTimeline.hpp b/src/helpers/sync/SyncTimeline.hpp index 5b5aa224..a2422149 100644 --- a/src/helpers/sync/SyncTimeline.hpp +++ b/src/helpers/sync/SyncTimeline.hpp @@ -20,22 +20,33 @@ class CSyncTimeline { static SP create(int drmFD_, Hyprutils::OS::CFileDescriptor&& drmSyncobjFD); ~CSyncTimeline(); + struct SWaiter { + std::function fn; + wl_event_source* source = nullptr; + WP timeline; + Hyprutils::OS::CFileDescriptor eventFd; + }; + // check if the timeline point has been signaled // flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE // std::nullopt on fail std::optional check(uint64_t point, uint32_t flags); - bool addWaiter(std::function&& waiter, uint64_t point, uint32_t flags); + bool addWaiter(const std::function& waiter, uint64_t point, uint32_t flags); + void removeWaiter(SWaiter*); + void removeAllWaiters(); Hyprutils::OS::CFileDescriptor exportAsSyncFileFD(uint64_t src); bool importFromSyncFileFD(uint64_t dst, Hyprutils::OS::CFileDescriptor& fd); bool transfer(SP from, uint64_t fromPoint, uint64_t toPoint); void signal(uint64_t point); - int m_drmFD = -1; - Hyprutils::OS::CFileDescriptor m_syncobjFD; - uint32_t m_handle = 0; - WP m_self; + int drmFD = -1; + Hyprutils::OS::CFileDescriptor syncobjFd; + uint32_t handle = 0; + WP self; private: CSyncTimeline() = default; + + std::vector> waiters; }; diff --git a/src/helpers/time/Time.cpp b/src/helpers/time/Time.cpp deleted file mode 100644 index f454b784..00000000 --- a/src/helpers/time/Time.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "Time.hpp" - -#define chr std::chrono -#define TIMESPEC_NSEC_PER_SEC 1000000000L - -using s_ns = std::pair; - -static s_ns timediff(const s_ns& a, const s_ns& b) { - s_ns d; - - d.first = a.first - b.first; - if (a.second >= b.second) - d.second = a.second - b.second; - else { - d.second = (TIMESPEC_NSEC_PER_SEC + a.second) - b.second; - d.first -= 1; - } - - return d; -} - -static s_ns timeadd(const s_ns& a, const s_ns& b) { - s_ns d; - - d.first = a.first + b.first; - if (a.second + b.second >= TIMESPEC_NSEC_PER_SEC) { - d.second = a.second + b.second - TIMESPEC_NSEC_PER_SEC; - d.first += 1; - } else - d.second = a.second + b.second; - - return d; -} - -Time::steady_tp Time::steadyNow() { - return chr::steady_clock::now(); -} - -Time::system_tp Time::systemNow() { - return chr::system_clock::now(); -} - -uint64_t Time::millis(const steady_tp& tp) { - return chr::duration_cast(tp.time_since_epoch()).count(); -} - -s_ns Time::secNsec(const steady_tp& tp) { - const uint64_t sec = chr::duration_cast(tp.time_since_epoch()).count(); - const auto nsecdur = tp - chr::steady_clock::time_point(chr::seconds(sec)); - return {sec, chr::duration_cast(nsecdur).count()}; -} - -uint64_t Time::millis(const system_tp& tp) { - return chr::duration_cast(tp.time_since_epoch()).count(); -} - -s_ns Time::secNsec(const system_tp& tp) { - const uint64_t sec = chr::duration_cast(tp.time_since_epoch()).count(); - const auto nsecdur = tp - chr::system_clock::time_point(chr::seconds(sec)); - return {sec, chr::duration_cast(nsecdur).count()}; -} - -// TODO: this is a mess, but C++ doesn't define what steady_clock is. -// At least on Linux, system_clock == CLOCK_REALTIME -// and steady_clock == CLOCK_MONOTONIC, -// or at least it seems so with gcc and gcc's stl. -// but, since we can't *ever* be sure, we have to guess. -// In general, this may shift the time around by a couple hundred ns. Doesn't matter, realistically. - -Time::steady_tp Time::fromTimespec(const timespec* ts) { - timespec mono{}, real{}; - clock_gettime(CLOCK_MONOTONIC, &mono); - clock_gettime(CLOCK_REALTIME, &real); - auto now = Time::steadyNow(); - auto nowSys = Time::systemNow(); - s_ns stdSteady, stdReal; - stdSteady = Time::secNsec(now); - stdReal = Time::secNsec(nowSys); - - // timespec difference, REAL - MONO - s_ns diff; - diff.first = real.tv_sec - mono.tv_sec; - if (real.tv_nsec >= mono.tv_nsec) - diff.second = real.tv_nsec - mono.tv_nsec; - else { - diff.second = TIMESPEC_NSEC_PER_SEC + real.tv_nsec - mono.tv_nsec; - diff.first -= 1; - } - - // STD difference, REAL - MONO - s_ns diff2 = timediff(stdReal, stdSteady); - - s_ns diffFinal; - s_ns monotime = {ts->tv_sec, ts->tv_nsec}; - - if (diff.first >= diff2.first || (diff.first == diff2.first && diff.second >= diff2.second)) - diffFinal = timediff(diff, diff2); - else - diffFinal = timediff(diff2, diff); - - auto sum = timeadd(monotime, diffFinal); - return chr::steady_clock::time_point(std::chrono::seconds(sum.first)) + chr::nanoseconds(sum.second); -} - -struct timespec Time::toTimespec(const steady_tp& tp) { - timespec mono{}, real{}; - clock_gettime(CLOCK_MONOTONIC, &mono); - clock_gettime(CLOCK_REALTIME, &real); - Time::steady_tp now = Time::steadyNow(); - Time::system_tp nowSys = Time::systemNow(); - s_ns stdSteady, stdReal; - stdSteady = Time::secNsec(now); - stdReal = Time::secNsec(nowSys); - - // timespec difference, REAL - MONO - s_ns diff; - diff.first = real.tv_sec - mono.tv_sec; - if (real.tv_nsec >= mono.tv_nsec) - diff.second = real.tv_nsec - mono.tv_nsec; - else { - diff.second = TIMESPEC_NSEC_PER_SEC + real.tv_nsec - mono.tv_nsec; - diff.first -= 1; - } - - // STD difference, REAL - MONO - s_ns diff2 = timediff(stdReal, stdSteady); - - s_ns diffFinal; - s_ns tpTime = secNsec(tp); - - if (diff.first >= diff2.first || (diff.first == diff2.first && diff.second >= diff2.second)) - diffFinal = timediff(diff, diff2); - else - diffFinal = timediff(diff2, diff); - - auto sum = timeadd(tpTime, diffFinal); - return timespec{.tv_sec = sum.first, .tv_nsec = sum.second}; -} - -Time::steady_dur Time::till(const timespec& ts) { - timespec mono{}; - clock_gettime(CLOCK_MONOTONIC, &mono); - const auto delay = (ts.tv_sec - mono.tv_sec) * 1000000000 + (ts.tv_nsec - mono.tv_nsec); - return std::chrono::nanoseconds(delay); -} diff --git a/src/helpers/time/Time.hpp b/src/helpers/time/Time.hpp deleted file mode 100644 index ce99982b..00000000 --- a/src/helpers/time/Time.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -//NOLINTNEXTLINE -namespace Time { - using steady_tp = std::chrono::steady_clock::time_point; - using system_tp = std::chrono::system_clock::time_point; - using steady_dur = std::chrono::steady_clock::duration; - using system_dur = std::chrono::system_clock::duration; - - steady_tp steadyNow(); - system_tp systemNow(); - - steady_tp fromTimespec(const timespec*); - struct timespec toTimespec(const steady_tp& tp); - steady_dur till(const timespec& ts); - - uint64_t millis(const steady_tp& tp); - uint64_t millis(const system_tp& tp); - std::pair secNsec(const steady_tp& tp); - std::pair secNsec(const system_tp& tp); -}; \ No newline at end of file diff --git a/src/helpers/time/Timer.cpp b/src/helpers/time/Timer.cpp deleted file mode 100644 index 97338b31..00000000 --- a/src/helpers/time/Timer.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "Timer.hpp" - -#define chr std::chrono - -void CTimer::reset() { - m_lastReset = Time::steadyNow(); -} - -Time::steady_dur CTimer::getDuration() const { - return Time::steadyNow() - m_lastReset; -} - -float CTimer::getMillis() const { - return chr::duration_cast(getDuration()).count() / 1000.F; -} - -float CTimer::getSeconds() const { - return chr::duration_cast(getDuration()).count() / 1000.F; -} - -const Time::steady_tp& CTimer::chrono() const { - return m_lastReset; -} diff --git a/src/helpers/time/Timer.hpp b/src/helpers/time/Timer.hpp deleted file mode 100644 index 3d5e0197..00000000 --- a/src/helpers/time/Timer.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Time.hpp" - -class CTimer { - public: - void reset(); - float getSeconds() const; - float getMillis() const; - const Time::steady_tp& chrono() const; - - private: - Time::steady_tp m_lastReset; - - Time::steady_dur getDuration() const; -}; diff --git a/src/helpers/varlist/VarList.hpp b/src/helpers/varlist/VarList.hpp index ca68751e..4cdc1728 100644 --- a/src/helpers/varlist/VarList.hpp +++ b/src/helpers/varlist/VarList.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include //NOLINTNEXTLINE using namespace Hyprutils::String; diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp index 60bf0a78..9f889fdf 100644 --- a/src/hyprerror/HyprError.cpp +++ b/src/hyprerror/HyprError.cpp @@ -2,57 +2,57 @@ #include "HyprError.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" -#include "../config/ConfigManager.hpp" #include "../render/pass/TexPassElement.hpp" -#include "../managers/animation/AnimationManager.hpp" +#include "../managers/AnimationManager.hpp" #include "../render/Renderer.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../event/EventBus.hpp" +#include "../managers/HookSystemManager.hpp" #include using namespace Hyprutils::Animation; CHyprError::CHyprError() { - g_pAnimationManager->createAnimation(0.f, m_fadeOpacity, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), AVARDAMAGE_NONE); + g_pAnimationManager->createAnimation(0.f, m_fFadeOpacity, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), AVARDAMAGE_NONE); - static auto P = Event::bus()->m_events.monitor.focused.listen([&](PHLMONITOR mon) { - if (!m_isCreated) + static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) { + if (!m_bIsCreated) return; - g_pHyprRenderer->damageMonitor(Desktop::focusState()->monitor()); - m_monitorChanged = true; + g_pHyprRenderer->damageMonitor(g_pCompositor->m_pLastMonitor.lock()); + m_bMonitorChanged = true; }); - static auto P2 = Event::bus()->m_events.render.pre.listen([&](PHLMONITOR mon) { - if (!m_isCreated) + static auto P2 = g_pHookSystem->hookDynamic("preRender", [&](void* self, SCallbackInfo& info, std::any param) { + if (!m_bIsCreated) return; - if (m_fadeOpacity->isBeingAnimated() || m_monitorChanged) - g_pHyprRenderer->damageBox(m_damageBox); + if (m_fFadeOpacity->isBeingAnimated() || m_bMonitorChanged) + g_pHyprRenderer->damageBox(m_bDamageBox); }); + + m_pTexture = makeShared(); } void CHyprError::queueCreate(std::string message, const CHyprColor& color) { - m_queued = message; - m_queuedColor = color; + m_szQueued = message; + m_cQueued = color; } void CHyprError::createQueued() { - if (m_isCreated && m_texture) - m_texture.reset(); + if (m_bIsCreated) + m_pTexture->destroyTexture(); - m_fadeOpacity->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn")); + m_fFadeOpacity->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn")); - m_fadeOpacity->setValueAndWarp(0.f); - *m_fadeOpacity = 1.f; + m_fFadeOpacity->setValueAndWarp(0.f); + *m_fFadeOpacity = 1.f; - const auto PMONITOR = g_pCompositor->m_monitors.front(); + const auto PMONITOR = g_pCompositor->m_vMonitors.front(); - const auto SCALE = PMONITOR->m_scale; + const auto SCALE = PMONITOR->scale; - const auto FONTSIZE = std::clamp(sc(10.f * ((PMONITOR->m_pixelSize.x * SCALE) / 1920.f)), 8, 40); + const auto FONTSIZE = std::clamp((int)(10.f * ((PMONITOR->vecPixelSize.x * SCALE) / 1920.f)), 8, 40); - const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y); + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y); const auto CAIRO = cairo_create(CAIROSURFACE); @@ -62,7 +62,7 @@ void CHyprError::createQueued() { cairo_paint(CAIRO); cairo_restore(CAIRO); - const auto LINECOUNT = Hyprlang::INT{1} + std::ranges::count(m_queued, '\n'); + const auto LINECOUNT = Hyprlang::INT{1} + std::count(m_szQueued.begin(), m_szQueued.end(), '\n'); static auto LINELIMIT = CConfigValue("debug:error_limit"); static auto BAR_POSITION = CConfigValue("debug:error_position"); @@ -75,14 +75,13 @@ void CHyprError::createQueued() { const double PAD = 10 * SCALE; - const double WIDTH = PMONITOR->m_pixelSize.x - PAD * 2; + const double WIDTH = PMONITOR->vecPixelSize.x - PAD * 2; const double HEIGHT = (FONTSIZE + 2 * (FONTSIZE / 10.0)) * (VISLINECOUNT + EXTRALINES) + 3; const double RADIUS = PAD > HEIGHT / 2 ? HEIGHT / 2 - 1 : PAD; const double X = PAD; - const double Y = TOPBAR ? PAD : PMONITOR->m_pixelSize.y - HEIGHT - PAD; + const double Y = TOPBAR ? PAD : PMONITOR->vecPixelSize.y - HEIGHT - PAD; - m_damageBox = {sc(PMONITOR->m_position.x), sc(PMONITOR->m_position.y + (TOPBAR ? 0 : PMONITOR->m_pixelSize.y - (HEIGHT + PAD * 2))), sc(PMONITOR->m_pixelSize.x), - sc(HEIGHT + PAD * 2)}; + m_bDamageBox = {0, 0, (int)PMONITOR->vecPixelSize.x, (int)HEIGHT + (int)PAD * 2}; cairo_new_sub_path(CAIRO); cairo_arc(CAIRO, X + WIDTH - RADIUS, Y + RADIUS, RADIUS, -90 * DEGREES, 0 * DEGREES); @@ -93,7 +92,7 @@ void CHyprError::createQueued() { cairo_set_source_rgba(CAIRO, 0.06, 0.06, 0.06, 1.0); cairo_fill_preserve(CAIRO); - cairo_set_source_rgba(CAIRO, m_queuedColor.r, m_queuedColor.g, m_queuedColor.b, m_queuedColor.a); + cairo_set_source_rgba(CAIRO, m_cQueued.r, m_cQueued.g, m_cQueued.b, m_cQueued.a); cairo_set_line_width(CAIRO, 2); cairo_stroke(CAIRO); @@ -110,17 +109,15 @@ void CHyprError::createQueued() { pango_font_description_set_style(pangoFD, PANGO_STYLE_NORMAL); pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL); pango_layout_set_font_description(layoutText, pangoFD); - pango_layout_set_width(layoutText, (WIDTH - 2 * (1 + RADIUS)) * PANGO_SCALE); - pango_layout_set_ellipsize(layoutText, PANGO_ELLIPSIZE_END); float yoffset = TOPBAR ? 0 : Y - PAD; int renderedcnt = 0; - while (!m_queued.empty() && renderedcnt < VISLINECOUNT) { - std::string current = m_queued.substr(0, m_queued.find('\n')); - if (const auto NEWLPOS = m_queued.find('\n'); NEWLPOS != std::string::npos) - m_queued = m_queued.substr(NEWLPOS + 1); + while (!m_szQueued.empty() && renderedcnt < VISLINECOUNT) { + std::string current = m_szQueued.substr(0, m_szQueued.find('\n')); + if (const auto NEWLPOS = m_szQueued.find('\n'); NEWLPOS != std::string::npos) + m_szQueued = m_szQueued.substr(NEWLPOS + 1); else - m_queued = ""; + m_szQueued = ""; cairo_move_to(CAIRO, PAD + 1 + RADIUS, yoffset + PAD + 1); pango_layout_set_text(layoutText, current.c_str(), -1); pango_cairo_show_layout(CAIRO, layoutText); @@ -133,8 +130,9 @@ void CHyprError::createQueued() { pango_layout_set_text(layoutText, moreString.c_str(), -1); pango_cairo_show_layout(CAIRO, layoutText); } + m_szQueued = ""; - m_lastHeight = HEIGHT; + m_fLastHeight = yoffset + PAD + 1 - (TOPBAR ? 0 : Y - PAD); pango_font_description_free(pangoFD); g_object_unref(layoutText); @@ -143,105 +141,89 @@ void CHyprError::createQueued() { // copy the data to an OpenGL texture we have const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - auto tex = texture(); - tex->allocate(PMONITOR->m_pixelSize); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); + 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); // delete cairo cairo_destroy(CAIRO); cairo_surface_destroy(CAIROSURFACE); - m_isCreated = true; - m_queued = ""; - m_queuedColor = CHyprColor(); + m_bIsCreated = true; + m_szQueued = ""; + m_cQueued = CHyprColor(); g_pHyprRenderer->damageMonitor(PMONITOR); - for (const auto& m : g_pCompositor->m_monitors) { - m->m_reservedArea.resetType(Desktop::RESERVED_DYNAMIC_TYPE_ERROR_BAR); - } - - const auto RESERVED = (HEIGHT + PAD) / SCALE; - PMONITOR->m_reservedArea.addType(Desktop::RESERVED_DYNAMIC_TYPE_ERROR_BAR, Vector2D{0.0, TOPBAR ? RESERVED : 0.0}, Vector2D{0.0, !TOPBAR ? RESERVED : 0.0}); - - for (const auto& m : g_pCompositor->m_monitors) { - g_pHyprRenderer->arrangeLayersForMonitor(m->m_id); - } + g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID); } void CHyprError::draw() { - if (!m_isCreated || !m_queued.empty()) { - if (!m_queued.empty()) + if (!m_bIsCreated || m_szQueued != "") { + if (m_szQueued != "") createQueued(); return; } - if (m_queuedDestroy) { - if (!m_fadeOpacity->isBeingAnimated()) { - if (m_fadeOpacity->value() == 0.f) { - m_queuedDestroy = false; - if (m_texture) - m_texture.reset(); - m_isCreated = false; - m_queued = ""; + if (m_bQueuedDestroy) { + if (!m_fFadeOpacity->isBeingAnimated()) { + if (m_fFadeOpacity->value() == 0.f) { + m_bQueuedDestroy = false; + m_pTexture->destroyTexture(); + m_bIsCreated = false; + m_szQueued = ""; - for (auto& m : g_pCompositor->m_monitors) { - g_pHyprRenderer->arrangeLayersForMonitor(m->m_id); - m->m_reservedArea.resetType(Desktop::RESERVED_DYNAMIC_TYPE_ERROR_BAR); + for (auto& m : g_pCompositor->m_vMonitors) { + g_pHyprRenderer->arrangeLayersForMonitor(m->ID); } return; } else { - m_fadeOpacity->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeOut")); - *m_fadeOpacity = 0.f; + m_fFadeOpacity->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeOut")); + *m_fFadeOpacity = 0.f; } } } - const auto PMONITOR = g_pHyprOpenGL->m_renderData.pMonitor; + const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor; - CBox monbox = {0, 0, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y}; + CBox monbox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y}; - static auto BAR_POSITION = CConfigValue("debug:error_position"); - m_damageBox.x = sc(PMONITOR->m_position.x); - m_damageBox.y = sc(PMONITOR->m_position.y + (*BAR_POSITION == 0 ? 0 : PMONITOR->m_pixelSize.y - m_damageBox.height)); + m_bDamageBox.x = (int)PMONITOR->vecPosition.x; + m_bDamageBox.y = (int)PMONITOR->vecPosition.y; - if (m_fadeOpacity->isBeingAnimated() || m_monitorChanged) - g_pHyprRenderer->damageBox(m_damageBox); + if (m_fFadeOpacity->isBeingAnimated() || m_bMonitorChanged) + g_pHyprRenderer->damageBox(m_bDamageBox); - m_monitorChanged = false; + m_bMonitorChanged = false; CTexPassElement::SRenderData data; - data.tex = texture(); + data.tex = m_pTexture; data.box = monbox; - data.a = m_fadeOpacity->value(); + data.a = m_fFadeOpacity->value(); - g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } void CHyprError::destroy() { - if (m_isCreated) - m_queuedDestroy = true; + if (m_bIsCreated) + m_bQueuedDestroy = true; else - m_queued = ""; + m_szQueued = ""; } bool CHyprError::active() { - return m_isCreated; + return m_bIsCreated; } float CHyprError::height() { - return m_lastHeight; + return m_fLastHeight; } - -SP CHyprError::texture() { - if (!m_texture) - m_texture = g_pHyprRenderer->createTexture(); - return m_texture; -} \ No newline at end of file diff --git a/src/hyprerror/HyprError.hpp b/src/hyprerror/HyprError.hpp index 48b9e805..12de0c81 100644 --- a/src/hyprerror/HyprError.hpp +++ b/src/hyprerror/HyprError.hpp @@ -18,21 +18,18 @@ class CHyprError { bool active(); float height(); // logical - // - SP texture(); - private: void createQueued(); - std::string m_queued = ""; - CHyprColor m_queuedColor; - bool m_queuedDestroy = false; - bool m_isCreated = false; - SP m_texture; - PHLANIMVAR m_fadeOpacity; - CBox m_damageBox = {0, 0, 0, 0}; - float m_lastHeight = 0.F; + std::string m_szQueued = ""; + CHyprColor m_cQueued; + bool m_bQueuedDestroy = false; + bool m_bIsCreated = false; + SP m_pTexture; + PHLANIMVAR m_fFadeOpacity; + CBox m_bDamageBox = {0, 0, 0, 0}; + float m_fLastHeight = 0.F; - bool m_monitorChanged = false; + bool m_bMonitorChanged = false; }; inline UP g_pHyprError; // This is a full-screen error. Treat it with respect, and there can only be one at a time. diff --git a/src/i18n/Engine.cpp b/src/i18n/Engine.cpp deleted file mode 100644 index c68400eb..00000000 --- a/src/i18n/Engine.cpp +++ /dev/null @@ -1,1693 +0,0 @@ -#include "Engine.hpp" - -#include -#include "../config/ConfigValue.hpp" - -using namespace I18n; -using namespace Hyprutils::I18n; - -static SP huEngine; -static std::string localeStr; - -// -SP I18n::i18nEngine() { - static SP engine = makeShared(); - return engine; -} - -I18n::CI18nEngine::CI18nEngine() { - huEngine = makeShared(); - huEngine->setFallbackLocale("en_US"); - localeStr = huEngine->getSystemLocale().locale(); - - // be_BY (Belarusian) - huEngine->registerEntry("be_BY", TXT_KEY_ANR_TITLE, "Праграма не адказвае"); - huEngine->registerEntry("be_BY", TXT_KEY_ANR_CONTENT, "Праграма {title} - {class} не адказвае.\nШто хочаце з ёй зрабіць?"); - huEngine->registerEntry("be_BY", TXT_KEY_ANR_OPTION_TERMINATE, "Прымусова спыніць"); - huEngine->registerEntry("be_BY", TXT_KEY_ANR_OPTION_WAIT, "Пачакаць"); - huEngine->registerEntry("be_BY", TXT_KEY_ANR_PROP_UNKNOWN, "(невядома)"); - - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Праграма {app} запытвае невядомы дазвол."); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Праграма {app} спрабуе здымаць экран.\n\nЦі хочаце дазволіць?"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Праграма {app} спрабуе загрузіць плагін: {plugin}.\n\nХочаце дазволіць?"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Выяўленая новая клавіятура: {keyboard}.\n\nХочаце дазволіць яе выкарыстанне?"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(невядома)"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_TITLE, "Запыт дазволу"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Падказка: вы можаце задаць пастаянныя правілы для гэтага ў файле канфігурацыі Hyprland."); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_ALLOW, "Дазволіць"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Дазволіць і запомніць"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_ALLOW_ONCE, "Дазволіць аднойчы"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_DENY, "Забараніць"); - huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Невядомая праграма (Ідэнтыфікатар кліента wayland {wayland_id})"); - - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Выглядае, што вашая пераменная асяроддзя XDG_CURRENT_DESKTOP зададзеная звонку, цяперашняе значэнне: {value}.\nГэта можа выклікаць праблемы, калі " - "гэта не зроблена наўмысна."); - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_NO_GUIUTILS, - "У вашай сістэме не ўсталяваны hyprland-guiutils, што выкарыстоўваецца для некаторых дыялогавых вокнаў. Разгледзьце ўсталёўку пакета."); - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland не змог загрузіць {count} важны рэсурс, вінавацьце ў гэтым адказнага за зборку пакетаў для свайго дыстрыбутыва!"; - return "Hyprland не змог загрузіць {count} важных рэсурсаў, вінавацьце ў гэтым адказнага за зборку пакетаў для свайго дыстрыбутыва!"; - }); - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Макет манітораў наладжаны некарэктна. Манітор {name} накладаецца на іншы(я) манітор(ы).\nДля падрабязнасцей звярніцеся да Wiki (Старонка Monitors). " - "Гэта абавязкова створыць праблемы."); - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Манітор {name} не змог наладзіць ніводны з запатрабаваных рэжымаў, аварыйна ўжыты рэжым {mode}."); - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Няверна зададзены маштаб для манітора {name}: {scale}, ужываецца прапанаваны маштаб: {fixed_scale}"); - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Не атрымалася загрузіць плагін {name}: {error}"); - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Не атрымалася перазагрузіць шэйдар CM, аварыйна ўжываецца rgba/rgbx."); - huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Манітор {name}: пашыраны каляровы дыяпазон уключаны, але экран не ў рэжыме 10-біт."); - - // bn_BD (Bengali) - huEngine->registerEntry("bn_BD", TXT_KEY_ANR_TITLE, "অ্যাপ্লিকেশন সাড়া দিচ্ছে না"); - huEngine->registerEntry("bn_BD", TXT_KEY_ANR_CONTENT, "অ্যাপ্লিকেশন {title} - {class} সাড়া দিচ্ছে না।\nআপনি এটি নিয়ে কি করতে চান?"); - huEngine->registerEntry("bn_BD", TXT_KEY_ANR_OPTION_TERMINATE, "বন্ধ করুন"); - huEngine->registerEntry("bn_BD", TXT_KEY_ANR_OPTION_WAIT, "অপেক্ষা করুন"); - huEngine->registerEntry("bn_BD", TXT_KEY_ANR_PROP_UNKNOWN, "(অজানা)"); - - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "একটি অ্যাপ্লিকেশন {app} একটি অজানা অনুমতির অনুরোধ করছে।"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "একটি অ্যাপ্লিকেশন {app} আপনার স্ক্রিন রেকর্ড করার চেষ্টা করছে।\n\nআপনি কি এটি অনুমতি দিতে চান?"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_REQUEST_PLUGIN, - "একটি অ্যাপ্লিকেশন {app} একটি প্লাগইন লোড করার চেষ্টা করছে: {plugin}।\n\nআপনি কি এটি অনুমতি দিতে চান?"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "একটি নতুন কীবোর্ড সনাক্ত করা হয়েছে: {keyboard}।\n\nআপনি কি এটি কাজ করতে অনুমতি দিতে চান?"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(অজানা)"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_TITLE, "অনুমতির অনুরোধ"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "টিপ: আপনি Hyprland কনফিগারেশন ফাইলে এর জন্য স্থায়ী নিয়ম সেট করতে পারেন।"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_ALLOW, "অনুমতি দিন"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "অনুমতি দিন এবং মনে রাখুন"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_ALLOW_ONCE, "একবার অনুমতি দিন"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_DENY, "প্রত্যাখ্যান করুন"); - huEngine->registerEntry("bn_BD", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "অজানা অ্যাপ্লিকেশন (wayland ক্লায়েন্ট ID {wayland_id})"); - - huEngine->registerEntry( - "bn_BD", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "আপনার XDG_CURRENT_DESKTOP পরিবেশ পরিবর্তনশীল বাহ্যিকভাবে পরিচালিত হচ্ছে বলে মনে হচ্ছে, বর্তমান মান: {value}।\nএটি সমস্যা সৃষ্টি করতে পারে যদি না এটি ইচ্ছাকৃত হয়।"); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_NO_GUIUTILS, "আপনার সিস্টেমে hyprland-guiutils ইনস্টল নেই যা কিছু ডায়ালগের জন্য ব্যবহৃত হয়। এটি ইনস্টল করার কথা বিবেচনা করুন।"); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland {count}টি প্রয়োজনীয় সম্পদ লোড করতে ব্যর্থ হয়েছে, খারাপ প্যাকেজিং কাজের জন্য আপনার ডিস্ট্রো প্যাকেজারদের দোষ দিন!"; - return "Hyprland {count}টি প্রয়োজনীয় সম্পদ লোড করতে ব্যর্থ হয়েছে, খারাপ প্যাকেজিং কাজের জন্য আপনার ডিস্ট্রো প্যাকেজারদের দোষ দিন!"; - }); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "আপনার মনিটর লেআউট ভুলভাবে কনফিগার করা হয়েছে। মনিটর {name} লেআউটে অন্য মনিটর(গুলি) এর সাথে ওভারল্যাপ করছে।\nবিস্তারিত জানতে wiki (Monitors page) দেখুন। " - "এটি অবশ্যই সমস্যা সৃষ্টি করবে।"); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "মনিটর {name} কোনো অনুরোধকৃত মোড সেট করতে পারেনি, মোড {mode} এ ফিরে যাচ্ছে।"); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "মনিটর {name} এর জন্য অবৈধ স্কেল পাঠানো হয়েছে: {scale}, প্রস্তাবিত স্কেল ব্যবহার করা হচ্ছে: {fixed_scale}"); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "প্লাগইন {name} লোড করতে ব্যর্থ: {error}"); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM শেডার পুনরায় লোড করতে ব্যর্থ, rgba/rgbx এ ফিরে যাচ্ছে।"); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "মনিটর {name}: ওয়াইড কালার গ্যামুট সক্রিয় কিন্তু স্ক্রিন 10-বিট মোডে নেই।"); - huEngine->registerEntry("bn_BD", TXT_KEY_NOTIF_NO_WATCHDOG, "Hyprland start-hyprland ছাড়া চালু করা হয়েছে। এটি অত্যন্ত সুপারিশকৃত নয় যদি না আপনি ডিবাগিং পরিবেশে থাকেন।"); - - huEngine->registerEntry("bn_BD", TXT_KEY_SAFE_MODE_TITLE, "নিরাপদ মোড"); - huEngine->registerEntry( - "bn_BD", TXT_KEY_SAFE_MODE_DESCRIPTION, - "Hyprland নিরাপদ মোডে চালু করা হয়েছে, যার মানে আপনার শেষ সেশন ক্র্যাশ হয়েছিল।\nনিরাপদ মোড আপনার কনফিগ লোড হওয়া থেকে প্রতিরোধ করে। আপনি " - "এই পরিবেশে সমস্যা সমাধান করতে পারেন, অথবা নিচের বাটন দিয়ে আপনার কনফিগ লোড করতে পারেন।\nডিফল্ট কীবাইন্ড প্রযোজ্য: kitty এর জন্য SUPER+Q, মৌলিক রানারের জন্য SUPER+R, " - "প্রস্থান করতে SUPER+M।\nHyprland পুনরায় চালু করলে আবার স্বাভাবিক মোডে চালু হবে।"); - huEngine->registerEntry("bn_BD", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "কনফিগ লোড করুন"); - huEngine->registerEntry("bn_BD", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "ক্র্যাশ রিপোর্ট ডিরেক্টরি খুলুন"); - huEngine->registerEntry("bn_BD", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "ঠিক আছে, এটি বন্ধ করুন"); - - // da_DK (Danish) - huEngine->registerEntry("da_DK", TXT_KEY_ANR_TITLE, "Applikationen Svarer Ikke"); - huEngine->registerEntry("da_DK", TXT_KEY_ANR_CONTENT, "En applikation {title} - {class} svarer ikke.\nHvad vil du gøre ved det?"); - huEngine->registerEntry("da_DK", TXT_KEY_ANR_OPTION_TERMINATE, "Luk"); - huEngine->registerEntry("da_DK", TXT_KEY_ANR_OPTION_WAIT, "Vent"); - huEngine->registerEntry("da_DK", TXT_KEY_ANR_PROP_UNKNOWN, "(ukendt)"); - - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "En applikation {app} forespørger en ukendt rettighed."); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "En applikation {app} forsøger at optage din skærm.\n\nVil du tillade dette?"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "En applikation {app} forsøger at indlæse et plugin: {plugin}.\n\nVil du tillade dette?"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Et nyt tastatur er fundet: {keyboard}.\n\nVil du tillade den at fungere?"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(ukendt)"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_TITLE, "Anmodning om tilladelse"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tip: Du kan indstille vedvarende regler for disse i Hyprland-konfigurationsfilen."); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_ALLOW, "Tillad"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Tillad og husk"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_ALLOW_ONCE, "Tillad én gang"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_DENY, "Nægt"); - huEngine->registerEntry("da_DK", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Ukendt applikation (wayland client ID {wayland_id})"); - - huEngine->registerEntry( - "da_DK", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Dit XDG_CURRENT_DESKTOP miljø ser ud til at være administreret externt, og den nuværende værdi er {value}.\nDette kan forårsage problemer, medmindre det er bevidst."); - huEngine->registerEntry("da_DK", TXT_KEY_NOTIF_NO_GUIUTILS, - "Dit system har ikke hyprland-guiutils installeret. Dette er en runtime-afhængighed for nogle dialoger. Overvej at installere den."); - huEngine->registerEntry("da_DK", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland kunne ikke indlæse {count} essentiale aktiver, skyd skylden på din distributions pakker for et dårligt stykke arbejde af pakningen!"; - return "Hyprland kunne ikke indlæse {count} essentiale aktiver, skyd skylden på din distributions pakker for et dårligt stykke arbejde af pakningen!"; - }); - huEngine->registerEntry("da_DK", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Dit skærmlayout har en ukorrekt opsætning. Skærm {name} overlapper med andre skærm(e) i layoutet.\nLæs venligst wiki'en (Monitors page) for " - "mere. Dette vil skabe problemer."); - huEngine->registerEntry("da_DK", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Skærm {name} kunne ikke indlæse nogen af de ønskede tilstande, vender tilbage til tilstand {mode}."); - huEngine->registerEntry("da_DK", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Ugyldig skalering sendt til skærm {name}: {scale}, bruger foreslået skalering: {fixed_scale}"); - huEngine->registerEntry("da_DK", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Kunne ikke indlæse plugin {name}: {error}"); - huEngine->registerEntry("da_DK", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Genindlæsning af CM-shader mislykkedes, går tilbage til rgba/rgbx."); - huEngine->registerEntry("da_DK", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Skærm {name}: wide color gamut er aktiveret men skærmen er ikke i 10-bit tilstand."); - - // en_US (English) - huEngine->registerEntry("en_US", TXT_KEY_ANR_TITLE, "Application Not Responding"); - huEngine->registerEntry("en_US", TXT_KEY_ANR_CONTENT, "An application {title} - {class} is not responding.\nWhat do you want to do with it?"); - huEngine->registerEntry("en_US", TXT_KEY_ANR_OPTION_TERMINATE, "Terminate"); - huEngine->registerEntry("en_US", TXT_KEY_ANR_OPTION_WAIT, "Wait"); - huEngine->registerEntry("en_US", TXT_KEY_ANR_PROP_UNKNOWN, "(unknown)"); - - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "An application {app} is requesting an unknown permission."); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "An application {app} is trying to capture your screen.\n\nDo you want to allow it to?"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_CURSOR_POS, - "An application {app} is trying to capture your cursor position.\n\nDo you want to allow it to?"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "An application {app} is trying to load a plugin: {plugin}.\n\nDo you want to allow it to?"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "A new keyboard has been detected: {keyboard}.\n\nDo you want to allow it to operate?"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(unknown)"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_TITLE, "Permission request"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Hint: you can set persistent rules for these in the Hyprland config file."); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW, "Allow"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Allow and remember"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW_ONCE, "Allow once"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_DENY, "Deny"); - huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Unknown application (wayland client ID {wayland_id})"); - - huEngine->registerEntry( - "en_US", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Your XDG_CURRENT_DESKTOP environment seems to be managed externally, and the current value is {value}.\nThis might cause issues unless it's intentional."); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_NO_GUIUTILS, - "Your system does not have hyprland-guiutils installed. This is a runtime dependency for some dialogs. Consider installing it."); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland failed to load {count} essential asset, blame your distro's packager for doing a bad job at packaging!"; - return "Hyprland failed to load {count} essential assets, blame your distro's packager for doing a bad job at packaging!"; - }); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Your monitor layout is set up incorrectly. Monitor {name} overlaps with other monitor(s) in the layout.\nPlease see the wiki (Monitors page) for " - "more. This will cause issues."); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} failed to set any requested modes, falling back to mode {mode}."); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Invalid scale passed to monitor {name}: {scale}, using suggested scale: {fixed_scale}"); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Failed to load plugin {name}: {error}"); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader reload failed, falling back to rgba/rgbx."); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: wide color gamut is enabled but the display is not in 10-bit mode."); - huEngine->registerEntry("en_US", TXT_KEY_NOTIF_NO_WATCHDOG, - "Hyprland was started without start-hyprland. This is highly not recommended unless you are in a debugging environment."); - - huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_TITLE, "Safe Mode"); - huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_DESCRIPTION, - "Hyprland has been launched in safe mode, which means your last session crashed.\nSafe mode prevents your config from being loaded. You can " - "troubleshoot in this environment, or load your config with the button below.\nDefault keybinds apply: SUPER+Q for kitty, SUPER+R for a basic runner, " - "SUPER+M to exit.\nRestarting " - "Hyprland will launch in normal mode again."); - huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "Load config"); - huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "Open crash report directory"); - huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "Ok, close this"); - - // as_IN (Assamese) - huEngine->registerEntry("as_IN", TXT_KEY_ANR_TITLE, "এপ্লিকেচনে উত্তৰ দিয়া নাই"); - huEngine->registerEntry("as_IN", TXT_KEY_ANR_CONTENT, "এপ্লিকেচন {title} - {class}-এ উত্তৰ দিয়া নাই।\nআপুনি এয়াৰ লগত কি কৰিব বিচাৰে?"); - huEngine->registerEntry("as_IN", TXT_KEY_ANR_OPTION_TERMINATE, "সমাপ্ত কৰক"); - huEngine->registerEntry("as_IN", TXT_KEY_ANR_OPTION_WAIT, "অপেক্ষা কৰক"); - huEngine->registerEntry("as_IN", TXT_KEY_ANR_PROP_UNKNOWN, "(অজ্ঞাত)"); - - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "এপ্লিকেচন {app}-এ এটা অজ্ঞাত অনুমতি বিচাৰিছে।"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "এটা এপ্লিকেচন {app}-এ আপোনাৰ স্ক্ৰীণ কেপচাৰ কৰিবলৈ চেষ্টা কৰিছে।\n\nআপুনি ইয়াক অনুমতি দিব বিচাৰেনে?"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, - "এপ্লিকেচন {app}-এ এটা প্লাগিন লোড কৰিবলৈ চেষ্টা কৰিছে: {plugin}।\n\nআপুনি ইয়াক অনুমতি দিব বিচাৰেনে?"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "এটা নতুন কিবৰ্ড ধৰা পৰিছে: {keyboard}।\n\nআপুনি ইয়াক চলাবলৈ অনুমতি দিব বিচাৰেনে?"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(অজ্ঞাত)"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_TITLE, "অনুমতিৰ অনুৰোধ"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "ইঙ্গিত: আপুনি হাইপাৰলেণ্ড কনফিগ ফাইলত এইবোৰৰ বাবে স্থায়ী নিয়ম স্থাপন কৰিব পাৰে।"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_ALLOW, "অনুমতি দিয়ক"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "অনুমতি দি মনত ৰাখক"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_ALLOW_ONCE, "এবাৰ অনুমতি দিয়ক"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_DENY, "অস্বীকাৰ কৰক"); - huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "অজ্ঞাত এপ্লিকেচন (ৱেইলেণ্ড ক্লায়েণ্ট আইডি {wayland_id})"); - - huEngine->registerEntry( - "as_IN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "আপোনাৰ XDG_CURRENT_DESKTOP পৰিৱেশটো বাহ্যিকভাৱে পৰিচালিত হোৱা যেন লাগিছে, আৰু বৰ্তমানৰ মান হৈছে {value}।\nযদি ই ইচ্ছাকৃতভাৱে নহয়, তেনে হলে সমস্যাৰ সৃষ্টি হ'ব পাৰে।"); - huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_NO_GUIUTILS, - "আপোনাৰ চিষ্টেমত hyprland-guiutils ইনষ্টল কৰা নাই। কিছুমান ডাইলগৰ বাবে ই এটা ৰানটাইম নিৰ্ভৰশীলতা। ইয়াক ইনষ্টল কৰাৰ কথা চিন্তা কৰক।"); - huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_FAILED_ASSETS, - "হাইপাৰলেণ্ড {count}-টা প্ৰয়োজনীয় সম্পদ লোড কৰাত অসফল হৈছে, বেয়া পেকজিং কৰাৰ বাবে আপোনাৰ ডিষ্ট্ৰ'ৰ পেকেজাৰক দোষাৰোপ কৰক!"); - huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "আপোনাৰ মনিটৰৰ লেআউট ভুলকৈ ছেট কৰা হৈছে। মনিটৰ {name} লেআউটত আন মনিটৰ(সমূহ)ৰ সৈতে ওপৰা-উপৰি হৈ আছে।\nঅধিক তথ্যৰ বাবে অনুগ্ৰহ কৰি ৱিকি (মনিটৰ পৃষ্ঠা) চাওক। ই " - "সমস্যাৰ সৃষ্টি কৰিব।"); - huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "মনিটৰ {name}-এ কোনো অনুৰোধ কৰা মোড ছেট কৰাত অসফল হৈছে, মোড {mode}-লৈ ঘূৰি আহিছে।"); - huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "মনিটৰ {name}: {scale}-লৈ অবৈধ মাপন দিয়া হৈছে, পৰামৰ্শ দিয়া মাপন ব্যৱহাৰ কৰা যাব: {fixed_scale}"); - huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "প্লাগিন {name} লোড কৰাত অসফল হৈছে: {error}"); - huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM শ্বেডাৰ ৰিলোড কৰাত অসফল হৈছে, rgba/rgbx-লৈ ঘূৰি আহিছে।"); - huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "প্ৰসাৰিত ৰঙৰ বৰ্গ সক্ষম কৰা হৈছে কিন্তু ডিচপ্লে 10-বিট মোডত নাই।"); - - // de_DE (German) - huEngine->registerEntry("de_DE", TXT_KEY_ANR_TITLE, "Anwendung Reagiert Nicht"); - huEngine->registerEntry("de_DE", TXT_KEY_ANR_CONTENT, "Eine Anwendung {title} - {class} reagiert nicht.\nWas möchten Sie damit tun?"); - huEngine->registerEntry("de_DE", TXT_KEY_ANR_OPTION_TERMINATE, "Beenden"); - huEngine->registerEntry("de_DE", TXT_KEY_ANR_OPTION_WAIT, "Warten"); - huEngine->registerEntry("de_DE", TXT_KEY_ANR_PROP_UNKNOWN, "(unbekannt)"); - - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Eine Anwendung {app} fordert eine unbekannte Berechtigung an."); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Eine Anwendung {app} versucht Ihren Bildschrim aufzunehmen.\n\nMöchten Sie dies erlauben?"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Eine Anwendung {app} versucht ein Plugin zu laden: {plugin}.\n\nMöchten Sie dies erlauben?"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Eine neue Tastatur wurde erkannt: {keyboard}.\n\nMöchten Sie diese in Betrieb nehmen?"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(unbekannt)"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_TITLE, "Berechtigungsanfrage"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tip: Sie können dafür permanente Regeln in der Hyprland-Konfigurationsdatei festlegen."); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_ALLOW, "Erlauben"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Erlauben und merken"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_ALLOW_ONCE, "Einmal erlauben"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_DENY, "Verweigern"); - huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Unbekannte Anwendung (wayland client ID {wayland_id})"); - - huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Ihre XDG_CURRENT_DESKTOP umgebung scheint extern gemanagt zu werden, und der aktuelle Wert ist {value}.\nDies könnte zu Problemen führen sofern es " - "nicht absichtlich so ist."); - huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_NO_GUIUTILS, - "Ihr System hat hyprland-guiutils nicht installiert. Dies ist eine Laufzeitabhängigkeit für einige Dialoge. Es ist empfohlen diese zu installieren."); - huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland konnte {count} essentielle Ressource nicht laden, geben Sie dem Packager ihrer Distribution die Schuld für ein schlechtes Package!"; - return "Hyprland konnte {count} essentielle Ressroucen nicht laden, geben Sie dem Packager ihrer Distribution die Schuld für ein schlechtes Package!"; - }); - huEngine->registerEntry( - "de_DE", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Ihr Bildschirmlayout ist fehlerhaft aufgesetzt. Der Bildschirm {name} überlappt mit anderen Bildschirm(en) im Layout.\nBitte siehe im Wiki (Monitors Seite) für " - "mehr Informationen. Dies wird zu Problemen führen."); - huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Bildschirm {name} konnte keinen der angeforderten Modi setzen fällt auf den Modus {mode} zurück."); - huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "Ungültiger Skalierungsfaktor {scale} für Bildschirm {name}, es wird der empfohlene Faktor {fixed_scale} verwendet."); - huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Plugin {name} konnte nicht geladen werden: {error}"); - huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader konnte nicht neu geladen werden und es wird auf rgba/rgbx zurückgefallen."); - huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Bildschirm {name}: wide color gamut ist aktiviert aber der Bildschirm ist nicht im 10-bit Modus."); - - // de_CH (Swiss German) - huEngine->registerEntry("de_CH", TXT_KEY_ANR_TITLE, "Aawändig Reagiert Ned"); - huEngine->registerEntry("de_CH", TXT_KEY_ANR_CONTENT, "En Aawändig {title} - {class} reagiert ned.\nWas wend Sie demet mache?"); - huEngine->registerEntry("de_CH", TXT_KEY_ANR_OPTION_TERMINATE, "Beände"); - huEngine->registerEntry("de_CH", TXT_KEY_ANR_OPTION_WAIT, "Warte"); - huEngine->registerEntry("de_CH", TXT_KEY_ANR_PROP_UNKNOWN, "(onbekannt)"); - - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "En Aawändig {app} fordert en onbekannti Berächtigong aa."); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "En Aawändig {app} versuecht Ehre Beldscherm uufznäh.\n\nWend Sie das erlaube?"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "En Aawändig {app} versuecht es Plugin z'lade: {plugin}.\n\nWend Sie das erlaube?"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "En neui Tastatur esch erkönne worde: {keyboard}.\n\nWend sie die in Betreb nä?"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(onbekannt)"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_TITLE, "Berächtigongsaafrog"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tip: Sie chönd permanenti Regle deför i ehrere Hyprland-Konfigurationsdatei festlegge."); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_ALLOW, "Erlaube"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Erlaube ond merke"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_ALLOW_ONCE, "Einisch erlaube"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_DENY, "Verweigere"); - huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Onbekannti Aawändig (wayland client ID {wayland_id})"); - - huEngine->registerEntry( - "de_CH", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Ehri XDG_CURRENT_DESKTOP omgäbig schiint extern gmanagt z'wärde, ond de aktuelli Wärt esch {value}.\nDas chönnt zo Problem füehre sofärn das ned absechtlech so esch."); - huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_NO_GUIUTILS, - "Ehres System hed hyprland-guiutils ned installiert. Das esch en Laufziitabhängigkeit för es paar Dialog. Es werd empfohle sie z'installiere."); - huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_FAILED_ASSETS, - "Hyprland hed {count} essentielli Ressource ned chönne lade, gäbed Sie im Packager vo ehrere Distribution schold för es schlächts Package!"); - huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Ehres Beldschermlayout esch fählerhaft uufgsetzt. De Beldscherm {name} öberlappt met andere Beldscherm(e) im Layout.\nBitte lueged sie im Wiki " - "(Monitors Siite) för meh Informatione. Das werd zo Problem füehre."); - huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "De Beldscherm {name} hed keine vode aagforderete Modi chönne setze, ond fallt uf de Modus {mode} zrogg."); - huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "Ongöltige Skalierigsfaktor {scale} för de Beldscherm {name}, es werd de empfohleni Faktor {fixed_scale} verwändet."); - huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "S Plugin {name} hed ned chönne glade wärde: {error}"); - huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader hed ned chönne neu glade wärde, es werd uf rgba/rgbx zrogggfalle."); - huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Beldscherm {name}: wide color gamut esch aktiviert aber de Beldscherm esch ned im 10-bit Modus."); - - // pt_BR (Brazilian Portuguese) - huEngine->registerEntry("pt_BR", TXT_KEY_ANR_TITLE, "O aplicativo não está respondendo"); - huEngine->registerEntry("pt_BR", TXT_KEY_ANR_CONTENT, "O aplicativo {title} - {class} não está respondendo.\nO que você deseja fazer?"); - huEngine->registerEntry("pt_BR", TXT_KEY_ANR_OPTION_TERMINATE, "Encerrar"); - huEngine->registerEntry("pt_BR", TXT_KEY_ANR_OPTION_WAIT, "Esperar"); - huEngine->registerEntry("pt_BR", TXT_KEY_ANR_PROP_UNKNOWN, "(Desconhecido)"); - - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "O aplicativo {app} está pedindo uma permissão desconhecida."); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "O aplicativo {app} está tentando capturar sua tela.\n\nVocê deseja permitir?"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "O aplicativo {app} está tentando carregar um plugin: {plugin}.\n\nVocê deseja permitir?"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Um novo teclado foi detectado: {keyboard}.\n\nVocê deseja permitir seu uso?"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(Desconhecido)"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_TITLE, "Solicitação de permissão"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_PERSISTENCE_HINT, - "Dica: você pode definir regras persistentes para essas permissões no arquivo de configuração do Hyprland"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_ALLOW, "Permitir"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permitir e lembrar"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permitir uma vez"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_DENY, "Negar"); - huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Aplicativo desconhecido (wayland client ID {wayland_id})"); - - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Seu XDG_CURRENT_DESKTOP parece estar sendo gerenciado externamente, e atualmente é {value}.\nIsso pode causar problemas caso não seja intencional."); - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_NO_GUIUTILS, - "Seu sistema não possui hyprland-guiutils instalado. Essa é uma dependência de execução para alguns diálogos. Considere instalá-lo."); - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "O Hyprland falhou ao carregar {count} recurso essencial, culpe o empacotador da sua distro por fazer um péssimo trabalho!"; - return "O Hyprland falhou ao carregar {count} recursos essenciais, culpe o empacotador da sua distro por fazer um péssimo trabalho!"; - }); - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Sua disposição de monitores está configurada incorretamente. O monitor {name} se sobrepõe a outro(s) monitor(es) na disposição.\nPor favor consulte " - "a wiki (Monitors page) para " - "mais informações. Isso vai causar problemas."); - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "O monitor {name} falhou em definir qualquer um dos modos solicitados, voltando ao modo {mode}."); - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "Um fator de escala inválido foi passado para o monitor {name}: {scale}, usando o fator sugerido: {fixed_scale}"); - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Falha ao carregar o plugin {name}: {error}"); - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Falha ao carregar o shader CM, voltando para rgba/rgbx."); - huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: o modo de gama de cores amplo está ativado, mas a tela não está configurada para 10 bits."); - - // es (Spanish) - huEngine->registerEntry("es", TXT_KEY_ANR_TITLE, "La aplicación no responde"); - huEngine->registerEntry("es", TXT_KEY_ANR_CONTENT, "La aplicación {title} - {class} no responde.\n¿Qué deseas hacer?"); - huEngine->registerEntry("es", TXT_KEY_ANR_OPTION_TERMINATE, "Forzar cierre"); - huEngine->registerEntry("es", TXT_KEY_ANR_OPTION_WAIT, "Esperar"); - huEngine->registerEntry("es", TXT_KEY_ANR_PROP_UNKNOWN, "(desconocido)"); - - huEngine->registerEntry("es", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Una aplicación {app} está solicitando un permiso desconocido."); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Una aplicación {app} está intentando capturar la pantalla.\n\n¿Deseas permitirlo?"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Una aplicación {app} está intentando cargar un plugin: {plugin}.\n\n¿Deseas permitirlo?"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Se ha detectado un nuevo teclado: {keyboard}.\n\n¿Deseas permitir su uso?"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(desconocido)"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_TITLE, "Solicitud de permiso"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_PERSISTENCE_HINT, - "Sugerencia: puedes establecer reglas persistentes para estos permisos en el archivo de configuración de Hyprland."); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_ALLOW, "Permitir"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permitir y recordar"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permitir una vez"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_DENY, "Denegar"); - huEngine->registerEntry("es", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Aplicación desconocida (ID de cliente de Wayland: {wayland_id})"); - - huEngine->registerEntry( - "es", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "La variable de entorno XDG_CURRENT_DESKTOP parece gestionarse externamente; su valor actual es {value}.\nEsto podría causar problemas a menos que sea intencional."); - huEngine->registerEntry("es", TXT_KEY_NOTIF_NO_GUIUTILS, - "Tu sistema no tiene instalado 'hyprland-guiutils'. Es una dependencia en tiempo de ejecución para algunos diálogos. Considera instalarlo."); - huEngine->registerEntry("es", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "No se pudo cargar {count} recurso esencial. Contacta al empaquetador de tu distribución."; - return "No se pudieron cargar {count} recursos esenciales. Contacta al empaquetador de tu distribución."; - }); - huEngine->registerEntry("es", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "La configuración de tus monitores no es correcta. El monitor {name} se superpone con otros monitores en la disposición. Consulta la wiki (página " - "Monitors, en inglés) para más información. Esto provocará problemas."); - huEngine->registerEntry("es", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "El monitor {name} no pudo configurar ninguno de los modos solicitados y ha vuelto al modo {mode}."); - huEngine->registerEntry("es", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Se pasó una escala no válida al monitor {name}: {scale}; se usará la escala sugerida: {fixed_scale}"); - huEngine->registerEntry("es", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Error al cargar el plugin {name}: {error}"); - huEngine->registerEntry("es", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Error al recargar el shader CM; volviendo a rgba/rgbx."); - huEngine->registerEntry("es", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: la gama de color amplia está habilitada, pero la pantalla no está en modo de 10 bits."); - - // fa_IR (Persian) - huEngine->registerEntry("fa_IR", TXT_KEY_ANR_TITLE, "برنامه پاسخ نمی‌دهد"); - huEngine->registerEntry("fa_IR", TXT_KEY_ANR_CONTENT, "برنامه {title} - {class} پاسخی نمی‌دهد.\nمی‌خواهید چه کاری انجام شود؟"); - huEngine->registerEntry("fa_IR", TXT_KEY_ANR_OPTION_TERMINATE, "بستن برنامه"); - huEngine->registerEntry("fa_IR", TXT_KEY_ANR_OPTION_WAIT, "صبر کنید"); - huEngine->registerEntry("fa_IR", TXT_KEY_ANR_PROP_UNKNOWN, "(نامشخص)"); - - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "برنامه {app} در حال درخواست یک مجوز ناشناخته است."); - - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, - "برنامه {app} می‌خواهد صفحه‌نمایش شما را ضبط کند.\n\nآیا اجازه می‌دهید؟"); - - huEngine->registerEntry( - "fa_IR", TXT_KEY_PERMISSION_REQUEST_PLUGIN, - "برنامه {app} می‌خواهد پلاگین {plugin} را بارگذاری کند.\n\nآیا اجازه می‌دهید پلاگین بارگذاری " - "شود؟"); - - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, - "یک کیبورد جدید شناسایی شد: {keyboard}.\n\nآیا اجازه استفاده از آن را صادر می‌کنید؟"); - - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(نامشخص)"); - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_TITLE, "درخواست مجوز"); - - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_PERSISTENCE_HINT, - "نکته: می‌توانید قوانین دائمی مرتبط را در فایل تنظیمات هایپرلند تعریف کنید."); - - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_ALLOW, "اجازه"); - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "اجازه و ذخیره"); - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_ALLOW_ONCE, "اجازه یک‌بار"); - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_DENY, "عدم اجازه"); - - huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "برنامه ناشناخته (شناسه Wayland: {wayland_id})"); - - huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "متغیر XDG_CURRENT_DESKTOP توسط محیطی خارجی تنظیم شده است و مقدار فعلی آن {value} است.\n" - "اگر این کار عمدی نباشد ممکن است باعث ایجاد مشکل شود."); - - huEngine->registerEntry( - "fa_IR", TXT_KEY_NOTIF_NO_GUIUTILS, - "بستهٔ hyprland-guiutils در سیستم نصب نیست. این بسته برای برخی از پنجره‌ها و دیالوگ‌ها لازم است. نصب " - "آن " - "پیشنهاد " - "می‌شود."); - - huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "هایپرلند نتوانست یک فایل ضروری را بارگذاری کند؛ ممکن است بسته‌بندی توزیع مشکل داشته " - "باشد."; - return "هایپرلند نتوانست {count} فایل ضروری را بارگذاری کند؛ ممکن است بسته‌بندی توزیع مشکل داشته " - "باشد."; - }); - - huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "چیدمان مانیتورها صحیح نیست. مانیتور {name} با یک یا چند مانیتور دیگر تداخل دارد.\n" - "برای اطلاعات بیشتر به صفحهٔ مانیتورها در ویکی مراجعه کنید. این موضوع حتماً باعث مشکل " - "می‌شود."); - - huEngine->registerEntry( - "fa_IR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, - "مانیتور {name} نتوانست هیچ‌کدام از حالت‌های درخواستی را اعمال کند؛ بازگشت به حالت {mode}."); - - huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "مقیاس واردشده برای مانیتور {name} نامعتبر است: {scale}. مقیاس پیشنهادی اعمال شد: {fixed_scale}"); - - huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "بارگذاری پلاگین {name} با خطا روبه‌رو شد: {error}"); - - huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "بارگذاری دوبارهٔ شیدر CM ناموفق بود؛ از حالت rgba/rgbx استفاده شد."); - - huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "مانیتور {name}: گسترهٔ رنگ وسیع فعال است اما نمایشگر در حالت ۱۰ بیتی نیست."); - - // fi_FI (Finnish) - huEngine->registerEntry("fi_FI", TXT_KEY_ANR_TITLE, "Sovellus ei vastaa"); - huEngine->registerEntry("fi_FI", TXT_KEY_ANR_CONTENT, "Sovellus {title} - {class} ei vastaa.\nMitä haluat tehdä sille?"); - huEngine->registerEntry("fi_FI", TXT_KEY_ANR_OPTION_TERMINATE, "Lopeta"); - huEngine->registerEntry("fi_FI", TXT_KEY_ANR_OPTION_WAIT, "Odota"); - huEngine->registerEntry("fi_FI", TXT_KEY_ANR_PROP_UNKNOWN, "(tuntematon)"); - - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Sovellus {app} pyytää tuntematonta käyttöoikeutta."); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Sovellus {app} yrittää nauhoittaa näyttöäsi.\n\nHaluatko sallia nauhoituksen?"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Sovellus {app} yrittää ladata laajennusta: {plugin}.\n\nHaluatko sallia latauksen?"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Uusi näppäimistö havaittu: {keyboard}.\n\nHaluatko sallia sen toiminnan?"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(tuntematon)"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_TITLE, "Käyttöoikeuspyyntö"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Vihje: voit asettaa nämä säännöt pysyvästi Hyprland konfiguraatio tiedostossa."); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_ALLOW, "Salli"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Salli ja muista"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_ALLOW_ONCE, "Salli kerran"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_DENY, "Kiellä"); - huEngine->registerEntry("fi_FI", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Tuntematon sovellus (wayland client ID {wayland_id})"); - - huEngine->registerEntry("fi_FI", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "XDG_CURRENT_DESKTOP ympäristösi näyttäisi olevan ulkoisesti hallittu, ja sen nykyinen arvo on {value}.\nTämä voi aiheuttaa ongelmia, jos sitä ei ole " - "tehty tarkoituksella."); - huEngine->registerEntry("fi_FI", TXT_KEY_NOTIF_NO_GUIUTILS, - "Paketti hyprland-guiutils ei ole asennettuna järjestelmääsi. Jotkin dialogit tarvitsevat sitä. Harkitse sen asentamista."); - huEngine->registerEntry("fi_FI", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland epäonnistui olennaisen resurssin ({count}) latauksessa. Tämä johtuu todennäköisesti jakelusi virheellisestä pakkauksesta."; - return "Hyprland epäonnistui olennaisten resurssien ({count}) latauksessa. Tämä johtuu todennäköisesti jakelusi virheellisestä pakkauksesta."; - }); - huEngine->registerEntry( - "fi_FI", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Näyttöjesi asettelu on virheellinen. Näyttö {name} on muiden näyttöjen päällä.\nLisätietoja löydät wikistä (Monitors sivu). Tämä tulee aiheuttamaan ongelmia."); - huEngine->registerEntry("fi_FI", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Näyttö {name} epäonnistui pyydetyn tilan asettamisessa, palataan tilaan {mode}."); - huEngine->registerEntry("fi_FI", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Näytölle {name} asetettu skaalaus: {scale} on virheellinen, asetetaan suositeltu skaalaus: {fixed_scale}."); - huEngine->registerEntry("fi_FI", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Laajennuksen {name} lataus epäonnistui: {error}"); - huEngine->registerEntry("fi_FI", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM varjostimen uudelleenlataus epäonnistui, palataan takaisin rgba/rgbx tilaan."); - huEngine->registerEntry("fi_FI", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Näyttö {name}: laaja väriskaala on otettu käyttöön, mutta näyttö ei ole 10-bit tilassa."); - - // fr_FR (French) - huEngine->registerEntry("fr_FR", TXT_KEY_ANR_TITLE, "L'application ne répond plus"); - huEngine->registerEntry("fr_FR", TXT_KEY_ANR_CONTENT, "L'application {title} - {class} ne répond plus.\nQue voulez-vous faire?"); - huEngine->registerEntry("fr_FR", TXT_KEY_ANR_OPTION_TERMINATE, "Forcer l'arrêt"); - huEngine->registerEntry("fr_FR", TXT_KEY_ANR_OPTION_WAIT, "Attendre"); - huEngine->registerEntry("fr_FR", TXT_KEY_ANR_PROP_UNKNOWN, "(inconnu)"); - - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Une application {app} demande une autorisation inconnue."); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Une application {app} tente de capturer votre écran.\n\nVoulez-vous l'autoriser?"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Une application {app} tente de charger un module : {plugin}.\n\nVoulez-vous l'autoriser?"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Un nouveau clavier a été détecté : {keyboard}.\n\nVoulez-vous l'autoriser à fonctionner?"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(inconnu)"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_TITLE, "Demande d'autorisation"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Astuce: vous pouvez définir des règles persistantes dans le fichier de configuration de Hyprland."); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_ALLOW, "Autoriser"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Autoriser et mémoriser"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_ALLOW_ONCE, "Autoriser une fois"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_DENY, "Refuser"); - huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Application inconnue (ID client wayland {wayland_id})"); - - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Votre variable d'environnement XDG_CURRENT_DESKTOP semble être gérée de manière externe, et sa valeur actuelle est {value}.\nCela peut provoquer des " - "problèmes si ce n'est pas intentionnel."); - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_NO_GUIUTILS, - "Votre système n'a pas hyprland-guiutils installé. C'est une dépendance d'éxécution pour certains dialogues. Envisagez de l'installer."); - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland n'a pas pu charger {count} ressource essentielle, cela indique très probablement un problème dans le paquet de votre distribution."; - return "Hyprland n'a pas pu charger {count} ressources essentielles, cela indique très probablement un problème dans le paquet de votre distribution."; - }); - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Votre disposition d'écrans est incorrecte. Le moniteur {name} chevauche un ou plusieurs autres.\nVeuillez consulter le wiki (page Moniteurs) pour" - "en savoir plus. Cela causera des problèmes."); - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Le moniteur {name} n'a pu appliquer les modes demandés, retour au mode {mode}."); - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Échelle invalide pour le moniteur {name}: {scale}. Utilisation de l'échelle suggérée: {fixed_scale}."); - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Échec du chargement du module {name} : {error}"); - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Le rechargement du shader CM a échoué, retour aux formats rgba/rgbx"); - huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Moniteur {name} : l'espace colorimétrique étendu est activé, mais l'écran n'est pas en mode 10-bits."); - - // hi_IN (Hindi) - huEngine->registerEntry("hi_IN", TXT_KEY_ANR_TITLE, "एप्लिकेशन प्रतिक्रिया नहीं दे रहा है"); - huEngine->registerEntry("hi_IN", TXT_KEY_ANR_CONTENT, - "एक एप्लिकेशन {title} - {class} प्रतिक्रिया नहीं दे रहा " - "है।\nआप इसके साथ क्या करना चाहेंगे?"); - huEngine->registerEntry("hi_IN", TXT_KEY_ANR_OPTION_TERMINATE, "समाप्त करें"); - huEngine->registerEntry("hi_IN", TXT_KEY_ANR_OPTION_WAIT, "इंतजार करें"); - huEngine->registerEntry("hi_IN", TXT_KEY_ANR_PROP_UNKNOWN, "(अज्ञात)"); - - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "एक एप्लिकेशन {app} एक अज्ञात अनुमति का अनुरोध कर रहा है।"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, - "एक एप्लिकेशन {app} आपकी स्क्रीन कैप्चर करने की " - "कोशिश कर रहा है।\n\nक्या आप इसे अनुमति देना चाहते हैं?"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, - "एक एप्लिकेशन {app} एक प्लगइन लोड करने की कोशिश कर रहा है: " - "{plugin}.\n\nक्या आप इसे अनुमति देना चाहते हैं?"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, - "नया कीबोर्ड पाया गया: {keyboard}.\n\nक्या आप " - "इसे काम करने की अनुमति देना चाहते हैं?"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(अज्ञात)"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_TITLE, "अनुमति अनुरोध"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "संकेत: आप Hyprland कॉन्फ़िग फ़ाइल में इनके लिए स्थायी नियम सेट कर सकते हैं।"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_ALLOW, "अनुमति दें"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "अनुमति दें और याद रखें"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_ALLOW_ONCE, "एक बार अनुमति दें"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_DENY, "अस्वीकार करें"); - huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "अज्ञात एप्लिकेशन (wayland क्लाइंट ID {wayland_id})"); - - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "आपका XDG_CURRENT_DESKTOP परिवेश बाहरी रूप से प्रबंधित लगता है, और वर्तमान मान " - "{value} है।\nयह समस्या पैदा कर सकता " - "है जब तक कि यह जानबूझकर न किया गया हो।"); - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_NO_GUIUTILS, - "आपके सिस्टम में hyprland-guiutils इंस्टॉल नहीं है। यह कुछ संवादों के लिए एक रनटाइम " - "निर्भरता है। इसे इंस्टॉल करने पर विचार करें।"); - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland {count} आवश्यक संसाधन लोड करने में विफल रहा, अपने डिस्ट्रो " - "के पैकेजर को पैकेजिंग में खराब काम करने का दोष दें!"; - return "Hyprland {count} आवश्यक संसाधनों को लोड करने में विफल रहा, अपने " - "डिस्ट्रो के पैकेजर को पैकेजिंग में खराब काम करने का दोष दें!"; - }); - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "आपका मॉनिटर लेआउट गलत तरीके से सेट है। मॉनिटर {name} लेआउट में अन्य मॉनिटर(ओं) के " - "साथ ओवरलैप कर रहा है।\nकृपया विकि " - " (Monitors पेज) देखें। यह समस्याएँ पैदा करेगा।"); - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, - "मॉनिटर {name} ने किसी भी अनुरोधित मोड को सेट करने में " - "विफल रहा, मोड {mode} पर वापस जा रहा है।"); - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "मॉनिटर {name} को अवैध स्केल दिया गया: {scale}, सुझाया " - "गया स्केल इस्तेमाल किया जा रहा है: {fixed_scale}"); - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "प्लगइन {name} लोड करने में विफल: {error}"); - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM शेडर रीलोड विफल हुआ, rgba/rgbx पर वापस जा रहा है।"); - huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "मॉनिटर {name}: वाइड कलर गैम सक्षम है लेकिन डिस्प्ले 10-बिट मोड में नहीं है।"); - - // id_ID (Indonesia) - huEngine->registerEntry("id_ID", TXT_KEY_ANR_TITLE, "Aplikasi Tidak Merespon"); - huEngine->registerEntry("id_ID", TXT_KEY_ANR_CONTENT, "Aplikasi {title} - {class} tidak merespon.\nApa yang ingin Anda lakukan?"); - huEngine->registerEntry("id_ID", TXT_KEY_ANR_OPTION_TERMINATE, "Hentikan"); - huEngine->registerEntry("id_ID", TXT_KEY_ANR_OPTION_WAIT, "Tunggu"); - huEngine->registerEntry("id_ID", TXT_KEY_ANR_PROP_UNKNOWN, "(tidak diketahui)"); - - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Aplikasi {app} meminta izin yang tidak dikenali."); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Aplikasi {app} mencoba merekam layar Anda.\n\nApakah Anda mengizinkannya?"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Aplikasi {app} mencoba memuat plugin: {plugin}.\n\nApakah Anda mengizinkannya?"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Keyboard baru terdeteksi: {keyboard}.\n\nApakah Anda mengizinkannya beroperasi?"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(tidak diketahui)"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_TITLE, "Permintaan Izin"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Petunjuk: Anda dapat mengatur rule ini secara permanen di file konfigurasi Hyprland."); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_ALLOW, "Izinkan"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Izinkan dan Ingat"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_ALLOW_ONCE, "Izinkan Sekali"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_DENY, "Tolak"); - huEngine->registerEntry("id_ID", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Aplikasi tidak dikenal (ID klien wayland {wayland_id})"); - - huEngine->registerEntry("id_ID", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Variabel environment XDG_CURRENT_DESKTOP Anda tampaknya dikelola secara eksternal, nilainya saat ini: {value}.\nHal ini dapat menyebabkan " - "masalah, kecuali jika disengaja."); - huEngine->registerEntry( - "id_ID", TXT_KEY_NOTIF_NO_GUIUTILS, - "hyprland-guiutils belum terpasang di Sistem Anda. Paket tersebut merupakan dependensi runtime untuk beberapa dialog. Mohon untuk menginstalnya."); - huEngine->registerEntry("id_ID", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland gagal memuat {count} aset penting. Salahkan pengelola paket distro Anda karena pengemasannya buruk!"; - return "Hyprland gagal memuat {count} aset penting. Salahkan pengelola paket distro Anda karena pengemasannya buruk!"; - }); - huEngine->registerEntry("id_ID", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Susunan monitor Anda tidak benar. Monitor {name} tertumpuk dengan monitor lain.\nSilakan lihat wiki (halaman Monitors) untuk " - "detailnya. Hal ini pasti akan menimbulkan masalah."); - huEngine->registerEntry("id_ID", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} gagal menerapkan mode yang diminta, kembali ke mode {mode}."); - huEngine->registerEntry("id_ID", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Skala tidak valid diberikan ke monitor {name}: {scale}, skala yang disarankan: {fixed_scale}"); - huEngine->registerEntry("id_ID", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Gagal memuat plugin {name}: {error}"); - huEngine->registerEntry("id_ID", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Gagal memuat ulang shader CM, kembali ke rgba/rgbx."); - huEngine->registerEntry("id_ID", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: wide color gamut aktif tetapi layar tidak dalam mode 10-bit."); - - // hr_HR (Croatian) - huEngine->registerEntry("hr_HR", TXT_KEY_ANR_TITLE, "Aplikacija ne reagira"); - huEngine->registerEntry("hr_HR", TXT_KEY_ANR_CONTENT, "Aplikacija {title} - {class} ne reagira.\nŠto želiš napraviti s njom?"); - huEngine->registerEntry("hr_HR", TXT_KEY_ANR_OPTION_TERMINATE, "Zaustavi"); - huEngine->registerEntry("hr_HR", TXT_KEY_ANR_OPTION_WAIT, "Pričekaj"); - huEngine->registerEntry("hr_HR", TXT_KEY_ANR_PROP_UNKNOWN, "(nepoznato)"); - - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Aplikacija {app} zahtijeva nepoznatu dozvolu."); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Aplikacija {app} pokušava snimati vaš zaslon.\n\nŽeliš li dopustiti?"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Aplikacija {app} pokušava učitati dodatak: {plugin}.\n\nŽeliš li dopustiti?"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Otkrivena je nova tipkovnica: {keyboard}.\n\nŽeliš li omogućiti njen rad?"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(nepoznato)"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_TITLE, "Zahtjev za dozvolu"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Savjet: za ovo možeš postaviti trajna pravila u Hyprland konfiguracijskoj datoteci."); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_ALLOW, "Dozvoli"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Dozvoli i zapamti"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_ALLOW_ONCE, "Dozvoli samo ovaj put"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_DENY, "Uskrati"); - huEngine->registerEntry("hr_HR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Nepoznata aplikacija (ID wayland klijenta {wayland_id})"); - - huEngine->registerEntry( - "hr_HR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Izgleda da je tvoja XDG_CURRENT_DESKTOP okolina vanjski upravljana te je trenutna vrijednost {value}.\nOvo može izazvati problem, osim ako je namjerno."); - huEngine->registerEntry("hr_HR", TXT_KEY_NOTIF_NO_GUIUTILS, - "Na tvojem sustavu nije instaliran hyprland-guiutils. Ovo je ovisnost tijekom pokretanja nekih dijaloga. Preporučeno je da je instaliraš."); - huEngine->registerEntry("hr_HR", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo % 10 <= 1 && assetsNo % 100 != 11) - return "Hyprland nije uspio učitati {count} neophodnu komponentu, krivi pakera svoje distribucije za loš posao pakiranja!"; - else if (assetsNo % 10 <= 4 && assetsNo % 100 > 14) - return "Hyprland nije uspio učitati {count} neophodne komponente, krivi pakera svoje distribucije za loš posao pakiranja!"; - return "Hyprland nije uspio učitati {count} neophodnih komponenata, krivi pakera svoje distribucije za loš posao pakiranja!"; - }); - huEngine->registerEntry("hr_HR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Raspored tvojih monitora je krivo postavljen. Monitor {name} preklapa se s ostalim monitorom/ima u rasporedu.\nProvjeri wiki (Monitors stranicu) za " - "više informacija. Ovo hoće izazvati probleme."); - huEngine->registerEntry("hr_HR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} nije uspio odrediti zatražene načine rada, povratak na zadani način rada: {mode}."); - huEngine->registerEntry("hr_HR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Nevažeći razmjer proslijeđen monitoru {name}: {scale}, koristi se predloženi razmjer: {fixed_scale}"); - huEngine->registerEntry("hr_HR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Učitavanje dodatka {name} nije uspjelo: {error}"); - huEngine->registerEntry("hr_HR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Ponovno učitavanje CM shadera nije uspjelo, povratak na zadano: rgba/rgbx."); - huEngine->registerEntry("hr_HR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: široki raspon boja je omogućen, ali ekran nije u 10-bitnom načinu rada."); - - // it_IT (Italian) - huEngine->registerEntry("it_IT", TXT_KEY_ANR_TITLE, "L'applicazione non risponde"); - huEngine->registerEntry("it_IT", TXT_KEY_ANR_CONTENT, "Un'applicazione {title} - {class} non risponde.\nCosa vuoi fare?"); - huEngine->registerEntry("it_IT", TXT_KEY_ANR_OPTION_TERMINATE, "Termina"); - huEngine->registerEntry("it_IT", TXT_KEY_ANR_OPTION_WAIT, "Attendi"); - huEngine->registerEntry("it_IT", TXT_KEY_ANR_PROP_UNKNOWN, "(sconosciuto)"); - - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Un'applicazione {app} richiede un'autorizzazione sconosciuta."); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Un'applicazione {app} sta provando a catturare il tuo schermo.\n\nVuoi permetterglielo?"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_PLUGIN, - "Un'applicazione {app} sta provando a caricare un plugin: {plugin}.\n\nVuoi permetterglielo?"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "È stata rilevata una nuova tastiera: {keyboard}.\n\nLe vuoi permettere di operare?"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(sconosciuto)"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_TITLE, "Richiesta di autorizzazione"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Consiglio: Puoi impostare una regola persistente nel tuo file di configurazione di Hyprland."); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW, "Permetti"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permetti e ricorda"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permetti una volta"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_DENY, "Nega"); - huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Applicazione sconosciuta (wayland client ID {wayland_id})"); - - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "L'ambiente XDG_CURRENT_DESKTOP sembra essere gestito esternamente, il valore attuale è {value}.\nSe non è voluto, potrebbe causare problemi."); - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_NO_GUIUTILS, - "Sembra che hyprland-guiutils non sia installato. È una dipendenza richiesta per alcuni dialoghi che potresti voler installare."); - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_FAILED_ASSETS, - "Hyprland non ha potuto caricare {count} asset, dai la colpa al packager della tua distribuzione per il suo cattivo lavoro!"); - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "I tuoi schermi sono configurati incorrettamente. Lo schermo {name} si sovrappone con altri nel layout.\nConsulta la wiki (voce Schermi) per " - "altre informazioni. Questo causerà problemi."); - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Lo schermo {name} non ha potuto impostare alcuna modalità richiesta, sarà usata la modalità {mode}."); - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "Fattore di scala non valido per lo schermo {name}: {scale}, utilizzando il fattore suggerito: {fixed_scale}"); - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Impossibile caricare il plugin {name}: {error}"); - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Impossibile ricaricare gli shader CM, sarà usato rgba/rgbx."); - huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Schermo {name}: la gamma di colori ampia è abilitata ma lo schermo non è in modalità 10-bit."); - - // ja_JP (Japanese) - huEngine->registerEntry("ja_JP", TXT_KEY_ANR_TITLE, "アプリが応答しません"); - huEngine->registerEntry("ja_JP", TXT_KEY_ANR_CONTENT, "アプリ {title} - {class} が応答しません。\nどうしますか?"); - huEngine->registerEntry("ja_JP", TXT_KEY_ANR_OPTION_TERMINATE, "強制終了"); - huEngine->registerEntry("ja_JP", TXT_KEY_ANR_OPTION_WAIT, "待機"); - huEngine->registerEntry("ja_JP", TXT_KEY_ANR_PROP_UNKNOWN, "(不明)"); - - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "アプリ {app} が権限を求めています。"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "アプリ {app} が画面をキャプチャしようとしています。\n\n許可しますか?"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "アプリ {app} がプラグイン {plugin} をロードしようとしています。\n\n許可しますか?"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "新しいキーボード {keyboard} が接続されました。\n\n使用を許可しますか?"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(不明)"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_TITLE, "権限の要求"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "ヒント:永続的なルールを Hyprland の設定ファイルに記述できます。"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW, "許可"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "許可して保存"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW_ONCE, "今回だけ許可"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_DENY, "却下"); - huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "不明なアプリ(wayland クライアント ID {wayland_id})"); - - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "環境変数 XDG_CURRENT_DESKTOP は外部から {value} に設定されています。\n意図的なものでなければ、何らかの問題を起こすかもしれません。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_NO_GUIUTILS, "hyprland-guiutils がありません。このパッケージをインストールしてください。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_FAILED_ASSETS, - "{count} 個の必要なアセットをロードできません。ディストリビューションのパッケージ作成者にこの問題を報告してください。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "モニタのレイアウトが正しく設定されていません。モニタ {name} の表示領域が他のモニタと重複しています。\n詳細は Wiki の Monitor " - "の項目を参照してください。これは絶対に問題を起こします。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "モニタ {name} のモード設定に失敗したため、モード {mode} を使用します。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "モニタ {name} のスケール設定が正しくないため、代わりにスケール {fixed_scale} を使用します。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "プラグイン {name} のロードで、エラー {error} が発生しました。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM シェーダのリロードに失敗したため、rgba/rgbx を使用します。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "広色域が有効なモニタ {name} を使用していますが、画面表示の設定は 10 ビットになっていません。"); - huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_NO_WATCHDOG, "start-hyprland なしで Hyprland を実行しています。これは、デバッグ目的以外ではおすすめしません。"); - - huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_TITLE, "セーフモード"); - huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_DESCRIPTION, - "前回のセッションがクラッシュしました。Hyprland " - "は設定ファイルをロードしない、セーフモードで動作しています。\n問題を解決するか、もしくは下のボタンで設定ファイルをロードしてください。" - "\nデフォルトのキーバインドは、SUPER+Q が kitty、SUPER+R が簡素なランチャー、SUPER+M が Hyprland の終了です。" - "\nHyprland を再起動することで、ノーマルモードで動作します。"); - huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "設定ファイルをロード"); - huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "クラッシュレポートフォルダを開く"); - huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "了解(このウィンドウを閉じる)"); - - // lv_LV (Latvian) - huEngine->registerEntry("lv_LV", TXT_KEY_ANR_TITLE, "Lietotne nereaģē"); - huEngine->registerEntry("lv_LV", TXT_KEY_ANR_CONTENT, "Lietotne {title} - {class} nereaģē.\nKo jūs vēlaties darīt?"); - huEngine->registerEntry("lv_LV", TXT_KEY_ANR_OPTION_TERMINATE, "Beigt procesu"); - huEngine->registerEntry("lv_LV", TXT_KEY_ANR_OPTION_WAIT, "Gaidīt"); - huEngine->registerEntry("lv_LV", TXT_KEY_ANR_PROP_UNKNOWN, "(nezināms)"); - - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Lietotne {app} pieprasa nezināmu atļauju."); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Lietotne {app} mēģina lasīt no jūsu ekrāna.\n\nVai vēlaties to atļaut?"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Lietotne {app} mēģina ielādēt spraudni: {plugin}.\n\nVai vēlaties to atļaut?"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Ir atrasta jauna tastatūra: {keyboard}.\n\nVai vēlaties atļaut tās darbību?"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(nezināms)"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_TITLE, "Atļaujas pieprasījums"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Padoms: Hyprland konfigurācijas failā varat arī iestatīt atļaujas."); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_ALLOW, "Atļaut"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Atļaut un atcerēties"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_ALLOW_ONCE, "Atļaut vienreiz"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_DENY, "Aizliegt"); - huEngine->registerEntry("lv_LV", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Nezināma lietotne (Wayland klienta ID {wayland_id})"); - - huEngine->registerEntry("lv_LV", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Jūsu XDG_CURRENT_DESKTOP tiek ārēji pārvaldīts, tās vērtība ir {value}.\nTas var neapzināti izraisīt problēmas."); - huEngine->registerEntry("lv_LV", TXT_KEY_NOTIF_NO_GUIUTILS, "Jums nav instalēts hyprland-guiutils. Šī pakotne ir nepieciešama dažiem dialogiem. Apsveriet tās instalēšanu."); - huEngine->registerEntry("lv_LV", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland nevarēja ielādēt {count} būtisku resursu, vainojiet sava distro iepakotāju par sliktu iepakošanu!"; - return "Hyprland nevarēja ielādēt {count} būtiskus resursus, vainojiet sava distro iepakotāju par sliktu iepakošanu!"; - }); - huEngine->registerEntry( - "lv_LV", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Jūsu monitora izkārtojums ir nepareizi iestatīts. Monitors {name} pārklājas ar citiem izkārtojumā iestatītajiem monitoriem.\nLūdzu apskatieties (Monitoru lapā)," - "lai uzzinātu vairāk. Tas radīs problēmas."); - huEngine->registerEntry("lv_LV", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitoram {name} neizdevās iestatīt nevienu no pieprasītajiem režīmiem, izmantojam {mode}."); - huEngine->registerEntry("lv_LV", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Monitoram {name} ir nodots nederīgs mērogs: {scale}, izmantojam ieteikto mērogu: {fixed_scale}"); - huEngine->registerEntry("lv_LV", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nevarēja ielādēt spraudni {name}: {error}"); - huEngine->registerEntry("lv_LV", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM šeiderus neizdevās pārlādēt, izmantojam rgba/rgbx."); - huEngine->registerEntry("lv_LV", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitors {name}: Ir iespējota plaša krāsu gamma, bet displejs nav 10-bitu režīmā."); - - // hu_HU (Hungarian) - huEngine->registerEntry("hu_HU", TXT_KEY_ANR_TITLE, "Az alkalmazás nem válaszol"); - huEngine->registerEntry("hu_HU", TXT_KEY_ANR_CONTENT, "A(z) {title} - {class} alkalmazás nem válaszol.\nMit szeretne tenni vele?"); - huEngine->registerEntry("hu_HU", TXT_KEY_ANR_OPTION_TERMINATE, "Leállítás"); - huEngine->registerEntry("hu_HU", TXT_KEY_ANR_OPTION_WAIT, "Várakozás"); - huEngine->registerEntry("hu_HU", TXT_KEY_ANR_PROP_UNKNOWN, "(ismeretlen)"); - - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "A(z) {app} alkalmazás ismeretlen engedélyt kér."); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "A(z) {app} alkalmazás megpróbálja rögzíteni a képernyőjét.\n\nEngedélyezi?"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "A(z) {app} alkalmazás megpróbál egy bővítményt betölteni: {plugin}.\n\nEngedélyezi?"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Új billentyűzetet észleltünk: {keyboard}.\n\nEngedélyezi a használatát?"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(ismeretlen)"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_TITLE, "Engedélykérés"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tipp: Állandó szabályokat állíthat be a Hyprland konfigurációs fájlban."); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_ALLOW, "Engedélyezés"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Mindig engedélyez"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_ALLOW_ONCE, "Egyszeri engedélyezés"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_DENY, "Elutasítás"); - huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Ismeretlen alkalmazás (wayland kliens ID {wayland_id})"); - - huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Úgy tűnik, hogy az XDG_CURRENT_DESKTOP környezetet külsőleg kezelik, és a jelenlegi érték {value}.\nEz problémákat okozhat, hacsak nem szándékos."); - huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_NO_GUIUTILS, - "A rendszerében nincs telepítve a hyprland-guiutils. Ez egy futásidejű függőség néhány párbeszédablakhoz. Fontolja meg a telepítését."); - huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "A Hyprland nem tudta betölteni az 1 szükséges erőforrást. Kérjük, jelezze a hibát a disztribúció csomagolójának."; - return "A Hyprland nem tudott betölteni {count} szükséges erőforrást. Kérjük, jelezze a hibát a disztribúció csomagolójának."; - }); - huEngine->registerEntry( - "hu_HU", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "A monitor elrendezése helytelenül van beállítva. A(z) {name} monitor átfedi a többi monitort az elrendezésben.\nKérjük, további információkért tekintse meg a wikit " - "(Monitors oldal). Ez problémákat fog okozni."); - huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "A(z) {name} monitor nem tudta beállítani a kért módokat, visszaáll a(z) {mode} módra."); - huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Érvénytelen skálázás a(z) {name} monitorhoz: {scale}, a javasolt skálázás használata: {fixed_scale}"); - huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nem sikerült betölteni a(z) {name} bővítményt: {error}"); - huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "A CM shader újratöltése sikertelen, visszaáll rgba/rgbx-re."); - huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: A széles színtartomány engedélyezve van, de a kijelző nem 10 bites módban van."); - - // ml_IN (Malayalam) - huEngine->registerEntry("ml_IN", TXT_KEY_ANR_TITLE, "ആപ്ലിക്കേഷൻ പ്രതികരിക്കുന്നില്ല"); - huEngine->registerEntry("ml_IN", TXT_KEY_ANR_CONTENT, "ആപ്ലിക്കേഷൻ {title} - {class} പ്രതികരിക്കുന്നില്ല.\nഇതിന് നിങ്ങൾ എന്ത് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു?"); - huEngine->registerEntry("ml_IN", TXT_KEY_ANR_OPTION_TERMINATE, "അവസാനിപ്പിക്കുക"); - huEngine->registerEntry("ml_IN", TXT_KEY_ANR_OPTION_WAIT, "കാത്തിരിക്കുക"); - huEngine->registerEntry("ml_IN", TXT_KEY_ANR_PROP_UNKNOWN, "(അജ്ഞാതം)"); - - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "ആപ്ലിക്കേഷൻ {app} ഒരു അജ്ഞാത അനുമതി അഭ്യർത്ഥിക്കുന്നു."); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "ആപ്ലിക്കേഷൻ {app} നിങ്ങളുടെ സ്ക്രീൻ പകർത്താൻ ശ്രമിക്കുന്നു.\n\nനിങ്ങൾ അത് അനുവദിക്കണോ?"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "ആപ്ലിക്കേഷൻ {app} ഒരു പ്ലഗിൻ ലോഡ് ചെയ്യാൻ ശ്രമിക്കുന്നു: {plugin}.\n\nഇത് അനുവദിക്കണോ?"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "പുതിയ കീബോർഡ് കണ്ടെത്തി: {keyboard}.\n\nഇത് പ്രവർത്തിക്കാൻ അനുവദിക്കണോ?"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(അജ്ഞാതം)"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_TITLE, "അനുമതി അഭ്യർത്ഥന"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "സൂചന: Hyprland കോൺഫിഗ് ഫയലിൽ സ്ഥിരനിയമങ്ങൾ സജ്ജമാക്കാം."); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_ALLOW, "അനുവദിക്കുക"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "അനുവദിച്ച് ഓർക്കുക"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_ALLOW_ONCE, "ഒന്നുതവണ അനുവദിക്കുക"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_DENY, "നിരസിക്കുക"); - huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "അജ്ഞാത അപ്ലിക്കേഷൻ (wayland client ID {wayland_id})"); - - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "നിങ്ങളുടെ XDG_CURRENT_DESKTOP പരിസ്ഥിതി പുറത്ത് നിന്ന് നിയന്ത്രിക്കപ്പെടുന്നു, ഇപ്പോഴത്തെ മൂല്യം " - "{value}.\nഇത് ഉദ്ദേശ്യമായല്ലെങ്കിൽ പ്രശ്നങ്ങൾ ഉണ്ടാകും."); - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_NO_GUIUTILS, - "നിങ്ങളുടെ സിസ്റ്റത്തിൽ hyprland-guiutils ഇൻസ്റ്റാൾ ചെയ്തിട്ടില്ല. ഇത് ചില ഡയലോഗുകൾക്ക് ആവശ്യമായ " - "റൺടൈം ആശ്രയമാണ്. ഇൻസ്റ്റാൾ ചെയ്യുക."); - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland {count} പ്രധാന അസറ്റ് ലോഡുചെയ്യാൻ പരാജയപ്പെട്ടു, നിങ്ങളുടെ " - "ഡിസ്‌ട്രോ " - "പാക്കേജർ പിശക് ചെയ്തിരിക്കുന്നു!"; - return "Hyprland {count} പ്രധാന അസറ്റുകൾ ലോഡുചെയ്യാൻ പരാജയപ്പെട്ടു, നിങ്ങളുടെ " - "ഡിസ്‌ട്രോ " - "പാക്കേജർ പിശക് ചെയ്തിരിക്കുന്നു!"; - }); - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "മോണിറ്റർ ലേയൗട്ട് തെറ്റാണ്. മോണിറ്റർ {name} മറ്റുള്ളവയുമായ് ഒതുങ്ങുന്നു.\nകൂടുതൽ വിവരങ്ങൾക്ക് Wiki " - "(Monitors page) കാണുക. ഇത് പ്രശ്നങ്ങൾ ഉണ്ടാക്കും."); - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "മോണിറ്റർ {name} ആവശ്യപ്പെട്ട മോഡുകൾ സജ്ജമാക്കാൻ പരാജയപ്പെട്ടു, ഇപ്പോൾ {mode} ഉപയോഗിക്കുന്നു."); - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "മോണിറ്റർ {name} ന് അസാധുവായ സ്കെയിൽ: {scale}, നിർദ്ദേശിച്ച സ്കെയിൽ: {fixed_scale}"); - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "പ്ലഗിൻ {name} ലോഡ് ചെയ്യാൻ പരാജയപ്പെട്ടു: {error}"); - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM ഷേഡർ റീലോഡ് പരാജയപ്പെട്ടു, rgba/rgbx ലേക്ക് മാറുന്നു."); - huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "മോണിറ്റർ {name}: വൈഡ് കളർ ഗാമട്ട് പ്രവർത്തനക്ഷമമാണെങ്കിലും, മോഡ് 10-bit അല്ല."); - - // nb_NO (Norwegian Bokmål) - huEngine->registerEntry("nb_NO", TXT_KEY_ANR_TITLE, "Applikasjonen svarer ikke"); - huEngine->registerEntry("nb_NO", TXT_KEY_ANR_CONTENT, "En applikasjon {title} - {class} svarer ikke.\nHva vil du gjøre med den?"); - huEngine->registerEntry("nb_NO", TXT_KEY_ANR_OPTION_TERMINATE, "Avslutt"); - huEngine->registerEntry("nb_NO", TXT_KEY_ANR_OPTION_WAIT, "Vent"); - huEngine->registerEntry("nb_NO", TXT_KEY_ANR_PROP_UNKNOWN, "(ukjent)"); - - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "En applikasjon {app} ber om en ukjent tillatelse."); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "En applikasjon {app} prøver å fange skjermen din.\n\nVil du tillate den?"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "En applikasjon {app} prøver å laste en plugin: {plugin}.\n\nVil du tillate den?"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Et nytt tastatur er oppdaget: {keyboard}.\n\nVil du tillate at det opererer?"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(ukjent)"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_TITLE, "Tillatelsesforespørsel"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Hint: du kan angi vedvarende regler for disse i Hyprland konfigurasjonsfilen."); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_ALLOW, "Tillat"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Tillat og husk"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_ALLOW_ONCE, "Tillat en gang"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_DENY, "Nekte"); - huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Ukjent applikasjon (wayland client ID {wayland_id})"); - - huEngine->registerEntry( - "nb_NO", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Ditt XDG_CURRENT_DESKTOP miljø ser ut til å være eksternt administrert, og den nåværende verdien er {value}.\nDette kan forårsake problemer med mindre det er bevisst."); - huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_NO_GUIUTILS, - "Ditt system har ikke hyprland-guiutils installert. Dette er en kjøretidsavhengighet for noen dialoger. Vurder å installere den."); - huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland kunne ikke laste {count} essensiell ressurs, skyld på distroens pakkeansvarlig for å ha gjort en dårlig jobb med pakkingen!"; - return "Hyprland kunne ikke laste {count} essensielle ressurser, skyld på distroens pakkeansvarlig for å ha gjort en dårlig jobb med pakkingen!"; - }); - huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Skjermoppsettet ditt er satt opp feil. Skjerm {name} overlapper med skjerm(er) i oppsettet.\nSjekk wiki (Skjerm oppsett siden) for " - "mer. Dette vil skape problemer."); - huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Skjerm {name} feilet å sette de forespurte modusene, faller tilbake til modus {mode}."); - huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Ugyldig skala sendt til skjerm {name}: {scale}, bruker foreslått skala: {fixed_scale}"); - huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Feilet å laste plugin {name}: {error}"); - huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader omlading feilet, faller tilbake til rgba/rgbx."); - huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Skjerm {name}: bredt fargespekter er aktivert, men skjermen er ikke i 10-bit modus."); - - // ne_NP (Nepali) - huEngine->registerEntry("ne_NP", TXT_KEY_ANR_TITLE, "एपले रिस्पन्ड गरिरहेको छैन"); - huEngine->registerEntry("ne_NP", TXT_KEY_ANR_CONTENT, "{title} - {class} एपले रिस्पन्ड गरिरहेको छैन।\nयससँग के गर्न चहानुहुन्छ?"); - huEngine->registerEntry("ne_NP", TXT_KEY_ANR_OPTION_TERMINATE, "टर्मिनेट गर्नुहोस्"); - huEngine->registerEntry("ne_NP", TXT_KEY_ANR_OPTION_WAIT, "पर्खनुहोस्"); - huEngine->registerEntry("ne_NP", TXT_KEY_ANR_PROP_UNKNOWN, "(अज्ञात)"); - - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "{app} एपले अज्ञात सुविधाको अनुमति मागिरहेको छ।"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "{app} एपले स्क्रिन क्याप्चर गर्न खोज्दै छ।\n\nयसलाई अनुमति दिन चहानुहुन्छ?"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "{app} एपले एउटा प्लगिन लोड गर्न खोज्दै छ: {plugin}।\n\nयसलाई अनुमति दिन चहानुहुन्छ?"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "एउटा नयाँ किबोर्ड डिटेक्ट गरिएको छ: {keyboard}।\n\nयसलाई चल्ने अनुमति दिन चहानुहुन्छ?"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(अज्ञात)"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_TITLE, "अनुमतिको माग"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "टिप: यसको लागि पर्सिस्टेन्ट नियमहरु तपाइँले हाइपरल्यान्डको कन्फीग्युरेसन फाइलमा राख्न सक्नुहुन्छ।"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_ALLOW, "अनुमति दिनुहोस्"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "अनुमति दिनुहोस् र सम्झनुहोस्"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_ALLOW_ONCE, "एकपटक अनुमति दिनुहोस्"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_DENY, "अनुमति नदिनुहोस्"); - huEngine->registerEntry("ne_NP", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "अज्ञात एप (wayland क्लाइन्ट आईडी {wayland_id})"); - - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "तपाईँको XDG_CURRENT_DESKTOP वातावरण बाहिरबाट व्यवस्थापन भइरहेको जस्तो देखिएको छ, अहिले {value} देखाइरहेको छ।\nजानीजानी नगरीएको भएमा यसले समस्याहरु निम्त्याउन सक्छ।"); - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_NO_GUIUTILS, "तपाइँको सिस्टममा hyprland-guiutils इन्सटल गरिएको छैन। केहि डायलगहरुका लागि यो रनटाइम डिपेन्डेन्सी हो। कृपया इन्सटल गर्नुहोला।"); - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "हाइपरल्यान्डले एउटा अत्यावश्यक एसेट लोड गर्न सकेन, तपाइँको डिस्ट्रोको प्याकेजरको प्याकेजिङ गतिलो छैन!"; - return "हाइपरल्यान्डले {count} अत्यावश्यक एसेटहरु लोड गर्न सकेन, तपाइँको डिस्ट्रोको प्याकेजरको प्याकेजिङ गतिलो छैन!"; - }); - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "तपाइँको मनिटरको लेआउट गलत तरिकाले मिलाइएको छ। लेआउटमा {name} मनिटर अर्को मनिटर वा मनिटरहरुसङ्ग ओभरल्याप भएको छ।\nथप बुझ्नलाई कृपया विकिको मनिटर पेज हेर्नुहोस्।" - "यसले निश्चित रुपमा समस्या निम्त्याउने छ।"); - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "{name} मनिटरले चाहेको कुनै पनि मोड सेट गर्न सकेन, {mode} मोडमा फर्कँदै।"); - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "{name} मनिटरलाई अमान्य स्केल पठाइयो: {scale}, सजेस्ट गरिएको स्केल प्रयोग गर्दै: {fixed_scale}"); - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "{name} प्लगिन लोेड गर्न सकिएन: {error}"); - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader रिलोड गर्न सकिएन, rgba/rgbx मा फर्कँदै।"); - huEngine->registerEntry("ne_NP", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "{name} मनिटर: wide color gamut अन छ तर डिस्प्ले 10-bit मोड मा छैन।"); - - // nl_NL (Dutch) - huEngine->registerEntry("nl_NL", TXT_KEY_ANR_TITLE, "Applicatie Reageert Niet"); - huEngine->registerEntry("nl_NL", TXT_KEY_ANR_CONTENT, "Een applicatie {title} - {class} reageert niet.\nWat wilt u doen?"); - huEngine->registerEntry("nl_NL", TXT_KEY_ANR_OPTION_TERMINATE, "Beëindigen"); - huEngine->registerEntry("nl_NL", TXT_KEY_ANR_OPTION_WAIT, "Wachten"); - huEngine->registerEntry("nl_NL", TXT_KEY_ANR_PROP_UNKNOWN, "(onbekend)"); - - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Een applicatie {app} vraagt om een onbekende machtiging."); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Een applicatie {app} probeert uw scherm op te nemen.\n\nWilt u dit toestaan?"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Een applicatie {app} probeert een plugin te laden: {plugin}.\n\nWilt u dit toestaan?"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, - "Een nieuw toetsenbord is gedetecteerd: {keyboard}.\n\nWilt u toestemming geven dat het wordt gebruikt?"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(onbekend)"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_TITLE, "Toestemmingsverzoek"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tip: U kunt hiervoor vaste regels instellen in het Hyprland-configuratiebestand."); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_ALLOW, "Toestaan"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Toestaan en onthouden"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_ALLOW_ONCE, "Één keer toestaan"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_DENY, "Weigeren"); - huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Onbekende applicatie (wayland client ID {wayland_id})"); - - huEngine->registerEntry( - "nl_NL", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "De XDG_CURRENT_DESKTOP omgevingsvariabele lijkt extern beheerd te worden en de huidige waarde is {value}.\nDit kan problemen veroorzaken, tenzij dit opzettelijk is."); - huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_NO_GUIUTILS, - "Hyprland-guiutils is niet op uw systeem geïnstalleerd. Dit is een runtime-afhankelijkheid voor sommige dialogen. Overweeg het te installeren."); - huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland kon {count} essentieel bestand niet laden, geef de pakketbeheerder van uw distro de schuld voor slecht verpakkingswerk!"; - return "Hyprland kon {count} essentiële bestanden niet laden, geef de pakketbeheerder van uw distro de schuld voor slecht verpakkingswerk!"; - }); - huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Uw monitorindeling is onjuist ingesteld. Monitor {name} overlapt met één of meerdere andere monitoren in de indeling.\n" - "Zie de wiki (Monitors pagina) voor meer informatie. Dit zal problemen veroorzaken."); - huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, - "Monitor {name} is er niet in geslaagd om een van de aangevraagde modi toe te passen en gebruikt nu de modus {mode}."); - huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "Ongeldige schaal opgegeven voor monitor {name}: {scale}, de voorgestelde schaal {fixed_scale} wordt gebruikt."); - huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Plugin {name} kon niet worden geladen: {error}"); - huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Het opnieuw laden van de CM-shader is mislukt. Er wordt teruggevallen op rgba/rgbx."); - huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: breed kleurbereik is ingeschakeld maar het scherm staat niet in 10-bitmodus."); - - // pl_PL (Polish) - huEngine->registerEntry("pl_PL", TXT_KEY_ANR_TITLE, "Aplikacja Nie Odpowiada"); - huEngine->registerEntry("pl_PL", TXT_KEY_ANR_CONTENT, "Aplikacja {title} - {class} nie odpowiada.\nCo chcesz z nią zrobić?"); - huEngine->registerEntry("pl_PL", TXT_KEY_ANR_OPTION_TERMINATE, "Zakończ proces"); - huEngine->registerEntry("pl_PL", TXT_KEY_ANR_OPTION_WAIT, "Czekaj"); - huEngine->registerEntry("pl_PL", TXT_KEY_ANR_PROP_UNKNOWN, "(nieznane)"); - - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Aplikacja {app} prosi o pozwolenie na nieznany typ operacji."); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Aplikacja {app} prosi o dostęp do twojego ekranu.\n\nCzy chcesz jej na to pozwolić?"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Aplikacja {app} próbuje załadować plugin: {plugin}.\n\nCzy chcesz jej na to pozwolić?"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Wykryto nową klawiaturę: {keyboard}.\n\nCzy chcesz jej pozwolić operować?"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(nieznane)"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_TITLE, "Prośba o pozwolenie"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Podpowiedź: możesz ustawić stałe zasady w konfiguracji Hyprland'a."); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW, "Zezwól"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Zezwól i zapamiętaj"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW_ONCE, "Zezwól raz"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_DENY, "Odmów"); - huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Nieznana aplikacja (ID klienta wayland {wayland_id})"); - - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Zmienna środowiska XDG_CURRENT_DESKTOP została ustawiona zewnętrznie na {value}.\nTo może sprawić problemy, chyba, że jest celowe."); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_NO_GUIUTILS, "Twój system nie ma hyprland-guiutils zainstalowanych, co może sprawić problemy. Zainstaluj pakiet."); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo == 1) - return "Nie udało się załadować {count} kluczowego zasobu, wiń swojego packager'a za robienie słabej roboty!"; - - return "Nie udało się załadować {count} kluczowych zasobów, wiń swojego packager'a za robienie słabej roboty!"; - }); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Pozycje twoich monitorów nie są ustawione poprawnie. Monitor {name} wchodzi na inne monitory.\nWejdź na wiki (stronę Monitory) " - "po więcej. To będzie sprawiać problemy."); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} nie zaakceptował żadnego wybranego programu. Użyto {mode}."); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Nieprawidłowa skala dla monitora {name}: {scale}, użyto proponowanej skali: {fixed_scale}"); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nie udało się załadować plugin'a {name}: {error}"); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Nie udało się przeładować shader'a CM, użyto rgba/rgbx."); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: skonfigurowano szeroką głębię barw, ale monitor nie jest w trybie 10-bit."); - huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_NO_WATCHDOG, - "Hyprland został uruchomiony bez start-hyprland. Nie jest to zalecane, chyba, że jest to środowisko do debugowania."); - - huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_TITLE, "Tryb Bezpieczny"); - huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_DESCRIPTION, - "Hyprland został uruchomiony w trybie bezpiecznym, co oznacza, że twoja ostatnia sesja uległa awarii.\nTryb bezpieczny zapobiega ładowaniu twojej " - "konfiguracji. Możesz próbować rozwiązać" - "problem w tym środowisku, lub załadować swoją konfigurację przyciskiem poniżej.\nDomyślne skróty klawiszowe są dostępne: SUPER+Q uruchamia kitty, " - "SUPER+R otwiera podstawowy launcher, SUPER+M zamyka Hyprland.\nUruchomienie ponowne Hyprland'a uruchomi go w trybie normalnym."); - huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "Załaduj konfigurację"); - huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "Otwórz folder z raportami awarii"); - huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "Ok, zamknij to okno"); - - // pt_PT (Portuguese Portugal) - huEngine->registerEntry("pt_PT", TXT_KEY_ANR_TITLE, "A aplicação não está a responder"); - huEngine->registerEntry("pt_PT", TXT_KEY_ANR_CONTENT, "Uma aplicação {title} - {class} não está a responder.\nO que pretendes fazer com ela?"); - huEngine->registerEntry("pt_PT", TXT_KEY_ANR_OPTION_TERMINATE, "Terminar"); - huEngine->registerEntry("pt_PT", TXT_KEY_ANR_OPTION_WAIT, "Esperar"); - huEngine->registerEntry("pt_PT", TXT_KEY_ANR_PROP_UNKNOWN, "(desconhecido)"); - - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Uma aplicação {app} está a pedir uma permissão desconhecida."); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Uma aplicação {app} está a tentar fazer uma captura do ecrã.\n\nQueres permiti-lo?"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "A aplicação {app} está a tentar carregar o plugin: {plugin}.\n\nQueres permiti-lo?"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Um novo teclado foi detectado: {keyboard}.\n\nQueres permitir a sua operação?"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(desconhecido)"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_TITLE, "Pedido de permissão"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Dica: podes definir regras persistentes para estes no ficheiro de configuração do Hyprland."); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_ALLOW, "Permitir"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permitir sempre"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permitir esta vez"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_DENY, "Recusar"); - huEngine->registerEntry("pt_PT", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Aplicação desconhecida (ID de cliente wayland {wayland_id})"); - - huEngine->registerEntry( - "pt_PT", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "O teu ambiente XDG_CURRENT_DESKTOP parece estar a ser gerido externamente, e o valor actual é {value}.\nIsto pode causar problemas a não ser que seja intencional."); - huEngine->registerEntry("pt_PT", TXT_KEY_NOTIF_NO_GUIUTILS, - "O teu sistema não tem o hyprland-guiutils instalado. Esta dependência de runtime é necessária para algumas caixas de diálogo, deverias instalá-la."); - huEngine->registerEntry("pt_PT", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland não conseguiu carregar {count} asset essencial, podes culpar o gestor de dependências da tua distro por fazer um mau trabalho!"; - return "Hyprland não conseguiu carregar {count} assets essenciais, podes culpar o gestor de dependências da tua distro por fazer um mau trabalho!"; - }); - huEngine->registerEntry( - "pt_PT", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "O layout do teu monitor não está configurado correctamente. Monitor {name} está em conflito com outro(s) monitor(es) no layout.\nProcura na wiki (página Monitores) para " - "mais informações. Isto vai causar problemas."); - huEngine->registerEntry("pt_PT", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} falhou ao configurar os modos requisitados, revertento para o modo {mode} de volta."); - huEngine->registerEntry("pt_PT", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Resolução inválida para o monitor {name}: {scale}, revertendo para a resolução sugerida: {fixed_scale}"); - huEngine->registerEntry("pt_PT", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Falha ao carregar o plugin {name}: {error}"); - huEngine->registerEntry("pt_PT", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader falhou ao recarregar, revertendo para rgba/rgbx."); - huEngine->registerEntry("pt_PT", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: gama de cores ampla está activada mas o monitor não está em modo 10-bits."); - - // zh_CN (Simplified Chinese) - huEngine->registerEntry("zh_CN", TXT_KEY_ANR_TITLE, "应用程序未响应"); - huEngine->registerEntry("zh_CN", TXT_KEY_ANR_CONTENT, "应用程序 {title} - {class} 未响应。\n你想要采取什么行动?"); - huEngine->registerEntry("zh_CN", TXT_KEY_ANR_OPTION_TERMINATE, "终止"); - huEngine->registerEntry("zh_CN", TXT_KEY_ANR_OPTION_WAIT, "等待"); - huEngine->registerEntry("zh_CN", TXT_KEY_ANR_PROP_UNKNOWN, "(未知)"); - - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "应用程序 {app} 正在请求一个未知的权限。"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "应用程序 {app} 想要捕获你的屏幕。\n\n允许它这么做吗?"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "应用程序 {app} 想要加载插件: {plugin}。\n\n允许它这么做吗?"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "检测到新的键盘 {keyboard} 接入了。\n\n允许这个键盘操作你的系统吗?"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(未知)"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_TITLE, "权限请求"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "提示:你可以在Hyprland配置中为他们创建永久性的规则。"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_ALLOW, "允许"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "总是允许"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_ALLOW_ONCE, "允许一次"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_DENY, "阻止"); - huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "未知的应用程序 (Wayland客户端ID {wayland_id})"); - - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "你的环境变量XDG_CURRENT_DESKTOP似乎被外部管理,且当前的值为{value}。如果你不是有意这么做,这可能会导致问题。"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_NO_GUIUTILS, "你的系统似乎没有安装hyprland-guiutils。这是一个用于部分对话框的运行时依赖。请考虑安装。"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_FAILED_ASSETS, "Hyprland无法加载{count}个重要资产,问问你发行版的打包者在打包个什么玩意!?"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "你的显示器没有被正确设置。显示器 {name} 和其他显示器的布局重叠了。请看wiki中的“显示器”一章获取更多信息。这导致问题。"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "显示器 {name} 无法被设置为任何请求的模式,将使用 {mode} 兜底。"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "显示器 {name} 被设置了非法的缩放:{scale},将使用建议的缩放:{fixed_scale}"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "无法加载插件 {name}:{error}"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "无法重新加载CM着色器,将使用rgba/rgbx兜底。"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "显示器 {name}:宽色域被启用了,但是显示器并不在10-bit模式。"); - huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_NO_WATCHDOG, "Hyprland 启动时未使用 start-hyprland。除非你处于调试环境,否则极度不推荐这样做。"); - - huEngine->registerEntry("zh_CN", TXT_KEY_SAFE_MODE_TITLE, "安全模式"); - huEngine->registerEntry("zh_CN", TXT_KEY_SAFE_MODE_DESCRIPTION, - "Hyprland " - "已在安全模式下启动,这意味着你上次会话崩溃了。\n安全模式会阻止加载你的配置。你可以在此环境中进行故障排除,或者使用下方按钮加载你的配置。\n默认快" - "捷键适用:SUPER+Q 打开 Kitty,SUPER+R 打开简易启动器,SUPER+M 退出。\n重新启动 " - "Hyprland 将再次进入正常模式。"); - huEngine->registerEntry("zh_CN", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "加载配置"); - huEngine->registerEntry("zh_CN", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "打开崩溃报告目录"); - huEngine->registerEntry("zh_CN", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "好的,关闭窗口"); - - // zh_TW (Traditional Chinese) - huEngine->registerEntry("zh_TW", TXT_KEY_ANR_TITLE, "應用程式沒有回應"); - huEngine->registerEntry("zh_TW", TXT_KEY_ANR_CONTENT, "應用程式 {title} - {class} 沒有回應。\n您想要怎麼做?"); - huEngine->registerEntry("zh_TW", TXT_KEY_ANR_OPTION_TERMINATE, "強制結束"); - huEngine->registerEntry("zh_TW", TXT_KEY_ANR_OPTION_WAIT, "等待"); - huEngine->registerEntry("zh_TW", TXT_KEY_ANR_PROP_UNKNOWN, "(未知)"); - - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "應用程式 {app} 正在請求未知的權限。"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "應用程式 {app} 試圖擷取您的螢幕畫面。\n\n您是否允許?"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "應用程式 {app} 試圖載入外掛:{plugin}。\n\n您是否允許?"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "偵測到新鍵盤:{keyboard}。\n\n您是否允許它進行操作?"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(未知)"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_TITLE, "權限請求"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "提示:您可以在 Hyprland 設定檔中為此建立永久規則。"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_ALLOW, "允許"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "總是允許"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_ALLOW_ONCE, "僅允許一次"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_DENY, "拒絕"); - huEngine->registerEntry("zh_TW", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "未知的應用程式 (Wayland 用戶端 ID {wayland_id})"); - - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "您的 XDG_CURRENT_DESKTOP 環境變數似乎由外部管理,目前的值為 {value}。\n除非您有意為之,否則這可能會導致問題。"); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_NO_GUIUTILS, "您的系統未安裝 hyprland-guiutils。這是部分對話視窗的執行期依賴元件。建議您安裝它。"); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland 無法載入 {count} 個必要資源,去怪那個把發行版打包成這副德性的維護者!"; - return "Hyprland 無法載入 {count} 個必要資源,去怪那個把發行版打包成這副德性的維護者!"; - }); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "您的螢幕配置設定不正確。螢幕 {name} 與配置中的其他螢幕重疊了。\n請參閱 Wiki(螢幕頁面)以了解詳情。這絕對會導致問題。"); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "螢幕 {name} 無法設定為任何請求的模式,將改用模式 {mode}。"); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "傳遞給螢幕 {name} 的縮放比例無效:{scale},將使用建議的比例:{fixed_scale}"); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "無法載入外掛 {name}:{error}"); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM 著色器重新載入失敗,將退回使用 rgba/rgbx。"); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "螢幕 {name}:已啟用廣色域,但顯示器並非處於 10-bit 模式。"); - huEngine->registerEntry("zh_TW", TXT_KEY_NOTIF_NO_WATCHDOG, "Hyprland 啟動時未使用 start-hyprland wrapper。除非您處於除錯環境,否則極度不建議這麼做。"); - - huEngine->registerEntry("zh_TW", TXT_KEY_SAFE_MODE_TITLE, "安全模式"); - huEngine->registerEntry("zh_TW", TXT_KEY_SAFE_MODE_DESCRIPTION, - "Hyprland " - "已在安全模式下啟動,這代表您的上個工作階段當機。\n安全模式會阻止載入您的設定檔。您可以在此環境中進行故障排除,或使用下方按鈕載入您的設定。\n預設快" - "捷鍵適用:SUPER+Q 開啟 Kitty,SUPER+R 開啟簡易啟動器,SUPER+M 退出。\n重新啟動 " - "Hyprland 將再次進入正常模式。"); - huEngine->registerEntry("zh_TW", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "載入設定檔"); - huEngine->registerEntry("zh_TW", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "開啟當機報告目錄"); - huEngine->registerEntry("zh_TW", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "好,關閉視窗"); - - // ar (Arabic - Modern Standard) - huEngine->registerEntry("ar", TXT_KEY_ANR_TITLE, "التطبيق لا يستجيب"); - huEngine->registerEntry("ar", TXT_KEY_ANR_CONTENT, "التطبيق {title} - {class} لا يستجيب.\nما الذي تريد فعله؟"); - huEngine->registerEntry("ar", TXT_KEY_ANR_OPTION_TERMINATE, "إنهاء"); - huEngine->registerEntry("ar", TXT_KEY_ANR_OPTION_WAIT, "الانتظار"); - huEngine->registerEntry("ar", TXT_KEY_ANR_PROP_UNKNOWN, "(غير معروف)"); - - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "يطلب التطبيق {app} صلاحية غير معروفة."); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "يحاول التطبيق {app} التقاط الشاشة.\n\nهل تريد السماح له بذلك؟"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "يحاول التطبيق {app} تحميل إضافة: {plugin}.\n\nهل تريد السماح له بذلك؟"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "تم اكتشاف لوحة مفاتيح جديدة: {keyboard}.\n\nهل تريد السماح لها بالعمل؟"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(غير معروف)"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_TITLE, "طلب الإذن"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "تلميح: يمكنك تعيين قواعد دائمة لهذه الطلبات في ملف إعدادات Hyprland."); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_ALLOW, "السماح"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "السماح مع تذكّر الاختيار"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_ALLOW_ONCE, "السماح لمرة واحدة"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_DENY, "الرفض"); - huEngine->registerEntry("ar", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "تطبيق غير معروف (معرّف عميل Wayland {wayland_id})"); - - huEngine->registerEntry("ar", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "يبدو أنّ متغيّر البيئة XDG_CURRENT_DESKTOP يُدار من خارج النظام، والقيمة الحالية هي {value}.\n" - "قد يؤدي ذلك إلى مشكلات ما لم يكن مقصودًا."); - huEngine->registerEntry("ar", TXT_KEY_NOTIF_NO_GUIUTILS, "لا يحتوي نظامك على الحزمة hyprland-guiutils مثبتة. هذه حزمة مطلوبة أثناء التشغيل لبعض مربعات الحوار. يُنصَح بتثبيتها."); - huEngine->registerEntry("ar", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "فشل Hyprland في تحميل مورد أساسي ({count}). قد يكون السبب سوء تغليف الحزم في التوزيعة."; - return "فشل Hyprland في تحميل {count} من الموارد الأساسية. قد يكون السبب سوء تغليف الحزم في التوزيعة."; - }); - huEngine->registerEntry("ar", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "تم إعداد مخطط الشاشات لديك بشكل غير صحيح. الشاشة {name} تتداخل مع شاشة أو أكثر في المخطط.\n" - "يرجى مراجعة صفحة الشاشات في الويكي لمزيد من التفاصيل. هذا سيسبب مشكلات."); - huEngine->registerEntry("ar", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "فشلت الشاشة {name} في ضبط أي من الأوضاع المطلوبة، وسيتم الرجوع إلى الوضع {mode}."); - huEngine->registerEntry("ar", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "تم تمرير قيمة تحجيم غير صالحة إلى الشاشة {name}: {scale}. سيتم استخدام قيمة التحجيم المقترحة: {fixed_scale}."); - huEngine->registerEntry("ar", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "فشل تحميل الإضافة {name}: {error}"); - huEngine->registerEntry("ar", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "فشلت إعادة تحميل نظام إدارة الألوان (CM). سيتم الرجوع إلى صيغة الألوان rgba/rgbx."); - huEngine->registerEntry("ar", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "الشاشة {name}: تم تفعيل نطاق الألوان الواسع، لكن العرض ليس في وضع 10 بت."); - - huEngine->registerEntry("ar", TXT_KEY_SAFE_MODE_TITLE, "الوضع الآمن"); - huEngine->registerEntry("ar", TXT_KEY_SAFE_MODE_DESCRIPTION, - "شُغل Hyprland في الوضع الآمن، هذا يعني أن جلستك الأخيرة قد انهارت.\nالوضع الآمن يمنع تحميل إعداداتك، " - "يمكنك البحث عن وحل المشاكل في هذه البيئة، أو تحميل إعداداتك باستخدام الزر أدناه.\n اختصارات المفاتيح الافتراضية: الطرفية (Kitty) — SUPER+Q، مشغّل " - "الأوامر البسيط — SUPER+R، الخروج — SUPER+M.\n" - "إعادة تشغيل Hyprland سيشغله في الوضع العادي"); - huEngine->registerEntry("ar", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "حمل ملف الإعدادات"); - huEngine->registerEntry("ar", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "افتح مجلد تقرير الانهيار"); - huEngine->registerEntry("ar", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "حسنًا، أغلق هذا"); - - // ro_RO (Romanian) - huEngine->registerEntry("ro_RO", TXT_KEY_ANR_TITLE, "Aplicația Nu Răspunde"); - huEngine->registerEntry("ro_RO", TXT_KEY_ANR_CONTENT, "O aplicație {title} - {class} nu răspunde.\nCe vrei să faci cu ea?"); - huEngine->registerEntry("ro_RO", TXT_KEY_ANR_OPTION_TERMINATE, "Închide"); - huEngine->registerEntry("ro_RO", TXT_KEY_ANR_OPTION_WAIT, "Așteaptă"); - huEngine->registerEntry("ro_RO", TXT_KEY_ANR_PROP_UNKNOWN, "(necunoscut)"); - - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "O aplicație {app} solicită o permisiune necunoscută."); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "O aplicație {app} încearcă să captureze ecranul.\n\nDorești să îi permiți acest lucru?"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_REQUEST_PLUGIN, - "O aplicație {app} încearcă să încarce un plugin: {plugin}.\n\nDorești să îi permiți acest lucru?"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "A fost detectată o tastatură nouă: {keyboard}.\n\nDorești să îi permiți să funcționeze?"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(necunoscut)"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_TITLE, "Cerere de permisiune"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Indiciu: poți seta reguli persistente pentru acestea în fișierul de configurare Hyprland."); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_ALLOW, "Permite"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permite și reține"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permite o dată"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_DENY, "Respinge"); - huEngine->registerEntry("ro_RO", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Aplicație necunoscută (ID client wayland {wayland_id})"); - - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Se pare că mediul tău XDG_CURRENT_DESKTOP este gestionat extern, iar valoarea curentă este {value}.\nAcest lucru ar putea cauza probleme, cu excepția " - "cazului în care este intenționat."); - huEngine->registerEntry( - "ro_RO", TXT_KEY_NOTIF_NO_GUIUTILS, - "Sistemul tău nu are instalat hyprland-guiutils. Aceasta este o dependență de execuție pentru anumite dialoguri. Ia în considerare instalarea acesteia."); - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo == 1) - return "Hyprland nu a reușit să încarce un element esențial. Dă vina pe packager-ul distro-ului tău că a făcut o treabă proastă la ambalare!"; - return "Hyprland nu a reușit să încarce {count} elemente esențiale. Dă vina pe packager-ul distro-ului tău că a făcut o treabă proastă la ambalare!"; - }); - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Configurația monitorului este incorectă. Monitorul {name} se suprapune cu alte monitoare.\nConsultați wiki-ul (pagina Monitoare) pentru " - "mai multe informații. Acest lucru va cauza probleme."); - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitorul {name} nu a reușit să seteze niciun mod solicitat, revenind la modul {mode}."); - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Scară nevalidă transmisă monitorului {name}: {scale}, se utilizează scara sugerată: {fixed_scale}"); - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nu s-a putut încărca pluginul {name}: {error}"); - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Reîncărcarea shaderului CM a eșuat, revenind la rgba/rgbx."); - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: gama largă de culori este activată, dar afișajul nu este în modul pe 10 biți."); - huEngine->registerEntry("ro_RO", TXT_KEY_NOTIF_NO_WATCHDOG, - "Hyprland a fost pornit fără start-hyprland. Acest lucru nu este recomandat decât dacă te afli într-un mediu de depanare."); - - huEngine->registerEntry("ro_RO", TXT_KEY_SAFE_MODE_TITLE, "Modul de Siguranță"); - huEngine->registerEntry( - "ro_RO", TXT_KEY_SAFE_MODE_DESCRIPTION, - "Hyprland a fost lansat în modul de siguranță, ceea ce înseamnă că ultima sesiune s-a blocat.\nModul de siguranță împiedică încărcarea configurației. Poți " - "depana în acest mediu sau să încarci configurația cu butonul de mai jos.\nSe aplică combinațiile de taste implicite: SUPER+Q pentru kitty, SUPER+R pentru un runner de " - "bază." - "SUPER+M pentru ieșire.\nLa repornire " - "Hyprland se va lansa din nou în modul normal."); - huEngine->registerEntry("ro_RO", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "Încarcă configurația"); - huEngine->registerEntry("ro_RO", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "Deschide locația rapoartelor de crash-uri"); - huEngine->registerEntry("ro_RO", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "Ok, închide"); - - // ru_RU (Russian) - huEngine->registerEntry("ru_RU", TXT_KEY_ANR_TITLE, "Приложение не отвечает"); - huEngine->registerEntry("ru_RU", TXT_KEY_ANR_CONTENT, "Приложение {title} - {class} не отвечает.\nЧто вы хотите сделать?"); - huEngine->registerEntry("ru_RU", TXT_KEY_ANR_OPTION_TERMINATE, "Завершить"); - huEngine->registerEntry("ru_RU", TXT_KEY_ANR_OPTION_WAIT, "Подождать"); - huEngine->registerEntry("ru_RU", TXT_KEY_ANR_PROP_UNKNOWN, "(неизвестно)"); - - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Приложение {app} запрашивает неизвестное разрешение."); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Приложение {app} пытается получить доступ к вашему экрану.\n\nРазрешить?"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Приложение {app} пытается загрузить плагин: {plugin}.\n\nРазрешить?"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Обнаружена новая клавиатура: {keyboard}.\n\nРазрешить ей работать?"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(неизвестно)"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_TITLE, "Запрос разрешения"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Подсказка: вы можете настроить постоянные правила для этого в конфигурационном файле Hyprland."); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_ALLOW, "Разрешить"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Разрешить и запомнить"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_ALLOW_ONCE, "Разрешить в этот раз"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_DENY, "Отклонить"); - huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Неизвестное приложение (wayland client ID {wayland_id})"); - - huEngine->registerEntry( - "ru_RU", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Переменная окружения XDG_CURRENT_DESKTOP установлена извне, текущее значение: {value}.\nЭто может вызвать проблемы, если только это не сделано намеренно."); - huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_NO_GUIUTILS, "Пакет hyprland-guiutils не установлен. Он необходим для некоторых диалогов. Рекомендуется установить его."); - huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Не удалось загрузить {count} критически важный ресурс, пожалуйтесь мейнтейнеру вашего дистрибутива за кривую сборку пакета!"; - return "Не удалось загрузить {count} критически важных ресурсов, пожалуйтесь мейнтейнеру вашего дистрибутива за кривую сборку пакета!"; - }); - huEngine->registerEntry( - "ru_RU", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Неправильно настроен макет мониторов. Монитор {name} перекрывает другие.\nПодробнее см. в документации (страница Monitors). Это обязательно вызовет проблемы."); - huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Монитор {name} не смог установить ни один из запрошенных режимов, выбран режим {mode}."); - huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Недопустимый масштаб для монитора {name}: {scale}, используется предложенный масштаб: {fixed_scale}"); - huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Не удалось загрузить плагин {name}: {error}"); - huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Не удалось перезагрузить CM shader, используется rgba/rgbx."); - huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Монитор {name}: расширенный цветовой охват включён, но дисплей не в 10-bit режиме."); - huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_NO_WATCHDOG, "Hyprland был запущен без start-hyprland. Это крайне не рекомендуется, если только вы не в отладочной среде."); - - huEngine->registerEntry("ru_RU", TXT_KEY_SAFE_MODE_TITLE, "Безопасный режим"); - huEngine->registerEntry( - "ru_RU", TXT_KEY_SAFE_MODE_DESCRIPTION, - "Hyprland запущен в безопасном режиме, это значит, что ваш прошлый сеанс завершился сбоем.\nБезопасный режим не загружает ваш конфиг. Вы можете " - "исправить проблему в этом окружении или загрузить конфиг кнопкой ниже.\nДействуют стандартные бинды: SUPER+Q запускает kitty, SUPER+R открывает лаунчер, " - "SUPER+M для выхода.\nПосле перезапуска Hyprland снова запустится в обычном режиме."); - huEngine->registerEntry("ru_RU", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "Загрузить конфиг"); - huEngine->registerEntry("ru_RU", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "Открыть каталог отчётов о сбоях"); - huEngine->registerEntry("ru_RU", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "Ок, закрыть"); - - // sl_SI (Slovenian) - huEngine->registerEntry("sl_SI", TXT_KEY_ANR_TITLE, "Program se ne odziva"); - huEngine->registerEntry("sl_SI", TXT_KEY_ANR_CONTENT, "Program {title} - {class} se ne odziva.\nKaj želite storiti?"); - huEngine->registerEntry("sl_SI", TXT_KEY_ANR_OPTION_TERMINATE, "Prekini"); - huEngine->registerEntry("sl_SI", TXT_KEY_ANR_OPTION_WAIT, "Počakaj"); - huEngine->registerEntry("sl_SI", TXT_KEY_ANR_PROP_UNKNOWN, "(neznano)"); - - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Program {app} zahteva neznano dovoljenje."); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Program {app} poskuša zajeti vaš zaslon.\n\nAli mu želite to dovoliti?"); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Program {app} skuša naložiti vtičnik: {plugin}.\n\nAli mu želite to dovoliti?"); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Nova tipkovnica je bila zaznana: {keyboard}.\n\nAli ji želite dovoliti delovanje?"); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(neznano)"); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_TITLE, "Zahteva za dovoljenje"); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Namig: v Hyprlandovi konfiguracijski datoteki lahko nastavite stalna pravila za dovoljenja."); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_ALLOW, "Dovoli"); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Dovoli in si zapomni"); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_ALLOW_ONCE, "Dovoli enkrat"); - huEngine->registerEntry("sl_SI", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Neznan program (ID stranke Wayland: {wayland_id})"); - - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Zdi se, da je Vaše okolje XDG_CURRENT_DESKTOP upravljano od zunaj, trenutna vrednost je {value}.\nTo lahko povzroči težave, če ni namerno."); - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_NO_GUIUTILS, - "hyprland-guiutils ni nameščen na vaši napravi. To je odvisnost od izvajalnega okolja za nekatera pogovorna okna. Razmislite o namestitvi."); - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assertNo = std::stoi(vars.at("count")); - if (assertNo <= 1) - return "Hyprlandu ni uspelo naložiti {count} bistvenega sredstva, krivite upravitelja paketov vaše distribucije za slabo opravljeno pakiranje!"; - return "Hyprlandu ni uspelo naložiti {count} bistvenih sredstev, krivite upravitelja paketov vaše distribucije za slabo opravljeno pakiranje!"; - }); - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Vaša zaslonska razporeditev je napačno nastavljena. Zaslon {monitor} se prekriva z drugimi zasloni v razporeditvi.\n" - "Prosimo poglejte wiki (stran Monitors) za več. To bo povzročilo težave."); - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Zaslon {name} ni uspel nastaviti nobenega zahtevanega načina, vrnitev k načinu {mode}."); - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "Neveljavna skala je bila posredovana zaslonu {name}: {scale}, uporabljena je predlagana skala: {fixed_scale}"); - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Vtičnika {name} ni bilo mogoče naložiti: {error}"); - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Ponovno nalaganje senčnika CM ni uspelo, vrnitev k rgba/rgbx."); - huEngine->registerEntry("sl_SI", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Zaslon {name}: širok barvni razpon je omogočen, vendar zaslon ni v 10-bitnem načinu."); - - // sr_RS (Serbian) - huEngine->registerEntry("sr_RS", TXT_KEY_ANR_TITLE, "Апликација не реагује"); - huEngine->registerEntry("sr_RS", TXT_KEY_ANR_CONTENT, "Апликација {title} - {class} не реагује.\nШта желите да урадите са њом?"); - huEngine->registerEntry("sr_RS", TXT_KEY_ANR_OPTION_TERMINATE, "Прекини"); - huEngine->registerEntry("sr_RS", TXT_KEY_ANR_OPTION_WAIT, "Чекај"); - huEngine->registerEntry("sr_RS", TXT_KEY_ANR_PROP_UNKNOWN, "(непознато)"); - - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Апликација {app} захтева непознату дозволу."); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Апликација {app} покушава да снима твој екран.\n\nДа ли желиш да то дозволиш?"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Апликација {app} покушава да учита додатак: {plugin}.\n\nДа ли желиш да то дозволиш?"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Нова тастатура је детектована: {keyboard}.\n\nДа ли желиш да дозволиш њен рад?"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(непознато)"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_TITLE, "Захтев за дозволу"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Савет: можеш направити трајна правила за ово у Hyprland конфигурационој датотеци."); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_ALLOW, "Дозволи"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Дозволи и запамти"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_ALLOW_ONCE, "Дозволи једном"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_DENY, "Одбиј"); - huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Непозната апликација (wayland client ID {wayland_id})"); - - huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Изгледа да се твојим XDG_CURRENT_DESKTOP окружењем управља споља, и тренутна вредност је {value}.\nОво може правити проблеме осим ако је намерно."); - huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_NO_GUIUTILS, - "Твој систем нема инсталиран hyprland-guiutils. Ово је зависност при покретању за неке дијалоге. Размотри инсталацију."); - huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland није успео да учита {count} кључни ресурс, криви пакера твоје дистрибуције за лоше одрађен посао!"; - if (assetsNo <= 4) - return "Hyprland није успео да учита {count} кључна ресурса, криви пакера твоје дистрибуције за лоше одрађен посао!"; - return "Hyprland није успео да учита {count} кључних ресурса, криви пакера твоје дистрибуције за лоше одрађен посао!"; - }); - huEngine->registerEntry( - "sr_RS", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Твој распоред монитора је неправилно постављен. Монитор {name} се преклапа са другим монитором/мониторима у распореду.\nМолим те погледај вики (Monitors страницу) за " - "више информација. Ово ће изазвати проблеме."); - huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Монитор {name} није успео да постави ниједан тражени режим, враћање на режим {mode}."); - huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Невалидна скала прослеђена монитору {name}: {scale}, користи се препоручена скала: {fixed_scale}"); - huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Неуспешно учитавање додатка {name}: {error}"); - huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Поново учитавање CM шејдера није успело, враћање на rgba/rgbx."); - huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Монитор {name}: широк спектар боја је омогућен али екран није у 10-битном режиму."); - - // sr_RS@latin (Serbian Latin) - huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_TITLE, "Aplikacija ne reaguje"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_CONTENT, "Aplikacija {title} - {class} ne reaguje.\nŠta želite da uradite sa njom?"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_OPTION_TERMINATE, "Prekini"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_OPTION_WAIT, "Čekaj"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_PROP_UNKNOWN, "(nepoznato)"); - - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Aplikacija {app} zahteva nepoznatu dozvolu."); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Aplikacija {app} pokušava da snima tvoj ekran.\n\nDa li želiš da to dozvoliš?"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Aplikacija {app} pokušava da učita dodatak: {plugin}.\n\nDa li želiš da to dozvoliš?"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Nova tastatura je detektovana: {keyboard}.\n\nDa li želiš da dozvoliš njen rad?"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(nepoznato)"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_TITLE, "Zahtev za dozvolu"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Savet: možeš napraviti trajna pravila za ovo u Hyprland konfiguracionoj datoteci."); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_ALLOW, "Dozvoli"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Dozvoli i zapamti"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_ALLOW_ONCE, "Dozvoli jednom"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_DENY, "Odbij"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Nepoznata aplikacija (wayland client ID {wayland_id})"); - - huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Izgleda da se tvojim XDG_CURRENT_DESKTOP okruženjem upravlja spolja, i trenutna vrednost je {value}.\nOvo može praviti probleme osim ako je namerno."); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_NO_GUIUTILS, - "Tvoj sistem nema instaliran hyprland-guiutils. Ovo je zavisnost pri pokretanju za neke dijaloge. Razmotri instalaciju."); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprland nije uspeo da učita {count} ključni resurs, krivi pakera tvoje distribucije za loše odrađen posao!"; - if (assetsNo <= 4) - return "Hyprland nije uspeo da učita {count} ključna resursa, krivi pakera tvoje distribucije za loše odrađen posao!"; - return "Hyprland nije uspeo da učita {count} ključnih resursa, krivi pakera tvoje distribucije za loše odrađen posao!"; - }); - huEngine->registerEntry( - "sr_RS@latin", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Tvoj raspored monitora je nepravilno postavljen. Monitor {name} se preklapa sa drugim monitorom/monitorima u rasporedu.\nMolim te pogledaj wiki (Monitors stranicu) za " - "više informacija. Ovo će izazvati probleme."); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} nije uspeo da postavi nijedan traženi režim, vraćanje na režim {mode}."); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Nevalidna skala prosleđena monitoru {name}: {scale}, koristi se preporučena skala: {fixed_scale}"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Neuspešno učitavanje dodatka {name}: {error}"); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Ponovno učitavanje CM šejdera nije uspelo, vraćanje na rgba/rgbx."); - huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: širok spektar boja je omogućen ali ekran nije u 10-bitnom režimu."); - - // tr_TR (Turkish) - huEngine->registerEntry("tr_TR", TXT_KEY_ANR_TITLE, "Uygulama Yanıt Vermiyor"); - huEngine->registerEntry("tr_TR", TXT_KEY_ANR_CONTENT, "Bir uygulama {title} - {class} yanıt vermiyor.\nBununla ne yapmak istiyorsun?"); - huEngine->registerEntry("tr_TR", TXT_KEY_ANR_OPTION_TERMINATE, "Sonlandır"); - huEngine->registerEntry("tr_TR", TXT_KEY_ANR_OPTION_WAIT, "Bekle"); - huEngine->registerEntry("tr_TR", TXT_KEY_ANR_PROP_UNKNOWN, "(bilinmiyor)"); - - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Bir uygulama {app} bilinmeyen bir izin istiyor."); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Bir uygulama {app} ekran kaydı yapmaya çalışıyor.\n\nİzin vermek istiyor musun?"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Bir uygulama {app} bir eklenti kurmaya çalışıyor: {plugin}.\n\nİzin vermek istiyor musun?"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Yeni bir klavye algılandı: {keyboard}.\n\nÇalışmasına izin vermek istiyor musun?"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(bilinmiyor)"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_TITLE, "İzin isteği"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "İpucu: Hyprland config dosyasında bunlar için kalıcı kurallar atayabilirsin."); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_ALLOW, "İzin ver"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "İzin ver ve seçimimi hatırla"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_ALLOW_ONCE, "Yalnızca bir defa izin ver"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_DENY, "Reddet"); - huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Bilinmeyen uygulama (wayland istemci ID {wayland_id})"); - - huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "XDG_CURRENT_DESKTOP ortamın harici olarak yönetiliyor gibi gözüküyor, ve mevcut değeri {value}.\nEğer bu bilinçli değilse sorunlara yol açabilir."); - huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_NO_GUIUTILS, - "Sisteminde hyprland-guiutils yüklü değil. Bu bazı diyaloglar için bir çalışma zamanı bağımlılığı. İndirmeyi göz önünde bulundurabilirsin."); - huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_FAILED_ASSETS, - "Hyprland {count} gerekli dosyayı yüklemekte başarısız oldu, kötü bir iş çıkardığı için kullandığın distronun paketleyicisini suçla!"); - huEngine->registerEntry( - "tr_TR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Monitör düzenin yanlış ayarlanmış. Monitör {name} düzenindeki başka monitörlerle çakışıyor.\nLütfen daha fazla bilgi için wiki'ye (Monitörler sayfası) göz at. " - "Bu sorunlara yol açacak."); - huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitör {name} istenen modları ayarlamada başarısız oldu, {mode} moduna geri dönülüyor."); - huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Monitöre geçersiz ölçek iletildi {name}: {scale}, önerilen ölçek kullanılıyor: {fixed_scale}"); - huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "{name} plugini yüklenemedi: {error}"); - huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader yeniden yüklemesi başarısız, rgba/rgbx'e geri dönülüyor."); - huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitör {name}: wide color gamut etkinleştirildi ama ekran 10-bit modunda değil."); - - // tt_RU (Tatar) - huEngine->registerEntry("tt_RU", TXT_KEY_ANR_TITLE, "Программа җавап бирми"); - huEngine->registerEntry("tt_RU", TXT_KEY_ANR_CONTENT, "Программасы {title} - {class} җавап бирми.\nСез аның белән нәрсә эшләргә телисез?"); - huEngine->registerEntry("tt_RU", TXT_KEY_ANR_OPTION_TERMINATE, "Тәмам итү"); - huEngine->registerEntry("tt_RU", TXT_KEY_ANR_OPTION_WAIT, "Көтү"); - huEngine->registerEntry("tt_RU", TXT_KEY_ANR_PROP_UNKNOWN, "(билгесез)"); - - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "{app} программасы билгесез рөхсәт сорый."); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "{app} программасы сезнең экранны яздырырга тели.\n\nРөхсәт бирәсезме?"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "{app} программасы плагин йөкләргә тели: {plugin}.\n\nРөхсәт бирәсезме?"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Яңа клавиатура табылды: {keyboard}.\n\nАның эшләргә рөхсәт бирәсезме?"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(билгесез)"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_TITLE, "Рөхсәт сорау"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Киңәш: сез Hyprland көйләү файлында даими кагыйдәләр куя аласыз."); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_ALLOW, "Рөхсәт бирү"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Рөхсәт бирү һәм истә калдыру"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_ALLOW_ONCE, "Бер тапкыр рөхсәт бирү"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_DENY, "Кире кагу"); - huEngine->registerEntry("tt_RU", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Билгесез программа (wayland client ID {wayland_id})"); - - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Сезнең XDG_CURRENT_DESKTOP мохите тыштан идарә ителә, хәзерге кыйммәте: {value}.\n" - "Бу теләгән булмаса, проблемалар китереп чыгарырга мөмкин."); - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_NO_GUIUTILS, - "Сезнең системада hyprland-guiutils урнаштырылмаган. Бу кайбер диалоглар өчен кирәкле вакыт бәйлелеге. Урнаштыруны карап чыгыгыз."); - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_FAILED_ASSETS, "Hyprland {count} мөһим ресурсны йөкли алмады. Ул дистрибутивыгыз пакетлаучысының хатасы!"); - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Сезнең мониторлар урнашуы дөрес түгел. {name} мониторы башка монитор белән өстәлә.\n" - "Зинһар, өстәмә мәгълүмат өчен викидагы (Monitors бит) мөрәҗәгать итегез. Бу һичшиксез проблемалар тудырачак."); - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "{name} мониторы соралган режимнарны куя алмады, {mode} режимына кайта."); - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "{name} мониторы өчен яраксыз масштаб билгеләнгән: {scale}. Тәкъдим ителгән масштаб кулланыла: {fixed_scale}"); - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "{name} плагинны йөкләүдә хата: {error}"); - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM шейдерын яңадан йөкләү уңышсыз булды, rgba/rgbx режимына кайтыла."); - huEngine->registerEntry("tt_RU", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Монитор {name}: киң төсләр диапазоны кушылган, ләкин дисплей 10-бит режимында түгел."); - - // uk_UA (Ukrainian) - huEngine->registerEntry("uk_UA", TXT_KEY_ANR_TITLE, "Програма не відповідає"); - huEngine->registerEntry("uk_UA", TXT_KEY_ANR_CONTENT, "Програма {title} - {class} не відповідає.\nЩо ви хочете з нею зробити?"); - huEngine->registerEntry("uk_UA", TXT_KEY_ANR_OPTION_TERMINATE, "Завершити"); - huEngine->registerEntry("uk_UA", TXT_KEY_ANR_OPTION_WAIT, "Чекати"); - huEngine->registerEntry("uk_UA", TXT_KEY_ANR_PROP_UNKNOWN, "(невідомо)"); - - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Програма {app} запитує невідомий дозвіл."); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Програма {app} намагається захопити екран.\n\nДозволити?"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Програма {app} намагається завантажити плагін: {plugin}.\n\nДозволити?"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Виявлено нову клавіатуру: {keyboard}.\n\nДозволити її використання?"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(невідомо)"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_TITLE, "Запит дозволу"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Підказка: ви можете встановити постійні правила для дозволів у файлі конфігурації Hyprland."); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_ALLOW, "Дозволити"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Дозволити та запам'ятати"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_ALLOW_ONCE, "Дозволити один раз"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_DENY, "Заборонити"); - huEngine->registerEntry("uk_UA", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Невідома програма (ідентифікатор клієнта wayland {wayland_id})"); - - huEngine->registerEntry( - "uk_UA", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Ваше середовище XDG_CURRENT_DESKTOP, схоже, керується ззовні, і поточне значення становить {value}.\nЦе може спричинити проблеми, якщо це не зроблено навмисно."); - huEngine->registerEntry( - "uk_UA", TXT_KEY_NOTIF_NO_GUIUTILS, - "У вашій системі не встановлено hyprland-guiutils. Це залежність, потрібна для роботи деяких діалогових вікон. Розгляньте можливість його встановлення."); - huEngine->registerEntry("uk_UA", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Не вдалося завантажити {count} необхідний ресурс, звинувачуйте пакувальника свого дистрибутива у недобросовісній роботі!"; - return "Не вдалося завантажити {count} необхідних ресурсів, звинувачуйте пакувальника свого дистрибутива у недобросовісній роботі!"; - }); - huEngine->registerEntry("uk_UA", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Макет моніторів налаштовано неправильно. Монітор {name} перекриває інші монітори у макеті.\nБудь ласка, перегляньте wiki (сторінка Monitors). " - "Це обов'язково створить проблеми."); - huEngine->registerEntry("uk_UA", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Монітор {name} не зміг встановити жодного із запитуваних режимів, повернення до режиму {mode}."); - huEngine->registerEntry("uk_UA", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - "Неправильний масштаб переданий монітору {name}: {scale}, використання запропонованого масштабу: {fixed_scale}"); - huEngine->registerEntry("uk_UA", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Помилка завантаження плагіна {name}: {error}"); - huEngine->registerEntry("uk_UA", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Не вдалося перезавантажити шейдер CM, повернення до rgba/rgbx."); - huEngine->registerEntry("uk_UA", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Монітор {name}: широка кольорова гама увімкнена, але дисплей не працює в 10-бітному режимі."); - - // vi_VN (Vietnamese) - huEngine->registerEntry("vi_VN", TXT_KEY_ANR_TITLE, "Ứng dụng không phản hồi"); - huEngine->registerEntry("vi_VN", TXT_KEY_ANR_CONTENT, "Ứng dụng {title} - {class} đang bị treo.\nBạn muốn xử lý thế nào?"); - huEngine->registerEntry("vi_VN", TXT_KEY_ANR_OPTION_TERMINATE, "Buộc dừng"); - huEngine->registerEntry("vi_VN", TXT_KEY_ANR_OPTION_WAIT, "Chờ"); - huEngine->registerEntry("vi_VN", TXT_KEY_ANR_PROP_UNKNOWN, "(không xác định)"); - - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Ứng dụng {app} đang yêu cầu một quyền không xác định."); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Ứng dụng {app} đang cố gắng ghi hình màn hình của bạn.\n\nBạn muốn cho phép không?"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_CURSOR_POS, "Ứng dụng {app} đang cố gắng đọc vị trí chuột của bạn.\n\nBạn muốn cho phép không?"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Ứng dụng {app} đang cố gắng tải plugin: {plugin}.\n\nBạn muốn cho phép không?"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Phát hiện bàn phím mới: {keyboard}.\n\nBạn muốn cho phép bàn phím này hoạt động không?"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(không xác định)"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_TITLE, "Yêu cầu cấp quyền"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Gợi ý: bạn có thể thiết lập các quyền này trong tệp cấu hình Hyprland."); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_ALLOW, "Cho phép"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Cho phép và ghi nhớ"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_ALLOW_ONCE, "Chỉ một lần"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_DENY, "Từ chối"); - huEngine->registerEntry("vi_VN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Ứng dụng không xác định (wayland client ID {wayland_id})"); - - huEngine->registerEntry( - "vi_VN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Biến môi trường XDG_CURRENT_DESKTOP dường như đang được thiết lập từ bên ngoài với giá trị là {value}.\nViệc này có thể gây ra lỗi trừ khi đó là chủ ý của bạn."); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_NO_GUIUTILS, "Hệ thống chưa cài hyprland-guiutils. Một số hộp thoại sẽ không hiển thị nếu thiếu nó."); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_FAILED_ASSETS, - "Hyprland không thể tải {count} tài nguyên quan trọng. Vui lòng báo lỗi cho người đóng gói (packager) của bản phân phối (distro) mà bạn dùng!"); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Bố cục màn hình không hợp lệ. Màn hình {name} đang bị đè lên các màn hình khác.\nVui lòng xem trang Monitors trên wiki để " - "khắc phục, nếu không chắc chắn sẽ có lỗi xảy ra."); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Màn hình {name} không thể áp dụng chế độ nào được yêu cầu, đang dùng tạm chế độ {mode}."); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Tỉ lệ {scale} cho màn hình {name} không hợp lệ, chuyển sang tỷ lệ gợi ý: {fixed_scale}"); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Lỗi tải plugin {name}: {error}"); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Tải lại CM shader thất bại, đang dùng tạm rgba/rgbx."); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Màn hình {name}: dải màu rộng (wide color gamut) khả dụng nhưng màn hình không ở chế độ 10-bit."); - huEngine->registerEntry("vi_VN", TXT_KEY_NOTIF_NO_WATCHDOG, - "Hyprland đã được khởi động mà không thông qua start-hyprland. Việc này không được khuyến khích trừ khi dùng cho mục đích gỡ lỗi."); - - huEngine->registerEntry("vi_VN", TXT_KEY_SAFE_MODE_TITLE, "Chế độ An toàn (Safe Mode)"); - huEngine->registerEntry("vi_VN", TXT_KEY_SAFE_MODE_DESCRIPTION, - "Phiên hoạt động trước đó đã bị sập (crash).\nHyprland hiện đang chạy ở chế độ an toàn và không tải tệp cấu hình của bạn. Bạn có thể " - "khắc phục sự cố trong môi trường này, hoặc bấm nút bên dưới để thử tải lại cấu hình.\nCác phím tắt mặc định: SUPER+Q (kitty), SUPER+R (runner), " - "SUPER+M (exit).\nHyprland sẽ về chế độ bình thường sau khi khởi động lại."); - huEngine->registerEntry("vi_VN", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "Tải cấu hình"); - huEngine->registerEntry("vi_VN", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "Mở thư mục báo cáo lỗi (crash report)"); - huEngine->registerEntry("vi_VN", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "OK, đã hiểu"); - - // cs_CZ (Czech) - huEngine->registerEntry("cs_CZ", TXT_KEY_ANR_TITLE, "Aplikace Neodpovídá"); - huEngine->registerEntry("cs_CZ", TXT_KEY_ANR_CONTENT, "Aplikace {title} - {class} neodpovídá.\nCo s ní chcete udělat?"); - huEngine->registerEntry("cs_CZ", TXT_KEY_ANR_OPTION_TERMINATE, "Ukončit"); - huEngine->registerEntry("cs_CZ", TXT_KEY_ANR_OPTION_WAIT, "Počkat"); - huEngine->registerEntry("cs_CZ", TXT_KEY_ANR_PROP_UNKNOWN, "(neznámé)"); - - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Aplikace {app} vyžaduje neznámé oprávnění."); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Aplikace {app} se pokouší zaznamenávat vaši obrazovku.\n\nChcete jí to povolit?"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Aplikace {app} se pokouší načíst plugin: {plugin}.\n\nChcete jí to povolit?"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Byla detekována nová klávesnice: {keyboard}.\n\nChcete jí povolit?"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(neznámé)"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_TITLE, "Žádost o oprávnění"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tip: pro tyto případy můžete nastavit trvalá pravidla v konfiguračním souboru Hyprland."); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_ALLOW, "Povolit"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Povolit a zapamatovat"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_ALLOW_ONCE, "Povolit jednou"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_DENY, "Zamítnout"); - huEngine->registerEntry("cs_CZ", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Neznámá aplikace (ID klienta Wayland {wayland_id})"); - - huEngine->registerEntry( - "cs_CZ", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - "Proměnná prostředí XDG_CURRENT_DESKTOP se zdá být spravována externě a její aktuální hodnota je {value}.\nPokud to není záměr, může to způsobit problémy."); - huEngine->registerEntry("cs_CZ", TXT_KEY_NOTIF_NO_GUIUTILS, - "V systému není nainstalován balíček hyprland-guiutils. Jedná se o závislost pro běh některých dialogových oken. Zvažte jeho instalaci."); - huEngine->registerEntry("cs_CZ", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) { - int assetsNo = std::stoi(vars.at("count")); - if (assetsNo <= 1) - return "Hyprlandu se nepodařilo načíst {count} nezbytnou součást. Za špatně odvedenou práci viňte tvůrce balíčku vaší distribuce!"; - if (assetsNo <= 4) - return "Hyprlandu se nepodařilo načíst {count} nezbytné součásti. Za špatně odvedenou práci viňte tvůrce balíčku vaší distribuce!"; - return "Hyprlandu se nepodařilo načíst {count} nezbytných součástí. Za špatně odvedenou práci viňte tvůrce balíčku vaší distribuce!"; - }); - huEngine->registerEntry("cs_CZ", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - "Rozložení vašich monitorů je nastaveno nesprávně. Monitor {name} se překrývá s ostatními monitory.\nVíce informací naleznete na wiki " - "(stránka Monitors). Toto způsobí problémy."); - huEngine->registerEntry("cs_CZ", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitoru {name} se nepodařilo nastavit žádný z požadovaných režimů, vrací se k režimu {mode}."); - huEngine->registerEntry("cs_CZ", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Monitoru {name} bylo předáno neplatné měřítko: {scale}, použije se navrhované měřítko: {fixed_scale}"); - huEngine->registerEntry("cs_CZ", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nepodařilo se načíst plugin {name}: {error}"); - huEngine->registerEntry("cs_CZ", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Nepodařilo se znovu načíst CM shader, vrací se k rgba/rgbx."); - huEngine->registerEntry("cs_CZ", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: široký barevný gamut je povolen, ale displej není v 10bitovém režimu."); -} - -std::string I18n::CI18nEngine::localize(eI18nKeys key, const Hyprutils::I18n::translationVarMap& vars) { - static auto CONFIG_LOCALE = CConfigValue("general:locale"); - std::string locale = *CONFIG_LOCALE != "" ? *CONFIG_LOCALE : localeStr; - return huEngine->localizeEntry(locale, key, vars); -} diff --git a/src/i18n/Engine.hpp b/src/i18n/Engine.hpp deleted file mode 100644 index 79ec86f8..00000000 --- a/src/i18n/Engine.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "../helpers/memory/Memory.hpp" -#include -#include -#include - -namespace I18n { - - enum eI18nKeys : uint8_t { - TXT_KEY_ANR_TITLE = 0, - TXT_KEY_ANR_CONTENT, - TXT_KEY_ANR_OPTION_TERMINATE, - TXT_KEY_ANR_OPTION_WAIT, - TXT_KEY_ANR_PROP_UNKNOWN, - - TXT_KEY_PERMISSION_REQUEST_UNKNOWN, - TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, - TXT_KEY_PERMISSION_REQUEST_CURSOR_POS, - TXT_KEY_PERMISSION_REQUEST_PLUGIN, - TXT_KEY_PERMISSION_REQUEST_KEYBOARD, - TXT_KEY_PERMISSION_UNKNOWN_NAME, - TXT_KEY_PERMISSION_TITLE, - TXT_KEY_PERMISSION_PERSISTENCE_HINT, - TXT_KEY_PERMISSION_ALLOW, - TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, - TXT_KEY_PERMISSION_ALLOW_ONCE, - TXT_KEY_PERMISSION_DENY, - TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, - - TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, - TXT_KEY_NOTIF_NO_GUIUTILS, - TXT_KEY_NOTIF_FAILED_ASSETS, - TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, - TXT_KEY_NOTIF_MONITOR_MODE_FAIL, - TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, - TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, - TXT_KEY_NOTIF_CM_RELOAD_FAILED, - TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, - TXT_KEY_NOTIF_NO_WATCHDOG, - - TXT_KEY_SAFE_MODE_TITLE, - TXT_KEY_SAFE_MODE_DESCRIPTION, - TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, - TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, - TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, - }; - - class CI18nEngine { - public: - CI18nEngine(); - ~CI18nEngine() = default; - - std::string localize(eI18nKeys key, const std::unordered_map& vars = {}); - }; - - SP i18nEngine(); -}; diff --git a/src/includes.hpp b/src/includes.hpp index e33e4e3f..c00fec97 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -17,9 +17,15 @@ #include #include +#ifdef LEGACY_RENDERER +#include +#include +#define GLES2 +#else #define GLES32 #include #include +#endif #ifdef NO_XWAYLAND #define XWAYLAND false diff --git a/src/init/initHelpers.cpp b/src/init/initHelpers.cpp index f3911c04..c27f625c 100644 --- a/src/init/initHelpers.cpp +++ b/src/init/initHelpers.cpp @@ -10,20 +10,20 @@ void NInit::gainRealTime() { struct sched_param param; if (pthread_getschedparam(pthread_self(), &old_policy, ¶m)) { - Log::logger->log(Log::WARN, "Failed to get old pthread scheduling priority"); + Debug::log(WARN, "Failed to get old pthread scheduling priority"); return; } param.sched_priority = minPrio; if (pthread_setschedparam(pthread_self(), SCHED_RR, ¶m)) { - Log::logger->log(Log::WARN, "Failed to change process scheduling strategy"); + Debug::log(WARN, "Failed to change process scheduling strategy"); return; } pthread_atfork(nullptr, nullptr, []() { const struct sched_param param = {.sched_priority = 0}; if (pthread_setschedparam(pthread_self(), SCHED_OTHER, ¶m)) - Log::logger->log(Log::WARN, "Failed to reset process scheduling strategy"); + Debug::log(WARN, "Failed to reset process scheduling strategy"); }); } \ No newline at end of file diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp new file mode 100644 index 00000000..ecd7abea --- /dev/null +++ b/src/layout/DwindleLayout.cpp @@ -0,0 +1,1122 @@ +#include "DwindleLayout.hpp" +#include "../Compositor.hpp" +#include "../config/ConfigValue.hpp" +#include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include "../render/Renderer.hpp" +#include "../managers/input/InputManager.hpp" +#include "../managers/LayoutManager.hpp" +#include "../managers/EventManager.hpp" + +void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverride, bool verticalOverride) { + if (children[0]) { + static auto PSMARTSPLIT = CConfigValue("dwindle:smart_split"); + static auto PPRESERVESPLIT = CConfigValue("dwindle:preserve_split"); + static auto PFLMULT = CConfigValue("dwindle:split_width_multiplier"); + + if (*PPRESERVESPLIT == 0 && *PSMARTSPLIT == 0) + splitTop = box.h * *PFLMULT > box.w; + + if (verticalOverride) + splitTop = true; + else if (horizontalOverride) + splitTop = false; + + const auto SPLITSIDE = !splitTop; + + if (SPLITSIDE) { + // split left/right + const float FIRSTSIZE = box.w / 2.0 * splitRatio; + children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize(); + children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize(); + } else { + // split top/bottom + const float FIRSTSIZE = box.h / 2.0 * splitRatio; + children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize(); + children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize(); + } + + children[0]->recalcSizePosRecursive(force); + children[1]->recalcSizePosRecursive(force); + } else { + layout->applyNodeDataToWindow(this, force); + } +} + +int CHyprDwindleLayout::getNodesOnWorkspace(const WORKSPACEID& id) { + int no = 0; + for (auto const& n : m_lDwindleNodesData) { + if (n.workspaceID == id && n.valid) + ++no; + } + return no; +} + +SDwindleNodeData* CHyprDwindleLayout::getFirstNodeOnWorkspace(const WORKSPACEID& id) { + for (auto& n : m_lDwindleNodesData) { + if (n.workspaceID == id && validMapped(n.pWindow)) + return &n; + } + return nullptr; +} + +SDwindleNodeData* CHyprDwindleLayout::getClosestNodeOnWorkspace(const WORKSPACEID& id, const Vector2D& point) { + SDwindleNodeData* res = nullptr; + double distClosest = -1; + for (auto& n : m_lDwindleNodesData) { + if (n.workspaceID == id && validMapped(n.pWindow)) { + auto distAnother = vecToRectDistanceSquared(point, n.box.pos(), n.box.pos() + n.box.size()); + if (!res || distAnother < distClosest) { + res = &n; + distClosest = distAnother; + } + } + } + return res; +} + +SDwindleNodeData* CHyprDwindleLayout::getNodeFromWindow(PHLWINDOW pWindow) { + for (auto& n : m_lDwindleNodesData) { + if (n.pWindow.lock() == pWindow && !n.isNode) + return &n; + } + + return nullptr; +} + +SDwindleNodeData* CHyprDwindleLayout::getMasterNodeOnWorkspace(const WORKSPACEID& id) { + for (auto& n : m_lDwindleNodesData) { + if (!n.pParent && n.workspaceID == id) + return &n; + } + return nullptr; +} + +void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool force) { + // Don't set nodes, only windows. + if (pNode->isNode) + return; + + PHLMONITOR PMONITOR = nullptr; + + if (g_pCompositor->isWorkspaceSpecial(pNode->workspaceID)) { + for (auto const& m : g_pCompositor->m_vMonitors) { + if (m->activeSpecialWorkspaceID() == pNode->workspaceID) { + PMONITOR = m; + break; + } + } + } else if (const auto WS = g_pCompositor->getWorkspaceByID(pNode->workspaceID); WS) + PMONITOR = WS->m_pMonitor.lock(); + + if (!PMONITOR) { + Debug::log(ERR, "Orphaned Node {}!!", pNode); + return; + } + + // for gaps outer + const bool DISPLAYLEFT = STICKS(pNode->box.x, PMONITOR->vecPosition.x + PMONITOR->vecReservedTopLeft.x); + const bool DISPLAYRIGHT = STICKS(pNode->box.x + pNode->box.w, PMONITOR->vecPosition.x + PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x); + const bool DISPLAYTOP = STICKS(pNode->box.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y); + const bool DISPLAYBOTTOM = STICKS(pNode->box.y + pNode->box.h, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y); + + const auto PWINDOW = pNode->pWindow.lock(); + // get specific gaps and rules for this workspace, + // if user specified them in config + const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(g_pCompositor->getWorkspaceByID(pNode->workspaceID)); + + if (!validMapped(PWINDOW)) { + Debug::log(ERR, "Node {} holding invalid {}!!", pNode, PWINDOW); + onWindowRemovedTiling(PWINDOW); + return; + } + + if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks) + return; + + PWINDOW->unsetWindowData(PRIORITY_LAYOUT); + PWINDOW->updateWindowData(); + + static auto PGAPSINDATA = CConfigValue("general:gaps_in"); + static auto PGAPSOUTDATA = CConfigValue("general:gaps_out"); + auto* const PGAPSIN = (CCssGapData*)(PGAPSINDATA.ptr())->getData(); + auto* const PGAPSOUT = (CCssGapData*)(PGAPSOUTDATA.ptr())->getData(); + + auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN); + auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT); + CBox nodeBox = pNode->box; + nodeBox.round(); + + PWINDOW->m_vSize = nodeBox.size(); + PWINDOW->m_vPosition = nodeBox.pos(); + + PWINDOW->updateWindowDecos(); + + auto calcPos = PWINDOW->m_vPosition; + auto calcSize = PWINDOW->m_vSize; + + const auto OFFSETTOPLEFT = Vector2D((double)(DISPLAYLEFT ? gapsOut.left : gapsIn.left), (double)(DISPLAYTOP ? gapsOut.top : gapsIn.top)); + + const auto OFFSETBOTTOMRIGHT = Vector2D((double)(DISPLAYRIGHT ? gapsOut.right : gapsIn.right), (double)(DISPLAYBOTTOM ? gapsOut.bottom : gapsIn.bottom)); + + calcPos = calcPos + OFFSETTOPLEFT; + calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT; + + if (PWINDOW->m_bIsPseudotiled) { + // Calculate pseudo + float scale = 1; + + // adjust if doesnt fit + if (PWINDOW->m_vPseudoSize.x > calcSize.x || PWINDOW->m_vPseudoSize.y > calcSize.y) { + if (PWINDOW->m_vPseudoSize.x > calcSize.x) { + scale = calcSize.x / PWINDOW->m_vPseudoSize.x; + } + + if (PWINDOW->m_vPseudoSize.y * scale > calcSize.y) { + scale = calcSize.y / PWINDOW->m_vPseudoSize.y; + } + + auto DELTA = calcSize - PWINDOW->m_vPseudoSize * scale; + calcSize = PWINDOW->m_vPseudoSize * scale; + calcPos = calcPos + DELTA / 2.f; // center + } else { + auto DELTA = calcSize - PWINDOW->m_vPseudoSize; + calcPos = calcPos + DELTA / 2.f; // center + calcSize = PWINDOW->m_vPseudoSize; + } + } + + const auto RESERVED = PWINDOW->getFullWindowReservedArea(); + calcPos = calcPos + RESERVED.topLeft; + calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight); + + if (PWINDOW->onSpecialWorkspace() && !PWINDOW->isFullscreen()) { + // if special, we adjust the coords a bit + static auto PSCALEFACTOR = CConfigValue("dwindle:special_scale_factor"); + + CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR}; + wb.round(); // avoid rounding mess + + *PWINDOW->m_vRealPosition = wb.pos(); + *PWINDOW->m_vRealSize = wb.size(); + } else { + CBox wb = {calcPos, calcSize}; + wb.round(); // avoid rounding mess + + *PWINDOW->m_vRealSize = wb.size(); + *PWINDOW->m_vRealPosition = wb.pos(); + } + + if (force) { + g_pHyprRenderer->damageWindow(PWINDOW); + + PWINDOW->m_vRealPosition->warp(); + PWINDOW->m_vRealSize->warp(); + + g_pHyprRenderer->damageWindow(PWINDOW); + } + + PWINDOW->updateWindowDecos(); +} + +void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection direction) { + if (pWindow->m_bIsFloating) + return; + + m_lDwindleNodesData.emplace_back(); + const auto PNODE = &m_lDwindleNodesData.back(); + + const auto PMONITOR = pWindow->m_pMonitor.lock(); + + static auto PUSEACTIVE = CConfigValue("dwindle:use_active_for_splits"); + static auto PDEFAULTSPLIT = CConfigValue("dwindle:default_split_ratio"); + + if (direction != DIRECTION_DEFAULT && overrideDirection == DIRECTION_DEFAULT) + overrideDirection = direction; + + // Populate the node with our window's data + PNODE->workspaceID = pWindow->workspaceID(); + PNODE->pWindow = pWindow; + PNODE->isNode = false; + PNODE->layout = this; + + SDwindleNodeData* OPENINGON; + + const auto MOUSECOORDS = m_vOverrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal()); + const auto MONFROMCURSOR = g_pCompositor->getMonitorFromVector(MOUSECOORDS); + + if (PMONITOR->ID == MONFROMCURSOR->ID && + (PNODE->workspaceID == PMONITOR->activeWorkspaceID() || (g_pCompositor->isWorkspaceSpecial(PNODE->workspaceID) && PMONITOR->activeSpecialWorkspace)) && !*PUSEACTIVE) { + OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS)); + + if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR)) + OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS); + + } else if (*PUSEACTIVE) { + if (g_pCompositor->m_pLastWindow.lock() && !g_pCompositor->m_pLastWindow->m_bIsFloating && g_pCompositor->m_pLastWindow.lock() != pWindow && + g_pCompositor->m_pLastWindow->m_pWorkspace == pWindow->m_pWorkspace && g_pCompositor->m_pLastWindow->m_bIsMapped) { + OPENINGON = getNodeFromWindow(g_pCompositor->m_pLastWindow.lock()); + } else { + OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS)); + } + + if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, PMONITOR)) + OPENINGON = getClosestNodeOnWorkspace(PNODE->workspaceID, MOUSECOORDS); + + } else + OPENINGON = getFirstNodeOnWorkspace(pWindow->workspaceID()); + + Debug::log(LOG, "OPENINGON: {}, Monitor: {}", OPENINGON, PMONITOR->ID); + + if (OPENINGON && OPENINGON->workspaceID != PNODE->workspaceID) { + // special workspace handling + OPENINGON = getFirstNodeOnWorkspace(PNODE->workspaceID); + } + + // first, check if OPENINGON isn't too big. + const auto PREDSIZEMAX = OPENINGON ? Vector2D(OPENINGON->box.w, OPENINGON->box.h) : PMONITOR->vecSize; + if (const auto MAXSIZE = pWindow->requestedMaxSize(); MAXSIZE.x < PREDSIZEMAX.x || MAXSIZE.y < PREDSIZEMAX.y) { + // we can't continue. make it floating. + pWindow->m_bIsFloating = true; + m_lDwindleNodesData.remove(*PNODE); + g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(pWindow); + return; + } + + // last fail-safe to avoid duplicate fullscreens + if ((!OPENINGON || OPENINGON->pWindow.lock() == pWindow) && getNodesOnWorkspace(PNODE->workspaceID) > 1) { + for (auto& node : m_lDwindleNodesData) { + if (node.workspaceID == PNODE->workspaceID && node.pWindow.lock() && node.pWindow.lock() != pWindow) { + OPENINGON = &node; + break; + } + } + } + + // if it's the first, it's easy. Make it fullscreen. + if (!OPENINGON || OPENINGON->pWindow.lock() == pWindow) { + PNODE->box = CBox{PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft, PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight}; + + applyNodeDataToWindow(PNODE); + + return; + } + + // get the node under our cursor + + m_lDwindleNodesData.emplace_back(); + const auto NEWPARENT = &m_lDwindleNodesData.back(); + + // make the parent have the OPENINGON's stats + NEWPARENT->box = OPENINGON->box; + NEWPARENT->workspaceID = OPENINGON->workspaceID; + NEWPARENT->pParent = OPENINGON->pParent; + NEWPARENT->isNode = true; // it is a node + NEWPARENT->splitRatio = std::clamp(*PDEFAULTSPLIT, 0.1f, 1.9f); + + static auto PWIDTHMULTIPLIER = CConfigValue("dwindle:split_width_multiplier"); + + // if cursor over first child, make it first, etc + const auto SIDEBYSIDE = NEWPARENT->box.w > NEWPARENT->box.h * *PWIDTHMULTIPLIER; + NEWPARENT->splitTop = !SIDEBYSIDE; + + static auto PFORCESPLIT = CConfigValue("dwindle:force_split"); + static auto PERMANENTDIRECTIONOVERRIDE = CConfigValue("dwindle:permanent_direction_override"); + static auto PSMARTSPLIT = CConfigValue("dwindle:smart_split"); + + bool horizontalOverride = false; + bool verticalOverride = false; + + // let user select position -> top, right, bottom, left + if (overrideDirection != DIRECTION_DEFAULT) { + + // this is horizontal + if (overrideDirection % 2 == 0) + verticalOverride = true; + else + horizontalOverride = true; + + // 0 -> top and left | 1,2 -> right and bottom + if (overrideDirection % 3 == 0) { + NEWPARENT->children[1] = OPENINGON; + NEWPARENT->children[0] = PNODE; + } else { + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } + + // whether or not the override persists after opening one window + if (*PERMANENTDIRECTIONOVERRIDE == 0) + overrideDirection = DIRECTION_DEFAULT; + } else if (*PSMARTSPLIT == 1) { + const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2; + const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w; + const auto DELTA = MOUSECOORDS - PARENT_CENTER; + const auto DELTA_SLOPE = DELTA.y / DELTA.x; + + if (abs(DELTA_SLOPE) < PARENT_PROPORTIONS) { + if (DELTA.x > 0) { + // right + NEWPARENT->splitTop = false; + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } else { + // left + NEWPARENT->splitTop = false; + NEWPARENT->children[0] = PNODE; + NEWPARENT->children[1] = OPENINGON; + } + } else { + if (DELTA.y > 0) { + // bottom + NEWPARENT->splitTop = true; + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } else { + // top + NEWPARENT->splitTop = true; + NEWPARENT->children[0] = PNODE; + NEWPARENT->children[1] = OPENINGON; + } + } + } else if (*PFORCESPLIT == 0 || !pWindow->m_bFirstMap) { + if ((SIDEBYSIDE && + VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) || + (!SIDEBYSIDE && + VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) { + // we are hovering over the first node, make PNODE first. + NEWPARENT->children[1] = OPENINGON; + NEWPARENT->children[0] = PNODE; + } else { + // we are hovering over the second node, make PNODE second. + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } + } else { + if (*PFORCESPLIT == 1) { + NEWPARENT->children[1] = OPENINGON; + NEWPARENT->children[0] = PNODE; + } else { + NEWPARENT->children[0] = OPENINGON; + NEWPARENT->children[1] = PNODE; + } + } + + // split in favor of a specific window + const auto first = NEWPARENT->children[0]; + static auto PSPLITBIAS = CConfigValue("dwindle:split_bias"); + if ((*PSPLITBIAS == 1 && first == PNODE) || (*PSPLITBIAS == 2 && first == OPENINGON)) + NEWPARENT->splitRatio = 2.f - NEWPARENT->splitRatio; + + // and update the previous parent if it exists + if (OPENINGON->pParent) { + if (OPENINGON->pParent->children[0] == OPENINGON) { + OPENINGON->pParent->children[0] = NEWPARENT; + } else { + OPENINGON->pParent->children[1] = NEWPARENT; + } + } + + // Update the children + if (!verticalOverride && (NEWPARENT->box.w * *PWIDTHMULTIPLIER > NEWPARENT->box.h || horizontalOverride)) { + // split left/right -> forced + OPENINGON->box = {NEWPARENT->box.pos(), Vector2D(NEWPARENT->box.w / 2.f, NEWPARENT->box.h)}; + PNODE->box = {Vector2D(NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y), Vector2D(NEWPARENT->box.w / 2.f, NEWPARENT->box.h)}; + } else { + // split top/bottom + OPENINGON->box = {NEWPARENT->box.pos(), Vector2D(NEWPARENT->box.w, NEWPARENT->box.h / 2.f)}; + PNODE->box = {Vector2D(NEWPARENT->box.x, NEWPARENT->box.y + NEWPARENT->box.h / 2.f), Vector2D(NEWPARENT->box.w, NEWPARENT->box.h / 2.f)}; + } + + OPENINGON->pParent = NEWPARENT; + PNODE->pParent = NEWPARENT; + + NEWPARENT->recalcSizePosRecursive(false, horizontalOverride, verticalOverride); + + recalculateMonitor(pWindow->monitorID()); +} + +void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) { + + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE) { + Debug::log(ERR, "onWindowRemovedTiling node null?"); + return; + } + + pWindow->unsetWindowData(PRIORITY_LAYOUT); + pWindow->updateWindowData(); + + if (pWindow->isFullscreen()) + g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE); + + const auto PPARENT = PNODE->pParent; + + if (!PPARENT) { + Debug::log(LOG, "Removing last node (dwindle)"); + m_lDwindleNodesData.remove(*PNODE); + return; + } + + const auto PSIBLING = PPARENT->children[0] == PNODE ? PPARENT->children[1] : PPARENT->children[0]; + + PSIBLING->box = PPARENT->box; + PSIBLING->pParent = PPARENT->pParent; + + if (PPARENT->pParent != nullptr) { + if (PPARENT->pParent->children[0] == PPARENT) { + PPARENT->pParent->children[0] = PSIBLING; + } else { + PPARENT->pParent->children[1] = PSIBLING; + } + } + + PPARENT->valid = false; + PNODE->valid = false; + + if (PSIBLING->pParent) + PSIBLING->pParent->recalcSizePosRecursive(); + else + PSIBLING->recalcSizePosRecursive(); + + m_lDwindleNodesData.remove(*PPARENT); + m_lDwindleNodesData.remove(*PNODE); +} + +void CHyprDwindleLayout::recalculateMonitor(const MONITORID& monid) { + const auto PMONITOR = g_pCompositor->getMonitorFromID(monid); + + if (!PMONITOR || !PMONITOR->activeWorkspace) + return; // ??? + + g_pHyprRenderer->damageMonitor(PMONITOR); + + if (PMONITOR->activeSpecialWorkspace) + calculateWorkspace(PMONITOR->activeSpecialWorkspace); + + calculateWorkspace(PMONITOR->activeWorkspace); +} + +void CHyprDwindleLayout::calculateWorkspace(const PHLWORKSPACE& pWorkspace) { + const auto PMONITOR = pWorkspace->m_pMonitor.lock(); + + if (!PMONITOR) + return; + + if (pWorkspace->m_bHasFullscreenWindow) { + // massive hack from the fullscreen func + const auto PFULLWINDOW = pWorkspace->getFullscreenWindow(); + + if (pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) { + *PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition; + *PFULLWINDOW->m_vRealSize = PMONITOR->vecSize; + } else if (pWorkspace->m_efFullscreenMode == FSMODE_MAXIMIZED) { + SDwindleNodeData fakeNode; + fakeNode.pWindow = PFULLWINDOW; + fakeNode.box = {PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft, PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight}; + fakeNode.workspaceID = pWorkspace->m_iID; + PFULLWINDOW->m_vPosition = fakeNode.box.pos(); + PFULLWINDOW->m_vSize = fakeNode.box.size(); + fakeNode.ignoreFullscreenChecks = true; + + applyNodeDataToWindow(&fakeNode); + } + + // if has fullscreen, don't calculate the rest + return; + } + + const auto TOPNODE = getMasterNodeOnWorkspace(pWorkspace->m_iID); + + if (TOPNODE) { + TOPNODE->box = {PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft, PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight}; + TOPNODE->recalcSizePosRecursive(); + } +} + +bool CHyprDwindleLayout::isWindowTiled(PHLWINDOW pWindow) { + return getNodeFromWindow(pWindow) != nullptr; +} + +void CHyprDwindleLayout::onBeginDragWindow() { + m_PseudoDragFlags.started = false; + m_PseudoDragFlags.pseudo = false; + IHyprLayout::onBeginDragWindow(); +} + +void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorner corner, PHLWINDOW pWindow) { + + const auto PWINDOW = pWindow ? pWindow : g_pCompositor->m_pLastWindow.lock(); + + if (!validMapped(PWINDOW)) + return; + + const auto PNODE = getNodeFromWindow(PWINDOW); + + if (!PNODE) { + *PWINDOW->m_vRealSize = + (PWINDOW->m_vRealSize->goal() + pixResize) + .clamp(PWINDOW->m_sWindowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), PWINDOW->m_sWindowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY})); + PWINDOW->updateWindowDecos(); + return; + } + + static auto PANIMATE = CConfigValue("misc:animate_manual_resizes"); + static auto PSMARTRESIZING = CConfigValue("dwindle:smart_resizing"); + + // get some data about our window + const auto PMONITOR = PWINDOW->m_pMonitor.lock(); + const bool DISPLAYLEFT = STICKS(PWINDOW->m_vPosition.x, PMONITOR->vecPosition.x + PMONITOR->vecReservedTopLeft.x); + const bool DISPLAYRIGHT = STICKS(PWINDOW->m_vPosition.x + PWINDOW->m_vSize.x, PMONITOR->vecPosition.x + PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x); + const bool DISPLAYTOP = STICKS(PWINDOW->m_vPosition.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y); + const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_vPosition.y + PWINDOW->m_vSize.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y); + + if (PWINDOW->m_bIsPseudotiled) { + if (!m_PseudoDragFlags.started) { + m_PseudoDragFlags.started = true; + + const auto pseudoSize = PWINDOW->m_vRealSize->goal(); + const auto mouseOffset = g_pInputManager->getMouseCoordsInternal() - (PNODE->box.pos() + ((PNODE->box.size() / 2) - (pseudoSize / 2))); + + if (mouseOffset.x > 0 && mouseOffset.x < pseudoSize.x && mouseOffset.y > 0 && mouseOffset.y < pseudoSize.y) { + m_PseudoDragFlags.pseudo = true; + m_PseudoDragFlags.xExtent = mouseOffset.x > pseudoSize.x / 2; + m_PseudoDragFlags.yExtent = mouseOffset.y > pseudoSize.y / 2; + + PWINDOW->m_vPseudoSize = pseudoSize; + } else { + m_PseudoDragFlags.pseudo = false; + } + } + + if (m_PseudoDragFlags.pseudo) { + if (m_PseudoDragFlags.xExtent) + PWINDOW->m_vPseudoSize.x += pixResize.x * 2; + else + PWINDOW->m_vPseudoSize.x -= pixResize.x * 2; + if (m_PseudoDragFlags.yExtent) + PWINDOW->m_vPseudoSize.y += pixResize.y * 2; + else + PWINDOW->m_vPseudoSize.y -= pixResize.y * 2; + + CBox wbox = PNODE->box; + wbox.round(); + + PWINDOW->m_vPseudoSize = {std::clamp(PWINDOW->m_vPseudoSize.x, 30.0, wbox.w), std::clamp(PWINDOW->m_vPseudoSize.y, 30.0, wbox.h)}; + + PWINDOW->m_vLastFloatingSize = PWINDOW->m_vPseudoSize; + PNODE->recalcSizePosRecursive(*PANIMATE == 0); + + return; + } + } + + // construct allowed movement + Vector2D allowedMovement = pixResize; + if (DISPLAYLEFT && DISPLAYRIGHT) + allowedMovement.x = 0; + + if (DISPLAYBOTTOM && DISPLAYTOP) + allowedMovement.y = 0; + + if (*PSMARTRESIZING == 1) { + // Identify inner and outer nodes for both directions + SDwindleNodeData* PVOUTER = nullptr; + SDwindleNodeData* PVINNER = nullptr; + SDwindleNodeData* PHOUTER = nullptr; + SDwindleNodeData* PHINNER = nullptr; + + const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || DISPLAYRIGHT; + const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || DISPLAYBOTTOM; + const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || DISPLAYLEFT; + const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || DISPLAYTOP; + const auto NONE = corner == CORNER_NONE; + + for (auto PCURRENT = PNODE; PCURRENT && PCURRENT->pParent; PCURRENT = PCURRENT->pParent) { + const auto PPARENT = PCURRENT->pParent; + + if (!PVOUTER && PPARENT->splitTop && (NONE || (TOP && PPARENT->children[1] == PCURRENT) || (BOTTOM && PPARENT->children[0] == PCURRENT))) + PVOUTER = PCURRENT; + else if (!PVOUTER && !PVINNER && PPARENT->splitTop) + PVINNER = PCURRENT; + else if (!PHOUTER && !PPARENT->splitTop && (NONE || (LEFT && PPARENT->children[1] == PCURRENT) || (RIGHT && PPARENT->children[0] == PCURRENT))) + PHOUTER = PCURRENT; + else if (!PHOUTER && !PHINNER && !PPARENT->splitTop) + PHINNER = PCURRENT; + + if (PVOUTER && PHOUTER) + break; + } + + if (PHOUTER) { + PHOUTER->pParent->splitRatio = std::clamp(PHOUTER->pParent->splitRatio + allowedMovement.x * 2.f / PHOUTER->pParent->box.w, 0.1, 1.9); + + if (PHINNER) { + const auto ORIGINAL = PHINNER->box.w; + PHOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + if (PHINNER->pParent->children[0] == PHINNER) + PHINNER->pParent->splitRatio = std::clamp((ORIGINAL - allowedMovement.x) / PHINNER->pParent->box.w * 2.f, 0.1, 1.9); + else + PHINNER->pParent->splitRatio = std::clamp(2 - (ORIGINAL + allowedMovement.x) / PHINNER->pParent->box.w * 2.f, 0.1, 1.9); + PHINNER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + } else + PHOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + } + + if (PVOUTER) { + PVOUTER->pParent->splitRatio = std::clamp(PVOUTER->pParent->splitRatio + allowedMovement.y * 2.f / PVOUTER->pParent->box.h, 0.1, 1.9); + + if (PVINNER) { + const auto ORIGINAL = PVINNER->box.h; + PVOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + if (PVINNER->pParent->children[0] == PVINNER) + PVINNER->pParent->splitRatio = std::clamp((ORIGINAL - allowedMovement.y) / PVINNER->pParent->box.h * 2.f, 0.1, 1.9); + else + PVINNER->pParent->splitRatio = std::clamp(2 - (ORIGINAL + allowedMovement.y) / PVINNER->pParent->box.h * 2.f, 0.1, 1.9); + PVINNER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + } else + PVOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); + } + } else { + // get the correct containers to apply splitratio to + const auto PPARENT = PNODE->pParent; + + if (!PPARENT) + return; // the only window on a workspace, ignore + + const bool PARENTSIDEBYSIDE = !PPARENT->splitTop; + + // Get the parent's parent + auto PPARENT2 = PPARENT->pParent; + + // No parent means we have only 2 windows, and thus one axis of freedom + if (!PPARENT2) { + if (PARENTSIDEBYSIDE) { + allowedMovement.x *= 2.f / PPARENT->box.w; + PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.x, 0.1, 1.9); + PPARENT->recalcSizePosRecursive(*PANIMATE == 0); + } else { + allowedMovement.y *= 2.f / PPARENT->box.h; + PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.y, 0.1, 1.9); + PPARENT->recalcSizePosRecursive(*PANIMATE == 0); + } + + return; + } + + // Get first parent with other split + while (PPARENT2 && PPARENT2->splitTop == !PARENTSIDEBYSIDE) + PPARENT2 = PPARENT2->pParent; + + // no parent, one axis of freedom + if (!PPARENT2) { + if (PARENTSIDEBYSIDE) { + allowedMovement.x *= 2.f / PPARENT->box.w; + PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.x, 0.1, 1.9); + PPARENT->recalcSizePosRecursive(*PANIMATE == 0); + } else { + allowedMovement.y *= 2.f / PPARENT->box.h; + PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.y, 0.1, 1.9); + PPARENT->recalcSizePosRecursive(*PANIMATE == 0); + } + + return; + } + + // 2 axes of freedom + const auto SIDECONTAINER = PARENTSIDEBYSIDE ? PPARENT : PPARENT2; + const auto TOPCONTAINER = PARENTSIDEBYSIDE ? PPARENT2 : PPARENT; + + allowedMovement.x *= 2.f / SIDECONTAINER->box.w; + allowedMovement.y *= 2.f / TOPCONTAINER->box.h; + + SIDECONTAINER->splitRatio = std::clamp(SIDECONTAINER->splitRatio + allowedMovement.x, 0.1, 1.9); + TOPCONTAINER->splitRatio = std::clamp(TOPCONTAINER->splitRatio + allowedMovement.y, 0.1, 1.9); + SIDECONTAINER->recalcSizePosRecursive(*PANIMATE == 0); + TOPCONTAINER->recalcSizePosRecursive(*PANIMATE == 0); + } +} + +void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) { + const auto PMONITOR = pWindow->m_pMonitor.lock(); + const auto PWORKSPACE = pWindow->m_pWorkspace; + + // save position and size if floating + if (pWindow->m_bIsFloating && CURRENT_EFFECTIVE_MODE == FSMODE_NONE) { + pWindow->m_vLastFloatingSize = pWindow->m_vRealSize->goal(); + pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition->goal(); + pWindow->m_vPosition = pWindow->m_vRealPosition->goal(); + pWindow->m_vSize = pWindow->m_vRealSize->goal(); + } + + if (EFFECTIVE_MODE == FSMODE_NONE) { + // if it got its fullscreen disabled, set back its node if it had one + const auto PNODE = getNodeFromWindow(pWindow); + if (PNODE) + applyNodeDataToWindow(PNODE); + else { + // get back its' dimensions from position and size + *pWindow->m_vRealPosition = pWindow->m_vLastFloatingPosition; + *pWindow->m_vRealSize = pWindow->m_vLastFloatingSize; + + pWindow->unsetWindowData(PRIORITY_LAYOUT); + pWindow->updateWindowData(); + } + } else { + // apply new pos and size being monitors' box + if (EFFECTIVE_MODE == FSMODE_FULLSCREEN) { + *pWindow->m_vRealPosition = PMONITOR->vecPosition; + *pWindow->m_vRealSize = PMONITOR->vecSize; + } else { + // This is a massive hack. + // We make a fake "only" node and apply + // To keep consistent with the settings without C+P code + + SDwindleNodeData fakeNode; + fakeNode.pWindow = pWindow; + fakeNode.box = {PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft, PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight}; + fakeNode.workspaceID = pWindow->workspaceID(); + pWindow->m_vPosition = fakeNode.box.pos(); + pWindow->m_vSize = fakeNode.box.size(); + fakeNode.ignoreFullscreenChecks = true; + + applyNodeDataToWindow(&fakeNode); + } + } + + g_pCompositor->changeWindowZOrder(pWindow, true); +} + +void CHyprDwindleLayout::recalculateWindow(PHLWINDOW pWindow) { + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE) + return; + + PNODE->recalcSizePosRecursive(); +} + +SWindowRenderLayoutHints CHyprDwindleLayout::requestRenderHints(PHLWINDOW pWindow) { + // window should be valid, insallah + SWindowRenderLayoutHints hints; + + const auto PNODE = getNodeFromWindow(pWindow); + if (!PNODE) + return hints; // left for the future, maybe floating funkiness + + return hints; +} + +void CHyprDwindleLayout::moveWindowTo(PHLWINDOW pWindow, const std::string& dir, bool silent) { + if (!isDirection(dir)) + return; + + const auto PNODE = getNodeFromWindow(pWindow); + const auto originalWorkspaceID = pWindow->workspaceID(); + const Vector2D originalPos = pWindow->middle(); + + if (!PNODE) + return; + + Vector2D focalPoint; + + switch (dir[0]) { + case 't': + case 'u': focalPoint = pWindow->m_vPosition + Vector2D{pWindow->m_vSize.x / 2.0, -1.0}; break; + case 'd': + case 'b': focalPoint = pWindow->m_vPosition + Vector2D{pWindow->m_vSize.x / 2.0, pWindow->m_vSize.y + 1.0}; break; + case 'l': focalPoint = pWindow->m_vPosition + Vector2D{-1.0, pWindow->m_vSize.y / 2.0}; break; + case 'r': focalPoint = pWindow->m_vPosition + Vector2D{pWindow->m_vSize.x + 1.0, pWindow->m_vSize.y / 2.0}; break; + default: UNREACHABLE(); + } + + pWindow->setAnimationsToMove(); + + onWindowRemovedTiling(pWindow); + + m_vOverrideFocalPoint = focalPoint; + + const auto PMONITORFOCAL = g_pCompositor->getMonitorFromVector(focalPoint); + + if (PMONITORFOCAL != pWindow->m_pMonitor) { + pWindow->moveToWorkspace(PMONITORFOCAL->activeWorkspace); + pWindow->m_pMonitor = PMONITORFOCAL; + } + + pWindow->updateGroupOutputs(); + if (!pWindow->m_sGroupData.pNextWindow.expired()) { + PHLWINDOW next = pWindow->m_sGroupData.pNextWindow.lock(); + while (next != pWindow) { + next->updateToplevel(); + next = next->m_sGroupData.pNextWindow.lock(); + } + } + + onWindowCreatedTiling(pWindow); + + m_vOverrideFocalPoint.reset(); + + // restore focus to the previous position + if (silent) { + const auto PNODETOFOCUS = getClosestNodeOnWorkspace(originalWorkspaceID, originalPos); + if (PNODETOFOCUS && PNODETOFOCUS->pWindow.lock()) + g_pCompositor->focusWindow(PNODETOFOCUS->pWindow.lock()); + } +} + +void CHyprDwindleLayout::switchWindows(PHLWINDOW pWindow, PHLWINDOW pWindow2) { + // windows should be valid, insallah + + auto PNODE = getNodeFromWindow(pWindow); + auto PNODE2 = getNodeFromWindow(pWindow2); + + if (!PNODE2 || !PNODE) + return; + + const eFullscreenMode MODE1 = pWindow->m_sFullscreenState.internal; + const eFullscreenMode MODE2 = pWindow2->m_sFullscreenState.internal; + + g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE); + g_pCompositor->setWindowFullscreenInternal(pWindow2, FSMODE_NONE); + + SDwindleNodeData* ACTIVE1 = nullptr; + SDwindleNodeData* ACTIVE2 = nullptr; + + // swap the windows and recalc + PNODE2->pWindow = pWindow; + PNODE->pWindow = pWindow2; + + if (PNODE->workspaceID != PNODE2->workspaceID) { + std::swap(pWindow2->m_pMonitor, pWindow->m_pMonitor); + std::swap(pWindow2->m_pWorkspace, pWindow->m_pWorkspace); + } + + pWindow->setAnimationsToMove(); + pWindow2->setAnimationsToMove(); + + // recalc the workspace + getMasterNodeOnWorkspace(PNODE->workspaceID)->recalcSizePosRecursive(); + + if (PNODE2->workspaceID != PNODE->workspaceID) + getMasterNodeOnWorkspace(PNODE2->workspaceID)->recalcSizePosRecursive(); + + if (ACTIVE1) { + ACTIVE1->box = PNODE->box; + ACTIVE1->pWindow->m_vPosition = ACTIVE1->box.pos(); + ACTIVE1->pWindow->m_vSize = ACTIVE1->box.size(); + } + + if (ACTIVE2) { + ACTIVE2->box = PNODE2->box; + ACTIVE2->pWindow->m_vPosition = ACTIVE2->box.pos(); + ACTIVE2->pWindow->m_vSize = ACTIVE2->box.size(); + } + + g_pHyprRenderer->damageWindow(pWindow); + g_pHyprRenderer->damageWindow(pWindow2); + + g_pCompositor->setWindowFullscreenInternal(pWindow2, MODE1); + g_pCompositor->setWindowFullscreenInternal(pWindow, MODE2); +} + +void CHyprDwindleLayout::alterSplitRatio(PHLWINDOW pWindow, float ratio, bool exact) { + // window should be valid, insallah + + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE || !PNODE->pParent) + return; + + float newRatio = exact ? ratio : PNODE->pParent->splitRatio + ratio; + PNODE->pParent->splitRatio = std::clamp(newRatio, 0.1f, 1.9f); + + PNODE->pParent->recalcSizePosRecursive(); +} + +std::any CHyprDwindleLayout::layoutMessage(SLayoutMessageHeader header, std::string message) { + const auto ARGS = CVarList(message, 0, ' '); + if (ARGS[0] == "togglesplit") { + toggleSplit(header.pWindow); + } else if (ARGS[0] == "swapsplit") { + swapSplit(header.pWindow); + } else if (ARGS[0] == "movetoroot") { + const auto WINDOW = ARGS[1].empty() ? header.pWindow : g_pCompositor->getWindowByRegex(ARGS[1]); + const auto STABLE = ARGS[2].empty() || ARGS[2] != "unstable"; + moveToRoot(WINDOW, STABLE); + } else if (ARGS[0] == "preselect") { + std::string direction = ARGS[1]; + + if (direction.empty()) { + Debug::log(ERR, "Expected direction for preselect"); + return ""; + } + + switch (direction.front()) { + case 'u': + case 't': { + overrideDirection = DIRECTION_UP; + break; + } + case 'd': + case 'b': { + overrideDirection = DIRECTION_DOWN; + break; + } + case 'r': { + overrideDirection = DIRECTION_RIGHT; + break; + } + case 'l': { + overrideDirection = DIRECTION_LEFT; + break; + } + default: { + // any other character resets the focus direction + // needed for the persistent mode + overrideDirection = DIRECTION_DEFAULT; + break; + } + } + } + + return ""; +} + +void CHyprDwindleLayout::toggleSplit(PHLWINDOW pWindow) { + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE || !PNODE->pParent) + return; + + if (pWindow->isFullscreen()) + return; + + PNODE->pParent->splitTop = !PNODE->pParent->splitTop; + + PNODE->pParent->recalcSizePosRecursive(); +} + +void CHyprDwindleLayout::swapSplit(PHLWINDOW pWindow) { + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE || !PNODE->pParent) + return; + + if (pWindow->isFullscreen()) + return; + + std::swap(PNODE->pParent->children[0], PNODE->pParent->children[1]); + + PNODE->pParent->recalcSizePosRecursive(); +} + +// goal: maximize the chosen window within current dwindle layout +// impl: swap the selected window with the other sub-tree below root +void CHyprDwindleLayout::moveToRoot(PHLWINDOW pWindow, bool stable) { + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE || !PNODE->pParent) + return; + + if (pWindow->isFullscreen()) + return; + + // already at root + if (!PNODE->pParent->pParent) + return; + + auto& pNode = PNODE->pParent->children[0] == PNODE ? PNODE->pParent->children[0] : PNODE->pParent->children[1]; + + // instead of [getMasterNodeOnWorkspace], we walk back to root since we need + // to know which children of root is our ancestor + auto pAncestor = PNODE, pRoot = PNODE->pParent; + while (pRoot->pParent) { + pAncestor = pRoot; + pRoot = pRoot->pParent; + } + + auto& pSwap = pRoot->children[0] == pAncestor ? pRoot->children[1] : pRoot->children[0]; + std::swap(pNode, pSwap); + std::swap(pNode->pParent, pSwap->pParent); + + // [stable] in that the focused window occupies same side of screen + if (stable) + std::swap(pRoot->children[0], pRoot->children[1]); + + // if the workspace is visible, recalculate layout + if (pWindow->m_pWorkspace && pWindow->m_pWorkspace->isVisible()) + pRoot->recalcSizePosRecursive(); +} + +void CHyprDwindleLayout::replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) { + const auto PNODE = getNodeFromWindow(from); + + if (!PNODE) + return; + + PNODE->pWindow = to; + + applyNodeDataToWindow(PNODE, true); +} + +std::string CHyprDwindleLayout::getLayoutName() { + return "dwindle"; +} + +void CHyprDwindleLayout::onEnable() { + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_bIsFloating || !w->m_bIsMapped || w->isHidden()) + continue; + + onWindowCreatedTiling(w); + } +} + +void CHyprDwindleLayout::onDisable() { + m_lDwindleNodesData.clear(); +} + +Vector2D CHyprDwindleLayout::predictSizeForNewWindowTiled() { + if (!g_pCompositor->m_pLastMonitor) + return {}; + + // get window candidate + PHLWINDOW candidate = g_pCompositor->m_pLastWindow.lock(); + + if (!candidate) + candidate = g_pCompositor->m_pLastMonitor->activeWorkspace->getFirstWindow(); + + // create a fake node + SDwindleNodeData node; + + if (!candidate) + return g_pCompositor->m_pLastMonitor->vecSize; + else { + const auto PNODE = getNodeFromWindow(candidate); + + if (!PNODE) + return {}; + + node = *PNODE; + node.pWindow.reset(); + + CBox box = PNODE->box; + + static auto PFLMULT = CConfigValue("dwindle:split_width_multiplier"); + + bool splitTop = box.h * *PFLMULT > box.w; + + const auto SPLITSIDE = !splitTop; + + if (SPLITSIDE) + node.box = {{}, {box.w / 2.0, box.h}}; + else + node.box = {{}, {box.w, box.h / 2.0}}; + + // TODO: make this better and more accurate + + return node.box.size(); + } + + return {}; +} diff --git a/src/layout/DwindleLayout.hpp b/src/layout/DwindleLayout.hpp new file mode 100644 index 00000000..be47e724 --- /dev/null +++ b/src/layout/DwindleLayout.hpp @@ -0,0 +1,108 @@ +#pragma once + +#include "IHyprLayout.hpp" +#include "../desktop/DesktopTypes.hpp" + +#include +#include +#include +#include +#include + +class CHyprDwindleLayout; +enum eFullscreenMode : int8_t; + +struct SDwindleNodeData { + SDwindleNodeData* pParent = nullptr; + bool isNode = false; + + PHLWINDOWREF pWindow; + + std::array children = {nullptr, nullptr}; + + bool splitTop = false; // for preserve_split + + CBox box = {0}; + + WORKSPACEID workspaceID = WORKSPACE_INVALID; + + float splitRatio = 1.f; + + bool valid = true; + + bool ignoreFullscreenChecks = false; + + // For list lookup + bool operator==(const SDwindleNodeData& rhs) const { + return pWindow.lock() == rhs.pWindow.lock() && workspaceID == rhs.workspaceID && box == rhs.box && pParent == rhs.pParent && children[0] == rhs.children[0] && + children[1] == rhs.children[1]; + } + + void recalcSizePosRecursive(bool force = false, bool horizontalOverride = false, bool verticalOverride = false); + CHyprDwindleLayout* layout = nullptr; +}; + +class CHyprDwindleLayout : public IHyprLayout { + public: + virtual void onWindowCreatedTiling(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT); + virtual void onWindowRemovedTiling(PHLWINDOW); + virtual bool isWindowTiled(PHLWINDOW); + virtual void recalculateMonitor(const MONITORID&); + virtual void recalculateWindow(PHLWINDOW); + virtual void onBeginDragWindow(); + virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr); + virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE); + virtual std::any layoutMessage(SLayoutMessageHeader, std::string); + virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW); + virtual void switchWindows(PHLWINDOW, PHLWINDOW); + virtual void moveWindowTo(PHLWINDOW, const std::string& dir, bool silent); + virtual void alterSplitRatio(PHLWINDOW, float, bool); + virtual std::string getLayoutName(); + virtual void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to); + virtual Vector2D predictSizeForNewWindowTiled(); + + virtual void onEnable(); + virtual void onDisable(); + + private: + std::list m_lDwindleNodesData; + + struct { + bool started = false; + bool pseudo = false; + bool xExtent = false; + bool yExtent = false; + } m_PseudoDragFlags; + + std::optional m_vOverrideFocalPoint; // for onWindowCreatedTiling. + + int getNodesOnWorkspace(const WORKSPACEID&); + void applyNodeDataToWindow(SDwindleNodeData*, bool force = false); + void calculateWorkspace(const PHLWORKSPACE& pWorkspace); + SDwindleNodeData* getNodeFromWindow(PHLWINDOW); + SDwindleNodeData* getFirstNodeOnWorkspace(const WORKSPACEID&); + SDwindleNodeData* getClosestNodeOnWorkspace(const WORKSPACEID&, const Vector2D&); + SDwindleNodeData* getMasterNodeOnWorkspace(const WORKSPACEID&); + + void toggleSplit(PHLWINDOW); + void swapSplit(PHLWINDOW); + void moveToRoot(PHLWINDOW, bool stable = true); + + eDirection overrideDirection = DIRECTION_DEFAULT; + + friend struct SDwindleNodeData; +}; + +template +struct std::formatter : std::formatter { + template + auto format(const SDwindleNodeData* const& node, FormatContext& ctx) const { + auto out = ctx.out(); + if (!node) + return std::format_to(out, "[Node nullptr]"); + std::format_to(out, "[Node {:x}: workspace: {}, pos: {:j2}, size: {:j2}", (uintptr_t)node, node->workspaceID, node->box.pos(), node->box.size()); + if (!node->isNode && !node->pWindow.expired()) + std::format_to(out, ", window: {:x}", node->pWindow.lock()); + return std::format_to(out, "]"); + } +}; diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp new file mode 100644 index 00000000..dc6de9e3 --- /dev/null +++ b/src/layout/IHyprLayout.cpp @@ -0,0 +1,954 @@ +#include "IHyprLayout.hpp" +#include "../defines.hpp" +#include "../Compositor.hpp" +#include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include "../config/ConfigValue.hpp" +#include "../desktop/Window.hpp" +#include "../protocols/XDGShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../xwayland/XSurface.hpp" +#include "../render/Renderer.hpp" +#include "../managers/input/InputManager.hpp" +#include "../managers/LayoutManager.hpp" +#include "../managers/EventManager.hpp" +#include "../managers/HookSystemManager.hpp" + +void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) { + CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(pWindow); + + const bool HASPERSISTENTSIZE = + std::any_of(pWindow->m_vMatchedRules.begin(), pWindow->m_vMatchedRules.end(), [](const auto& rule) { return rule->ruleType == CWindowRule::RULE_PERSISTENTSIZE; }); + + const auto STOREDSIZE = HASPERSISTENTSIZE ? g_pConfigManager->getStoredFloatingSize(pWindow) : std::nullopt; + + if (STOREDSIZE.has_value()) { + Debug::log(LOG, "using stored size {}x{} for new window {}::{}", STOREDSIZE->x, STOREDSIZE->y, pWindow->m_szClass, pWindow->m_szTitle); + pWindow->m_vLastFloatingSize = STOREDSIZE.value(); + } else if (desiredGeometry.width <= 5 || desiredGeometry.height <= 5) { + const auto PMONITOR = pWindow->m_pMonitor.lock(); + pWindow->m_vLastFloatingSize = PMONITOR->vecSize / 2.f; + } else + pWindow->m_vLastFloatingSize = Vector2D(desiredGeometry.width, desiredGeometry.height); + + pWindow->m_vPseudoSize = pWindow->m_vLastFloatingSize; + + bool autoGrouped = IHyprLayout::onWindowCreatedAutoGroup(pWindow); + if (autoGrouped) + return; + + if (pWindow->m_bIsFloating) + onWindowCreatedFloating(pWindow); + else + onWindowCreatedTiling(pWindow, direction); + + if (!g_pXWaylandManager->shouldBeFloated(pWindow)) // do not apply group rules to child windows + pWindow->applyGroupRules(); +} + +void IHyprLayout::onWindowRemoved(PHLWINDOW pWindow) { + if (pWindow->isFullscreen()) + g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE); + + if (!pWindow->m_sGroupData.pNextWindow.expired()) { + if (pWindow->m_sGroupData.pNextWindow.lock() == pWindow) + pWindow->m_sGroupData.pNextWindow.reset(); + else { + // find last window and update + PHLWINDOW PWINDOWPREV = pWindow->getGroupPrevious(); + const auto WINDOWISVISIBLE = pWindow->getGroupCurrent() == pWindow; + + if (WINDOWISVISIBLE) + PWINDOWPREV->setGroupCurrent(pWindow->m_sGroupData.head ? pWindow->m_sGroupData.pNextWindow.lock() : PWINDOWPREV); + + PWINDOWPREV->m_sGroupData.pNextWindow = pWindow->m_sGroupData.pNextWindow; + + pWindow->m_sGroupData.pNextWindow.reset(); + + if (pWindow->m_sGroupData.head) { + std::swap(PWINDOWPREV->m_sGroupData.pNextWindow->m_sGroupData.head, pWindow->m_sGroupData.head); + std::swap(PWINDOWPREV->m_sGroupData.pNextWindow->m_sGroupData.locked, pWindow->m_sGroupData.locked); + } + + if (pWindow == m_pLastTiledWindow) + m_pLastTiledWindow.reset(); + + pWindow->setHidden(false); + + pWindow->updateWindowDecos(); + PWINDOWPREV->getGroupCurrent()->updateWindowDecos(); + g_pCompositor->updateWindowAnimatedDecorationValues(pWindow); + + return; + } + } + + if (pWindow->m_bIsFloating) { + onWindowRemovedFloating(pWindow); + } else { + onWindowRemovedTiling(pWindow); + } + + if (pWindow == m_pLastTiledWindow) + m_pLastTiledWindow.reset(); +} + +void IHyprLayout::onWindowRemovedFloating(PHLWINDOW pWindow) { + ; // no-op +} + +void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) { + + CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(pWindow); + const auto PMONITOR = pWindow->m_pMonitor.lock(); + + if (pWindow->m_bIsX11) { + Vector2D xy = {desiredGeometry.x, desiredGeometry.y}; + xy = g_pXWaylandManager->xwaylandToWaylandCoords(xy); + desiredGeometry.x = xy.x; + desiredGeometry.y = xy.y; + } + + static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); + + if (!PMONITOR) { + Debug::log(ERR, "{:m} has an invalid monitor in onWindowCreatedFloating!!!", pWindow); + return; + } + + if (desiredGeometry.width <= 5 || desiredGeometry.height <= 5) { + const auto PWINDOWSURFACE = pWindow->m_pWLSurface->resource(); + *pWindow->m_vRealSize = PWINDOWSURFACE->current.size; + + if ((desiredGeometry.width <= 1 || desiredGeometry.height <= 1) && pWindow->m_bIsX11 && + pWindow->isX11OverrideRedirect()) { // XDG windows should be fine. TODO: check for weird atoms? + pWindow->setHidden(true); + return; + } + + // reject any windows with size <= 5x5 + if (pWindow->m_vRealSize->goal().x <= 5 || pWindow->m_vRealSize->goal().y <= 5) + *pWindow->m_vRealSize = PMONITOR->vecSize / 2.f; + + if (pWindow->m_bIsX11 && pWindow->isX11OverrideRedirect()) { + + if (pWindow->m_pXWaylandSurface->geometry.x != 0 && pWindow->m_pXWaylandSurface->geometry.y != 0) + *pWindow->m_vRealPosition = g_pXWaylandManager->xwaylandToWaylandCoords(pWindow->m_pXWaylandSurface->geometry.pos()); + else + *pWindow->m_vRealPosition = Vector2D(PMONITOR->vecPosition.x + (PMONITOR->vecSize.x - pWindow->m_vRealSize->goal().x) / 2.f, + PMONITOR->vecPosition.y + (PMONITOR->vecSize.y - pWindow->m_vRealSize->goal().y) / 2.f); + } else { + *pWindow->m_vRealPosition = Vector2D(PMONITOR->vecPosition.x + (PMONITOR->vecSize.x - pWindow->m_vRealSize->goal().x) / 2.f, + PMONITOR->vecPosition.y + (PMONITOR->vecSize.y - pWindow->m_vRealSize->goal().y) / 2.f); + } + } else { + // we respect the size. + *pWindow->m_vRealSize = Vector2D(desiredGeometry.width, desiredGeometry.height); + + // check if it's on the correct monitor! + Vector2D middlePoint = Vector2D(desiredGeometry.x, desiredGeometry.y) + Vector2D(desiredGeometry.width, desiredGeometry.height) / 2.f; + + // check if it's visible on any monitor (only for XDG) + bool visible = pWindow->m_bIsX11; + + if (!visible) { + visible = g_pCompositor->isPointOnAnyMonitor(Vector2D(desiredGeometry.x, desiredGeometry.y)) && + g_pCompositor->isPointOnAnyMonitor(Vector2D(desiredGeometry.x + desiredGeometry.width, desiredGeometry.y)) && + g_pCompositor->isPointOnAnyMonitor(Vector2D(desiredGeometry.x, desiredGeometry.y + desiredGeometry.height)) && + g_pCompositor->isPointOnAnyMonitor(Vector2D(desiredGeometry.x + desiredGeometry.width, desiredGeometry.y + desiredGeometry.height)); + } + + // TODO: detect a popup in a more consistent way. + if ((desiredGeometry.x == 0 && desiredGeometry.y == 0) || !visible || !pWindow->m_bIsX11) { + // if the pos isn't set, fall back to the center placement if it's not a child, otherwise middle of parent if available + if (!pWindow->m_bIsX11 && pWindow->m_pXDGSurface->toplevel->parent && validMapped(pWindow->m_pXDGSurface->toplevel->parent->window)) + *pWindow->m_vRealPosition = pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealPosition->goal() + + pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealSize->goal() / 2.F - desiredGeometry.size() / 2.F; + else + *pWindow->m_vRealPosition = PMONITOR->vecPosition + PMONITOR->vecSize / 2.F - desiredGeometry.size() / 2.F; + } else { + // if it is, we respect where it wants to put itself, but apply monitor offset if outside + // most of these are popups + + if (const auto POPENMON = g_pCompositor->getMonitorFromVector(middlePoint); POPENMON->ID != PMONITOR->ID) + *pWindow->m_vRealPosition = Vector2D(desiredGeometry.x, desiredGeometry.y) - POPENMON->vecPosition + PMONITOR->vecPosition; + else + *pWindow->m_vRealPosition = Vector2D(desiredGeometry.x, desiredGeometry.y); + } + } + + if (*PXWLFORCESCALEZERO && pWindow->m_bIsX11) + *pWindow->m_vRealSize = pWindow->m_vRealSize->goal() / PMONITOR->scale; + + if (pWindow->m_bX11DoesntWantBorders || (pWindow->m_bIsX11 && pWindow->isX11OverrideRedirect())) { + pWindow->m_vRealPosition->warp(); + pWindow->m_vRealSize->warp(); + } + + if (!pWindow->isX11OverrideRedirect()) + g_pCompositor->changeWindowZOrder(pWindow, true); + else { + pWindow->m_vPendingReportedSize = pWindow->m_vRealSize->goal(); + pWindow->m_vReportedSize = pWindow->m_vPendingReportedSize; + } +} + +bool IHyprLayout::onWindowCreatedAutoGroup(PHLWINDOW pWindow) { + static auto PAUTOGROUP = CConfigValue("group:auto_group"); + const PHLWINDOW OPENINGON = g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_pWorkspace == pWindow->m_pWorkspace ? + g_pCompositor->m_pLastWindow.lock() : + (pWindow->m_pWorkspace ? pWindow->m_pWorkspace->getFirstWindow() : nullptr); + const bool FLOATEDINTOTILED = pWindow->m_bIsFloating && !OPENINGON->m_bIsFloating; + const bool SWALLOWING = pWindow->m_pSwallowed || pWindow->m_bGroupSwallowed; + + if ((*PAUTOGROUP || SWALLOWING) // continue if auto_group is enabled or if dealing with window swallowing. + && OPENINGON // this shouldn't be 0, but honestly, better safe than sorry. + && OPENINGON != pWindow // prevent freeze when the "group set" window rule makes the new window to be already a group. + && OPENINGON->m_sGroupData.pNextWindow.lock() // check if OPENINGON is a group. + && pWindow->canBeGroupedInto(OPENINGON) // check if the new window can be grouped into OPENINGON. + && !g_pXWaylandManager->shouldBeFloated(pWindow) // don't group child windows. Fix for floated groups. Tiled groups don't need this because we check if !FLOATEDINTOTILED. + && !FLOATEDINTOTILED) { // don't group a new floated window into a tiled group (for convenience). + + pWindow->m_bIsFloating = OPENINGON->m_bIsFloating; // match the floating state. Needed to autogroup a new tiled window into a floated group. + + static auto USECURRPOS = CConfigValue("group:insert_after_current"); + (*USECURRPOS ? OPENINGON : OPENINGON->getGroupTail())->insertWindowToGroup(pWindow); + + OPENINGON->setGroupCurrent(pWindow); + pWindow->applyGroupRules(); + pWindow->updateWindowDecos(); + recalculateWindow(pWindow); + + if (!pWindow->getDecorationByType(DECORATION_GROUPBAR)) + pWindow->addWindowDeco(makeUnique(pWindow)); + + return true; + } + + return false; +} + +void IHyprLayout::onBeginDragWindow() { + const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock(); + + m_iMouseMoveEventCount = 1; + m_vBeginDragSizeXY = Vector2D(); + + // Window will be floating. Let's check if it's valid. It should be, but I don't like crashing. + if (!validMapped(DRAGGINGWINDOW)) { + Debug::log(ERR, "Dragging attempted on an invalid window!"); + g_pKeybindManager->changeMouseBindMode(MBIND_INVALID); + return; + } + + if (DRAGGINGWINDOW->isFullscreen()) { + Debug::log(LOG, "Dragging a fullscreen window"); + g_pCompositor->setWindowFullscreenInternal(DRAGGINGWINDOW, FSMODE_NONE); + } + + const auto PWORKSPACE = DRAGGINGWINDOW->m_pWorkspace; + + if (PWORKSPACE->m_bHasFullscreenWindow && (!DRAGGINGWINDOW->m_bCreatedOverFullscreen || !DRAGGINGWINDOW->m_bIsFloating)) { + Debug::log(LOG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)"); + g_pKeybindManager->changeMouseBindMode(MBIND_INVALID); + return; + } + + DRAGGINGWINDOW->m_bDraggingTiled = false; + + m_vDraggingWindowOriginalFloatSize = DRAGGINGWINDOW->m_vLastFloatingSize; + + if (!DRAGGINGWINDOW->m_bIsFloating) { + if (g_pInputManager->dragMode == MBIND_MOVE) { + DRAGGINGWINDOW->m_vLastFloatingSize = (DRAGGINGWINDOW->m_vRealSize->goal() * 0.8489).clamp(Vector2D{5, 5}, Vector2D{}).floor(); + changeWindowFloatingMode(DRAGGINGWINDOW); + DRAGGINGWINDOW->m_bIsFloating = true; + DRAGGINGWINDOW->m_bDraggingTiled = true; + + *DRAGGINGWINDOW->m_vRealPosition = g_pInputManager->getMouseCoordsInternal() - DRAGGINGWINDOW->m_vRealSize->goal() / 2.f; + } + } + + m_vBeginDragXY = g_pInputManager->getMouseCoordsInternal(); + m_vBeginDragPositionXY = DRAGGINGWINDOW->m_vRealPosition->goal(); + m_vBeginDragSizeXY = DRAGGINGWINDOW->m_vRealSize->goal(); + m_vLastDragXY = m_vBeginDragXY; + + // get the grab corner + static auto RESIZECORNER = CConfigValue("general:resize_corner"); + if (*RESIZECORNER != 0 && *RESIZECORNER <= 4 && DRAGGINGWINDOW->m_bIsFloating) { + switch (*RESIZECORNER) { + case 1: + m_eGrabbedCorner = CORNER_TOPLEFT; + g_pInputManager->setCursorImageUntilUnset("nw-resize"); + break; + case 2: + m_eGrabbedCorner = CORNER_TOPRIGHT; + g_pInputManager->setCursorImageUntilUnset("ne-resize"); + break; + case 3: + m_eGrabbedCorner = CORNER_BOTTOMRIGHT; + g_pInputManager->setCursorImageUntilUnset("se-resize"); + break; + case 4: + m_eGrabbedCorner = CORNER_BOTTOMLEFT; + g_pInputManager->setCursorImageUntilUnset("sw-resize"); + break; + } + } else if (m_vBeginDragXY.x < m_vBeginDragPositionXY.x + m_vBeginDragSizeXY.x / 2.0) { + if (m_vBeginDragXY.y < m_vBeginDragPositionXY.y + m_vBeginDragSizeXY.y / 2.0) { + m_eGrabbedCorner = CORNER_TOPLEFT; + g_pInputManager->setCursorImageUntilUnset("nw-resize"); + } else { + m_eGrabbedCorner = CORNER_BOTTOMLEFT; + g_pInputManager->setCursorImageUntilUnset("sw-resize"); + } + } else { + if (m_vBeginDragXY.y < m_vBeginDragPositionXY.y + m_vBeginDragSizeXY.y / 2.0) { + m_eGrabbedCorner = CORNER_TOPRIGHT; + g_pInputManager->setCursorImageUntilUnset("ne-resize"); + } else { + m_eGrabbedCorner = CORNER_BOTTOMRIGHT; + g_pInputManager->setCursorImageUntilUnset("se-resize"); + } + } + + if (g_pInputManager->dragMode != MBIND_RESIZE && g_pInputManager->dragMode != MBIND_RESIZE_FORCE_RATIO && g_pInputManager->dragMode != MBIND_RESIZE_BLOCK_RATIO) + g_pInputManager->setCursorImageUntilUnset("grabbing"); + + g_pHyprRenderer->damageWindow(DRAGGINGWINDOW); + + g_pKeybindManager->shadowKeybinds(); + + g_pCompositor->focusWindow(DRAGGINGWINDOW); + g_pCompositor->changeWindowZOrder(DRAGGINGWINDOW, true); +} + +void IHyprLayout::onEndDragWindow() { + const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock(); + + m_iMouseMoveEventCount = 1; + + if (!validMapped(DRAGGINGWINDOW)) { + if (DRAGGINGWINDOW) { + g_pInputManager->unsetCursorImage(); + g_pInputManager->currentlyDraggedWindow.reset(); + } + return; + } + + g_pInputManager->unsetCursorImage(); + g_pInputManager->currentlyDraggedWindow.reset(); + g_pInputManager->m_bWasDraggingWindow = true; + + if (g_pInputManager->dragMode == MBIND_MOVE) { + g_pHyprRenderer->damageWindow(DRAGGINGWINDOW); + const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); + PHLWINDOW pWindow = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING, DRAGGINGWINDOW); + + if (pWindow) { + if (pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_END, MOUSECOORDS, DRAGGINGWINDOW)) + return; + + const bool FLOATEDINTOTILED = !pWindow->m_bIsFloating && !DRAGGINGWINDOW->m_bDraggingTiled; + static auto PDRAGINTOGROUP = CConfigValue("group:drag_into_group"); + + if (pWindow->m_sGroupData.pNextWindow.lock() && DRAGGINGWINDOW->canBeGroupedInto(pWindow) && *PDRAGINTOGROUP == 1 && !FLOATEDINTOTILED) { + + if (DRAGGINGWINDOW->m_sGroupData.pNextWindow) { + PHLWINDOW next = DRAGGINGWINDOW->m_sGroupData.pNextWindow.lock(); + while (next != DRAGGINGWINDOW) { + next->m_bIsFloating = pWindow->m_bIsFloating; // match the floating state of group members + *next->m_vRealSize = pWindow->m_vRealSize->goal(); // match the size of group members + *next->m_vRealPosition = pWindow->m_vRealPosition->goal(); // match the position of group members + next = next->m_sGroupData.pNextWindow.lock(); + } + } + + DRAGGINGWINDOW->m_bIsFloating = pWindow->m_bIsFloating; // match the floating state of the window + DRAGGINGWINDOW->m_vLastFloatingSize = m_vDraggingWindowOriginalFloatSize; + DRAGGINGWINDOW->m_bDraggingTiled = false; + + static auto USECURRPOS = CConfigValue("group:insert_after_current"); + (*USECURRPOS ? pWindow : pWindow->getGroupTail())->insertWindowToGroup(DRAGGINGWINDOW); + pWindow->setGroupCurrent(DRAGGINGWINDOW); + DRAGGINGWINDOW->applyGroupRules(); + DRAGGINGWINDOW->updateWindowDecos(); + + if (!DRAGGINGWINDOW->getDecorationByType(DECORATION_GROUPBAR)) + DRAGGINGWINDOW->addWindowDeco(makeUnique(DRAGGINGWINDOW)); + } + } + } + + if (DRAGGINGWINDOW->m_bDraggingTiled) { + DRAGGINGWINDOW->m_bIsFloating = false; + g_pInputManager->refocus(); + changeWindowFloatingMode(DRAGGINGWINDOW); + DRAGGINGWINDOW->m_vLastFloatingSize = m_vDraggingWindowOriginalFloatSize; + } + + g_pHyprRenderer->damageWindow(DRAGGINGWINDOW); + g_pCompositor->focusWindow(DRAGGINGWINDOW); + + g_pInputManager->m_bWasDraggingWindow = false; +} + +static inline bool canSnap(const double SIDEA, const double SIDEB, const double GAP) { + return std::abs(SIDEA - SIDEB) < GAP; +} + +static void snapMove(double& start, double& end, const double P) { + end = P + (end - start); + start = P; +} + +static void snapResize(double& start, double& end, const double P) { + start = P; +} + +typedef std::function SnapFn; + +static void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, PHLWINDOW DRAGGINGWINDOW, const eMouseBindMode MODE, const int CORNER, const Vector2D& BEGINSIZE) { + static auto SNAPWINDOWGAP = CConfigValue("general:snap:window_gap"); + static auto SNAPMONITORGAP = CConfigValue("general:snap:monitor_gap"); + static auto SNAPBORDEROVERLAP = CConfigValue("general:snap:border_overlap"); + + const SnapFn SNAP = (MODE == MBIND_MOVE) ? snapMove : snapResize; + int snaps = 0; + + const bool OVERLAP = *SNAPBORDEROVERLAP; + const int DRAGGINGBORDERSIZE = DRAGGINGWINDOW->getRealBorderSize(); + + struct SRange { + double start = 0; + double end = 0; + }; + SRange sourceX = {sourcePos.x, sourcePos.x + sourceSize.x}; + SRange sourceY = {sourcePos.y, sourcePos.y + sourceSize.y}; + + if (*SNAPWINDOWGAP) { + const double GAPSIZE = *SNAPWINDOWGAP; + const auto WSID = DRAGGINGWINDOW->workspaceID(); + const bool HASFULLSCREEN = DRAGGINGWINDOW->m_pWorkspace && DRAGGINGWINDOW->m_pWorkspace->m_bHasFullscreenWindow; + + for (auto& other : g_pCompositor->m_vWindows) { + if ((HASFULLSCREEN && !other->m_bCreatedOverFullscreen) || other == DRAGGINGWINDOW || other->workspaceID() != WSID || !other->m_bIsMapped || other->m_bFadingOut || + other->isX11OverrideRedirect()) + continue; + + const int OTHERBORDERSIZE = other->getRealBorderSize(); + const double BORDERSIZE = OVERLAP ? std::max(DRAGGINGBORDERSIZE, OTHERBORDERSIZE) : (DRAGGINGBORDERSIZE + OTHERBORDERSIZE); + + const CBox SURF = other->getWindowMainSurfaceBox(); + const SRange SURFBX = {SURF.x - BORDERSIZE, SURF.x + SURF.w + BORDERSIZE}; + const SRange SURFBY = {SURF.y - BORDERSIZE, SURF.y + SURF.h + BORDERSIZE}; + + // only snap windows if their ranges overlap in the opposite axis + if (sourceY.start <= SURFBY.end && SURFBY.start <= sourceY.end) { + if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && canSnap(sourceX.start, SURFBX.end, GAPSIZE)) { + SNAP(sourceX.start, sourceX.end, SURFBX.end); + snaps |= SNAP_LEFT; + } else if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && canSnap(sourceX.end, SURFBX.start, GAPSIZE)) { + SNAP(sourceX.end, sourceX.start, SURFBX.start); + snaps |= SNAP_RIGHT; + } + } + if (sourceX.start <= SURFBX.end && SURFBX.start <= sourceX.end) { + if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && canSnap(sourceY.start, SURFBY.end, GAPSIZE)) { + SNAP(sourceY.start, sourceY.end, SURFBY.end); + snaps |= SNAP_UP; + } else if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && canSnap(sourceY.end, SURFBY.start, GAPSIZE)) { + SNAP(sourceY.end, sourceY.start, SURFBY.start); + snaps |= SNAP_DOWN; + } + } + + // corner snapping + const double BORDERDIFF = OTHERBORDERSIZE - DRAGGINGBORDERSIZE; + if (sourceX.start == SURFBX.end || SURFBX.start == sourceX.end) { + const SRange SURFY = {SURF.y - BORDERDIFF, SURF.y + SURF.h + BORDERDIFF}; + if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && !(snaps & SNAP_UP) && canSnap(sourceY.start, SURFY.start, GAPSIZE)) { + SNAP(sourceY.start, sourceY.end, SURFY.start); + snaps |= SNAP_UP; + } else if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && !(snaps & SNAP_DOWN) && canSnap(sourceY.end, SURFY.end, GAPSIZE)) { + SNAP(sourceY.end, sourceY.start, SURFY.end); + snaps |= SNAP_DOWN; + } + } + if (sourceY.start == SURFBY.end || SURFBY.start == sourceY.end) { + const SRange SURFX = {SURF.x - BORDERDIFF, SURF.x + SURF.w + BORDERDIFF}; + if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && !(snaps & SNAP_LEFT) && canSnap(sourceX.start, SURFX.start, GAPSIZE)) { + SNAP(sourceX.start, sourceX.end, SURFX.start); + snaps |= SNAP_LEFT; + } else if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && !(snaps & SNAP_RIGHT) && canSnap(sourceX.end, SURFX.end, GAPSIZE)) { + SNAP(sourceX.end, sourceX.start, SURFX.end); + snaps |= SNAP_RIGHT; + } + } + } + } + + if (*SNAPMONITORGAP) { + const double GAPSIZE = *SNAPMONITORGAP; + const double BORDERDIFF = OVERLAP ? DRAGGINGBORDERSIZE : 0; + const auto MON = DRAGGINGWINDOW->m_pMonitor.lock(); + + SRange monX = {MON->vecPosition.x + MON->vecReservedTopLeft.x + DRAGGINGBORDERSIZE, + MON->vecPosition.x + MON->vecSize.x - MON->vecReservedBottomRight.x - DRAGGINGBORDERSIZE}; + SRange monY = {MON->vecPosition.y + MON->vecReservedTopLeft.y + DRAGGINGBORDERSIZE, + MON->vecPosition.y + MON->vecSize.y - MON->vecReservedBottomRight.y - DRAGGINGBORDERSIZE}; + + if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && + ((MON->vecReservedTopLeft.x > 0 && canSnap(sourceX.start, monX.start, GAPSIZE)) || + canSnap(sourceX.start, (monX.start -= MON->vecReservedTopLeft.x + BORDERDIFF), GAPSIZE))) { + SNAP(sourceX.start, sourceX.end, monX.start); + snaps |= SNAP_LEFT; + } + if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && + ((MON->vecReservedBottomRight.x > 0 && canSnap(sourceX.end, monX.end, GAPSIZE)) || + canSnap(sourceX.end, (monX.end += MON->vecReservedBottomRight.x + BORDERDIFF), GAPSIZE))) { + SNAP(sourceX.end, sourceX.start, monX.end); + snaps |= SNAP_RIGHT; + } + if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && + ((MON->vecReservedTopLeft.y > 0 && canSnap(sourceY.start, monY.start, GAPSIZE)) || + canSnap(sourceY.start, (monY.start -= MON->vecReservedTopLeft.y + BORDERDIFF), GAPSIZE))) { + SNAP(sourceY.start, sourceY.end, monY.start); + snaps |= SNAP_UP; + } + if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && + ((MON->vecReservedBottomRight.y > 0 && canSnap(sourceY.end, monY.end, GAPSIZE)) || + canSnap(sourceY.end, (monY.end += MON->vecReservedBottomRight.y + BORDERDIFF), GAPSIZE))) { + SNAP(sourceY.end, sourceY.start, monY.end); + snaps |= SNAP_DOWN; + } + } + + if (MODE == MBIND_RESIZE_FORCE_RATIO) { + if ((CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && snaps & SNAP_LEFT) || (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && snaps & SNAP_RIGHT)) { + const double SIZEY = (sourceX.end - sourceX.start) * (BEGINSIZE.y / BEGINSIZE.x); + if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT)) + sourceY.start = sourceY.end - SIZEY; + else + sourceY.end = sourceY.start + SIZEY; + } else if ((CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && snaps & SNAP_UP) || (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && snaps & SNAP_DOWN)) { + const double SIZEX = (sourceY.end - sourceY.start) * (BEGINSIZE.x / BEGINSIZE.y); + if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT)) + sourceX.start = sourceX.end - SIZEX; + else + sourceX.end = sourceX.start + SIZEX; + } + } + + sourcePos = {sourceX.start, sourceY.start}; + sourceSize = {sourceX.end - sourceX.start, sourceY.end - sourceY.start}; +} + +void IHyprLayout::onMouseMove(const Vector2D& mousePos) { + if (g_pInputManager->currentlyDraggedWindow.expired()) + return; + + const auto DRAGGINGWINDOW = g_pInputManager->currentlyDraggedWindow.lock(); + + // Window invalid or drag begin size 0,0 meaning we rejected it. + if ((!validMapped(DRAGGINGWINDOW) || m_vBeginDragSizeXY == Vector2D())) { + g_pKeybindManager->changeMouseBindMode(MBIND_INVALID); + return; + } + + static auto TIMER = std::chrono::high_resolution_clock::now(), MSTIMER = TIMER; + + const auto SPECIAL = DRAGGINGWINDOW->onSpecialWorkspace(); + + const auto DELTA = Vector2D(mousePos.x - m_vBeginDragXY.x, mousePos.y - m_vBeginDragXY.y); + const auto TICKDELTA = Vector2D(mousePos.x - m_vLastDragXY.x, mousePos.y - m_vLastDragXY.y); + + static auto PANIMATEMOUSE = CConfigValue("misc:animate_mouse_windowdragging"); + static auto PANIMATE = CConfigValue("misc:animate_manual_resizes"); + + static auto SNAPENABLED = CConfigValue("general:snap:enabled"); + + const auto TIMERDELTA = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - TIMER).count(); + const auto MSDELTA = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - MSTIMER).count(); + const auto MSMONITOR = 1000.0 / g_pHyprRenderer->m_pMostHzMonitor->refreshRate; + static int totalMs = 0; + bool canSkipUpdate = true; + + MSTIMER = std::chrono::high_resolution_clock::now(); + + if (m_iMouseMoveEventCount == 1) + totalMs = 0; + + if (MSMONITOR > 16.0) { + totalMs += MSDELTA < MSMONITOR ? MSDELTA : std::round(totalMs * 1.0 / m_iMouseMoveEventCount); + m_iMouseMoveEventCount += 1; + + // check if time-window is enough to skip update on 60hz monitor + canSkipUpdate = std::clamp(MSMONITOR - TIMERDELTA, 0.0, MSMONITOR) > totalMs * 1.0 / m_iMouseMoveEventCount; + } + + if ((abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f) || (TIMERDELTA < MSMONITOR && canSkipUpdate && (g_pInputManager->dragMode != MBIND_MOVE || *PANIMATEMOUSE))) + return; + + TIMER = std::chrono::high_resolution_clock::now(); + + m_vLastDragXY = mousePos; + + g_pHyprRenderer->damageWindow(DRAGGINGWINDOW); + + if (g_pInputManager->dragMode == MBIND_MOVE) { + + Vector2D newPos = m_vBeginDragPositionXY + DELTA; + Vector2D newSize = DRAGGINGWINDOW->m_vRealSize->goal(); + + if (*SNAPENABLED && !DRAGGINGWINDOW->m_bDraggingTiled) + performSnap(newPos, newSize, DRAGGINGWINDOW, MBIND_MOVE, -1, m_vBeginDragSizeXY); + + CBox wb = {newPos, newSize}; + wb.round(); + + if (*PANIMATEMOUSE) + *DRAGGINGWINDOW->m_vRealPosition = wb.pos(); + else { + DRAGGINGWINDOW->m_vRealPosition->setValueAndWarp(wb.pos()); + DRAGGINGWINDOW->sendWindowSize(); + } + + } else if (g_pInputManager->dragMode == MBIND_RESIZE || g_pInputManager->dragMode == MBIND_RESIZE_FORCE_RATIO || g_pInputManager->dragMode == MBIND_RESIZE_BLOCK_RATIO) { + if (DRAGGINGWINDOW->m_bIsFloating) { + + Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_sWindowData.minSize.valueOr(Vector2D(20, 20))); + Vector2D MAXSIZE; + if (DRAGGINGWINDOW->m_sWindowData.maxSize.hasValue()) + MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, DRAGGINGWINDOW->m_sWindowData.maxSize.value()); + else + MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, Vector2D(std::numeric_limits::max(), std::numeric_limits::max())); + + Vector2D newSize = m_vBeginDragSizeXY; + Vector2D newPos = m_vBeginDragPositionXY; + + if (m_eGrabbedCorner == CORNER_BOTTOMRIGHT) + newSize = newSize + DELTA; + else if (m_eGrabbedCorner == CORNER_TOPLEFT) + newSize = newSize - DELTA; + else if (m_eGrabbedCorner == CORNER_TOPRIGHT) + newSize = newSize + Vector2D(DELTA.x, -DELTA.y); + else if (m_eGrabbedCorner == CORNER_BOTTOMLEFT) + newSize = newSize + Vector2D(-DELTA.x, DELTA.y); + + eMouseBindMode mode = g_pInputManager->dragMode; + if (DRAGGINGWINDOW->m_sWindowData.keepAspectRatio.valueOrDefault() && mode != MBIND_RESIZE_BLOCK_RATIO) + mode = MBIND_RESIZE_FORCE_RATIO; + + if (m_vBeginDragSizeXY.x >= 1 && m_vBeginDragSizeXY.y >= 1 && mode == MBIND_RESIZE_FORCE_RATIO) { + + const float RATIO = m_vBeginDragSizeXY.y / m_vBeginDragSizeXY.x; + + if (MINSIZE.x * RATIO > MINSIZE.y) + MINSIZE = Vector2D(MINSIZE.x, MINSIZE.x * RATIO); + else + MINSIZE = Vector2D(MINSIZE.y / RATIO, MINSIZE.y); + + if (MAXSIZE.x * RATIO < MAXSIZE.y) + MAXSIZE = Vector2D(MAXSIZE.x, MAXSIZE.x * RATIO); + else + MAXSIZE = Vector2D(MAXSIZE.y / RATIO, MAXSIZE.y); + + if (newSize.x * RATIO > newSize.y) + newSize = Vector2D(newSize.x, newSize.x * RATIO); + else + newSize = Vector2D(newSize.y / RATIO, newSize.y); + } + + newSize = newSize.clamp(MINSIZE, MAXSIZE); + + if (m_eGrabbedCorner == CORNER_TOPLEFT) + newPos = newPos - newSize + m_vBeginDragSizeXY; + else if (m_eGrabbedCorner == CORNER_TOPRIGHT) + newPos = newPos + Vector2D(0.0, (m_vBeginDragSizeXY - newSize).y); + else if (m_eGrabbedCorner == CORNER_BOTTOMLEFT) + newPos = newPos + Vector2D((m_vBeginDragSizeXY - newSize).x, 0.0); + + if (*SNAPENABLED) { + performSnap(newPos, newSize, DRAGGINGWINDOW, mode, m_eGrabbedCorner, m_vBeginDragSizeXY); + newSize = newSize.clamp(MINSIZE, MAXSIZE); + } + + CBox wb = {newPos, newSize}; + wb.round(); + + if (*PANIMATE) { + *DRAGGINGWINDOW->m_vRealSize = wb.size(); + *DRAGGINGWINDOW->m_vRealPosition = wb.pos(); + } else { + DRAGGINGWINDOW->m_vRealSize->setValueAndWarp(wb.size()); + DRAGGINGWINDOW->m_vRealPosition->setValueAndWarp(wb.pos()); + DRAGGINGWINDOW->sendWindowSize(); + } + } else { + resizeActiveWindow(TICKDELTA, m_eGrabbedCorner, DRAGGINGWINDOW); + } + } + + // get middle point + Vector2D middle = DRAGGINGWINDOW->m_vRealPosition->value() + DRAGGINGWINDOW->m_vRealSize->value() / 2.f; + + // and check its monitor + const auto PMONITOR = g_pCompositor->getMonitorFromVector(middle); + + if (PMONITOR && !SPECIAL) { + DRAGGINGWINDOW->m_pMonitor = PMONITOR; + DRAGGINGWINDOW->moveToWorkspace(PMONITOR->activeWorkspace); + DRAGGINGWINDOW->updateGroupOutputs(); + + DRAGGINGWINDOW->updateToplevel(); + } + + DRAGGINGWINDOW->updateWindowDecos(); + + g_pHyprRenderer->damageWindow(DRAGGINGWINDOW); +} + +void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) { + + if (pWindow->isFullscreen()) { + Debug::log(LOG, "changeWindowFloatingMode: fullscreen"); + g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE); + } + + pWindow->m_bPinned = false; + + g_pHyprRenderer->damageWindow(pWindow, true); + + const auto TILED = isWindowTiled(pWindow); + + // event + g_pEventManager->postEvent(SHyprIPCEvent{"changefloatingmode", std::format("{:x},{}", (uintptr_t)pWindow.get(), (int)TILED)}); + EMIT_HOOK_EVENT("changeFloatingMode", pWindow); + + if (!TILED) { + const auto PNEWMON = g_pCompositor->getMonitorFromVector(pWindow->m_vRealPosition->value() + pWindow->m_vRealSize->value() / 2.f); + pWindow->m_pMonitor = PNEWMON; + pWindow->moveToWorkspace(PNEWMON->activeSpecialWorkspace ? PNEWMON->activeSpecialWorkspace : PNEWMON->activeWorkspace); + pWindow->updateGroupOutputs(); + + const auto PWORKSPACE = PNEWMON->activeSpecialWorkspace ? PNEWMON->activeSpecialWorkspace : PNEWMON->activeWorkspace; + + if (PWORKSPACE->m_bHasFullscreenWindow) + g_pCompositor->setWindowFullscreenInternal(PWORKSPACE->getFullscreenWindow(), FSMODE_NONE); + + // save real pos cuz the func applies the default 5,5 mid + const auto PSAVEDPOS = pWindow->m_vRealPosition->goal(); + const auto PSAVEDSIZE = pWindow->m_vRealSize->goal(); + + // if the window is pseudo, update its size + if (!pWindow->m_bDraggingTiled) + pWindow->m_vPseudoSize = pWindow->m_vRealSize->goal(); + + pWindow->m_vLastFloatingSize = PSAVEDSIZE; + + // move to narnia because we don't wanna find our own node. onWindowCreatedTiling should apply the coords back. + pWindow->m_vPosition = Vector2D(-999999, -999999); + + onWindowCreatedTiling(pWindow); + + pWindow->m_vRealPosition->setValue(PSAVEDPOS); + pWindow->m_vRealSize->setValue(PSAVEDSIZE); + + // fix pseudo leaving artifacts + g_pHyprRenderer->damageMonitor(pWindow->m_pMonitor.lock()); + + if (pWindow == g_pCompositor->m_pLastWindow) + m_pLastTiledWindow = pWindow; + } else { + onWindowRemovedTiling(pWindow); + + g_pCompositor->changeWindowZOrder(pWindow, true); + + CBox wb = {pWindow->m_vRealPosition->goal() + (pWindow->m_vRealSize->goal() - pWindow->m_vLastFloatingSize) / 2.f, pWindow->m_vLastFloatingSize}; + wb.round(); + + if (!(pWindow->m_bIsFloating && pWindow->m_bIsPseudotiled) && DELTALESSTHAN(pWindow->m_vRealSize->value().x, pWindow->m_vLastFloatingSize.x, 10) && + DELTALESSTHAN(pWindow->m_vRealSize->value().y, pWindow->m_vLastFloatingSize.y, 10)) { + wb = {wb.pos() + Vector2D{10, 10}, wb.size() - Vector2D{20, 20}}; + } + + *pWindow->m_vRealPosition = wb.pos(); + *pWindow->m_vRealSize = wb.size(); + + pWindow->m_vSize = wb.pos(); + pWindow->m_vPosition = wb.size(); + + g_pHyprRenderer->damageMonitor(pWindow->m_pMonitor.lock()); + + pWindow->unsetWindowData(PRIORITY_LAYOUT); + pWindow->updateWindowData(); + + if (pWindow == m_pLastTiledWindow) + m_pLastTiledWindow.reset(); + } + + g_pCompositor->updateWindowAnimatedDecorationValues(pWindow); + pWindow->updateToplevel(); + g_pHyprRenderer->damageWindow(pWindow); +} + +void IHyprLayout::moveActiveWindow(const Vector2D& delta, PHLWINDOW pWindow) { + const auto PWINDOW = pWindow ? pWindow : g_pCompositor->m_pLastWindow.lock(); + + if (!validMapped(PWINDOW)) + return; + + if (!PWINDOW->m_bIsFloating) { + Debug::log(LOG, "Dwindle cannot move a tiled window in moveActiveWindow!"); + return; + } + + PWINDOW->setAnimationsToMove(); + + *PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition->goal() + delta; + + g_pHyprRenderer->damageWindow(PWINDOW); +} + +void IHyprLayout::onWindowFocusChange(PHLWINDOW pNewFocus) { + m_pLastTiledWindow = pNewFocus && !pNewFocus->m_bIsFloating ? pNewFocus : m_pLastTiledWindow; +} + +PHLWINDOW IHyprLayout::getNextWindowCandidate(PHLWINDOW pWindow) { + // although we don't expect nullptrs here, let's verify jic + if (!pWindow) + return nullptr; + + const auto PWORKSPACE = pWindow->m_pWorkspace; + + // first of all, if this is a fullscreen workspace, + if (PWORKSPACE->m_bHasFullscreenWindow) + return PWORKSPACE->getFullscreenWindow(); + + if (pWindow->m_bIsFloating) { + + // find whether there is a floating window below this one + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && !w->isX11OverrideRedirect() && w->m_pWorkspace == pWindow->m_pWorkspace && !w->m_bX11ShouldntFocus && + !w->m_sWindowData.noFocus.valueOrDefault() && w != pWindow) { + if (VECINRECT((pWindow->m_vSize / 2.f + pWindow->m_vPosition), w->m_vPosition.x, w->m_vPosition.y, w->m_vPosition.x + w->m_vSize.x, + w->m_vPosition.y + w->m_vSize.y)) { + return w; + } + } + } + + // let's try the last tiled window. + if (m_pLastTiledWindow.lock() && m_pLastTiledWindow->m_pWorkspace == pWindow->m_pWorkspace) + return m_pLastTiledWindow.lock(); + + // if we don't, let's try to find any window that is in the middle + if (const auto PWINDOWCANDIDATE = g_pCompositor->vectorToWindowUnified(pWindow->middle(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); + PWINDOWCANDIDATE && PWINDOWCANDIDATE != pWindow) + return PWINDOWCANDIDATE; + + // if not, floating window + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_bIsMapped && !w->isHidden() && w->m_bIsFloating && !w->isX11OverrideRedirect() && w->m_pWorkspace == pWindow->m_pWorkspace && !w->m_bX11ShouldntFocus && + !w->m_sWindowData.noFocus.valueOrDefault() && w != pWindow) + return w; + } + + // if there is no candidate, too bad + return nullptr; + } + + // if it was a tiled window, we first try to find the window that will replace it. + auto pWindowCandidate = g_pCompositor->vectorToWindowUnified(pWindow->middle(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); + + if (!pWindowCandidate) + pWindowCandidate = PWORKSPACE->getTopLeftWindow(); + + if (!pWindowCandidate) + pWindowCandidate = PWORKSPACE->getFirstWindow(); + + if (!pWindowCandidate || pWindow == pWindowCandidate || !pWindowCandidate->m_bIsMapped || pWindowCandidate->isHidden() || pWindowCandidate->m_bX11ShouldntFocus || + pWindowCandidate->isX11OverrideRedirect() || pWindowCandidate->m_pMonitor != g_pCompositor->m_pLastMonitor) + return nullptr; + + return pWindowCandidate; +} + +bool IHyprLayout::isWindowReachable(PHLWINDOW pWindow) { + return pWindow && (!pWindow->isHidden() || pWindow->m_sGroupData.pNextWindow); +} + +void IHyprLayout::bringWindowToTop(PHLWINDOW pWindow) { + if (pWindow == nullptr) + return; + + if (pWindow->isHidden() && pWindow->m_sGroupData.pNextWindow) { + // grouped, change the current to this window + pWindow->setGroupCurrent(pWindow); + } +} + +void IHyprLayout::requestFocusForWindow(PHLWINDOW pWindow) { + bringWindowToTop(pWindow); + g_pCompositor->focusWindow(pWindow); + g_pCompositor->warpCursorTo(pWindow->middle()); +} + +Vector2D IHyprLayout::predictSizeForNewWindowFloating(PHLWINDOW pWindow) { // get all rules, see if we have any size overrides. + Vector2D sizeOverride = {}; + if (g_pCompositor->m_pLastMonitor) { + for (auto const& r : g_pConfigManager->getMatchingRules(pWindow, true, true)) { + if (r->ruleType != CWindowRule::RULE_SIZE) + continue; + + try { + const auto VALUE = r->szRule.substr(r->szRule.find(' ') + 1); + const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' ')); + const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1); + + const auto MAXSIZE = pWindow->requestedMaxSize(); + + const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, g_pCompositor->m_pLastMonitor->vecSize.x) : + stringToPercentage(SIZEXSTR, g_pCompositor->m_pLastMonitor->vecSize.x); + + const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, g_pCompositor->m_pLastMonitor->vecSize.y) : + stringToPercentage(SIZEYSTR, g_pCompositor->m_pLastMonitor->vecSize.y); + + sizeOverride = {SIZEX, SIZEY}; + + } catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->szRule, r->szValue); } + break; + } + } + + return sizeOverride; +} + +Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) { + bool shouldBeFloated = g_pXWaylandManager->shouldBeFloated(pWindow, true); + + if (!shouldBeFloated) { + for (auto const& r : g_pConfigManager->getMatchingRules(pWindow, true, true)) { + if (r->ruleType != CWindowRule::RULE_FLOAT) + continue; + + shouldBeFloated = true; + break; + } + } + + Vector2D sizePredicted = {}; + + if (!shouldBeFloated) + sizePredicted = predictSizeForNewWindowTiled(); + else + sizePredicted = predictSizeForNewWindowFloating(pWindow); + + Vector2D maxSize = pWindow->m_pXDGSurface->toplevel->pending.maxSize; + + if ((maxSize.x > 0 && maxSize.x < sizePredicted.x) || (maxSize.y > 0 && maxSize.y < sizePredicted.y)) + sizePredicted = {}; + + return sizePredicted; +} diff --git a/src/layout/IHyprLayout.hpp b/src/layout/IHyprLayout.hpp new file mode 100644 index 00000000..e31bb63e --- /dev/null +++ b/src/layout/IHyprLayout.hpp @@ -0,0 +1,217 @@ +#pragma once + +#include "../defines.hpp" +#include + +class CWindow; +class CGradientValueData; + +struct SWindowRenderLayoutHints { + bool isBorderGradient = false; + CGradientValueData* borderGradient = nullptr; +}; + +struct SLayoutMessageHeader { + PHLWINDOW pWindow; +}; + +enum eFullscreenMode : int8_t; + +enum eRectCorner : uint8_t { + CORNER_NONE = 0, + CORNER_TOPLEFT = (1 << 0), + CORNER_TOPRIGHT = (1 << 1), + CORNER_BOTTOMRIGHT = (1 << 2), + CORNER_BOTTOMLEFT = (1 << 3), +}; + +enum eSnapEdge : uint8_t { + SNAP_INVALID = 0, + SNAP_UP = (1 << 0), + SNAP_DOWN = (1 << 1), + SNAP_LEFT = (1 << 2), + SNAP_RIGHT = (1 << 3), +}; + +enum eDirection : int8_t { + DIRECTION_DEFAULT = -1, + DIRECTION_UP = 0, + DIRECTION_RIGHT, + DIRECTION_DOWN, + DIRECTION_LEFT +}; + +class IHyprLayout { + public: + virtual ~IHyprLayout() = default; + virtual void onEnable() = 0; + virtual void onDisable() = 0; + + /* + Called when a window is created (mapped) + The layout HAS TO set the goal pos and size (anim mgr will use it) + If !animationinprogress, then the anim mgr will not apply an anim. + */ + virtual void onWindowCreated(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT); + virtual void onWindowCreatedTiling(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT) = 0; + virtual void onWindowCreatedFloating(PHLWINDOW); + virtual bool onWindowCreatedAutoGroup(PHLWINDOW); + + /* + Return tiled status + */ + virtual bool isWindowTiled(PHLWINDOW) = 0; + + /* + Called when a window is removed (unmapped) + */ + virtual void onWindowRemoved(PHLWINDOW); + virtual void onWindowRemovedTiling(PHLWINDOW) = 0; + virtual void onWindowRemovedFloating(PHLWINDOW); + /* + Called when the monitor requires a layout recalculation + this usually means reserved area changes + */ + virtual void recalculateMonitor(const MONITORID&) = 0; + + /* + Called when the compositor requests a window + to be recalculated, e.g. when pseudo is toggled. + */ + virtual void recalculateWindow(PHLWINDOW) = 0; + + /* + Called when a window is requested to be floated + */ + virtual void changeWindowFloatingMode(PHLWINDOW); + /* + Called when a window is clicked on, beginning a drag + this might be a resize, move, whatever the layout defines it + as. + */ + virtual void onBeginDragWindow(); + /* + Called when a user requests a resize of the current window by a vec + Vector2D holds pixel values + Optional pWindow for a specific window + */ + virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr) = 0; + /* + Called when a user requests a move of the current window by a vec + Vector2D holds pixel values + Optional pWindow for a specific window + */ + virtual void moveActiveWindow(const Vector2D&, PHLWINDOW pWindow = nullptr); + /* + Called when a window is ended being dragged + (mouse up) + */ + virtual void onEndDragWindow(); + /* + Called whenever the mouse moves, should the layout want to + do anything with it. + Useful for dragging. + */ + virtual void onMouseMove(const Vector2D&); + + /* + Called when a window / the user requests to toggle the fullscreen state of a window + The layout sets all the fullscreen flags. + It can either accept or ignore. + */ + virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) = 0; + + /* + Called when a dispatcher requests a custom message + The layout is free to ignore. + std::any is the reply. Can be empty. + */ + virtual std::any layoutMessage(SLayoutMessageHeader, std::string) = 0; + + /* + Required to be handled, but may return just SWindowRenderLayoutHints() + Called when the renderer requests any special draw flags for + a specific window, e.g. border color for groups. + */ + virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW) = 0; + + /* + Called when the user requests two windows to be swapped places. + The layout is free to ignore. + */ + virtual void switchWindows(PHLWINDOW, PHLWINDOW) = 0; + + /* + Called when the user requests a window move in a direction. + The layout is free to ignore. + */ + virtual void moveWindowTo(PHLWINDOW, const std::string& direction, bool silent = false) = 0; + + /* + Called when the user requests to change the splitratio by or to X + on a window + */ + virtual void alterSplitRatio(PHLWINDOW, float, bool exact = false) = 0; + + /* + Called when something wants the current layout's name + */ + virtual std::string getLayoutName() = 0; + + /* + Called for getting the next candidate for a focus + */ + virtual PHLWINDOW getNextWindowCandidate(PHLWINDOW); + + /* + Internal: called when window focus changes + */ + virtual void onWindowFocusChange(PHLWINDOW); + + /* + Called for replacing any data a layout has for a new window + */ + virtual void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) = 0; + + /* + Determines if a window can be focused. If hidden this usually means the window is part of a group. + */ + virtual bool isWindowReachable(PHLWINDOW); + + /* + Called before an attempt is made to focus a window. + Brings the window to the top of any groups and ensures it is not hidden. + If the window is unmapped following this call, the focus attempt will fail. + */ + virtual void bringWindowToTop(PHLWINDOW); + + /* + Called via the foreign toplevel activation protocol. + Focuses a window, bringing it to the top of its group if applicable. + May be ignored. + */ + virtual void requestFocusForWindow(PHLWINDOW); + + /* + Called to predict the size of a newly opened window to send it a configure. + Return 0,0 if unpredictable + */ + virtual Vector2D predictSizeForNewWindowTiled() = 0; + + /* + Prefer not overriding, use predictSizeForNewWindowTiled. + */ + virtual Vector2D predictSizeForNewWindow(PHLWINDOW pWindow); + virtual Vector2D predictSizeForNewWindowFloating(PHLWINDOW pWindow); + + private: + int m_iMouseMoveEventCount; + Vector2D m_vBeginDragXY; + Vector2D m_vLastDragXY; + Vector2D m_vBeginDragPositionXY; + Vector2D m_vBeginDragSizeXY; + Vector2D m_vDraggingWindowOriginalFloatSize; + eRectCorner m_eGrabbedCorner = CORNER_TOPLEFT; + + PHLWINDOWREF m_pLastTiledWindow; +}; diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp deleted file mode 100644 index 4a93809c..00000000 --- a/src/layout/LayoutManager.cpp +++ /dev/null @@ -1,345 +0,0 @@ -#include "LayoutManager.hpp" - -#include "space/Space.hpp" -#include "target/Target.hpp" - -#include "../config/ConfigManager.hpp" -#include "../Compositor.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../desktop/view/Group.hpp" -#include "../event/EventBus.hpp" - -using namespace Layout; - -CLayoutManager::CLayoutManager() = default; - -void CLayoutManager::newTarget(SP target, SP space) { - // on a new target: remember desired pos for float, if available - if (const auto DESIRED_GEOM = target->desiredGeometry(); DESIRED_GEOM) - target->rememberFloatingSize(DESIRED_GEOM->size); - - target->assignToSpace(space); -} - -void CLayoutManager::removeTarget(SP target) { - target->assignToSpace(nullptr); -} - -void CLayoutManager::changeFloatingMode(SP target) { - if (!target->space()) - return; - - target->space()->toggleTargetFloating(target); -} - -void CLayoutManager::beginDragTarget(SP target, eMouseBindMode mode) { - m_dragStateController->dragBegin(target, mode); -} - -void CLayoutManager::moveMouse(const Vector2D& mousePos) { - m_dragStateController->mouseMove(mousePos); -} - -void CLayoutManager::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { - if (target->isPseudo()) { - auto fixedΔ = Δ; - if (corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT) - fixedΔ.x = -fixedΔ.x; - if (corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT) - fixedΔ.y = -fixedΔ.y; - - auto newPseudoSize = target->pseudoSize() + fixedΔ; - const auto TARGET_TILE_SIZE = target->position().size(); - newPseudoSize.x = std::clamp(newPseudoSize.x, MIN_WINDOW_SIZE, TARGET_TILE_SIZE.x); - newPseudoSize.y = std::clamp(newPseudoSize.y, MIN_WINDOW_SIZE, TARGET_TILE_SIZE.y); - - target->setPseudoSize(newPseudoSize); - - return; - } - - target->space()->resizeTarget(Δ, target, corner); -} - -void CLayoutManager::setTargetGeom(const CBox& box, SP target) { - if (!target->floating()) - return; - - target->space()->setTargetGeom(box, target); -} - -std::expected CLayoutManager::layoutMsg(const std::string_view& sv) { - - const auto MONITOR = Desktop::focusState()->monitor(); - // forward to the active workspace - if (!MONITOR) - return std::unexpected("No monitor, can't find ws to target"); - - auto ws = MONITOR->m_activeSpecialWorkspace ? MONITOR->m_activeSpecialWorkspace : MONITOR->m_activeWorkspace; - - if (!ws) - return std::unexpected("No workspace, can't target"); - - return ws->m_space->layoutMsg(sv); -} - -void CLayoutManager::moveTarget(const Vector2D& Δ, SP target) { - if (!target->floating()) - return; - - target->space()->moveTarget(Δ, target); -} - -void CLayoutManager::endDragTarget() { - m_dragStateController->dragEnd(); -} - -void CLayoutManager::fullscreenRequestForTarget(SP target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) { - target->space()->setFullscreen(target, effectiveMode); -} - -void CLayoutManager::switchTargets(SP a, SP b, bool preserveFocus) { - - if (preserveFocus) { - a->swap(b); - return; - } - - const auto IS_A_ACTIVE = Desktop::focusState()->window() == a->window(); - const auto IS_B_ACTIVE = Desktop::focusState()->window() == b->window(); - - a->swap(b); - - if (IS_A_ACTIVE && b->window()) - Desktop::focusState()->fullWindowFocus(b->window(), Desktop::FOCUS_REASON_KEYBIND); - - if (IS_B_ACTIVE && a->window()) - Desktop::focusState()->fullWindowFocus(a->window(), Desktop::FOCUS_REASON_KEYBIND); -} - -void CLayoutManager::moveInDirection(SP target, const std::string& direction, bool silent) { - Math::eDirection dir = Math::fromChar(direction.at(0)); - if (dir == Math::DIRECTION_DEFAULT) { - Log::logger->log(Log::ERR, "invalid direction for moveInDirection: {}", direction); - return; - } - - target->space()->moveTargetInDirection(target, dir, silent); -} - -SP CLayoutManager::getNextCandidate(SP space, SP from) { - return space->getNextCandidate(from); -} - -bool CLayoutManager::isReachable(SP target) { - return true; -} - -void CLayoutManager::bringTargetToTop(SP target) { - if (!target) - return; - - if (target->window()->m_group) { - // grouped, change the current to this window - target->window()->m_group->setCurrent(target->window()); - } -} - -std::optional CLayoutManager::predictSizeForNewTiledTarget() { - const auto FOCUSED_MON = Desktop::focusState()->monitor(); - - if (!FOCUSED_MON || !FOCUSED_MON->m_activeWorkspace) - return std::nullopt; - - if (FOCUSED_MON->m_activeSpecialWorkspace) - return FOCUSED_MON->m_activeSpecialWorkspace->m_space->predictSizeForNewTiledTarget(); - - return FOCUSED_MON->m_activeWorkspace->m_space->predictSizeForNewTiledTarget(); -} - -const UP& CLayoutManager::dragController() { - return m_dragStateController; -} - -static inline bool canSnap(const double SIDEA, const double SIDEB, const double GAP) { - return std::abs(SIDEA - SIDEB) < GAP; -} - -static void snapMove(double& start, double& end, const double P) { - end = P + (end - start); - start = P; -} - -static void snapResize(double& start, double& end, const double P) { - start = P; -} - -using SnapFn = std::function; - -void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP DRAGGINGTARGET, const eMouseBindMode MODE, const int CORNER, const Vector2D& BEGINSIZE) { - - const auto DRAGGINGWINDOW = DRAGGINGTARGET->window(); - - if (!Desktop::View::validMapped(DRAGGINGWINDOW)) - return; - - static auto SNAPWINDOWGAP = CConfigValue("general:snap:window_gap"); - static auto SNAPMONITORGAP = CConfigValue("general:snap:monitor_gap"); - static auto SNAPBORDEROVERLAP = CConfigValue("general:snap:border_overlap"); - static auto SNAPRESPECTGAPS = CConfigValue("general:snap:respect_gaps"); - - static auto PGAPSIN = CConfigValue("general:gaps_in"); - static auto PGAPSOUT = CConfigValue("general:gaps_out"); - const auto GAPSNONE = CCssGapData{0, 0, 0, 0}; - - const SnapFn SNAP = (MODE == MBIND_MOVE) ? snapMove : snapResize; - int snaps = 0; - - struct SRange { - double start = 0; - double end = 0; - }; - const auto EXTENTS = DRAGGINGWINDOW->getWindowExtentsUnified(Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS); - SRange sourceX = {sourcePos.x - EXTENTS.topLeft.x, sourcePos.x + sourceSize.x + EXTENTS.bottomRight.x}; - SRange sourceY = {sourcePos.y - EXTENTS.topLeft.y, sourcePos.y + sourceSize.y + EXTENTS.bottomRight.y}; - - if (*SNAPWINDOWGAP) { - const double GAPSIZE = *SNAPWINDOWGAP; - const auto WSID = DRAGGINGWINDOW->workspaceID(); - const bool HASFULLSCREEN = DRAGGINGWINDOW->m_workspace && DRAGGINGWINDOW->m_workspace->m_hasFullscreenWindow; - - const auto* GAPSIN = *SNAPRESPECTGAPS ? sc(PGAPSIN.ptr()->getData()) : &GAPSNONE; - const double GAPSX = GAPSIN->m_left + GAPSIN->m_right; - const double GAPSY = GAPSIN->m_top + GAPSIN->m_bottom; - - for (auto& other : g_pCompositor->m_windows) { - if ((HASFULLSCREEN && !other->m_createdOverFullscreen) || other == DRAGGINGWINDOW || other->workspaceID() != WSID || !other->m_isMapped || other->m_fadingOut || - other->isX11OverrideRedirect()) - continue; - - const CBox SURF = other->getWindowBoxUnified(Desktop::View::RESERVED_EXTENTS); - const SRange SURFBX = {SURF.x - GAPSX, SURF.x + SURF.w + GAPSX}; - const SRange SURFBY = {SURF.y - GAPSY, SURF.y + SURF.h + GAPSY}; - - // only snap windows if their ranges overlap in the opposite axis - if (sourceY.start <= SURFBY.end && SURFBY.start <= sourceY.end) { - if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && canSnap(sourceX.start, SURFBX.end, GAPSIZE)) { - SNAP(sourceX.start, sourceX.end, SURFBX.end); - snaps |= SNAP_LEFT; - } else if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && canSnap(sourceX.end, SURFBX.start, GAPSIZE)) { - SNAP(sourceX.end, sourceX.start, SURFBX.start); - snaps |= SNAP_RIGHT; - } - } - if (sourceX.start <= SURFBX.end && SURFBX.start <= sourceX.end) { - if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && canSnap(sourceY.start, SURFBY.end, GAPSIZE)) { - SNAP(sourceY.start, sourceY.end, SURFBY.end); - snaps |= SNAP_UP; - } else if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && canSnap(sourceY.end, SURFBY.start, GAPSIZE)) { - SNAP(sourceY.end, sourceY.start, SURFBY.start); - snaps |= SNAP_DOWN; - } - } - - // corner snapping - if (sourceX.start == SURFBX.end || SURFBX.start == sourceX.end) { - const SRange SURFY = {SURFBY.start + GAPSY, SURFBY.end - GAPSY}; - if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && !(snaps & SNAP_UP) && canSnap(sourceY.start, SURFY.start, GAPSIZE)) { - SNAP(sourceY.start, sourceY.end, SURFY.start); - snaps |= SNAP_UP; - } else if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && !(snaps & SNAP_DOWN) && canSnap(sourceY.end, SURFY.end, GAPSIZE)) { - SNAP(sourceY.end, sourceY.start, SURFY.end); - snaps |= SNAP_DOWN; - } - } - if (sourceY.start == SURFBY.end || SURFBY.start == sourceY.end) { - const SRange SURFX = {SURFBX.start + GAPSX, SURFBX.end - GAPSX}; - if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && !(snaps & SNAP_LEFT) && canSnap(sourceX.start, SURFX.start, GAPSIZE)) { - SNAP(sourceX.start, sourceX.end, SURFX.start); - snaps |= SNAP_LEFT; - } else if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && !(snaps & SNAP_RIGHT) && canSnap(sourceX.end, SURFX.end, GAPSIZE)) { - SNAP(sourceX.end, sourceX.start, SURFX.end); - snaps |= SNAP_RIGHT; - } - } - } - } - - if (*SNAPMONITORGAP) { - const double GAPSIZE = *SNAPMONITORGAP; - const auto EXTENTNONE = SBoxExtents{{0, 0}, {0, 0}}; - const auto* EXTENTDIFF = *SNAPBORDEROVERLAP ? &EXTENTS : &EXTENTNONE; - const auto MON = DRAGGINGWINDOW->m_monitor.lock(); - - const auto* GAPSOUT = *SNAPRESPECTGAPS ? sc(PGAPSOUT.ptr()->getData()) : &GAPSNONE; - const auto WORK_AREA = Desktop::CReservedArea{GAPSOUT->m_top, GAPSOUT->m_right, GAPSOUT->m_bottom, GAPSOUT->m_left}.apply(MON->logicalBoxMinusReserved()); - - SRange monX = {WORK_AREA.x, WORK_AREA.x + WORK_AREA.w}; - SRange monY = {WORK_AREA.y, WORK_AREA.y + WORK_AREA.h}; - - const bool HAS_LEFT = MON->m_reservedArea.left() > 0; - const bool HAS_TOP = MON->m_reservedArea.top() > 0; - const bool HAS_BOTTOM = MON->m_reservedArea.bottom() > 0; - const bool HAS_RIGHT = MON->m_reservedArea.right() > 0; - - if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && - ((HAS_LEFT && canSnap(sourceX.start, monX.start, GAPSIZE)) || canSnap(sourceX.start, (monX.start -= MON->m_reservedArea.left() + EXTENTDIFF->topLeft.x), GAPSIZE))) { - SNAP(sourceX.start, sourceX.end, monX.start); - snaps |= SNAP_LEFT; - } - if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && - ((HAS_RIGHT && canSnap(sourceX.end, monX.end, GAPSIZE)) || canSnap(sourceX.end, (monX.end += MON->m_reservedArea.right() + EXTENTDIFF->bottomRight.x), GAPSIZE))) { - SNAP(sourceX.end, sourceX.start, monX.end); - snaps |= SNAP_RIGHT; - } - if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && - ((HAS_TOP && canSnap(sourceY.start, monY.start, GAPSIZE)) || canSnap(sourceY.start, (monY.start -= MON->m_reservedArea.top() + EXTENTDIFF->topLeft.y), GAPSIZE))) { - SNAP(sourceY.start, sourceY.end, monY.start); - snaps |= SNAP_UP; - } - if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && - ((HAS_BOTTOM && canSnap(sourceY.end, monY.end, GAPSIZE)) || canSnap(sourceY.end, (monY.end += MON->m_reservedArea.bottom() + EXTENTDIFF->bottomRight.y), GAPSIZE))) { - SNAP(sourceY.end, sourceY.start, monY.end); - snaps |= SNAP_DOWN; - } - } - - // remove extents from main surface - sourceX = {sourceX.start + EXTENTS.topLeft.x, sourceX.end - EXTENTS.bottomRight.x}; - sourceY = {sourceY.start + EXTENTS.topLeft.y, sourceY.end - EXTENTS.bottomRight.y}; - - if (MODE == MBIND_RESIZE_FORCE_RATIO) { - if ((CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && snaps & SNAP_LEFT) || (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && snaps & SNAP_RIGHT)) { - const double SIZEY = (sourceX.end - sourceX.start) * (BEGINSIZE.y / BEGINSIZE.x); - if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT)) - sourceY.start = sourceY.end - SIZEY; - else - sourceY.end = sourceY.start + SIZEY; - } else if ((CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && snaps & SNAP_UP) || (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && snaps & SNAP_DOWN)) { - const double SIZEX = (sourceY.end - sourceY.start) * (BEGINSIZE.x / BEGINSIZE.y); - if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT)) - sourceX.start = sourceX.end - SIZEX; - else - sourceX.end = sourceX.start + SIZEX; - } - } - - sourcePos = {sourceX.start, sourceY.start}; - sourceSize = {sourceX.end - sourceX.start, sourceY.end - sourceY.start}; -} - -void CLayoutManager::recalculateMonitor(PHLMONITOR m) { - if (m->m_activeSpecialWorkspace) - m->m_activeSpecialWorkspace->m_space->recalculate(); - if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(); -} - -void CLayoutManager::invalidateMonitorGeometries(PHLMONITOR m) { - for (const auto& ws : g_pCompositor->getWorkspaces()) { - if (ws && ws->m_monitor == m) { - ws->m_space->recheckWorkArea(); - ws->m_space->recalculate(); - } - } -} diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp deleted file mode 100644 index e99911d5..00000000 --- a/src/layout/LayoutManager.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include "../helpers/memory/Memory.hpp" -#include "../helpers/math/Math.hpp" -#include "../managers/input/InputManager.hpp" - -#include "supplementary/DragController.hpp" - -#include -#include - -enum eFullscreenMode : int8_t; - -namespace Layout { - class ITarget; - class CSpace; - - enum eRectCorner : uint8_t { - CORNER_NONE = 0, - CORNER_TOPLEFT = (1 << 0), - CORNER_TOPRIGHT = (1 << 1), - CORNER_BOTTOMRIGHT = (1 << 2), - CORNER_BOTTOMLEFT = (1 << 3), - }; - - inline eRectCorner cornerFromBox(const CBox& box, const Vector2D& pos) { - const auto CENTER = box.middle(); - - if (pos.x < CENTER.x) - return pos.y < CENTER.y ? CORNER_TOPLEFT : CORNER_BOTTOMLEFT; - return pos.y < CENTER.y ? CORNER_TOPRIGHT : CORNER_BOTTOMRIGHT; - } - - enum eSnapEdge : uint8_t { - SNAP_INVALID = 0, - SNAP_UP = (1 << 0), - SNAP_DOWN = (1 << 1), - SNAP_LEFT = (1 << 2), - SNAP_RIGHT = (1 << 3), - }; - - class CLayoutManager { - public: - CLayoutManager(); - ~CLayoutManager() = default; - - void newTarget(SP target, SP space); - void removeTarget(SP target); - - void changeFloatingMode(SP target); - - void beginDragTarget(SP target, eMouseBindMode mode); - void moveMouse(const Vector2D& mousePos); - void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - void moveTarget(const Vector2D& Δ, SP target); - void setTargetGeom(const CBox& box, SP target); // floats only - void endDragTarget(); - - std::expected layoutMsg(const std::string_view& sv); - - void fullscreenRequestForTarget(SP target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode); - - void switchTargets(SP a, SP b, bool preserveFocus = true); - - void moveInDirection(SP target, const std::string& direction, bool silent = false); - - SP getNextCandidate(SP space, SP from); - - bool isReachable(SP target); - - void bringTargetToTop(SP target); - - std::optional predictSizeForNewTiledTarget(); - - void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP target, eMouseBindMode mode, int corner, const Vector2D& beginSize); - - void invalidateMonitorGeometries(PHLMONITOR); - void recalculateMonitor(PHLMONITOR); - - const UP& dragController(); - - private: - UP m_dragStateController = makeUnique(); - }; -} - -inline UP g_layoutManager; \ No newline at end of file diff --git a/src/layout/MasterLayout.cpp b/src/layout/MasterLayout.cpp new file mode 100644 index 00000000..59a5b14f --- /dev/null +++ b/src/layout/MasterLayout.cpp @@ -0,0 +1,1480 @@ +#include "MasterLayout.hpp" +#include "../Compositor.hpp" +#include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include "config/ConfigDataValues.hpp" +#include +#include "../config/ConfigValue.hpp" +#include "../render/Renderer.hpp" +#include "../managers/input/InputManager.hpp" +#include "../managers/LayoutManager.hpp" +#include "../managers/EventManager.hpp" + +SMasterNodeData* CHyprMasterLayout::getNodeFromWindow(PHLWINDOW pWindow) { + for (auto& nd : m_lMasterNodesData) { + if (nd.pWindow.lock() == pWindow) + return &nd; + } + + return nullptr; +} + +int CHyprMasterLayout::getNodesOnWorkspace(const WORKSPACEID& ws) { + int no = 0; + for (auto const& n : m_lMasterNodesData) { + if (n.workspaceID == ws) + no++; + } + + return no; +} + +int CHyprMasterLayout::getMastersOnWorkspace(const WORKSPACEID& ws) { + int no = 0; + for (auto const& n : m_lMasterNodesData) { + if (n.workspaceID == ws && n.isMaster) + no++; + } + + return no; +} + +SMasterWorkspaceData* CHyprMasterLayout::getMasterWorkspaceData(const WORKSPACEID& ws) { + for (auto& n : m_lMasterWorkspacesData) { + if (n.workspaceID == ws) + return &n; + } + + //create on the fly if it doesn't exist yet + const auto PWORKSPACEDATA = &m_lMasterWorkspacesData.emplace_back(); + PWORKSPACEDATA->workspaceID = ws; + static auto PORIENTATION = CConfigValue("master:orientation"); + + if (*PORIENTATION == "top") + PWORKSPACEDATA->orientation = ORIENTATION_TOP; + else if (*PORIENTATION == "right") + PWORKSPACEDATA->orientation = ORIENTATION_RIGHT; + else if (*PORIENTATION == "bottom") + PWORKSPACEDATA->orientation = ORIENTATION_BOTTOM; + else if (*PORIENTATION == "center") + PWORKSPACEDATA->orientation = ORIENTATION_CENTER; + else + PWORKSPACEDATA->orientation = ORIENTATION_LEFT; + + return PWORKSPACEDATA; +} + +std::string CHyprMasterLayout::getLayoutName() { + return "Master"; +} + +SMasterNodeData* CHyprMasterLayout::getMasterNodeOnWorkspace(const WORKSPACEID& ws) { + for (auto& n : m_lMasterNodesData) { + if (n.isMaster && n.workspaceID == ws) + return &n; + } + + return nullptr; +} + +void CHyprMasterLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection direction) { + if (pWindow->m_bIsFloating) + return; + + static auto PNEWONACTIVE = CConfigValue("master:new_on_active"); + static auto PNEWONTOP = CConfigValue("master:new_on_top"); + static auto PNEWSTATUS = CConfigValue("master:new_status"); + + const auto PMONITOR = pWindow->m_pMonitor.lock(); + + const bool BNEWBEFOREACTIVE = *PNEWONACTIVE == "before"; + const bool BNEWISMASTER = *PNEWSTATUS == "master"; + + const auto PNODE = [&]() { + if (*PNEWONACTIVE != "none" && !BNEWISMASTER) { + const auto pLastNode = getNodeFromWindow(g_pCompositor->m_pLastWindow.lock()); + if (pLastNode && !(pLastNode->isMaster && (getMastersOnWorkspace(pWindow->workspaceID()) == 1 || *PNEWSTATUS == "slave"))) { + auto it = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *pLastNode); + if (!BNEWBEFOREACTIVE) + ++it; + return &(*m_lMasterNodesData.emplace(it)); + } + } + return *PNEWONTOP ? &m_lMasterNodesData.emplace_front() : &m_lMasterNodesData.emplace_back(); + }(); + + PNODE->workspaceID = pWindow->workspaceID(); + PNODE->pWindow = pWindow; + + const auto WINDOWSONWORKSPACE = getNodesOnWorkspace(PNODE->workspaceID); + static auto PMFACT = CConfigValue("master:mfact"); + float lastSplitPercent = *PMFACT; + + auto OPENINGON = isWindowTiled(g_pCompositor->m_pLastWindow.lock()) && g_pCompositor->m_pLastWindow->m_pWorkspace == pWindow->m_pWorkspace ? + getNodeFromWindow(g_pCompositor->m_pLastWindow.lock()) : + getMasterNodeOnWorkspace(pWindow->workspaceID()); + + const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); + static auto PDROPATCURSOR = CConfigValue("master:drop_at_cursor"); + eOrientation orientation = getDynamicOrientation(pWindow->m_pWorkspace); + const auto NODEIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *PNODE); + + bool forceDropAsMaster = false; + // if dragging window to move, drop it at the cursor position instead of bottom/top of stack + if (*PDROPATCURSOR && g_pInputManager->dragMode == MBIND_MOVE) { + if (WINDOWSONWORKSPACE > 2) { + for (auto it = m_lMasterNodesData.begin(); it != m_lMasterNodesData.end(); ++it) { + if (it->workspaceID != pWindow->workspaceID()) + continue; + const CBox box = it->pWindow->getWindowIdealBoundingBoxIgnoreReserved(); + if (box.containsPoint(MOUSECOORDS)) { + switch (orientation) { + case ORIENTATION_LEFT: + case ORIENTATION_RIGHT: + if (MOUSECOORDS.y > it->pWindow->middle().y) + ++it; + break; + case ORIENTATION_TOP: + case ORIENTATION_BOTTOM: + if (MOUSECOORDS.x > it->pWindow->middle().x) + ++it; + break; + case ORIENTATION_CENTER: break; + default: UNREACHABLE(); + } + m_lMasterNodesData.splice(it, m_lMasterNodesData, NODEIT); + break; + } + } + } else if (WINDOWSONWORKSPACE == 2) { + // when dropping as the second tiled window in the workspace, + // make it the master only if the cursor is on the master side of the screen + for (auto const& nd : m_lMasterNodesData) { + if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) { + switch (orientation) { + case ORIENTATION_LEFT: + case ORIENTATION_CENTER: + if (MOUSECOORDS.x < nd.pWindow->middle().x) + forceDropAsMaster = true; + break; + case ORIENTATION_RIGHT: + if (MOUSECOORDS.x > nd.pWindow->middle().x) + forceDropAsMaster = true; + break; + case ORIENTATION_TOP: + if (MOUSECOORDS.y < nd.pWindow->middle().y) + forceDropAsMaster = true; + break; + case ORIENTATION_BOTTOM: + if (MOUSECOORDS.y > nd.pWindow->middle().y) + forceDropAsMaster = true; + break; + default: UNREACHABLE(); + } + break; + } + } + } + } + + if ((BNEWISMASTER && g_pInputManager->dragMode != MBIND_MOVE) // + || WINDOWSONWORKSPACE == 1 // + || (WINDOWSONWORKSPACE > 2 && !pWindow->m_bFirstMap && OPENINGON->isMaster) // + || forceDropAsMaster // + || (*PNEWSTATUS == "inherit" && OPENINGON && OPENINGON->isMaster && g_pInputManager->dragMode != MBIND_MOVE)) { + + if (BNEWBEFOREACTIVE) { + for (auto& nd : m_lMasterNodesData | std::views::reverse) { + if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) { + nd.isMaster = false; + lastSplitPercent = nd.percMaster; + break; + } + } + } else { + for (auto& nd : m_lMasterNodesData) { + if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) { + nd.isMaster = false; + lastSplitPercent = nd.percMaster; + break; + } + } + } + + PNODE->isMaster = true; + PNODE->percMaster = lastSplitPercent; + + // first, check if it isn't too big. + if (const auto MAXSIZE = pWindow->requestedMaxSize(); MAXSIZE.x < PMONITOR->vecSize.x * lastSplitPercent || MAXSIZE.y < PMONITOR->vecSize.y) { + // we can't continue. make it floating. + pWindow->m_bIsFloating = true; + m_lMasterNodesData.remove(*PNODE); + g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(pWindow); + return; + } + } else { + PNODE->isMaster = false; + PNODE->percMaster = lastSplitPercent; + + // first, check if it isn't too big. + if (const auto MAXSIZE = pWindow->requestedMaxSize(); + MAXSIZE.x < PMONITOR->vecSize.x * (1 - lastSplitPercent) || MAXSIZE.y < PMONITOR->vecSize.y * (1.f / (WINDOWSONWORKSPACE - 1))) { + // we can't continue. make it floating. + pWindow->m_bIsFloating = true; + m_lMasterNodesData.remove(*PNODE); + g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(pWindow); + return; + } + } + + // recalc + recalculateMonitor(pWindow->monitorID()); +} + +void CHyprMasterLayout::onWindowRemovedTiling(PHLWINDOW pWindow) { + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE) + return; + + const auto WORKSPACEID = PNODE->workspaceID; + const auto MASTERSLEFT = getMastersOnWorkspace(WORKSPACEID); + static auto SMALLSPLIT = CConfigValue("master:allow_small_split"); + + pWindow->unsetWindowData(PRIORITY_LAYOUT); + pWindow->updateWindowData(); + + if (pWindow->isFullscreen()) + g_pCompositor->setWindowFullscreenInternal(pWindow, FSMODE_NONE); + + if (PNODE->isMaster && (MASTERSLEFT <= 1 || *SMALLSPLIT == 1)) { + // find a new master from top of the list + for (auto& nd : m_lMasterNodesData) { + if (!nd.isMaster && nd.workspaceID == WORKSPACEID) { + nd.isMaster = true; + nd.percMaster = PNODE->percMaster; + break; + } + } + } + + m_lMasterNodesData.remove(*PNODE); + + if (getMastersOnWorkspace(WORKSPACEID) == getNodesOnWorkspace(WORKSPACEID) && MASTERSLEFT > 1) { + for (auto& nd : m_lMasterNodesData | std::views::reverse) { + if (nd.workspaceID == WORKSPACEID) { + nd.isMaster = false; + break; + } + } + } + // BUGFIX: correct bug where closing one master in a stack of 2 would leave + // the screen half bare, and make it difficult to select remaining window + if (getNodesOnWorkspace(WORKSPACEID) == 1) { + for (auto& nd : m_lMasterNodesData) { + if (nd.workspaceID == WORKSPACEID && !nd.isMaster) { + nd.isMaster = true; + break; + } + } + } + recalculateMonitor(pWindow->monitorID()); +} + +void CHyprMasterLayout::recalculateMonitor(const MONITORID& monid) { + const auto PMONITOR = g_pCompositor->getMonitorFromID(monid); + + if (!PMONITOR || !PMONITOR->activeWorkspace) + return; + + g_pHyprRenderer->damageMonitor(PMONITOR); + + if (PMONITOR->activeSpecialWorkspace) + calculateWorkspace(PMONITOR->activeSpecialWorkspace); + + calculateWorkspace(PMONITOR->activeWorkspace); +} + +void CHyprMasterLayout::calculateWorkspace(PHLWORKSPACE pWorkspace) { + const auto PMONITOR = pWorkspace->m_pMonitor.lock(); + + if (!PMONITOR) + return; + + if (pWorkspace->m_bHasFullscreenWindow) { + // massive hack from the fullscreen func + const auto PFULLWINDOW = pWorkspace->getFullscreenWindow(); + + if (pWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) { + *PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition; + *PFULLWINDOW->m_vRealSize = PMONITOR->vecSize; + } else if (pWorkspace->m_efFullscreenMode == FSMODE_MAXIMIZED) { + SMasterNodeData fakeNode; + fakeNode.pWindow = PFULLWINDOW; + fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft; + fakeNode.size = PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight; + fakeNode.workspaceID = pWorkspace->m_iID; + PFULLWINDOW->m_vPosition = fakeNode.position; + PFULLWINDOW->m_vSize = fakeNode.size; + fakeNode.ignoreFullscreenChecks = true; + + applyNodeDataToWindow(&fakeNode); + } + + // if has fullscreen, don't calculate the rest + return; + } + + const auto PMASTERNODE = getMasterNodeOnWorkspace(pWorkspace->m_iID); + + if (!PMASTERNODE) + return; + + eOrientation orientation = getDynamicOrientation(pWorkspace); + bool centerMasterWindow = false; + static auto SLAVECOUNTFORCENTER = CConfigValue("master:slave_count_for_center_master"); + static auto CMSLAVESONRIGHT = CConfigValue("master:center_master_slaves_on_right"); + static auto PIGNORERESERVED = CConfigValue("master:center_ignores_reserved"); + static auto PSMARTRESIZING = CConfigValue("master:smart_resizing"); + + const auto MASTERS = getMastersOnWorkspace(pWorkspace->m_iID); + const auto WINDOWS = getNodesOnWorkspace(pWorkspace->m_iID); + const auto STACKWINDOWS = WINDOWS - MASTERS; + const auto WSSIZE = PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight; + const auto WSPOS = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft; + + if (orientation == ORIENTATION_CENTER) { + if (STACKWINDOWS >= *SLAVECOUNTFORCENTER) { + centerMasterWindow = true; + } else { + if (*CMSLAVESONRIGHT) + orientation = ORIENTATION_LEFT; + else + orientation = ORIENTATION_RIGHT; + } + } + + const float totalSize = (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) ? WSSIZE.x : WSSIZE.y; + const float masterAverageSize = totalSize / MASTERS; + const float slaveAverageSize = totalSize / STACKWINDOWS; + float masterAccumulatedSize = 0; + float slaveAccumulatedSize = 0; + + if (*PSMARTRESIZING) { + // check the total width and height so that later + // if larger/smaller than screen size them down/up + for (auto const& nd : m_lMasterNodesData) { + if (nd.workspaceID == pWorkspace->m_iID) { + if (nd.isMaster) + masterAccumulatedSize += totalSize / MASTERS * nd.percSize; + else + slaveAccumulatedSize += totalSize / STACKWINDOWS * nd.percSize; + } + } + } + + // compute placement of master window(s) + if (WINDOWS == 1 && !centerMasterWindow) { + static auto PALWAYSKEEPPOSITION = CConfigValue("master:always_keep_position"); + if (*PALWAYSKEEPPOSITION) { + const float WIDTH = WSSIZE.x * PMASTERNODE->percMaster; + float nextX = 0; + + if (orientation == ORIENTATION_RIGHT) + nextX = WSSIZE.x - WIDTH; + else if (orientation == ORIENTATION_CENTER) + nextX = (WSSIZE.x - WIDTH) / 2; + + PMASTERNODE->size = Vector2D(WIDTH, WSSIZE.y); + PMASTERNODE->position = WSPOS + Vector2D((double)nextX, 0.0); + } else { + PMASTERNODE->size = WSSIZE; + PMASTERNODE->position = WSPOS; + } + + applyNodeDataToWindow(PMASTERNODE); + return; + } else if (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) { + const float HEIGHT = STACKWINDOWS != 0 ? WSSIZE.y * PMASTERNODE->percMaster : WSSIZE.y; + float widthLeft = WSSIZE.x; + int mastersLeft = MASTERS; + float nextX = 0; + float nextY = 0; + + if (orientation == ORIENTATION_BOTTOM) + nextY = WSSIZE.y - HEIGHT; + + for (auto& nd : m_lMasterNodesData) { + if (nd.workspaceID != pWorkspace->m_iID || !nd.isMaster) + continue; + + float WIDTH = mastersLeft > 1 ? widthLeft / mastersLeft * nd.percSize : widthLeft; + if (WIDTH > widthLeft * 0.9f && mastersLeft > 1) + WIDTH = widthLeft * 0.9f; + + if (*PSMARTRESIZING) { + nd.percSize *= WSSIZE.x / masterAccumulatedSize; + WIDTH = masterAverageSize * nd.percSize; + } + + nd.size = Vector2D(WIDTH, HEIGHT); + nd.position = WSPOS + Vector2D(nextX, nextY); + applyNodeDataToWindow(&nd); + + mastersLeft--; + widthLeft -= WIDTH; + nextX += WIDTH; + } + } else { // orientation left, right or center + float WIDTH = *PIGNORERESERVED && centerMasterWindow ? PMONITOR->vecSize.x : WSSIZE.x; + float heightLeft = WSSIZE.y; + int mastersLeft = MASTERS; + float nextX = 0; + float nextY = 0; + + if (STACKWINDOWS > 0 || centerMasterWindow) + WIDTH *= PMASTERNODE->percMaster; + + if (orientation == ORIENTATION_RIGHT) { + nextX = WSSIZE.x - WIDTH; + } else if (centerMasterWindow) { + nextX = ((*PIGNORERESERVED && centerMasterWindow ? PMONITOR->vecSize.x : WSSIZE.x) - WIDTH) / 2; + } + + for (auto& nd : m_lMasterNodesData) { + if (nd.workspaceID != pWorkspace->m_iID || !nd.isMaster) + continue; + + float HEIGHT = mastersLeft > 1 ? heightLeft / mastersLeft * nd.percSize : heightLeft; + if (HEIGHT > heightLeft * 0.9f && mastersLeft > 1) + HEIGHT = heightLeft * 0.9f; + + if (*PSMARTRESIZING) { + nd.percSize *= WSSIZE.y / masterAccumulatedSize; + HEIGHT = masterAverageSize * nd.percSize; + } + + nd.size = Vector2D(WIDTH, HEIGHT); + nd.position = (*PIGNORERESERVED && centerMasterWindow ? PMONITOR->vecPosition : WSPOS) + Vector2D(nextX, nextY); + applyNodeDataToWindow(&nd); + + mastersLeft--; + heightLeft -= HEIGHT; + nextY += HEIGHT; + } + } + + if (STACKWINDOWS == 0) + return; + + // compute placement of slave window(s) + int slavesLeft = STACKWINDOWS; + if (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) { + const float HEIGHT = WSSIZE.y - PMASTERNODE->size.y; + float widthLeft = WSSIZE.x; + float nextX = 0; + float nextY = 0; + + if (orientation == ORIENTATION_TOP) + nextY = PMASTERNODE->size.y; + + for (auto& nd : m_lMasterNodesData) { + if (nd.workspaceID != pWorkspace->m_iID || nd.isMaster) + continue; + + float WIDTH = slavesLeft > 1 ? widthLeft / slavesLeft * nd.percSize : widthLeft; + if (WIDTH > widthLeft * 0.9f && slavesLeft > 1) + WIDTH = widthLeft * 0.9f; + + if (*PSMARTRESIZING) { + nd.percSize *= WSSIZE.x / slaveAccumulatedSize; + WIDTH = slaveAverageSize * nd.percSize; + } + + nd.size = Vector2D(WIDTH, HEIGHT); + nd.position = WSPOS + Vector2D(nextX, nextY); + applyNodeDataToWindow(&nd); + + slavesLeft--; + widthLeft -= WIDTH; + nextX += WIDTH; + } + } else if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT) { + const float WIDTH = WSSIZE.x - PMASTERNODE->size.x; + float heightLeft = WSSIZE.y; + float nextY = 0; + float nextX = 0; + + if (orientation == ORIENTATION_LEFT) + nextX = PMASTERNODE->size.x; + + for (auto& nd : m_lMasterNodesData) { + if (nd.workspaceID != pWorkspace->m_iID || nd.isMaster) + continue; + + float HEIGHT = slavesLeft > 1 ? heightLeft / slavesLeft * nd.percSize : heightLeft; + if (HEIGHT > heightLeft * 0.9f && slavesLeft > 1) + HEIGHT = heightLeft * 0.9f; + + if (*PSMARTRESIZING) { + nd.percSize *= WSSIZE.y / slaveAccumulatedSize; + HEIGHT = slaveAverageSize * nd.percSize; + } + + nd.size = Vector2D(WIDTH, HEIGHT); + nd.position = WSPOS + Vector2D(nextX, nextY); + applyNodeDataToWindow(&nd); + + slavesLeft--; + heightLeft -= HEIGHT; + nextY += HEIGHT; + } + } else { // slaves for centered master window(s) + const float WIDTH = ((*PIGNORERESERVED ? PMONITOR->vecSize.x : WSSIZE.x) - PMASTERNODE->size.x) / 2.0; + float heightLeft = 0; + float heightLeftL = WSSIZE.y; + float heightLeftR = WSSIZE.y; + float nextX = 0; + float nextY = 0; + float nextYL = 0; + float nextYR = 0; + bool onRight = *CMSLAVESONRIGHT; + int slavesLeftL = 1 + (slavesLeft - 1) / 2; + int slavesLeftR = slavesLeft - slavesLeftL; + + if (*CMSLAVESONRIGHT) { + slavesLeftR = 1 + (slavesLeft - 1) / 2; + slavesLeftL = slavesLeft - slavesLeftR; + } + + const float slaveAverageHeightL = WSSIZE.y / slavesLeftL; + const float slaveAverageHeightR = WSSIZE.y / slavesLeftR; + float slaveAccumulatedHeightL = 0; + float slaveAccumulatedHeightR = 0; + + if (*PSMARTRESIZING) { + for (auto const& nd : m_lMasterNodesData) { + if (nd.workspaceID != pWorkspace->m_iID || nd.isMaster) + continue; + + if (onRight) { + slaveAccumulatedHeightR += slaveAverageHeightR * nd.percSize; + } else { + slaveAccumulatedHeightL += slaveAverageHeightL * nd.percSize; + } + onRight = !onRight; + } + + onRight = *CMSLAVESONRIGHT; + } + + for (auto& nd : m_lMasterNodesData) { + if (nd.workspaceID != pWorkspace->m_iID || nd.isMaster) + continue; + + if (onRight) { + nextX = WIDTH + PMASTERNODE->size.x - (*PIGNORERESERVED ? PMONITOR->vecReservedTopLeft.x : 0); + nextY = nextYR; + heightLeft = heightLeftR; + slavesLeft = slavesLeftR; + } else { + nextX = 0; + nextY = nextYL; + heightLeft = heightLeftL; + slavesLeft = slavesLeftL; + } + + float HEIGHT = slavesLeft > 1 ? heightLeft / slavesLeft * nd.percSize : heightLeft; + if (HEIGHT > heightLeft * 0.9f && slavesLeft > 1) + HEIGHT = heightLeft * 0.9f; + + if (*PSMARTRESIZING) { + if (onRight) { + nd.percSize *= WSSIZE.y / slaveAccumulatedHeightR; + HEIGHT = slaveAverageHeightR * nd.percSize; + } else { + nd.percSize *= WSSIZE.y / slaveAccumulatedHeightL; + HEIGHT = slaveAverageHeightL * nd.percSize; + } + } + + nd.size = Vector2D(*PIGNORERESERVED ? (WIDTH - (onRight ? PMONITOR->vecReservedBottomRight.x : PMONITOR->vecReservedTopLeft.x)) : WIDTH, HEIGHT); + nd.position = WSPOS + Vector2D(nextX, nextY); + applyNodeDataToWindow(&nd); + + if (onRight) { + heightLeftR -= HEIGHT; + nextYR += HEIGHT; + slavesLeftR--; + } else { + heightLeftL -= HEIGHT; + nextYL += HEIGHT; + slavesLeftL--; + } + + onRight = !onRight; + } + } +} + +void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) { + PHLMONITOR PMONITOR = nullptr; + + if (g_pCompositor->isWorkspaceSpecial(pNode->workspaceID)) { + for (auto const& m : g_pCompositor->m_vMonitors) { + if (m->activeSpecialWorkspaceID() == pNode->workspaceID) { + PMONITOR = m; + break; + } + } + } else + PMONITOR = g_pCompositor->getWorkspaceByID(pNode->workspaceID)->m_pMonitor.lock(); + + if (!PMONITOR) { + Debug::log(ERR, "Orphaned Node {}!!", pNode); + return; + } + + // for gaps outer + const bool DISPLAYLEFT = STICKS(pNode->position.x, PMONITOR->vecPosition.x + PMONITOR->vecReservedTopLeft.x); + const bool DISPLAYRIGHT = STICKS(pNode->position.x + pNode->size.x, PMONITOR->vecPosition.x + PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x); + const bool DISPLAYTOP = STICKS(pNode->position.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y); + const bool DISPLAYBOTTOM = STICKS(pNode->position.y + pNode->size.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y); + + const auto PWINDOW = pNode->pWindow.lock(); + // get specific gaps and rules for this workspace, + // if user specified them in config + const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(PWINDOW->m_pWorkspace); + + if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks) + return; + + PWINDOW->unsetWindowData(PRIORITY_LAYOUT); + PWINDOW->updateWindowData(); + + static auto PANIMATE = CConfigValue("misc:animate_manual_resizes"); + static auto PGAPSINDATA = CConfigValue("general:gaps_in"); + static auto PGAPSOUTDATA = CConfigValue("general:gaps_out"); + auto* PGAPSIN = (CCssGapData*)(PGAPSINDATA.ptr())->getData(); + auto* PGAPSOUT = (CCssGapData*)(PGAPSOUTDATA.ptr())->getData(); + + auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN); + auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT); + + if (!validMapped(PWINDOW)) { + Debug::log(ERR, "Node {} holding invalid {}!!", pNode, PWINDOW); + return; + } + + PWINDOW->m_vSize = pNode->size; + PWINDOW->m_vPosition = pNode->position; + + PWINDOW->updateWindowDecos(); + + auto calcPos = PWINDOW->m_vPosition; + auto calcSize = PWINDOW->m_vSize; + + const auto OFFSETTOPLEFT = Vector2D((double)(DISPLAYLEFT ? gapsOut.left : gapsIn.left), (double)(DISPLAYTOP ? gapsOut.top : gapsIn.top)); + + const auto OFFSETBOTTOMRIGHT = Vector2D((double)(DISPLAYRIGHT ? gapsOut.right : gapsIn.right), (double)(DISPLAYBOTTOM ? gapsOut.bottom : gapsIn.bottom)); + + calcPos = calcPos + OFFSETTOPLEFT; + calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT; + + const auto RESERVED = PWINDOW->getFullWindowReservedArea(); + calcPos = calcPos + RESERVED.topLeft; + calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight); + + if (PWINDOW->onSpecialWorkspace() && !PWINDOW->isFullscreen()) { + static auto PSCALEFACTOR = CConfigValue("master:special_scale_factor"); + + CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR}; + wb.round(); // avoid rounding mess + + *PWINDOW->m_vRealPosition = wb.pos(); + *PWINDOW->m_vRealSize = wb.size(); + } else { + CBox wb = {calcPos, calcSize}; + wb.round(); // avoid rounding mess + + *PWINDOW->m_vRealPosition = wb.pos(); + *PWINDOW->m_vRealSize = wb.size(); + } + + if (m_bForceWarps && !*PANIMATE) { + g_pHyprRenderer->damageWindow(PWINDOW); + + PWINDOW->m_vRealPosition->warp(); + PWINDOW->m_vRealSize->warp(); + + g_pHyprRenderer->damageWindow(PWINDOW); + } + + PWINDOW->updateWindowDecos(); +} + +bool CHyprMasterLayout::isWindowTiled(PHLWINDOW pWindow) { + return getNodeFromWindow(pWindow) != nullptr; +} + +void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorner corner, PHLWINDOW pWindow) { + const auto PWINDOW = pWindow ? pWindow : g_pCompositor->m_pLastWindow.lock(); + + if (!validMapped(PWINDOW)) + return; + + const auto PNODE = getNodeFromWindow(PWINDOW); + + if (!PNODE) { + *PWINDOW->m_vRealSize = + (PWINDOW->m_vRealSize->goal() + pixResize) + .clamp(PWINDOW->m_sWindowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), PWINDOW->m_sWindowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY})); + PWINDOW->updateWindowDecos(); + return; + } + + const auto PMONITOR = PWINDOW->m_pMonitor.lock(); + static auto SLAVECOUNTFORCENTER = CConfigValue("master:slave_count_for_center_master"); + static auto PSMARTRESIZING = CConfigValue("master:smart_resizing"); + + const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_vPosition.y + PWINDOW->m_vSize.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y); + const bool DISPLAYRIGHT = STICKS(PWINDOW->m_vPosition.x + PWINDOW->m_vSize.x, PMONITOR->vecPosition.x + PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x); + const bool DISPLAYTOP = STICKS(PWINDOW->m_vPosition.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y); + const bool DISPLAYLEFT = STICKS(PWINDOW->m_vPosition.x, PMONITOR->vecPosition.x + PMONITOR->vecReservedTopLeft.x); + + const bool LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT; + const bool TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT; + const bool NONE = corner == CORNER_NONE; + + const auto MASTERS = getMastersOnWorkspace(PNODE->workspaceID); + const auto WINDOWS = getNodesOnWorkspace(PNODE->workspaceID); + const auto STACKWINDOWS = WINDOWS - MASTERS; + + eOrientation orientation = getDynamicOrientation(PWINDOW->m_pWorkspace); + bool centered = orientation == ORIENTATION_CENTER && (STACKWINDOWS >= *SLAVECOUNTFORCENTER); + double delta = 0; + + if (getNodesOnWorkspace(PWINDOW->workspaceID()) == 1 && !centered) + return; + + m_bForceWarps = true; + + switch (orientation) { + case ORIENTATION_LEFT: delta = pixResize.x / PMONITOR->vecSize.x; break; + case ORIENTATION_RIGHT: delta = -pixResize.x / PMONITOR->vecSize.x; break; + case ORIENTATION_BOTTOM: delta = -pixResize.y / PMONITOR->vecSize.y; break; + case ORIENTATION_TOP: delta = pixResize.y / PMONITOR->vecSize.y; break; + case ORIENTATION_CENTER: + delta = pixResize.x / PMONITOR->vecSize.x; + if (STACKWINDOWS >= *SLAVECOUNTFORCENTER) { + if (!NONE || !PNODE->isMaster) + delta *= 2; + if ((!PNODE->isMaster && DISPLAYLEFT) || (PNODE->isMaster && LEFT && *PSMARTRESIZING)) + delta = -delta; + } + break; + default: UNREACHABLE(); + } + + const auto workspaceIdForResizing = PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspaceID() : PMONITOR->activeWorkspaceID(); + for (auto& n : m_lMasterNodesData) { + if (n.isMaster && n.workspaceID == workspaceIdForResizing) + n.percMaster = std::clamp(n.percMaster + delta, 0.05, 0.95); + } + + // check the up/down resize + const bool isStackVertical = orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT || orientation == ORIENTATION_CENTER; + + const auto RESIZEDELTA = isStackVertical ? pixResize.y : pixResize.x; + const auto WSSIZE = PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight; + + auto nodesInSameColumn = PNODE->isMaster ? MASTERS : STACKWINDOWS; + if (orientation == ORIENTATION_CENTER && !PNODE->isMaster) + nodesInSameColumn = DISPLAYRIGHT ? (nodesInSameColumn + 1) / 2 : nodesInSameColumn / 2; + + const auto SIZE = isStackVertical ? WSSIZE.y / nodesInSameColumn : WSSIZE.x / nodesInSameColumn; + + if (RESIZEDELTA != 0 && nodesInSameColumn > 1) { + if (!*PSMARTRESIZING) { + PNODE->percSize = std::clamp(PNODE->percSize + RESIZEDELTA / SIZE, 0.05, 1.95); + } else { + const auto NODEIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *PNODE); + const auto REVNODEIT = std::find(m_lMasterNodesData.rbegin(), m_lMasterNodesData.rend(), *PNODE); + + const float totalSize = isStackVertical ? WSSIZE.y : WSSIZE.x; + const float minSize = totalSize / nodesInSameColumn * 0.2; + const bool resizePrevNodes = isStackVertical ? (TOP || DISPLAYBOTTOM) && !DISPLAYTOP : (LEFT || DISPLAYRIGHT) && !DISPLAYLEFT; + + int nodesLeft = 0; + float sizeLeft = 0; + int nodeCount = 0; + // check the sizes of all the nodes to be resized for later calculation + auto checkNodesLeft = [&sizeLeft, &nodesLeft, orientation, isStackVertical, &nodeCount, PNODE](auto it) { + if (it.isMaster != PNODE->isMaster || it.workspaceID != PNODE->workspaceID) + return; + nodeCount++; + if (!it.isMaster && orientation == ORIENTATION_CENTER && nodeCount % 2 == 1) + return; + sizeLeft += isStackVertical ? it.size.y : it.size.x; + nodesLeft++; + }; + float resizeDiff; + if (resizePrevNodes) { + std::for_each(std::next(REVNODEIT), m_lMasterNodesData.rend(), checkNodesLeft); + resizeDiff = -RESIZEDELTA; + } else { + std::for_each(std::next(NODEIT), m_lMasterNodesData.end(), checkNodesLeft); + resizeDiff = RESIZEDELTA; + } + + const float nodeSize = isStackVertical ? PNODE->size.y : PNODE->size.x; + const float maxSizeIncrease = sizeLeft - nodesLeft * minSize; + const float maxSizeDecrease = minSize - nodeSize; + + // leaves enough room for the other nodes + resizeDiff = std::clamp(resizeDiff, maxSizeDecrease, maxSizeIncrease); + PNODE->percSize += resizeDiff / SIZE; + + // resize the other nodes + nodeCount = 0; + auto resizeNodesLeft = [maxSizeIncrease, resizeDiff, minSize, orientation, isStackVertical, SIZE, &nodeCount, nodesLeft, PNODE](auto& it) { + if (it.isMaster != PNODE->isMaster || it.workspaceID != PNODE->workspaceID) + return; + nodeCount++; + // if center orientation, only resize when on the same side + if (!it.isMaster && orientation == ORIENTATION_CENTER && nodeCount % 2 == 1) + return; + const float size = isStackVertical ? it.size.y : it.size.x; + const float resizeDeltaForEach = maxSizeIncrease != 0 ? resizeDiff * (size - minSize) / maxSizeIncrease : resizeDiff / nodesLeft; + it.percSize -= resizeDeltaForEach / SIZE; + }; + if (resizePrevNodes) { + std::for_each(std::next(REVNODEIT), m_lMasterNodesData.rend(), resizeNodesLeft); + } else { + std::for_each(std::next(NODEIT), m_lMasterNodesData.end(), resizeNodesLeft); + } + } + } + + recalculateMonitor(PMONITOR->ID); + + m_bForceWarps = false; +} + +void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) { + const auto PMONITOR = pWindow->m_pMonitor.lock(); + const auto PWORKSPACE = pWindow->m_pWorkspace; + + // save position and size if floating + if (pWindow->m_bIsFloating && CURRENT_EFFECTIVE_MODE == FSMODE_NONE) { + pWindow->m_vLastFloatingSize = pWindow->m_vRealSize->goal(); + pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition->goal(); + pWindow->m_vPosition = pWindow->m_vRealPosition->goal(); + pWindow->m_vSize = pWindow->m_vRealSize->goal(); + } + + if (EFFECTIVE_MODE == FSMODE_NONE) { + // if it got its fullscreen disabled, set back its node if it had one + const auto PNODE = getNodeFromWindow(pWindow); + if (PNODE) + applyNodeDataToWindow(PNODE); + else { + // get back its' dimensions from position and size + *pWindow->m_vRealPosition = pWindow->m_vLastFloatingPosition; + *pWindow->m_vRealSize = pWindow->m_vLastFloatingSize; + + pWindow->unsetWindowData(PRIORITY_LAYOUT); + pWindow->updateWindowData(); + } + } else { + // apply new pos and size being monitors' box + if (EFFECTIVE_MODE == FSMODE_FULLSCREEN) { + *pWindow->m_vRealPosition = PMONITOR->vecPosition; + *pWindow->m_vRealSize = PMONITOR->vecSize; + } else { + // This is a massive hack. + // We make a fake "only" node and apply + // To keep consistent with the settings without C+P code + + SMasterNodeData fakeNode; + fakeNode.pWindow = pWindow; + fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft; + fakeNode.size = PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight; + fakeNode.workspaceID = pWindow->workspaceID(); + pWindow->m_vPosition = fakeNode.position; + pWindow->m_vSize = fakeNode.size; + fakeNode.ignoreFullscreenChecks = true; + + applyNodeDataToWindow(&fakeNode); + } + } + + g_pCompositor->changeWindowZOrder(pWindow, true); +} + +void CHyprMasterLayout::recalculateWindow(PHLWINDOW pWindow) { + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE) + return; + + recalculateMonitor(pWindow->monitorID()); +} + +SWindowRenderLayoutHints CHyprMasterLayout::requestRenderHints(PHLWINDOW pWindow) { + // window should be valid, insallah + + SWindowRenderLayoutHints hints; + + return hints; // master doesnt have any hints +} + +void CHyprMasterLayout::moveWindowTo(PHLWINDOW pWindow, const std::string& dir, bool silent) { + if (!isDirection(dir)) + return; + + const auto PWINDOW2 = g_pCompositor->getWindowInDirection(pWindow, dir[0]); + + if (!PWINDOW2) + return; + + pWindow->setAnimationsToMove(); + + if (pWindow->m_pWorkspace != PWINDOW2->m_pWorkspace) { + // if different monitors, send to monitor + onWindowRemovedTiling(pWindow); + pWindow->moveToWorkspace(PWINDOW2->m_pWorkspace); + pWindow->m_pMonitor = PWINDOW2->m_pMonitor; + if (!silent) { + const auto pMonitor = pWindow->m_pMonitor.lock(); + g_pCompositor->setActiveMonitor(pMonitor); + } + onWindowCreatedTiling(pWindow); + } else { + // if same monitor, switch windows + switchWindows(pWindow, PWINDOW2); + if (silent) + g_pCompositor->focusWindow(PWINDOW2); + } + + pWindow->updateGroupOutputs(); + if (!pWindow->m_sGroupData.pNextWindow.expired()) { + PHLWINDOW next = pWindow->m_sGroupData.pNextWindow.lock(); + while (next != pWindow) { + next->updateToplevel(); + next = next->m_sGroupData.pNextWindow.lock(); + } + } +} + +void CHyprMasterLayout::switchWindows(PHLWINDOW pWindow, PHLWINDOW pWindow2) { + // windows should be valid, insallah + + const auto PNODE = getNodeFromWindow(pWindow); + const auto PNODE2 = getNodeFromWindow(pWindow2); + + if (!PNODE2 || !PNODE) + return; + + if (PNODE->workspaceID != PNODE2->workspaceID) { + std::swap(pWindow2->m_pMonitor, pWindow->m_pMonitor); + std::swap(pWindow2->m_pWorkspace, pWindow->m_pWorkspace); + } + + // massive hack: just swap window pointers, lol + PNODE->pWindow = pWindow2; + PNODE2->pWindow = pWindow; + + pWindow->setAnimationsToMove(); + pWindow2->setAnimationsToMove(); + + recalculateMonitor(pWindow->monitorID()); + if (PNODE2->workspaceID != PNODE->workspaceID) + recalculateMonitor(pWindow2->monitorID()); + + g_pHyprRenderer->damageWindow(pWindow); + g_pHyprRenderer->damageWindow(pWindow2); +} + +void CHyprMasterLayout::alterSplitRatio(PHLWINDOW pWindow, float ratio, bool exact) { + // window should be valid, insallah + + const auto PNODE = getNodeFromWindow(pWindow); + + if (!PNODE) + return; + + const auto PMASTER = getMasterNodeOnWorkspace(pWindow->workspaceID()); + + float newRatio = exact ? ratio : PMASTER->percMaster + ratio; + PMASTER->percMaster = std::clamp(newRatio, 0.05f, 0.95f); + + recalculateMonitor(pWindow->monitorID()); +} + +PHLWINDOW CHyprMasterLayout::getNextWindow(PHLWINDOW pWindow, bool next, bool loop) { + if (!isWindowTiled(pWindow)) + return nullptr; + + const auto PNODE = getNodeFromWindow(pWindow); + + auto nodes = m_lMasterNodesData; + if (!next) + std::reverse(nodes.begin(), nodes.end()); + + const auto NODEIT = std::find(nodes.begin(), nodes.end(), *PNODE); + + const bool ISMASTER = PNODE->isMaster; + + auto CANDIDATE = std::find_if(NODEIT, nodes.end(), [&](const auto& other) { return other != *PNODE && ISMASTER == other.isMaster && other.workspaceID == PNODE->workspaceID; }); + if (CANDIDATE == nodes.end()) + CANDIDATE = + std::find_if(nodes.begin(), nodes.end(), [&](const auto& other) { return other != *PNODE && ISMASTER != other.isMaster && other.workspaceID == PNODE->workspaceID; }); + + if (CANDIDATE != nodes.end() && !loop) { + if (CANDIDATE->isMaster && next) + return nullptr; + if (!CANDIDATE->isMaster && ISMASTER && !next) + return nullptr; + } + + return CANDIDATE == nodes.end() ? nullptr : CANDIDATE->pWindow.lock(); +} + +std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::string message) { + auto switchToWindow = [&](PHLWINDOW PWINDOWTOCHANGETO) { + if (!validMapped(PWINDOWTOCHANGETO)) + return; + + if (header.pWindow->isFullscreen()) { + const auto PWORKSPACE = header.pWindow->m_pWorkspace; + const auto FSMODE = header.pWindow->m_sFullscreenState.internal; + static auto INHERITFULLSCREEN = CConfigValue("master:inherit_fullscreen"); + g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE); + g_pCompositor->focusWindow(PWINDOWTOCHANGETO); + if (*INHERITFULLSCREEN) + g_pCompositor->setWindowFullscreenInternal(PWINDOWTOCHANGETO, FSMODE); + } else { + g_pCompositor->focusWindow(PWINDOWTOCHANGETO); + g_pCompositor->warpCursorTo(PWINDOWTOCHANGETO->middle()); + } + + g_pInputManager->m_pForcedFocus = PWINDOWTOCHANGETO; + g_pInputManager->simulateMouseMovement(); + g_pInputManager->m_pForcedFocus.reset(); + }; + + CVarList vars(message, 0, ' '); + + if (vars.size() < 1 || vars[0].empty()) { + Debug::log(ERR, "layoutmsg called without params"); + return 0; + } + + auto command = vars[0]; + + // swapwithmaster + // first message argument can have the following values: + // * master - keep the focus at the new master + // * child - keep the focus at the new child + // * auto (default) - swap the focus (keep the focus of the previously selected window) + if (command == "swapwithmaster") { + const auto PWINDOW = header.pWindow; + + if (!PWINDOW) + return 0; + + if (!isWindowTiled(PWINDOW)) + return 0; + + const auto PMASTER = getMasterNodeOnWorkspace(PWINDOW->workspaceID()); + + if (!PMASTER) + return 0; + + const auto NEWCHILD = PMASTER->pWindow.lock(); + + if (PMASTER->pWindow.lock() != PWINDOW) { + const auto NEWMASTER = PWINDOW; + const bool newFocusToChild = vars.size() >= 2 && vars[1] == "child"; + switchWindows(NEWMASTER, NEWCHILD); + const auto NEWFOCUS = newFocusToChild ? NEWCHILD : NEWMASTER; + switchToWindow(NEWFOCUS); + } else { + for (auto const& n : m_lMasterNodesData) { + if (n.workspaceID == PMASTER->workspaceID && !n.isMaster) { + const auto NEWMASTER = n.pWindow.lock(); + switchWindows(NEWMASTER, NEWCHILD); + const bool newFocusToMaster = vars.size() >= 2 && vars[1] == "master"; + const auto NEWFOCUS = newFocusToMaster ? NEWMASTER : NEWCHILD; + switchToWindow(NEWFOCUS); + break; + } + } + } + + return 0; + } + // focusmaster + // first message argument can have the following values: + // * master - keep the focus at the new master, even if it was focused before + // * auto (default) - swap the focus with the first child, if the current focus was master, otherwise focus master + else if (command == "focusmaster") { + const auto PWINDOW = header.pWindow; + + if (!PWINDOW) + return 0; + + const auto PMASTER = getMasterNodeOnWorkspace(PWINDOW->workspaceID()); + + if (!PMASTER) + return 0; + + if (PMASTER->pWindow.lock() != PWINDOW) { + switchToWindow(PMASTER->pWindow.lock()); + } else if (vars.size() >= 2 && vars[1] == "master") { + return 0; + } else { + // if master is focused keep master focused (don't do anything) + for (auto const& n : m_lMasterNodesData) { + if (n.workspaceID == PMASTER->workspaceID && !n.isMaster) { + switchToWindow(n.pWindow.lock()); + break; + } + } + } + + return 0; + } else if (command == "cyclenext") { + const auto PWINDOW = header.pWindow; + + if (!PWINDOW) + return 0; + + const bool NOLOOP = vars.size() >= 2 && vars[1] == "noloop"; + const auto PNEXTWINDOW = getNextWindow(PWINDOW, true, !NOLOOP); + switchToWindow(PNEXTWINDOW); + } else if (command == "cycleprev") { + const auto PWINDOW = header.pWindow; + + if (!PWINDOW) + return 0; + + const bool NOLOOP = vars.size() >= 2 && vars[1] == "noloop"; + const auto PPREVWINDOW = getNextWindow(PWINDOW, false, !NOLOOP); + switchToWindow(PPREVWINDOW); + } else if (command == "swapnext") { + if (!validMapped(header.pWindow)) + return 0; + + if (header.pWindow->m_bIsFloating) { + g_pKeybindManager->m_mDispatchers["swapnext"](""); + return 0; + } + + const bool NOLOOP = vars.size() >= 2 && vars[1] == "noloop"; + const auto PWINDOWTOSWAPWITH = getNextWindow(header.pWindow, true, !NOLOOP); + + if (PWINDOWTOSWAPWITH) { + g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE); + switchWindows(header.pWindow, PWINDOWTOSWAPWITH); + switchToWindow(header.pWindow); + } + } else if (command == "swapprev") { + if (!validMapped(header.pWindow)) + return 0; + + if (header.pWindow->m_bIsFloating) { + g_pKeybindManager->m_mDispatchers["swapnext"]("prev"); + return 0; + } + + const bool NOLOOP = vars.size() >= 2 && vars[1] == "noloop"; + const auto PWINDOWTOSWAPWITH = getNextWindow(header.pWindow, false, !NOLOOP); + + if (PWINDOWTOSWAPWITH) { + g_pCompositor->setWindowFullscreenClient(header.pWindow, FSMODE_NONE); + switchWindows(header.pWindow, PWINDOWTOSWAPWITH); + switchToWindow(header.pWindow); + } + } else if (command == "addmaster") { + if (!validMapped(header.pWindow)) + return 0; + + if (header.pWindow->m_bIsFloating) + return 0; + + const auto PNODE = getNodeFromWindow(header.pWindow); + + const auto WINDOWS = getNodesOnWorkspace(header.pWindow->workspaceID()); + const auto MASTERS = getMastersOnWorkspace(header.pWindow->workspaceID()); + static auto SMALLSPLIT = CConfigValue("master:allow_small_split"); + + if (MASTERS + 2 > WINDOWS && *SMALLSPLIT == 0) + return 0; + + g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE); + + if (!PNODE || PNODE->isMaster) { + // first non-master node + for (auto& n : m_lMasterNodesData) { + if (n.workspaceID == header.pWindow->workspaceID() && !n.isMaster) { + n.isMaster = true; + break; + } + } + } else { + PNODE->isMaster = true; + } + + recalculateMonitor(header.pWindow->monitorID()); + + } else if (command == "removemaster") { + + if (!validMapped(header.pWindow)) + return 0; + + if (header.pWindow->m_bIsFloating) + return 0; + + const auto PNODE = getNodeFromWindow(header.pWindow); + + const auto WINDOWS = getNodesOnWorkspace(header.pWindow->workspaceID()); + const auto MASTERS = getMastersOnWorkspace(header.pWindow->workspaceID()); + + if (WINDOWS < 2 || MASTERS < 2) + return 0; + + g_pCompositor->setWindowFullscreenInternal(header.pWindow, FSMODE_NONE); + + if (!PNODE || !PNODE->isMaster) { + // first non-master node + for (auto& nd : m_lMasterNodesData | std::views::reverse) { + if (nd.workspaceID == header.pWindow->workspaceID() && nd.isMaster) { + nd.isMaster = false; + break; + } + } + } else { + PNODE->isMaster = false; + } + + recalculateMonitor(header.pWindow->monitorID()); + } else if (command == "orientationleft" || command == "orientationright" || command == "orientationtop" || command == "orientationbottom" || command == "orientationcenter") { + const auto PWINDOW = header.pWindow; + + if (!PWINDOW) + return 0; + + g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); + + const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->workspaceID()); + + if (command == "orientationleft") + PWORKSPACEDATA->orientation = ORIENTATION_LEFT; + else if (command == "orientationright") + PWORKSPACEDATA->orientation = ORIENTATION_RIGHT; + else if (command == "orientationtop") + PWORKSPACEDATA->orientation = ORIENTATION_TOP; + else if (command == "orientationbottom") + PWORKSPACEDATA->orientation = ORIENTATION_BOTTOM; + else if (command == "orientationcenter") + PWORKSPACEDATA->orientation = ORIENTATION_CENTER; + + recalculateMonitor(header.pWindow->monitorID()); + + } else if (command == "orientationnext") { + runOrientationCycle(header, nullptr, 1); + } else if (command == "orientationprev") { + runOrientationCycle(header, nullptr, -1); + } else if (command == "orientationcycle") { + runOrientationCycle(header, &vars, 1); + } else if (command == "mfact") { + g_pKeybindManager->m_mDispatchers["splitratio"](vars[1] + " " + vars[2]); + } else if (command == "rollnext") { + const auto PWINDOW = header.pWindow; + const auto PNODE = getNodeFromWindow(PWINDOW); + + if (!PNODE) + return 0; + + const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNodeOnWorkspace(PNODE->workspaceID); + if (!OLDMASTER) + return 0; + + const auto OLDMASTERIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *OLDMASTER); + + for (auto& nd : m_lMasterNodesData) { + if (nd.workspaceID == PNODE->workspaceID && !nd.isMaster) { + nd.isMaster = true; + const auto NEWMASTERIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), nd); + m_lMasterNodesData.splice(OLDMASTERIT, m_lMasterNodesData, NEWMASTERIT); + switchToWindow(nd.pWindow.lock()); + OLDMASTER->isMaster = false; + m_lMasterNodesData.splice(m_lMasterNodesData.end(), m_lMasterNodesData, OLDMASTERIT); + break; + } + } + + recalculateMonitor(PWINDOW->monitorID()); + } else if (command == "rollprev") { + const auto PWINDOW = header.pWindow; + const auto PNODE = getNodeFromWindow(PWINDOW); + + if (!PNODE) + return 0; + + const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNodeOnWorkspace(PNODE->workspaceID); + if (!OLDMASTER) + return 0; + + const auto OLDMASTERIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), *OLDMASTER); + + for (auto& nd : m_lMasterNodesData | std::views::reverse) { + if (nd.workspaceID == PNODE->workspaceID && !nd.isMaster) { + nd.isMaster = true; + const auto NEWMASTERIT = std::find(m_lMasterNodesData.begin(), m_lMasterNodesData.end(), nd); + m_lMasterNodesData.splice(OLDMASTERIT, m_lMasterNodesData, NEWMASTERIT); + switchToWindow(nd.pWindow.lock()); + OLDMASTER->isMaster = false; + m_lMasterNodesData.splice(m_lMasterNodesData.begin(), m_lMasterNodesData, OLDMASTERIT); + break; + } + } + + recalculateMonitor(PWINDOW->monitorID()); + } + + return 0; +} + +// If vars is null, we use the default list +void CHyprMasterLayout::runOrientationCycle(SLayoutMessageHeader& header, CVarList* vars, int direction) { + std::vector cycle; + if (vars != nullptr) + buildOrientationCycleVectorFromVars(cycle, *vars); + + if (cycle.size() == 0) + buildOrientationCycleVectorFromEOperation(cycle); + + const auto PWINDOW = header.pWindow; + + if (!PWINDOW) + return; + + g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); + + const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->workspaceID()); + + int nextOrPrev = 0; + for (size_t i = 0; i < cycle.size(); ++i) { + if (PWORKSPACEDATA->orientation == cycle[i]) { + nextOrPrev = i + direction; + break; + } + } + + if (nextOrPrev >= (int)cycle.size()) + nextOrPrev = nextOrPrev % (int)cycle.size(); + else if (nextOrPrev < 0) + nextOrPrev = cycle.size() + (nextOrPrev % (int)cycle.size()); + + PWORKSPACEDATA->orientation = cycle.at(nextOrPrev); + recalculateMonitor(header.pWindow->monitorID()); +} + +void CHyprMasterLayout::buildOrientationCycleVectorFromEOperation(std::vector& cycle) { + for (int i = 0; i <= ORIENTATION_CENTER; ++i) { + cycle.push_back((eOrientation)i); + } +} + +void CHyprMasterLayout::buildOrientationCycleVectorFromVars(std::vector& cycle, CVarList& vars) { + for (size_t i = 1; i < vars.size(); ++i) { + if (vars[i] == "top") { + cycle.push_back(ORIENTATION_TOP); + } else if (vars[i] == "right") { + cycle.push_back(ORIENTATION_RIGHT); + } else if (vars[i] == "bottom") { + cycle.push_back(ORIENTATION_BOTTOM); + } else if (vars[i] == "left") { + cycle.push_back(ORIENTATION_LEFT); + } else if (vars[i] == "center") { + cycle.push_back(ORIENTATION_CENTER); + } + } +} + +eOrientation CHyprMasterLayout::getDynamicOrientation(PHLWORKSPACE pWorkspace) { + const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(pWorkspace); + std::string orientationString; + if (WORKSPACERULE.layoutopts.contains("orientation")) + orientationString = WORKSPACERULE.layoutopts.at("orientation"); + + eOrientation orientation = getMasterWorkspaceData(pWorkspace->m_iID)->orientation; + // override if workspace rule is set + if (!orientationString.empty()) { + if (orientationString == "top") + orientation = ORIENTATION_TOP; + else if (orientationString == "right") + orientation = ORIENTATION_RIGHT; + else if (orientationString == "bottom") + orientation = ORIENTATION_BOTTOM; + else if (orientationString == "center") + orientation = ORIENTATION_CENTER; + else + orientation = ORIENTATION_LEFT; + } + + return orientation; +} + +void CHyprMasterLayout::replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) { + const auto PNODE = getNodeFromWindow(from); + + if (!PNODE) + return; + + PNODE->pWindow = to; + + applyNodeDataToWindow(PNODE); +} + +Vector2D CHyprMasterLayout::predictSizeForNewWindowTiled() { + static auto PNEWSTATUS = CConfigValue("master:new_status"); + + if (!g_pCompositor->m_pLastMonitor) + return {}; + + const int NODES = getNodesOnWorkspace(g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID); + + if (NODES <= 0) + return g_pCompositor->m_pLastMonitor->vecSize; + + const auto MASTER = getMasterNodeOnWorkspace(g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID); + if (!MASTER) // wtf + return {}; + + if (*PNEWSTATUS == "master") { + return MASTER->size; + } else { + const auto SLAVES = NODES - getMastersOnWorkspace(g_pCompositor->m_pLastMonitor->activeWorkspace->m_iID); + + // TODO: make this better + return {g_pCompositor->m_pLastMonitor->vecSize.x - MASTER->size.x, g_pCompositor->m_pLastMonitor->vecSize.y / (SLAVES + 1)}; + } + + return {}; +} + +void CHyprMasterLayout::onEnable() { + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_bIsFloating || !w->m_bIsMapped || w->isHidden()) + continue; + + onWindowCreatedTiling(w); + } +} + +void CHyprMasterLayout::onDisable() { + m_lMasterNodesData.clear(); +} diff --git a/src/layout/MasterLayout.hpp b/src/layout/MasterLayout.hpp new file mode 100644 index 00000000..f658fdaa --- /dev/null +++ b/src/layout/MasterLayout.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include "IHyprLayout.hpp" +#include "../desktop/DesktopTypes.hpp" +#include "../helpers/varlist/VarList.hpp" +#include +#include +#include + +enum eFullscreenMode : int8_t; + +//orientation determines which side of the screen the master area resides +enum eOrientation : uint8_t { + ORIENTATION_LEFT = 0, + ORIENTATION_TOP, + ORIENTATION_RIGHT, + ORIENTATION_BOTTOM, + ORIENTATION_CENTER +}; + +struct SMasterNodeData { + bool isMaster = false; + float percMaster = 0.5f; + + PHLWINDOWREF pWindow; + + Vector2D position; + Vector2D size; + + float percSize = 1.f; // size multiplier for resizing children + + WORKSPACEID workspaceID = WORKSPACE_INVALID; + + bool ignoreFullscreenChecks = false; + + // + bool operator==(const SMasterNodeData& rhs) const { + return pWindow.lock() == rhs.pWindow.lock(); + } +}; + +struct SMasterWorkspaceData { + WORKSPACEID workspaceID = WORKSPACE_INVALID; + eOrientation orientation = ORIENTATION_LEFT; + + // + bool operator==(const SMasterWorkspaceData& rhs) const { + return workspaceID == rhs.workspaceID; + } +}; + +class CHyprMasterLayout : public IHyprLayout { + public: + virtual void onWindowCreatedTiling(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT); + virtual void onWindowRemovedTiling(PHLWINDOW); + virtual bool isWindowTiled(PHLWINDOW); + virtual void recalculateMonitor(const MONITORID&); + virtual void recalculateWindow(PHLWINDOW); + virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr); + virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE); + virtual std::any layoutMessage(SLayoutMessageHeader, std::string); + virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW); + virtual void switchWindows(PHLWINDOW, PHLWINDOW); + virtual void moveWindowTo(PHLWINDOW, const std::string& dir, bool silent); + virtual void alterSplitRatio(PHLWINDOW, float, bool); + virtual std::string getLayoutName(); + virtual void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to); + virtual Vector2D predictSizeForNewWindowTiled(); + + virtual void onEnable(); + virtual void onDisable(); + + private: + std::list m_lMasterNodesData; + std::vector m_lMasterWorkspacesData; + + bool m_bForceWarps = false; + + void buildOrientationCycleVectorFromVars(std::vector& cycle, CVarList& vars); + void buildOrientationCycleVectorFromEOperation(std::vector& cycle); + void runOrientationCycle(SLayoutMessageHeader& header, CVarList* vars, int next); + eOrientation getDynamicOrientation(PHLWORKSPACE); + int getNodesOnWorkspace(const WORKSPACEID&); + void applyNodeDataToWindow(SMasterNodeData*); + SMasterNodeData* getNodeFromWindow(PHLWINDOW); + SMasterNodeData* getMasterNodeOnWorkspace(const WORKSPACEID&); + SMasterWorkspaceData* getMasterWorkspaceData(const WORKSPACEID&); + void calculateWorkspace(PHLWORKSPACE); + PHLWINDOW getNextWindow(PHLWINDOW, bool, bool); + int getMastersOnWorkspace(const WORKSPACEID&); + + friend struct SMasterNodeData; + friend struct SMasterWorkspaceData; +}; + +template +struct std::formatter : std::formatter { + template + auto format(const SMasterNodeData* const& node, FormatContext& ctx) const { + auto out = ctx.out(); + if (!node) + return std::format_to(out, "[Node nullptr]"); + std::format_to(out, "[Node {:x}: workspace: {}, pos: {:j2}, size: {:j2}", (uintptr_t)node, node->workspaceID, node->position, node->size); + if (node->isMaster) + std::format_to(out, ", master"); + if (!node->pWindow.expired()) + std::format_to(out, ", window: {:x}", node->pWindow.lock()); + return std::format_to(out, "]"); + } +}; diff --git a/src/layout/algorithm/Algorithm.cpp b/src/layout/algorithm/Algorithm.cpp deleted file mode 100644 index cfb5b7e3..00000000 --- a/src/layout/algorithm/Algorithm.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#include "Algorithm.hpp" - -#include "FloatingAlgorithm.hpp" -#include "TiledAlgorithm.hpp" -#include "../target/WindowTarget.hpp" -#include "../space/Space.hpp" -#include "../../desktop/view/Window.hpp" -#include "../../desktop/history/WindowHistoryTracker.hpp" -#include "../../helpers/Monitor.hpp" -#include "../../render/Renderer.hpp" - -#include "../../debug/log/Logger.hpp" - -using namespace Layout; - -SP CAlgorithm::create(UP&& tiled, UP&& floating, SP space) { - auto algo = SP(new CAlgorithm(std::move(tiled), std::move(floating), space)); - algo->m_self = algo; - algo->m_tiled->m_parent = algo; - algo->m_floating->m_parent = algo; - return algo; -} - -CAlgorithm::CAlgorithm(UP&& tiled, UP&& floating, SP space) : - m_tiled(std::move(tiled)), m_floating(std::move(floating)), m_space(space) { - ; -} - -void CAlgorithm::addTarget(SP target) { - const bool SHOULD_FLOAT = target->floating(); - - if (SHOULD_FLOAT) { - m_floatingTargets.emplace_back(target); - m_floating->newTarget(target); - } else { - m_tiledTargets.emplace_back(target); - m_tiled->newTarget(target); - } -} - -void CAlgorithm::removeTarget(SP target) { - const bool IS_FLOATING = std::ranges::contains(m_floatingTargets, target); - - if (IS_FLOATING) { - std::erase(m_floatingTargets, target); - m_floating->removeTarget(target); - return; - } - - const bool IS_TILED = std::ranges::contains(m_tiledTargets, target); - - if (IS_TILED) { - std::erase(m_tiledTargets, target); - m_tiled->removeTarget(target); - return; - } - - Log::logger->log(Log::ERR, "BUG THIS: CAlgorithm::removeTarget, but not found"); -} - -void CAlgorithm::moveTarget(SP target, std::optional focalPoint, bool reposition) { - const bool SHOULD_FLOAT = target->floating(); - - if (SHOULD_FLOAT) { - m_floatingTargets.emplace_back(target); - if (reposition) - m_floating->newTarget(target); - else - m_floating->movedTarget(target, focalPoint); - } else { - m_tiledTargets.emplace_back(target); - if (reposition) - m_tiled->newTarget(target); - else - m_tiled->movedTarget(target, focalPoint); - } -} - -SP CAlgorithm::space() const { - return m_space.lock(); -} - -void CAlgorithm::setFloating(SP target, bool floating, bool reposition) { - removeTarget(target); - - g_pHyprRenderer->damageWindow(target->window()); - - target->setFloating(floating); - - moveTarget(target, std::nullopt, reposition); - - g_pHyprRenderer->damageWindow(target->window()); -} - -size_t CAlgorithm::tiledTargets() const { - return m_tiledTargets.size(); -} - -size_t CAlgorithm::floatingTargets() const { - return m_floatingTargets.size(); -} - -void CAlgorithm::recalculate() { - m_tiled->recalculate(); - m_floating->recalculate(); - - const auto PWORKSPACE = m_space->workspace(); - const auto PMONITOR = PWORKSPACE->m_monitor; - - if (PWORKSPACE->m_hasFullscreenWindow && PMONITOR) { - // massive hack from the fullscreen func - const auto PFULLWINDOW = PWORKSPACE->getFullscreenWindow(); - - if (PFULLWINDOW) { - if (PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) { - *PFULLWINDOW->m_realPosition = PMONITOR->m_position; - *PFULLWINDOW->m_realSize = PMONITOR->m_size; - } else if (PWORKSPACE->m_fullscreenMode == FSMODE_MAXIMIZED) - PFULLWINDOW->layoutTarget()->setPositionGlobal(m_space->workArea()); - } - - return; - } -} - -void CAlgorithm::recenter(SP t) { - if (t->floating()) - m_floating->recenter(t); -} - -std::expected CAlgorithm::layoutMsg(const std::string_view& sv) { - if (const auto ret = m_floating->layoutMsg(sv); !ret) - return ret; - return m_tiled->layoutMsg(sv); -} - -std::optional CAlgorithm::predictSizeForNewTiledTarget() { - return m_tiled->predictSizeForNewTarget(); -} - -void CAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { - if (target->floating()) - m_floating->resizeTarget(Δ, target, corner); - else - m_tiled->resizeTarget(Δ, target, corner); -} - -void CAlgorithm::moveTarget(const Vector2D& Δ, SP target) { - if (target->floating()) - m_floating->moveTarget(Δ, target); -} - -void CAlgorithm::swapTargets(SP a, SP b) { - auto swapFirst = [&a, &b](std::vector>& targets) -> bool { - auto ia = std::ranges::find(targets, a); - auto ib = std::ranges::find(targets, b); - - if (ia != std::ranges::end(targets) && ib != std::ranges::end(targets)) { - std::iter_swap(ia, ib); - return true; - } else if (ia != std::ranges::end(targets)) - *ia = b; - else if (ib != std::ranges::end(targets)) - *ib = a; - - return false; - }; - - if (!swapFirst(m_tiledTargets)) - swapFirst(m_floatingTargets); - - const WP algA = a->floating() ? WP(m_floating) : WP(m_tiled); - const WP algB = b->floating() ? WP(m_floating) : WP(m_tiled); - - algA->swapTargets(a, b); - if (algA != algB) - algB->swapTargets(b, a); -} - -void CAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - if (t->floating()) - m_floating->moveTargetInDirection(t, dir, silent); - else - m_tiled->moveTargetInDirection(t, dir, silent); -} - -void CAlgorithm::updateFloatingAlgo(UP&& algo) { - algo->m_parent = m_self; - - for (const auto& t : m_floatingTargets) { - const auto TARGET = t.lock(); - if (!TARGET) - continue; - - // Unhide windows when switching layouts to prevent them from being permanently lost - const auto WINDOW = TARGET->window(); - if (WINDOW) - WINDOW->setHidden(false); - - m_floating->removeTarget(TARGET); - algo->newTarget(TARGET); - } - - m_floating = std::move(algo); -} - -void CAlgorithm::updateTiledAlgo(UP&& algo) { - algo->m_parent = m_self; - - for (const auto& t : m_tiledTargets) { - const auto TARGET = t.lock(); - if (!TARGET) - continue; - - // Unhide windows when switching layouts to prevent them from being permanently lost - // This is a safeguard for layouts (including third-party plugins) that use setHidden - const auto WINDOW = TARGET->window(); - if (WINDOW) - WINDOW->setHidden(false); - - m_tiled->removeTarget(TARGET); - algo->newTarget(TARGET); - } - - m_tiled = std::move(algo); -} - -const UP& CAlgorithm::tiledAlgo() const { - return m_tiled; -} - -const UP& CAlgorithm::floatingAlgo() const { - return m_floating; -} - -SP CAlgorithm::getNextCandidate(SP old) { - if (old->floating()) { - // use window history to determine best target - for (const auto& w : Desktop::History::windowTracker()->fullHistory() | std::views::reverse) { - if (!w->m_workspace || w->m_workspace->m_space != m_space || !w->layoutTarget() || !w->layoutTarget()->space()) - continue; - - return w->layoutTarget(); - } - - // no history, fall back - } else { - // ask the layout - const auto CANDIDATE = m_tiled->getNextCandidate(old); - if (CANDIDATE) - return CANDIDATE; - - // no candidate, fall back - } - - // fallback: try to focus anything - if (!m_tiledTargets.empty()) - return m_tiledTargets.back().lock(); - if (!m_floatingTargets.empty()) - return m_floatingTargets.back().lock(); - - // god damn it, maybe empty? - return nullptr; -} - -void CAlgorithm::setTargetGeom(const CBox& box, SP target) { - if (!target->floating() || !std::ranges::contains(m_floatingTargets, target)) - return; - - m_floating->setTargetGeom(box, target); -} diff --git a/src/layout/algorithm/Algorithm.hpp b/src/layout/algorithm/Algorithm.hpp deleted file mode 100644 index 7df6c5c1..00000000 --- a/src/layout/algorithm/Algorithm.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include "../../helpers/math/Math.hpp" -#include "../../helpers/math/Direction.hpp" -#include "../../helpers/memory/Memory.hpp" - -#include "../LayoutManager.hpp" - -#include -#include - -namespace Layout { - class ITarget; - class IFloatingAlgorithm; - class ITiledAlgorithm; - class CSpace; - - class CAlgorithm { - public: - static SP create(UP&& tiled, UP&& floating, SP space); - ~CAlgorithm() = default; - - void addTarget(SP target); - void moveTarget(SP target, std::optional focalPoint = std::nullopt, bool reposition = false); - void removeTarget(SP target); - - void swapTargets(SP a, SP b); - void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - - SP getNextCandidate(SP old); - - void setFloating(SP target, bool floating, bool reposition = false); - - std::expected layoutMsg(const std::string_view& sv); - std::optional predictSizeForNewTiledTarget(); - - void recalculate(); - void recenter(SP t); - - void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - void moveTarget(const Vector2D& Δ, SP target); - - void setTargetGeom(const CBox& box, SP target); // only for float - - void updateFloatingAlgo(UP&& algo); - void updateTiledAlgo(UP&& algo); - - const UP& tiledAlgo() const; - const UP& floatingAlgo() const; - - SP space() const; - - size_t tiledTargets() const; - size_t floatingTargets() const; - - private: - CAlgorithm(UP&& tiled, UP&& floating, SP space); - - UP m_tiled; - UP m_floating; - WP m_space; - WP m_self; - - std::vector> m_tiledTargets, m_floatingTargets; - }; -} \ No newline at end of file diff --git a/src/layout/algorithm/FloatingAlgorithm.cpp b/src/layout/algorithm/FloatingAlgorithm.cpp deleted file mode 100644 index 058887bf..00000000 --- a/src/layout/algorithm/FloatingAlgorithm.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "FloatingAlgorithm.hpp" -#include "Algorithm.hpp" -#include "../space/Space.hpp" - -using namespace Layout; - -void IFloatingAlgorithm::recalculate() { - ; -} - -void IFloatingAlgorithm::recenter(SP t) { - const auto LAST = t->lastFloatingSize(); - - if (LAST.x <= 5 || LAST.y <= 5) - return; - - t->setPositionGlobal({m_parent->space()->workArea().middle() - LAST / 2.F, LAST}); -} diff --git a/src/layout/algorithm/FloatingAlgorithm.hpp b/src/layout/algorithm/FloatingAlgorithm.hpp deleted file mode 100644 index 40e53034..00000000 --- a/src/layout/algorithm/FloatingAlgorithm.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "../../helpers/math/Math.hpp" -#include "../../helpers/memory/Memory.hpp" - -#include "ModeAlgorithm.hpp" - -namespace Layout { - - class ITarget; - class CAlgorithm; - - class IFloatingAlgorithm : public IModeAlgorithm { - public: - virtual ~IFloatingAlgorithm() = default; - - // a target is being moved by a delta - virtual void moveTarget(const Vector2D& Δ, SP target) = 0; - - // a target is moved to a pos x size - virtual void setTargetGeom(const CBox& geom, SP target) = 0; - - virtual void recenter(SP t); - - virtual void recalculate(); - - protected: - IFloatingAlgorithm() = default; - - WP m_parent; - - friend class Layout::CAlgorithm; - }; -} \ No newline at end of file diff --git a/src/layout/algorithm/ModeAlgorithm.cpp b/src/layout/algorithm/ModeAlgorithm.cpp deleted file mode 100644 index dea5bb17..00000000 --- a/src/layout/algorithm/ModeAlgorithm.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "ModeAlgorithm.hpp" - -#include "../space/Space.hpp" -#include "Algorithm.hpp" -#include "../../helpers/Monitor.hpp" -#include "../../desktop/view/Window.hpp" - -using namespace Layout; - -std::expected IModeAlgorithm::layoutMsg(const std::string_view& sv) { - return {}; -} - -std::optional IModeAlgorithm::predictSizeForNewTarget() { - return std::nullopt; -} - -std::optional IModeAlgorithm::focalPointForDir(SP t, Math::eDirection dir) { - Vector2D focalPoint; - - const auto WINDOWIDEALBB = - t->fullscreenMode() != FSMODE_NONE ? m_parent->space()->workspace()->m_monitor->logicalBox() : t->window()->getWindowIdealBoundingBoxIgnoreReserved(); - - switch (dir) { - case Math::DIRECTION_UP: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, -1.0}; break; - case Math::DIRECTION_DOWN: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, WINDOWIDEALBB.size().y + 1.0}; break; - case Math::DIRECTION_LEFT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{-1.0, WINDOWIDEALBB.size().y / 2.0}; break; - case Math::DIRECTION_RIGHT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x + 1.0, WINDOWIDEALBB.size().y / 2.0}; break; - default: return std::nullopt; - } - - return focalPoint; -} diff --git a/src/layout/algorithm/ModeAlgorithm.hpp b/src/layout/algorithm/ModeAlgorithm.hpp deleted file mode 100644 index 0fedc3da..00000000 --- a/src/layout/algorithm/ModeAlgorithm.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "../../helpers/math/Math.hpp" -#include "../../helpers/math/Direction.hpp" -#include "../../helpers/memory/Memory.hpp" - -#include "../LayoutManager.hpp" - -#include - -namespace Layout { - - class ITarget; - class CAlgorithm; - - class IModeAlgorithm { - public: - virtual ~IModeAlgorithm() = default; - - // a completely new target - virtual void newTarget(SP target) = 0; - - // a target moved into the algorithm (from another) - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt) = 0; - - // a target removed - virtual void removeTarget(SP target) = 0; - - // a target is being resized by a delta. Corner none likely means not interactive - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE) = 0; - - // recalculate layout - virtual void recalculate() = 0; - - // swap targets - virtual void swapTargets(SP a, SP b) = 0; - - // move a target in a given direction - virtual void moveTargetInDirection(SP t, Math::eDirection dir, bool silent) = 0; - - // optional: handle layout messages - virtual std::expected layoutMsg(const std::string_view& sv); - - // optional: predict new window's size - virtual std::optional predictSizeForNewTarget(); - - // Impl'd here: focal point for dir - virtual std::optional focalPointForDir(SP t, Math::eDirection dir); - - protected: - IModeAlgorithm() = default; - - WP m_parent; - - friend class Layout::CAlgorithm; - }; -} \ No newline at end of file diff --git a/src/layout/algorithm/TiledAlgorithm.hpp b/src/layout/algorithm/TiledAlgorithm.hpp deleted file mode 100644 index 99d1bd99..00000000 --- a/src/layout/algorithm/TiledAlgorithm.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "../../helpers/math/Math.hpp" -#include "../../helpers/memory/Memory.hpp" - -#include "ModeAlgorithm.hpp" - -namespace Layout { - - class ITarget; - class CAlgorithm; - - class ITiledAlgorithm : public IModeAlgorithm { - public: - virtual ~ITiledAlgorithm() = default; - - virtual SP getNextCandidate(SP old) = 0; - - protected: - ITiledAlgorithm() = default; - - WP m_parent; - - friend class Layout::CAlgorithm; - }; -} \ No newline at end of file diff --git a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp b/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp deleted file mode 100644 index 0d069e4f..00000000 --- a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#include "DefaultFloatingAlgorithm.hpp" - -#include "../../Algorithm.hpp" - -#include "../../../target/WindowTarget.hpp" -#include "../../../space/Space.hpp" - -#include "../../../../Compositor.hpp" -#include "../../../../helpers/Monitor.hpp" - -using namespace Layout; -using namespace Layout::Floating; - -constexpr const Vector2D DEFAULT_SIZE = {640, 400}; - -// -void CDefaultFloatingAlgorithm::newTarget(SP target) { - const auto WORK_AREA = m_parent->space()->workArea(true); - const auto DESIRED_GEOM = target->desiredGeometry(); - const auto MONITOR_POS = m_parent->space()->workspace()->m_monitor->logicalBox().pos(); - - CBox windowGeometry; - - if (!DESIRED_GEOM) { - switch (DESIRED_GEOM.error()) { - case GEOMETRY_INVALID_DESIRED: { - // if the desired is invalid, we hide the window. - if (target->type() == TARGET_TYPE_WINDOW) - dynamicPointerCast(target)->window()->setHidden(true); - return; - } - case GEOMETRY_NO_DESIRED: { - // add a default geom - windowGeometry = CBox{WORK_AREA.middle() - DEFAULT_SIZE / 2.F, DEFAULT_SIZE}; - break; - } - } - } else { - if (DESIRED_GEOM->pos) - windowGeometry = CBox{DESIRED_GEOM->pos.value(), DESIRED_GEOM->size}; - else - windowGeometry = CBox{WORK_AREA.middle() - DESIRED_GEOM->size / 2.F, DESIRED_GEOM->size}; - } - - bool posOverridden = false; - - if (target->window() && target->window()->m_firstMap) { - const auto WINDOW = target->window(); - - // set this here so that expressions can use it. This could be wrong of course. - WINDOW->m_realSize->setValueAndWarp(DESIRED_GEOM ? DESIRED_GEOM->size : DEFAULT_SIZE); - - if (!WINDOW->m_ruleApplicator->static_.size.empty()) { - const auto COMPUTED = WINDOW->calculateExpression(WINDOW->m_ruleApplicator->static_.size); - if (!COMPUTED) - Log::logger->log(Log::ERR, "failed to parse {} as an expression", WINDOW->m_ruleApplicator->static_.size); - else { - windowGeometry.w = COMPUTED->x; - windowGeometry.h = COMPUTED->y; - - // update for pos to work with size. - WINDOW->m_realPosition->setValueAndWarp(*COMPUTED); - } - } - - if (!WINDOW->m_ruleApplicator->static_.position.empty()) { - const auto COMPUTED = WINDOW->calculateExpression(WINDOW->m_ruleApplicator->static_.position); - if (!COMPUTED) - Log::logger->log(Log::ERR, "failed to parse {} as an expression", WINDOW->m_ruleApplicator->static_.position); - else { - windowGeometry.x = COMPUTED->x + MONITOR_POS.x; - windowGeometry.y = COMPUTED->y + MONITOR_POS.y; - posOverridden = true; - } - } - - if (WINDOW->m_ruleApplicator->static_.center.value_or(false)) { - const auto POS = WORK_AREA.middle() - windowGeometry.size() / 2.f; - windowGeometry.x = POS.x; - windowGeometry.y = POS.y; - posOverridden = true; - } - } else if (target->lastFloatingSize().x > 5 && target->lastFloatingSize().y > 5) { - windowGeometry.w = target->lastFloatingSize().x; - windowGeometry.h = target->lastFloatingSize().y; - } - - if (!posOverridden && (!DESIRED_GEOM || !DESIRED_GEOM->pos)) - windowGeometry = CBox{WORK_AREA.middle() - windowGeometry.size() / 2.F, windowGeometry.size()}; - - if (posOverridden // pos is overridden by a rule - || (DESIRED_GEOM && DESIRED_GEOM->pos && target->window() && target->window()->m_isX11) // X11 window with a geom - || WORK_AREA.containsPoint(windowGeometry.middle())) // geometry within work area - target->setPositionGlobal(windowGeometry); - else { - const auto POS = WORK_AREA.middle() - windowGeometry.size() / 2.f; - windowGeometry.x = POS.x; - windowGeometry.y = POS.y; - - target->setPositionGlobal(windowGeometry); - } - - // TODO: not very OOP, is it? - if (const auto WTARGET = dynamicPointerCast(target); WTARGET) { - const auto PWINDOW = WTARGET->window(); - - if (PWINDOW->m_X11DoesntWantBorders || (PWINDOW->m_isX11 && PWINDOW->isX11OverrideRedirect())) { - PWINDOW->m_realPosition->warp(); - PWINDOW->m_realSize->warp(); - } - - if (!PWINDOW->isX11OverrideRedirect()) - g_pCompositor->changeWindowZOrder(PWINDOW, true); - else { - PWINDOW->m_pendingReportedSize = PWINDOW->m_realSize->goal(); - PWINDOW->m_reportedSize = PWINDOW->m_pendingReportedSize; - } - } - - updateTarget(target); -} - -void CDefaultFloatingAlgorithm::movedTarget(SP target, std::optional focalPoint) { - auto LAST_SIZE = target->lastFloatingSize(); - const auto CURRENT_SIZE = target->position().size(); - - // ignore positioning a dragged target - if (g_layoutManager->dragController()->target() == target) - return; - - if (LAST_SIZE.x < 5 || LAST_SIZE.y < 5) { - const auto DESIRED = target->desiredGeometry(); - LAST_SIZE = DESIRED ? DESIRED->size : DEFAULT_SIZE; - } - - if (target->wasTiling()) { - // Avoid floating toggles that don't change size, they aren't easily visible to the user - if (std::abs(LAST_SIZE.x - CURRENT_SIZE.x) < 5 && std::abs(LAST_SIZE.y - CURRENT_SIZE.y) < 5) - LAST_SIZE += Vector2D{10, 10}; - - // calculate new position - const auto OLD_CENTER = target->position().middle(); - - // put around the current center, fit in workArea - target->setPositionGlobal(fitBoxInWorkArea(CBox{OLD_CENTER - LAST_SIZE / 2.F, LAST_SIZE}, target)); - - } else { - // calculate new position - const auto THIS_MON_POS = m_parent->space()->workspace()->m_monitor->m_position; - const auto OLD_POS = target->position().pos(); - const auto MON_FROM_OLD = g_pCompositor->getMonitorFromVector(OLD_POS); - const auto NEW_POS = MON_FROM_OLD ? OLD_POS - MON_FROM_OLD->m_position + THIS_MON_POS : OLD_POS; - - // put around the current center, fit in workArea - target->setPositionGlobal(fitBoxInWorkArea(CBox{NEW_POS, LAST_SIZE}, target)); - } - - updateTarget(target); -} - -CBox CDefaultFloatingAlgorithm::fitBoxInWorkArea(const CBox& box, SP t) { - const auto WORK_AREA = m_parent->space()->workArea(true); - const auto EXTENTS = t->window() ? t->window()->getWindowExtentsUnified(Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS) : SBoxExtents{}; - CBox targetBox = box.copy().addExtents(EXTENTS); - - targetBox.x = std::max(targetBox.x, WORK_AREA.x); - targetBox.y = std::max(targetBox.y, WORK_AREA.y); - - if (targetBox.x + targetBox.w > WORK_AREA.x + WORK_AREA.w) - targetBox.x = WORK_AREA.x + WORK_AREA.w - targetBox.w; - - if (targetBox.y + targetBox.h > WORK_AREA.y + WORK_AREA.h) - targetBox.y = WORK_AREA.y + WORK_AREA.h - targetBox.h; - - return targetBox.addExtents(SBoxExtents{.topLeft = -EXTENTS.topLeft, .bottomRight = -EXTENTS.bottomRight}); -} - -void CDefaultFloatingAlgorithm::removeTarget(SP target) { - target->rememberFloatingSize(target->position().size()); - m_datas.erase(target); -} - -void CDefaultFloatingAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { - auto pos = target->position(); - pos.w += Δ.x; - pos.h += Δ.y; - pos.translate(-Δ / 2.F); - target->setPositionGlobal(pos); - - if (g_layoutManager->dragController()->target() == target) - target->warpPositionSize(); - - updateTarget(target); -} - -void CDefaultFloatingAlgorithm::moveTarget(const Vector2D& Δ, SP target) { - auto pos = target->position(); - pos.translate(Δ); - target->setPositionGlobal(pos); - - if (g_layoutManager->dragController()->target() == target) - target->warpPositionSize(); - - updateTarget(target); -} - -void CDefaultFloatingAlgorithm::swapTargets(SP a, SP b) { - auto posABackup = a->position(); - a->setPositionGlobal(b->position()); - b->setPositionGlobal(posABackup); - - updateTarget(a); - updateTarget(b); -} - -void CDefaultFloatingAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - auto pos = t->position(); - auto work = m_parent->space()->workArea(true); - - const auto EXTENTS = t->window() ? t->window()->getWindowExtentsUnified(Desktop::View::RESERVED_EXTENTS) : SBoxExtents{}; - - switch (dir) { - case Math::DIRECTION_LEFT: pos.x = work.x + EXTENTS.topLeft.x; break; - case Math::DIRECTION_RIGHT: pos.x = work.x + work.w - pos.w - EXTENTS.bottomRight.x; break; - case Math::DIRECTION_UP: pos.y = work.y + EXTENTS.topLeft.y; break; - case Math::DIRECTION_DOWN: pos.y = work.y + work.h - pos.h - EXTENTS.bottomRight.y; break; - default: Log::logger->log(Log::ERR, "Invalid direction in CDefaultFloatingAlgorithm::moveTargetInDirection"); break; - } - - t->setPositionGlobal(pos); - - updateTarget(t); -} - -void CDefaultFloatingAlgorithm::recenter(SP t) { - if (!m_datas.contains(t)) { - IFloatingAlgorithm::recenter(t); - return; - } - - t->setPositionGlobal(m_datas.at(t).lastBox); -} - -void CDefaultFloatingAlgorithm::setTargetGeom(const CBox& geom, SP target) { - target->setPositionGlobal(geom); - - updateTarget(target); -} - -void CDefaultFloatingAlgorithm::updateTarget(SP t) { - m_datas[t] = {.lastBox = t->position()}; -} diff --git a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.hpp b/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.hpp deleted file mode 100644 index 1e87fac1..00000000 --- a/src/layout/algorithm/floating/default/DefaultFloatingAlgorithm.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "../../FloatingAlgorithm.hpp" - -#include - -namespace Layout { - class CAlgorithm; -} - -namespace Layout::Floating { - class CDefaultFloatingAlgorithm : public IFloatingAlgorithm { - public: - CDefaultFloatingAlgorithm() = default; - virtual ~CDefaultFloatingAlgorithm() = default; - - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); - - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void moveTarget(const Vector2D& Δ, SP target); - - virtual void setTargetGeom(const CBox& geom, SP target); - - virtual void swapTargets(SP a, SP b); - virtual void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - - virtual void recenter(SP t); - - private: - CBox fitBoxInWorkArea(const CBox& box, SP t); - - void updateTarget(SP); - - struct SWindowData { - CBox lastBox; - }; - - std::map, SWindowData> m_datas; - }; -}; \ No newline at end of file diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp deleted file mode 100644 index 7ef36753..00000000 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp +++ /dev/null @@ -1,845 +0,0 @@ -#include "DwindleAlgorithm.hpp" - -#include "../../Algorithm.hpp" -#include "../../../space/Space.hpp" -#include "../../../target/WindowTarget.hpp" -#include "../../../LayoutManager.hpp" - -#include "../../../../config/ConfigValue.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../helpers/Monitor.hpp" -#include "../../../../Compositor.hpp" - -#include - -using namespace Layout; -using namespace Layout::Tiled; - -struct Layout::Tiled::SDwindleNodeData { - WP pParent; - bool isNode = false; - WP pTarget; - std::array, 2> children = {}; - WP self; - bool splitTop = false; // for preserve_split - CBox box = {0}; - float splitRatio = 1.f; - bool valid = true; - bool ignoreFullscreenChecks = false; - - // For list lookup - bool operator==(const SDwindleNodeData& rhs) const { - return pTarget.lock() == rhs.pTarget.lock() && box == rhs.box && pParent == rhs.pParent && children[0] == rhs.children[0] && children[1] == rhs.children[1]; - } - - void recalcSizePosRecursive(bool force = false, bool horizontalOverride = false, bool verticalOverride = false) { - if (children[0]) { - static auto PSMARTSPLIT = CConfigValue("dwindle:smart_split"); - static auto PPRESERVESPLIT = CConfigValue("dwindle:preserve_split"); - static auto PFLMULT = CConfigValue("dwindle:split_width_multiplier"); - - if (*PPRESERVESPLIT == 0 && *PSMARTSPLIT == 0) - splitTop = box.h * *PFLMULT > box.w; - - if (verticalOverride) - splitTop = true; - else if (horizontalOverride) - splitTop = false; - - const auto SPLITSIDE = !splitTop; - - if (SPLITSIDE) { - // split left/right - const float FIRSTSIZE = box.w / 2.0 * splitRatio; - children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize(); - children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize(); - } else { - // split top/bottom - const float FIRSTSIZE = box.h / 2.0 * splitRatio; - children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize(); - children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize(); - } - - children[0]->recalcSizePosRecursive(force); - children[1]->recalcSizePosRecursive(force); - } else - pTarget->setPositionGlobal(box); - } -}; - -void CDwindleAlgorithm::newTarget(SP target) { - addTarget(target); -} - -void CDwindleAlgorithm::addTarget(SP target, bool newTarget) { - const auto WORK_AREA = m_parent->space()->workArea(); - - const auto PNODE = m_dwindleNodesData.emplace_back(makeShared()); - PNODE->self = PNODE; - - const auto PMONITOR = m_parent->space()->workspace()->m_monitor; - const auto PWORKSPACE = m_parent->space()->workspace(); - - static auto PUSEACTIVE = CConfigValue("dwindle:use_active_for_splits"); - static auto PDEFAULTSPLIT = CConfigValue("dwindle:default_split_ratio"); - - // Populate the node with our window's data - PNODE->pTarget = target; - PNODE->isNode = false; - - SP OPENINGON; - - const auto MOUSECOORDS = m_overrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal()); - const auto ACTIVE_MON = Desktop::focusState()->monitor(); - - if ((PWORKSPACE == ACTIVE_MON->m_activeWorkspace || (PWORKSPACE->m_isSpecialWorkspace && PMONITOR->m_activeSpecialWorkspace)) && !*PUSEACTIVE) { - OPENINGON = getNodeFromWindow( - g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::SKIP_FULLSCREEN_PRIORITY)); - - if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, ACTIVE_MON)) - OPENINGON = getClosestNode(MOUSECOORDS); - - } else if (*PUSEACTIVE || m_overrideFocalPoint) { - const auto ACTIVE_WINDOW = Desktop::focusState()->window(); - - if (m_overrideFocalPoint) - OPENINGON = getClosestNode(*m_overrideFocalPoint); - else if (!m_overrideFocalPoint && ACTIVE_WINDOW && !ACTIVE_WINDOW->m_isFloating && ACTIVE_WINDOW != target->window() && ACTIVE_WINDOW->m_workspace == PWORKSPACE && - ACTIVE_WINDOW->m_isMapped) - OPENINGON = getNodeFromWindow(ACTIVE_WINDOW); - - if (!OPENINGON) - OPENINGON = getClosestNode(MOUSECOORDS, target); - } else - OPENINGON = getFirstNode(); - - // first, check if OPENINGON isn't too big. - const auto PREDSIZEMAX = OPENINGON ? Vector2D(OPENINGON->box.w, OPENINGON->box.h) : PMONITOR->m_size; - if (const auto MAXSIZE = target->maxSize().value_or(Math::VECTOR2D_MAX); MAXSIZE.x < PREDSIZEMAX.x || MAXSIZE.y < PREDSIZEMAX.y) { - // we can't continue. make it floating. - std::erase(m_dwindleNodesData, PNODE); - m_parent->setFloating(target, true, true); - return; - } - - // last fail-safe to avoid duplicate fullscreens - if ((!OPENINGON || OPENINGON->pTarget.lock() == target) && getNodes() > 1) { - for (auto& node : m_dwindleNodesData) { - if (node->pTarget.lock() && node->pTarget.lock() != target) { - OPENINGON = node; - break; - } - } - } - - // if it's the first, it's easy. Make it fullscreen. - if (!OPENINGON || OPENINGON->pTarget.lock() == target) { - PNODE->box = WORK_AREA; - PNODE->pTarget->setPositionGlobal(PNODE->box); - return; - } - - // get the node under our cursor - - const auto NEWPARENT = m_dwindleNodesData.emplace_back(makeShared()); - - // make the parent have the OPENINGON's stats - NEWPARENT->box = OPENINGON->box; - NEWPARENT->pParent = OPENINGON->pParent; - NEWPARENT->isNode = true; // it is a node - NEWPARENT->splitRatio = std::clamp(*PDEFAULTSPLIT, 0.1F, 1.9F); - - static auto PWIDTHMULTIPLIER = CConfigValue("dwindle:split_width_multiplier"); - - // if cursor over first child, make it first, etc - const auto SIDEBYSIDE = NEWPARENT->box.w > NEWPARENT->box.h * *PWIDTHMULTIPLIER; - NEWPARENT->splitTop = !SIDEBYSIDE; - - static auto PFORCESPLIT = CConfigValue("dwindle:force_split"); - static auto PERMANENTDIRECTIONOVERRIDE = CConfigValue("dwindle:permanent_direction_override"); - static auto PSMARTSPLIT = CConfigValue("dwindle:smart_split"); - static auto PSPLITBIAS = CConfigValue("dwindle:split_bias"); - - bool horizontalOverride = false; - bool verticalOverride = false; - - // let user select position -> top, right, bottom, left - if (m_overrideDirection != Math::DIRECTION_DEFAULT) { - - // this is horizontal - if (m_overrideDirection % 2 == 0) - verticalOverride = true; - else - horizontalOverride = true; - - // 0 -> top and left | 1,2 -> right and bottom - if (m_overrideDirection % 3 == 0) { - NEWPARENT->children[1] = OPENINGON; - NEWPARENT->children[0] = PNODE; - } else { - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; - } - - // whether or not the override persists after opening one window - if (*PERMANENTDIRECTIONOVERRIDE == 0) - m_overrideDirection = Math::DIRECTION_DEFAULT; - } else if (*PSMARTSPLIT == 1 || m_overrideFocalPoint) { - const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2; - const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w; - const auto DELTA = MOUSECOORDS - PARENT_CENTER; - const auto DELTA_SLOPE = DELTA.y / DELTA.x; - - if (abs(DELTA_SLOPE) < PARENT_PROPORTIONS) { - if (DELTA.x > 0) { - // right - NEWPARENT->splitTop = false; - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; - } else { - // left - NEWPARENT->splitTop = false; - NEWPARENT->children[0] = PNODE; - NEWPARENT->children[1] = OPENINGON; - } - } else { - if (DELTA.y > 0) { - // bottom - NEWPARENT->splitTop = true; - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; - } else { - // top - NEWPARENT->splitTop = true; - NEWPARENT->children[0] = PNODE; - NEWPARENT->children[1] = OPENINGON; - } - } - } else if (*PFORCESPLIT == 0 || !newTarget) { - if ((SIDEBYSIDE && MOUSECOORDS.x < NEWPARENT->box.x + (NEWPARENT->box.w / 2.F)) || (!SIDEBYSIDE && MOUSECOORDS.y < NEWPARENT->box.y + (NEWPARENT->box.h / 2.F))) { - // we are hovering over the first node, make PNODE first. - NEWPARENT->children[1] = OPENINGON; - NEWPARENT->children[0] = PNODE; - } else { - // we are hovering over the second node, make PNODE second. - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; - } - } else { - if (*PFORCESPLIT == 1) { - NEWPARENT->children[1] = OPENINGON; - NEWPARENT->children[0] = PNODE; - } else { - NEWPARENT->children[0] = OPENINGON; - NEWPARENT->children[1] = PNODE; - } - } - - // split in favor of a specific window - if (*PSPLITBIAS && NEWPARENT->children[0] == PNODE) - NEWPARENT->splitRatio = 2.f - NEWPARENT->splitRatio; - - // and update the previous parent if it exists - if (OPENINGON->pParent) { - if (OPENINGON->pParent->children[0] == OPENINGON) - OPENINGON->pParent->children[0] = NEWPARENT; - else - OPENINGON->pParent->children[1] = NEWPARENT; - } - - // Update the children - if (!verticalOverride && (NEWPARENT->box.w * *PWIDTHMULTIPLIER > NEWPARENT->box.h || horizontalOverride)) { - // split left/right -> forced - OPENINGON->box = {NEWPARENT->box.pos(), Vector2D(NEWPARENT->box.w / 2.f, NEWPARENT->box.h)}; - PNODE->box = {Vector2D(NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y), Vector2D(NEWPARENT->box.w / 2.f, NEWPARENT->box.h)}; - } else { - // split top/bottom - OPENINGON->box = {NEWPARENT->box.pos(), Vector2D(NEWPARENT->box.w, NEWPARENT->box.h / 2.f)}; - PNODE->box = {Vector2D(NEWPARENT->box.x, NEWPARENT->box.y + NEWPARENT->box.h / 2.f), Vector2D(NEWPARENT->box.w, NEWPARENT->box.h / 2.f)}; - } - - OPENINGON->pParent = NEWPARENT; - PNODE->pParent = NEWPARENT; - - NEWPARENT->recalcSizePosRecursive(false, horizontalOverride, verticalOverride); - - calculateWorkspace(); -} - -void CDwindleAlgorithm::movedTarget(SP target, std::optional focalPoint) { - m_overrideFocalPoint = focalPoint; - addTarget(target, false); - m_overrideFocalPoint.reset(); -} - -void CDwindleAlgorithm::removeTarget(SP target) { - const auto PNODE = getNodeFromTarget(target); - - if (!PNODE) { - Log::logger->log(Log::ERR, "onWindowRemovedTiling node null?"); - return; - } - - if (target->fullscreenMode() != FSMODE_NONE) - g_pCompositor->setWindowFullscreenInternal(target->window(), FSMODE_NONE); - - const auto PPARENT = PNODE->pParent; - - if (!PPARENT) { - Log::logger->log(Log::DEBUG, "Removing last node (dwindle)"); - std::erase(m_dwindleNodesData, PNODE); - return; - } - - const auto PSIBLING = PPARENT->children[0] == PNODE ? PPARENT->children[1] : PPARENT->children[0]; - - PSIBLING->pParent = PPARENT->pParent; - - if (PPARENT->pParent != nullptr) { - if (PPARENT->pParent->children[0] == PPARENT) - PPARENT->pParent->children[0] = PSIBLING; - else - PPARENT->pParent->children[1] = PSIBLING; - } - - PPARENT->valid = false; - PNODE->valid = false; - - std::erase(m_dwindleNodesData, PPARENT); - std::erase(m_dwindleNodesData, PNODE); - - recalculate(); -} - -void CDwindleAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { - if (!validMapped(target->window())) - return; - - const auto PNODE = getNodeFromTarget(target); - - if (!PNODE) - return; - - static auto PANIMATE = CConfigValue("misc:animate_manual_resizes"); - static auto PSMARTRESIZING = CConfigValue("dwindle:smart_resizing"); - - // get some data about our window - const auto PMONITOR = m_parent->space()->workspace()->m_monitor; - const auto MONITOR_WORKAREA = PMONITOR->logicalBoxMinusReserved(); - const auto BOX = target->position(); - const bool DISPLAYLEFT = STICKS(BOX.x, MONITOR_WORKAREA.x); - const bool DISPLAYRIGHT = STICKS(BOX.x + BOX.w, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w); - const bool DISPLAYTOP = STICKS(BOX.y, MONITOR_WORKAREA.y); - const bool DISPLAYBOTTOM = STICKS(BOX.y + BOX.h, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h); - - // construct allowed movement - Vector2D allowedMovement = Δ; - if (DISPLAYLEFT && DISPLAYRIGHT) - allowedMovement.x = 0; - - if (DISPLAYBOTTOM && DISPLAYTOP) - allowedMovement.y = 0; - - if (*PSMARTRESIZING == 1) { - // Identify inner and outer nodes for both directions - SP PVOUTER = nullptr; - SP PVINNER = nullptr; - SP PHOUTER = nullptr; - SP PHINNER = nullptr; - - const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || DISPLAYRIGHT; - const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || DISPLAYBOTTOM; - const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || DISPLAYLEFT; - const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || DISPLAYTOP; - const auto NONE = corner == CORNER_NONE; - - for (auto PCURRENT = PNODE; PCURRENT && PCURRENT->pParent; PCURRENT = PCURRENT->pParent.lock()) { - const auto PPARENT = PCURRENT->pParent; - - if (!PVOUTER && PPARENT->splitTop && (NONE || (TOP && PPARENT->children[1] == PCURRENT) || (BOTTOM && PPARENT->children[0] == PCURRENT))) - PVOUTER = PCURRENT; - else if (!PVOUTER && !PVINNER && PPARENT->splitTop) - PVINNER = PCURRENT; - else if (!PHOUTER && !PPARENT->splitTop && (NONE || (LEFT && PPARENT->children[1] == PCURRENT) || (RIGHT && PPARENT->children[0] == PCURRENT))) - PHOUTER = PCURRENT; - else if (!PHOUTER && !PHINNER && !PPARENT->splitTop) - PHINNER = PCURRENT; - - if (PVOUTER && PHOUTER) - break; - } - - if (PHOUTER) { - PHOUTER->pParent->splitRatio = std::clamp(PHOUTER->pParent->splitRatio + allowedMovement.x * 2.f / PHOUTER->pParent->box.w, 0.1, 1.9); - - if (PHINNER) { - const auto ORIGINAL = PHINNER->box.w; - PHOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); - if (PHINNER->pParent->children[0] == PHINNER) - PHINNER->pParent->splitRatio = std::clamp((ORIGINAL - allowedMovement.x) / PHINNER->pParent->box.w * 2.f, 0.1, 1.9); - else - PHINNER->pParent->splitRatio = std::clamp(2 - (ORIGINAL + allowedMovement.x) / PHINNER->pParent->box.w * 2.f, 0.1, 1.9); - PHINNER->pParent->recalcSizePosRecursive(*PANIMATE == 0); - } else - PHOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); - } - - if (PVOUTER) { - PVOUTER->pParent->splitRatio = std::clamp(PVOUTER->pParent->splitRatio + allowedMovement.y * 2.f / PVOUTER->pParent->box.h, 0.1, 1.9); - - if (PVINNER) { - const auto ORIGINAL = PVINNER->box.h; - PVOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); - if (PVINNER->pParent->children[0] == PVINNER) - PVINNER->pParent->splitRatio = std::clamp((ORIGINAL - allowedMovement.y) / PVINNER->pParent->box.h * 2.f, 0.1, 1.9); - else - PVINNER->pParent->splitRatio = std::clamp(2 - (ORIGINAL + allowedMovement.y) / PVINNER->pParent->box.h * 2.f, 0.1, 1.9); - PVINNER->pParent->recalcSizePosRecursive(*PANIMATE == 0); - } else - PVOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0); - } - } else { - // get the correct containers to apply splitratio to - const auto PPARENT = PNODE->pParent; - - if (!PPARENT) - return; // the only window on a workspace, ignore - - const bool PARENTSIDEBYSIDE = !PPARENT->splitTop; - - // Get the parent's parent - auto PPARENT2 = PPARENT->pParent; - - Hyprutils::Utils::CScopeGuard x([target, this] { - // snap all windows, don't animate resizes if they are manual - if (target == g_layoutManager->dragController()->target()) { - for (const auto& w : m_dwindleNodesData) { - if (w->isNode) - continue; - - w->pTarget->warpPositionSize(); - } - } - }); - - // No parent means we have only 2 windows, and thus one axis of freedom - if (!PPARENT2) { - if (PARENTSIDEBYSIDE) { - allowedMovement.x *= 2.f / PPARENT->box.w; - PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.x, 0.1, 1.9); - PPARENT->recalcSizePosRecursive(*PANIMATE == 0); - } else { - allowedMovement.y *= 2.f / PPARENT->box.h; - PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.y, 0.1, 1.9); - PPARENT->recalcSizePosRecursive(*PANIMATE == 0); - } - - return; - } - - // Get first parent with other split - while (PPARENT2 && PPARENT2->splitTop == !PARENTSIDEBYSIDE) - PPARENT2 = PPARENT2->pParent; - - // no parent, one axis of freedom - if (!PPARENT2) { - if (PARENTSIDEBYSIDE) { - allowedMovement.x *= 2.f / PPARENT->box.w; - PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.x, 0.1, 1.9); - PPARENT->recalcSizePosRecursive(*PANIMATE == 0); - } else { - allowedMovement.y *= 2.f / PPARENT->box.h; - PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.y, 0.1, 1.9); - PPARENT->recalcSizePosRecursive(*PANIMATE == 0); - } - - return; - } - - // 2 axes of freedom - const auto SIDECONTAINER = PARENTSIDEBYSIDE ? PPARENT : PPARENT2; - const auto TOPCONTAINER = PARENTSIDEBYSIDE ? PPARENT2 : PPARENT; - - allowedMovement.x *= 2.f / SIDECONTAINER->box.w; - allowedMovement.y *= 2.f / TOPCONTAINER->box.h; - - SIDECONTAINER->splitRatio = std::clamp(SIDECONTAINER->splitRatio + allowedMovement.x, 0.1, 1.9); - TOPCONTAINER->splitRatio = std::clamp(TOPCONTAINER->splitRatio + allowedMovement.y, 0.1, 1.9); - SIDECONTAINER->recalcSizePosRecursive(*PANIMATE == 0); - TOPCONTAINER->recalcSizePosRecursive(*PANIMATE == 0); - } - - // snap all windows, don't animate resizes if they are manual - if (target == g_layoutManager->dragController()->target()) { - for (const auto& w : m_dwindleNodesData) { - if (w->isNode) - continue; - - w->pTarget->warpPositionSize(); - } - } -} - -SP CDwindleAlgorithm::getNextCandidate(SP old) { - const auto MIDDLE = old->position().middle(); - - if (const auto NODE = getClosestNode(MIDDLE); NODE) - return NODE->pTarget.lock(); - - if (const auto NODE = getFirstNode(); NODE) - return NODE->pTarget.lock(); - - return nullptr; -} - -void CDwindleAlgorithm::swapTargets(SP a, SP b) { - auto nodeA = getNodeFromTarget(a); - auto nodeB = getNodeFromTarget(b); - - if (nodeA) - nodeA->pTarget = b; - if (nodeB) - nodeB->pTarget = a; -} - -void CDwindleAlgorithm::recalculate() { - calculateWorkspace(); -} - -std::optional CDwindleAlgorithm::predictSizeForNewTarget() { - // get window candidate - PHLWINDOW candidate = Desktop::focusState()->window(); - - if (!candidate || candidate->m_workspace != m_parent->space()->workspace()) - candidate = m_parent->space()->workspace()->getFirstWindow(); - - // create a fake node - SDwindleNodeData node; - - if (!candidate) - return Desktop::focusState()->monitor()->m_size; - else { - const auto PNODE = getNodeFromWindow(candidate); - - if (!PNODE) - return {}; - - node = *PNODE; - node.pTarget.reset(); - - CBox box = PNODE->box; - - static auto PFLMULT = CConfigValue("dwindle:split_width_multiplier"); - - bool splitTop = box.h * *PFLMULT > box.w; - - const auto SPLITSIDE = !splitTop; - - if (SPLITSIDE) - node.box = {{}, {box.w / 2.0, box.h}}; - else - node.box = {{}, {box.w, box.h / 2.0}}; - - // TODO: make this better and more accurate - - return node.box.size(); - } - - return {}; -} - -void CDwindleAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - - const auto PNODE = getNodeFromTarget(t); - const Vector2D originalPos = t->position().middle(); - - if (!PNODE || !t->window()) - return; - - const auto FOCAL_POINT = focalPointForDir(t, dir); - - const auto PMONITORFOCAL = g_pCompositor->getMonitorFromVector(FOCAL_POINT.value_or(t->position().middle())); - - if (PMONITORFOCAL != m_parent->space()->workspace()->m_monitor && !*PMONITORFALLBACK) - return; // noop - - t->window()->setAnimationsToMove(); - - removeTarget(t); - - if (PMONITORFOCAL != m_parent->space()->workspace()->m_monitor) { - // move with a focal point - - if (PMONITORFOCAL->m_activeWorkspace) - t->assignToSpace(PMONITORFOCAL->m_activeWorkspace->m_space, FOCAL_POINT); - - return; - } - - movedTarget(t, FOCAL_POINT); - - // restore focus to the previous position - if (silent) { - const auto PNODETOFOCUS = getClosestNode(originalPos); - if (PNODETOFOCUS && PNODETOFOCUS->pTarget) - Desktop::focusState()->fullWindowFocus(PNODETOFOCUS->pTarget->window(), Desktop::FOCUS_REASON_KEYBIND); - } -} - -// --------- internal --------- // - -void CDwindleAlgorithm::calculateWorkspace() { - const auto PWORKSPACE = m_parent->space()->workspace(); - const auto PMONITOR = PWORKSPACE->m_monitor; - - if (!PMONITOR || PWORKSPACE->m_hasFullscreenWindow) - return; - - const auto TOPNODE = getMasterNode(); - - if (TOPNODE) { - TOPNODE->box = m_parent->space()->workArea(); - TOPNODE->recalcSizePosRecursive(); - } -} - -SP CDwindleAlgorithm::getNodeFromTarget(SP t) { - for (const auto& n : m_dwindleNodesData) { - if (n->pTarget == t) - return n; - } - - return nullptr; -} - -SP CDwindleAlgorithm::getNodeFromWindow(PHLWINDOW w) { - return w ? getNodeFromTarget(w->layoutTarget()) : nullptr; -} - -int CDwindleAlgorithm::getNodes() { - return m_dwindleNodesData.size(); -} - -SP CDwindleAlgorithm::getFirstNode() { - return m_dwindleNodesData.empty() ? nullptr : m_dwindleNodesData.at(0); -} - -SP CDwindleAlgorithm::getClosestNode(const Vector2D& point, SP skip) { - SP res = nullptr; - double distClosest = -1; - for (auto& n : m_dwindleNodesData) { - if (skip && n->pTarget == skip) - continue; - - if (n->pTarget && Desktop::View::validMapped(n->pTarget->window())) { - auto distAnother = vecToRectDistanceSquared(point, n->box.pos(), n->box.pos() + n->box.size()); - if (!res || distAnother < distClosest) { - res = n; - distClosest = distAnother; - } - } - } - return res; -} - -SP CDwindleAlgorithm::getMasterNode() { - for (auto& n : m_dwindleNodesData) { - if (!n->pParent) - return n; - } - return nullptr; -} - -std::expected CDwindleAlgorithm::layoutMsg(const std::string_view& sv) { - const auto ARGS = CVarList2(std::string{sv}, 0, ' '); - - const auto CURRENT_NODE = getNodeFromWindow(Desktop::focusState()->window()); - - if (ARGS[0] == "togglesplit") { - if (CURRENT_NODE) { - if (!toggleSplit(CURRENT_NODE)) - return std::unexpected("can't togglesplit in the current workspace"); - } - } else if (ARGS[0] == "swapsplit") { - if (CURRENT_NODE) { - if (!swapSplit(CURRENT_NODE)) - return std::unexpected("can't swapsplit in the current workspace"); - } - } else if (ARGS[0] == "rotatesplit") { - if (CURRENT_NODE) { - int angle = 90; - if (!ARGS[1].empty()) { - try { - angle = std::stoi(std::string{ARGS[1]}); - } catch (const std::exception& e) { - Log::logger->log(Log::WARN, "Invalid angle argument for rotatesplit: {}", ARGS[1]); - return std::unexpected("Invalid angle argument"); - } - } - rotateSplit(CURRENT_NODE, angle); - } - } else if (ARGS[0] == "movetoroot") { - auto node = CURRENT_NODE; - if (!ARGS[1].empty()) { - auto w = g_pCompositor->getWindowByRegex(std::string{ARGS[1]}); - if (w) - node = getNodeFromWindow(w); - } - - const auto STABLE = ARGS[2].empty() || ARGS[2] != "unstable"; - if (!moveToRoot(node, STABLE)) - return std::unexpected("can't movetoroot in the current workspace"); - } else if (ARGS[0] == "preselect") { - auto direction = ARGS[1]; - - if (direction.empty()) { - Log::logger->log(Log::ERR, "Expected direction for preselect"); - return std::unexpected("No direction for preselect"); - } - - switch (direction.front()) { - case 'u': - case 't': { - m_overrideDirection = Math::DIRECTION_UP; - break; - } - case 'd': - case 'b': { - m_overrideDirection = Math::DIRECTION_DOWN; - break; - } - case 'r': { - m_overrideDirection = Math::DIRECTION_RIGHT; - break; - } - case 'l': { - m_overrideDirection = Math::DIRECTION_LEFT; - break; - } - default: { - // any other character resets the focus direction - // needed for the persistent mode - m_overrideDirection = Math::DIRECTION_DEFAULT; - break; - } - } - } else if (ARGS[0] == "splitratio") { - auto ratio = ARGS[1]; - bool exact = ARGS[2].starts_with("exact"); - - if (ratio.empty()) - return std::unexpected("splitratio requires an arg"); - - auto delta = getPlusMinusKeywordResult(std::string{ratio}, 0.F); - - if (!CURRENT_NODE || !CURRENT_NODE->pParent) - return std::unexpected("cannot alter split ratio on no / single node"); - - if (!delta) - return std::unexpected(std::format("failed to parse \"{}\" as a delta", ratio)); - - const float newRatio = exact ? *delta : CURRENT_NODE->pParent->splitRatio + *delta; - CURRENT_NODE->pParent->splitRatio = std::clamp(newRatio, 0.1F, 1.9F); - - CURRENT_NODE->pParent->recalcSizePosRecursive(); - } - - return {}; -} - -bool CDwindleAlgorithm::toggleSplit(SP x) { - if (!x || !x->pParent) - return false; - - if (x->pTarget->fullscreenMode() != FSMODE_NONE) - return false; - - x->pParent->splitTop = !x->pParent->splitTop; - - x->pParent->recalcSizePosRecursive(); - - return true; -} - -bool CDwindleAlgorithm::swapSplit(SP x) { - if (x->pTarget->fullscreenMode() != FSMODE_NONE || !x->pParent) - return false; - - std::swap(x->pParent->children[0], x->pParent->children[1]); - - x->pParent->recalcSizePosRecursive(); - - return true; -} - -void CDwindleAlgorithm::rotateSplit(SP x, int angle) { - if (!x || !x->pParent) - return; - - if (x->pTarget->fullscreenMode() != FSMODE_NONE) - return; - - // normalize the angle to multiples of 90 degrees - int normalizedAngle = ((sc(angle / 90) % 4) + 4) % 4; // ensures positive modulo - - auto pParent = x->pParent; - - bool shouldSwap = false; - - switch (normalizedAngle) { - case 0: // 0 degrees - no change - break; - case 1: - if (pParent->splitTop) - shouldSwap = true; - pParent->splitTop = !pParent->splitTop; - break; - case 2: shouldSwap = true; break; - case 3: - if (!pParent->splitTop) - shouldSwap = true; - pParent->splitTop = !pParent->splitTop; - break; - default: break; // should never happen - } - - if (shouldSwap) - std::swap(pParent->children[0], pParent->children[1]); - - pParent->recalcSizePosRecursive(); -} - -bool CDwindleAlgorithm::moveToRoot(SP x, bool stable) { - if (!x || !x->pParent) - return false; - - if (x->pTarget->fullscreenMode() != FSMODE_NONE) - return false; - - // already at root - if (!x->pParent->pParent) - return false; - - auto& pNode = x->pParent->children[0] == x ? x->pParent->children[0] : x->pParent->children[1]; - - // instead of [getMasterNodeOnWorkspace], we walk back to root since we need - // to know which children of root is our ancestor - auto pAncestor = x, pRoot = x->pParent.lock(); - while (pRoot->pParent) { - pAncestor = pRoot; - pRoot = pRoot->pParent.lock(); - } - - auto& pSwap = pRoot->children[0] == pAncestor ? pRoot->children[1] : pRoot->children[0]; - std::swap(pNode, pSwap); - std::swap(pNode->pParent, pSwap->pParent); - - // [stable] in that the focused window occupies same side of screen - if (stable) - std::swap(pRoot->children[0], pRoot->children[1]); - - pRoot->recalcSizePosRecursive(); - - return true; -} diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp deleted file mode 100644 index 41cbf8bb..00000000 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "../../TiledAlgorithm.hpp" - -namespace Layout { - class CAlgorithm; -} - -namespace Layout::Tiled { - struct SDwindleNodeData; - - class CDwindleAlgorithm : public ITiledAlgorithm { - public: - CDwindleAlgorithm() = default; - virtual ~CDwindleAlgorithm() = default; - - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); - - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(); - - virtual SP getNextCandidate(SP old); - - virtual std::expected layoutMsg(const std::string_view& sv); - virtual std::optional predictSizeForNewTarget(); - - virtual void swapTargets(SP a, SP b); - virtual void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - - private: - std::vector> m_dwindleNodesData; - - struct { - bool started = false; - bool pseudo = false; - bool xExtent = false; - bool yExtent = false; - } m_pseudoDragFlags; - - std::optional m_overrideFocalPoint; // for onWindowCreatedTiling. - - void addTarget(SP target, bool newTarget = true); - void calculateWorkspace(); - SP getNodeFromTarget(SP); - SP getNodeFromWindow(PHLWINDOW w); - int getNodes(); - SP getFirstNode(); - SP getClosestNode(const Vector2D&, SP skip = nullptr); - SP getMasterNode(); - - bool toggleSplit(SP); - bool swapSplit(SP); - void rotateSplit(SP, int angle = 90); - bool moveToRoot(SP, bool stable = true); - - Math::eDirection m_overrideDirection = Math::DIRECTION_DEFAULT; - }; -}; \ No newline at end of file diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp deleted file mode 100644 index 7c436b31..00000000 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp +++ /dev/null @@ -1,1307 +0,0 @@ -#include "MasterAlgorithm.hpp" - -#include "../../Algorithm.hpp" -#include "../../../space/Space.hpp" -#include "../../../target/WindowTarget.hpp" - -#include "../../../../config/ConfigValue.hpp" -#include "../../../../config/ConfigManager.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../helpers/Monitor.hpp" -#include "../../../../Compositor.hpp" -#include "../../../../render/Renderer.hpp" - -#include - -using namespace Layout; -using namespace Layout::Tiled; - -struct Layout::Tiled::SMasterNodeData { - bool isMaster = false; - float percMaster = 0.5f; - - WP pTarget; - - Vector2D position; - Vector2D size; - - float percSize = 1.f; // size multiplier for resizing children - - bool ignoreFullscreenChecks = false; - - // - bool operator==(const SMasterNodeData& rhs) const { - return pTarget.lock() == rhs.pTarget.lock(); - } -}; - -void CMasterAlgorithm::newTarget(SP target) { - addTarget(target, true); -} - -void CMasterAlgorithm::movedTarget(SP target, std::optional focalPoint) { - addTarget(target, false); -} - -void CMasterAlgorithm::addTarget(SP target, bool firstMap) { - static auto PNEWONACTIVE = CConfigValue("master:new_on_active"); - static auto PNEWONTOP = CConfigValue("master:new_on_top"); - static auto PNEWSTATUS = CConfigValue("master:new_status"); - - const auto PWORKSPACE = m_parent->space()->workspace(); - const auto PMONITOR = PWORKSPACE->m_monitor; - - bool dragOntoMaster = false; - - if (g_layoutManager->dragController()->wasDraggingWindow()) { - if (const auto n = getClosestNode(g_pInputManager->getMouseCoordsInternal()); n && n->isMaster) - dragOntoMaster = true; - } - - const bool BNEWBEFOREACTIVE = *PNEWONACTIVE == "before"; - const bool BNEWISMASTER = dragOntoMaster || *PNEWSTATUS == "master"; - - const auto PNODE = [&]() -> SP { - if (*PNEWONACTIVE != "none" && !BNEWISMASTER) { - const auto pLastNode = getNodeFromWindow(Desktop::focusState()->window()); - if (pLastNode && !(pLastNode->isMaster && (getMastersNo() == 1 || *PNEWSTATUS == "slave"))) { - auto it = std::ranges::find(m_masterNodesData, pLastNode); - if (!BNEWBEFOREACTIVE) - ++it; - return *m_masterNodesData.emplace(it, makeShared()); - } - } - return *PNEWONTOP ? *m_masterNodesData.emplace(m_masterNodesData.begin(), makeShared()) : m_masterNodesData.emplace_back(makeShared()); - }(); - - PNODE->pTarget = target; - - const auto WINDOWSONWORKSPACE = getNodesNo(); - static auto PMFACT = CConfigValue("master:mfact"); - float lastSplitPercent = *PMFACT; - - auto OPENINGON = isWindowTiled(Desktop::focusState()->window()) && Desktop::focusState()->window()->m_workspace == PWORKSPACE ? - getNodeFromWindow(Desktop::focusState()->window()) : - getMasterNode(); - - const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); - static auto PDROPATCURSOR = CConfigValue("master:drop_at_cursor"); - eOrientation orientation = getDynamicOrientation(); - const auto NODEIT = std::ranges::find(m_masterNodesData, PNODE); - - bool forceDropAsMaster = false; - // if dragging window to move, drop it at the cursor position instead of bottom/top of stack - if (*PDROPATCURSOR && g_layoutManager->dragController()->mode() == MBIND_MOVE) { - if (WINDOWSONWORKSPACE > 2) { - auto& v = m_masterNodesData; - - const std::size_t srcIndex = static_cast(std::distance(v.begin(), NODEIT)); - - for (std::size_t i = 0; i < v.size(); ++i) { - const CBox box = v[i]->pTarget->position(); - if (!box.containsPoint(MOUSECOORDS)) - continue; - - std::size_t insertIndex = i; - - switch (orientation) { - case ORIENTATION_LEFT: - case ORIENTATION_RIGHT: - if (MOUSECOORDS.y > box.middle().y) - ++insertIndex; // insert after - break; - - case ORIENTATION_TOP: - case ORIENTATION_BOTTOM: - if (MOUSECOORDS.x > box.middle().x) - ++insertIndex; // insert after - break; - - case ORIENTATION_CENTER: break; - - default: UNREACHABLE(); - } - - if (insertIndex > srcIndex) - --insertIndex; - - if (insertIndex == srcIndex) - break; - - auto node = std::move(v[srcIndex]); - v.erase(v.begin() + static_cast(srcIndex)); - v.insert(v.begin() + static_cast(insertIndex), std::move(node)); - - break; - } - } else if (WINDOWSONWORKSPACE == 2) { - // when dropping as the second tiled window in the workspace, - // make it the master only if the cursor is on the master side of the screen - for (auto const& nd : m_masterNodesData) { - if (nd->isMaster) { - const auto MIDDLE = nd->pTarget->position().middle(); - switch (orientation) { - case ORIENTATION_LEFT: - case ORIENTATION_CENTER: - if (MOUSECOORDS.x < MIDDLE.x) - forceDropAsMaster = true; - break; - case ORIENTATION_RIGHT: - if (MOUSECOORDS.x > MIDDLE.x) - forceDropAsMaster = true; - break; - case ORIENTATION_TOP: - if (MOUSECOORDS.y < MIDDLE.y) - forceDropAsMaster = true; - break; - case ORIENTATION_BOTTOM: - if (MOUSECOORDS.y > MIDDLE.y) - forceDropAsMaster = true; - break; - default: UNREACHABLE(); - } - break; - } - } - } - } - - if (BNEWISMASTER // - || WINDOWSONWORKSPACE == 1 // - || (WINDOWSONWORKSPACE > 2 && !firstMap && OPENINGON && OPENINGON->isMaster) // - || forceDropAsMaster // - || (*PNEWSTATUS == "inherit" && OPENINGON && OPENINGON->isMaster && g_layoutManager->dragController()->mode() != MBIND_MOVE)) { - - if (BNEWBEFOREACTIVE) { - for (auto& nd : m_masterNodesData | std::views::reverse) { - if (nd->isMaster) { - nd->isMaster = false; - lastSplitPercent = nd->percMaster; - break; - } - } - } else { - for (auto& nd : m_masterNodesData) { - if (nd->isMaster) { - nd->isMaster = false; - lastSplitPercent = nd->percMaster; - break; - } - } - } - - PNODE->isMaster = true; - PNODE->percMaster = lastSplitPercent; - - // first, check if it isn't too big. - if (const auto MAXSIZE = target->maxSize().value_or(Math::VECTOR2D_MAX); MAXSIZE.x < PMONITOR->m_size.x * lastSplitPercent || MAXSIZE.y < PMONITOR->m_size.y) { - // we can't continue. make it floating. - m_parent->setFloating(target, true, true); - std::erase(m_masterNodesData, PNODE); - return; - } - } else { - PNODE->isMaster = false; - PNODE->percMaster = lastSplitPercent; - - // first, check if it isn't too big. - if (const auto MAXSIZE = target->maxSize().value_or(Math::VECTOR2D_MAX); - MAXSIZE.x < PMONITOR->m_size.x * (1 - lastSplitPercent) || MAXSIZE.y < PMONITOR->m_size.y * (1.f / (WINDOWSONWORKSPACE - 1))) { - // we can't continue. make it floating. - m_parent->setFloating(target, true); - std::erase(m_masterNodesData, PNODE); - return; - } - } - - // recalc - calculateWorkspace(); -} - -void CMasterAlgorithm::removeTarget(SP target) { - const auto MASTERSLEFT = getMastersNo(); - static auto SMALLSPLIT = CConfigValue("master:allow_small_split"); - - const auto PNODE = getNodeFromTarget(target); - - if (target->fullscreenMode() != FSMODE_NONE) - g_pCompositor->setWindowFullscreenInternal(target->window(), FSMODE_NONE); - - if (PNODE->isMaster && (MASTERSLEFT <= 1 || *SMALLSPLIT == 1)) { - // find a new master from top of the list - for (auto& nd : m_masterNodesData) { - if (!nd->isMaster) { - nd->isMaster = true; - nd->percMaster = PNODE->percMaster; - break; - } - } - } - - std::erase(m_masterNodesData, PNODE); - - if (getMastersNo() == getNodesNo() && MASTERSLEFT > 1) { - for (auto& nd : m_masterNodesData | std::views::reverse) { - nd->isMaster = false; - break; - } - } - // BUGFIX: correct bug where closing one master in a stack of 2 would leave - // the screen half bare, and make it difficult to select remaining window - if (getNodesNo() == 1) { - for (auto& nd : m_masterNodesData) { - if (!nd->isMaster) { - nd->isMaster = true; - break; - } - } - } - - calculateWorkspace(); -} - -void CMasterAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { - const auto PNODE = getNodeFromTarget(target); - - if (!PNODE) - return; - - const auto PMONITOR = m_parent->space()->workspace()->m_monitor; - static auto SLAVECOUNTFORCENTER = CConfigValue("master:slave_count_for_center_master"); - static auto PSMARTRESIZING = CConfigValue("master:smart_resizing"); - - const auto WORKAREA = PMONITOR->logicalBoxMinusReserved(); - const bool DISPLAYBOTTOM = STICKS(PNODE->position.y + PNODE->size.y, WORKAREA.y + WORKAREA.h); - const bool DISPLAYRIGHT = STICKS(PNODE->position.x + PNODE->size.x, WORKAREA.x + WORKAREA.w); - const bool DISPLAYTOP = STICKS(PNODE->position.y, WORKAREA.y); - const bool DISPLAYLEFT = STICKS(PNODE->position.x, WORKAREA.x); - - const bool LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT; - const bool TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT; - const bool NONE = corner == CORNER_NONE; - - const auto MASTERS = getMastersNo(); - const auto WINDOWS = getNodesNo(); - const auto STACKWINDOWS = WINDOWS - MASTERS; - - eOrientation orientation = getDynamicOrientation(); - bool centered = orientation == ORIENTATION_CENTER && (STACKWINDOWS >= *SLAVECOUNTFORCENTER); - double delta = 0; - - if (getNodesNo() == 1 && !centered) - return; - - m_forceWarps = true; - - switch (orientation) { - case ORIENTATION_LEFT: delta = Δ.x / PMONITOR->m_size.x; break; - case ORIENTATION_RIGHT: delta = -Δ.x / PMONITOR->m_size.x; break; - case ORIENTATION_BOTTOM: delta = -Δ.y / PMONITOR->m_size.y; break; - case ORIENTATION_TOP: delta = Δ.y / PMONITOR->m_size.y; break; - case ORIENTATION_CENTER: - delta = Δ.x / PMONITOR->m_size.x; - if (STACKWINDOWS >= *SLAVECOUNTFORCENTER) { - if (!NONE || !PNODE->isMaster) - delta *= 2; - if ((!PNODE->isMaster && DISPLAYLEFT) || (PNODE->isMaster && LEFT && *PSMARTRESIZING)) - delta = -delta; - } - break; - default: UNREACHABLE(); - } - - for (auto& n : m_masterNodesData) { - if (n->isMaster) - n->percMaster = std::clamp(n->percMaster + delta, 0.05, 0.95); - } - - // check the up/down resize - const bool isStackVertical = orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT || orientation == ORIENTATION_CENTER; - - const auto RESIZEDELTA = isStackVertical ? Δ.y : Δ.x; - - auto nodesInSameColumn = PNODE->isMaster ? MASTERS : STACKWINDOWS; - if (orientation == ORIENTATION_CENTER && !PNODE->isMaster) - nodesInSameColumn = DISPLAYRIGHT ? (nodesInSameColumn + 1) / 2 : nodesInSameColumn / 2; - - const auto SIZE = isStackVertical ? WORKAREA.h / nodesInSameColumn : WORKAREA.w / nodesInSameColumn; - - if (RESIZEDELTA != 0 && nodesInSameColumn > 1) { - if (!*PSMARTRESIZING) { - PNODE->percSize = std::clamp(PNODE->percSize + RESIZEDELTA / SIZE, 0.05, 1.95); - } else { - const auto NODEIT = std::ranges::find(m_masterNodesData, PNODE); - const auto REVNODEIT = std::ranges::find(m_masterNodesData | std::views::reverse, PNODE); - - const float totalSize = isStackVertical ? WORKAREA.h : WORKAREA.w; - const float minSize = totalSize / nodesInSameColumn * 0.2; - const bool resizePrevNodes = isStackVertical ? (TOP || DISPLAYBOTTOM) && !DISPLAYTOP : (LEFT || DISPLAYRIGHT) && !DISPLAYLEFT; - - int nodesLeft = 0; - float sizeLeft = 0; - int nodeCount = 0; - // check the sizes of all the nodes to be resized for later calculation - auto checkNodesLeft = [&sizeLeft, &nodesLeft, orientation, isStackVertical, &nodeCount, PNODE](auto it) { - if (it->isMaster != PNODE->isMaster) - return; - nodeCount++; - if (!it->isMaster && orientation == ORIENTATION_CENTER && nodeCount % 2 == 1) - return; - sizeLeft += isStackVertical ? it->size.y : it->size.x; - nodesLeft++; - }; - float resizeDiff; - if (resizePrevNodes) { - std::for_each(std::next(REVNODEIT), m_masterNodesData.rend(), checkNodesLeft); - resizeDiff = -RESIZEDELTA; - } else { - std::for_each(std::next(NODEIT), m_masterNodesData.end(), checkNodesLeft); - resizeDiff = RESIZEDELTA; - } - - const float nodeSize = isStackVertical ? PNODE->size.y : PNODE->size.x; - const float maxSizeIncrease = sizeLeft - nodesLeft * minSize; - const float maxSizeDecrease = minSize - nodeSize; - - // leaves enough room for the other nodes - resizeDiff = std::clamp(resizeDiff, maxSizeDecrease, maxSizeIncrease); - PNODE->percSize += resizeDiff / SIZE; - - // resize the other nodes - nodeCount = 0; - auto resizeNodesLeft = [maxSizeIncrease, resizeDiff, minSize, orientation, isStackVertical, SIZE, &nodeCount, nodesLeft, PNODE](auto& it) { - if (it->isMaster != PNODE->isMaster) - return; - nodeCount++; - // if center orientation, only resize when on the same side - if (!it->isMaster && orientation == ORIENTATION_CENTER && nodeCount % 2 == 1) - return; - const float size = isStackVertical ? it->size.y : it->size.x; - const float resizeDeltaForEach = maxSizeIncrease != 0 ? resizeDiff * (size - minSize) / maxSizeIncrease : resizeDiff / nodesLeft; - it->percSize -= resizeDeltaForEach / SIZE; - }; - if (resizePrevNodes) - std::for_each(std::next(REVNODEIT), m_masterNodesData.rend(), resizeNodesLeft); - else - std::for_each(std::next(NODEIT), m_masterNodesData.end(), resizeNodesLeft); - } - } - - recalculate(); - - m_forceWarps = false; -} - -void CMasterAlgorithm::swapTargets(SP a, SP b) { - auto nodeA = getNodeFromTarget(a); - auto nodeB = getNodeFromTarget(b); - - if (nodeA) - nodeA->pTarget = b; - if (nodeB) - nodeB->pTarget = a; -} - -void CMasterAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - - const auto PWINDOW2 = g_pCompositor->getWindowInDirection(t->window(), dir); - - if (!t->window()) - return; - - PHLWORKSPACE targetWs; - - if (!PWINDOW2 && t->space() && t->space()->workspace()) { - // try to find a monitor in dir - const auto PMONINDIR = g_pCompositor->getMonitorInDirection(t->space()->workspace()->m_monitor.lock(), dir); - if (PMONINDIR) - targetWs = PMONINDIR->m_activeWorkspace; - } else - targetWs = PWINDOW2->m_workspace; - - if (!targetWs) - return; - - t->window()->setAnimationsToMove(); - - if (t->window()->m_workspace != targetWs) { - if (!*PMONITORFALLBACK) - return; // noop - - t->assignToSpace(targetWs->m_space, focalPointForDir(t, dir)); - } else if (PWINDOW2) { - // if same monitor, switch windows - g_layoutManager->switchTargets(t, PWINDOW2->layoutTarget()); - if (silent) - Desktop::focusState()->fullWindowFocus(PWINDOW2, Desktop::FOCUS_REASON_KEYBIND); - - recalculate(); - } -} - -void CMasterAlgorithm::recalculate() { - calculateWorkspace(); -} - -std::expected CMasterAlgorithm::layoutMsg(const std::string_view& sv) { - auto switchToWindow = [&](SP target) { - if (!target || !validMapped(target->window())) - return; - - Desktop::focusState()->fullWindowFocus(target->window(), Desktop::FOCUS_REASON_KEYBIND); - g_pCompositor->warpCursorTo(target->position().middle()); - - g_pInputManager->m_forcedFocus = target->window(); - g_pInputManager->simulateMouseMovement(); - g_pInputManager->m_forcedFocus.reset(); - }; - - CVarList2 vars(std::string{sv}, 0, 's'); - - if (vars.size() < 1 || vars[0].empty()) { - Log::logger->log(Log::ERR, "layoutmsg called without params"); - return std::unexpected("layoutmsg without params"); - } - - auto command = vars[0]; - - // swapwithmaster - // first message argument can have the following values: - // * master - keep the focus at the new master - // * child - keep the focus at the new child - // * auto (default) - swap the focus (keep the focus of the previously selected window) - // * ignoremaster - ignore if master is focused - - const auto PWINDOW = Desktop::focusState()->window(); - - if (command == "swapwithmaster") { - if (!PWINDOW) - return std::unexpected("No focused window"); - - if (!isWindowTiled(PWINDOW)) - return std::unexpected("focused window isn't tiled"); - - const auto PMASTER = getMasterNode(); - - if (!PMASTER) - return std::unexpected("no master node"); - - const auto NEWCHILD = PMASTER->pTarget.lock(); - - const bool IGNORE_IF_MASTER = vars.size() >= 2 && std::ranges::any_of(vars, [](const auto& e) { return e == "ignoremaster"; }); - - if (PMASTER->pTarget.lock() != PWINDOW->layoutTarget()) { - const auto& NEWMASTER = PWINDOW->layoutTarget(); - const bool newFocusToChild = vars.size() >= 2 && vars[1] == "child"; - g_layoutManager->switchTargets(NEWMASTER, NEWCHILD); - const auto NEWFOCUS = newFocusToChild ? NEWCHILD : NEWMASTER; - switchToWindow(NEWFOCUS); - } else if (!IGNORE_IF_MASTER) { - for (auto const& n : m_masterNodesData) { - if (!n->isMaster) { - const auto NEWMASTER = n->pTarget.lock(); - g_layoutManager->switchTargets(NEWMASTER, NEWCHILD); - const bool newFocusToMaster = vars.size() >= 2 && vars[1] == "master"; - const auto NEWFOCUS = newFocusToMaster ? NEWMASTER : NEWCHILD; - switchToWindow(NEWFOCUS); - break; - } - } - } - - return {}; - } - // focusmaster - // first message argument can have the following values: - // * master - keep the focus at the new master, even if it was focused before - // * previous - focus window which was previously switched from using `focusmaster previous` command, otherwise fallback to `auto` - // * auto (default) - swap the focus with the first child, if the current focus was master, otherwise focus master - else if (command == "focusmaster") { - if (!PWINDOW) - return std::unexpected("no focused window"); - - const auto PMASTER = getMasterNode(); - - if (!PMASTER) - return std::unexpected("no master"); - - const auto& ARG = vars[1]; // returns empty string if out of bounds - - if (PMASTER->pTarget.lock() != PWINDOW->layoutTarget()) { - switchToWindow(PMASTER->pTarget.lock()); - // save previously focused window (only for `previous` mode) - if (ARG == "previous") - m_workspaceData.focusMasterPrev = PWINDOW->layoutTarget(); - return {}; - } - - const auto focusAuto = [&]() { - // focus first non-master window - for (auto const& n : m_masterNodesData) { - if (!n->isMaster) { - switchToWindow(n->pTarget.lock()); - break; - } - } - }; - - if (ARG == "master") - return {}; - // switch to previously saved window - else if (ARG == "previous") { - const auto PREVWINDOW = m_workspaceData.focusMasterPrev.lock(); - const bool VALID = PREVWINDOW && getNodeFromWindow(PREVWINDOW->window()) && (PWINDOW != PREVWINDOW->window()); - VALID ? switchToWindow(PREVWINDOW) : focusAuto(); - } else - focusAuto(); - } else if (command == "cyclenext") { - if (!PWINDOW) - return std::unexpected("no window"); - - const bool NOLOOP = vars.size() >= 2 && vars[1] == "noloop"; - const auto PNEXTWINDOW = getNextTarget(PWINDOW->layoutTarget(), true, !NOLOOP); - switchToWindow(PNEXTWINDOW); - } else if (command == "cycleprev") { - if (!PWINDOW) - return std::unexpected("no window"); - - const bool NOLOOP = vars.size() >= 2 && vars[1] == "noloop"; - const auto PPREVWINDOW = getNextTarget(PWINDOW->layoutTarget(), false, !NOLOOP); - switchToWindow(PPREVWINDOW); - } else if (command == "swapnext") { - if (!validMapped(PWINDOW)) - return std::unexpected("no window"); - - if (PWINDOW->layoutTarget()->floating()) { - g_pKeybindManager->m_dispatchers["swapnext"](""); - return {}; - } - - const bool NOLOOP = vars.size() >= 2 && vars[1] == "noloop"; - const auto PWINDOWTOSWAPWITH = getNextTarget(PWINDOW->layoutTarget(), true, !NOLOOP); - - if (PWINDOWTOSWAPWITH) { - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - g_layoutManager->switchTargets(PWINDOW->layoutTarget(), PWINDOWTOSWAPWITH); - switchToWindow(PWINDOW->layoutTarget()); - } - } else if (command == "swapprev") { - if (!validMapped(PWINDOW)) - return std::unexpected("no window"); - - if (PWINDOW->layoutTarget()->floating()) { - g_pKeybindManager->m_dispatchers["swapnext"]("prev"); - return {}; - } - - const bool NOLOOP = vars.size() >= 2 && vars[1] == "noloop"; - const auto PWINDOWTOSWAPWITH = getNextTarget(PWINDOW->layoutTarget(), false, !NOLOOP); - - if (PWINDOWTOSWAPWITH) { - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - g_layoutManager->switchTargets(PWINDOW->layoutTarget(), PWINDOWTOSWAPWITH); - switchToWindow(PWINDOW->layoutTarget()); - } - } else if (command == "addmaster") { - if (!validMapped(PWINDOW)) - return std::unexpected("no window"); - - if (PWINDOW->layoutTarget()->floating()) - return std::unexpected("window is floating"); - - const auto PNODE = getNodeFromTarget(PWINDOW->layoutTarget()); - - const auto WINDOWS = getNodesNo(); - const auto MASTERS = getMastersNo(); - static auto SMALLSPLIT = CConfigValue("master:allow_small_split"); - - if (MASTERS + 2 > WINDOWS && *SMALLSPLIT == 0) - return std::unexpected("nothing to do"); - - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - - if (!PNODE || PNODE->isMaster) { - // first non-master node - for (auto& n : m_masterNodesData) { - if (!n->isMaster) { - n->isMaster = true; - break; - } - } - } else { - PNODE->isMaster = true; - } - - calculateWorkspace(); - - } else if (command == "removemaster") { - - if (!validMapped(PWINDOW)) - return std::unexpected("no window"); - - if (PWINDOW->layoutTarget()->floating()) - return std::unexpected("window isnt tiled"); - - const auto PNODE = getNodeFromTarget(PWINDOW->layoutTarget()); - - const auto WINDOWS = getNodesNo(); - const auto MASTERS = getMastersNo(); - - if (WINDOWS < 2 || MASTERS < 2) - return std::unexpected("nothing to do"); - - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - - if (!PNODE || !PNODE->isMaster) { - // first non-master node - for (auto& nd : m_masterNodesData | std::views::reverse) { - if (nd->isMaster) { - nd->isMaster = false; - break; - } - } - } else { - PNODE->isMaster = false; - } - - calculateWorkspace(); - } else if (command == "orientationleft" || command == "orientationright" || command == "orientationtop" || command == "orientationbottom" || command == "orientationcenter") { - if (!PWINDOW) - return std::unexpected("no window"); - - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - - if (command == "orientationleft") - m_workspaceData.explicitOrientation = ORIENTATION_LEFT; - else if (command == "orientationright") - m_workspaceData.explicitOrientation = ORIENTATION_RIGHT; - else if (command == "orientationtop") - m_workspaceData.explicitOrientation = ORIENTATION_TOP; - else if (command == "orientationbottom") - m_workspaceData.explicitOrientation = ORIENTATION_BOTTOM; - else if (command == "orientationcenter") - m_workspaceData.explicitOrientation = ORIENTATION_CENTER; - - calculateWorkspace(); - } else if (command == "orientationnext") { - runOrientationCycle(nullptr, 1); - } else if (command == "orientationprev") { - runOrientationCycle(nullptr, -1); - } else if (command == "orientationcycle") { - runOrientationCycle(&vars, 1); - } else if (command == "mfact") { - - if (!PWINDOW) - return std::unexpected("no window"); - - const bool exact = vars[1] == "exact"; - - float ratio = 0.F; - - try { - ratio = std::stof(std::string{exact ? vars[2] : vars[1]}); - } catch (...) { return std::unexpected("bad ratio"); } - - const auto PNODE = getNodeFromWindow(PWINDOW); - - const auto PMASTER = getMasterNode(); - - float newRatio = exact ? ratio : PMASTER->percMaster + ratio; - PMASTER->percMaster = std::clamp(newRatio, 0.05f, 0.95f); - - recalculate(); - } else if (command == "rollnext") { - const auto PNODE = getNodeFromWindow(PWINDOW); - - if (!PNODE) - return std::unexpected("window couldnt be found"); - - const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNode(); - if (!OLDMASTER) - return std::unexpected("no old master"); - - auto oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER); - - for (auto& nd : m_masterNodesData) { - if (!nd->isMaster) { - const auto& newMaster = nd; - newMaster->isMaster = true; - - auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster); - - if (newMasterIt < oldMasterIt) - std::ranges::rotate(newMasterIt, std::next(newMasterIt), oldMasterIt); - else if (newMasterIt > oldMasterIt) - std::ranges::rotate(oldMasterIt, newMasterIt, std::next(newMasterIt)); - - switchToWindow(newMaster->pTarget.lock()); - OLDMASTER->isMaster = false; - - oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER); - if (oldMasterIt != m_masterNodesData.end()) - std::ranges::rotate(oldMasterIt, std::next(oldMasterIt), m_masterNodesData.end()); - - break; - } - } - - calculateWorkspace(); - } else if (command == "rollprev") { - const auto PNODE = getNodeFromWindow(PWINDOW); - - if (!PNODE) - return std::unexpected("window couldnt be found"); - - const auto OLDMASTER = PNODE->isMaster ? PNODE : getMasterNode(); - if (!OLDMASTER) - return std::unexpected("no old master"); - - auto oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER); - - for (auto& nd : m_masterNodesData | std::views::reverse) { - if (!nd->isMaster) { - const auto& newMaster = nd; - newMaster->isMaster = true; - - auto newMasterIt = std::ranges::find(m_masterNodesData, newMaster); - - if (newMasterIt < oldMasterIt) - std::ranges::rotate(newMasterIt, std::next(newMasterIt), oldMasterIt); - else if (newMasterIt > oldMasterIt) - std::ranges::rotate(oldMasterIt, newMasterIt, std::next(newMasterIt)); - - switchToWindow(newMaster->pTarget.lock()); - OLDMASTER->isMaster = false; - - oldMasterIt = std::ranges::find(m_masterNodesData, OLDMASTER); - if (oldMasterIt != m_masterNodesData.begin()) - std::ranges::rotate(m_masterNodesData.begin(), oldMasterIt, std::next(oldMasterIt)); - - break; - } - } - - calculateWorkspace(); - } - - return {}; -} - -std::optional CMasterAlgorithm::predictSizeForNewTarget() { - static auto PNEWSTATUS = CConfigValue("master:new_status"); - - const auto MONITOR = m_parent->space()->workspace()->m_monitor; - - if (!MONITOR) - return std::nullopt; - - const int NODES = getNodesNo(); - - if (NODES <= 0) - return Desktop::focusState()->monitor()->m_size; - - const auto MASTER = getMasterNode(); - if (!MASTER) // wtf - return std::nullopt; - - if (*PNEWSTATUS == "master") { - return MASTER->size; - } else { - const auto SLAVES = NODES - getMastersNo(); - - // TODO: make this better - if (SLAVES == 0) - return Vector2D{MONITOR->m_size.x / 2.F, MONITOR->m_size.y}; - else - return Vector2D{MONITOR->m_size.x - MASTER->size.x, MONITOR->m_size.y / (SLAVES + 1)}; - } - - return std::nullopt; -} - -void CMasterAlgorithm::buildOrientationCycleVectorFromVars(std::vector& cycle, Hyprutils::String::CVarList2* vars) { - for (size_t i = 1; i < vars->size(); ++i) { - if ((*vars)[i] == "top") { - cycle.emplace_back(ORIENTATION_TOP); - } else if ((*vars)[i] == "right") { - cycle.emplace_back(ORIENTATION_RIGHT); - } else if ((*vars)[i] == "bottom") { - cycle.emplace_back(ORIENTATION_BOTTOM); - } else if ((*vars)[i] == "left") { - cycle.emplace_back(ORIENTATION_LEFT); - } else if ((*vars)[i] == "center") { - cycle.emplace_back(ORIENTATION_CENTER); - } - } -} - -void CMasterAlgorithm::buildOrientationCycleVectorFromEOperation(std::vector& cycle) { - for (int i = 0; i <= ORIENTATION_CENTER; ++i) { - cycle.push_back(sc(i)); - } -} - -eOrientation CMasterAlgorithm::defaultOrientation() { - static auto PORIENT = CConfigValue("master:orientation"); - - const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_parent->space()->workspace()); - std::string orientationString; - if (WORKSPACERULE.layoutopts.contains("orientation")) - orientationString = WORKSPACERULE.layoutopts.at("orientation"); - else - orientationString = *PORIENT; - - eOrientation orientation = ORIENTATION_LEFT; - // override if workspace rule is set - if (!orientationString.empty()) { - if (orientationString == "top") - orientation = ORIENTATION_TOP; - else if (orientationString == "right") - orientation = ORIENTATION_RIGHT; - else if (orientationString == "bottom") - orientation = ORIENTATION_BOTTOM; - else if (orientationString == "center") - orientation = ORIENTATION_CENTER; - else - orientation = ORIENTATION_LEFT; - } - - return orientation; -} - -void CMasterAlgorithm::runOrientationCycle(Hyprutils::String::CVarList2* vars, int next) { - std::vector cycle; - if (vars != nullptr) - buildOrientationCycleVectorFromVars(cycle, vars); - - if (cycle.empty()) - buildOrientationCycleVectorFromEOperation(cycle); - - const auto PWINDOW = Desktop::focusState()->window(); - - if (!PWINDOW) - return; - - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - - int nextOrPrev = 0; - for (size_t i = 0; i < cycle.size(); ++i) { - if (m_workspaceData.explicitOrientation.value_or(defaultOrientation()) == cycle[i]) { - nextOrPrev = i + next; - break; - } - } - - if (nextOrPrev >= sc(cycle.size())) - nextOrPrev = nextOrPrev % sc(cycle.size()); - else if (nextOrPrev < 0) - nextOrPrev = cycle.size() + (nextOrPrev % sc(cycle.size())); - - m_workspaceData.explicitOrientation = cycle.at(nextOrPrev); - calculateWorkspace(); -} - -eOrientation CMasterAlgorithm::getDynamicOrientation() { - return m_workspaceData.explicitOrientation.value_or(defaultOrientation()); -} - -int CMasterAlgorithm::getNodesNo() { - return m_masterNodesData.size(); -} - -SP CMasterAlgorithm::getNodeFromWindow(PHLWINDOW x) { - return x ? getNodeFromTarget(x->layoutTarget()) : nullptr; -} - -SP CMasterAlgorithm::getNodeFromTarget(SP x) { - for (const auto& n : m_masterNodesData) { - if (n->pTarget == x) - return n; - } - - return nullptr; -} - -SP CMasterAlgorithm::getMasterNode() { - for (const auto& n : m_masterNodesData) { - if (n->isMaster) - return n; - } - - return nullptr; -} - -void CMasterAlgorithm::calculateWorkspace() { - const auto PMASTERNODE = getMasterNode(); - - if (!PMASTERNODE) - return; - - Hyprutils::Utils::CScopeGuard x([this] { - g_pHyprRenderer->damageMonitor(m_parent->space()->workspace()->m_monitor.lock()); - - if (!m_forceWarps) - return; - - for (const auto& n : m_masterNodesData) { - n->pTarget->warpPositionSize(); - } - }); - - eOrientation orientation = getDynamicOrientation(); - bool centerMasterWindow = false; - static auto SLAVECOUNTFORCENTER = CConfigValue("master:slave_count_for_center_master"); - static auto CMFALLBACK = CConfigValue("master:center_master_fallback"); - static auto PIGNORERESERVED = CConfigValue("master:center_ignores_reserved"); - static auto PSMARTRESIZING = CConfigValue("master:smart_resizing"); - - const auto MASTERS = getMastersNo(); - const auto WINDOWS = getNodesNo(); - const auto STACKWINDOWS = WINDOWS - MASTERS; - const auto WORKAREA = m_parent->space()->workArea(); - const auto PMONITOR = m_parent->space()->workspace()->m_monitor; - const auto reservedLeft = PMONITOR ? PMONITOR->m_reservedArea.left() : 0; - const auto reservedRight = PMONITOR ? PMONITOR->m_reservedArea.right() : 0; - const auto UNRESERVED_WIDTH = WORKAREA.width + reservedLeft + reservedRight; - - if (orientation == ORIENTATION_CENTER) { - if (STACKWINDOWS >= *SLAVECOUNTFORCENTER) - centerMasterWindow = true; - else { - if (*CMFALLBACK == "left") - orientation = ORIENTATION_LEFT; - else if (*CMFALLBACK == "right") - orientation = ORIENTATION_RIGHT; - else if (*CMFALLBACK == "top") - orientation = ORIENTATION_TOP; - else if (*CMFALLBACK == "bottom") - orientation = ORIENTATION_BOTTOM; - else - orientation = ORIENTATION_LEFT; - } - } - - const float totalSize = (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) ? WORKAREA.w : WORKAREA.h; - const float masterAverageSize = totalSize / MASTERS; - const float slaveAverageSize = totalSize / STACKWINDOWS; - float masterAccumulatedSize = 0; - float slaveAccumulatedSize = 0; - - if (*PSMARTRESIZING) { - // check the total width and height so that later - // if larger/smaller than screen size them down/up - for (auto const& nd : m_masterNodesData) { - if (nd->isMaster) - masterAccumulatedSize += totalSize / MASTERS * nd->percSize; - else - slaveAccumulatedSize += totalSize / STACKWINDOWS * nd->percSize; - } - } - - // compute placement of master window(s) - if (WINDOWS == 1 && !centerMasterWindow) { - static auto PALWAYSKEEPPOSITION = CConfigValue("master:always_keep_position"); - if (*PALWAYSKEEPPOSITION) { - const float WIDTH = WORKAREA.w * PMASTERNODE->percMaster; - float nextX = 0; - - if (orientation == ORIENTATION_RIGHT) - nextX = WORKAREA.w - WIDTH; - else if (orientation == ORIENTATION_CENTER) - nextX = (WORKAREA.w - WIDTH) / 2; - - PMASTERNODE->size = Vector2D(WIDTH, WORKAREA.h); - PMASTERNODE->position = WORKAREA.pos() + Vector2D(nextX, 0.0); - } else { - PMASTERNODE->size = WORKAREA.size(); - PMASTERNODE->position = WORKAREA.pos(); - } - - PMASTERNODE->pTarget->setPositionGlobal({PMASTERNODE->position, PMASTERNODE->size}); - return; - } else if (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) { - const float HEIGHT = STACKWINDOWS != 0 ? WORKAREA.h * PMASTERNODE->percMaster : WORKAREA.h; - float widthLeft = WORKAREA.w; - int mastersLeft = MASTERS; - float nextX = 0; - float nextY = 0; - - if (orientation == ORIENTATION_BOTTOM) - nextY = WORKAREA.h - HEIGHT; - - for (auto& nd : m_masterNodesData) { - if (!nd->isMaster) - continue; - - float WIDTH = mastersLeft > 1 ? widthLeft / mastersLeft * nd->percSize : widthLeft; - if (WIDTH > widthLeft * 0.9f && mastersLeft > 1) - WIDTH = widthLeft * 0.9f; - - if (*PSMARTRESIZING) { - nd->percSize *= WORKAREA.w / masterAccumulatedSize; - WIDTH = masterAverageSize * nd->percSize; - } - - nd->size = Vector2D(WIDTH, HEIGHT); - nd->position = WORKAREA.pos() + Vector2D(nextX, nextY); - nd->pTarget->setPositionGlobal({nd->position, nd->size}); - - mastersLeft--; - widthLeft -= WIDTH; - nextX += WIDTH; - } - } else { // orientation left, right or center - const float TOTAL_WIDTH = *PIGNORERESERVED && centerMasterWindow ? UNRESERVED_WIDTH : WORKAREA.w; - float WIDTH = TOTAL_WIDTH; - float heightLeft = WORKAREA.h; - int mastersLeft = MASTERS; - float nextX = 0; - float nextY = 0; - - if (STACKWINDOWS > 0 || centerMasterWindow) - WIDTH *= PMASTERNODE->percMaster; - - if (orientation == ORIENTATION_RIGHT) - nextX = WORKAREA.w - WIDTH; - else if (centerMasterWindow) - nextX += (TOTAL_WIDTH - WIDTH) / 2; - - for (auto& nd : m_masterNodesData) { - if (!nd->isMaster) - continue; - - float HEIGHT = mastersLeft > 1 ? heightLeft / mastersLeft * nd->percSize : heightLeft; - if (HEIGHT > heightLeft * 0.9f && mastersLeft > 1) - HEIGHT = heightLeft * 0.9f; - - if (*PSMARTRESIZING) { - nd->percSize *= WORKAREA.h / masterAccumulatedSize; - HEIGHT = masterAverageSize * nd->percSize; - } - - nd->size = Vector2D(WIDTH, HEIGHT); - nd->position = (*PIGNORERESERVED && centerMasterWindow ? WORKAREA.pos() - Vector2D(reservedLeft, 0.0) : WORKAREA.pos()) + Vector2D(nextX, nextY); - nd->pTarget->setPositionGlobal({nd->position, nd->size}); - - mastersLeft--; - heightLeft -= HEIGHT; - nextY += HEIGHT; - } - } - - if (STACKWINDOWS == 0) - return; - - // compute placement of slave window(s) - int slavesLeft = STACKWINDOWS; - if (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) { - const float HEIGHT = WORKAREA.h - PMASTERNODE->size.y; - float widthLeft = WORKAREA.w; - float nextX = 0; - float nextY = 0; - - if (orientation == ORIENTATION_TOP) - nextY = PMASTERNODE->size.y; - - for (auto& nd : m_masterNodesData) { - if (nd->isMaster) - continue; - - float WIDTH = slavesLeft > 1 ? widthLeft / slavesLeft * nd->percSize : widthLeft; - if (WIDTH > widthLeft * 0.9f && slavesLeft > 1) - WIDTH = widthLeft * 0.9f; - - if (*PSMARTRESIZING) { - nd->percSize *= WORKAREA.w / slaveAccumulatedSize; - WIDTH = slaveAverageSize * nd->percSize; - } - - nd->size = Vector2D(WIDTH, HEIGHT); - nd->position = WORKAREA.pos() + Vector2D(nextX, nextY); - nd->pTarget->setPositionGlobal({nd->position, nd->size}); - - slavesLeft--; - widthLeft -= WIDTH; - nextX += WIDTH; - } - } else if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT) { - const float WIDTH = WORKAREA.w - PMASTERNODE->size.x; - float heightLeft = WORKAREA.h; - float nextY = 0; - float nextX = 0; - - if (orientation == ORIENTATION_LEFT) - nextX = PMASTERNODE->size.x; - - for (auto& nd : m_masterNodesData) { - if (nd->isMaster) - continue; - - float HEIGHT = slavesLeft > 1 ? heightLeft / slavesLeft * nd->percSize : heightLeft; - if (HEIGHT > heightLeft * 0.9f && slavesLeft > 1) - HEIGHT = heightLeft * 0.9f; - - if (*PSMARTRESIZING) { - nd->percSize *= WORKAREA.h / slaveAccumulatedSize; - HEIGHT = slaveAverageSize * nd->percSize; - } - - nd->size = Vector2D(WIDTH, HEIGHT); - nd->position = WORKAREA.pos() + Vector2D(nextX, nextY); - nd->pTarget->setPositionGlobal({nd->position, nd->size}); - - slavesLeft--; - heightLeft -= HEIGHT; - nextY += HEIGHT; - } - } else { // slaves for centered master window(s) - const float WIDTH = ((*PIGNORERESERVED ? UNRESERVED_WIDTH : WORKAREA.w) - PMASTERNODE->size.x) / 2.0; - float heightLeft = 0; - float heightLeftL = WORKAREA.h; - float heightLeftR = WORKAREA.h; - float nextX = 0; - float nextY = 0; - float nextYL = 0; - float nextYR = 0; - bool onRight = *CMFALLBACK == "right"; - int slavesLeftL = 1 + (slavesLeft - 1) / 2; - int slavesLeftR = slavesLeft - slavesLeftL; - - if (onRight) { - slavesLeftR = 1 + (slavesLeft - 1) / 2; - slavesLeftL = slavesLeft - slavesLeftR; - } - - const float slaveAverageHeightL = WORKAREA.h / slavesLeftL; - const float slaveAverageHeightR = WORKAREA.h / slavesLeftR; - float slaveAccumulatedHeightL = 0; - float slaveAccumulatedHeightR = 0; - - if (*PSMARTRESIZING) { - for (auto const& nd : m_masterNodesData) { - if (nd->isMaster) - continue; - - if (onRight) - slaveAccumulatedHeightR += slaveAverageHeightR * nd->percSize; - else - slaveAccumulatedHeightL += slaveAverageHeightL * nd->percSize; - - onRight = !onRight; - } - - onRight = *CMFALLBACK == "right"; - } - - for (auto& nd : m_masterNodesData) { - if (nd->isMaster) - continue; - - if (onRight) { - nextX = WIDTH + PMASTERNODE->size.x - (*PIGNORERESERVED ? reservedLeft : 0); - nextY = nextYR; - heightLeft = heightLeftR; - slavesLeft = slavesLeftR; - } else { - nextX = 0; - nextY = nextYL; - heightLeft = heightLeftL; - slavesLeft = slavesLeftL; - } - - float HEIGHT = slavesLeft > 1 ? heightLeft / slavesLeft * nd->percSize : heightLeft; - if (HEIGHT > heightLeft * 0.9f && slavesLeft > 1) - HEIGHT = heightLeft * 0.9f; - - if (*PSMARTRESIZING) { - if (onRight) { - nd->percSize *= WORKAREA.h / slaveAccumulatedHeightR; - HEIGHT = slaveAverageHeightR * nd->percSize; - } else { - nd->percSize *= WORKAREA.h / slaveAccumulatedHeightL; - HEIGHT = slaveAverageHeightL * nd->percSize; - } - } - - nd->size = Vector2D(*PIGNORERESERVED ? (WIDTH - (onRight ? reservedRight : reservedLeft)) : WIDTH, HEIGHT); - nd->position = WORKAREA.pos() + Vector2D(nextX, nextY); - nd->pTarget->setPositionGlobal({nd->position, nd->size}); - - if (onRight) { - heightLeftR -= HEIGHT; - nextYR += HEIGHT; - slavesLeftR--; - } else { - heightLeftL -= HEIGHT; - nextYL += HEIGHT; - slavesLeftL--; - } - - onRight = !onRight; - } - } -} - -SP CMasterAlgorithm::getNextCandidate(SP old) { - const auto MIDDLE = old->position().middle(); - - if (const auto NODE = getClosestNode(MIDDLE); NODE) - return NODE->pTarget.lock(); - - if (const auto NODE = getMasterNode(); NODE) - return NODE->pTarget.lock(); - - return nullptr; -} - -SP CMasterAlgorithm::getNextTarget(SP t, bool next, bool loop) { - if (t->floating()) - return nullptr; - - const auto PNODE = getNodeFromTarget(t); - - auto nodes = m_masterNodesData; - if (!next) - std::ranges::reverse(nodes); - - const auto NODEIT = std::ranges::find(nodes, PNODE); - - const bool ISMASTER = PNODE->isMaster; - - auto CANDIDATE = std::find_if(NODEIT, nodes.end(), [&](const auto& other) { return other != PNODE && ISMASTER == other->isMaster; }); - if (CANDIDATE == nodes.end()) - CANDIDATE = std::ranges::find_if(nodes, [&](const auto& other) { return other != PNODE && ISMASTER != other->isMaster; }); - - if (CANDIDATE != nodes.end() && !loop) { - if ((*CANDIDATE)->isMaster && next) - return nullptr; - if (!(*CANDIDATE)->isMaster && ISMASTER && !next) - return nullptr; - } - - return CANDIDATE == nodes.end() ? nullptr : (*CANDIDATE)->pTarget.lock(); -} - -int CMasterAlgorithm::getMastersNo() { - return std::ranges::count_if(m_masterNodesData, [](const auto& n) { return n->isMaster; }); -} - -bool CMasterAlgorithm::isWindowTiled(PHLWINDOW x) { - return x && !x->layoutTarget()->floating(); -} - -SP CMasterAlgorithm::getClosestNode(const Vector2D& point) { - SP res = nullptr; - double distClosest = -1; - for (auto& n : m_masterNodesData) { - if (n->pTarget && Desktop::View::validMapped(n->pTarget->window())) { - auto distAnother = vecToRectDistanceSquared(point, n->position, n->position + n->size); - if (!res || distAnother < distClosest) { - res = n; - distClosest = distAnother; - } - } - } - return res; -} diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp deleted file mode 100644 index 5cfa6b36..00000000 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "../../TiledAlgorithm.hpp" - -#include - -namespace Layout { - class CAlgorithm; -} - -namespace Layout::Tiled { - struct SMasterNodeData; - - //orientation determines which side of the screen the master area resides - enum eOrientation : uint8_t { - ORIENTATION_LEFT = 0, - ORIENTATION_TOP, - ORIENTATION_RIGHT, - ORIENTATION_BOTTOM, - ORIENTATION_CENTER - }; - - struct SMasterWorkspaceData { - WORKSPACEID workspaceID = WORKSPACE_INVALID; - std::optional explicitOrientation; - // Previously focused non-master window when `focusmaster previous` command was issued - WP focusMasterPrev; - - // - bool operator==(const SMasterWorkspaceData& rhs) const { - return workspaceID == rhs.workspaceID; - } - }; - - class CMasterAlgorithm : public ITiledAlgorithm { - public: - CMasterAlgorithm() = default; - virtual ~CMasterAlgorithm() = default; - - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); - - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(); - - virtual SP getNextCandidate(SP old); - - virtual std::expected layoutMsg(const std::string_view& sv); - virtual std::optional predictSizeForNewTarget(); - - virtual void swapTargets(SP a, SP b); - virtual void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - - private: - std::vector> m_masterNodesData; - SMasterWorkspaceData m_workspaceData; - - void addTarget(SP target, bool firstMap); - - bool m_forceWarps = false; - - void buildOrientationCycleVectorFromVars(std::vector& cycle, Hyprutils::String::CVarList2* vars); - void buildOrientationCycleVectorFromEOperation(std::vector& cycle); - void runOrientationCycle(Hyprutils::String::CVarList2* vars, int next); - eOrientation getDynamicOrientation(); - int getNodesNo(); - SP getNodeFromWindow(PHLWINDOW); - SP getNodeFromTarget(SP); - SP getMasterNode(); - SP getClosestNode(const Vector2D&); - void calculateWorkspace(); - SP getNextTarget(SP, bool, bool); - int getMastersNo(); - bool isWindowTiled(PHLWINDOW); - eOrientation defaultOrientation(); - }; -}; \ No newline at end of file diff --git a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp deleted file mode 100644 index fe92f27c..00000000 --- a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp +++ /dev/null @@ -1,278 +0,0 @@ -#include "MonocleAlgorithm.hpp" - -#include "../../Algorithm.hpp" -#include "../../../space/Space.hpp" -#include "../../../target/WindowTarget.hpp" -#include "../../../LayoutManager.hpp" - -#include "../../../../config/ConfigValue.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../desktop/history/WindowHistoryTracker.hpp" -#include "../../../../helpers/Monitor.hpp" -#include "../../../../Compositor.hpp" -#include "../../../../event/EventBus.hpp" - -#include -#include -#include - -using namespace Hyprutils::String; -using namespace Hyprutils::Utils; -using namespace Layout; -using namespace Layout::Tiled; - -CMonocleAlgorithm::CMonocleAlgorithm() { - // hook into focus changes to bring focused window to front - m_focusCallback = Event::bus()->m_events.window.active.listen([this](PHLWINDOW pWindow, Desktop::eFocusReason reason) { - if (!pWindow) - return; - - if (!pWindow->m_workspace->isVisible()) - return; - - const auto TARGET = pWindow->layoutTarget(); - if (!TARGET) - return; - - focusTargetUpdate(TARGET); - }); -} - -CMonocleAlgorithm::~CMonocleAlgorithm() { - // unhide all windows before destruction - for (const auto& data : m_targetDatas) { - const auto TARGET = data->target.lock(); - if (!TARGET) - continue; - - const auto WINDOW = TARGET->window(); - if (WINDOW) - WINDOW->setHidden(false); - } - - m_focusCallback.reset(); -} - -SP CMonocleAlgorithm::dataFor(SP t) { - for (auto& data : m_targetDatas) { - if (data->target.lock() == t) - return data; - } - return nullptr; -} - -void CMonocleAlgorithm::newTarget(SP target) { - const auto DATA = m_targetDatas.emplace_back(makeShared(target)); - - m_currentVisibleIndex = m_targetDatas.size() - 1; - - recalculate(); -} - -void CMonocleAlgorithm::movedTarget(SP target, std::optional focalPoint) { - newTarget(target); -} - -void CMonocleAlgorithm::removeTarget(SP target) { - auto it = std::ranges::find_if(m_targetDatas, [target](const auto& data) { return data->target.lock() == target; }); - - if (it == m_targetDatas.end()) - return; - - // unhide window when removing from monocle layout - const auto WINDOW = target->window(); - if (WINDOW) - WINDOW->setHidden(false); - - const auto INDEX = std::distance(m_targetDatas.begin(), it); - m_targetDatas.erase(it); - - if (m_targetDatas.empty()) { - m_currentVisibleIndex = 0; - return; - } - - // try to use the last window in history if we can - for (const auto& historyWindow : Desktop::History::windowTracker()->historyForWorkspace(m_parent->space()->workspace()) | std::views::reverse) { - auto it = std::ranges::find_if(m_targetDatas, [&historyWindow](const auto& d) { return d->target == historyWindow->layoutTarget(); }); - - if (it == m_targetDatas.end()) - continue; - - // we found a historical target, use that first - m_currentVisibleIndex = std::distance(m_targetDatas.begin(), it); - - recalculate(); - - return; - } - - // if we didn't find history, fall back to last - - if (m_currentVisibleIndex >= (int)m_targetDatas.size()) - m_currentVisibleIndex = m_targetDatas.size() - 1; - else if (INDEX <= m_currentVisibleIndex && m_currentVisibleIndex > 0) - m_currentVisibleIndex--; - - recalculate(); -} - -void CMonocleAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { - // monocle layout doesn't support manual resizing, all windows are fullscreen -} - -void CMonocleAlgorithm::recalculate() { - if (m_targetDatas.empty()) - return; - - const auto WORK_AREA = m_parent->space()->workArea(); - - for (size_t i = 0; i < m_targetDatas.size(); ++i) { - const auto& DATA = m_targetDatas[i]; - const auto TARGET = DATA->target.lock(); - - if (!TARGET) - continue; - - const auto WINDOW = TARGET->window(); - if (!WINDOW) - continue; - - DATA->layoutBox = WORK_AREA; - TARGET->setPositionGlobal(WORK_AREA); - - const bool SHOULD_BE_VISIBLE = ((int)i == m_currentVisibleIndex); - WINDOW->setHidden(!SHOULD_BE_VISIBLE); - } -} - -SP CMonocleAlgorithm::getNextCandidate(SP old) { - if (m_targetDatas.empty()) - return nullptr; - - auto it = std::ranges::find_if(m_targetDatas, [old](const auto& data) { return data->target.lock() == old; }); - - if (it == m_targetDatas.end()) { - if (m_currentVisibleIndex >= 0 && m_currentVisibleIndex < (int)m_targetDatas.size()) - return m_targetDatas[m_currentVisibleIndex]->target.lock(); - return nullptr; - } - - auto next = std::next(it); - if (next == m_targetDatas.end()) - next = m_targetDatas.begin(); - - return next->get()->target.lock(); -} - -std::expected CMonocleAlgorithm::layoutMsg(const std::string_view& sv) { - CVarList2 vars(std::string{sv}, 0, 's'); - - if (vars.size() < 1) - return std::unexpected("layoutmsg requires at least 1 argument"); - - const auto COMMAND = vars[0]; - - if (COMMAND == "cyclenext") { - cycleNext(); - return {}; - } else if (COMMAND == "cycleprev") { - cyclePrev(); - return {}; - } - - return std::unexpected(std::format("Unknown monocle layoutmsg: {}", COMMAND)); -} - -std::optional CMonocleAlgorithm::predictSizeForNewTarget() { - const auto WORK_AREA = m_parent->space()->workArea(); - return WORK_AREA.size(); -} - -void CMonocleAlgorithm::swapTargets(SP a, SP b) { - auto nodeA = dataFor(a); - auto nodeB = dataFor(b); - - if (nodeA) - nodeA->target = b; - if (nodeB) - nodeB->target = a; - - recalculate(); -} - -void CMonocleAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - - if (!*PMONITORFALLBACK) - return; // noop - - // try to find a monitor in the specified direction, thats the logical thing - if (!t || !t->space() || !t->space()->workspace()) - return; - - const auto PMONINDIR = g_pCompositor->getMonitorInDirection(t->space()->workspace()->m_monitor.lock(), dir); - - // if we found a monitor, move the window there - if (PMONINDIR && PMONINDIR != t->space()->workspace()->m_monitor.lock()) { - const auto TARGETWS = PMONINDIR->m_activeWorkspace; - - if (t->window()) - t->window()->setAnimationsToMove(); - - t->assignToSpace(TARGETWS->m_space, focalPointForDir(t, dir)); - } -} - -void CMonocleAlgorithm::cycleNext() { - if (m_targetDatas.empty()) - return; - - m_currentVisibleIndex = (m_currentVisibleIndex + 1) % m_targetDatas.size(); - updateVisible(); -} - -void CMonocleAlgorithm::cyclePrev() { - if (m_targetDatas.empty()) - return; - - m_currentVisibleIndex--; - if (m_currentVisibleIndex < 0) - m_currentVisibleIndex = m_targetDatas.size() - 1; - updateVisible(); -} - -void CMonocleAlgorithm::focusTargetUpdate(SP target) { - auto it = std::ranges::find_if(m_targetDatas, [target](const auto& data) { return data->target.lock() == target; }); - - if (it == m_targetDatas.end()) - return; - - const auto NEW_INDEX = std::distance(m_targetDatas.begin(), it); - - if (m_currentVisibleIndex != NEW_INDEX) { - m_currentVisibleIndex = NEW_INDEX; - updateVisible(); - } -} - -void CMonocleAlgorithm::updateVisible() { - recalculate(); - - const auto VISIBLE_TARGET = getVisibleTarget(); - if (!VISIBLE_TARGET) - return; - - const auto WINDOW = VISIBLE_TARGET->window(); - if (!WINDOW) - return; - - Desktop::focusState()->fullWindowFocus(WINDOW, Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE); -} - -SP CMonocleAlgorithm::getVisibleTarget() { - if (m_currentVisibleIndex < 0 || m_currentVisibleIndex >= (int)m_targetDatas.size()) - return nullptr; - - return m_targetDatas[m_currentVisibleIndex]->target.lock(); -} diff --git a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp deleted file mode 100644 index b23f85be..00000000 --- a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "../../TiledAlgorithm.hpp" -#include "../../../../helpers/signal/Signal.hpp" - -#include - -namespace Layout::Tiled { - - struct SMonocleTargetData { - SMonocleTargetData(SP t) : target(t) { - ; - } - - WP target; - CBox layoutBox; - }; - - class CMonocleAlgorithm : public ITiledAlgorithm { - public: - CMonocleAlgorithm(); - virtual ~CMonocleAlgorithm(); - - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); - - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(); - - virtual SP getNextCandidate(SP old); - - virtual std::expected layoutMsg(const std::string_view& sv); - virtual std::optional predictSizeForNewTarget(); - - virtual void swapTargets(SP a, SP b); - virtual void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - - private: - std::vector> m_targetDatas; - CHyprSignalListener m_focusCallback; - - int m_currentVisibleIndex = 0; - - SP dataFor(SP t); - void cycleNext(); - void cyclePrev(); - void focusTargetUpdate(SP target); - void updateVisible(); - SP getVisibleTarget(); - }; -}; diff --git a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp deleted file mode 100644 index 93a7dac1..00000000 --- a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp +++ /dev/null @@ -1,298 +0,0 @@ -#include "ScrollTapeController.hpp" -#include "ScrollingAlgorithm.hpp" -#include -#include - -using namespace Layout::Tiled; - -CScrollTapeController::CScrollTapeController(eScrollDirection direction) : m_direction(direction) { - ; -} - -void CScrollTapeController::setDirection(eScrollDirection dir) { - m_direction = dir; -} - -eScrollDirection CScrollTapeController::getDirection() const { - return m_direction; -} - -bool CScrollTapeController::isPrimaryHorizontal() const { - return m_direction == SCROLL_DIR_RIGHT || m_direction == SCROLL_DIR_LEFT; -} - -bool CScrollTapeController::isReversed() const { - return m_direction == SCROLL_DIR_LEFT || m_direction == SCROLL_DIR_UP; -} - -size_t CScrollTapeController::stripCount() const { - return m_strips.size(); -} - -SStripData& CScrollTapeController::getStrip(size_t index) { - return m_strips[index]; -} - -const SStripData& CScrollTapeController::getStrip(size_t index) const { - return m_strips[index]; -} - -void CScrollTapeController::setOffset(double offset) { - m_offset = offset; -} - -double CScrollTapeController::getOffset() const { - return m_offset; -} - -void CScrollTapeController::adjustOffset(double delta) { - m_offset += delta; -} - -size_t CScrollTapeController::addStrip(float size) { - m_strips.emplace_back(); - m_strips.back().size = size; - return m_strips.size() - 1; -} - -void CScrollTapeController::insertStrip(ssize_t afterIndex, float size) { - if (afterIndex >= sc(m_strips.size())) { - addStrip(size); - return; - } - - afterIndex = std::clamp(afterIndex, sc(-1L), sc(INT32_MAX)); - - SStripData newStrip; - newStrip.size = size; - m_strips.insert(m_strips.begin() + afterIndex + 1, newStrip); -} - -void CScrollTapeController::removeStrip(size_t index) { - if (index < m_strips.size()) - m_strips.erase(m_strips.begin() + index); -} - -double CScrollTapeController::getPrimary(const Vector2D& v) const { - return isPrimaryHorizontal() ? v.x : v.y; -} - -double CScrollTapeController::getSecondary(const Vector2D& v) const { - return isPrimaryHorizontal() ? v.y : v.x; -} - -void CScrollTapeController::setPrimary(Vector2D& v, double val) const { - if (isPrimaryHorizontal()) - v.x = val; - else - v.y = val; -} - -void CScrollTapeController::setSecondary(Vector2D& v, double val) const { - if (isPrimaryHorizontal()) - v.y = val; - else - v.x = val; -} - -Vector2D CScrollTapeController::makeVector(double primary, double secondary) const { - if (isPrimaryHorizontal()) - return {primary, secondary}; - else - return {secondary, primary}; -} - -double CScrollTapeController::calculateMaxExtent(const CBox& usableArea, bool fullscreenOnOne) const { - if (m_strips.empty()) - return 0.0; - - if (fullscreenOnOne && m_strips.size() == 1) - return getPrimary(usableArea.size()); - - double total = 0.0; - const double usablePrimary = getPrimary(usableArea.size()); - - for (const auto& strip : m_strips) { - total += usablePrimary * strip.size; - } - - return total; -} - -double CScrollTapeController::calculateStripStart(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) const { - if (stripIndex >= m_strips.size()) - return 0.0; - - const double usablePrimary = getPrimary(usableArea.size()); - double current = 0.0; - - for (size_t i = 0; i < stripIndex; ++i) { - const double stripSize = (fullscreenOnOne && m_strips.size() == 1) ? usablePrimary : usablePrimary * m_strips[i].size; - current += stripSize; - } - - return current; -} - -double CScrollTapeController::calculateStripSize(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) const { - if (stripIndex >= m_strips.size()) - return 0.0; - - const double usablePrimary = getPrimary(usableArea.size()); - - if (fullscreenOnOne && m_strips.size() == 1) - return usablePrimary; - - return usablePrimary * m_strips[stripIndex].size; -} - -CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) { - if (stripIndex >= m_strips.size()) - return {}; - - const auto& strip = m_strips[stripIndex]; - if (targetIndex >= strip.targetSizes.size()) - return {}; - - const double usableSecondary = getSecondary(usableArea.size()); - const double usablePrimary = getPrimary(usableArea.size()); - const double cameraOffset = calculateCameraOffset(usableArea, fullscreenOnOne); - - // calculate position along primary axis (strip position) - double primaryPos = calculateStripStart(stripIndex, usableArea, fullscreenOnOne); - double primarySize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne); - - // calculate position along secondary axis (within strip) - double secondaryPos = 0.0; - for (size_t i = 0; i < targetIndex; ++i) { - secondaryPos += strip.targetSizes[i] * usableSecondary; - } - double secondarySize = strip.targetSizes[targetIndex] * usableSecondary; - - // apply camera offset based on direction - // for RIGHT/DOWN: scroll offset moves content left/up (subtract) - // for LEFT/UP: scroll offset moves content right/down (different coordinate system) - if (m_direction == SCROLL_DIR_LEFT) { - // LEFT: flip the entire primary axis, then apply offset - primaryPos = usablePrimary - primaryPos - primarySize + cameraOffset; - } else if (m_direction == SCROLL_DIR_UP) { - // UP: flip the entire primary axis, then apply offset - primaryPos = usablePrimary - primaryPos - primarySize + cameraOffset; - } else { - // RIGHT/DOWN: normal offset - primaryPos -= cameraOffset; - } - - // create the box in primary/secondary coordinates - Vector2D pos = makeVector(primaryPos, secondaryPos); - Vector2D size = makeVector(primarySize, secondarySize); - - // translate to workspace position - pos = pos + workspaceOffset; - - return CBox{pos, size}; -} - -double CScrollTapeController::calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne) { - const double maxExtent = calculateMaxExtent(usableArea, fullscreenOnOne); - const double usablePrimary = getPrimary(usableArea.size()); - - // don't adjust the offset if we are dragging - if (isBeingDragged()) - return m_offset; - - // if the content fits in viewport, center it - if (maxExtent < usablePrimary) - m_offset = std::round((maxExtent - usablePrimary) / 2.0); - - // if the offset is negative but we already extended, reset offset to 0 - if (maxExtent > usablePrimary && m_offset < 0.0) - m_offset = 0.0; - - return m_offset; -} - -Vector2D CScrollTapeController::getCameraTranslation(const CBox& usableArea, bool fullscreenOnOne) { - const double offset = calculateCameraOffset(usableArea, fullscreenOnOne); - - if (isReversed()) - return makeVector(offset, 0.0); - else - return makeVector(-offset, 0.0); -} - -void CScrollTapeController::centerStrip(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) { - if (stripIndex >= m_strips.size()) - return; - - const double usablePrimary = getPrimary(usableArea.size()); - const double stripStart = calculateStripStart(stripIndex, usableArea, fullscreenOnOne); - const double stripSize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne); - - m_offset = stripStart - (usablePrimary - stripSize) / 2.0; -} - -void CScrollTapeController::fitStrip(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) { - if (stripIndex >= m_strips.size()) - return; - - const double usablePrimary = getPrimary(usableArea.size()); - const double stripStart = calculateStripStart(stripIndex, usableArea, fullscreenOnOne); - const double stripSize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne); - - m_offset = std::clamp(m_offset, stripStart - usablePrimary + stripSize, stripStart); -} - -bool CScrollTapeController::isStripVisible(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne, bool full) const { - if (stripIndex >= m_strips.size()) - return false; - - const double stripStart = calculateStripStart(stripIndex, usableArea, fullscreenOnOne); - const double stripEnd = stripStart + calculateStripSize(stripIndex, usableArea, fullscreenOnOne); - const double viewStart = m_offset; - const double viewEnd = m_offset + getPrimary(usableArea.size()); - - if (!full) - return stripStart < viewEnd && viewStart < stripEnd; - else - return stripStart >= viewStart && stripEnd <= viewEnd; -} - -size_t CScrollTapeController::getStripAtCenter(const CBox& usableArea, bool fullscreenOnOne) const { - if (m_strips.empty()) - return 0; - - const double usablePrimary = getPrimary(usableArea.size()); - double currentPos = m_offset; - - for (size_t i = 0; i < m_strips.size(); ++i) { - const double stripSize = calculateStripSize(i, usableArea, fullscreenOnOne); - currentPos += stripSize; - - if (currentPos >= usablePrimary / 2.0 - 2.0) - return i; - } - - return m_strips.empty() ? 0 : m_strips.size() - 1; -} - -void CScrollTapeController::swapStrips(size_t a, size_t b) { - if (a >= m_strips.size() || b >= m_strips.size()) - return; - - std::swap(m_strips.at(a), m_strips.at(b)); -} - -bool CScrollTapeController::isBeingDragged() const { - for (const auto& s : m_strips) { - if (!s.userData) - continue; - - for (const auto& d : s.userData->targetDatas) { - if (d->target == g_layoutManager->dragController()->target()) - return true; - } - } - - return false; -} diff --git a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp deleted file mode 100644 index da2efbba..00000000 --- a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include "../../../../helpers/math/Math.hpp" -#include "../../../../helpers/memory/Memory.hpp" -#include - -namespace Layout::Tiled { - - struct SColumnData; - - enum eScrollDirection : uint8_t { - SCROLL_DIR_RIGHT = 0, - SCROLL_DIR_LEFT, - SCROLL_DIR_DOWN, - SCROLL_DIR_UP, - }; - - struct SStripData { - float size = 1.F; // size along primary axis - std::vector targetSizes; // sizes along secondary axis for each target in this strip - WP userData; - - SStripData() = default; - }; - - struct STapeLayoutResult { - CBox box; - size_t stripIndex = 0; - size_t targetIndex = 0; - }; - - class CScrollTapeController { - public: - CScrollTapeController(eScrollDirection direction = SCROLL_DIR_RIGHT); - ~CScrollTapeController() = default; - - void setDirection(eScrollDirection dir); - eScrollDirection getDirection() const; - bool isPrimaryHorizontal() const; - bool isReversed() const; - - size_t addStrip(float size = 1.0F); - void insertStrip(ssize_t afterIndex, float size = 1.0F); - void removeStrip(size_t index); - size_t stripCount() const; - SStripData& getStrip(size_t index); - const SStripData& getStrip(size_t index) const; - void swapStrips(size_t a, size_t b); - - void setOffset(double offset); - double getOffset() const; - void adjustOffset(double delta); - - double calculateMaxExtent(const CBox& usableArea, bool fullscreenOnOne = false) const; - double calculateStripStart(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const; - double calculateStripSize(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const; - - CBox calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne = false); - - double calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne = false); - Vector2D getCameraTranslation(const CBox& usableArea, bool fullscreenOnOne = false); - - void centerStrip(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false); - void fitStrip(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false); - - bool isStripVisible(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false, bool full = false) const; - - size_t getStripAtCenter(const CBox& usableArea, bool fullscreenOnOne = false) const; - - private: - eScrollDirection m_direction = SCROLL_DIR_RIGHT; - std::vector m_strips; - double m_offset = 0.0; - - double getPrimary(const Vector2D& v) const; - double getSecondary(const Vector2D& v) const; - void setPrimary(Vector2D& v, double val) const; - void setSecondary(Vector2D& v, double val) const; - bool isBeingDragged() const; - - Vector2D makeVector(double primary, double secondary) const; - }; -}; diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp deleted file mode 100644 index ae7c6ecc..00000000 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ /dev/null @@ -1,1518 +0,0 @@ -#include "ScrollingAlgorithm.hpp" -#include "ScrollTapeController.hpp" - -#include "../../Algorithm.hpp" -#include "../../../space/Space.hpp" -#include "../../../LayoutManager.hpp" - -#include "../../../../Compositor.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../config/ConfigValue.hpp" -#include "../../../../config/ConfigManager.hpp" -#include "../../../../render/Renderer.hpp" -#include "../../../../managers/input/InputManager.hpp" -#include "../../../../event/EventBus.hpp" - -#include -#include -#include - -using namespace Hyprutils::String; -using namespace Hyprutils::Utils; -using namespace Layout; -using namespace Layout::Tiled; - -constexpr float MIN_COLUMN_WIDTH = 0.05F; -constexpr float MAX_COLUMN_WIDTH = 1.F; -constexpr float MIN_ROW_HEIGHT = 0.1F; -constexpr float MAX_ROW_HEIGHT = 1.F; - -// -float SColumnData::getColumnWidth() const { - if (!scrollingData || !scrollingData->controller) - return 1.F; - - auto sd = scrollingData.lock(); - if (!sd) - return 1.F; - - int64_t idx = sd->idx(self.lock()); - if (idx < 0 || (size_t)idx >= sd->controller->stripCount()) - return 1.F; - - return sd->controller->getStrip(idx).size; -} - -void SColumnData::setColumnWidth(float width) { - if (!scrollingData || !scrollingData->controller) - return; - - auto sd = scrollingData.lock(); - if (!sd) - return; - - int64_t idx = sd->idx(self.lock()); - if (idx < 0 || (size_t)idx >= sd->controller->stripCount()) - return; - - sd->controller->getStrip(idx).size = width; -} - -float SColumnData::getTargetSize(size_t idx) const { - if (!scrollingData || !scrollingData->controller) - return 1.F; - - auto sd = scrollingData.lock(); - if (!sd) - return 1.F; - - int64_t colIdx = sd->idx(self.lock()); - if (colIdx < 0 || (size_t)colIdx >= sd->controller->stripCount()) - return 1.F; - - const auto& strip = sd->controller->getStrip(colIdx); - if (idx >= strip.targetSizes.size()) - return 1.F; - - return strip.targetSizes[idx]; -} - -void SColumnData::setTargetSize(size_t idx, float size) { - if (!scrollingData || !scrollingData->controller) - return; - - auto sd = scrollingData.lock(); - if (!sd) - return; - - int64_t colIdx = sd->idx(self.lock()); - if (colIdx < 0 || (size_t)colIdx >= sd->controller->stripCount()) - return; - - auto& strip = sd->controller->getStrip(colIdx); - if (idx >= strip.targetSizes.size()) - strip.targetSizes.resize(idx + 1, 1.F); - - strip.targetSizes[idx] = size; -} - -float SColumnData::getTargetSize(SP target) const { - for (size_t i = 0; i < targetDatas.size(); ++i) { - if (targetDatas[i] == target) - return getTargetSize(i); - } - return 1.F; -} - -void SColumnData::setTargetSize(SP target, float size) { - for (size_t i = 0; i < targetDatas.size(); ++i) { - if (targetDatas[i] == target) { - setTargetSize(i, size); - return; - } - } -} - -void SColumnData::add(SP t) { - const float newSize = 1.F / (float)(targetDatas.size() + 1); - - for (size_t i = 0; i < targetDatas.size(); ++i) { - setTargetSize(i, getTargetSize(i) * (float)targetDatas.size() / (float)(targetDatas.size() + 1)); - } - - targetDatas.emplace_back(makeShared(t, self.lock())); - setTargetSize(targetDatas.size() - 1, newSize); -} - -void SColumnData::add(SP t, int after) { - const float newSize = 1.F / (float)(targetDatas.size() + 1); - - for (size_t i = 0; i < targetDatas.size(); ++i) { - setTargetSize(i, getTargetSize(i) * (float)targetDatas.size() / (float)(targetDatas.size() + 1)); - } - - targetDatas.insert(targetDatas.begin() + after + 1, makeShared(t, self.lock())); - - // Sync sizes - need to insert at the right position - if (scrollingData) { - auto sd = scrollingData.lock(); - if (sd && sd->controller) { - int64_t colIdx = sd->idx(self.lock()); - if (colIdx >= 0 && (size_t)colIdx < sd->controller->stripCount()) { - auto& strip = sd->controller->getStrip(colIdx); - strip.targetSizes.insert(strip.targetSizes.begin() + after + 1, newSize); - } - } - } -} - -void SColumnData::add(SP w) { - const float newSize = 1.F / (float)(targetDatas.size() + 1); - - for (size_t i = 0; i < targetDatas.size(); ++i) { - setTargetSize(i, getTargetSize(i) * (float)targetDatas.size() / (float)(targetDatas.size() + 1)); - } - - targetDatas.emplace_back(w); - w->column = self; - setTargetSize(targetDatas.size() - 1, newSize); -} - -void SColumnData::add(SP w, int after) { - const float newSize = 1.F / (float)(targetDatas.size() + 1); - - for (size_t i = 0; i < targetDatas.size(); ++i) { - setTargetSize(i, getTargetSize(i) * (float)targetDatas.size() / (float)(targetDatas.size() + 1)); - } - - targetDatas.insert(targetDatas.begin() + after + 1, w); - w->column = self; - - // Sync sizes - if (scrollingData) { - auto sd = scrollingData.lock(); - if (sd && sd->controller) { - int64_t colIdx = sd->idx(self.lock()); - if (colIdx >= 0 && (size_t)colIdx < sd->controller->stripCount()) { - auto& strip = sd->controller->getStrip(colIdx); - strip.targetSizes.insert(strip.targetSizes.begin() + after + 1, newSize); - } - } - } -} - -size_t SColumnData::idx(SP t) { - for (size_t i = 0; i < targetDatas.size(); ++i) { - if (targetDatas[i]->target == t) - return i; - } - return 0; -} - -size_t SColumnData::idxForHeight(float y) { - if (targetDatas.empty()) - return 0; - for (size_t i = 0; i < targetDatas.size(); ++i) { - if (targetDatas[i]->target->position().y < y) - continue; - return i == 0 ? 0 : i - 1; - } - return targetDatas.size() - 1; -} - -void SColumnData::remove(SP t) { - const auto SIZE_BEFORE = targetDatas.size(); - size_t removedIdx = 0; - bool found = false; - - for (size_t i = 0; i < targetDatas.size(); ++i) { - if (targetDatas[i]->target == t) { - removedIdx = i; - found = true; - break; - } - } - - std::erase_if(targetDatas, [&t](const auto& e) { return e->target == t; }); - - if (SIZE_BEFORE == targetDatas.size() && SIZE_BEFORE > 0) - return; - - if (found && scrollingData) { - auto sd = scrollingData.lock(); - if (sd && sd->controller) { - int64_t colIdx = sd->idx(self.lock()); - if (colIdx >= 0 && (size_t)colIdx < sd->controller->stripCount()) { - auto& strip = sd->controller->getStrip(colIdx); - if (removedIdx < strip.targetSizes.size()) { - strip.targetSizes.erase(strip.targetSizes.begin() + removedIdx); - } - } - } - } - - // Renormalize sizes - float newMaxSize = 0.F; - for (size_t i = 0; i < targetDatas.size(); ++i) { - newMaxSize += getTargetSize(i); - } - - if (newMaxSize > 0.F) { - for (size_t i = 0; i < targetDatas.size(); ++i) { - setTargetSize(i, getTargetSize(i) / newMaxSize); - } - } - - if (targetDatas.empty() && scrollingData) - scrollingData->remove(self.lock()); -} - -bool SColumnData::up(SP w) { - for (size_t i = 1; i < targetDatas.size(); ++i) { - if (targetDatas[i] != w) - continue; - - std::swap(targetDatas[i], targetDatas[i - 1]); - return true; - } - - return false; -} - -bool SColumnData::down(SP w) { - for (size_t i = 0; i < targetDatas.size() - 1; ++i) { - if (targetDatas[i] != w) - continue; - - std::swap(targetDatas[i], targetDatas[i + 1]); - return true; - } - - return false; -} - -SP SColumnData::next(SP w) { - for (size_t i = 0; i < targetDatas.size() - 1; ++i) { - if (targetDatas[i] != w) - continue; - - return targetDatas[i + 1]; - } - - return nullptr; -} - -SP SColumnData::prev(SP w) { - for (size_t i = 1; i < targetDatas.size(); ++i) { - if (targetDatas[i] != w) - continue; - - return targetDatas[i - 1]; - } - - return nullptr; -} - -bool SColumnData::has(SP t) { - return std::ranges::find_if(targetDatas, [t](const auto& e) { return e->target == t; }) != targetDatas.end(); -} - -SScrollingData::SScrollingData(CScrollingAlgorithm* algo) : algorithm(algo) { - controller = makeUnique(SCROLL_DIR_RIGHT); -} - -SP SScrollingData::add() { - auto col = columns.emplace_back(makeShared(self.lock())); - col->self = col; - - size_t stripIdx = controller->addStrip(algorithm->defaultColumnWidth()); - controller->getStrip(stripIdx).userData = col; - - return col; -} - -SP SScrollingData::add(int after) { - auto col = makeShared(self.lock()); - col->self = col; - columns.insert(columns.begin() + after + 1, col); - - controller->insertStrip(after, algorithm->defaultColumnWidth()); - controller->getStrip(after + 1).userData = col; - - return col; -} - -int64_t SScrollingData::idx(SP c) { - for (size_t i = 0; i < columns.size(); ++i) { - if (columns[i] == c) - return i; - } - - return -1; -} - -void SScrollingData::remove(SP c) { - // find index before removing - int64_t index = idx(c); - - std::erase(columns, c); - - // sync with controller - if (index >= 0) - controller->removeStrip(index); -} - -SP SScrollingData::next(SP c) { - for (size_t i = 0; i < columns.size(); ++i) { - if (columns[i] != c) - continue; - - if (i == columns.size() - 1) - return nullptr; - - return columns[i + 1]; - } - - return nullptr; -} - -SP SScrollingData::prev(SP c) { - for (size_t i = 0; i < columns.size(); ++i) { - if (columns[i] != c) - continue; - - if (i == 0) - return nullptr; - - return columns[i - 1]; - } - - return nullptr; -} - -void SScrollingData::centerCol(SP c) { - if (!c) - return; - - static const auto PFSONONE = CConfigValue("scrolling:fullscreen_on_one_column"); - const auto USABLE = algorithm->usableArea(); - int64_t colIdx = idx(c); - - if (colIdx >= 0) - controller->centerStrip(colIdx, USABLE, *PFSONONE); -} - -void SScrollingData::fitCol(SP c) { - if (!c) - return; - - static const auto PFSONONE = CConfigValue("scrolling:fullscreen_on_one_column"); - const auto USABLE = algorithm->usableArea(); - int64_t colIdx = idx(c); - - if (colIdx >= 0) - controller->fitStrip(colIdx, USABLE, *PFSONONE); -} - -void SScrollingData::centerOrFitCol(SP c) { - if (!c) - return; - - static const auto PFITMETHOD = CConfigValue("scrolling:focus_fit_method"); - - if (*PFITMETHOD == 1) - fitCol(c); - else - centerCol(c); -} - -SP SScrollingData::atCenter() { - static const auto PFSONONE = CConfigValue("scrolling:fullscreen_on_one_column"); - const auto USABLE = algorithm->usableArea(); - - size_t centerIdx = controller->getStripAtCenter(USABLE, *PFSONONE); - - if (centerIdx < columns.size()) - return columns[centerIdx]; - - return nullptr; -} - -void SScrollingData::recalculate(bool forceInstant) { - if (!algorithm->m_parent->space()->workspace() || algorithm->m_parent->space()->workspace()->m_hasFullscreenWindow) - return; - - static const auto PFSONONE = CConfigValue("scrolling:fullscreen_on_one_column"); - - const CBox USABLE = algorithm->usableArea(); - const auto WORKAREA = algorithm->m_parent->space()->workArea(); - - controller->setDirection(algorithm->getDynamicDirection()); - - for (size_t i = 0; i < columns.size(); ++i) { - const auto& COL = columns[i]; - - for (size_t j = 0; j < COL->targetDatas.size(); ++j) { - const auto& TARGET = COL->targetDatas[j]; - - TARGET->layoutBox = controller->calculateTargetBox(i, j, USABLE, WORKAREA.pos(), *PFSONONE); - - if (TARGET->target) - TARGET->target->setPositionGlobal(TARGET->layoutBox); - if (forceInstant && TARGET->target) - TARGET->target->warpPositionSize(); - } - } -} - -double SScrollingData::maxWidth() { - static const auto PFSONONE = CConfigValue("scrolling:fullscreen_on_one_column"); - const auto USABLE = algorithm->usableArea(); - - return controller->calculateMaxExtent(USABLE, *PFSONONE); -} - -bool SScrollingData::visible(SP c, bool full) { - static const auto PFSONONE = CConfigValue("scrolling:fullscreen_on_one_column"); - const auto USABLE = algorithm->usableArea(); - int64_t colIdx = idx(c); - - if (colIdx >= 0) - return controller->isStripVisible(colIdx, USABLE, *PFSONONE, full); - - return false; -} - -CScrollingAlgorithm::CScrollingAlgorithm() { - static const auto PCONFWIDTHS = CConfigValue("scrolling:explicit_column_widths"); - static const auto PCONFDIRECTION = CConfigValue("scrolling:direction"); - - m_scrollingData = makeShared(this); - m_scrollingData->self = m_scrollingData; - - // Helper to parse explicit_column_widths string - auto parseColumnWidths = [](const std::string& dir) -> std::vector { - auto widthVec = std::vector(); - - CConstVarList widths(dir, 0, ','); - for (auto& w : widths) { - try { - widthVec.emplace_back(std::clamp(std::stof(std::string{w}), MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH)); - } catch (...) { Log::logger->log(Log::ERR, "scrolling: Failed to parse width {} as float", w); } - } - if (widthVec.empty()) - widthVec = {0.333, 0.5, 0.667, 1.0}; // default - return widthVec; - }; - - // Helper to parse direction string - auto parseDirection = [](const std::string& dir) -> eScrollDirection { - if (dir == "left") - return SCROLL_DIR_LEFT; - else if (dir == "down") - return SCROLL_DIR_DOWN; - else if (dir == "up") - return SCROLL_DIR_UP; - else - return SCROLL_DIR_RIGHT; // default - }; - - m_configCallback = Event::bus()->m_events.config.reloaded.listen([this, parseColumnWidths, parseDirection] { - static const auto PCONFDIRECTION = CConfigValue("scrolling:direction"); - - m_config.configuredWidths.clear(); - m_config.configuredWidths = parseColumnWidths(*PCONFWIDTHS); - - // Update scroll direction - m_scrollingData->controller->setDirection(parseDirection(*PCONFDIRECTION)); - }); - - m_mouseButtonCallback = Event::bus()->m_events.input.mouse.button.listen([this](IPointer::SButtonEvent e, Event::SCallbackInfo&) { - static const auto PFOLLOW_FOCUS = CConfigValue("scrolling:follow_focus"); - - if (*PFOLLOW_FOCUS && e.state == WL_POINTER_BUTTON_STATE_RELEASED && Desktop::focusState()->window()) - focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_CLICK); - }); - - m_focusCallback = Event::bus()->m_events.window.active.listen([this](PHLWINDOW pWindow, Desktop::eFocusReason reason) { - if (!pWindow) - return; - - static const auto PFOLLOW_FOCUS = CConfigValue("scrolling:follow_focus"); - - if (!*PFOLLOW_FOCUS && !Desktop::isHardInputFocusReason(reason)) - return; - - if (pWindow->m_workspace != m_parent->space()->workspace()) - return; - - const auto TARGET = pWindow->layoutTarget(); - if (!TARGET || TARGET->floating()) - return; - - focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_KB : INPUT_MODE_SOFT)); - }); - - // Initialize default widths and direction - m_config.configuredWidths = parseColumnWidths(*PCONFWIDTHS); - m_scrollingData->controller->setDirection(parseDirection(*PCONFDIRECTION)); -} - -CScrollingAlgorithm::~CScrollingAlgorithm() { - m_configCallback.reset(); - m_focusCallback.reset(); -} - -void CScrollingAlgorithm::focusOnInput(SP target, eInputMode input) { - static const auto PFOLLOW_FOCUS_MIN_PERC = CConfigValue("scrolling:follow_min_visible"); - - if (!target || target->space() != m_parent->space()) - return; - - const auto TARGETDATA = dataFor(target); - if (!TARGETDATA) - return; - - if (*PFOLLOW_FOCUS_MIN_PERC > 0.F && input == INPUT_MODE_SOFT) { - // check how much of the window is visible, unless hard input focus - - const auto IS_HORIZ = m_scrollingData->controller->isPrimaryHorizontal(); - - const auto MON_BOX = m_parent->space()->workspace()->m_monitor->logicalBox(); - const auto TARGET_POS = target->position(); - const double VISIBLE_LEN = IS_HORIZ ? // - std::abs(std::min(MON_BOX.x + MON_BOX.w, TARGET_POS.x + TARGET_POS.w) - (std::max(MON_BOX.x, TARGET_POS.x))) // - : - std::abs(std::min(MON_BOX.y + MON_BOX.h, TARGET_POS.y + TARGET_POS.h) - (std::max(MON_BOX.y, TARGET_POS.y))); - - // if the amount of visible X is below minimum, reject - if (VISIBLE_LEN < (IS_HORIZ ? MON_BOX.w : MON_BOX.h) * std::clamp(*PFOLLOW_FOCUS_MIN_PERC, 0.F, 1.F)) - return; - } - - // if we moved via non-kb, and it's fully visible, ignore - if (m_scrollingData->visible(TARGETDATA->column.lock(), true) && input != INPUT_MODE_KB) - return; - - static const auto PFITMETHOD = CConfigValue("scrolling:focus_fit_method"); - if (*PFITMETHOD == 1 || input == INPUT_MODE_CLICK) - m_scrollingData->fitCol(TARGETDATA->column.lock()); - else - m_scrollingData->centerCol(TARGETDATA->column.lock()); - m_scrollingData->recalculate(); -} - -void CScrollingAlgorithm::newTarget(SP target) { - auto droppingOn = Desktop::focusState()->window(); - - if (droppingOn && droppingOn->layoutTarget() == target) - droppingOn = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS); - - SP droppingData = droppingOn ? dataFor(droppingOn->layoutTarget()) : nullptr; - SP droppingColumn = droppingData ? droppingData->column.lock() : nullptr; - - if (!droppingColumn) { - auto col = m_scrollingData->add(); - col->add(target); - m_scrollingData->fitCol(col); - } else { - if (g_layoutManager->dragController()->wasDraggingWindow() && g_layoutManager->dragController()->draggingTiled()) { - if (droppingOn) { - const auto IDX = droppingColumn->idx(droppingOn->layoutTarget()); - const auto TOP = droppingOn->getWindowIdealBoundingBoxIgnoreReserved().middle().y > g_pInputManager->getMouseCoordsInternal().y; - droppingColumn->add(target, TOP ? (IDX == 0 ? -1 : IDX - 1) : (IDX)); - } else - droppingColumn->add(target); - m_scrollingData->fitCol(droppingColumn); - } else { - auto idx = m_scrollingData->idx(droppingColumn); - auto col = idx == -1 ? m_scrollingData->add() : m_scrollingData->add(idx); - col->add(target); - m_scrollingData->fitCol(col); - } - } - - m_scrollingData->recalculate(); -} - -void CScrollingAlgorithm::movedTarget(SP target, std::optional focalPoint) { - newTarget(target); -} - -void CScrollingAlgorithm::removeTarget(SP target) { - const auto DATA = dataFor(target); - - if (!DATA) - return; - - if (!m_scrollingData->next(DATA->column.lock()) && DATA->column->targetDatas.size() <= 1) { - // move the view if this is the last column - const auto USABLE = usableArea(); - const bool isPrimaryHoriz = m_scrollingData->controller->isPrimaryHorizontal(); - const double usablePrimary = isPrimaryHoriz ? USABLE.w : USABLE.h; - m_scrollingData->controller->adjustOffset(-(usablePrimary * DATA->column->getColumnWidth())); - } - - DATA->column->remove(target); - - if (!DATA->column) { - // column got removed, let's ensure we don't leave any cringe extra space - const auto USABLE = usableArea(); - const bool isPrimaryHoriz = m_scrollingData->controller->isPrimaryHorizontal(); - const double usablePrimary = isPrimaryHoriz ? USABLE.w : USABLE.h; - const double newOffset = std::clamp(m_scrollingData->controller->getOffset(), 0.0, std::max(m_scrollingData->maxWidth() - usablePrimary, 1.0)); - m_scrollingData->controller->setOffset(newOffset); - } - - m_scrollingData->recalculate(); -} - -void CScrollingAlgorithm::resizeTarget(const Vector2D& delta, SP target, eRectCorner corner) { - if (!validMapped(target->window())) - return; - - const auto DATA = dataFor(target); - - if (!DATA) { - const auto PWINDOW = target->window(); - *PWINDOW->m_realSize = (PWINDOW->m_realSize->goal() + delta) - .clamp(PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), - PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY})); - PWINDOW->updateWindowDecos(); - return; - } - - if (!DATA->column || !DATA->column->scrollingData) - return; - - static const auto PFSONONE = CConfigValue("scrolling:fullscreen_on_one_column"); - - const auto ADJUSTED_DELTA = m_scrollingData->controller->isPrimaryHorizontal() ? delta : Vector2D{delta.y, delta.x}; - const auto USABLE = usableArea(); - const auto DELTA_AS_PERC = ADJUSTED_DELTA / USABLE.size(); - Vector2D modDelta = ADJUSTED_DELTA; - - const auto CURR_COLUMN = DATA->column.lock(); - const int64_t COL_IDX = m_scrollingData->idx(CURR_COLUMN); - - if (COL_IDX < 0) - return; - - const double currentStart = m_scrollingData->controller->calculateStripStart(COL_IDX, USABLE, *PFSONONE); - const double currentSize = m_scrollingData->controller->calculateStripSize(COL_IDX, USABLE, *PFSONONE); - const double currentEnd = currentStart + currentSize; - - const double cameraOffset = m_scrollingData->controller->getOffset(); - const bool isPrimaryHoriz = m_scrollingData->controller->isPrimaryHorizontal(); - const double usablePrimary = isPrimaryHoriz ? USABLE.w : USABLE.h; - - const double onScreenStart = currentStart - cameraOffset; - const double onScreenEnd = currentEnd - cameraOffset; - - // set the offset because we'll prevent centering during a drag - m_scrollingData->controller->setOffset(cameraOffset); - - const bool RESIZING_LEFT = isPrimaryHoriz ? corner == CORNER_BOTTOMLEFT || corner == CORNER_TOPLEFT : corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT; - - if (RESIZING_LEFT) { - // resize from left edge (inner edge) - grow/shrink column width and adjust offset to keep RIGHT edge stationary - const float oldWidth = CURR_COLUMN->getColumnWidth(); - const float requestedDelta = -(float)DELTA_AS_PERC.x; // negative delta means grow when dragging left - float actualDelta = requestedDelta; - - // clamp delta so we don't shrink below MIN or grow above MAX - const float newWidthUnclamped = oldWidth + actualDelta; - const float newWidthClamped = std::clamp(newWidthUnclamped, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH); - actualDelta = newWidthClamped - oldWidth; - - if (actualDelta * usablePrimary > onScreenStart) - actualDelta = onScreenStart / usablePrimary; - - if (actualDelta != 0.F) { - CURR_COLUMN->setColumnWidth(oldWidth + actualDelta); - // adjust camera offset so the RIGHT edge stays stationary on screen - // when column grows (actualDelta > 0), we need to increase offset by the same amount - m_scrollingData->controller->adjustOffset(actualDelta * usablePrimary); - } - - } else { - // resize from right edge (outer edge) - adjust column width only, keep left edge fixed - const float oldWidth = CURR_COLUMN->getColumnWidth(); - const float requestedDelta = (float)DELTA_AS_PERC.x; - float actualDelta = requestedDelta; - - // clamp delta so we don't shrink below MIN or grow above MAX - const float newWidthUnclamped = oldWidth + actualDelta; - const float newWidthClamped = std::clamp(newWidthUnclamped, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH); - actualDelta = newWidthClamped - oldWidth; - - // also clamp so right edge doesn't go past right viewport boundary - if (onScreenEnd + (actualDelta * usablePrimary) > usablePrimary) - actualDelta = (usablePrimary - onScreenEnd) / usablePrimary; - - if (actualDelta != 0.F) - CURR_COLUMN->setColumnWidth(oldWidth + actualDelta); - } - - if (DATA->column->targetDatas.size() > 1) { - const auto& CURR_TD = DATA; - const auto NEXT_TD = DATA->column->next(DATA); - const auto PREV_TD = DATA->column->prev(DATA); - if (corner == CORNER_NONE) { - if (!PREV_TD) - corner = CORNER_BOTTOMRIGHT; - else { - corner = CORNER_TOPRIGHT; - modDelta.y *= -1.0f; - } - } - - switch (corner) { - case CORNER_BOTTOMLEFT: - case CORNER_BOTTOMRIGHT: { - if (!NEXT_TD) - break; - - float nextSize = CURR_COLUMN->getTargetSize(NEXT_TD); - float currSize = CURR_COLUMN->getTargetSize(CURR_TD); - - if (nextSize <= MIN_ROW_HEIGHT && delta.y >= 0) - break; - - float adjust = std::clamp((float)(delta.y / USABLE.h), (-currSize + MIN_ROW_HEIGHT), (nextSize - MIN_ROW_HEIGHT)); - - CURR_COLUMN->setTargetSize(NEXT_TD, std::clamp(nextSize - adjust, MIN_ROW_HEIGHT, MAX_ROW_HEIGHT)); - CURR_COLUMN->setTargetSize(CURR_TD, std::clamp(currSize + adjust, MIN_ROW_HEIGHT, MAX_ROW_HEIGHT)); - break; - } - case CORNER_TOPLEFT: - case CORNER_TOPRIGHT: { - if (!PREV_TD) - break; - - float prevSize = CURR_COLUMN->getTargetSize(PREV_TD); - float currSize = CURR_COLUMN->getTargetSize(CURR_TD); - - if ((prevSize <= MIN_ROW_HEIGHT && modDelta.y <= 0) || (currSize <= MIN_ROW_HEIGHT && delta.y >= 0)) - break; - - float adjust = std::clamp((float)(modDelta.y / USABLE.h), -(prevSize - MIN_ROW_HEIGHT), (currSize - MIN_ROW_HEIGHT)); - - CURR_COLUMN->setTargetSize(PREV_TD, std::clamp(prevSize + adjust, MIN_ROW_HEIGHT, MAX_ROW_HEIGHT)); - CURR_COLUMN->setTargetSize(CURR_TD, std::clamp(currSize - adjust, MIN_ROW_HEIGHT, MAX_ROW_HEIGHT)); - break; - } - - default: break; - } - } - - m_scrollingData->recalculate(true); -} - -void CScrollingAlgorithm::recalculate() { - if (Desktop::focusState()->window()) { - const auto TARGET = Desktop::focusState()->window()->layoutTarget(); - - const auto TARGETDATA = dataFor(TARGET); - - if (TARGETDATA && !m_scrollingData->visible(TARGETDATA->column.lock(), true)) - focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_KB); - } - - m_scrollingData->recalculate(); -} - -SP CScrollingAlgorithm::closestNode(const Vector2D& posGlobglobgabgalab) { - SP res = nullptr; - double distClosest = -1; - for (auto& c : m_scrollingData->columns) { - for (auto& n : c->targetDatas) { - if (n->target && Desktop::View::validMapped(n->target->window())) { - auto distAnother = vecToRectDistanceSquared(posGlobglobgabgalab, n->layoutBox.pos(), n->layoutBox.pos() + n->layoutBox.size()); - if (!res || distAnother < distClosest) { - res = n; - distClosest = distAnother; - } - } - } - } - return res; -} - -SP CScrollingAlgorithm::getNextCandidate(SP old) { - const auto CENTER = old->position().middle(); - - const auto NODE = closestNode(CENTER); - - if (!NODE) - return nullptr; - - return NODE->target.lock(); -} - -void CScrollingAlgorithm::swapTargets(SP a, SP b) { - auto nodeA = dataFor(a); - auto nodeB = dataFor(b); - - if (nodeA) - nodeA->target = b; - if (nodeB) - nodeB->target = a; - - m_scrollingData->recalculate(); -} - -void CScrollingAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - moveTargetTo(t, dir, silent); -} - -void CScrollingAlgorithm::moveTargetTo(SP t, Math::eDirection dir, bool silent) { - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - - const auto DATA = dataFor(t); - - if (!DATA) - return; - - const auto CURRENT_COL = DATA->column.lock(); - const auto current_idx = m_scrollingData->idx(CURRENT_COL); - - auto rotateDir = [this](Math::eDirection dir) -> Math::eDirection { - switch (m_scrollingData->controller->getDirection()) { - case SCROLL_DIR_RIGHT: return dir; - case SCROLL_DIR_LEFT: { - if (dir == Math::DIRECTION_LEFT) - return Math::DIRECTION_RIGHT; - if (dir == Math::DIRECTION_RIGHT) - return Math::DIRECTION_LEFT; - return dir; - } - case SCROLL_DIR_UP: { - switch (dir) { - case Math::DIRECTION_UP: return Math::DIRECTION_RIGHT; - case Math::DIRECTION_DOWN: return Math::DIRECTION_LEFT; - case Math::DIRECTION_LEFT: return Math::DIRECTION_DOWN; - case Math::DIRECTION_RIGHT: return Math::DIRECTION_UP; - default: break; - } - - return dir; - } - case SCROLL_DIR_DOWN: { - switch (dir) { - case Math::DIRECTION_UP: return Math::DIRECTION_LEFT; - case Math::DIRECTION_DOWN: return Math::DIRECTION_RIGHT; - case Math::DIRECTION_LEFT: return Math::DIRECTION_DOWN; - case Math::DIRECTION_RIGHT: return Math::DIRECTION_UP; - default: break; - } - - return dir; - } - default: break; - } - - return dir; - }; - - const auto ROTATED_DIR = rotateDir(dir); - - auto commenceDir = [&]() -> bool { - if (ROTATED_DIR == Math::DIRECTION_LEFT) { - const auto COL = m_scrollingData->prev(DATA->column.lock()); - - // ignore moves to the origin if we are alone - if (!COL && current_idx == 0 && DATA->column->targetDatas.size() == 1) - return false; - - DATA->column->remove(t); - - if (!COL) { - const auto NEWCOL = m_scrollingData->add(-1); - NEWCOL->add(DATA); - m_scrollingData->centerOrFitCol(NEWCOL); - } else { - if (COL->targetDatas.size() > 0) - COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y)); - else - COL->add(DATA); - m_scrollingData->centerOrFitCol(COL); - } - - return true; - } else if (ROTATED_DIR == Math::DIRECTION_RIGHT) { - const auto COL = m_scrollingData->next(DATA->column.lock()); - - // ignore move to the right when there is no next column and we're alone - if (!COL && current_idx == (int64_t)m_scrollingData->columns.size() - 1 && DATA->column->targetDatas.size() == 1) - return false; - - DATA->column->remove(t); - - if (!COL) { - // make a new one - const auto NEWCOL = m_scrollingData->add(); - NEWCOL->add(DATA); - m_scrollingData->centerOrFitCol(NEWCOL); - } else { - if (COL->targetDatas.size() > 0) - COL->add(DATA, COL->idxForHeight(g_pInputManager->getMouseCoordsInternal().y)); - else - COL->add(DATA); - m_scrollingData->centerOrFitCol(COL); - } - - return true; - } else if (ROTATED_DIR == Math::DIRECTION_UP) - return DATA->column->up(DATA); - else if (ROTATED_DIR == Math::DIRECTION_DOWN) - return DATA->column->down(DATA); - - return false; - }; - - if (!commenceDir()) { - // dir wasn't commenced, move to a workspace if possible - // with the original dir - - if (!*PMONITORFALLBACK) - return; // noop - - const auto MONINDIR = g_pCompositor->getMonitorInDirection(m_parent->space()->workspace()->m_monitor.lock(), dir); - if (MONINDIR && MONINDIR != m_parent->space()->workspace()->m_monitor && MONINDIR->m_activeWorkspace) { - t->assignToSpace(MONINDIR->m_activeWorkspace->m_space, focalPointForDir(t, dir)); - - m_scrollingData->recalculate(); - - return; - } - } - - m_scrollingData->recalculate(); - focusTargetUpdate(t); -} - -std::expected CScrollingAlgorithm::layoutMsg(const std::string_view& sv) { - auto centerOrFit = [this](const SP COL) -> void { - static const auto PFITMETHOD = CConfigValue("scrolling:focus_fit_method"); - if (*PFITMETHOD == 1) - m_scrollingData->fitCol(COL); - else - m_scrollingData->centerCol(COL); - }; - - const auto ARGS = CVarList(std::string{sv}, 0, ' '); - if (ARGS[0] == "move") { - if (ARGS[1] == "+col" || ARGS[1] == "col") { - const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr); - if (!TDATA) - return std::unexpected("no window"); - - const auto COL = m_scrollingData->next(TDATA->column.lock()); - if (!COL) { - // move to max - double maxOffset = m_scrollingData->maxWidth(); - m_scrollingData->controller->setOffset(maxOffset); - m_scrollingData->recalculate(); - focusTargetUpdate(nullptr); - return {}; - } - - centerOrFit(COL); - m_scrollingData->recalculate(); - - focusTargetUpdate(COL->targetDatas.front()->target.lock()); - if (COL->targetDatas.front()->target->window()) - g_pCompositor->warpCursorTo(COL->targetDatas.front()->target->window()->middle()); - - return {}; - } else if (ARGS[1] == "-col") { - const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr); - if (!TDATA) { - if (m_scrollingData->columns.size() > 0) { - m_scrollingData->centerCol(m_scrollingData->columns.back()); - m_scrollingData->recalculate(); - focusTargetUpdate((m_scrollingData->columns.back()->targetDatas.back())->target.lock()); - if (m_scrollingData->columns.back()->targetDatas.back()->target->window()) - g_pCompositor->warpCursorTo((m_scrollingData->columns.back()->targetDatas.back())->target->window()->middle()); - } - - return {}; - } - - const auto COL = m_scrollingData->prev(TDATA->column.lock()); - if (!COL) - return {}; - - centerOrFit(COL); - m_scrollingData->recalculate(); - - focusTargetUpdate(COL->targetDatas.back()->target.lock()); - if (COL->targetDatas.front()->target->window()) - g_pCompositor->warpCursorTo(COL->targetDatas.front()->target->window()->middle()); - - return {}; - } - - const auto PLUSMINUS = getPlusMinusKeywordResult(ARGS[1], 0); - - if (!PLUSMINUS.has_value()) - return std::unexpected("failed to parse offset"); - - m_scrollingData->controller->adjustOffset(-(*PLUSMINUS)); - m_scrollingData->recalculate(); - - const auto ATCENTER = m_scrollingData->atCenter(); - - focusTargetUpdate(ATCENTER ? (*ATCENTER->targetDatas.begin())->target.lock() : nullptr); - } else if (ARGS[0] == "colresize") { - const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr); - - if (!TDATA) - return {}; - - if (ARGS[1] == "all") { - float abs = 0; - try { - abs = std::stof(ARGS[2]); - } catch (...) { return {}; } - - for (const auto& c : m_scrollingData->columns) { - c->setColumnWidth(abs); - } - - m_scrollingData->recalculate(); - return {}; - } - - CScopeGuard x([this, TDATA] { - auto col = TDATA->column.lock(); - if (col) { - col->setColumnWidth(std::clamp(col->getColumnWidth(), MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH)); - m_scrollingData->centerOrFitCol(col); - } - m_scrollingData->recalculate(); - }); - - if (ARGS[1][0] == '+' || ARGS[1][0] == '-') { - if (ARGS[1] == "+conf") { - auto col = TDATA->column.lock(); - if (col) { - for (size_t i = 0; i < m_config.configuredWidths.size(); ++i) { - if (m_config.configuredWidths[i] > col->getColumnWidth()) { - col->setColumnWidth(m_config.configuredWidths[i]); - break; - } - - if (i == m_config.configuredWidths.size() - 1) - col->setColumnWidth(m_config.configuredWidths[0]); - } - } - - return {}; - } else if (ARGS[1] == "-conf") { - auto col = TDATA->column.lock(); - if (col) { - for (size_t i = m_config.configuredWidths.size() - 1;; --i) { - if (m_config.configuredWidths[i] < col->getColumnWidth()) { - col->setColumnWidth(m_config.configuredWidths[i]); - break; - } - - if (i == 0) { - col->setColumnWidth(m_config.configuredWidths.back()); - break; - } - } - } - - return {}; - } - - const auto PLUSMINUS = getPlusMinusKeywordResult(ARGS[1], 0); - - if (!PLUSMINUS.has_value()) - return {}; - - auto col = TDATA->column.lock(); - if (col) - col->setColumnWidth(col->getColumnWidth() + *PLUSMINUS); - } else { - float abs = 0; - try { - abs = std::stof(ARGS[1]); - } catch (...) { return {}; } - - auto col = TDATA->column.lock(); - if (col) - col->setColumnWidth(abs); - } - } else if (ARGS[0] == "fit") { - const auto PWINDOW = Desktop::focusState()->window(); - - if (!PWINDOW) - return std::unexpected("no focused window"); - - const auto WDATA = dataFor(PWINDOW->layoutTarget()); - - if (!WDATA || m_scrollingData->columns.size() == 0) - return std::unexpected("can't fit: no window or columns"); - - if (ARGS[1] == "active") { - // fit the current column to 1.F - const auto USABLE = usableArea(); - - WDATA->column->setColumnWidth(1.F); - - double off = 0.F; - for (size_t i = 0; i < m_scrollingData->columns.size(); ++i) { - if (m_scrollingData->columns[i]->has(PWINDOW->layoutTarget())) - break; - - off += USABLE.w * m_scrollingData->columns[i]->getColumnWidth(); - } - - m_scrollingData->controller->setOffset(off); - m_scrollingData->recalculate(); - } else if (ARGS[1] == "all") { - // fit all columns on screen - const size_t LEN = m_scrollingData->columns.size(); - for (const auto& c : m_scrollingData->columns) { - c->setColumnWidth(1.F / (float)LEN); - } - - m_scrollingData->controller->setOffset(0); - m_scrollingData->recalculate(); - } else if (ARGS[1] == "toend") { - // fit all columns on screen that start from the current and end on the last - bool begun = false; - size_t foundAt = 0; - for (size_t i = 0; i < m_scrollingData->columns.size(); ++i) { - if (!begun && !m_scrollingData->columns[i]->has(PWINDOW->layoutTarget())) - continue; - - if (!begun) { - begun = true; - foundAt = i; - } - - m_scrollingData->columns[i]->setColumnWidth(1.F / (float)(m_scrollingData->columns.size() - foundAt)); - } - - if (!begun) - return std::unexpected("couldn't find beginning"); - - const auto USABLE = usableArea(); - - double off = 0; - for (size_t i = 0; i < foundAt; ++i) { - off += USABLE.w * m_scrollingData->columns[i]->getColumnWidth(); - } - - m_scrollingData->controller->setOffset(off); - m_scrollingData->recalculate(); - } else if (ARGS[1] == "tobeg") { - // fit all columns on screen that start from the current and end on the last - bool begun = false; - size_t foundAt = 0; - for (int64_t i = (int64_t)m_scrollingData->columns.size() - 1; i >= 0; --i) { - if (!begun && !m_scrollingData->columns[i]->has(PWINDOW->layoutTarget())) - continue; - - if (!begun) { - begun = true; - foundAt = i; - } - - m_scrollingData->columns[i]->setColumnWidth(1.F / (float)(foundAt + 1)); - } - - if (!begun) - return {}; - - m_scrollingData->controller->setOffset(0); - m_scrollingData->recalculate(); - } else if (ARGS[1] == "visible") { - // fit all columns on screen that start from the current and end on the last - - bool begun = false; - size_t foundAt = 0; - std::vector> visible; - for (size_t i = 0; i < m_scrollingData->columns.size(); ++i) { - if (!begun && !m_scrollingData->visible(m_scrollingData->columns[i])) - continue; - - if (!begun) { - begun = true; - foundAt = i; - } - - if (!m_scrollingData->visible(m_scrollingData->columns[i])) - break; - - visible.emplace_back(m_scrollingData->columns[i]); - } - - if (!begun) - return {}; - - double off = 0; - - if (foundAt != 0) { - const auto USABLE = usableArea(); - - for (size_t i = 0; i < foundAt; ++i) { - off += USABLE.w * m_scrollingData->columns[i]->getColumnWidth(); - } - } - - for (const auto& v : visible) { - v->setColumnWidth(1.F / (float)visible.size()); - } - - m_scrollingData->controller->setOffset(off); - m_scrollingData->recalculate(); - } - } else if (ARGS[0] == "focus") { - const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr); - static const auto PNOFALLBACK = CConfigValue("general:no_focus_fallback"); - static const auto PCONFWRAPFOCUS = CConfigValue("scrolling:wrap_focus"); - - if (!TDATA || ARGS[1].empty()) - return std::unexpected("no window to focus"); - - // Determine if we're in vertical scroll mode (strips are horizontal) - const bool isVerticalScroll = (getDynamicDirection() == SCROLL_DIR_DOWN || getDynamicDirection() == SCROLL_DIR_UP); - - // Map direction keys based on scroll mode: - // Horizontal scroll (RIGHT/LEFT): u/d move within strip, l/r move between strips - // Vertical scroll (DOWN/UP): l/r move within strip, u/d move between strips - char dirChar = ARGS[1][0]; - - // Convert to semantic directions - bool isPrevInStrip = (!isVerticalScroll && (dirChar == 'u' || dirChar == 't')) || (isVerticalScroll && dirChar == 'l'); - bool isNextInStrip = (!isVerticalScroll && (dirChar == 'b' || dirChar == 'd')) || (isVerticalScroll && dirChar == 'r'); - bool isPrevStrip = (!isVerticalScroll && dirChar == 'l') || (isVerticalScroll && (dirChar == 'u' || dirChar == 't')); - bool isNextStrip = (!isVerticalScroll && dirChar == 'r') || (isVerticalScroll && (dirChar == 'b' || dirChar == 'd')); - - if (isPrevInStrip) { - // Move to previous target within current strip - auto PREV = TDATA->column->prev(TDATA); - if (!PREV) { - if (!*PNOFALLBACK) - PREV = TDATA->column->targetDatas.back(); - else - return std::unexpected("fallback disabled (no target)"); - } - - focusTargetUpdate(PREV->target.lock()); - if (PREV->target->window()) - g_pCompositor->warpCursorTo(PREV->target->window()->middle()); - } else if (isNextInStrip) { - // Move to next target within current strip - auto NEXT = TDATA->column->next(TDATA); - if (!NEXT) { - if (!*PNOFALLBACK) - NEXT = TDATA->column->targetDatas.front(); - else - return std::unexpected("fallback disabled (no target)"); - } - - focusTargetUpdate(NEXT->target.lock()); - if (NEXT->target->window()) - g_pCompositor->warpCursorTo(NEXT->target->window()->middle()); - } else if (isPrevStrip) { - // Move to previous strip - auto PREV = m_scrollingData->prev(TDATA->column.lock()); - if (!PREV) { - if (*PNOFALLBACK) { - centerOrFit(TDATA->column.lock()); - m_scrollingData->recalculate(); - if (TDATA->target->window()) - g_pCompositor->warpCursorTo(TDATA->target->window()->middle()); - return {}; - } else - PREV = (*PCONFWRAPFOCUS == 1) ? m_scrollingData->columns.back() : m_scrollingData->columns.front(); - } - - auto pTargetData = findBestNeighbor(TDATA, PREV); - if (pTargetData) { - focusTargetUpdate(pTargetData->target.lock()); - centerOrFit(PREV); - m_scrollingData->recalculate(); - if (pTargetData->target->window()) - g_pCompositor->warpCursorTo(pTargetData->target->window()->middle()); - } - } else if (isNextStrip) { - // Move to next strip - auto NEXT = m_scrollingData->next(TDATA->column.lock()); - if (!NEXT) { - if (*PNOFALLBACK) { - centerOrFit(TDATA->column.lock()); - m_scrollingData->recalculate(); - if (TDATA->target->window()) - g_pCompositor->warpCursorTo(TDATA->target->window()->middle()); - return {}; - } else - NEXT = (*PCONFWRAPFOCUS == 1) ? m_scrollingData->columns.front() : m_scrollingData->columns.back(); - } - - auto pTargetData = findBestNeighbor(TDATA, NEXT); - if (pTargetData) { - focusTargetUpdate(pTargetData->target.lock()); - centerOrFit(NEXT); - m_scrollingData->recalculate(); - if (pTargetData->target->window()) - g_pCompositor->warpCursorTo(pTargetData->target->window()->middle()); - } - } - } else if (ARGS[0] == "promote") { - const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr); - - if (!TDATA) - return std::unexpected("no window focused"); - - auto idx = m_scrollingData->idx(TDATA->column.lock()); - auto col = idx == -1 ? m_scrollingData->add() : m_scrollingData->add(idx); - - TDATA->column->remove(TDATA->target.lock()); - - col->add(TDATA); - - m_scrollingData->recalculate(); - } else if (ARGS[0] == "swapcol") { - static const auto PCONFWRAPSWAPCOL = CConfigValue("scrolling:wrap_swapcol"); - - if (ARGS.size() < 2) - return std::unexpected("not enough args"); - - const auto TDATA = dataFor(Desktop::focusState()->window() ? Desktop::focusState()->window()->layoutTarget() : nullptr); - if (!TDATA) - return std::unexpected("no window"); - - const auto CURRENT_COL = TDATA->column.lock(); - if (!CURRENT_COL) - return std::unexpected("no current col"); - - if (m_scrollingData->columns.size() < 2) - return std::unexpected("not enough columns to swap"); - - const int64_t currentIdx = m_scrollingData->idx(CURRENT_COL); - const size_t colCount = m_scrollingData->columns.size(); - - if (currentIdx == -1) - return std::unexpected("no current column"); - - const std::string& direction = ARGS[1]; - int64_t targetIdx = -1; - - // wrap around swaps - if (direction == "l") - if (*PCONFWRAPSWAPCOL == 1) - targetIdx = (currentIdx == 0) ? (colCount - 1) : (currentIdx - 1); - else - targetIdx = (currentIdx == 0) ? 0 : (currentIdx - 1); - else if (direction == "r") - if (*PCONFWRAPSWAPCOL == 1) - targetIdx = (currentIdx == (int64_t)colCount - 1) ? 0 : (currentIdx + 1); - else - targetIdx = (currentIdx == (int64_t)colCount - 1) ? (colCount - 1) : (currentIdx + 1); - else - return std::unexpected("no target (invalid direction?)"); - ; - - std::swap(m_scrollingData->columns.at(currentIdx), m_scrollingData->columns.at(targetIdx)); - - m_scrollingData->controller->swapStrips(currentIdx, targetIdx); - - m_scrollingData->centerOrFitCol(CURRENT_COL); - m_scrollingData->recalculate(); - } else - return std::unexpected("no such layoutmsg for scrolling"); - - return {}; -} - -std::optional CScrollingAlgorithm::predictSizeForNewTarget() { - return std::nullopt; -} - -void CScrollingAlgorithm::focusTargetUpdate(SP target) { - if (!target || !validMapped(target->window())) { - Desktop::focusState()->fullWindowFocus(nullptr, Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE); - return; - } - Desktop::focusState()->fullWindowFocus(target->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE); - const auto TARGETDATA = dataFor(target); - if (TARGETDATA) { - if (auto col = TARGETDATA->column.lock()) - col->lastFocusedTarget = TARGETDATA; - } -} - -SP CScrollingAlgorithm::findBestNeighbor(SP pCurrent, SP pTargetCol) { - if (!pCurrent || !pTargetCol || pTargetCol->targetDatas.empty()) - return nullptr; - - const double currentTop = pCurrent->layoutBox.y; - const double currentBottom = pCurrent->layoutBox.y + pCurrent->layoutBox.h; - std::vector> overlappingTargets; - for (const auto& candidate : pTargetCol->targetDatas) { - const double candidateTop = candidate->layoutBox.y; - const double candidateBottom = candidate->layoutBox.y + candidate->layoutBox.h; - const bool overlaps = (candidateTop < currentBottom) && (candidateBottom > currentTop); - - if (overlaps) - overlappingTargets.emplace_back(candidate); - } - if (!overlappingTargets.empty()) { - auto lastFocused = pTargetCol->lastFocusedTarget.lock(); - - if (lastFocused) { - auto it = std::ranges::find(overlappingTargets, lastFocused); - if (it != overlappingTargets.end()) - return lastFocused; - } - - auto topmost = std::ranges::min_element(overlappingTargets, std::less<>{}, [](const SP& t) { return t->layoutBox.y; }); - return *topmost; - } - if (!pTargetCol->targetDatas.empty()) - return pTargetCol->targetDatas.front(); - return nullptr; -} - -SP CScrollingAlgorithm::dataFor(SP t) { - if (!t) - return nullptr; - - for (const auto& c : m_scrollingData->columns) { - for (const auto& d : c->targetDatas) { - if (d->target == t) - return d; - } - } - - return nullptr; -} - -eScrollDirection CScrollingAlgorithm::getDynamicDirection() { - const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_parent->space()->workspace()); - std::string directionString; - if (WORKSPACERULE.layoutopts.contains("direction")) - directionString = WORKSPACERULE.layoutopts.at("direction"); - - static const auto PCONFDIRECTION = CConfigValue("scrolling:direction"); - std::string configDirection = *PCONFDIRECTION; - - // Workspace rule overrides global config - if (!directionString.empty()) - configDirection = directionString; - - // Parse direction string - if (configDirection == "left") - return SCROLL_DIR_LEFT; - else if (configDirection == "down") - return SCROLL_DIR_DOWN; - else if (configDirection == "up") - return SCROLL_DIR_UP; - else - return SCROLL_DIR_RIGHT; // default -} - -CBox CScrollingAlgorithm::usableArea() { - CBox box = m_parent->space()->workArea(); - - // doesn't matter, this happens when this algo is about to be destroyed - if (!m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor) - return box; - - box.translate(-m_parent->space()->workspace()->m_monitor->m_position); - return box; -} - -float CScrollingAlgorithm::defaultColumnWidth() { - static const auto PCOLWIDTH = CConfigValue("scrolling:column_width"); - return std::clamp(*PCOLWIDTH, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH); -} diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp deleted file mode 100644 index d95b3197..00000000 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once - -#include "../../TiledAlgorithm.hpp" -#include "../../../../helpers/math/Direction.hpp" -#include "ScrollTapeController.hpp" -#include "../../../../helpers/signal/Signal.hpp" - -#include - -namespace Layout::Tiled { - class CScrollingAlgorithm; - struct SColumnData; - struct SScrollingData; - - struct SScrollingTargetData { - SScrollingTargetData(SP t, SP col) : target(t), column(col) { - ; - } - - WP target; - WP column; - bool ignoreFullscreenChecks = false; - - CBox layoutBox; - }; - - struct SColumnData { - SColumnData(SP data) : scrollingData(data) { - ; - } - - void add(SP t); - void add(SP t, int after); - void add(SP w); - void add(SP w, int after); - void remove(SP t); - bool has(SP t); - size_t idx(SP t); - - // index of lowest target that is above y. - size_t idxForHeight(float y); - - bool up(SP w); - bool down(SP w); - - SP next(SP w); - SP prev(SP w); - - std::vector> targetDatas; - WP scrollingData; - WP lastFocusedTarget; - - WP self; - - // Helper methods to access controller-managed data - float getColumnWidth() const; - void setColumnWidth(float width); - float getTargetSize(size_t idx) const; - void setTargetSize(size_t idx, float size); - float getTargetSize(SP target) const; - void setTargetSize(SP target, float size); - }; - - struct SScrollingData { - SScrollingData(CScrollingAlgorithm* algo); - - std::vector> columns; - - UP controller; - - SP add(); - SP add(int after); - int64_t idx(SP c); - void remove(SP c); - double maxWidth(); - SP next(SP c); - SP prev(SP c); - SP atCenter(); - - bool visible(SP c, bool full = false); - void centerCol(SP c); - void fitCol(SP c); - void centerOrFitCol(SP c); - - void recalculate(bool forceInstant = false); - - CScrollingAlgorithm* algorithm = nullptr; - WP self; - std::optional lockedCameraOffset; - }; - - class CScrollingAlgorithm : public ITiledAlgorithm { - public: - CScrollingAlgorithm(); - virtual ~CScrollingAlgorithm(); - - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); - - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(); - - virtual SP getNextCandidate(SP old); - - virtual std::expected layoutMsg(const std::string_view& sv); - virtual std::optional predictSizeForNewTarget(); - - virtual void swapTargets(SP a, SP b); - virtual void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - - CBox usableArea(); - - enum eInputMode : uint8_t { - INPUT_MODE_SOFT = 0, - INPUT_MODE_CLICK, - INPUT_MODE_KB - }; - - private: - SP m_scrollingData; - - CHyprSignalListener m_configCallback; - CHyprSignalListener m_focusCallback; - CHyprSignalListener m_mouseButtonCallback; - - struct { - std::vector configuredWidths; - } m_config; - - eScrollDirection getDynamicDirection(); - - SP findBestNeighbor(SP pCurrent, SP pTargetCol); - SP dataFor(SP t); - SP closestNode(const Vector2D& posGlobglobgabgalab); - - void focusTargetUpdate(SP target); - void moveTargetTo(SP t, Math::eDirection dir, bool silent); - void focusOnInput(SP target, eInputMode input); - - float defaultColumnWidth(); - - friend struct SScrollingData; - }; -}; diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp deleted file mode 100644 index db3925f6..00000000 --- a/src/layout/space/Space.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include "Space.hpp" - -#include "../target/Target.hpp" -#include "../algorithm/Algorithm.hpp" - -#include "../../debug/log/Logger.hpp" -#include "../../desktop/Workspace.hpp" -#include "../../config/ConfigManager.hpp" -#include "../../event/EventBus.hpp" - -using namespace Layout; - -SP CSpace::create(PHLWORKSPACE w) { - auto space = SP(new CSpace(w)); - space->m_self = space; - return space; -} - -CSpace::CSpace(PHLWORKSPACE parent) : m_parent(parent) { - recheckWorkArea(); - - // NOLINTNEXTLINE - m_geomUpdateCallback = Event::bus()->m_events.monitor.layoutChanged.listen([this] { - recheckWorkArea(); - m_algorithm->recalculate(); - }); -} - -void CSpace::add(SP t) { - m_targets.emplace_back(t); - - recheckWorkArea(); - - if (m_algorithm) - m_algorithm->addTarget(t); - - m_parent->updateWindows(); -} - -void CSpace::move(SP t, std::optional focalPoint) { - m_targets.emplace_back(t); - - recheckWorkArea(); - - if (m_algorithm) - m_algorithm->moveTarget(t, focalPoint); - - m_parent->updateWindows(); -} - -void CSpace::remove(SP t) { - std::erase_if(m_targets, [&t](const auto& e) { return !e || e == t; }); - - recheckWorkArea(); - - if (m_algorithm) - m_algorithm->removeTarget(t); - - if (m_parent) // can be null if the workspace is gone - m_parent->updateWindows(); -} - -void CSpace::setAlgorithmProvider(SP algo) { - m_algorithm = algo; -} - -void CSpace::recheckWorkArea() { - if (!m_parent || !m_parent->m_monitor) { - Log::logger->log(Log::ERR, "CSpace: recheckWorkArea on no parent / mon?!"); - return; - } - - const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_parent.lock()); - - auto workArea = m_parent->m_monitor->logicalBoxMinusReserved(); - - static auto PGAPSOUTDATA = CConfigValue("general:gaps_out"); - static auto PFLOATGAPSDATA = CConfigValue("general:float_gaps"); - auto* const PGAPSOUT = sc((PGAPSOUTDATA.ptr())->getData()); - auto* PFLOATGAPS = sc(PFLOATGAPSDATA.ptr()->getData()); - if (PFLOATGAPS->m_bottom < 0 || PFLOATGAPS->m_left < 0 || PFLOATGAPS->m_right < 0 || PFLOATGAPS->m_top < 0) - PFLOATGAPS = PGAPSOUT; - - auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT); - auto gapsFloat = WORKSPACERULE.gapsOut.value_or(*PFLOATGAPS); - - Desktop::CReservedArea reservedGaps{gapsOut.m_top, gapsOut.m_right, gapsOut.m_bottom, gapsOut.m_left}; - Desktop::CReservedArea reservedFloatGaps{gapsFloat.m_top, gapsFloat.m_right, gapsFloat.m_bottom, gapsFloat.m_left}; - - auto floatWorkArea = workArea; - - reservedFloatGaps.applyip(floatWorkArea); - reservedGaps.applyip(workArea); - - m_workArea = workArea; - m_floatingWorkArea = floatWorkArea; -} - -const CBox& CSpace::workArea(bool floating) const { - return floating ? m_floatingWorkArea : m_workArea; -} - -PHLWORKSPACE CSpace::workspace() const { - return m_parent.lock(); -} - -void CSpace::toggleTargetFloating(SP t) { - t->setWasTiling(true); - m_algorithm->setFloating(t, !t->floating()); - t->setWasTiling(false); - - m_parent->updateWindows(); - - recalculate(); -} - -CBox CSpace::targetPositionLocal(SP t) const { - return t->position().translate(-m_workArea.pos()); -} - -void CSpace::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { - if (!m_algorithm) - return; - - m_algorithm->resizeTarget(Δ, target, corner); -} - -void CSpace::moveTarget(const Vector2D& Δ, SP target) { - if (!m_algorithm) - return; - - m_algorithm->moveTarget(Δ, target); -} - -SP CSpace::algorithm() const { - return m_algorithm; -} - -void CSpace::recalculate() { - recheckWorkArea(); - - if (m_algorithm) - m_algorithm->recalculate(); -} - -void CSpace::setFullscreen(SP t, eFullscreenMode mode) { - t->setFullscreenMode(mode); - - if (mode == FSMODE_NONE && m_algorithm && t->floating()) - m_algorithm->recenter(t); - - recalculate(); -} - -std::expected CSpace::layoutMsg(const std::string_view& sv) { - if (m_algorithm) - return m_algorithm->layoutMsg(sv); - - return {}; -} - -std::optional CSpace::predictSizeForNewTiledTarget() { - if (m_algorithm) - return m_algorithm->predictSizeForNewTiledTarget(); - - return std::nullopt; -} - -void CSpace::swap(SP a, SP b) { - for (auto& t : m_targets) { - if (t == a) - t = b; - else if (t == b) - t = a; - } - - if (m_algorithm) - m_algorithm->swapTargets(a, b); -} - -void CSpace::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { - if (m_algorithm) - m_algorithm->moveTargetInDirection(t, dir, silent); -} - -void CSpace::setTargetGeom(const CBox& box, SP target) { - if (m_algorithm) - m_algorithm->setTargetGeom(box, target); -} - -SP CSpace::getNextCandidate(SP old) { - return !m_algorithm ? nullptr : m_algorithm->getNextCandidate(old); -} - -const std::vector>& CSpace::targets() const { - return m_targets; -} diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp deleted file mode 100644 index e29a6d8f..00000000 --- a/src/layout/space/Space.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include "../../helpers/math/Math.hpp" -#include "../../helpers/math/Direction.hpp" -#include "../../helpers/memory/Memory.hpp" - -#include "../../desktop/DesktopTypes.hpp" - -#include "../LayoutManager.hpp" - -#include -#include - -namespace Layout { - class ITarget; - class CAlgorithm; - - class CSpace { - public: - static SP create(PHLWORKSPACE w); - ~CSpace() = default; - - void add(SP t); - void remove(SP t); - void move(SP t, std::optional focalPoint = std::nullopt); - - void swap(SP a, SP b); - - SP getNextCandidate(SP old); - - void setAlgorithmProvider(SP algo); - void recheckWorkArea(); - void setFullscreen(SP t, eFullscreenMode mode); - - void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - - void recalculate(); - - void toggleTargetFloating(SP t); - - std::expected layoutMsg(const std::string_view& sv); - std::optional predictSizeForNewTiledTarget(); - - const CBox& workArea(bool floating = false) const; - PHLWORKSPACE workspace() const; - CBox targetPositionLocal(SP t) const; - - void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - void moveTarget(const Vector2D& Δ, SP target); - void setTargetGeom(const CBox& box, SP target); // only for float - - SP algorithm() const; - - const std::vector>& targets() const; - - private: - CSpace(PHLWORKSPACE parent); - - WP m_self; - - std::vector> m_targets; - SP m_algorithm; - PHLWORKSPACEREF m_parent; - - // work area is in global coords - CBox m_workArea, m_floatingWorkArea; - - // for recalc - CHyprSignalListener m_geomUpdateCallback; - }; -}; \ No newline at end of file diff --git a/src/layout/supplementary/DragController.cpp b/src/layout/supplementary/DragController.cpp deleted file mode 100644 index be70f4ac..00000000 --- a/src/layout/supplementary/DragController.cpp +++ /dev/null @@ -1,398 +0,0 @@ -#include "DragController.hpp" - -#include "../space/Space.hpp" - -#include "../../Compositor.hpp" -#include "../../managers/cursor/CursorShapeOverrideController.hpp" -#include "../../desktop/state/FocusState.hpp" -#include "../../desktop/view/Group.hpp" -#include "../../render/Renderer.hpp" - -using namespace Layout; -using namespace Layout::Supplementary; - -SP CDragStateController::target() const { - return m_target.lock(); -} - -eMouseBindMode CDragStateController::mode() const { - return m_dragMode; -} - -bool CDragStateController::wasDraggingWindow() const { - return m_wasDraggingWindow; -} - -bool CDragStateController::dragThresholdReached() const { - return m_dragThresholdReached; -} - -void CDragStateController::resetDragThresholdReached() { - m_dragThresholdReached = false; -} - -bool CDragStateController::draggingTiled() const { - return m_draggingTiled; -} - -bool CDragStateController::updateDragWindow() { - const auto DRAGGINGTARGET = m_target.lock(); - const bool WAS_FULLSCREEN = DRAGGINGTARGET->fullscreenMode() != FSMODE_NONE; - - if (m_dragThresholdReached) { - if (WAS_FULLSCREEN) { - Log::logger->log(Log::DEBUG, "Dragging a fullscreen window"); - g_pCompositor->setWindowFullscreenInternal(DRAGGINGTARGET->window(), FSMODE_NONE); - } - - const auto PWORKSPACE = DRAGGINGTARGET->workspace(); - const auto DRAGGINGWINDOW = DRAGGINGTARGET->window(); - - if (PWORKSPACE->m_hasFullscreenWindow && (!DRAGGINGTARGET->floating() || (!DRAGGINGWINDOW->m_createdOverFullscreen && !DRAGGINGWINDOW->m_pinned))) { - Log::logger->log(Log::DEBUG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)"); - CKeybindManager::changeMouseBindMode(MBIND_INVALID); - return true; - } - } - - m_draggingTiled = false; - m_draggingWindowOriginalFloatSize = DRAGGINGTARGET->lastFloatingSize(); - - if (WAS_FULLSCREEN && DRAGGINGTARGET->floating()) { - const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); - DRAGGINGTARGET->setPositionGlobal(CBox{MOUSECOORDS - DRAGGINGTARGET->position().size() / 2.F, DRAGGINGTARGET->position().size()}); - } else if (!DRAGGINGTARGET->floating() && m_dragMode == MBIND_MOVE) { - Vector2D MINSIZE = DRAGGINGTARGET->minSize().value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}); - DRAGGINGTARGET->rememberFloatingSize((DRAGGINGTARGET->position().size() * 0.8489).clamp(MINSIZE, Vector2D{}).floor()); - DRAGGINGTARGET->setPositionGlobal(CBox{g_pInputManager->getMouseCoordsInternal() - DRAGGINGTARGET->position().size() / 2.F, DRAGGINGTARGET->position().size()}); - - if (m_dragThresholdReached) { - g_layoutManager->changeFloatingMode(DRAGGINGTARGET); - m_draggingTiled = true; - } - } - - const auto DRAG_ORIGINAL_BOX = DRAGGINGTARGET->position(); - - m_beginDragXY = g_pInputManager->getMouseCoordsInternal(); - m_beginDragPositionXY = DRAG_ORIGINAL_BOX.pos(); - m_beginDragSizeXY = DRAG_ORIGINAL_BOX.size(); - m_lastDragXY = m_beginDragXY; - - return false; -} - -void CDragStateController::dragBegin(SP target, eMouseBindMode mode) { - m_target = target; - m_dragMode = mode; - - const auto DRAGGINGTARGET = m_target.lock(); - static auto PDRAGTHRESHOLD = CConfigValue("binds:drag_threshold"); - - m_mouseMoveEventCount = 1; - m_beginDragSizeXY = Vector2D(); - - // Window will be floating. Let's check if it's valid. It should be, but I don't like crashing. - if (!validMapped(DRAGGINGTARGET->window())) { - Log::logger->log(Log::ERR, "Dragging attempted on an invalid window (not mapped)"); - CKeybindManager::changeMouseBindMode(MBIND_INVALID); - return; - } - - if (!DRAGGINGTARGET->workspace()) { - Log::logger->log(Log::ERR, "Dragging attempted on an invalid window (no workspace)"); - CKeybindManager::changeMouseBindMode(MBIND_INVALID); - return; - } - - // Try to pick up dragged window now if drag_threshold is disabled - // or at least update dragging related variables for the cursors - m_dragThresholdReached = *PDRAGTHRESHOLD <= 0; - if (updateDragWindow()) - return; - - // get the grab corner - static auto RESIZECORNER = CConfigValue("general:resize_corner"); - if (*RESIZECORNER != 0 && *RESIZECORNER <= 4 && DRAGGINGTARGET->floating()) { - switch (*RESIZECORNER) { - case 1: - m_grabbedCorner = CORNER_TOPLEFT; - Cursor::overrideController->setOverride("nw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - break; - case 2: - m_grabbedCorner = CORNER_TOPRIGHT; - Cursor::overrideController->setOverride("ne-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - break; - case 3: - m_grabbedCorner = CORNER_BOTTOMRIGHT; - Cursor::overrideController->setOverride("se-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - break; - case 4: - m_grabbedCorner = CORNER_BOTTOMLEFT; - Cursor::overrideController->setOverride("sw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - break; - } - } else if (m_beginDragXY.x < m_beginDragPositionXY.x + m_beginDragSizeXY.x / 2.F) { - if (m_beginDragXY.y < m_beginDragPositionXY.y + m_beginDragSizeXY.y / 2.F) { - m_grabbedCorner = CORNER_TOPLEFT; - Cursor::overrideController->setOverride("nw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - } else { - m_grabbedCorner = CORNER_BOTTOMLEFT; - Cursor::overrideController->setOverride("sw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - } - } else { - if (m_beginDragXY.y < m_beginDragPositionXY.y + m_beginDragSizeXY.y / 2.F) { - m_grabbedCorner = CORNER_TOPRIGHT; - Cursor::overrideController->setOverride("ne-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - } else { - m_grabbedCorner = CORNER_BOTTOMRIGHT; - Cursor::overrideController->setOverride("se-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - } - } - - if (m_dragMode != MBIND_RESIZE && m_dragMode != MBIND_RESIZE_FORCE_RATIO && m_dragMode != MBIND_RESIZE_BLOCK_RATIO) - Cursor::overrideController->setOverride("grabbing", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - - DRAGGINGTARGET->damageEntire(); - - g_pKeybindManager->shadowKeybinds(); - - if (DRAGGINGTARGET->window()) { - Desktop::focusState()->rawWindowFocus(DRAGGINGTARGET->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE); - g_pCompositor->changeWindowZOrder(DRAGGINGTARGET->window(), true); - } -} -void CDragStateController::dragEnd() { - auto draggingTarget = m_target.lock(); - - m_mouseMoveEventCount = 1; - - if (!validMapped(draggingTarget->window())) { - if (draggingTarget->window()) { - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - m_target.reset(); - } - return; - } - - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); - m_target.reset(); - m_wasDraggingWindow = true; - - if (m_dragMode == MBIND_MOVE && draggingTarget->window()) { - draggingTarget->damageEntire(); - - const auto DRAGGING_WINDOW = draggingTarget->window(); - - const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); - PHLWINDOW pWindow = - g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, DRAGGING_WINDOW); - - if (pWindow) { - if (pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_END, MOUSECOORDS, DRAGGING_WINDOW)) - return; - - const bool FLOATEDINTOTILED = !pWindow->m_isFloating && !m_draggingTiled; - static auto PDRAGINTOGROUP = CConfigValue("group:drag_into_group"); - - if (pWindow->m_group && DRAGGING_WINDOW->canBeGroupedInto(pWindow->m_group) && *PDRAGINTOGROUP == 1 && !FLOATEDINTOTILED) { - pWindow->m_group->add(DRAGGING_WINDOW); - // fix the draggingTarget, now it's DRAGGING_WINDOW - draggingTarget = DRAGGING_WINDOW->m_target; - } - } - } - - if (m_draggingTiled) { - // static auto PPRECISEMOUSE = CConfigValue("dwindle:precise_mouse_move"); - - // FIXME: remove or rethink - // if (*PPRECISEMOUSE) { - // eDirection direction = DIRECTION_DEFAULT; - - // const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); - // const PHLWINDOW pReferenceWindow = - // g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, DRAGGINGWINDOW); - - // if (pReferenceWindow && pReferenceWindow != DRAGGINGWINDOW) { - // const Vector2D draggedCenter = DRAGGINGWINDOW->m_realPosition->goal() + DRAGGINGWINDOW->m_realSize->goal() / 2.f; - // const Vector2D referenceCenter = pReferenceWindow->m_realPosition->goal() + pReferenceWindow->m_realSize->goal() / 2.f; - // const float xDiff = draggedCenter.x - referenceCenter.x; - // const float yDiff = draggedCenter.y - referenceCenter.y; - - // if (fabs(xDiff) > fabs(yDiff)) - // direction = xDiff < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - // else - // direction = yDiff < 0 ? DIRECTION_UP : DIRECTION_DOWN; - // } - - // onWindowRemovedTiling(DRAGGINGWINDOW); - // onWindowCreatedTiling(DRAGGINGWINDOW, direction); - // } else - - // make sure to check if we are floating because drag into group could make us tiled already - if (draggingTarget->floating()) - g_layoutManager->changeFloatingMode(draggingTarget); - - draggingTarget->rememberFloatingSize(m_draggingWindowOriginalFloatSize); - } - - draggingTarget->damageEntire(); - - g_layoutManager->setTargetGeom(draggingTarget->position(), draggingTarget); - - Desktop::focusState()->fullWindowFocus(draggingTarget->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE); - - m_wasDraggingWindow = false; - m_dragMode = MBIND_INVALID; -} - -void CDragStateController::mouseMove(const Vector2D& mousePos) { - if (m_target.expired()) - return; - - const auto DRAGGINGTARGET = m_target.lock(); - static auto PDRAGTHRESHOLD = CConfigValue("binds:drag_threshold"); - - // Window invalid or drag begin size 0,0 meaning we rejected it. - if ((!validMapped(DRAGGINGTARGET->window()) || m_beginDragSizeXY == Vector2D())) { - CKeybindManager::changeMouseBindMode(MBIND_INVALID); - return; - } - - // Yoink dragged window here instead if using drag_threshold and it has been reached - if (*PDRAGTHRESHOLD > 0 && !m_dragThresholdReached) { - if ((m_beginDragXY.distanceSq(mousePos) <= std::pow(*PDRAGTHRESHOLD, 2) && m_beginDragXY == m_lastDragXY)) - return; - m_dragThresholdReached = true; - if (updateDragWindow()) - return; - } - - static auto TIMER = std::chrono::high_resolution_clock::now(), MSTIMER = TIMER; - - const auto DELTA = Vector2D(mousePos.x - m_beginDragXY.x, mousePos.y - m_beginDragXY.y); - const auto TICKDELTA = Vector2D(mousePos.x - m_lastDragXY.x, mousePos.y - m_lastDragXY.y); - - static auto SNAPENABLED = CConfigValue("general:snap:enabled"); - - const auto TIMERDELTA = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - TIMER).count(); - const auto MSDELTA = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - MSTIMER).count(); - const auto MSMONITOR = 1000.0 / g_pHyprRenderer->m_mostHzMonitor->m_refreshRate; - static int totalMs = 0; - bool canSkipUpdate = true; - - MSTIMER = std::chrono::high_resolution_clock::now(); - - if (m_mouseMoveEventCount == 1) - totalMs = 0; - - if (MSMONITOR > 16.0) { - totalMs += MSDELTA < MSMONITOR ? MSDELTA : std::round(totalMs * 1.0 / m_mouseMoveEventCount); - m_mouseMoveEventCount += 1; - - // check if time-window is enough to skip update on 60hz monitor - canSkipUpdate = std::clamp(MSMONITOR - TIMERDELTA, 0.0, MSMONITOR) > totalMs * 1.0 / m_mouseMoveEventCount; - } - - if ((abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f) || (TIMERDELTA < MSMONITOR && canSkipUpdate && (m_dragMode != MBIND_MOVE))) - return; - - TIMER = std::chrono::high_resolution_clock::now(); - - m_lastDragXY = mousePos; - - DRAGGINGTARGET->damageEntire(); - - if (m_dragMode == MBIND_MOVE) { - - Vector2D newPos = m_beginDragPositionXY + DELTA; - Vector2D newSize = DRAGGINGTARGET->position().size(); - - if (*SNAPENABLED && !m_draggingTiled) - g_layoutManager->performSnap(newPos, newSize, DRAGGINGTARGET, MBIND_MOVE, -1, m_beginDragSizeXY); - - newPos = newPos.round(); - - DRAGGINGTARGET->setPositionGlobal({newPos, newSize}); - DRAGGINGTARGET->warpPositionSize(); - } else if (m_dragMode == MBIND_RESIZE || m_dragMode == MBIND_RESIZE_FORCE_RATIO || m_dragMode == MBIND_RESIZE_BLOCK_RATIO) { - if (DRAGGINGTARGET->floating()) { - - Vector2D MINSIZE = DRAGGINGTARGET->minSize().value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}); - Vector2D MAXSIZE = DRAGGINGTARGET->maxSize().value_or(Math::VECTOR2D_MAX); - - Vector2D newSize = m_beginDragSizeXY; - Vector2D newPos = m_beginDragPositionXY; - - if (m_grabbedCorner == CORNER_BOTTOMRIGHT) - newSize = newSize + DELTA; - else if (m_grabbedCorner == CORNER_TOPLEFT) - newSize = newSize - DELTA; - else if (m_grabbedCorner == CORNER_TOPRIGHT) - newSize = newSize + Vector2D(DELTA.x, -DELTA.y); - else if (m_grabbedCorner == CORNER_BOTTOMLEFT) - newSize = newSize + Vector2D(-DELTA.x, DELTA.y); - - eMouseBindMode mode = m_dragMode; - if (DRAGGINGTARGET->window() && DRAGGINGTARGET->window()->m_ruleApplicator->keepAspectRatio().valueOrDefault() && mode != MBIND_RESIZE_BLOCK_RATIO) - mode = MBIND_RESIZE_FORCE_RATIO; - - if (m_beginDragSizeXY.x >= 1 && m_beginDragSizeXY.y >= 1 && mode == MBIND_RESIZE_FORCE_RATIO) { - - const float RATIO = m_beginDragSizeXY.y / m_beginDragSizeXY.x; - - if (MINSIZE.x * RATIO > MINSIZE.y) - MINSIZE = Vector2D(MINSIZE.x, MINSIZE.x * RATIO); - else - MINSIZE = Vector2D(MINSIZE.y / RATIO, MINSIZE.y); - - if (MAXSIZE.x * RATIO < MAXSIZE.y) - MAXSIZE = Vector2D(MAXSIZE.x, MAXSIZE.x * RATIO); - else - MAXSIZE = Vector2D(MAXSIZE.y / RATIO, MAXSIZE.y); - - if (newSize.x * RATIO > newSize.y) - newSize = Vector2D(newSize.x, newSize.x * RATIO); - else - newSize = Vector2D(newSize.y / RATIO, newSize.y); - } - - newSize = newSize.clamp(MINSIZE, MAXSIZE); - - if (m_grabbedCorner == CORNER_TOPLEFT) - newPos = newPos - newSize + m_beginDragSizeXY; - else if (m_grabbedCorner == CORNER_TOPRIGHT) - newPos = newPos + Vector2D(0.0, (m_beginDragSizeXY - newSize).y); - else if (m_grabbedCorner == CORNER_BOTTOMLEFT) - newPos = newPos + Vector2D((m_beginDragSizeXY - newSize).x, 0.0); - - if (*SNAPENABLED) { - g_layoutManager->performSnap(newPos, newSize, DRAGGINGTARGET, mode, m_grabbedCorner, m_beginDragSizeXY); - newSize = newSize.clamp(MINSIZE, MAXSIZE); - } - - CBox wb = {newPos, newSize}; - wb.round(); - - DRAGGINGTARGET->setPositionGlobal(wb); - DRAGGINGTARGET->warpPositionSize(); - } else { - g_layoutManager->resizeTarget(TICKDELTA, DRAGGINGTARGET, m_grabbedCorner); - DRAGGINGTARGET->warpPositionSize(); - } - } - - // get middle point - Vector2D middle = DRAGGINGTARGET->position().middle(); - - // and check its monitor - const auto PMONITOR = g_pCompositor->getMonitorFromVector(middle); - - if (PMONITOR && PMONITOR->m_activeWorkspace && DRAGGINGTARGET->floating() /* If we're resaizing a tiled target, don't do this */) { - const auto WS = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace; - DRAGGINGTARGET->assignToSpace(WS->m_space); - } - - DRAGGINGTARGET->damageEntire(); -} diff --git a/src/layout/supplementary/DragController.hpp b/src/layout/supplementary/DragController.hpp deleted file mode 100644 index 3a0d8071..00000000 --- a/src/layout/supplementary/DragController.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "../target/Target.hpp" -#include "../../managers/input/InputManager.hpp" - -namespace Layout { - enum eRectCorner : uint8_t; -} - -namespace Layout::Supplementary { - - // DragStateController contains logic to begin and end a drag, which shouldn't be part of the layout's job. It's stuff like - // toggling float when dragging tiled, remembering sizes, checking deltas, etc. - class CDragStateController { - public: - CDragStateController() = default; - ~CDragStateController() = default; - - void dragBegin(SP target, eMouseBindMode mode); - void dragEnd(); - - void mouseMove(const Vector2D& mousePos); - eMouseBindMode mode() const; - bool wasDraggingWindow() const; - bool dragThresholdReached() const; - void resetDragThresholdReached(); - bool draggingTiled() const; - - /* - Called to try to pick up window for dragging. - Updates drag related variables and floats window if threshold reached. - Return true to reject - */ - bool updateDragWindow(); - - SP target() const; - - private: - WP m_target; - - eMouseBindMode m_dragMode = MBIND_INVALID; - bool m_wasDraggingWindow = false; - bool m_dragThresholdReached = false; - bool m_draggingTiled = false; - - int m_mouseMoveEventCount = 0; - Vector2D m_beginDragXY; - Vector2D m_lastDragXY; - Vector2D m_beginDragPositionXY; - Vector2D m_beginDragSizeXY; - Vector2D m_draggingWindowOriginalFloatSize; - Layout::eRectCorner m_grabbedCorner = sc(0) /* CORNER_NONE */; - }; -}; diff --git a/src/layout/supplementary/WorkspaceAlgoMatcher.cpp b/src/layout/supplementary/WorkspaceAlgoMatcher.cpp deleted file mode 100644 index b476c3a0..00000000 --- a/src/layout/supplementary/WorkspaceAlgoMatcher.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "WorkspaceAlgoMatcher.hpp" - -#include "../../config/ConfigValue.hpp" -#include "../../config/ConfigManager.hpp" - -#include "../algorithm/Algorithm.hpp" -#include "../space/Space.hpp" - -#include "../algorithm/floating/default/DefaultFloatingAlgorithm.hpp" -#include "../algorithm/tiled/dwindle/DwindleAlgorithm.hpp" -#include "../algorithm/tiled/master/MasterAlgorithm.hpp" -#include "../algorithm/tiled/scrolling/ScrollingAlgorithm.hpp" -#include "../algorithm/tiled/monocle/MonocleAlgorithm.hpp" - -#include "../../Compositor.hpp" - -using namespace Layout; -using namespace Layout::Supplementary; - -constexpr const char* DEFAULT_FLOATING_ALGO = "default"; -constexpr const char* DEFAULT_TILED_ALGO = "dwindle"; - -const UP& Supplementary::algoMatcher() { - static UP m = makeUnique(); - return m; -} - -CWorkspaceAlgoMatcher::CWorkspaceAlgoMatcher() { - m_tiledAlgos = { - {"dwindle", [] { return makeUnique(); }}, - {"master", [] { return makeUnique(); }}, - {"scrolling", [] { return makeUnique(); }}, - {"monocle", [] { return makeUnique(); }}, - }; - - m_floatingAlgos = { - {"default", [] { return makeUnique(); }}, - }; - - m_algoNames = { - {&typeid(Tiled::CDwindleAlgorithm), "dwindle"}, - {&typeid(Tiled::CMasterAlgorithm), "master"}, - {&typeid(Tiled::CScrollingAlgorithm), "scrolling"}, - {&typeid(Tiled::CMonocleAlgorithm), "monocle"}, - {&typeid(Floating::CDefaultFloatingAlgorithm), "default"}, - }; -} - -bool CWorkspaceAlgoMatcher::registerTiledAlgo(const std::string& name, const std::type_info* typeInfo, std::function()>&& factory) { - if (m_tiledAlgos.contains(name) || m_floatingAlgos.contains(name)) - return false; - - m_tiledAlgos.emplace(name, std::move(factory)); - m_algoNames.emplace(typeInfo, name); - - updateWorkspaceLayouts(); - - return true; -} - -bool CWorkspaceAlgoMatcher::registerFloatingAlgo(const std::string& name, const std::type_info* typeInfo, std::function()>&& factory) { - if (m_tiledAlgos.contains(name) || m_floatingAlgos.contains(name)) - return false; - - m_floatingAlgos.emplace(name, std::move(factory)); - m_algoNames.emplace(typeInfo, name); - - updateWorkspaceLayouts(); - - return true; -} - -bool CWorkspaceAlgoMatcher::unregisterAlgo(const std::string& name) { - if (!m_tiledAlgos.contains(name) && !m_floatingAlgos.contains(name)) - return false; - - std::erase_if(m_algoNames, [&name](const auto& e) { return e.second == name; }); - - if (m_floatingAlgos.contains(name)) - m_floatingAlgos.erase(name); - else - m_tiledAlgos.erase(name); - - // this is needed here to avoid situations where a plugin unloads and we still have a UP - // to a plugin layout - updateWorkspaceLayouts(); - - return true; -} - -UP CWorkspaceAlgoMatcher::algoForNameTiled(const std::string& s) { - if (m_tiledAlgos.contains(s)) - return m_tiledAlgos.at(s)(); - return m_tiledAlgos.at(DEFAULT_TILED_ALGO)(); -} - -UP CWorkspaceAlgoMatcher::algoForNameFloat(const std::string& s) { - if (m_floatingAlgos.contains(s)) - return m_floatingAlgos.at(s)(); - return m_floatingAlgos.at(DEFAULT_FLOATING_ALGO)(); -} - -std::string CWorkspaceAlgoMatcher::tiledAlgoForWorkspace(const PHLWORKSPACE& w) { - static auto PLAYOUT = CConfigValue("general:layout"); - - auto rule = g_pConfigManager->getWorkspaceRuleFor(w); - return rule.layout.value_or(*PLAYOUT); -} - -SP CWorkspaceAlgoMatcher::createAlgorithmForWorkspace(PHLWORKSPACE w) { - return CAlgorithm::create(algoForNameTiled(tiledAlgoForWorkspace(w)), makeUnique(), w->m_space); -} - -void CWorkspaceAlgoMatcher::updateWorkspaceLayouts() { - // TODO: make this ID-based, string comparison is slow - for (const auto& ws : g_pCompositor->getWorkspaces()) { - if (!ws) - continue; - - const auto& TILED_ALGO = ws->m_space->algorithm()->tiledAlgo(); - - if (!TILED_ALGO) - continue; - - const auto LAYOUT_TO_USE = tiledAlgoForWorkspace(ws.lock()); - - if (m_algoNames.contains(&typeid(*TILED_ALGO.get())) && m_algoNames.at(&typeid(*TILED_ALGO.get())) == LAYOUT_TO_USE) - continue; - - // needs a switchup - ws->m_space->algorithm()->updateTiledAlgo(algoForNameTiled(LAYOUT_TO_USE)); - } -} - -std::string CWorkspaceAlgoMatcher::getNameForTiledAlgo(const std::type_info* type) { - if (m_algoNames.contains(type)) - return m_algoNames.at(type); - return "unknown"; -} diff --git a/src/layout/supplementary/WorkspaceAlgoMatcher.hpp b/src/layout/supplementary/WorkspaceAlgoMatcher.hpp deleted file mode 100644 index d39e2998..00000000 --- a/src/layout/supplementary/WorkspaceAlgoMatcher.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "../../desktop/DesktopTypes.hpp" - -#include -#include -#include -#include - -namespace Layout { - class CAlgorithm; - class ITiledAlgorithm; - class IFloatingAlgorithm; -} - -namespace Layout::Supplementary { - class CWorkspaceAlgoMatcher { - public: - CWorkspaceAlgoMatcher(); - ~CWorkspaceAlgoMatcher() = default; - - SP createAlgorithmForWorkspace(PHLWORKSPACE w); - void updateWorkspaceLayouts(); - std::string getNameForTiledAlgo(const std::type_info* type); - - // these fns can fail due to name collisions - bool registerTiledAlgo(const std::string& name, const std::type_info* typeInfo, std::function()>&& factory); - bool registerFloatingAlgo(const std::string& name, const std::type_info* typeInfo, std::function()>&& factory); - - // this fn fails if the algo isn't registered - bool unregisterAlgo(const std::string& name); - - private: - UP algoForNameTiled(const std::string& s); - UP algoForNameFloat(const std::string& s); - - std::string tiledAlgoForWorkspace(const PHLWORKSPACE&); - - std::map()>> m_tiledAlgos; - std::map()>> m_floatingAlgos; - - std::map m_algoNames; - }; - - const UP& algoMatcher(); -} \ No newline at end of file diff --git a/src/layout/target/Target.cpp b/src/layout/target/Target.cpp deleted file mode 100644 index e433c237..00000000 --- a/src/layout/target/Target.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "Target.hpp" -#include "../space/Space.hpp" -#include "../../debug/log/Logger.hpp" - -#include - -using namespace Layout; -using namespace Hyprutils::Utils; - -void ITarget::setPositionGlobal(const CBox& box) { - m_box = box; - m_box.round(); -} - -void ITarget::assignToSpace(const SP& space, std::optional focalPoint) { - if (m_space == space && !m_ghostSpace) - return; - - const bool HAD_SPACE = !!m_space; - - if (m_space && !m_ghostSpace) - m_space->remove(m_self.lock()); - - m_space = space; - - if (space && HAD_SPACE) - space->move(m_self.lock(), focalPoint); - else if (space) - space->add(m_self.lock()); - - if (!space) - Log::logger->log(Log::WARN, "ITarget: assignToSpace with a null space?"); - - m_ghostSpace = false; - - onUpdateSpace(); -} - -void ITarget::setSpaceGhost(const SP& space) { - if (m_space) - assignToSpace(nullptr); - - m_space = space; - - m_ghostSpace = true; -} - -SP ITarget::space() const { - return m_space; -} - -PHLWORKSPACE ITarget::workspace() const { - if (!m_space) - return nullptr; - - return m_space->workspace(); -} - -CBox ITarget::position() const { - return m_box; -} - -void ITarget::rememberFloatingSize(const Vector2D& size) { - m_floatingSize = size; -} - -Vector2D ITarget::lastFloatingSize() const { - return m_floatingSize; -} - -void ITarget::recalc() { - setPositionGlobal(m_box); -} - -void ITarget::setPseudo(bool x) { - if (m_pseudo == x) - return; - - m_pseudo = x; - - recalc(); -} - -bool ITarget::isPseudo() const { - return m_pseudo; -} - -void ITarget::setPseudoSize(const Vector2D& size) { - m_pseudoSize = size; - - recalc(); -} - -Vector2D ITarget::pseudoSize() { - return m_pseudoSize; -} - -void ITarget::swap(SP b) { - const auto IS_FLOATING = floating(); - const auto IS_FLOATING_B = b->floating(); - - // Keep workspaces alive during a swap: moving one window will unref the ws - - // NOLINTNEXTLINE - const auto PWS1 = workspace(); - // NOLINTNEXTLINE - const auto PWS2 = b->workspace(); - - CScopeGuard x([&] { - b->setFloating(IS_FLOATING); - setFloating(IS_FLOATING_B); - - // update the spaces - b->onUpdateSpace(); - onUpdateSpace(); - }); - - if (b->space() == m_space) { - // simplest - m_space->swap(m_self.lock(), b); - m_space->recalculate(); - return; - } - - // spaces differ - if (m_space) - m_space->swap(m_self.lock(), b); - if (b->space()) - b->space()->swap(b, m_self.lock()); - - std::swap(m_space, b->m_space); - - // recalc both - if (m_space) - m_space->recalculate(); - if (b->space()) - b->space()->recalculate(); -} - -bool ITarget::wasTiling() const { - return m_wasTiling; -} - -void ITarget::setWasTiling(bool x) { - m_wasTiling = x; -} diff --git a/src/layout/target/Target.hpp b/src/layout/target/Target.hpp deleted file mode 100644 index dcaefdb4..00000000 --- a/src/layout/target/Target.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "../../helpers/math/Math.hpp" -#include "../../helpers/memory/Memory.hpp" -#include "../../desktop/Workspace.hpp" - -#include -#include - -namespace Layout { - enum eTargetType : uint8_t { - TARGET_TYPE_WINDOW = 0, - TARGET_TYPE_GROUP, - }; - - enum eGeometryFailure : uint8_t { - GEOMETRY_NO_DESIRED = 0, - GEOMETRY_INVALID_DESIRED = 1, - }; - - class CSpace; - - struct SGeometryRequested { - Vector2D size; - std::optional pos; - }; - - class ITarget { - public: - virtual ~ITarget() = default; - - virtual eTargetType type() = 0; - - // position is within its space - virtual void setPositionGlobal(const CBox& box); - virtual CBox position() const; - virtual void assignToSpace(const SP& space, std::optional focalPoint = std::nullopt); - virtual void setSpaceGhost(const SP& space); - virtual SP space() const; - virtual PHLWORKSPACE workspace() const; - virtual PHLWINDOW window() const = 0; - virtual void recalc(); - virtual bool wasTiling() const; - virtual void setWasTiling(bool x); - - virtual void rememberFloatingSize(const Vector2D& size); - virtual Vector2D lastFloatingSize() const; - - virtual void setPseudo(bool x); - virtual bool isPseudo() const; - virtual void setPseudoSize(const Vector2D& size); - virtual Vector2D pseudoSize(); - virtual void swap(SP b); - - // - virtual bool floating() = 0; - virtual void setFloating(bool x) = 0; - virtual std::expected desiredGeometry() = 0; - virtual eFullscreenMode fullscreenMode() = 0; - virtual void setFullscreenMode(eFullscreenMode mode) = 0; - virtual std::optional minSize() = 0; - virtual std::optional maxSize() = 0; - virtual void damageEntire() = 0; - virtual void warpPositionSize() = 0; - virtual void onUpdateSpace() = 0; - - protected: - ITarget() = default; - - CBox m_box; - SP m_space; - WP m_self; - Vector2D m_floatingSize; - bool m_pseudo = false; - bool m_ghostSpace = false; // ghost space means a target belongs to a space, but isn't sent to the layout - Vector2D m_pseudoSize = {1280, 720}; - bool m_wasTiling = false; - }; -}; \ No newline at end of file diff --git a/src/layout/target/WindowGroupTarget.cpp b/src/layout/target/WindowGroupTarget.cpp deleted file mode 100644 index ae883751..00000000 --- a/src/layout/target/WindowGroupTarget.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "WindowGroupTarget.hpp" - -#include "../space/Space.hpp" -#include "../algorithm/Algorithm.hpp" -#include "WindowTarget.hpp" -#include "Target.hpp" - -#include "../../render/Renderer.hpp" - -using namespace Layout; - -SP CWindowGroupTarget::create(SP g) { - auto target = SP(new CWindowGroupTarget(g)); - target->m_self = target; - return target; -} - -CWindowGroupTarget::CWindowGroupTarget(SP g) : m_group(g) { - ; -} - -eTargetType CWindowGroupTarget::type() { - return TARGET_TYPE_GROUP; -} - -void CWindowGroupTarget::setPositionGlobal(const CBox& box) { - ITarget::setPositionGlobal(box); - - updatePos(); -} - -void CWindowGroupTarget::updatePos() { - for (const auto& w : m_group->windows()) { - w->m_target->setPositionGlobal(m_box); - } -} - -void CWindowGroupTarget::assignToSpace(const SP& space, std::optional focalPoint) { - ITarget::assignToSpace(space, focalPoint); - - m_group->updateWorkspace(space->workspace()); -} - -bool CWindowGroupTarget::floating() { - return m_group->current()->m_target->floating(); -} - -void CWindowGroupTarget::setFloating(bool x) { - for (const auto& w : m_group->windows()) { - w->m_target->setFloating(x); - } -} - -std::expected CWindowGroupTarget::desiredGeometry() { - return m_group->current()->m_target->desiredGeometry(); -} - -PHLWINDOW CWindowGroupTarget::window() const { - return m_group->current(); -} - -eFullscreenMode CWindowGroupTarget::fullscreenMode() { - return m_group->current()->m_fullscreenState.internal; -} - -void CWindowGroupTarget::setFullscreenMode(eFullscreenMode mode) { - m_group->current()->m_fullscreenState.internal = mode; -} - -std::optional CWindowGroupTarget::minSize() { - return m_group->current()->minSize(); -} - -std::optional CWindowGroupTarget::maxSize() { - return m_group->current()->maxSize(); -} - -void CWindowGroupTarget::damageEntire() { - g_pHyprRenderer->damageWindow(m_group->current()); -} - -void CWindowGroupTarget::warpPositionSize() { - for (const auto& w : m_group->windows()) { - w->m_target->warpPositionSize(); - } -} - -void CWindowGroupTarget::onUpdateSpace() { - for (const auto& w : m_group->windows()) { - w->m_target->onUpdateSpace(); - } -} diff --git a/src/layout/target/WindowGroupTarget.hpp b/src/layout/target/WindowGroupTarget.hpp deleted file mode 100644 index 3d4b85a0..00000000 --- a/src/layout/target/WindowGroupTarget.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "Target.hpp" - -#include "../../desktop/view/Window.hpp" -#include "../../desktop/view/Group.hpp" - -namespace Layout { - - class CWindowGroupTarget : public ITarget { - public: - static SP create(SP g); - virtual ~CWindowGroupTarget() = default; - - virtual eTargetType type(); - - virtual void setPositionGlobal(const CBox& box); - virtual void assignToSpace(const SP& space, std::optional focalPoint = std::nullopt); - virtual PHLWINDOW window() const; - - virtual bool floating(); - virtual void setFloating(bool x); - virtual std::expected desiredGeometry(); - virtual eFullscreenMode fullscreenMode(); - virtual void setFullscreenMode(eFullscreenMode mode); - virtual std::optional minSize(); - virtual std::optional maxSize(); - virtual void damageEntire(); - virtual void warpPositionSize(); - virtual void onUpdateSpace(); - - private: - CWindowGroupTarget(SP g); - - void updatePos(); - - WP m_group; - }; -}; \ No newline at end of file diff --git a/src/layout/target/WindowTarget.cpp b/src/layout/target/WindowTarget.cpp deleted file mode 100644 index db03a385..00000000 --- a/src/layout/target/WindowTarget.cpp +++ /dev/null @@ -1,382 +0,0 @@ -#include "WindowTarget.hpp" - -#include "../space/Space.hpp" -#include "../algorithm/Algorithm.hpp" - -#include "../../protocols/core/Compositor.hpp" -#include "../../config/ConfigManager.hpp" -#include "../../helpers/Monitor.hpp" -#include "../../xwayland/XSurface.hpp" -#include "../../Compositor.hpp" -#include "../../render/Renderer.hpp" - -#include - -using namespace Hyprutils::Utils; -using namespace Layout; - -SP CWindowTarget::create(PHLWINDOW w) { - auto target = SP(new CWindowTarget(w)); - target->m_self = target; - return target; -} - -CWindowTarget::CWindowTarget(PHLWINDOW w) : m_window(w) { - ; -} - -eTargetType CWindowTarget::type() { - return TARGET_TYPE_WINDOW; -} - -void CWindowTarget::setPositionGlobal(const CBox& box) { - ITarget::setPositionGlobal(box); - - updatePos(); -} - -void CWindowTarget::updatePos() { - - g_pHyprRenderer->damageWindow(m_window.lock()); - CScopeGuard x([this] { g_pHyprRenderer->damageWindow(m_window.lock()); }); - - if (!m_space) - return; - - if (fullscreenMode() == FSMODE_FULLSCREEN) - return; - - if (floating() && fullscreenMode() != FSMODE_MAXIMIZED) { - m_window->m_position = m_box.pos(); - m_window->m_size = m_box.size(); - - *m_window->m_realPosition = m_box.pos(); - *m_window->m_realSize = m_box.size(); - - m_window->sendWindowSize(); - m_window->updateWindowDecos(); - - return; - } - - // Tiled is more complicated. - - // if we are in maximized, force the box to be max work area. - // TODO: this shouldn't be here. - if (fullscreenMode() == FSMODE_MAXIMIZED) - ITarget::setPositionGlobal(m_space->workArea(floating())); - - const auto PMONITOR = m_space->workspace()->m_monitor; - const auto PWORKSPACE = m_space->workspace(); - - // for gaps outer - const auto MONITOR_WORKAREA = m_space->workArea(); - const bool DISPLAYLEFT = STICKS(m_box.x, MONITOR_WORKAREA.x); - const bool DISPLAYRIGHT = STICKS(m_box.x + m_box.w, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w); - const bool DISPLAYTOP = STICKS(m_box.y, MONITOR_WORKAREA.y); - const bool DISPLAYBOTTOM = STICKS(m_box.y + m_box.h, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h); - - // this is used for scrolling, so that the gaps are correct when a window is the full width and has neighbors - const bool DISPLAYINVERSELEFT = STICKS(m_box.x, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w); - const bool DISPLAYINVERSERIGHT = STICKS(m_box.x + m_box.w, MONITOR_WORKAREA.x); - - // get specific gaps and rules for this workspace, - // if user specified them in config - const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE); - - if (!validMapped(m_window)) { - if (m_window) - g_layoutManager->removeTarget(m_window->layoutTarget()); - return; - } - - if (fullscreenMode() == FSMODE_FULLSCREEN) - return; - - g_pHyprRenderer->damageWindow(window()); - - static auto PGAPSINDATA = CConfigValue("general:gaps_in"); - auto* const PGAPSIN = sc((PGAPSINDATA.ptr())->getData()); - - auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN); - CBox nodeBox = m_box; - nodeBox.round(); - - m_window->m_size = nodeBox.size(); - m_window->m_position = nodeBox.pos(); - - m_window->updateWindowDecos(); - - auto calcPos = m_window->m_position; - auto calcSize = m_window->m_size; - - const static auto REQUESTEDRATIO = CConfigValue("layout:single_window_aspect_ratio"); - const static auto REQUESTEDRATIOTOLERANCE = CConfigValue("layout:single_window_aspect_ratio_tolerance"); - - Vector2D ratioPadding; - - if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1 && fullscreenMode() == FSMODE_NONE) { - const Vector2D originalSize = MONITOR_WORKAREA.size(); - - const double requestedRatio = (*REQUESTEDRATIO).x / (*REQUESTEDRATIO).y; - const double originalRatio = originalSize.x / originalSize.y; - - if (requestedRatio > originalRatio) { - double padding = originalSize.y - (originalSize.x / requestedRatio); - - if (padding / 2 > (*REQUESTEDRATIOTOLERANCE) * originalSize.y) - ratioPadding = Vector2D{0., padding}; - } else if (requestedRatio < originalRatio) { - double padding = originalSize.x - (originalSize.y * requestedRatio); - - if (padding / 2 > (*REQUESTEDRATIOTOLERANCE) * originalSize.x) - ratioPadding = Vector2D{padding, 0.}; - } - } - - const auto GAPOFFSETTOPLEFT = Vector2D(sc(DISPLAYLEFT ? 0 : (DISPLAYINVERSELEFT ? 2 * gapsIn.m_left : gapsIn.m_left)), sc(DISPLAYTOP ? 0 : gapsIn.m_top)); - - const auto GAPOFFSETBOTTOMRIGHT = - Vector2D(sc(DISPLAYRIGHT ? 0 : (DISPLAYINVERSERIGHT ? 2 * gapsIn.m_right : gapsIn.m_right)), sc(DISPLAYBOTTOM ? 0 : gapsIn.m_bottom)); - - calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2; - calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding; - - if (isPseudo() && fullscreenMode() == FSMODE_NONE) { - // Calculate pseudo - float scale = 1; - - // adjust if doesn't fit - if (m_pseudoSize.x > calcSize.x || m_pseudoSize.y > calcSize.y) { - if (m_pseudoSize.x > calcSize.x) - scale = calcSize.x / m_pseudoSize.x; - - if (m_pseudoSize.y * scale > calcSize.y) - scale = calcSize.y / m_pseudoSize.y; - - auto DELTA = calcSize - m_pseudoSize * scale; - calcSize = m_pseudoSize * scale; - calcPos = calcPos + DELTA / 2.f; // center - } else { - auto DELTA = calcSize - m_pseudoSize; - calcPos = calcPos + DELTA / 2.f; // center - calcSize = m_pseudoSize; - } - } - - const auto RESERVED = m_window->getFullWindowReservedArea(); - calcPos = calcPos + RESERVED.topLeft; - calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight); - - Vector2D availableSpace = calcSize; - - static auto PCLAMP_TILED = CConfigValue("misc:size_limits_tiled"); - - if (*PCLAMP_TILED) { - Vector2D minSize = m_window->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}); - Vector2D maxSize = m_window->isFullscreen() ? Vector2D{INFINITY, INFINITY} : m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}); - calcSize = calcSize.clamp(minSize, maxSize); - - calcPos += (availableSpace - calcSize) / 2.0; - - calcPos.x = std::clamp(calcPos.x, MONITOR_WORKAREA.x, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w - calcSize.x); - calcPos.y = std::clamp(calcPos.y, MONITOR_WORKAREA.y, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y); - } - - if (m_window->onSpecialWorkspace() && !m_window->isFullscreen()) { - // if special, we adjust the coords a bit - static auto PSCALEFACTOR = CConfigValue("dwindle:special_scale_factor"); - - CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR}; - wb.round(); // avoid rounding mess - - *m_window->m_realPosition = wb.pos(); - *m_window->m_realSize = wb.size(); - } else { - CBox wb = {calcPos, calcSize}; - wb.round(); // avoid rounding mess - - *m_window->m_realSize = wb.size(); - *m_window->m_realPosition = wb.pos(); - } - - m_window->updateWindowDecos(); -} - -void CWindowTarget::assignToSpace(const SP& space, std::optional focalPoint) { - if (!space) { - ITarget::assignToSpace(space, focalPoint); - return; - } - - // keep the ref here so that moveToWorkspace doesn't unref the workspace - // and assignToSpace doesn't think this is a new target because space wp is dead - const auto WSREF = space->workspace(); - - m_window->m_monitor = space->workspace()->m_monitor; - m_window->moveToWorkspace(space->workspace()); - - // layout and various update fns want the target to already have m_workspace set - ITarget::assignToSpace(space, focalPoint); - - m_window->updateToplevel(); - m_window->updateWindowDecos(); -} - -bool CWindowTarget::floating() { - return m_window->m_isFloating; -} - -void CWindowTarget::setFloating(bool x) { - if (x == m_window->m_isFloating) - return; - - m_window->m_isFloating = x; - m_window->m_pinned = false; - - m_window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FLOATING); -} - -Vector2D CWindowTarget::clampSizeForDesired(const Vector2D& size) const { - Vector2D newSize = size; - if (const auto m = m_window->minSize(); m) - newSize = newSize.clamp(*m); - if (const auto m = m_window->maxSize(); m) - newSize = newSize.clamp(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}, *m); - return newSize; -} - -std::expected CWindowTarget::desiredGeometry() { - - SGeometryRequested requested; - - CBox DESIRED_GEOM = g_pXWaylandManager->getGeometryForWindow(m_window.lock()); - const auto PMONITOR = m_window->m_monitor.lock(); - - requested.size = clampSizeForDesired(DESIRED_GEOM.size()); - - if (m_window->m_isX11) { - Vector2D xy = {DESIRED_GEOM.x, DESIRED_GEOM.y}; - xy = g_pXWaylandManager->xwaylandToWaylandCoords(xy); - requested.pos = xy; - DESIRED_GEOM.x = xy.x; - DESIRED_GEOM.y = xy.y; - } - - const auto STOREDSIZE = m_window->m_ruleApplicator->persistentSize().valueOrDefault() ? g_pConfigManager->getStoredFloatingSize(m_window.lock()) : std::nullopt; - - if (STOREDSIZE) - requested.size = clampSizeForDesired(*STOREDSIZE); - - if (!PMONITOR) { - Log::logger->log(Log::ERR, "{:m} has an invalid monitor in desiredGeometry!", m_window.lock()); - return std::unexpected(GEOMETRY_NO_DESIRED); - } - - static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - const auto toLogical = [&](SGeometryRequested& req) { - if (m_window->m_isX11 && *PXWLFORCESCALEZERO && PMONITOR) - req.size /= PMONITOR->m_scale; - }; - - if (DESIRED_GEOM.width <= 2 || DESIRED_GEOM.height <= 2) { - const auto SURFACE = m_window->wlSurface()->resource(); - - if (SURFACE->m_current.size.x > 5 && SURFACE->m_current.size.y > 5) { - // center on mon and call it a day - requested.pos.reset(); - requested.size = clampSizeForDesired(SURFACE->m_current.size); - toLogical(requested); - return requested; - } - - if (m_window->m_isX11 && m_window->isX11OverrideRedirect()) { - // check OR windows, they like their shit - const auto SIZE = clampSizeForDesired(m_window->m_xwaylandSurface->m_geometry.w > 0 && m_window->m_xwaylandSurface->m_geometry.h > 0 ? - m_window->m_xwaylandSurface->m_geometry.size() : - Vector2D{600, 400}); - - if (m_window->m_xwaylandSurface->m_geometry.x != 0 && m_window->m_xwaylandSurface->m_geometry.y != 0) { - requested.size = SIZE; - requested.pos = g_pXWaylandManager->xwaylandToWaylandCoords(m_window->m_xwaylandSurface->m_geometry.pos()); - toLogical(requested); - return requested; - } - } - - return std::unexpected(m_window->m_isX11 && m_window->isX11OverrideRedirect() ? GEOMETRY_INVALID_DESIRED : GEOMETRY_NO_DESIRED); - } - - // TODO: detect a popup in a more consistent way. - if ((DESIRED_GEOM.x == 0 && DESIRED_GEOM.y == 0) || !m_window->m_isX11) { - // middle of parent if available - if (!m_window->m_isX11) { - if (const auto PARENT = m_window->parent(); PARENT) { - const auto POS = PARENT->m_realPosition->goal() + PARENT->m_realSize->goal() / 2.F - DESIRED_GEOM.size() / 2.F; - requested.pos = POS; - } - } - } else { - // if it is, we respect where it wants to put itself, but apply monitor offset if outside - // most of these are popups - - Vector2D pos; - - if (const auto POPENMON = g_pCompositor->getMonitorFromVector(DESIRED_GEOM.middle()); POPENMON->m_id != PMONITOR->m_id) - pos = Vector2D(DESIRED_GEOM.x, DESIRED_GEOM.y) - POPENMON->m_position + PMONITOR->m_position; - else - pos = Vector2D(DESIRED_GEOM.x, DESIRED_GEOM.y); - - requested.pos = pos; - } - - if (DESIRED_GEOM.w <= 2 || DESIRED_GEOM.h <= 2) - return std::unexpected(GEOMETRY_NO_DESIRED); - - toLogical(requested); - return requested; -} - -PHLWINDOW CWindowTarget::window() const { - return m_window.lock(); -} - -eFullscreenMode CWindowTarget::fullscreenMode() { - return m_window->m_fullscreenState.internal; -} - -void CWindowTarget::setFullscreenMode(eFullscreenMode mode) { - if (floating() && m_window->m_fullscreenState.internal == FSMODE_NONE) - rememberFloatingSize(m_box.size()); - - m_window->m_fullscreenState.internal = mode; -} - -std::optional CWindowTarget::minSize() { - return m_window->minSize(); -} - -std::optional CWindowTarget::maxSize() { - return m_window->maxSize(); -} - -void CWindowTarget::damageEntire() { - g_pHyprRenderer->damageWindow(m_window.lock()); -} - -void CWindowTarget::warpPositionSize() { - m_window->m_realSize->warp(); - m_window->m_realPosition->warp(); - m_window->updateWindowDecos(); -} - -void CWindowTarget::onUpdateSpace() { - if (!space()) - return; - - m_window->m_monitor = space()->workspace()->m_monitor; - m_window->moveToWorkspace(space()->workspace()); - m_window->updateToplevel(); - m_window->updateWindowData(); - m_window->updateWindowDecos(); -} diff --git a/src/layout/target/WindowTarget.hpp b/src/layout/target/WindowTarget.hpp deleted file mode 100644 index 2939fd74..00000000 --- a/src/layout/target/WindowTarget.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "Target.hpp" - -#include "../../desktop/view/Window.hpp" - -namespace Layout { - - class CWindowTarget : public ITarget { - public: - static SP create(PHLWINDOW w); - virtual ~CWindowTarget() = default; - - virtual eTargetType type(); - - virtual void setPositionGlobal(const CBox& box); - virtual void assignToSpace(const SP& space, std::optional focalPoint = std::nullopt); - virtual PHLWINDOW window() const; - - virtual bool floating(); - virtual void setFloating(bool x); - virtual std::expected desiredGeometry(); - virtual eFullscreenMode fullscreenMode(); - virtual void setFullscreenMode(eFullscreenMode mode); - virtual std::optional minSize(); - virtual std::optional maxSize(); - virtual void damageEntire(); - virtual void warpPositionSize(); - virtual void onUpdateSpace(); - - private: - CWindowTarget(PHLWINDOW w); - - Vector2D clampSizeForDesired(const Vector2D& size) const; - - void updatePos(); - - PHLWINDOWREF m_window; - }; -}; \ No newline at end of file diff --git a/src/macros.hpp b/src/macros.hpp index fc109296..9ba0e188 100644 --- a/src/macros.hpp +++ b/src/macros.hpp @@ -6,7 +6,7 @@ #include #include "helpers/memory/Memory.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #ifndef NDEBUG #ifdef HYPRLAND_DEBUG @@ -31,8 +31,12 @@ #define MIN_WINDOW_SIZE 20.0 -// max value 32 because killed is a int uniform -#define POINTER_PRESSED_HISTORY_LENGTH 32 +#define LISTENER(name) \ + void listener_##name(wl_listener*, void*); \ + inline wl_listener listen_##name = {.notify = listener_##name} +#define DYNLISTENFUNC(name) void listener_##name(void*, void*) +#define DYNLISTENER(name) CHyprWLListener hyprListener_##name +#define DYNMULTILISTENER(name) wl_listener listen_##name #define VECINRECT(vec, x1, y1, x2, y2) ((vec).x >= (x1) && (vec).x < (x2) && (vec).y >= (y1) && (vec).y < (y2)) #define VECNOTINRECT(vec, x1, y1, x2, y2) ((vec).x < (x1) || (vec).x >= (x2) || (vec).y < (y1) || (vec).y >= (y2)) @@ -45,9 +49,9 @@ #define RASSERT(expr, reason, ...) \ if (!(expr)) { \ - Log::logger->log(Log::CRIT, "\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}", \ - std::format(reason, ##__VA_ARGS__), __LINE__, \ - ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })()); \ + Debug::log(CRIT, "\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}", \ + std::format(reason, ##__VA_ARGS__), __LINE__, \ + ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })()); \ std::print("Assertion failed! See the log in /tmp/hypr/hyprland.log for more info."); \ raise(SIGABRT); \ } @@ -83,7 +87,7 @@ #if ISDEBUG #define UNREACHABLE() \ { \ - Log::logger->log(Log::CRIT, "\n\nMEMORY CORRUPTED: Unreachable failed! (Reached an unreachable position, memory corruption!!!)"); \ + Debug::log(CRIT, "\n\nMEMORY CORRUPTED: Unreachable failed! (Reached an unreachable position, memory corruption!!!)"); \ raise(SIGABRT); \ std::unreachable(); \ } @@ -91,28 +95,16 @@ #define UNREACHABLE() std::unreachable(); #endif -#if ISDEBUG - #define GLCALL(__CALL__) \ { \ __CALL__; \ - static const auto GLDEBUG = CConfigValue("debug:gl_debugging"); \ - if (*GLDEBUG) { \ - auto err = glGetError(); \ - if (err != GL_NO_ERROR) { \ - Log::logger->log(Log::ERR, "[GLES] Error in call at {}@{}: 0x{:x}", __LINE__, \ - ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })(), err); \ - } \ + auto err = glGetError(); \ + if (err != GL_NO_ERROR) { \ + Debug::log(ERR, "[GLES] Error in call at {}@{}: 0x{:x}", __LINE__, \ + ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })(), err); \ } \ } -#else - -#define GLCALL(__CALL__) \ - { __CALL__; } - -#endif - #define HYPRUTILS_FORWARD(ns, name) \ namespace Hyprutils { \ namespace ns { \ diff --git a/src/main.cpp b/src/main.cpp index 99e64675..397d7463 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,14 @@ #include "defines.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #include "Compositor.hpp" #include "config/ConfigManager.hpp" #include "init/initHelpers.hpp" #include "debug/HyprCtl.hpp" -#include "helpers/env/Env.hpp" -#include #include #include -#include #include using namespace Hyprutils::String; -using namespace Hyprutils::Memory; #include #include @@ -20,35 +16,19 @@ using namespace Hyprutils::Memory; #include #include #include -#include -#include #include static void help() { std::println("usage: Hyprland [arg [...]].\n"); - std::println(R"#(Arguments: + std::println(R"(Arguments: --help -h - Show this message again --config FILE -c FILE - Specify config file to use --socket NAME - Sets the Wayland socket name (for Wayland socket handover) --wayland-fd FD - Sets the Wayland socket fd (for Wayland socket handover) - --watchdog-fd FD - Used by start-hyprland - --safe-mode - Starts Hyprland in safe mode --systeminfo - Prints system infos --i-am-really-stupid - Omits root user privileges check (why would you do that?) --verify-config - Do not run Hyprland, only print if the config has any errors - --version -v - Print this binary's version - --version-json - Print this binary's version as json)#"); -} - -static void reapZombieChildrenAutomatically() { - struct sigaction act; - act.sa_handler = SIG_DFL; - sigemptyset(&act.sa_mask); - act.sa_flags = SA_NOCLDWAIT; -#ifdef SA_RESTORER - act.sa_restorer = NULL; -#endif - sigaction(SIGCHLD, &act, nullptr); + --version -v - Print this binary's version)"); } int main(int argc, char** argv) { @@ -68,117 +48,92 @@ int main(int argc, char** argv) { setenv("MOZ_ENABLE_WAYLAND", "1", 1); // parse some args - std::string configPath; - std::string socketName; - int socketFd = -1; - bool ignoreSudo = false, verifyConfig = false, safeMode = false; - int watchdogFd = -1; + std::string configPath; + std::string socketName; + int socketFd = -1; + bool ignoreSudo = false, verifyConfig = false; - if (argc > 1) { - std::span args{argv + 1, sc(argc - 1)}; + std::vector args{argv + 1, argv + argc}; - for (auto it = args.begin(); it != args.end(); it++) { - std::string_view value = *it; + for (auto it = args.begin(); it != args.end(); it++) { + if (*it == "--i-am-really-stupid" && !ignoreSudo) { + std::println("[ WARNING ] Running Hyprland with superuser privileges might damage your system"); - if (value == "--i-am-really-stupid" && !ignoreSudo) { - std::println("[ WARNING ] Running Hyprland with superuser privileges might damage your system"); - - ignoreSudo = true; - } else if (value == "--socket") { - if (std::next(it) == args.end()) { - help(); - - return 1; - } - - socketName = *std::next(it); - it++; - } else if (value == "--wayland-fd") { - if (std::next(it) == args.end()) { - help(); - - return 1; - } - - try { - socketFd = std::stoi(*std::next(it)); - - // check if socketFd is a valid file descriptor - if (fcntl(socketFd, F_GETFD) == -1) - throw std::exception(); - } catch (...) { - std::println(stderr, "[ ERROR ] Invalid Wayland FD!"); - help(); - - return 1; - } - - it++; - } else if (value == "-c" || value == "--config") { - 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; - } - - Log::logger->log(Log::DEBUG, "User-specified config location: '{}'", configPath); - - it++; - - continue; - } else if (value == "-h" || value == "--help") { - help(); - - return 0; - } else if (value == "-v" || value == "--version") { - std::println("{}", versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "")); - return 0; - } else if (value == "--version-json") { - std::println("{}", versionRequest(eHyprCtlOutputFormat::FORMAT_JSON, "")); - return 0; - } else if (value == "--systeminfo") { - std::println("{}", systemInfoRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "")); - return 0; - } else if (value == "--verify-config") { - verifyConfig = true; - continue; - } else if (value == "--safe-mode") { - safeMode = true; - continue; - } else if (value == "--watchdog-fd") { - if (std::next(it) == args.end()) { - help(); - return 1; - } - - try { - watchdogFd = std::stoi(*std::next(it)); - it++; - } catch (...) { - std::println(stderr, "[ ERROR ] Invalid fd for watchdog fd"); - help(); - return 1; - } - } else { - std::println(stderr, "[ ERROR ] Unknown option '{}' !", value); + ignoreSudo = true; + } else if (*it == "--socket") { + if (std::next(it) == args.end()) { help(); return 1; } + + socketName = *std::next(it); + it++; + } else if (*it == "--wayland-fd") { + if (std::next(it) == args.end()) { + help(); + + return 1; + } + + try { + socketFd = std::stoi(*std::next(it)); + + // check if socketFd is a valid file descriptor + if (fcntl(socketFd, F_GETFD) == -1) + throw std::exception(); + } catch (...) { + std::println(stderr, "[ ERROR ] Invalid Wayland FD!"); + help(); + + return 1; + } + + it++; + } else if (*it == "-c" || *it == "--config") { + 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; + } + + Debug::log(LOG, "User-specified config location: '{}'", configPath); + + it++; + + continue; + } else if (*it == "-h" || *it == "--help") { + help(); + + return 0; + } else if (*it == "-v" || *it == "--version") { + std::println("{}", versionRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "")); + return 0; + } else if (*it == "--systeminfo") { + std::println("{}", systemInfoRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, "")); + return 0; + } else if (*it == "--verify-config") { + verifyConfig = true; + continue; + } else { + std::println(stderr, "[ ERROR ] Unknown option '{}' !", *it); + help(); + + return 1; } } @@ -205,34 +160,22 @@ int main(int argc, char** argv) { // let's init the compositor. // it initializes basic Wayland stuff in the constructor. try { - g_pCompositor = makeUnique(verifyConfig); - g_pCompositor->m_explicitConfigPath = configPath; + g_pCompositor = makeUnique(verifyConfig); + g_pCompositor->explicitConfigPath = configPath; } catch (const std::exception& e) { std::println(stderr, "Hyprland threw in ctor: {}\nCannot continue.", e.what()); return 1; } - reapZombieChildrenAutomatically(); - - bool watchdogOk = watchdogFd > 0; - - if (watchdogFd > 0) - watchdogOk = g_pCompositor->setWatchdogFd(watchdogFd); - if (safeMode) - g_pCompositor->m_safeMode = true; - - if (!watchdogOk && !verifyConfig) - Log::logger->log(Log::WARN, "WARNING: Hyprland is being launched without start-hyprland. This is highly advised against."); - g_pCompositor->initServer(socketName, socketFd); if (verifyConfig) - return !g_pConfigManager->m_lastConfigVerificationWasSuccessful; + return !g_pConfigManager->m_bLastConfigVerificationWasSuccessful; - if (!Env::envEnabled("HYPRLAND_NO_RT")) + if (!envEnabled("HYPRLAND_NO_RT")) NInit::gainRealTime(); - Log::logger->log(Log::DEBUG, "Hyprland init finished."); + Debug::log(LOG, "Hyprland init finished."); // If all's good to go, start. g_pCompositor->startCompositor(); @@ -241,7 +184,7 @@ int main(int argc, char** argv) { g_pCompositor.reset(); - Log::logger->log(Log::DEBUG, "Hyprland has reached the end."); + Debug::log(LOG, "Hyprland has reached the end."); return EXIT_SUCCESS; } diff --git a/src/managers/ANRManager.cpp b/src/managers/ANRManager.cpp index 9f613df8..ebf02001 100644 --- a/src/managers/ANRManager.cpp +++ b/src/managers/ANRManager.cpp @@ -1,15 +1,13 @@ #include "ANRManager.hpp" - #include "../helpers/fs/FsUtils.hpp" -#include "../debug/log/Logger.hpp" +#include "../debug/Log.hpp" #include "../macros.hpp" +#include "HookSystemManager.hpp" #include "../Compositor.hpp" #include "../protocols/XDGShell.hpp" #include "./eventLoop/EventLoopManager.hpp" #include "../config/ConfigValue.hpp" #include "../xwayland/XSurface.hpp" -#include "../i18n/Engine.hpp" -#include "../event/EventBus.hpp" using namespace Hyprutils::OS; @@ -17,7 +15,7 @@ static constexpr auto TIMER_TIMEOUT = std::chrono::milliseconds(1500); CANRManager::CANRManager() { if (!NFsUtils::executableExistsInPath("hyprland-dialog")) { - Log::logger->log(Log::ERR, "hyprland-dialog missing from PATH, cannot start ANRManager"); + Debug::log(ERR, "hyprland-dialog missing from PATH, cannot start ANRManager"); return; } @@ -26,12 +24,10 @@ CANRManager::CANRManager() { m_active = true; - static auto P = Event::bus()->m_events.window.open.listen([this](PHLWINDOW window) { - for (const auto& d : m_data) { - // Window is ANR dialog - if (d->isRunning() && d->dialogBox->getPID() == window->getPID()) - return; + static auto P = g_pHookSystem->hookDynamic("openWindow", [this](void* self, SCallbackInfo& info, std::any data) { + auto window = std::any_cast(data); + for (const auto& d : m_data) { if (d->fitsWindow(window)) return; } @@ -39,27 +35,13 @@ CANRManager::CANRManager() { m_data.emplace_back(makeShared(window)); }); - static auto P1 = Event::bus()->m_events.window.close.listen([this](PHLWINDOW window) { - for (const auto& d : m_data) { - if (!d->fitsWindow(window)) - continue; - - // kill the dialog, act as if we got a "ping" in case there's more than one - // window from this client, in which case the dialog will re-appear. - d->killDialog(); - d->missedResponses = 0; - d->dialogSaidWait = false; - } - - std::erase_if(m_data, [&window](auto& w) { return w == window; }); - }); - m_timer->updateTimeout(TIMER_TIMEOUT); } void CANRManager::onTick() { - static auto PENABLEANR = CConfigValue("misc:enable_anr_dialog"); - static auto PANRTHRESHOLD = CConfigValue("misc:anr_missed_pings"); + std::erase_if(m_data, [](const auto& e) { return e->isDefunct(); }); + + static auto PENABLEANR = CConfigValue("misc:enable_anr_dialog"); if (!*PENABLEANR) { m_timer->updateTimeout(TIMER_TIMEOUT * 10); @@ -69,8 +51,8 @@ void CANRManager::onTick() { for (auto& data : m_data) { PHLWINDOW firstWindow; int count = 0; - for (const auto& w : g_pCompositor->m_windows) { - if (!w->m_isMapped) + for (const auto& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped) continue; if (!data->fitsWindow(w)) @@ -84,12 +66,12 @@ void CANRManager::onTick() { if (count == 0) continue; - if (data->missedResponses >= *PANRTHRESHOLD) { - if (!data->isRunning() && !data->dialogSaidWait) { - data->runDialog(firstWindow->m_title, firstWindow->m_class, data->getPID()); + if (data->missedResponses > 0) { + if (!data->isThreadRunning() && !data->dialogThreadSaidWait) { + data->runDialog("Application Not Responding", firstWindow->m_szTitle, firstWindow->m_szClass, data->getPid()); - for (const auto& w : g_pCompositor->m_windows) { - if (!w->m_isMapped) + for (const auto& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped) continue; if (!data->fitsWindow(w)) @@ -98,11 +80,11 @@ void CANRManager::onTick() { *w->m_notRespondingTint = 0.2F; } } - } else if (data->isRunning()) + } else if (data->isThreadRunning()) data->killDialog(); if (data->missedResponses == 0) - data->dialogSaidWait = false; + data->dialogThreadSaidWait = false; data->missedResponses++; @@ -132,7 +114,7 @@ void CANRManager::onResponse(SP pXwaylandSurface) { void CANRManager::onResponse(SP data) { data->missedResponses = 0; - if (data->isRunning()) + if (data->isThreadRunning()) data->killDialog(); } @@ -146,16 +128,15 @@ bool CANRManager::isNotResponding(PHLWINDOW pWindow) { } bool CANRManager::isNotResponding(SP data) { - static auto PANRTHRESHOLD = CConfigValue("misc:anr_missed_pings"); - return data->missedResponses > *PANRTHRESHOLD; + return data->missedResponses > 1; } SP CANRManager::dataFor(PHLWINDOW pWindow) { auto it = m_data.end(); - if (pWindow->m_xwaylandSurface) - it = std::ranges::find_if(m_data, [&pWindow](const auto& data) { return data->xwaylandSurface && data->xwaylandSurface == pWindow->m_xwaylandSurface; }); - else if (pWindow->m_xdgSurface) - it = std::ranges::find_if(m_data, [&pWindow](const auto& data) { return data->xdgBase && data->xdgBase == pWindow->m_xdgSurface->m_owner; }); + if (pWindow->m_pXWaylandSurface) + it = std::ranges::find_if(m_data, [&pWindow](const auto& data) { return data->xwaylandSurface && data->xwaylandSurface == pWindow->m_pXWaylandSurface; }); + else if (pWindow->m_pXDGSurface) + it = std::ranges::find_if(m_data, [&pWindow](const auto& data) { return data->xdgBase && data->xdgBase == pWindow->m_pXDGSurface->owner; }); return it == m_data.end() ? nullptr : *it; } @@ -170,75 +151,75 @@ SP CANRManager::dataFor(SP pXwaylandSur } CANRManager::SANRData::SANRData(PHLWINDOW pWindow) : - xwaylandSurface(pWindow->m_xwaylandSurface), xdgBase(pWindow->m_xdgSurface ? pWindow->m_xdgSurface->m_owner : WP{}) { + xwaylandSurface(pWindow->m_pXWaylandSurface), xdgBase(pWindow->m_pXDGSurface ? pWindow->m_pXDGSurface->owner : WP{}) { ; } CANRManager::SANRData::~SANRData() { - if (dialogBox && dialogBox->isRunning()) + if (dialogThread.joinable()) { killDialog(); + // dangerous: might lock if the above failed!! + dialogThread.join(); + } } -void CANRManager::SANRData::runDialog(const std::string& appName, const std::string appClass, pid_t dialogWmPID) { - if (dialogBox && dialogBox->isRunning()) +void CANRManager::SANRData::runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID) { + if (!dialogThreadExited) killDialog(); - const auto OPTION_TERMINATE_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_TERMINATE, {}); - const auto OPTION_WAIT_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_OPTION_WAIT, {}); - const auto OPTIONS = std::vector{OPTION_TERMINATE_STR, OPTION_WAIT_STR}; - const auto CLASS_STR = appClass.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appClass; - const auto TITLE_STR = appName.empty() ? I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_PROP_UNKNOWN, {}) : appName; - const auto DESCRIPTION_STR = I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_CONTENT, {{"title", TITLE_STR}, {"class", CLASS_STR}}); + // dangerous: might lock if the above failed!! + if (dialogThread.joinable()) + dialogThread.join(); - dialogBox = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_ANR_TITLE, {}), DESCRIPTION_STR, OPTIONS); + dialogThreadExited = false; + dialogThreadSaidWait = false; + dialogThread = std::thread([title, appName, appClass, dialogWmPID, this]() { + SP proc = makeShared("hyprland-dialog", + std::vector{"--title", title, "--text", + std::format("Application {} with class of {} is not responding.\nWhat do you want to do with it?", + appName.empty() ? "unknown" : appName, appClass.empty() ? "unknown" : appClass), + "--buttons", "Terminate;Wait"}); - for (const auto& w : g_pCompositor->m_windows) { - if (!w->m_isMapped) - continue; + dialogProc = proc; + proc->runSync(); - if (!fitsWindow(w)) - continue; + dialogThreadExited = true; - if (w->m_workspace) - dialogBox->setExecRule(std::format("workspace {} silent", w->m_workspace->getConfigName())); - - break; - } - - dialogBox->open()->then([dialogWmPID, this, OPTION_TERMINATE_STR, OPTION_WAIT_STR](SP> r) { - if (r->hasError()) { - Log::logger->log(Log::ERR, "CANRManager::SANRData::runDialog: error spawning dialog"); + if (proc->stdOut().empty()) return; - } - const auto& result = r->result(); - - if (result.starts_with(OPTION_TERMINATE_STR)) - ::kill(dialogWmPID, SIGKILL); - else if (result.starts_with(OPTION_WAIT_STR)) - dialogSaidWait = true; - else - Log::logger->log(Log::ERR, "CANRManager::SANRData::runDialog: lambda: unrecognized result: {}", result); + if (proc->stdOut().starts_with("Terminate")) + kill(dialogWmPID, SIGKILL); + if (proc->stdOut().starts_with("Wait")) + dialogThreadSaidWait = true; }); } -bool CANRManager::SANRData::isRunning() { - return dialogBox && dialogBox->isRunning(); +bool CANRManager::SANRData::isThreadRunning() { + if (dialogThread.native_handle() == 0) + return false; + if (dialogThreadExited) + return false; + return pthread_kill(dialogThread.native_handle(), 0) != ESRCH; } -void CANRManager::SANRData::killDialog() { - if (!dialogBox) +void CANRManager::SANRData::killDialog() const { + if (!dialogProc) return; - dialogBox->kill(); - dialogBox = nullptr; + if (!dialogProc->pid()) { + Debug::log(ERR, "ANR: cannot kill dialogProc, as it doesn't have a pid. If you have hyprutils <= 0.6.0, you will crash soon. Otherwise, dialog failed to spawn??"); + return; + } + + kill(dialogProc->pid(), SIGKILL); } bool CANRManager::SANRData::fitsWindow(PHLWINDOW pWindow) const { - if (pWindow->m_xwaylandSurface) - return pWindow->m_xwaylandSurface == xwaylandSurface; - else if (pWindow->m_xdgSurface) - return pWindow->m_xdgSurface->m_owner == xdgBase && xdgBase; + if (pWindow->m_pXWaylandSurface) + return pWindow->m_pXWaylandSurface == xwaylandSurface; + else if (pWindow->m_pXDGSurface) + return pWindow->m_pXDGSurface->owner == xdgBase && xdgBase; return false; } @@ -246,7 +227,7 @@ bool CANRManager::SANRData::isDefunct() const { return xdgBase.expired() && xwaylandSurface.expired(); } -pid_t CANRManager::SANRData::getPID() const { +pid_t CANRManager::SANRData::getPid() const { if (xdgBase) { pid_t pid = 0; wl_client_get_credentials(xdgBase->client(), &pid, nullptr, nullptr); @@ -254,7 +235,7 @@ pid_t CANRManager::SANRData::getPID() const { } if (xwaylandSurface) - return xwaylandSurface->m_pid; + return xwaylandSurface->pid; return 0; } diff --git a/src/managers/ANRManager.hpp b/src/managers/ANRManager.hpp index 3880249d..26319776 100644 --- a/src/managers/ANRManager.hpp +++ b/src/managers/ANRManager.hpp @@ -7,7 +7,8 @@ #include #include "./eventLoop/EventLoopTimer.hpp" #include "../helpers/signal/Signal.hpp" -#include "../helpers/AsyncDialogBox.hpp" +#include +#include #include class CXDGWMBase; @@ -31,21 +32,22 @@ class CANRManager { SANRData(PHLWINDOW pWindow); ~SANRData(); - WP xwaylandSurface; - WP xdgBase; + WP xwaylandSurface; + WP xdgBase; - int missedResponses = 0; + int missedResponses = 0; + std::thread dialogThread; + SP dialogProc; + std::atomic dialogThreadExited = false; + std::atomic dialogThreadSaidWait = false; - bool dialogSaidWait = false; - SP dialogBox; - - void runDialog(const std::string& appName, const std::string appClass, pid_t dialogWmPID); - bool isRunning(); - void killDialog(); - bool isDefunct() const; - bool fitsWindow(PHLWINDOW pWindow) const; - pid_t getPID() const; - void ping(); + void runDialog(const std::string& title, const std::string& appName, const std::string appClass, pid_t dialogWmPID); + bool isThreadRunning(); + void killDialog() const; + bool isDefunct() const; + bool fitsWindow(PHLWINDOW pWindow) const; + pid_t getPid() const; + void ping(); }; void onResponse(SP data); @@ -57,4 +59,4 @@ class CANRManager { std::vector> m_data; }; -inline UP g_pANRManager; +inline UP g_pANRManager; \ No newline at end of file diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp new file mode 100644 index 00000000..18dae2a4 --- /dev/null +++ b/src/managers/AnimationManager.cpp @@ -0,0 +1,518 @@ +#include "AnimationManager.hpp" +#include "../Compositor.hpp" +#include "HookSystemManager.hpp" +#include "../config/ConfigManager.hpp" +#include "../desktop/DesktopTypes.hpp" +#include "../helpers/AnimatedVariable.hpp" +#include "../macros.hpp" +#include "../config/ConfigValue.hpp" +#include "../desktop/Window.hpp" +#include "../desktop/LayerSurface.hpp" +#include "eventLoop/EventLoopManager.hpp" +#include "../helpers/varlist/VarList.hpp" +#include "../render/Renderer.hpp" + +#include +#include +#include + +static int wlTick(SP self, void* data) { + if (g_pAnimationManager) + g_pAnimationManager->onTicked(); + + if (g_pCompositor->m_bSessionActive && g_pAnimationManager && g_pHookSystem && !g_pCompositor->m_bUnsafeState && + std::ranges::any_of(g_pCompositor->m_vMonitors, [](const auto& mon) { return mon->m_bEnabled && mon->output; })) { + g_pAnimationManager->tick(); + EMIT_HOOK_EVENT("tick", nullptr); + } + + if (g_pAnimationManager && g_pAnimationManager->shouldTickForNext()) + g_pAnimationManager->scheduleTick(); + + return 0; +} + +CHyprAnimationManager::CHyprAnimationManager() { + m_pAnimationTimer = SP(new CEventLoopTimer(std::chrono::microseconds(500), wlTick, nullptr)); + if (g_pEventLoopManager) // null in --verify-config mode + g_pEventLoopManager->addTimer(m_pAnimationTimer); + + addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0)); +} + +template +static void updateVariable(CAnimatedVariable& av, const float POINTY, bool warp = false) { + if (warp || av.value() == av.goal()) { + av.warp(true, false); + return; + } + + const auto DELTA = av.goal() - av.begun(); + av.value() = av.begun() + DELTA * POINTY; +} + +static void updateColorVariable(CAnimatedVariable& av, const float POINTY, bool warp) { + if (warp || av.value() == av.goal()) { + av.warp(true, false); + return; + } + + // convert both to OkLab, then lerp that, and convert back. + // This is not as fast as just lerping rgb, but it's WAY more precise... + // Use the CHyprColor cache for OkLab + + const auto& L1 = av.begun().asOkLab(); + const auto& L2 = av.goal().asOkLab(); + + static const auto lerp = [](const float one, const float two, const float progress) -> float { return one + (two - one) * progress; }; + + const Hyprgraphics::CColor lerped = Hyprgraphics::CColor::SOkLab{ + .l = lerp(L1.l, L2.l, POINTY), + .a = lerp(L1.a, L2.a, POINTY), + .b = lerp(L1.b, L2.b, POINTY), + }; + + av.value() = {lerped, lerp(av.begun().a, av.goal().a, POINTY)}; +} + +template +static void handleUpdate(CAnimatedVariable& av, bool warp) { + PHLWINDOW PWINDOW = av.m_Context.pWindow.lock(); + PHLWORKSPACE PWORKSPACE = av.m_Context.pWorkspace.lock(); + PHLLS PLAYER = av.m_Context.pLayer.lock(); + PHLMONITOR PMONITOR = nullptr; + bool animationsDisabled = warp; + + if (PWINDOW) { + if (av.m_Context.eDamagePolicy == AVARDAMAGE_ENTIRE) + g_pHyprRenderer->damageWindow(PWINDOW); + else if (av.m_Context.eDamagePolicy == AVARDAMAGE_BORDER) { + const auto PDECO = PWINDOW->getDecorationByType(DECORATION_BORDER); + PDECO->damageEntire(); + } else if (av.m_Context.eDamagePolicy == AVARDAMAGE_SHADOW) { + const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW); + PDECO->damageEntire(); + } + + PMONITOR = PWINDOW->m_pMonitor.lock(); + if (!PMONITOR) + return; + + animationsDisabled = PWINDOW->m_sWindowData.noAnim.valueOr(animationsDisabled); + } else if (PWORKSPACE) { + PMONITOR = PWORKSPACE->m_pMonitor.lock(); + if (!PMONITOR) + return; + + // dont damage the whole monitor on workspace change, unless it's a special workspace, because dim/blur etc + if (PWORKSPACE->m_bIsSpecialWorkspace) + g_pHyprRenderer->damageMonitor(PMONITOR); + + // TODO: just make this into a damn callback already vax... + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped || w->isHidden() || w->m_pWorkspace != PWORKSPACE) + continue; + + if (w->m_bIsFloating && !w->m_bPinned) { + // still doing the full damage hack for floating because sometimes when the window + // goes through multiple monitors the last rendered frame is missing damage somehow?? + const CBox windowBoxNoOffset = w->getFullWindowBoundingBox(); + const CBox monitorBox = {PMONITOR->vecPosition, PMONITOR->vecSize}; + if (windowBoxNoOffset.intersection(monitorBox) != windowBoxNoOffset) // on edges between multiple monitors + g_pHyprRenderer->damageWindow(w, true); + } + + if (PWORKSPACE->m_bIsSpecialWorkspace) + g_pHyprRenderer->damageWindow(w, true); // hack for special too because it can cross multiple monitors + } + + // damage any workspace window that is on any monitor + for (auto const& w : g_pCompositor->m_vWindows) { + if (!validMapped(w) || w->m_pWorkspace != PWORKSPACE || w->m_bPinned) + continue; + + g_pHyprRenderer->damageWindow(w); + } + } else if (PLAYER) { + // "some fucking layers miss 1 pixel???" -- vaxry + CBox expandBox = CBox{PLAYER->realPosition->value(), PLAYER->realSize->value()}; + expandBox.expand(5); + g_pHyprRenderer->damageBox(expandBox); + + PMONITOR = g_pCompositor->getMonitorFromVector(PLAYER->realPosition->goal() + PLAYER->realSize->goal() / 2.F); + if (!PMONITOR) + return; + animationsDisabled = animationsDisabled || PLAYER->noAnimations; + } + + const auto SPENT = av.getPercent(); + const auto PBEZIER = g_pAnimationManager->getBezier(av.getBezierName()); + const auto POINTY = PBEZIER->getYForPoint(SPENT); + const bool WARP = animationsDisabled || SPENT >= 1.f; + + if constexpr (std::same_as) + updateColorVariable(av, POINTY, WARP); + else + updateVariable(av, POINTY, WARP); + + av.onUpdate(); + + switch (av.m_Context.eDamagePolicy) { + case AVARDAMAGE_ENTIRE: { + if (PWINDOW) { + PWINDOW->updateWindowDecos(); + g_pHyprRenderer->damageWindow(PWINDOW); + } else if (PWORKSPACE) { + for (auto const& w : g_pCompositor->m_vWindows) { + if (!validMapped(w) || w->m_pWorkspace != PWORKSPACE) + continue; + + w->updateWindowDecos(); + + // damage any workspace window that is on any monitor + if (!w->m_bPinned) + g_pHyprRenderer->damageWindow(w); + } + } else if (PLAYER) { + if (PLAYER->layer <= 1) + g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); + + // some fucking layers miss 1 pixel??? + CBox expandBox = CBox{PLAYER->realPosition->value(), PLAYER->realSize->value()}; + expandBox.expand(5); + g_pHyprRenderer->damageBox(expandBox); + } + break; + } + case AVARDAMAGE_BORDER: { + RASSERT(PWINDOW, "Tried to AVARDAMAGE_BORDER a non-window AVAR!"); + + const auto PDECO = PWINDOW->getDecorationByType(DECORATION_BORDER); + PDECO->damageEntire(); + + break; + } + case AVARDAMAGE_SHADOW: { + RASSERT(PWINDOW, "Tried to AVARDAMAGE_SHADOW a non-window AVAR!"); + + const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW); + + PDECO->damageEntire(); + + break; + } + default: { + break; + } + } + + // manually schedule a frame + if (PMONITOR) + g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_ANIMATION); +} + +void CHyprAnimationManager::tick() { + static std::chrono::time_point lastTick = std::chrono::high_resolution_clock::now(); + m_fLastTickTime = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastTick).count() / 1000.0; + lastTick = std::chrono::high_resolution_clock::now(); + + static auto PANIMENABLED = CConfigValue("animations:enabled"); + + for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) { + const auto PAV = m_vActiveAnimatedVariables[i].lock(); + if (!PAV) + continue; + + // for disabled anims just warp + bool warp = !*PANIMENABLED || !PAV->enabled(); + + switch (PAV->m_Type) { + case AVARTYPE_FLOAT: { + auto pTypedAV = dynamic_cast*>(PAV.get()); + RASSERT(pTypedAV, "Failed to upcast animated float"); + handleUpdate(*pTypedAV, warp); + } break; + case AVARTYPE_VECTOR: { + auto pTypedAV = dynamic_cast*>(PAV.get()); + RASSERT(pTypedAV, "Failed to upcast animated Vector2D"); + handleUpdate(*pTypedAV, warp); + } break; + case AVARTYPE_COLOR: { + auto pTypedAV = dynamic_cast*>(PAV.get()); + RASSERT(pTypedAV, "Failed to upcast animated CHyprColor"); + handleUpdate(*pTypedAV, warp); + } break; + default: UNREACHABLE(); + } + } + + tickDone(); +} + +void CHyprAnimationManager::scheduleTick() { + if (m_bTickScheduled) + return; + + m_bTickScheduled = true; + + const auto PMOSTHZ = g_pHyprRenderer->m_pMostHzMonitor; + + if (!PMOSTHZ) { + m_pAnimationTimer->updateTimeout(std::chrono::milliseconds(16)); + return; + } + + float refreshDelayMs = std::floor(1000.f / PMOSTHZ->refreshRate); + + const float SINCEPRES = std::chrono::duration_cast(std::chrono::steady_clock::now() - PMOSTHZ->lastPresentationTimer.chrono()).count() / 1000.f; + + const auto TOPRES = std::clamp(refreshDelayMs - SINCEPRES, 1.1f, 1000.f); // we can't send 0, that will disarm it + + m_pAnimationTimer->updateTimeout(std::chrono::milliseconds((int)std::floor(TOPRES))); +} + +void CHyprAnimationManager::onTicked() { + m_bTickScheduled = false; +} + +// +// Anims +// +// + +void CHyprAnimationManager::animationPopin(PHLWINDOW pWindow, bool close, float minPerc) { + const auto GOALPOS = pWindow->m_vRealPosition->goal(); + const auto GOALSIZE = pWindow->m_vRealSize->goal(); + + if (!close) { + pWindow->m_vRealSize->setValue((GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y})); + pWindow->m_vRealPosition->setValue(GOALPOS + GOALSIZE / 2.f - pWindow->m_vRealSize->value() / 2.f); + } else { + *pWindow->m_vRealSize = (GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y}); + *pWindow->m_vRealPosition = GOALPOS + GOALSIZE / 2.f - pWindow->m_vRealSize->goal() / 2.f; + } +} + +void CHyprAnimationManager::animationSlide(PHLWINDOW pWindow, std::string force, bool close) { + pWindow->m_vRealSize->warp(false); // size we preserve in slide + + const auto GOALPOS = pWindow->m_vRealPosition->goal(); + const auto GOALSIZE = pWindow->m_vRealSize->goal(); + + const auto PMONITOR = pWindow->m_pMonitor.lock(); + + if (!PMONITOR) + return; // unsafe state most likely + + Vector2D posOffset; + + if (force != "") { + if (force == "bottom") + posOffset = Vector2D(GOALPOS.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y); + else if (force == "left") + posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0); + else if (force == "right") + posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0); + else + posOffset = Vector2D(GOALPOS.x, PMONITOR->vecPosition.y - GOALSIZE.y); + + if (!close) + pWindow->m_vRealPosition->setValue(posOffset); + else + *pWindow->m_vRealPosition = posOffset; + + return; + } + + const auto MIDPOINT = GOALPOS + GOALSIZE / 2.f; + + // check sides it touches + const bool DISPLAYLEFT = STICKS(pWindow->m_vPosition.x, PMONITOR->vecPosition.x + PMONITOR->vecReservedTopLeft.x); + const bool DISPLAYRIGHT = STICKS(pWindow->m_vPosition.x + pWindow->m_vSize.x, PMONITOR->vecPosition.x + PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x); + const bool DISPLAYTOP = STICKS(pWindow->m_vPosition.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y); + const bool DISPLAYBOTTOM = STICKS(pWindow->m_vPosition.y + pWindow->m_vSize.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y); + + if (DISPLAYBOTTOM && DISPLAYTOP) { + if (DISPLAYLEFT && DISPLAYRIGHT) { + posOffset = GOALPOS + Vector2D(0.0, GOALSIZE.y); + } else if (DISPLAYLEFT) { + posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0); + } else { + posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0); + } + } else if (DISPLAYTOP) { + posOffset = GOALPOS - Vector2D(0.0, GOALSIZE.y); + } else if (DISPLAYBOTTOM) { + posOffset = GOALPOS + Vector2D(0.0, GOALSIZE.y); + } else { + if (MIDPOINT.y > PMONITOR->vecPosition.y + PMONITOR->vecSize.y / 2.f) + posOffset = Vector2D(GOALPOS.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y); + else + posOffset = Vector2D(GOALPOS.x, PMONITOR->vecPosition.y - GOALSIZE.y); + } + + if (!close) + pWindow->m_vRealPosition->setValue(posOffset); + else + *pWindow->m_vRealPosition = posOffset; +} + +void CHyprAnimationManager::animationGnomed(PHLWINDOW pWindow, bool close) { + const auto GOALPOS = pWindow->m_vRealPosition->goal(); + const auto GOALSIZE = pWindow->m_vRealSize->goal(); + + if (close) { + *pWindow->m_vRealPosition = GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F}; + *pWindow->m_vRealSize = Vector2D{GOALSIZE.x, 0.F}; + } else { + pWindow->m_vRealPosition->setValueAndWarp(GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F}); + pWindow->m_vRealSize->setValueAndWarp(Vector2D{GOALSIZE.x, 0.F}); + *pWindow->m_vRealPosition = GOALPOS; + *pWindow->m_vRealSize = GOALSIZE; + } +} + +void CHyprAnimationManager::onWindowPostCreateClose(PHLWINDOW pWindow, bool close) { + if (!close) { + pWindow->m_vRealPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn")); + pWindow->m_vRealSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn")); + pWindow->m_fAlpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn")); + } else { + pWindow->m_vRealPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut")); + pWindow->m_vRealSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut")); + pWindow->m_fAlpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeOut")); + } + + std::string ANIMSTYLE = pWindow->m_vRealPosition->getStyle(); + transform(ANIMSTYLE.begin(), ANIMSTYLE.end(), ANIMSTYLE.begin(), ::tolower); + + CVarList animList(ANIMSTYLE, 0, 's'); + + // if the window is not being animated, that means the layout set a fixed size for it, don't animate. + if (!pWindow->m_vRealPosition->isBeingAnimated() && !pWindow->m_vRealSize->isBeingAnimated()) + return; + + // if the animation is disabled and we are leaving, ignore the anim to prevent the snapshot being fucked + if (!pWindow->m_vRealPosition->enabled()) + return; + + if (pWindow->m_sWindowData.animationStyle.hasValue()) { + const auto STYLE = pWindow->m_sWindowData.animationStyle.value(); + // the window has config'd special anim + if (STYLE.starts_with("slide")) { + CVarList animList2(STYLE, 0, 's'); + animationSlide(pWindow, animList2[1], close); + } else if (STYLE == "gnomed" || STYLE == "gnome") + animationGnomed(pWindow, close); + else { + // anim popin, fallback + + float minPerc = 0.f; + if (STYLE.find("%") != std::string::npos) { + try { + auto percstr = STYLE.substr(STYLE.find_last_of(' ')); + minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { + ; // oops + } + } + + animationPopin(pWindow, close, minPerc / 100.f); + } + } else { + if (animList[0] == "slide") + animationSlide(pWindow, animList[1], close); + else if (animList[0] == "gnomed" || animList[0] == "gnome") + animationGnomed(pWindow, close); + else { + // anim popin, fallback + + float minPerc = 0.f; + if (!ANIMSTYLE.starts_with("%")) { + try { + auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ')); + minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { + ; // oops + } + } + + animationPopin(pWindow, close, minPerc / 100.f); + } + } +} + +std::string CHyprAnimationManager::styleValidInConfigVar(const std::string& config, const std::string& style) { + if (config.starts_with("window")) { + if (style.starts_with("slide") || style == "gnome" || style == "gnomed") + return ""; + else if (style.starts_with("popin")) { + // try parsing + float minPerc = 0.f; + if (style.find('%') != std::string::npos) { + try { + auto percstr = style.substr(style.find_last_of(' ')); + minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { return "invalid minperc"; } + + return ""; + } + + minPerc; // fix warning + + return ""; + } + + return "unknown style"; + } else if (config.starts_with("workspaces") || config.starts_with("specialWorkspace")) { + if (style == "slide" || style == "slidevert" || style == "fade") + return ""; + else if (style.starts_with("slidefade")) { + // try parsing + float movePerc = 0.f; + if (style.find('%') != std::string::npos) { + try { + auto percstr = style.substr(style.find_last_of(' ') + 1); + movePerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { return "invalid movePerc"; } + + return ""; + } + + movePerc; // fix warning + + return ""; + } + + return "unknown style"; + } else if (config == "borderangle") { + if (style == "loop" || style == "once") + return ""; + return "unknown style"; + } else if (config.starts_with("layers")) { + if (style == "fade" || style == "" || style == "slide") + return ""; + else if (style.starts_with("popin")) { + // try parsing + float minPerc = 0.f; + if (style.find('%') != std::string::npos) { + try { + auto percstr = style.substr(style.find_last_of(' ')); + minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); + } catch (std::exception& e) { return "invalid minperc"; } + + return ""; + } + + minPerc; // fix warning + + return ""; + } + return ""; + return "unknown style"; + } else { + return "animation has no styles"; + } + + return ""; +} diff --git a/src/managers/animation/AnimationManager.hpp b/src/managers/AnimationManager.hpp similarity index 66% rename from src/managers/animation/AnimationManager.hpp rename to src/managers/AnimationManager.hpp index 35bb1e8a..833087d7 100644 --- a/src/managers/animation/AnimationManager.hpp +++ b/src/managers/AnimationManager.hpp @@ -3,33 +3,30 @@ #include #include -#include "../../defines.hpp" -#include "../../helpers/AnimatedVariable.hpp" -#include "../../desktop/DesktopTypes.hpp" -#include "../../helpers/time/Timer.hpp" -#include "../eventLoop/EventLoopTimer.hpp" +#include "../defines.hpp" +#include "../helpers/AnimatedVariable.hpp" +#include "../desktop/DesktopTypes.hpp" +#include "eventLoop/EventLoopTimer.hpp" class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager { public: CHyprAnimationManager(); void tick(); - void frameTick(); virtual void scheduleTick(); virtual void onTicked(); - // Reset tick state after session changes (suspend/wake, lock/unlock) - void resetTickState(); - using SAnimationPropertyConfig = Hyprutils::Animation::SAnimationPropertyConfig; template void createAnimation(const VarType& v, PHLANIMVAR& pav, SP pConfig, eAVarDamagePolicy policy) { constexpr const eAnimatedVarType EAVTYPE = typeToeAnimatedVarType; - pav = makeUnique>(); + const auto PAV = makeShared>(); - pav->create2(EAVTYPE, sc(this), pav, v); - pav->setConfig(pConfig); - pav->m_Context.eDamagePolicy = policy; + PAV->create(EAVTYPE, static_cast(this), PAV, v); + PAV->setConfig(pConfig); + PAV->m_Context.eDamagePolicy = policy; + + pav = std::move(PAV); } template @@ -48,16 +45,21 @@ class CHyprAnimationManager : public Hyprutils::Animation::CAnimationManager { pav->m_Context.pLayer = pLayer; } + void onWindowPostCreateClose(PHLWINDOW, bool close = false); + std::string styleValidInConfigVar(const std::string&, const std::string&); - SP m_animationTimer; + SP m_pAnimationTimer; - float m_lastTickTimeMs; + float m_fLastTickTime; // in ms private: - bool m_tickScheduled = false; - bool m_lastTickValid = false; - CTimer m_lastTickTimer; + bool m_bTickScheduled = false; + + // Anim stuff + void animationPopin(PHLWINDOW, bool close = false, float minPerc = 0.f); + void animationSlide(PHLWINDOW, std::string force = "", bool close = false); + void animationGnomed(PHLWINDOW, bool close = false); }; inline UP g_pAnimationManager; diff --git a/src/managers/CursorManager.cpp b/src/managers/CursorManager.cpp index 7564ca75..160ef2eb 100644 --- a/src/managers/CursorManager.cpp +++ b/src/managers/CursorManager.cpp @@ -3,11 +3,10 @@ #include "../config/ConfigValue.hpp" #include "PointerManager.hpp" #include "../xwayland/XWayland.hpp" -#include "../helpers/Monitor.hpp" -#include "../event/EventBus.hpp" +#include "../managers/HookSystemManager.hpp" static int cursorAnimTimer(SP self, void* data) { - const auto cursorMgr = sc(data); + const auto cursorMgr = reinterpret_cast(data); cursorMgr->tickAnimatedCursor(); return 1; } @@ -16,19 +15,15 @@ static void hcLogger(enum eHyprcursorLogLevel level, char* message) { if (level == HC_LOG_TRACE) return; - Log::logger->log(Log::DEBUG, "[hc] {}", message); + Debug::log(NONE, "[hc] {}", message); } -CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : m_hotspot(hot_), m_stride(cairo_image_surface_get_stride(surf)) { +CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_), surface(surf), stride(cairo_image_surface_get_stride(surf)) { size = size_; - - m_data = std::vector(cairo_image_surface_get_data(surf), cairo_image_surface_get_data(surf) + (cairo_image_surface_get_height(surf) * m_stride)); } -CCursorBuffer::CCursorBuffer(const uint8_t* pixelData, const Vector2D& size_, const Vector2D& hot_) : m_hotspot(hot_), m_stride(4 * size_.x) { +CCursorBuffer::CCursorBuffer(uint8_t* pixelData_, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_), pixelData(pixelData_), stride(4 * size_.x) { size = size_; - - m_data = std::vector(pixelData, pixelData + (sc(size_.y) * m_stride)); } Aquamarine::eBufferCapability CCursorBuffer::caps() { @@ -56,12 +51,12 @@ Aquamarine::SSHMAttrs CCursorBuffer::shm() { attrs.success = true; attrs.format = DRM_FORMAT_ARGB8888; attrs.size = size; - attrs.stride = m_stride; + attrs.stride = stride; return attrs; } std::tuple CCursorBuffer::beginDataPtr(uint32_t flags) { - return {m_data.data(), DRM_FORMAT_ARGB8888, m_stride}; + return {pixelData ? pixelData : cairo_image_surface_get_data(surface), DRM_FORMAT_ARGB8888, stride}; } void CCursorBuffer::endDataPtr() { @@ -69,93 +64,93 @@ void CCursorBuffer::endDataPtr() { } CCursorManager::CCursorManager() { - m_hyprcursor = makeUnique(m_theme.empty() ? nullptr : m_theme.c_str(), hcLogger); - m_xcursor = makeUnique(); + m_pHyprcursor = makeUnique(m_szTheme.empty() ? nullptr : m_szTheme.c_str(), hcLogger); + m_pXcursor = makeUnique(); static auto PUSEHYPRCURSOR = CConfigValue("cursor:enable_hyprcursor"); - if (m_hyprcursor->valid() && *PUSEHYPRCURSOR) { + if (m_pHyprcursor->valid() && *PUSEHYPRCURSOR) { // find default size. First, HYPRCURSOR_SIZE then default to 24 auto const* SIZE = getenv("HYPRCURSOR_SIZE"); if (SIZE) { try { - m_size = std::stoi(SIZE); + m_iSize = std::stoi(SIZE); } catch (...) { ; } } - if (m_size <= 0) { - Log::logger->log(Log::WARN, "HYPRCURSOR_SIZE size not set, defaulting to size 24"); - m_size = 24; + if (m_iSize <= 0) { + Debug::log(WARN, "HYPRCURSOR_SIZE size not set, defaulting to size 24"); + m_iSize = 24; } } else { - Log::logger->log(Log::ERR, "Hyprcursor failed loading theme \"{}\", falling back to Xcursor.", m_theme); + Debug::log(ERR, "Hyprcursor failed loading theme \"{}\", falling back to Xcursor.", m_szTheme); auto const* SIZE = getenv("XCURSOR_SIZE"); if (SIZE) { try { - m_size = std::stoi(SIZE); + m_iSize = std::stoi(SIZE); } catch (...) { ; } } - if (m_size <= 0) { - Log::logger->log(Log::WARN, "XCURSOR_SIZE size not set, defaulting to size 24"); - m_size = 24; + if (m_iSize <= 0) { + Debug::log(WARN, "XCURSOR_SIZE size not set, defaulting to size 24"); + m_iSize = 24; } } // since we fallback to xcursor always load it on startup. otherwise we end up with a empty theme if hyprcursor is enabled in the config // and then later is disabled. - m_xcursor->loadTheme(getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "default", m_size, m_cursorScale); + m_pXcursor->loadTheme(getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "default", m_iSize, m_fCursorScale); - m_animationTimer = makeShared(std::nullopt, cursorAnimTimer, this); - g_pEventLoopManager->addTimer(m_animationTimer); + m_pAnimationTimer = makeShared(std::nullopt, cursorAnimTimer, this); + g_pEventLoopManager->addTimer(m_pAnimationTimer); updateTheme(); - static auto P = Event::bus()->m_events.monitor.layoutChanged.listen([this] { this->updateTheme(); }); + static auto P = g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateTheme(); }); } CCursorManager::~CCursorManager() { - if (m_animationTimer && g_pEventLoopManager) { - g_pEventLoopManager->removeTimer(m_animationTimer); - m_animationTimer.reset(); + if (m_pAnimationTimer && g_pEventLoopManager) { + g_pEventLoopManager->removeTimer(m_pAnimationTimer); + m_pAnimationTimer.reset(); } - if (m_hyprcursor->valid() && m_currentStyleInfo.size > 0) - m_hyprcursor->cursorSurfaceStyleDone(m_currentStyleInfo); + if (m_pHyprcursor->valid() && m_sCurrentStyleInfo.size > 0) + m_pHyprcursor->cursorSurfaceStyleDone(m_sCurrentStyleInfo); } SP CCursorManager::getCursorBuffer() { - return !m_cursorBuffers.empty() ? m_cursorBuffers.back() : nullptr; + return !m_vCursorBuffers.empty() ? m_vCursorBuffers.back() : nullptr; } -void CCursorManager::setCursorSurface(SP surf, const Vector2D& hotspot) { +void CCursorManager::setCursorSurface(SP surf, const Vector2D& hotspot) { if (!surf || !surf->resource()) g_pPointerManager->resetCursorImage(); else g_pPointerManager->setCursorSurface(surf, hotspot); - m_ourBufferConnected = false; + m_bOurBufferConnected = false; } void CCursorManager::setCursorBuffer(SP buf, const Vector2D& hotspot, const float& scale) { - m_cursorBuffers.emplace_back(buf); + m_vCursorBuffers.emplace_back(buf); g_pPointerManager->setCursorBuffer(getCursorBuffer(), hotspot, scale); - if (m_cursorBuffers.size() > 1) - std::erase_if(m_cursorBuffers, [this](const auto& buf) { return buf.get() == m_cursorBuffers.front().get(); }); + if (m_vCursorBuffers.size() > 1) + std::erase_if(m_vCursorBuffers, [this](const auto& buf) { return buf.get() == m_vCursorBuffers.front().get(); }); - m_ourBufferConnected = true; + m_bOurBufferConnected = true; } void CCursorManager::setAnimationTimer(const int& frame, const int& delay) { if (delay > 0) { // arm - m_animationTimer->updateTimeout(std::chrono::milliseconds(delay)); + m_pAnimationTimer->updateTimeout(std::chrono::milliseconds(delay)); } else { // disarm - m_animationTimer->updateTimeout(std::nullopt); + m_pAnimationTimer->updateTimeout(std::nullopt); } - m_currentAnimationFrame = frame; + m_iCurrentAnimationFrame = frame; } void CCursorManager::setCursorFromName(const std::string& name) { @@ -163,11 +158,11 @@ void CCursorManager::setCursorFromName(const std::string& name) { static auto PUSEHYPRCURSOR = CConfigValue("cursor:enable_hyprcursor"); auto setXCursor = [this](auto const& name) { - float scale = std::ceil(m_cursorScale); + float scale = std::ceil(m_fCursorScale); - auto xcursor = m_xcursor->getShape(name, m_size, m_cursorScale); + auto xcursor = m_pXcursor->getShape(name, m_iSize, m_fCursorScale); auto& icon = xcursor->images.front(); - auto buf = makeShared(rc(icon.pixels.data()), icon.size, icon.hotspot); + auto buf = makeShared((uint8_t*)icon.pixels.data(), icon.size, icon.hotspot); setCursorBuffer(buf, icon.hotspot / scale, scale); m_currentXcursor = xcursor; @@ -181,89 +176,90 @@ void CCursorManager::setCursorFromName(const std::string& name) { }; auto setHyprCursor = [this](auto const& name) { - m_currentCursorShapeData = m_hyprcursor->getShape(name.c_str(), m_currentStyleInfo); + m_sCurrentCursorShapeData = m_pHyprcursor->getShape(name.c_str(), m_sCurrentStyleInfo); - if (m_currentCursorShapeData.images.empty()) { + if (m_sCurrentCursorShapeData.images.size() < 1) { // try with '_' first (old hc, etc) std::string newName = name; - std::ranges::replace(newName, '-', '_'); + std::replace(newName.begin(), newName.end(), '-', '_'); - m_currentCursorShapeData = m_hyprcursor->getShape(newName.c_str(), m_currentStyleInfo); + m_sCurrentCursorShapeData = m_pHyprcursor->getShape(newName.c_str(), m_sCurrentStyleInfo); } - if (m_currentCursorShapeData.images.empty()) { + if (m_sCurrentCursorShapeData.images.size() < 1) { // fallback to a default if available constexpr const std::array fallbackShapes = {"default", "left_ptr", "left-ptr"}; for (auto const& s : fallbackShapes) { - m_currentCursorShapeData = m_hyprcursor->getShape(s, m_currentStyleInfo); + m_sCurrentCursorShapeData = m_pHyprcursor->getShape(s, m_sCurrentStyleInfo); - if (!m_currentCursorShapeData.images.empty()) + if (m_sCurrentCursorShapeData.images.size() > 0) break; } - if (m_currentCursorShapeData.images.empty()) { - Log::logger->log(Log::ERR, "BUG THIS: No fallback found for a cursor in setCursorFromName"); + if (m_sCurrentCursorShapeData.images.size() < 1) { + Debug::log(ERR, "BUG THIS: No fallback found for a cursor in setCursorFromName"); return false; } } - auto buf = makeShared(m_currentCursorShapeData.images[0].surface, Vector2D{m_currentCursorShapeData.images[0].size, m_currentCursorShapeData.images[0].size}, - Vector2D{m_currentCursorShapeData.images[0].hotspotX, m_currentCursorShapeData.images[0].hotspotY}); - auto hotspot = Vector2D{m_currentCursorShapeData.images[0].hotspotX, m_currentCursorShapeData.images[0].hotspotY} / m_cursorScale; - setCursorBuffer(buf, hotspot, m_cursorScale); + auto buf = + makeShared(m_sCurrentCursorShapeData.images[0].surface, Vector2D{m_sCurrentCursorShapeData.images[0].size, m_sCurrentCursorShapeData.images[0].size}, + Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY}); + auto hotspot = Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY} / m_fCursorScale; + setCursorBuffer(buf, hotspot, m_fCursorScale); int delay = 0; int frame = 0; - if (m_currentCursorShapeData.images.size() > 1) - delay = m_currentCursorShapeData.images[frame].delay; + if (m_sCurrentCursorShapeData.images.size() > 1) + delay = m_sCurrentCursorShapeData.images[frame].delay; setAnimationTimer(frame, delay); return true; }; - if (!m_hyprcursor->valid() || !*PUSEHYPRCURSOR || !setHyprCursor(name)) + if (!m_pHyprcursor->valid() || !*PUSEHYPRCURSOR || !setHyprCursor(name)) setXCursor(name); } void CCursorManager::tickAnimatedCursor() { - if (!m_ourBufferConnected) + if (!m_bOurBufferConnected) return; - if (!m_hyprcursor->valid() && m_currentXcursor->images.size() > 1) { - m_currentAnimationFrame++; + if (!m_pHyprcursor->valid() && m_currentXcursor->images.size() > 1) { + m_iCurrentAnimationFrame++; - if (sc(m_currentAnimationFrame) >= m_currentXcursor->images.size()) - m_currentAnimationFrame = 0; + if ((size_t)m_iCurrentAnimationFrame >= m_currentXcursor->images.size()) + m_iCurrentAnimationFrame = 0; - float scale = std::ceil(m_cursorScale); - auto& icon = m_currentXcursor->images.at(m_currentAnimationFrame); - auto buf = makeShared(rc(icon.pixels.data()), icon.size, icon.hotspot); + float scale = std::ceil(m_fCursorScale); + auto& icon = m_currentXcursor->images.at(m_iCurrentAnimationFrame); + auto buf = makeShared((uint8_t*)icon.pixels.data(), icon.size, icon.hotspot); setCursorBuffer(buf, icon.hotspot / scale, scale); - setAnimationTimer(m_currentAnimationFrame, m_currentXcursor->images[m_currentAnimationFrame].delay); - } else if (m_currentCursorShapeData.images.size() > 1) { - m_currentAnimationFrame++; + setAnimationTimer(m_iCurrentAnimationFrame, m_currentXcursor->images[m_iCurrentAnimationFrame].delay); + } else if (m_sCurrentCursorShapeData.images.size() > 1) { + m_iCurrentAnimationFrame++; - if (sc(m_currentAnimationFrame) >= m_currentCursorShapeData.images.size()) - m_currentAnimationFrame = 0; + if ((size_t)m_iCurrentAnimationFrame >= m_sCurrentCursorShapeData.images.size()) + m_iCurrentAnimationFrame = 0; auto hotspot = - Vector2D{m_currentCursorShapeData.images[m_currentAnimationFrame].hotspotX, m_currentCursorShapeData.images[m_currentAnimationFrame].hotspotY} / m_cursorScale; + Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY} / m_fCursorScale; auto buf = makeShared( - m_currentCursorShapeData.images[m_currentAnimationFrame].surface, - Vector2D{m_currentCursorShapeData.images[m_currentAnimationFrame].size, m_currentCursorShapeData.images[m_currentAnimationFrame].size}, - Vector2D{m_currentCursorShapeData.images[m_currentAnimationFrame].hotspotX, m_currentCursorShapeData.images[m_currentAnimationFrame].hotspotY}); - setCursorBuffer(buf, hotspot, m_cursorScale); - setAnimationTimer(m_currentAnimationFrame, m_currentCursorShapeData.images[m_currentAnimationFrame].delay); + m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].surface, + Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size}, + Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY}); + setCursorBuffer(buf, hotspot, m_fCursorScale); + setAnimationTimer(m_iCurrentAnimationFrame, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].delay); } } SCursorImageData CCursorManager::dataFor(const std::string& name) { - if (!m_hyprcursor->valid()) + if (!m_pHyprcursor->valid()) return {}; - const auto IMAGES = m_hyprcursor->getShape(name.c_str(), m_currentStyleInfo); + const auto IMAGES = m_pHyprcursor->getShape(name.c_str(), m_sCurrentStyleInfo); if (IMAGES.images.empty()) return {}; @@ -278,10 +274,10 @@ void CCursorManager::setXWaylandCursor() { g_pXWayland->setCursor(cairo_image_surface_get_data(CURSOR.surface), cairo_image_surface_get_stride(CURSOR.surface), {CURSOR.size, CURSOR.size}, {CURSOR.hotspotX, CURSOR.hotspotY}); else { - auto xcursor = m_xcursor->getShape("left_ptr", m_size, 1); + auto xcursor = m_pXcursor->getShape("left_ptr", m_iSize, 1); auto& icon = xcursor->images.front(); - g_pXWayland->setCursor(rc(icon.pixels.data()), icon.size.x * 4, icon.size, icon.hotspot); + g_pXWayland->setCursor((uint8_t*)icon.pixels.data(), icon.size.x * 4, icon.size, icon.hotspot); } } @@ -289,50 +285,51 @@ void CCursorManager::updateTheme() { static auto PUSEHYPRCURSOR = CConfigValue("cursor:enable_hyprcursor"); float highestScale = 1.0; - for (auto const& m : g_pCompositor->m_monitors) { - if (m->m_scale > highestScale) - highestScale = m->m_scale; + for (auto const& m : g_pCompositor->m_vMonitors) { + if (m->scale > highestScale) + highestScale = m->scale; } - m_cursorScale = highestScale; - m_currentCursorShapeData = {}; + m_fCursorScale = highestScale; if (*PUSEHYPRCURSOR) { - if (m_currentStyleInfo.size > 0 && m_hyprcursor->valid()) - m_hyprcursor->cursorSurfaceStyleDone(m_currentStyleInfo); + if (m_sCurrentStyleInfo.size > 0 && m_pHyprcursor->valid()) + m_pHyprcursor->cursorSurfaceStyleDone(m_sCurrentStyleInfo); - m_currentStyleInfo.size = std::round(m_size * highestScale); + m_sCurrentStyleInfo.size = std::round(m_iSize * highestScale); - if (m_hyprcursor->valid()) - m_hyprcursor->loadThemeStyle(m_currentStyleInfo); + if (m_pHyprcursor->valid()) + m_pHyprcursor->loadThemeStyle(m_sCurrentStyleInfo); } - for (auto const& m : g_pCompositor->m_monitors) { - m->m_forceFullFrames = 5; + setCursorFromName("left_ptr"); + + for (auto const& m : g_pCompositor->m_vMonitors) { + m->forceFullFrames = 5; g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE); } } bool CCursorManager::changeTheme(const std::string& name, const int size) { static auto PUSEHYPRCURSOR = CConfigValue("cursor:enable_hyprcursor"); - m_theme = name.empty() ? "" : name; - m_size = size <= 0 ? 24 : size; + m_szTheme = name.empty() ? "" : name; + m_iSize = size <= 0 ? 24 : size; auto xcursor_theme = getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "default"; if (*PUSEHYPRCURSOR) { auto options = Hyprcursor::SManagerOptions(); options.logFn = hcLogger; options.allowDefaultFallback = false; - m_theme = name.empty() ? "" : name; - m_size = size; + m_szTheme = name.empty() ? "" : name; + m_iSize = size; - m_hyprcursor = makeUnique(m_theme.empty() ? nullptr : m_theme.c_str(), options); - if (!m_hyprcursor->valid()) { - Log::logger->log(Log::ERR, "Hyprcursor failed loading theme \"{}\", falling back to XCursor.", m_theme); - m_xcursor->loadTheme(m_theme.empty() ? xcursor_theme : m_theme, m_size, m_cursorScale); + m_pHyprcursor = makeUnique(m_szTheme.empty() ? nullptr : m_szTheme.c_str(), options); + if (!m_pHyprcursor->valid()) { + Debug::log(ERR, "Hyprcursor failed loading theme \"{}\", falling back to XCursor.", m_szTheme); + m_pXcursor->loadTheme(m_szTheme.empty() ? xcursor_theme : m_szTheme, m_iSize, m_fCursorScale); } } else - m_xcursor->loadTheme(m_theme.empty() ? xcursor_theme : m_theme, m_size, m_cursorScale); + m_pXcursor->loadTheme(m_szTheme.empty() ? xcursor_theme : m_szTheme, m_iSize, m_fCursorScale); updateTheme(); @@ -340,9 +337,5 @@ bool CCursorManager::changeTheme(const std::string& name, const int size) { } void CCursorManager::syncGsettings() { - m_xcursor->syncGsettings(); -} - -float CCursorManager::getScaledSize() const { - return m_size * m_cursorScale; + m_pXcursor->syncGsettings(); } diff --git a/src/managers/CursorManager.hpp b/src/managers/CursorManager.hpp index f4c42d30..c5ded5da 100644 --- a/src/managers/CursorManager.hpp +++ b/src/managers/CursorManager.hpp @@ -3,7 +3,6 @@ #include #include #include "../includes.hpp" -#include "../desktop/view/WLSurface.hpp" #include "../helpers/math/Math.hpp" #include "../helpers/memory/Memory.hpp" #include "../macros.hpp" @@ -11,12 +10,14 @@ #include "managers/XCursorManager.hpp" #include +class CWLSurface; + AQUAMARINE_FORWARD(IBuffer); class CCursorBuffer : public Aquamarine::IBuffer { public: CCursorBuffer(cairo_surface_t* surf, const Vector2D& size, const Vector2D& hotspot); - CCursorBuffer(const uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot); + CCursorBuffer(uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot); ~CCursorBuffer() = default; virtual Aquamarine::eBufferCapability caps(); @@ -29,9 +30,10 @@ class CCursorBuffer : public Aquamarine::IBuffer { virtual void endDataPtr(); private: - Vector2D m_hotspot; - std::vector m_data; - size_t m_stride = 0; + Vector2D hotspot; + cairo_surface_t* surface = nullptr; + uint8_t* pixelData = nullptr; + size_t stride = 0; }; class CCursorManager { @@ -42,7 +44,7 @@ class CCursorManager { SP getCursorBuffer(); void setCursorFromName(const std::string& name); - void setCursorSurface(SP surf, const Vector2D& hotspot); + void setCursorSurface(SP surf, const Vector2D& hotspot); void setCursorBuffer(SP buf, const Vector2D& hotspot, const float& scale); void setAnimationTimer(const int& frame, const int& delay); @@ -54,25 +56,23 @@ class CCursorManager { void tickAnimatedCursor(); - float getScaledSize() const; - private: - bool m_ourBufferConnected = false; - std::vector> m_cursorBuffers; + bool m_bOurBufferConnected = false; + std::vector> m_vCursorBuffers; - UP m_hyprcursor; - UP m_xcursor; + UP m_pHyprcursor; + UP m_pXcursor; SP m_currentXcursor; - std::string m_theme = ""; - int m_size = 0; - float m_cursorScale = 1.0; + std::string m_szTheme = ""; + int m_iSize = 0; + float m_fCursorScale = 1.0; - Hyprcursor::SCursorStyleInfo m_currentStyleInfo; + Hyprcursor::SCursorStyleInfo m_sCurrentStyleInfo; - SP m_animationTimer; - int m_currentAnimationFrame = 0; - Hyprcursor::SCursorShapeData m_currentCursorShapeData; + SP m_pAnimationTimer; + int m_iCurrentAnimationFrame = 0; + Hyprcursor::SCursorShapeData m_sCurrentCursorShapeData; }; inline UP g_pCursorManager; diff --git a/src/managers/DonationNagManager.cpp b/src/managers/DonationNagManager.cpp index 62dd15b7..d7eab9ae 100644 --- a/src/managers/DonationNagManager.cpp +++ b/src/managers/DonationNagManager.cpp @@ -1,5 +1,5 @@ #include "DonationNagManager.hpp" -#include "../debug/log/Logger.hpp" +#include "../debug/Log.hpp" #include "VersionKeeperManager.hpp" #include "eventLoop/EventLoopManager.hpp" #include "../config/ConfigValue.hpp" @@ -10,9 +10,7 @@ #include "../helpers/fs/FsUtils.hpp" #include -#include using namespace Hyprutils::OS; -using namespace Hyprutils::String; constexpr const char* LAST_NAG_FILE_NAME = "lastNag"; constexpr uint64_t DAY_IN_SECONDS = 3600ULL * 24; @@ -48,33 +46,34 @@ CDonationNagManager::CDonationNagManager() { const auto EPOCH = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - uint64_t currentMajor = 0; - try { - CVarList vl(HYPRLAND_VERSION, 0, '.'); - currentMajor = std::stoull(vl[1]); - } catch (...) { - // ???? + const auto LASTNAGSTR = NFsUtils::readFileAsString(*DATAROOT + "/" + LAST_NAG_FILE_NAME); + + if (!LASTNAGSTR) { + const auto EPOCHSTR = std::format("{}", EPOCH); + NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR); return; } - auto state = getState(); + uint64_t LAST_EPOCH = 0; - if ((!state.major && currentMajor <= 48) || !state.epoch) { - state.major = currentMajor; - state.epoch = state.epoch == 0 ? EPOCH : state.epoch; - writeState(state); + try { + LAST_EPOCH = std::stoull(*LASTNAGSTR); + } catch (std::exception& e) { + Debug::log(ERR, "DonationNag: Last epoch invalid? Failed to parse \"{}\". Setting to today.", *LASTNAGSTR); + const auto EPOCHSTR = std::format("{}", EPOCH); + NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR); return; } // don't nag if the last nag was less than a month ago. This is // mostly for first-time nags, as other nags happen in specific time frames shorter than a month - if (EPOCH - state.epoch < MONTH_IN_SECONDS) { - Log::logger->log(Log::DEBUG, "DonationNag: last nag was {} days ago, too early for a nag.", sc(std::round((EPOCH - state.epoch) / sc(DAY_IN_SECONDS)))); + if (EPOCH - LAST_EPOCH < MONTH_IN_SECONDS) { + Debug::log(LOG, "DonationNag: last nag was {} days ago, too early for a nag.", (int)std::round((EPOCH - LAST_EPOCH) / (double)MONTH_IN_SECONDS)); return; } if (!NFsUtils::executableExistsInPath("hyprland-donate-screen")) { - Log::logger->log(Log::ERR, "DonationNag: executable doesn't exist, skipping."); + Debug::log(ERR, "DonationNag: executable doesn't exist, skipping."); return; } @@ -91,68 +90,25 @@ CDonationNagManager::CDonationNagManager() { if (DAY < nagPoint.dayStart || DAY > nagPoint.dayEnd) continue; - Log::logger->log(Log::DEBUG, "DonationNag: hit nag month {} days {}-{}, it's {} today, nagging", MONTH, nagPoint.dayStart, nagPoint.dayEnd, DAY); + Debug::log(LOG, "DonationNag: hit nag month {} days {}-{}, it's {} today, nagging", MONTH, nagPoint.dayStart, nagPoint.dayEnd, DAY); - fire(); + m_bFired = true; - state.major = currentMajor; - state.epoch = EPOCH; - writeState(state); + const auto EPOCHSTR = std::format("{}", EPOCH); + NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, EPOCHSTR); + + g_pEventLoopManager->doLater([] { + CProcess proc("hyprland-donate-screen", {}); + proc.runAsync(); + }); break; } - if (!m_fired) - Log::logger->log(Log::DEBUG, "DonationNag: didn't hit any nagging periods, checking update"); - - if (state.major < currentMajor) { - Log::logger->log(Log::DEBUG, "DonationNag: hit nag for major update {} -> {}", state.major, currentMajor); - - fire(); - - state.major = currentMajor; - state.epoch = EPOCH; - writeState(state); - } - - if (!m_fired) - Log::logger->log(Log::DEBUG, "DonationNag: didn't hit nagging conditions"); + if (!m_bFired) + Debug::log(LOG, "DonationNag: didn't hit any nagging periods"); } bool CDonationNagManager::fired() { - return m_fired; -} - -void CDonationNagManager::fire() { - static const auto DATAROOT = NFsUtils::getDataHome(); - - m_fired = true; - - g_pEventLoopManager->doLater([] { - CProcess proc("hyprland-donate-screen", {}); - proc.runAsync(); - }); -} - -CDonationNagManager::SStateData CDonationNagManager::getState() { - static const auto DATAROOT = NFsUtils::getDataHome(); - const auto STR = NFsUtils::readFileAsString(*DATAROOT + "/" + LAST_NAG_FILE_NAME); - - if (!STR.has_value()) - return {}; - - CVarList lines(*STR, 0, '\n'); - CDonationNagManager::SStateData state; - - try { - state.epoch = std::stoull(lines[0]); - state.major = std::stoull(lines[1]); - } catch (...) { ; } - - return state; -} - -void CDonationNagManager::writeState(const SStateData& s) { - static const auto DATAROOT = NFsUtils::getDataHome(); - NFsUtils::writeToFile(*DATAROOT + "/" + LAST_NAG_FILE_NAME, std::format("{}\n{}", s.epoch, s.major)); -} + return m_bFired; +} \ No newline at end of file diff --git a/src/managers/DonationNagManager.hpp b/src/managers/DonationNagManager.hpp index c33ae07a..357f979f 100644 --- a/src/managers/DonationNagManager.hpp +++ b/src/managers/DonationNagManager.hpp @@ -10,16 +10,7 @@ class CDonationNagManager { bool fired(); private: - struct SStateData { - uint64_t epoch = 0; - uint64_t major = 0; - }; - - SStateData getState(); - void writeState(const SStateData& s); - void fire(); - - bool m_fired = false; + bool m_bFired = false; }; inline UP g_pDonationNagManager; \ No newline at end of file diff --git a/src/managers/EventManager.cpp b/src/managers/EventManager.cpp index 6d42a818..6231f0bc 100644 --- a/src/managers/EventManager.cpp +++ b/src/managers/EventManager.cpp @@ -11,42 +11,42 @@ #include using namespace Hyprutils::OS; -CEventManager::CEventManager() : m_socketFD(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) { - if (!m_socketFD.isValid()) { - Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket 2. (1) IPC will not work."); +CEventManager::CEventManager() : m_iSocketFD(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) { + if (!m_iSocketFD.isValid()) { + Debug::log(ERR, "Couldn't start the Hyprland Socket 2. (1) IPC will not work."); return; } sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX}; - const auto PATH = g_pCompositor->m_instancePath + "/.socket2.sock"; + const auto PATH = g_pCompositor->m_szInstancePath + "/.socket2.sock"; if (PATH.length() > sizeof(SERVERADDRESS.sun_path) - 1) { - Log::logger->log(Log::ERR, "Socket2 path is too long. (2) IPC will not work."); + Debug::log(ERR, "Socket2 path is too long. (2) IPC will not work."); return; } strncpy(SERVERADDRESS.sun_path, PATH.c_str(), sizeof(SERVERADDRESS.sun_path) - 1); - if (bind(m_socketFD.get(), rc(&SERVERADDRESS), SUN_LEN(&SERVERADDRESS)) < 0) { - Log::logger->log(Log::ERR, "Couldn't bind the Hyprland Socket 2. (3) IPC will not work."); + if (bind(m_iSocketFD.get(), (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS)) < 0) { + Debug::log(ERR, "Couldn't bind the Hyprland Socket 2. (3) IPC will not work."); return; } // 10 max queued. - if (listen(m_socketFD.get(), 10) < 0) { - Log::logger->log(Log::ERR, "Couldn't listen on the Hyprland Socket 2. (4) IPC will not work."); + if (listen(m_iSocketFD.get(), 10) < 0) { + Debug::log(ERR, "Couldn't listen on the Hyprland Socket 2. (4) IPC will not work."); return; } - m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_socketFD.get(), WL_EVENT_READABLE, onClientEvent, nullptr); + m_pEventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD.get(), WL_EVENT_READABLE, onClientEvent, nullptr); } CEventManager::~CEventManager() { - for (const auto& client : m_clients) { + for (const auto& client : m_vClients) { wl_event_source_remove(client.eventSource); } - if (m_eventSource != nullptr) - wl_event_source_remove(m_eventSource); + if (m_pEventSource != nullptr) + wl_event_source_remove(m_pEventSource); } int CEventManager::onServerEvent(int fd, uint32_t mask, void* data) { @@ -59,34 +59,34 @@ int CEventManager::onClientEvent(int fd, uint32_t mask, void* data) { int CEventManager::onServerEvent(int fd, uint32_t mask) { if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) { - Log::logger->log(Log::ERR, "Socket2 hangup?? IPC broke"); + Debug::log(ERR, "Socket2 hangup?? IPC broke"); - wl_event_source_remove(m_eventSource); - m_eventSource = nullptr; - m_socketFD.reset(); + wl_event_source_remove(m_pEventSource); + m_pEventSource = nullptr; + m_iSocketFD.reset(); return 0; } sockaddr_in clientAddress; socklen_t clientSize = sizeof(clientAddress); - CFileDescriptor ACCEPTEDCONNECTION{accept4(m_socketFD.get(), rc(&clientAddress), &clientSize, SOCK_CLOEXEC | SOCK_NONBLOCK)}; + CFileDescriptor ACCEPTEDCONNECTION{accept4(m_iSocketFD.get(), (sockaddr*)&clientAddress, &clientSize, SOCK_CLOEXEC | SOCK_NONBLOCK)}; if (!ACCEPTEDCONNECTION.isValid()) { if (errno != EAGAIN) { - Log::logger->log(Log::ERR, "Socket2 failed receiving connection, errno: {}", errno); - wl_event_source_remove(m_eventSource); - m_eventSource = nullptr; - m_socketFD.reset(); + Debug::log(ERR, "Socket2 failed receiving connection, errno: {}", errno); + wl_event_source_remove(m_pEventSource); + m_pEventSource = nullptr; + m_iSocketFD.reset(); } return 0; } - Log::logger->log(Log::DEBUG, "Socket2 accepted a new client at FD {}", ACCEPTEDCONNECTION.get()); + Debug::log(LOG, "Socket2 accepted a new client at FD {}", ACCEPTEDCONNECTION.get()); // add to event loop so we can close it when we need to - auto* eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, ACCEPTEDCONNECTION.get(), 0, onServerEvent, nullptr); - m_clients.emplace_back(SClient{ + auto* eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, ACCEPTEDCONNECTION.get(), 0, onServerEvent, nullptr); + m_vClients.emplace_back(SClient{ std::move(ACCEPTEDCONNECTION), {}, eventSource, @@ -97,7 +97,7 @@ int CEventManager::onServerEvent(int fd, uint32_t mask) { int CEventManager::onClientEvent(int fd, uint32_t mask) { if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) { - Log::logger->log(Log::DEBUG, "Socket2 fd {} hung up", fd); + Debug::log(LOG, "Socket2 fd {} hung up", fd); removeClientByFD(fd); return 0; } @@ -123,14 +123,14 @@ int CEventManager::onClientEvent(int fd, uint32_t mask) { } std::vector::iterator CEventManager::findClientByFD(int fd) { - return std::ranges::find_if(m_clients, [fd](const auto& client) { return client.fd.get() == fd; }); + return std::find_if(m_vClients.begin(), m_vClients.end(), [fd](const auto& client) { return client.fd.get() == fd; }); } std::vector::iterator CEventManager::removeClientByFD(int fd) { const auto CLIENTIT = findClientByFD(fd); wl_event_source_remove(CLIENTIT->eventSource); - return m_clients.erase(CLIENTIT); + return m_vClients.erase(CLIENTIT); } std::string CEventManager::formatEvent(const SHyprIPCEvent& event) const { @@ -141,20 +141,20 @@ std::string CEventManager::formatEvent(const SHyprIPCEvent& event) const { } void CEventManager::postEvent(const SHyprIPCEvent& event) { - if (g_pCompositor->m_isShuttingDown) { - Log::logger->log(Log::WARN, "Suppressed (shutting down) event of type {}, content: {}", event.event, event.data); + if (g_pCompositor->m_bIsShuttingDown) { + Debug::log(WARN, "Suppressed (shutting down) event of type {}, content: {}", event.event, event.data); return; } const size_t MAX_QUEUED_EVENTS = 64; auto sharedEvent = makeShared(formatEvent(event)); - for (auto it = m_clients.begin(); it != m_clients.end();) { + for (auto it = m_vClients.begin(); it != m_vClients.end();) { // try to send the event immediately if the queue is empty const auto QUEUESIZE = it->events.size(); if (QUEUESIZE > 0 || write(it->fd.get(), sharedEvent->c_str(), sharedEvent->length()) < 0) { if (QUEUESIZE >= MAX_QUEUED_EVENTS) { // too many events queued, remove the client - Log::logger->log(Log::ERR, "Socket2 fd {} overflowed event queue, removing", it->fd.get()); + Debug::log(ERR, "Socket2 fd {} overflowed event queue, removing", it->fd.get()); it = removeClientByFD(it->fd.get()); continue; } diff --git a/src/managers/EventManager.hpp b/src/managers/EventManager.hpp index bfed9790..c8335d20 100644 --- a/src/managers/EventManager.hpp +++ b/src/managers/EventManager.hpp @@ -35,10 +35,10 @@ class CEventManager { std::vector::iterator removeClientByFD(int fd); private: - Hyprutils::OS::CFileDescriptor m_socketFD; - wl_event_source* m_eventSource = nullptr; + Hyprutils::OS::CFileDescriptor m_iSocketFD; + wl_event_source* m_pEventSource = nullptr; - std::vector m_clients; + std::vector m_vClients; }; inline UP g_pEventManager; diff --git a/src/managers/HookSystemManager.cpp b/src/managers/HookSystemManager.cpp new file mode 100644 index 00000000..624d2fe1 --- /dev/null +++ b/src/managers/HookSystemManager.cpp @@ -0,0 +1,83 @@ +#include "HookSystemManager.hpp" + +#include "../plugins/PluginSystem.hpp" + +CHookSystemManager::CHookSystemManager() { + ; // +} + +// returns the pointer to the function +SP CHookSystemManager::hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, HANDLE handle) { + SP hookFN = makeShared(fn); + m_mRegisteredHooks[event].emplace_back(SCallbackFNPtr{.fn = hookFN, .handle = handle}); + return hookFN; +} + +void CHookSystemManager::unhook(SP fn) { + for (auto& [k, v] : m_mRegisteredHooks) { + std::erase_if(v, [&](const auto& other) { + SP fn_ = other.fn.lock(); + + return fn_.get() == fn.get(); + }); + } +} + +void CHookSystemManager::emit(std::vector* const callbacks, SCallbackInfo& info, std::any data) { + if (callbacks->empty()) + return; + + std::vector faultyHandles; + volatile bool needsDeadCleanup = false; + + for (auto const& cb : *callbacks) { + + m_bCurrentEventPlugin = false; + + if (!cb.handle) { + // we don't guard hl hooks + + if (SP fn = cb.fn.lock()) + (*fn)(fn.get(), info, data); + else + needsDeadCleanup = true; + continue; + } + + m_bCurrentEventPlugin = true; + + if (std::find(faultyHandles.begin(), faultyHandles.end(), cb.handle) != faultyHandles.end()) + continue; + + try { + if (!setjmp(m_jbHookFaultJumpBuf)) { + if (SP fn = cb.fn.lock()) + (*fn)(fn.get(), info, data); + else + needsDeadCleanup = true; + } else { + // this module crashed. + throw std::exception(); + } + } catch (std::exception& e) { + // TODO: this works only once...? + faultyHandles.push_back(cb.handle); + Debug::log(ERR, "[hookSystem] Hook from plugin {:x} caused a SIGSEGV, queueing for unloading.", (uintptr_t)cb.handle); + } + } + + if (needsDeadCleanup) + std::erase_if(*callbacks, [](const auto& fn) { return !fn.fn.lock(); }); + + if (!faultyHandles.empty()) { + for (auto const& h : faultyHandles) + g_pPluginSystem->unloadPlugin(g_pPluginSystem->getPluginByHandle(h), true); + } +} + +std::vector* CHookSystemManager::getVecForEvent(const std::string& event) { + if (!m_mRegisteredHooks.contains(event)) + Debug::log(LOG, "[hookSystem] New hook event registered: {}", event); + + return &m_mRegisteredHooks[event]; +} diff --git a/src/managers/HookSystemManager.hpp b/src/managers/HookSystemManager.hpp new file mode 100644 index 00000000..c679c380 --- /dev/null +++ b/src/managers/HookSystemManager.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "../defines.hpp" + +#include +#include +#include +#include + +#include + +#define HANDLE void* + +// global typedef for hooked functions. Passes itself as a ptr when called, and `data` additionally. + +typedef std::function HOOK_CALLBACK_FN; + +struct SCallbackFNPtr { + WP fn; + HANDLE handle = nullptr; +}; + +#define EMIT_HOOK_EVENT(name, param) \ + { \ + static auto* const PEVENTVEC = g_pHookSystem->getVecForEvent(name); \ + SCallbackInfo info; \ + g_pHookSystem->emit(PEVENTVEC, info, param); \ + } + +#define EMIT_HOOK_EVENT_CANCELLABLE(name, param) \ + { \ + static auto* const PEVENTVEC = g_pHookSystem->getVecForEvent(name); \ + SCallbackInfo info; \ + g_pHookSystem->emit(PEVENTVEC, info, param); \ + if (info.cancelled) \ + return; \ + } + +class CHookSystemManager { + public: + CHookSystemManager(); + + // returns the pointer to the function. + // losing this pointer (letting it get destroyed) + // will equal to unregistering the callback. + [[nodiscard("Losing this pointer instantly unregisters the callback")]] SP hookDynamic(const std::string& event, HOOK_CALLBACK_FN fn, + HANDLE handle = nullptr); + void unhook(SP fn); + + void emit(std::vector* const callbacks, SCallbackInfo& info, std::any data = 0); + std::vector* getVecForEvent(const std::string& event); + + bool m_bCurrentEventPlugin = false; + jmp_buf m_jbHookFaultJumpBuf; + + private: + std::unordered_map> m_mRegisteredHooks; +}; + +inline UP g_pHookSystem; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index fd7c4e72..42e2bc14 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1,13 +1,9 @@ #include "../config/ConfigValue.hpp" #include "../devices/IKeyboard.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../desktop/history/WindowHistoryTracker.hpp" -#include "../desktop/history/WorkspaceHistoryTracker.hpp" #include "../managers/SeatManager.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/ShortcutsInhibit.hpp" #include "../protocols/GlobalShortcuts.hpp" -#include "../protocols/IdleNotify.hpp" #include "../protocols/core/DataDevice.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "KeybindManager.hpp" @@ -15,23 +11,13 @@ #include "Compositor.hpp" #include "TokenManager.hpp" #include "eventLoop/EventLoopManager.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" +#include "../managers/HookSystemManager.hpp" #include "../managers/input/InputManager.hpp" -#include "../managers/animation/DesktopAnimationManager.hpp" +#include "../managers/LayoutManager.hpp" #include "../managers/EventManager.hpp" #include "../render/Renderer.hpp" #include "../hyprerror/HyprError.hpp" -#include "../config/ConfigManager.hpp" -#include "../desktop/rule/windowRule/WindowRule.hpp" -#include "../desktop/rule/Engine.hpp" -#include "../desktop/view/Group.hpp" -#include "../layout/LayoutManager.hpp" -#include "../layout/target/WindowTarget.hpp" -#include "../layout/space/Space.hpp" -#include "../layout/algorithm/Algorithm.hpp" -#include "../layout/algorithm/tiled/master/MasterAlgorithm.hpp" -#include "../layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp" -#include "../event/EventBus.hpp" #include #include @@ -40,7 +26,6 @@ #include #include -#include #include using namespace Hyprutils::String; using namespace Hyprutils::OS; @@ -59,24 +44,24 @@ using namespace Hyprutils::OS; static std::vector> getHyprlandLaunchEnv(PHLWORKSPACE pInitialWorkspace) { static auto PINITIALWSTRACKING = CConfigValue("misc:initial_workspace_tracking"); - if (!*PINITIALWSTRACKING || g_pConfigManager->m_isLaunchingExecOnce) + if (!*PINITIALWSTRACKING || g_pConfigManager->isLaunchingExecOnce) return {}; - const auto PMONITOR = Desktop::focusState()->monitor(); - if (!PMONITOR || !PMONITOR->m_activeWorkspace) + const auto PMONITOR = g_pCompositor->m_pLastMonitor; + if (!PMONITOR || !PMONITOR->activeWorkspace) return {}; std::vector> result; if (!pInitialWorkspace) { - if (PMONITOR->m_activeSpecialWorkspace) - pInitialWorkspace = PMONITOR->m_activeSpecialWorkspace; + if (PMONITOR->activeSpecialWorkspace) + pInitialWorkspace = PMONITOR->activeSpecialWorkspace; else - pInitialWorkspace = PMONITOR->m_activeWorkspace; + pInitialWorkspace = PMONITOR->activeWorkspace; } result.push_back(std::make_pair<>("HL_INITIAL_WORKSPACE_TOKEN", - g_pTokenManager->registerNewToken(Desktop::View::SInitialWorkspaceToken{{}, pInitialWorkspace->getConfigName()}, std::chrono::months(1337)))); + g_pTokenManager->registerNewToken(SInitialWorkspaceToken{{}, pInitialWorkspace->getConfigName()}, std::chrono::months(1337)))); return result; } @@ -84,159 +69,154 @@ static std::vector> getHyprlandLaunchEnv(PHL CKeybindManager::CKeybindManager() { // initialize all dispatchers - m_dispatchers["exec"] = spawn; - m_dispatchers["execr"] = spawnRaw; - m_dispatchers["killactive"] = closeActive; - m_dispatchers["forcekillactive"] = killActive; - m_dispatchers["closewindow"] = closeWindow; - m_dispatchers["killwindow"] = killWindow; - m_dispatchers["signal"] = signalActive; - m_dispatchers["signalwindow"] = signalWindow; - m_dispatchers["togglefloating"] = toggleActiveFloating; - m_dispatchers["setfloating"] = setActiveFloating; - m_dispatchers["settiled"] = setActiveTiled; - m_dispatchers["workspace"] = changeworkspace; - m_dispatchers["renameworkspace"] = renameWorkspace; - m_dispatchers["fullscreen"] = fullscreenActive; - m_dispatchers["fullscreenstate"] = fullscreenStateActive; - m_dispatchers["movetoworkspace"] = moveActiveToWorkspace; - m_dispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent; - m_dispatchers["pseudo"] = toggleActivePseudo; - m_dispatchers["movefocus"] = moveFocusTo; - m_dispatchers["movewindow"] = moveActiveTo; - m_dispatchers["swapwindow"] = swapActive; - m_dispatchers["centerwindow"] = centerWindow; - m_dispatchers["togglegroup"] = toggleGroup; - m_dispatchers["changegroupactive"] = changeGroupActive; - m_dispatchers["movegroupwindow"] = moveGroupWindow; - m_dispatchers["focusmonitor"] = focusMonitor; - m_dispatchers["movecursortocorner"] = moveCursorToCorner; - m_dispatchers["movecursor"] = moveCursor; - m_dispatchers["workspaceopt"] = workspaceOpt; - m_dispatchers["exit"] = exitHyprland; - m_dispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor; - m_dispatchers["focusworkspaceoncurrentmonitor"] = focusWorkspaceOnCurrentMonitor; - m_dispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor; - m_dispatchers["togglespecialworkspace"] = toggleSpecialWorkspace; - m_dispatchers["forcerendererreload"] = forceRendererReload; - m_dispatchers["resizeactive"] = resizeActive; - m_dispatchers["moveactive"] = moveActive; - m_dispatchers["cyclenext"] = circleNext; - m_dispatchers["focuswindowbyclass"] = focusWindow; - m_dispatchers["focuswindow"] = focusWindow; - m_dispatchers["tagwindow"] = tagWindow; - m_dispatchers["toggleswallow"] = toggleSwallow; - m_dispatchers["submap"] = setSubmap; - m_dispatchers["pass"] = pass; - m_dispatchers["sendshortcut"] = sendshortcut; - m_dispatchers["sendkeystate"] = sendkeystate; - m_dispatchers["layoutmsg"] = layoutmsg; - m_dispatchers["dpms"] = dpms; - m_dispatchers["movewindowpixel"] = moveWindow; - m_dispatchers["resizewindowpixel"] = resizeWindow; - m_dispatchers["swapnext"] = swapnext; - m_dispatchers["swapactiveworkspaces"] = swapActiveWorkspaces; - m_dispatchers["pin"] = pinActive; - m_dispatchers["mouse"] = mouse; - m_dispatchers["bringactivetotop"] = bringActiveToTop; - m_dispatchers["alterzorder"] = alterZOrder; - m_dispatchers["focusurgentorlast"] = focusUrgentOrLast; - m_dispatchers["focuscurrentorlast"] = focusCurrentOrLast; - m_dispatchers["lockgroups"] = lockGroups; - m_dispatchers["lockactivegroup"] = lockActiveGroup; - m_dispatchers["moveintogroup"] = moveIntoGroup; - m_dispatchers["moveoutofgroup"] = moveOutOfGroup; - m_dispatchers["movewindoworgroup"] = moveWindowOrGroup; - m_dispatchers["setignoregrouplock"] = setIgnoreGroupLock; - m_dispatchers["denywindowfromgroup"] = denyWindowFromGroup; - m_dispatchers["event"] = event; - m_dispatchers["global"] = global; - m_dispatchers["setprop"] = setProp; - m_dispatchers["forceidle"] = forceIdle; + m_mDispatchers["exec"] = spawn; + m_mDispatchers["execr"] = spawnRaw; + m_mDispatchers["killactive"] = closeActive; + m_mDispatchers["forcekillactive"] = killActive; + m_mDispatchers["closewindow"] = closeWindow; + m_mDispatchers["killwindow"] = killWindow; + m_mDispatchers["signal"] = signalActive; + m_mDispatchers["signalwindow"] = signalWindow; + m_mDispatchers["togglefloating"] = toggleActiveFloating; + m_mDispatchers["setfloating"] = setActiveFloating; + m_mDispatchers["settiled"] = setActiveTiled; + m_mDispatchers["workspace"] = changeworkspace; + m_mDispatchers["renameworkspace"] = renameWorkspace; + m_mDispatchers["fullscreen"] = fullscreenActive; + m_mDispatchers["fullscreenstate"] = fullscreenStateActive; + m_mDispatchers["movetoworkspace"] = moveActiveToWorkspace; + m_mDispatchers["movetoworkspacesilent"] = moveActiveToWorkspaceSilent; + m_mDispatchers["pseudo"] = toggleActivePseudo; + m_mDispatchers["movefocus"] = moveFocusTo; + m_mDispatchers["movewindow"] = moveActiveTo; + m_mDispatchers["swapwindow"] = swapActive; + m_mDispatchers["centerwindow"] = centerWindow; + m_mDispatchers["togglegroup"] = toggleGroup; + m_mDispatchers["changegroupactive"] = changeGroupActive; + m_mDispatchers["movegroupwindow"] = moveGroupWindow; + m_mDispatchers["togglesplit"] = toggleSplit; + m_mDispatchers["swapsplit"] = swapSplit; + m_mDispatchers["splitratio"] = alterSplitRatio; + m_mDispatchers["focusmonitor"] = focusMonitor; + m_mDispatchers["movecursortocorner"] = moveCursorToCorner; + m_mDispatchers["movecursor"] = moveCursor; + m_mDispatchers["workspaceopt"] = workspaceOpt; + m_mDispatchers["exit"] = exitHyprland; + m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor; + m_mDispatchers["focusworkspaceoncurrentmonitor"] = focusWorkspaceOnCurrentMonitor; + m_mDispatchers["moveworkspacetomonitor"] = moveWorkspaceToMonitor; + m_mDispatchers["togglespecialworkspace"] = toggleSpecialWorkspace; + m_mDispatchers["forcerendererreload"] = forceRendererReload; + m_mDispatchers["resizeactive"] = resizeActive; + m_mDispatchers["moveactive"] = moveActive; + m_mDispatchers["cyclenext"] = circleNext; + m_mDispatchers["focuswindowbyclass"] = focusWindow; + m_mDispatchers["focuswindow"] = focusWindow; + m_mDispatchers["tagwindow"] = tagWindow; + m_mDispatchers["toggleswallow"] = toggleSwallow; + m_mDispatchers["submap"] = setSubmap; + m_mDispatchers["pass"] = pass; + m_mDispatchers["sendshortcut"] = sendshortcut; + m_mDispatchers["layoutmsg"] = layoutmsg; + m_mDispatchers["dpms"] = dpms; + m_mDispatchers["movewindowpixel"] = moveWindow; + m_mDispatchers["resizewindowpixel"] = resizeWindow; + m_mDispatchers["swapnext"] = swapnext; + m_mDispatchers["swapactiveworkspaces"] = swapActiveWorkspaces; + m_mDispatchers["pin"] = pinActive; + m_mDispatchers["mouse"] = mouse; + m_mDispatchers["bringactivetotop"] = bringActiveToTop; + m_mDispatchers["alterzorder"] = alterZOrder; + m_mDispatchers["focusurgentorlast"] = focusUrgentOrLast; + m_mDispatchers["focuscurrentorlast"] = focusCurrentOrLast; + m_mDispatchers["lockgroups"] = lockGroups; + m_mDispatchers["lockactivegroup"] = lockActiveGroup; + m_mDispatchers["moveintogroup"] = moveIntoGroup; + m_mDispatchers["moveoutofgroup"] = moveOutOfGroup; + m_mDispatchers["movewindoworgroup"] = moveWindowOrGroup; + m_mDispatchers["setignoregrouplock"] = setIgnoreGroupLock; + m_mDispatchers["denywindowfromgroup"] = denyWindowFromGroup; + m_mDispatchers["event"] = event; + m_mDispatchers["global"] = global; + m_mDispatchers["setprop"] = setProp; - m_scrollTimer.reset(); + m_tScrollTimer.reset(); - m_longPressTimer = makeShared( + m_pLongPressTimer = makeShared( std::nullopt, [this](SP self, void* data) { - if (!m_lastLongPressKeybind || g_pSeatManager->m_keyboard.expired()) + if (!m_pLastLongPressKeybind || g_pSeatManager->keyboard.expired()) return; - const auto PACTIVEKEEB = g_pSeatManager->m_keyboard.lock(); - if (!PACTIVEKEEB->m_allowBinds) - return; + const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(m_pLastLongPressKeybind->handler); - const auto DISPATCHER = g_pKeybindManager->m_dispatchers.find(m_lastLongPressKeybind->handler); - - Log::logger->log(Log::DEBUG, "Long press timeout passed, calling dispatcher."); - DISPATCHER->second(m_lastLongPressKeybind->arg); + Debug::log(LOG, "Long press timeout passed, calling dispatcher."); + DISPATCHER->second(m_pLastLongPressKeybind->arg); }, nullptr); - m_repeatKeyTimer = makeShared( + m_pRepeatKeyTimer = makeShared( std::nullopt, [this](SP self, void* data) { - if (m_activeKeybinds.empty() || g_pSeatManager->m_keyboard.expired()) + if (m_vActiveKeybinds.size() == 0 || g_pSeatManager->keyboard.expired()) return; - const auto PACTIVEKEEB = g_pSeatManager->m_keyboard.lock(); - if (!PACTIVEKEEB->m_allowBinds) - return; + for (const auto& k : m_vActiveKeybinds) { + const auto DISPATCHER = g_pKeybindManager->m_mDispatchers.find(k->handler); - for (const auto& k : m_activeKeybinds) { - const auto DISPATCHER = g_pKeybindManager->m_dispatchers.find(k->handler); - - Log::logger->log(Log::DEBUG, "Keybind repeat triggered, calling dispatcher."); + Debug::log(LOG, "Keybind repeat triggered, calling dispatcher."); DISPATCHER->second(k->arg); } - self->updateTimeout(std::chrono::milliseconds(1000 / m_repeatKeyRate)); + const auto PACTIVEKEEB = g_pSeatManager->keyboard.lock(); + self->updateTimeout(std::chrono::milliseconds(1000 / PACTIVEKEEB->repeatRate)); }, nullptr); // null in --verify-config mode if (g_pEventLoopManager) { - g_pEventLoopManager->addTimer(m_longPressTimer); - g_pEventLoopManager->addTimer(m_repeatKeyTimer); + g_pEventLoopManager->addTimer(m_pLongPressTimer); + g_pEventLoopManager->addTimer(m_pRepeatKeyTimer); } - static auto P = Event::bus()->m_events.config.reloaded.listen([this] { - m_activeKeybinds.clear(); - m_lastLongPressKeybind.reset(); - m_pressedSpecialBinds.clear(); + static auto P = g_pHookSystem->hookDynamic("configReloaded", [this](void* hk, SCallbackInfo& info, std::any param) { + // clear cuz realloc'd + m_vActiveKeybinds.clear(); + m_pLastLongPressKeybind.reset(); + m_vPressedSpecialBinds.clear(); }); } CKeybindManager::~CKeybindManager() { - if (m_xkbTranslationState) - xkb_state_unref(m_xkbTranslationState); - if (m_longPressTimer && g_pEventLoopManager) { - g_pEventLoopManager->removeTimer(m_longPressTimer); - m_longPressTimer.reset(); + if (m_pXKBTranslationState) + xkb_state_unref(m_pXKBTranslationState); + if (m_pLongPressTimer && g_pEventLoopManager) { + g_pEventLoopManager->removeTimer(m_pLongPressTimer); + m_pLongPressTimer.reset(); } - if (m_repeatKeyTimer && g_pEventLoopManager) { - g_pEventLoopManager->removeTimer(m_repeatKeyTimer); - m_repeatKeyTimer.reset(); + if (m_pRepeatKeyTimer && g_pEventLoopManager) { + g_pEventLoopManager->removeTimer(m_pRepeatKeyTimer); + m_pRepeatKeyTimer.reset(); } } void CKeybindManager::addKeybind(SKeybind kb) { - m_keybinds.emplace_back(makeShared(kb)); + m_vKeybinds.emplace_back(makeShared(kb)); - m_activeKeybinds.clear(); - m_lastLongPressKeybind.reset(); + m_vActiveKeybinds.clear(); + m_pLastLongPressKeybind.reset(); } void CKeybindManager::removeKeybind(uint32_t mod, const SParsedKey& key) { - std::erase_if(m_keybinds, [&mod, &key](const auto& el) { return el->modmask == mod && el->key == key.key && el->keycode == key.keycode && el->catchAll == key.catchAll; }); + std::erase_if(m_vKeybinds, [&mod, &key](const auto& el) { return el->modmask == mod && el->key == key.key && el->keycode == key.keycode && el->catchAll == key.catchAll; }); - m_activeKeybinds.clear(); - m_lastLongPressKeybind.reset(); + m_vActiveKeybinds.clear(); + m_pLastLongPressKeybind.reset(); } uint32_t CKeybindManager::stringToModMask(std::string mods) { uint32_t modMask = 0; - std::ranges::transform(mods, mods.begin(), ::toupper); + std::transform(mods.begin(), mods.end(), mods.begin(), ::toupper); if (mods.contains("SHIFT")) modMask |= HL_MODIFIER_SHIFT; if (mods.contains("CAPS")) @@ -277,10 +257,10 @@ uint32_t CKeybindManager::keycodeToModifier(xkb_keycode_t keycode) { } void CKeybindManager::updateXKBTranslationState() { - if (m_xkbTranslationState) { - xkb_state_unref(m_xkbTranslationState); + if (m_pXKBTranslationState) { + xkb_state_unref(m_pXKBTranslationState); - m_xkbTranslationState = nullptr; + m_pXKBTranslationState = nullptr; } static auto PFILEPATH = CConfigValue("input:kb_file"); @@ -299,10 +279,10 @@ void CKeybindManager::updateXKBTranslationState() { xkb_rule_names rules = {.rules = RULES.c_str(), .model = MODEL.c_str(), .layout = LAYOUT.c_str(), .variant = VARIANT.c_str(), .options = OPTIONS.c_str()}; const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - FILE* const KEYMAPFILE = FILEPATH.empty() ? nullptr : fopen(absolutePath(FILEPATH, g_pConfigManager->m_configCurrentPath).c_str(), "r"); + FILE* const KEYMAPFILE = FILEPATH == "" ? nullptr : fopen(absolutePath(FILEPATH, g_pConfigManager->configCurrentPath).c_str(), "r"); - auto PKEYMAP = KEYMAPFILE ? xkb_keymap_new_from_file(PCONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS) : - xkb_keymap_new_from_names2(PCONTEXT, &rules, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + auto PKEYMAP = KEYMAPFILE ? xkb_keymap_new_from_file(PCONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS) : + xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (KEYMAPFILE) fclose(KEYMAPFILE); @@ -311,23 +291,23 @@ void CKeybindManager::updateXKBTranslationState() { ", layout: " + LAYOUT + " )", CHyprColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0)); - Log::logger->log(Log::ERR, "[XKBTranslationState] Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded.", rules.layout, - rules.variant, rules.rules, rules.model, rules.options); + Debug::log(ERR, "[XKBTranslationState] Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded.", rules.layout, rules.variant, + rules.rules, rules.model, rules.options); memset(&rules, 0, sizeof(rules)); - PKEYMAP = xkb_keymap_new_from_names2(PCONTEXT, &rules, XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + PKEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); } xkb_context_unref(PCONTEXT); - m_xkbTranslationState = xkb_state_new(PKEYMAP); + m_pXKBTranslationState = xkb_state_new(PKEYMAP); xkb_keymap_unref(PKEYMAP); } bool CKeybindManager::ensureMouseBindState() { - if (!g_layoutManager->dragController()->target()) + if (!g_pInputManager->currentlyDraggedWindow) return false; - if (g_layoutManager->dragController()->target()) { + if (!g_pInputManager->currentlyDraggedWindow.expired()) { changeMouseBindMode(MBIND_INVALID); return true; } @@ -341,57 +321,58 @@ static void updateRelativeCursorCoords() { if (*PNOWARPS) return; - if (Desktop::focusState()->window()) - Desktop::focusState()->window()->m_relativeCursorCoordsOnLastWarp = g_pInputManager->getMouseCoordsInternal() - Desktop::focusState()->window()->m_position; + if (g_pCompositor->m_pLastWindow) + g_pCompositor->m_pLastWindow->m_vRelativeCursorCoordsOnLastWarp = g_pInputManager->getMouseCoordsInternal() - g_pCompositor->m_pLastWindow->m_vPosition; } bool CKeybindManager::tryMoveFocusToMonitor(PHLMONITOR monitor) { if (!monitor) return false; - const auto LASTMONITOR = Desktop::focusState()->monitor(); + const auto LASTMONITOR = g_pCompositor->m_pLastMonitor.lock(); if (!LASTMONITOR) return false; if (LASTMONITOR == monitor) { - Log::logger->log(Log::DEBUG, "Tried to move to active monitor"); + Debug::log(LOG, "Tried to move to active monitor"); return false; } static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); static auto PNOWARPS = CConfigValue("cursor:no_warps"); - const auto PWORKSPACE = Desktop::focusState()->monitor()->m_activeWorkspace; - const auto PNEWMAINWORKSPACE = monitor->m_activeWorkspace; + const auto PWORKSPACE = g_pCompositor->m_pLastMonitor->activeWorkspace; + const auto PNEWMAINWORKSPACE = monitor->activeWorkspace; g_pInputManager->unconstrainMouse(); + PNEWMAINWORKSPACE->rememberPrevWorkspace(PWORKSPACE); - const auto PNEWWORKSPACE = monitor->m_activeSpecialWorkspace ? monitor->m_activeSpecialWorkspace : PNEWMAINWORKSPACE; + const auto PNEWWORKSPACE = monitor->activeSpecialWorkspace ? monitor->activeSpecialWorkspace : PNEWMAINWORKSPACE; const auto PNEWWINDOW = PNEWWORKSPACE->getLastFocusedWindow(); if (PNEWWINDOW) { updateRelativeCursorCoords(); - Desktop::focusState()->fullWindowFocus(PNEWWINDOW, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->focusWindow(PNEWWINDOW); PNEWWINDOW->warpCursor(); if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) { - g_pInputManager->m_forcedFocus = PNEWWINDOW; + g_pInputManager->m_pForcedFocus = PNEWWINDOW; g_pInputManager->simulateMouseMovement(); - g_pInputManager->m_forcedFocus.reset(); + g_pInputManager->m_pForcedFocus.reset(); } } else { - Desktop::focusState()->rawWindowFocus(nullptr, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->focusWindow(nullptr); g_pCompositor->warpCursorTo(monitor->middle()); } - Desktop::focusState()->rawMonitorFocus(monitor); + g_pCompositor->setActiveMonitor(monitor); return true; } -void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool forceFSCycle) { +void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool preserveFocusHistory) { static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); static auto PNOWARPS = CConfigValue("cursor:no_warps"); - const auto PLASTWINDOW = Desktop::focusState()->window(); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); if (PWINDOWTOCHANGETO == PLASTWINDOW || !PWINDOWTOCHANGETO) return; @@ -399,43 +380,53 @@ void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool forceFSCy // remove constraints g_pInputManager->unconstrainMouse(); - if (PLASTWINDOW && PLASTWINDOW->m_workspace == PWINDOWTOCHANGETO->m_workspace && PLASTWINDOW->isFullscreen()) - Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle); - else { + if (PLASTWINDOW && PLASTWINDOW->m_pWorkspace == PWINDOWTOCHANGETO->m_pWorkspace && PLASTWINDOW->isFullscreen()) { + const auto PWORKSPACE = PLASTWINDOW->m_pWorkspace; + const auto MODE = PWORKSPACE->m_efFullscreenMode; + + if (!PWINDOWTOCHANGETO->m_bPinned) + g_pCompositor->setWindowFullscreenInternal(PLASTWINDOW, FSMODE_NONE); + + g_pCompositor->focusWindow(PWINDOWTOCHANGETO, nullptr, preserveFocusHistory); + + if (!PWINDOWTOCHANGETO->m_bPinned) + g_pCompositor->setWindowFullscreenInternal(PWINDOWTOCHANGETO, MODE); + + // warp the position + size animation, otherwise it looks weird. + PWINDOWTOCHANGETO->m_vRealPosition->warp(); + PWINDOWTOCHANGETO->m_vRealSize->warp(); + } else { updateRelativeCursorCoords(); - Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle); + g_pCompositor->focusWindow(PWINDOWTOCHANGETO, nullptr, preserveFocusHistory); PWINDOWTOCHANGETO->warpCursor(); // Move mouse focus to the new window if required by current follow_mouse and warp modes if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) { - g_pInputManager->m_forcedFocus = PWINDOWTOCHANGETO; + g_pInputManager->m_pForcedFocus = PWINDOWTOCHANGETO; g_pInputManager->simulateMouseMovement(); - g_pInputManager->m_forcedFocus.reset(); + g_pInputManager->m_pForcedFocus.reset(); } - if (PLASTWINDOW && PLASTWINDOW->m_monitor != PWINDOWTOCHANGETO->m_monitor) { + if (PLASTWINDOW && PLASTWINDOW->m_pMonitor != PWINDOWTOCHANGETO->m_pMonitor) { // event - const auto PNEWMON = PWINDOWTOCHANGETO->m_monitor.lock(); + const auto PNEWMON = PWINDOWTOCHANGETO->m_pMonitor.lock(); - Desktop::focusState()->rawMonitorFocus(PNEWMON); + g_pCompositor->setActiveMonitor(PNEWMON); } } }; bool CKeybindManager::onKeyEvent(std::any event, SP pKeyboard) { - if (!g_pCompositor->m_sessionActive || g_pCompositor->m_unsafeState) { - m_pressedKeys.clear(); + if (!g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) { + m_dPressedKeys.clear(); return true; } - if (!pKeyboard->m_allowBinds) - return true; - - if (!m_xkbTranslationState) { - Log::logger->log(Log::ERR, "BUG THIS: m_pXKBTranslationState nullptr!"); + if (!m_pXKBTranslationState) { + Debug::log(ERR, "BUG THIS: m_pXKBTranslationState nullptr!"); updateXKBTranslationState(); - if (!m_xkbTranslationState) + if (!m_pXKBTranslationState) return true; } @@ -443,8 +434,8 @@ bool CKeybindManager::onKeyEvent(std::any event, SP pKeyboard) { const auto KEYCODE = e.keycode + 8; // Because to xkbcommon it's +8 from libinput - const xkb_keysym_t keysym = xkb_state_key_get_one_sym(pKeyboard->m_resolveBindsBySym ? pKeyboard->m_xkbSymState : m_xkbTranslationState, KEYCODE); - const xkb_keysym_t internalKeysym = xkb_state_key_get_one_sym(pKeyboard->m_xkbState, KEYCODE); + const xkb_keysym_t keysym = xkb_state_key_get_one_sym(pKeyboard->resolveBindsBySym ? pKeyboard->xkbSymState : m_pXKBTranslationState, KEYCODE); + const xkb_keysym_t internalKeysym = xkb_state_key_get_one_sym(pKeyboard->xkbState, KEYCODE); if (keysym == XKB_KEY_Escape || internalKeysym == XKB_KEY_Escape) PROTO::data->abortDndIfPresent(); @@ -454,11 +445,11 @@ bool CKeybindManager::onKeyEvent(std::any event, SP pKeyboard) { if (handleInternalKeybinds(internalKeysym)) return false; - const auto MODS = g_pInputManager->getModsFromAllKBs(); + const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); - m_timeLastMs = e.timeMs; - m_lastCode = KEYCODE; - m_lastMouseCode = 0; + m_uTimeLastMs = e.timeMs; + m_uLastCode = KEYCODE; + m_uLastMouseCode = 0; bool mouseBindWasActive = ensureMouseBindState(); @@ -467,42 +458,41 @@ bool CKeybindManager::onKeyEvent(std::any event, SP pKeyboard) { .keycode = KEYCODE, .modmaskAtPressTime = MODS, .sent = true, - .submapAtPress = m_currentSelectedSubmap, - .mousePosAtPress = g_pInputManager->getMouseCoordsInternal(), + .submapAtPress = m_szCurrentSelectedSubmap, }; - m_activeKeybinds.clear(); + m_vActiveKeybinds.clear(); - m_lastLongPressKeybind.reset(); + m_pLastLongPressKeybind.reset(); bool suppressEvent = false; if (e.state == WL_KEYBOARD_KEY_STATE_PRESSED) { - m_pressedKeys.push_back(KEY); + m_dPressedKeys.push_back(KEY); - suppressEvent = !handleKeybinds(MODS, KEY, true, pKeyboard).passEvent; + suppressEvent = !handleKeybinds(MODS, KEY, true).passEvent; if (suppressEvent) shadowKeybinds(keysym, KEYCODE); - m_pressedKeys.back().sent = !suppressEvent; + m_dPressedKeys.back().sent = !suppressEvent; } else { // key release bool foundInPressedKeys = false; - for (auto it = m_pressedKeys.begin(); it != m_pressedKeys.end();) { + for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) { if (it->keycode == KEYCODE) { - handleKeybinds(MODS, *it, false, pKeyboard); + handleKeybinds(MODS, *it, false); foundInPressedKeys = true; suppressEvent = !it->sent; - it = m_pressedKeys.erase(it); + it = m_dPressedKeys.erase(it); } else { ++it; } } if (!foundInPressedKeys) { - Log::logger->log(Log::ERR, "BUG THIS: key not found in m_dPressedKeys"); + Debug::log(ERR, "BUG THIS: key not found in m_dPressedKeys"); // fallback with wrong `KEY.modmaskAtPressTime`, this can be buggy - suppressEvent = !handleKeybinds(MODS, KEY, false, pKeyboard).passEvent; + suppressEvent = !handleKeybinds(MODS, KEY, false).passEvent; } shadowKeybinds(); @@ -512,28 +502,30 @@ bool CKeybindManager::onKeyEvent(std::any event, SP pKeyboard) { } bool CKeybindManager::onAxisEvent(const IPointer::SAxisEvent& e) { - const auto MODS = g_pInputManager->getModsFromAllKBs(); + const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); static auto PDELAY = CConfigValue("binds:scroll_event_delay"); - if (m_scrollTimer.getMillis() < *PDELAY) + if (m_tScrollTimer.getMillis() < *PDELAY) { + m_tScrollTimer.reset(); return true; // timer hasn't passed yet! + } - m_scrollTimer.reset(); + m_tScrollTimer.reset(); - m_activeKeybinds.clear(); + m_vActiveKeybinds.clear(); bool found = false; if (e.source == WL_POINTER_AXIS_SOURCE_WHEEL && e.axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { if (e.delta < 0) - found = !handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_down"}, true, nullptr).passEvent; + found = !handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_down"}, true).passEvent; else - found = !handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_up"}, true, nullptr).passEvent; + found = !handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_up"}, true).passEvent; } else if (e.source == WL_POINTER_AXIS_SOURCE_WHEEL && e.axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { if (e.delta < 0) - found = !handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_left"}, true, nullptr).passEvent; + found = !handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_left"}, true).passEvent; else - found = !handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_right"}, true, nullptr).passEvent; + found = !handleKeybinds(MODS, SPressedKeyWithMods{.keyName = "mouse_right"}, true).passEvent; } if (found) @@ -543,13 +535,13 @@ bool CKeybindManager::onAxisEvent(const IPointer::SAxisEvent& e) { } bool CKeybindManager::onMouseEvent(const IPointer::SButtonEvent& e) { - const auto MODS = g_pInputManager->getModsFromAllKBs(); + const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); bool suppressEvent = false; - m_lastMouseCode = e.button; - m_lastCode = 0; - m_timeLastMs = e.timeMs; + m_uLastMouseCode = e.button; + m_uLastCode = 0; + m_uTimeLastMs = e.timeMs; bool mouseBindWasActive = ensureMouseBindState(); @@ -558,36 +550,35 @@ bool CKeybindManager::onMouseEvent(const IPointer::SButtonEvent& e) { const auto KEY = SPressedKeyWithMods{ .keyName = KEY_NAME, .modmaskAtPressTime = MODS, - .mousePosAtPress = g_pInputManager->getMouseCoordsInternal(), }; - m_activeKeybinds.clear(); + m_vActiveKeybinds.clear(); if (e.state == WL_POINTER_BUTTON_STATE_PRESSED) { - m_pressedKeys.push_back(KEY); + m_dPressedKeys.push_back(KEY); - suppressEvent = !handleKeybinds(MODS, KEY, true, nullptr).passEvent; + suppressEvent = !handleKeybinds(MODS, KEY, true).passEvent; if (suppressEvent) shadowKeybinds(); - m_pressedKeys.back().sent = !suppressEvent; + m_dPressedKeys.back().sent = !suppressEvent; } else { bool foundInPressedKeys = false; - for (auto it = m_pressedKeys.begin(); it != m_pressedKeys.end();) { + for (auto it = m_dPressedKeys.begin(); it != m_dPressedKeys.end();) { if (it->keyName == KEY_NAME) { - suppressEvent = !handleKeybinds(MODS, *it, false, nullptr).passEvent; + suppressEvent = !handleKeybinds(MODS, *it, false).passEvent; foundInPressedKeys = true; suppressEvent = !it->sent; - it = m_pressedKeys.erase(it); + it = m_dPressedKeys.erase(it); } else { ++it; } } if (!foundInPressedKeys) { - Log::logger->log(Log::ERR, "BUG THIS: key not found in m_dPressedKeys (2)"); + Debug::log(ERR, "BUG THIS: key not found in m_dPressedKeys (2)"); // fallback with wrong `KEY.modmaskAtPressTime`, this can be buggy - suppressEvent = !handleKeybinds(MODS, KEY, false, nullptr).passEvent; + suppressEvent = !handleKeybinds(MODS, KEY, false).passEvent; } shadowKeybinds(); @@ -601,15 +592,15 @@ void CKeybindManager::resizeWithBorder(const IPointer::SButtonEvent& e) { } void CKeybindManager::onSwitchEvent(const std::string& switchName) { - handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:" + switchName}, true, nullptr); + handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:" + switchName}, true); } void CKeybindManager::onSwitchOnEvent(const std::string& switchName) { - handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:on:" + switchName}, true, nullptr); + handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:on:" + switchName}, true); } void CKeybindManager::onSwitchOffEvent(const std::string& switchName) { - handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:off:" + switchName}, true, nullptr); + handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:off:" + switchName}, true); } eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::set keybindKeysyms, const std::set pressedKeysyms) { @@ -619,57 +610,52 @@ eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::set k std::set boundKeysNotPressed; std::set pressedKeysNotBound; - std::ranges::set_difference(keybindKeysyms, pressedKeysyms, std::inserter(boundKeysNotPressed, boundKeysNotPressed.begin())); - std::ranges::set_difference(pressedKeysyms, keybindKeysyms, std::inserter(pressedKeysNotBound, pressedKeysNotBound.begin())); + std::set_difference(keybindKeysyms.begin(), keybindKeysyms.end(), pressedKeysyms.begin(), pressedKeysyms.end(), + std::inserter(boundKeysNotPressed, boundKeysNotPressed.begin())); + std::set_difference(pressedKeysyms.begin(), pressedKeysyms.end(), keybindKeysyms.begin(), keybindKeysyms.end(), + std::inserter(pressedKeysNotBound, pressedKeysNotBound.begin())); if (boundKeysNotPressed.empty() && pressedKeysNotBound.empty()) return MK_FULL_MATCH; - if (!boundKeysNotPressed.empty() && pressedKeysNotBound.empty()) + if (boundKeysNotPressed.size() && pressedKeysNotBound.empty()) return MK_PARTIAL_MATCH; return MK_NO_MATCH; } eMultiKeyCase CKeybindManager::mkBindMatches(const SP keybind) { - if (mkKeysymSetMatches(keybind->sMkMods, m_mkMods) != MK_FULL_MATCH) + if (mkKeysymSetMatches(keybind->sMkMods, m_sMkMods) != MK_FULL_MATCH) return MK_NO_MATCH; - return mkKeysymSetMatches(keybind->sMkKeys, m_mkKeys); + return mkKeysymSetMatches(keybind->sMkKeys, m_sMkKeys); } -SSubmap CKeybindManager::getCurrentSubmap() { - return m_currentSelectedSubmap; +std::string CKeybindManager::getCurrentSubmap() { + return m_szCurrentSelectedSubmap; } -SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed, SP keyboard) { +SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) { static auto PDISABLEINHIBIT = CConfigValue("binds:disable_keybind_grabbing"); - static auto PDRAGTHRESHOLD = CConfigValue("binds:drag_threshold"); - - bool found = false; + bool found = false; SDispatchResult res; - // Skip keysym tracking for events with no keysym (e.g., scroll wheel events). - // Scroll events have keysym=0 and are always "pressed" (never released), - // so without this check, 0 gets inserted into m_mkKeys and never removed, - // breaking multi-key binds (binds flag 's'). See issue #8699. - if (key.keysym != 0) { - if (pressed) { - if (keycodeToModifier(key.keycode)) - m_mkMods.insert(key.keysym); - else - m_mkKeys.insert(key.keysym); - } else { - if (keycodeToModifier(key.keycode)) - m_mkMods.erase(key.keysym); - else - m_mkKeys.erase(key.keysym); - } + if (pressed) { + if (keycodeToModifier(key.keycode)) + m_sMkMods.insert(key.keysym); + else + m_sMkKeys.insert(key.keysym); + } else { + if (keycodeToModifier(key.keycode)) + m_sMkMods.erase(key.keysym); + else + m_sMkKeys.erase(key.keysym); } - for (auto& k : m_keybinds) { + for (auto& k : m_vKeybinds) { const bool SPECIALDISPATCHER = k->handler == "global" || k->handler == "pass" || k->handler == "sendshortcut" || k->handler == "mouse"; - const bool SPECIALTRIGGERED = std::ranges::find_if(m_pressedSpecialBinds, [&](const auto& other) { return other == k; }) != m_pressedSpecialBinds.end(); + const bool SPECIALTRIGGERED = + std::find_if(m_vPressedSpecialBinds.begin(), m_vPressedSpecialBinds.end(), [&](const auto& other) { return other == k; }) != m_vPressedSpecialBinds.end(); const bool IGNORECONDITIONS = SPECIALDISPATCHER && !pressed && SPECIALTRIGGERED; // ignore mods. Pass, global dispatchers should be released immediately once the key is released. @@ -679,7 +665,7 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP if (!k->locked && g_pSessionLockManager->isSessionLocked()) continue; - if (!IGNORECONDITIONS && ((modmask != k->modmask && !k->ignoreMods) || (k->submap != m_currentSelectedSubmap && !k->submapUniversal) || k->shadowed)) + if (!IGNORECONDITIONS && ((modmask != k->modmask && !k->ignoreMods) || k->submap != m_szCurrentSelectedSubmap || k->shadowed)) continue; if (k->multiKey) { @@ -695,7 +681,7 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP if (key.keycode != k->keycode) continue; } else if (k->catchAll) { - if (found || key.submapAtPress != m_currentSelectedSubmap) + if (found || key.submapAtPress != m_szCurrentSelectedSubmap) continue; } else { // in this case, we only have the keysym to go off of for this keybind, and it's invalid @@ -749,40 +735,32 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP found = true; // suppress the event continue; } - - // Require mouse to stay inside drag_threshold for clicks, outside for drags - // Check if either a mouse bind has triggered or currently over the threshold (maybe there is no mouse bind on the same key) - const auto THRESHOLDREACHED = key.mousePosAtPress.distanceSq(g_pInputManager->getMouseCoordsInternal()) > std::pow(*PDRAGTHRESHOLD, 2); - if (k->click && (g_layoutManager->dragController()->dragThresholdReached() || THRESHOLDREACHED)) - continue; - else if (k->drag && !g_layoutManager->dragController()->dragThresholdReached() && !THRESHOLDREACHED) - continue; } - if (pressed && k->longPress) { - const auto PACTIVEKEEB = g_pSeatManager->m_keyboard.lock(); + if (k->longPress) { + const auto PACTIVEKEEB = g_pSeatManager->keyboard.lock(); - m_longPressTimer->updateTimeout(std::chrono::milliseconds(PACTIVEKEEB->m_repeatDelay)); - m_lastLongPressKeybind = k; + m_pLongPressTimer->updateTimeout(std::chrono::milliseconds(PACTIVEKEEB->repeatDelay)); + m_pLastLongPressKeybind = k; continue; } - const auto DISPATCHER = m_dispatchers.find(k->mouse ? "mouse" : k->handler); + const auto DISPATCHER = m_mDispatchers.find(k->mouse ? "mouse" : k->handler); if (SPECIALTRIGGERED && !pressed) - std::erase_if(m_pressedSpecialBinds, [&](const auto& other) { return other == k; }); + std::erase_if(m_vPressedSpecialBinds, [&](const auto& other) { return other == k; }); else if (SPECIALDISPATCHER && pressed) - m_pressedSpecialBinds.emplace_back(k); + m_vPressedSpecialBinds.emplace_back(k); // Should never happen, as we check in the ConfigManager, but oh well - if (DISPATCHER == m_dispatchers.end()) { - Log::logger->log(Log::ERR, "Invalid handler in a keybind! (handler {} does not exist)", k->handler); + if (DISPATCHER == m_mDispatchers.end()) { + Debug::log(ERR, "Invalid handler in a keybind! (handler {} does not exist)", k->handler); } else { // call the dispatcher - Log::logger->log(Log::DEBUG, "Keybind triggered, calling dispatcher ({}, {}, {}, {})", modmask, key.keyName, key.keysym, DISPATCHER->first); + Debug::log(LOG, "Keybind triggered, calling dispatcher ({}, {}, {}, {})", modmask, key.keyName, key.keysym, DISPATCHER->first); - m_passPressed = sc(pressed); + m_iPassPressed = (int)pressed; // if the dispatchers says to pass event then we will if (k->handler == "mouse") @@ -790,35 +768,30 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP else res = DISPATCHER->second(k->arg); - m_passPressed = -1; + m_iPassPressed = -1; if (k->handler == "submap") { found = true; // don't process keybinds on submap change. break; } - if (k->handler != "submap" && !k->submap.reset.empty()) - setSubmap(k->submap.reset); } - if (pressed && k->repeat) { - const auto KEEB = keyboard ? keyboard : g_pSeatManager->m_keyboard.lock(); - m_repeatKeyRate = KEEB->m_repeatRate; + if (k->repeat) { + const auto PACTIVEKEEB = g_pSeatManager->keyboard.lock(); - m_activeKeybinds.emplace_back(k); - m_repeatKeyTimer->updateTimeout(std::chrono::milliseconds(KEEB->m_repeatDelay)); + m_vActiveKeybinds.emplace_back(k); + m_pRepeatKeyTimer->updateTimeout(std::chrono::milliseconds(PACTIVEKEEB->repeatDelay)); } if (!k->nonConsuming) found = true; } - g_layoutManager->dragController()->resetDragThresholdReached(); - // if keybind wasn't found (or dispatcher said to) then pass event res.passEvent |= !found; if (!found && !*PDISABLEINHIBIT && PROTO::shortcutsInhibit->isInhibited()) { - Log::logger->log(Log::DEBUG, "Keybind handling is disabled due to an inhibitor"); + Debug::log(LOG, "Keybind handling is disabled due to an inhibitor"); res.success = false; if (res.error.empty()) @@ -831,7 +804,7 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint32_t doesntHaveCode) { // shadow disables keybinds after one has been triggered - for (auto& k : m_keybinds) { + for (auto& k : m_vKeybinds) { bool shadow = false; @@ -844,7 +817,7 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint3 const auto KBKEY = xkb_keysym_from_name(k->key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY); - for (auto const& pk : m_pressedKeys) { + for (auto const& pk : m_dPressedKeys) { if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) { shadow = true; @@ -877,17 +850,30 @@ bool CKeybindManager::handleVT(xkb_keysym_t keysym) { // beyond this point, return true to not handle anything else. // we'll avoid printing shit to active windows. - if (g_pCompositor->m_aqBackend->hasSession()) { + if (g_pCompositor->m_pAqBackend->hasSession()) { const unsigned int TTY = keysym - XKB_KEY_XF86Switch_VT_1 + 1; - const auto CURRENT_TTY = g_pCompositor->getVTNr(); + // vtnr is bugged for some reason. + unsigned int ttynum = 0; + Hyprutils::OS::CFileDescriptor fd{open("/dev/tty", O_RDONLY | O_NOCTTY)}; + if (fd.isValid()) { +#if defined(VT_GETSTATE) + struct vt_stat st; + if (!ioctl(fd.get(), VT_GETSTATE, &st)) + ttynum = st.v_active; +#elif defined(VT_GETACTIVE) + int vt; + if (!ioctl(fd.get(), VT_GETACTIVE, &vt)) + ttynum = vt; +#endif + } - if (!CURRENT_TTY.has_value() || *CURRENT_TTY == TTY) + if (ttynum == TTY) return true; - Log::logger->log(Log::DEBUG, "Switching from VT {} to VT {}", *CURRENT_TTY, TTY); + Debug::log(LOG, "Switching from VT {} to VT {}", ttynum, TTY); - g_pCompositor->m_aqBackend->session->switchVT(TTY); + g_pCompositor->m_pAqBackend->session->switchVT(TTY); } return true; @@ -912,13 +898,11 @@ bool CKeybindManager::handleInternalKeybinds(xkb_keysym_t keysym) { // Dispatchers SDispatchResult CKeybindManager::spawn(std::string args) { - const auto PROC = spawnWithRules(args, nullptr); - if (!PROC.has_value()) - return {.success = false, .error = std::format("Failed to start process. No closing bracket in exec rule. {}", args)}; - return {.success = PROC.value() > 0, .error = std::format("Failed to start process {}", args)}; + const uint64_t PROC = spawnWithRules(args, nullptr); + return {.success = PROC > 0, .error = std::format("Failed to start process {}", args)}; } -std::optional CKeybindManager::spawnWithRules(std::string args, PHLWORKSPACE pInitialWorkspace) { +uint64_t CKeybindManager::spawnWithRules(std::string args, PHLWORKSPACE pInitialWorkspace) { args = trim(args); @@ -926,30 +910,21 @@ std::optional CKeybindManager::spawnWithRules(std::string args, PHLWOR if (args[0] == '[') { // we have exec rules - const auto end = args.find_first_of(']'); - if (end == std::string::npos) - return std::nullopt; - - RULES = args.substr(1, end - 1); - args = args.substr(end + 1); + RULES = args.substr(1, args.substr(1).find_first_of(']')); + args = args.substr(args.find_first_of(']') + 1); } - std::string execToken = ""; + const uint64_t PROC = spawnRawProc(args, pInitialWorkspace); if (!RULES.empty()) { - auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(RULES)); + const auto RULESLIST = CVarList(RULES, 0, ';'); - const auto TOKEN = g_pTokenManager->registerNewToken(nullptr, std::chrono::seconds(1)); + for (auto const& r : RULESLIST) { + g_pConfigManager->addExecRule({r, (unsigned long)PROC}); + } - const uint64_t PROC = spawnRawProc(args, pInitialWorkspace, TOKEN); - rule->markAsExecRule(TOKEN, PROC, false /* TODO: could be nice. */); - rule->registerMatch(Desktop::Rule::RULE_PROP_EXEC_TOKEN, TOKEN); - rule->registerMatch(Desktop::Rule::RULE_PROP_EXEC_PID, std::to_string(PROC)); - Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); - Log::logger->log(Log::DEBUG, "Applied rule arguments for exec."); - return PROC; + Debug::log(LOG, "Applied {} rule arguments for exec.", RULESLIST.size()); } - const uint64_t PROC = spawnRawProc(args, pInitialWorkspace, execToken); return PROC; } @@ -959,14 +934,22 @@ SDispatchResult CKeybindManager::spawnRaw(std::string args) { return {.success = PROC > 0, .error = std::format("Failed to start process {}", args)}; } -uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWorkspace, const std::string& execRuleToken) { - Log::logger->log(Log::DEBUG, "Executing {}", args); +uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWorkspace) { + Debug::log(LOG, "Executing {}", args); const auto HLENV = getHyprlandLaunchEnv(pInitialWorkspace); - pid_t child = fork(); + int socket[2]; + if (pipe(socket) != 0) { + Debug::log(LOG, "Unable to create pipe for fork"); + } + + CFileDescriptor pipeSock[2] = {CFileDescriptor{socket[0]}, CFileDescriptor{socket[1]}}; + + pid_t child, grandchild; + child = fork(); if (child < 0) { - Log::logger->log(Log::DEBUG, "Fail to fork"); + Debug::log(LOG, "Fail to create the first fork"); return 0; } if (child == 0) { @@ -977,37 +960,48 @@ uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWo sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, nullptr); - for (auto const& e : HLENV) { - setenv(e.first.c_str(), e.second.c_str(), 1); + grandchild = fork(); + if (grandchild == 0) { + // run in grandchild + for (auto const& e : HLENV) { + setenv(e.first.c_str(), e.second.c_str(), 1); + } + setenv("WAYLAND_DISPLAY", g_pCompositor->m_szWLDisplaySocket.c_str(), 1); + + int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC); + if (devnull != -1) { + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + close(devnull); + } + + execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr); + // exit grandchild + _exit(0); } - setenv("WAYLAND_DISPLAY", g_pCompositor->m_wlDisplaySocket.c_str(), 1); - if (!execRuleToken.empty()) - setenv(Desktop::Rule::EXEC_RULE_ENV_NAME, execRuleToken.c_str(), true); - - int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC); - if (devnull != -1) { - dup2(devnull, STDOUT_FILENO); - dup2(devnull, STDERR_FILENO); - close(devnull); - } - - execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr); - + write(pipeSock[1].get(), &grandchild, sizeof(grandchild)); // exit child _exit(0); } // run in parent + read(pipeSock[0].get(), &grandchild, sizeof(grandchild)); + // clear child and leave grandchild to init + waitpid(child, nullptr, 0); + if (grandchild < 0) { + Debug::log(LOG, "Fail to create the second fork"); + return 0; + } - Log::logger->log(Log::DEBUG, "Process Created with pid {}", child); + Debug::log(LOG, "Process Created with pid {}", grandchild); - return child; + return grandchild; } SDispatchResult CKeybindManager::killActive(std::string args) { - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) { - Log::logger->log(Log::ERR, "killActive: no window found"); + Debug::log(ERR, "killActive: no window found"); return {.success = false, .error = "killActive: no window found"}; } @@ -1017,10 +1011,7 @@ SDispatchResult CKeybindManager::killActive(std::string args) { } SDispatchResult CKeybindManager::closeActive(std::string args) { - if (Desktop::focusState()->window() && Desktop::focusState()->window()->m_closeableSince > Time::steadyNow()) - return {.success = false, .error = "can't close window, it's not closeable yet (noclosefor)"}; - - g_pCompositor->closeWindow(Desktop::focusState()->window()); + g_pCompositor->closeWindow(g_pCompositor->m_pLastWindow.lock()); return {}; } @@ -1029,13 +1020,10 @@ SDispatchResult CKeybindManager::closeWindow(std::string args) { const auto PWINDOW = g_pCompositor->getWindowByRegex(args); if (!PWINDOW) { - Log::logger->log(Log::ERR, "closeWindow: no window found"); + Debug::log(ERR, "closeWindow: no window found"); return {.success = false, .error = "closeWindow: no window found"}; } - if (PWINDOW->m_closeableSince > Time::steadyNow()) - return {.success = false, .error = "can't close window, it's not closeable yet (noclosefor)"}; - g_pCompositor->closeWindow(PWINDOW); return {}; @@ -1045,7 +1033,7 @@ SDispatchResult CKeybindManager::killWindow(std::string args) { const auto PWINDOW = g_pCompositor->getWindowByRegex(args); if (!PWINDOW) { - Log::logger->log(Log::ERR, "killWindow: no window found"); + Debug::log(ERR, "killWindow: no window found"); return {.success = false, .error = "killWindow: no window found"}; } @@ -1061,16 +1049,16 @@ SDispatchResult CKeybindManager::signalActive(std::string args) { try { const auto SIGNALNUM = std::stoi(args); if (SIGNALNUM < 1 || SIGNALNUM > 31) { - Log::logger->log(Log::ERR, "signalActive: invalid signal number {}", SIGNALNUM); + Debug::log(ERR, "signalActive: invalid signal number {}", SIGNALNUM); return {.success = false, .error = std::format("signalActive: invalid signal number {}", SIGNALNUM)}; } - kill(Desktop::focusState()->window()->getPID(), SIGNALNUM); + kill(g_pCompositor->m_pLastWindow.lock()->getPID(), SIGNALNUM); } catch (const std::exception& e) { - Log::logger->log(Log::ERR, "signalActive: invalid signal format \"{}\"", args); + Debug::log(ERR, "signalActive: invalid signal format \"{}\"", args); return {.success = false, .error = std::format("signalActive: invalid signal format \"{}\"", args)}; } - kill(Desktop::focusState()->window()->getPID(), std::stoi(args)); + kill(g_pCompositor->m_pLastWindow.lock()->getPID(), std::stoi(args)); return {}; } @@ -1082,22 +1070,22 @@ SDispatchResult CKeybindManager::signalWindow(std::string args) { const auto PWINDOW = g_pCompositor->getWindowByRegex(WINDOWREGEX); if (!PWINDOW) { - Log::logger->log(Log::ERR, "signalWindow: no window"); + Debug::log(ERR, "signalWindow: no window"); return {.success = false, .error = "signalWindow: no window"}; } - if (!std::ranges::all_of(SIGNAL, ::isdigit)) + if (!std::all_of(SIGNAL.begin(), SIGNAL.end(), ::isdigit)) return {.success = false, .error = "signalWindow: signal has to be int"}; try { const auto SIGNALNUM = std::stoi(SIGNAL); if (SIGNALNUM < 1 || SIGNALNUM > 31) { - Log::logger->log(Log::ERR, "signalWindow: invalid signal number {}", SIGNALNUM); + Debug::log(ERR, "signalWindow: invalid signal number {}", SIGNALNUM); return {.success = false, .error = std::format("signalWindow: invalid signal number {}", SIGNALNUM)}; } kill(PWINDOW->getPID(), SIGNALNUM); } catch (const std::exception& e) { - Log::logger->log(Log::ERR, "signalWindow: invalid signal format \"{}\"", SIGNAL); + Debug::log(ERR, "signalWindow: invalid signal format \"{}\"", SIGNAL); return {.success = false, .error = std::format("signalWindow: invalid signal format \"{}\"", SIGNAL)}; } @@ -1105,7 +1093,7 @@ SDispatchResult CKeybindManager::signalWindow(std::string args) { } void CKeybindManager::clearKeybinds() { - m_keybinds.clear(); + m_vKeybinds.clear(); } static SDispatchResult toggleActiveFloatingCore(std::string args, std::optional floatState) { @@ -1114,25 +1102,41 @@ static SDispatchResult toggleActiveFloatingCore(std::string args, std::optional< if (args != "active" && args.length() > 1) PWINDOW = g_pCompositor->getWindowByRegex(args); else - PWINDOW = Desktop::focusState()->window(); + PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "Window not found"}; - if (floatState.has_value() && floatState == PWINDOW->m_isFloating) + if (floatState.has_value() && floatState == PWINDOW->m_bIsFloating) return {}; // remove drag status - if (g_layoutManager->dragController()->target()) - CKeybindManager::changeMouseBindMode(MBIND_INVALID); + if (!g_pInputManager->currentlyDraggedWindow.expired()) + g_pKeybindManager->changeMouseBindMode(MBIND_INVALID); - g_layoutManager->changeFloatingMode(PWINDOW->layoutTarget()); + if (PWINDOW->m_sGroupData.pNextWindow.lock() && PWINDOW->m_sGroupData.pNextWindow.lock() != PWINDOW) { + const auto PCURRENT = PWINDOW->getGroupCurrent(); - if (PWINDOW->m_workspace) { - PWINDOW->m_workspace->updateWindows(); - PWINDOW->m_workspace->updateWindowData(); + PCURRENT->m_bIsFloating = !PCURRENT->m_bIsFloating; + g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(PCURRENT); + + PHLWINDOW curr = PCURRENT->m_sGroupData.pNextWindow.lock(); + while (curr != PCURRENT) { + curr->m_bIsFloating = PCURRENT->m_bIsFloating; + curr = curr->m_sGroupData.pNextWindow.lock(); + } + } else { + PWINDOW->m_bIsFloating = !PWINDOW->m_bIsFloating; + + g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(PWINDOW); } + if (PWINDOW->m_pWorkspace) { + PWINDOW->m_pWorkspace->updateWindows(); + PWINDOW->m_pWorkspace->updateWindowData(); + } + + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID()); g_pCompositor->updateAllWindowsAnimatedDecorationValues(); return {}; @@ -1151,14 +1155,19 @@ SDispatchResult CKeybindManager::setActiveTiled(std::string args) { } SDispatchResult CKeybindManager::centerWindow(std::string args) { - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); - if (!PWINDOW || !PWINDOW->m_isFloating || PWINDOW->isFullscreen()) + if (!PWINDOW || !PWINDOW->m_bIsFloating || PWINDOW->isFullscreen()) return {.success = false, .error = "No floating window found"}; - const auto PMONITOR = PWINDOW->m_monitor.lock(); + const auto PMONITOR = PWINDOW->m_pMonitor.lock(); - PWINDOW->layoutTarget()->setPositionGlobal(CBox{PMONITOR->logicalBoxMinusReserved().middle() - PWINDOW->m_realSize->goal() / 2.F, PWINDOW->layoutTarget()->position().size()}); + auto RESERVEDOFFSET = Vector2D(); + if (args == "1") + RESERVEDOFFSET = (PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight) / 2.f; + + *PWINDOW->m_vRealPosition = PMONITOR->middle() - PWINDOW->m_vRealSize->goal() / 2.f + RESERVEDOFFSET; + PWINDOW->m_vPosition = PWINDOW->m_vRealPosition->goal(); return {}; } @@ -1169,12 +1178,15 @@ SDispatchResult CKeybindManager::toggleActivePseudo(std::string args) { if (args != "active" && args.length() > 1) PWINDOW = g_pCompositor->getWindowByRegex(args); else - PWINDOW = Desktop::focusState()->window(); + PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "Window not found"}; - PWINDOW->layoutTarget()->setPseudo(!PWINDOW->layoutTarget()->isPseudo()); + PWINDOW->m_bIsPseudotiled = !PWINDOW->m_bIsPseudotiled; + + if (!PWINDOW->isFullscreen()) + g_pLayoutManager->getCurrentLayout()->recalculateWindow(PWINDOW); return {}; } @@ -1185,16 +1197,15 @@ static SWorkspaceIDName getWorkspaceToChangeFromArgs(std::string args, PHLWORKSP } const bool PER_MON = args.contains("_per_monitor"); - const SWorkspaceIDName PPREVWS = PER_MON ? Desktop::History::workspaceTracker()->previousWorkspaceIDName(PCURRENTWORKSPACE, PMONITOR.lock()) : - Desktop::History::workspaceTracker()->previousWorkspaceIDName(PCURRENTWORKSPACE); + const SWorkspaceIDName PPREVWS = PER_MON ? PMONITOR->getPrevWorkspaceIDName(PCURRENTWORKSPACE->m_iID) : PCURRENTWORKSPACE->getPrevWorkspaceIDName(); // Do nothing if there's no previous workspace, otherwise switch to it. - if (PPREVWS.id == -1 || PPREVWS.id == PCURRENTWORKSPACE->m_id) { - Log::logger->log(Log::DEBUG, "No previous workspace to change to"); + if (PPREVWS.id == -1 || PPREVWS.id == PCURRENTWORKSPACE->m_iID) { + Debug::log(LOG, "No previous workspace to change to"); return {.id = WORKSPACE_NOT_CHANGED}; } if (const auto PWORKSPACETOCHANGETO = g_pCompositor->getWorkspaceByID(PPREVWS.id); PWORKSPACETOCHANGETO) { - return {.id = PWORKSPACETOCHANGETO->m_id, .name = PWORKSPACETOCHANGETO->m_name}; + return {.id = PWORKSPACETOCHANGETO->m_iID, .name = PWORKSPACETOCHANGETO->m_szName}; } return {.id = PPREVWS.id, .name = PPREVWS.name.empty() ? std::to_string(PPREVWS.id) : PPREVWS.name}; @@ -1203,47 +1214,42 @@ static SWorkspaceIDName getWorkspaceToChangeFromArgs(std::string args, PHLWORKSP SDispatchResult CKeybindManager::changeworkspace(std::string args) { // Workspace_back_and_forth being enabled means that an attempt to switch to // the current workspace will instead switch to the previous. - static auto PBACKANDFORTH = CConfigValue("binds:workspace_back_and_forth"); - static auto PWORKSPACECENTERON = CConfigValue("binds:workspace_center_on"); - static auto PHIDESPECIALONWORKSPACECHANGE = CConfigValue("binds:hide_special_on_workspace_change"); + static auto PBACKANDFORTH = CConfigValue("binds:workspace_back_and_forth"); + static auto PALLOWWORKSPACECYCLES = CConfigValue("binds:allow_workspace_cycles"); + static auto PWORKSPACECENTERON = CConfigValue("binds:workspace_center_on"); - const auto PMONITOR = Desktop::focusState()->monitor(); + const auto PMONITOR = g_pCompositor->m_pLastMonitor.lock(); if (!PMONITOR) return {.success = false, .error = "Last monitor not found"}; - const auto PCURRENTWORKSPACE = PMONITOR->m_activeWorkspace; + const auto PCURRENTWORKSPACE = PMONITOR->activeWorkspace; const bool EXPLICITPREVIOUS = args.contains("previous"); - const auto& [workspaceToChangeTo, workspaceName, isAutoID] = getWorkspaceToChangeFromArgs(args, PCURRENTWORKSPACE, PMONITOR); + const auto& [workspaceToChangeTo, workspaceName] = getWorkspaceToChangeFromArgs(args, PCURRENTWORKSPACE, PMONITOR); if (workspaceToChangeTo == WORKSPACE_INVALID) { - Log::logger->log(Log::ERR, "Error in changeworkspace, invalid value"); + Debug::log(ERR, "Error in changeworkspace, invalid value"); return {.success = false, .error = "Error in changeworkspace, invalid value"}; } if (workspaceToChangeTo == WORKSPACE_NOT_CHANGED) return {}; - const SWorkspaceIDName PPREVWS = args.contains("_per_monitor") ? Desktop::History::workspaceTracker()->previousWorkspaceIDName(PCURRENTWORKSPACE, PMONITOR) : - Desktop::History::workspaceTracker()->previousWorkspaceIDName(PCURRENTWORKSPACE); - - const bool BISWORKSPACECURRENT = workspaceToChangeTo == PCURRENTWORKSPACE->m_id; - if (BISWORKSPACECURRENT && (!(*PBACKANDFORTH || EXPLICITPREVIOUS) || PPREVWS.id == -1)) { - if (*PHIDESPECIALONWORKSPACECHANGE) - PMONITOR->setSpecialWorkspace(nullptr); + const SWorkspaceIDName PPREVWS = args.contains("_per_monitor") ? PMONITOR->getPrevWorkspaceIDName(PCURRENTWORKSPACE->m_iID) : PCURRENTWORKSPACE->getPrevWorkspaceIDName(); + const bool BISWORKSPACECURRENT = workspaceToChangeTo == PCURRENTWORKSPACE->m_iID; + if (BISWORKSPACECURRENT && (!(*PBACKANDFORTH || EXPLICITPREVIOUS) || PPREVWS.id == -1)) return {.success = false, .error = "Previous workspace doesn't exist"}; - } g_pInputManager->unconstrainMouse(); - g_pInputManager->m_emptyFocusCursorSet = false; + g_pInputManager->m_bEmptyFocusCursorSet = false; auto pWorkspaceToChangeTo = g_pCompositor->getWorkspaceByID(BISWORKSPACECURRENT ? PPREVWS.id : workspaceToChangeTo); if (!pWorkspaceToChangeTo) pWorkspaceToChangeTo = - g_pCompositor->createNewWorkspace(BISWORKSPACECURRENT ? PPREVWS.id : workspaceToChangeTo, PMONITOR->m_id, BISWORKSPACECURRENT ? PPREVWS.name : workspaceName); + g_pCompositor->createNewWorkspace(BISWORKSPACECURRENT ? PPREVWS.id : workspaceToChangeTo, PMONITOR->ID, BISWORKSPACECURRENT ? PPREVWS.name : workspaceName); - if (!BISWORKSPACECURRENT && pWorkspaceToChangeTo->m_isSpecialWorkspace) { + if (!BISWORKSPACECURRENT && pWorkspaceToChangeTo->m_bIsSpecialWorkspace) { PMONITOR->setSpecialWorkspace(pWorkspaceToChangeTo); g_pInputManager->simulateMouseMovement(); return {}; @@ -1251,31 +1257,37 @@ SDispatchResult CKeybindManager::changeworkspace(std::string args) { g_pInputManager->releaseAllMouseButtons(); - const auto PMONITORWORKSPACEOWNER = PMONITOR == pWorkspaceToChangeTo->m_monitor ? PMONITOR : pWorkspaceToChangeTo->m_monitor.lock(); + const auto PMONITORWORKSPACEOWNER = PMONITOR == pWorkspaceToChangeTo->m_pMonitor ? PMONITOR : pWorkspaceToChangeTo->m_pMonitor.lock(); if (!PMONITORWORKSPACEOWNER) return {.success = false, .error = "Workspace to switch to has no monitor"}; updateRelativeCursorCoords(); - Desktop::focusState()->rawMonitorFocus(PMONITORWORKSPACEOWNER); + g_pCompositor->setActiveMonitor(PMONITORWORKSPACEOWNER); + + if (BISWORKSPACECURRENT) { + if (*PALLOWWORKSPACECYCLES) + pWorkspaceToChangeTo->rememberPrevWorkspace(PCURRENTWORKSPACE); + else if (!EXPLICITPREVIOUS && !*PBACKANDFORTH) + pWorkspaceToChangeTo->rememberPrevWorkspace(nullptr); + } else + pWorkspaceToChangeTo->rememberPrevWorkspace(PCURRENTWORKSPACE); - if (*PHIDESPECIALONWORKSPACECHANGE) - PMONITORWORKSPACEOWNER->setSpecialWorkspace(nullptr); PMONITORWORKSPACEOWNER->changeWorkspace(pWorkspaceToChangeTo, false, true); if (PMONITOR != PMONITORWORKSPACEOWNER) { Vector2D middle = PMONITORWORKSPACEOWNER->middle(); if (const auto PLAST = pWorkspaceToChangeTo->getLastFocusedWindow(); PLAST) { - Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->focusWindow(PLAST); if (*PWORKSPACECENTERON == 1) middle = PLAST->middle(); } g_pCompositor->warpCursorTo(middle); } - if (!g_pInputManager->m_lastFocusOnLS) { - if (Desktop::focusState()->surface()) + if (!g_pInputManager->m_bLastFocusOnLS) { + if (g_pCompositor->m_pLastFocus) g_pInputManager->sendMotionEventsToFocused(); else g_pInputManager->simulateMouseMovement(); @@ -1285,9 +1297,9 @@ SDispatchResult CKeybindManager::changeworkspace(std::string args) { if (*PWARPONWORKSPACECHANGE > 0) { auto PLAST = pWorkspaceToChangeTo->getLastFocusedWindow(); - auto HLSurface = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.pointerFocus.lock()); + auto HLSurface = CWLSurface::fromResource(g_pSeatManager->state.pointerFocus.lock()); - if (PLAST && (!HLSurface || HLSurface->view()->type() == Desktop::View::VIEW_TYPE_WINDOW)) + if (PLAST && (!HLSurface || HLSurface->getWindow())) PLAST->warpCursor(*PWARPONWORKSPACECHANGE == 2); } @@ -1295,37 +1307,29 @@ SDispatchResult CKeybindManager::changeworkspace(std::string args) { } SDispatchResult CKeybindManager::fullscreenActive(std::string args) { - const auto PWINDOW = Desktop::focusState()->window(); - const auto ARGS = CConstVarList(args, 2, ' '); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "Window not found"}; - const eFullscreenMode MODE = ARGS.size() > 0 && ARGS[0] == "1" ? FSMODE_MAXIMIZED : FSMODE_FULLSCREEN; + const eFullscreenMode MODE = args == "1" ? FSMODE_MAXIMIZED : FSMODE_FULLSCREEN; - if (ARGS.size() <= 1 || ARGS[1] == "toggle") { - if (PWINDOW->isEffectiveInternalFSMode(MODE)) - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - else - g_pCompositor->setWindowFullscreenInternal(PWINDOW, MODE); - } else { - if (ARGS[1] == "set") - g_pCompositor->setWindowFullscreenInternal(PWINDOW, MODE); - else if (ARGS[1] == "unset") - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - } + if (PWINDOW->isEffectiveInternalFSMode(MODE)) + g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); + else + g_pCompositor->setWindowFullscreenInternal(PWINDOW, MODE); return {}; } SDispatchResult CKeybindManager::fullscreenStateActive(std::string args) { - const auto PWINDOW = Desktop::focusState()->window(); - const auto ARGS = CVarList(args, 3, ' '); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); + const auto ARGS = CVarList(args, 2, ' '); if (!PWINDOW) return {.success = false, .error = "Window not found"}; - PWINDOW->m_ruleApplicator->syncFullscreenOverride(Desktop::Types::COverridableVar(false, Desktop::Types::PRIORITY_SET_PROP)); + PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_SET_PROP); int internalMode, clientMode; try { @@ -1335,25 +1339,19 @@ SDispatchResult CKeybindManager::fullscreenStateActive(std::string args) { clientMode = std::stoi(ARGS[1]); } catch (std::exception& e) { clientMode = -1; } - const Desktop::View::SFullscreenState STATE = - Desktop::View::SFullscreenState{.internal = (internalMode != -1 ? sc(internalMode) : PWINDOW->m_fullscreenState.internal), - .client = (clientMode != -1 ? sc(clientMode) : PWINDOW->m_fullscreenState.client)}; + const SFullscreenState STATE = SFullscreenState{.internal = (internalMode != -1 ? (eFullscreenMode)internalMode : PWINDOW->m_sFullscreenState.internal), + .client = (clientMode != -1 ? (eFullscreenMode)clientMode : PWINDOW->m_sFullscreenState.client)}; - if (ARGS.size() <= 2 || ARGS[2] == "toggle") { - if (internalMode != -1 && clientMode != -1 && PWINDOW->m_fullscreenState.internal == STATE.internal && PWINDOW->m_fullscreenState.client == STATE.client) - g_pCompositor->setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = FSMODE_NONE, .client = FSMODE_NONE}); - else if (internalMode != -1 && clientMode == -1 && PWINDOW->m_fullscreenState.internal == STATE.internal) - g_pCompositor->setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = FSMODE_NONE, .client = PWINDOW->m_fullscreenState.client}); - else if (internalMode == -1 && clientMode != -1 && PWINDOW->m_fullscreenState.client == STATE.client) - g_pCompositor->setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = PWINDOW->m_fullscreenState.internal, .client = FSMODE_NONE}); - else - g_pCompositor->setWindowFullscreenState(PWINDOW, STATE); - } else if (ARGS[2] == "set") { + if (internalMode != -1 && clientMode != -1 && PWINDOW->m_sFullscreenState.internal == STATE.internal && PWINDOW->m_sFullscreenState.client == STATE.client) + g_pCompositor->setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = FSMODE_NONE, .client = FSMODE_NONE}); + else if (internalMode != -1 && clientMode == -1 && PWINDOW->m_sFullscreenState.internal == STATE.internal) + g_pCompositor->setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = FSMODE_NONE, .client = PWINDOW->m_sFullscreenState.client}); + else if (internalMode == -1 && clientMode != -1 && PWINDOW->m_sFullscreenState.client == STATE.client) + g_pCompositor->setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = PWINDOW->m_sFullscreenState.internal, .client = FSMODE_NONE}); + else g_pCompositor->setWindowFullscreenState(PWINDOW, STATE); - } - PWINDOW->m_ruleApplicator->syncFullscreenOverride( - Desktop::Types::COverridableVar(PWINDOW->m_fullscreenState.internal == PWINDOW->m_fullscreenState.client, Desktop::Types::PRIORITY_SET_PROP)); + PWINDOW->m_sWindowData.syncFullscreen = CWindowOverridableVar(PWINDOW->m_sFullscreenState.internal == PWINDOW->m_sFullscreenState.client, PRIORITY_SET_PROP); return {}; } @@ -1366,53 +1364,55 @@ SDispatchResult CKeybindManager::moveActiveToWorkspace(std::string args) { PWINDOW = g_pCompositor->getWindowByRegex(args.substr(args.find_last_of(',') + 1)); args = args.substr(0, args.find_last_of(',')); } else { - PWINDOW = Desktop::focusState()->window(); + PWINDOW = g_pCompositor->m_pLastWindow.lock(); } if (!PWINDOW) return {.success = false, .error = "Window not found"}; - const auto& [WORKSPACEID, workspaceName, isAutoID] = getWorkspaceIDNameFromString(args); + const auto& [WORKSPACEID, workspaceName] = getWorkspaceIDNameFromString(args); if (WORKSPACEID == WORKSPACE_INVALID) { - Log::logger->log(Log::DEBUG, "Invalid workspace in moveActiveToWorkspace"); + Debug::log(LOG, "Invalid workspace in moveActiveToWorkspace"); return {.success = false, .error = "Invalid workspace in moveActiveToWorkspace"}; } if (WORKSPACEID == PWINDOW->workspaceID()) { - Log::logger->log(Log::DEBUG, "Not moving to workspace because it didn't change."); + Debug::log(LOG, "Not moving to workspace because it didn't change."); return {.success = false, .error = "Not moving to workspace because it didn't change."}; } - auto pWorkspace = g_pCompositor->getWorkspaceByID(WORKSPACEID); - PHLMONITOR pMonitor = nullptr; - const auto POLDWS = PWINDOW->m_workspace; + auto pWorkspace = g_pCompositor->getWorkspaceByID(WORKSPACEID); + PHLMONITOR pMonitor = nullptr; + const auto POLDWS = PWINDOW->m_pWorkspace; + static auto PALLOWWORKSPACECYCLES = CConfigValue("binds:allow_workspace_cycles"); updateRelativeCursorCoords(); g_pHyprRenderer->damageWindow(PWINDOW); if (pWorkspace) { - const auto FULLSCREENMODE = PWINDOW->m_fullscreenState.internal; g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace); - pMonitor = pWorkspace->m_monitor.lock(); - Desktop::focusState()->rawMonitorFocus(pMonitor); - g_pCompositor->setWindowFullscreenInternal(PWINDOW, FULLSCREENMODE); + pMonitor = pWorkspace->m_pMonitor.lock(); + g_pCompositor->setActiveMonitor(pMonitor); } else { pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->monitorID(), workspaceName, false); - pMonitor = pWorkspace->m_monitor.lock(); + pMonitor = pWorkspace->m_pMonitor.lock(); g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace); } - POLDWS->m_lastFocusedWindow = POLDWS->getFirstWindow(); + POLDWS->m_pLastFocusedWindow = POLDWS->getFirstWindow(); - if (pWorkspace->m_isSpecialWorkspace) + if (pWorkspace->m_bIsSpecialWorkspace) pMonitor->setSpecialWorkspace(pWorkspace); - else if (POLDWS->m_isSpecialWorkspace) - POLDWS->m_monitor.lock()->setSpecialWorkspace(nullptr); + else if (POLDWS->m_bIsSpecialWorkspace) + POLDWS->m_pMonitor.lock()->setSpecialWorkspace(nullptr); + + if (*PALLOWWORKSPACECYCLES) + pWorkspace->rememberPrevWorkspace(POLDWS); pMonitor->changeWorkspace(pWorkspace); - Desktop::focusState()->fullWindowFocus(PWINDOW, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->focusWindow(PWINDOW); PWINDOW->warpCursor(); return {}; @@ -1425,15 +1425,15 @@ SDispatchResult CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { PWINDOW = g_pCompositor->getWindowByRegex(args.substr(args.find_last_of(',') + 1)); args = args.substr(0, args.find_last_of(',')); } else { - PWINDOW = Desktop::focusState()->window(); + PWINDOW = g_pCompositor->m_pLastWindow.lock(); } if (!PWINDOW) return {.success = false, .error = "Window not found"}; - const auto& [WORKSPACEID, workspaceName, isAutoID] = getWorkspaceIDNameFromString(args); + const auto& [WORKSPACEID, workspaceName] = getWorkspaceIDNameFromString(args); if (WORKSPACEID == WORKSPACE_INVALID) { - Log::logger->log(Log::ERR, "Error in moveActiveToWorkspaceSilent, invalid value"); + Debug::log(ERR, "Error in moveActiveToWorkspaceSilent, invalid value"); return {.success = false, .error = "Error in moveActiveToWorkspaceSilent, invalid value"}; } @@ -1452,11 +1452,9 @@ SDispatchResult CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace); } - if (PWINDOW == Desktop::focusState()->window()) { - if (const auto PATCOORDS = - g_pCompositor->vectorToWindowUnified(OLDMIDDLE, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, PWINDOW); - PATCOORDS) - Desktop::focusState()->fullWindowFocus(PATCOORDS, Desktop::FOCUS_REASON_KEYBIND); + if (PWINDOW == g_pCompositor->m_pLastWindow) { + if (const auto PATCOORDS = g_pCompositor->vectorToWindowUnified(OLDMIDDLE, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING, PWINDOW); PATCOORDS) + g_pCompositor->focusWindow(PATCOORDS); else g_pInputManager->refocus(); } @@ -1465,92 +1463,94 @@ SDispatchResult CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { } SDispatchResult CKeybindManager::moveFocusTo(std::string args) { - static auto PFULLCYCLE = CConfigValue("binds:movefocus_cycles_fullscreen"); - static auto PGROUPCYCLE = CConfigValue("binds:movefocus_cycles_groupfirst"); - static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); - Math::eDirection dir = Math::fromChar(args[0]); + static auto PFULLCYCLE = CConfigValue("binds:movefocus_cycles_fullscreen"); + static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); + static auto PGROUPCYCLE = CConfigValue("binds:movefocus_cycles_groupfirst"); + char arg = args[0]; - if (dir == Math::DIRECTION_DEFAULT) { - Log::logger->log(Log::ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]); - return {.success = false, .error = std::format("Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0])}; + if (!isDirection(args)) { + Debug::log(ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg); + return {.success = false, .error = std::format("Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)}; } - const auto PLASTWINDOW = Desktop::focusState()->window(); - if (!PLASTWINDOW || !PLASTWINDOW->aliveAndVisible()) { + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); + if (!PLASTWINDOW) { if (*PMONITORFALLBACK) - tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir)); + tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg)); + return {}; } const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->isFullscreen() ? - g_pCompositor->getWindowCycle(PLASTWINDOW, true, {}, false, dir != Math::DIRECTION_DOWN && dir != Math::DIRECTION_RIGHT) : - g_pCompositor->getWindowInDirection(PLASTWINDOW, dir); + g_pCompositor->getWindowCycle(PLASTWINDOW, true, {}, false, arg != 'd' && arg != 'b' && arg != 'r') : + g_pCompositor->getWindowInDirection(PLASTWINDOW, arg); // Prioritize focus change within groups if the window is a part of it. - if (*PGROUPCYCLE && PLASTWINDOW->m_group) { - auto isTheOnlyGroupOnWs = !PWINDOWTOCHANGETO && g_pCompositor->m_monitors.size() == 1; - if (dir == Math::DIRECTION_LEFT && (PLASTWINDOW != PLASTWINDOW->m_group->head() || isTheOnlyGroupOnWs)) { - PLASTWINDOW->m_group->moveCurrent(false); + if (*PGROUPCYCLE && PLASTWINDOW->m_sGroupData.pNextWindow) { + auto isTheOnlyGroupOnWs = !PWINDOWTOCHANGETO && g_pCompositor->m_vMonitors.size() == 1; + if (arg == 'l' && (PLASTWINDOW != PLASTWINDOW->getGroupHead() || isTheOnlyGroupOnWs)) { + PLASTWINDOW->setGroupCurrent(PLASTWINDOW->getGroupPrevious()); return {}; } - else if (dir == Math::DIRECTION_RIGHT && (PLASTWINDOW != PLASTWINDOW->m_group->tail() || isTheOnlyGroupOnWs)) { - PLASTWINDOW->m_group->moveCurrent(true); + else if (arg == 'r' && (PLASTWINDOW != PLASTWINDOW->getGroupTail() || isTheOnlyGroupOnWs)) { + PLASTWINDOW->setGroupCurrent(PLASTWINDOW->m_sGroupData.pNextWindow.lock()); return {}; } } // Found window in direction, switch to it if (PWINDOWTOCHANGETO) { - switchToWindow(PWINDOWTOCHANGETO, *PFULLCYCLE && PLASTWINDOW->isFullscreen()); + switchToWindow(PWINDOWTOCHANGETO); return {}; } - Log::logger->log(Log::DEBUG, "No window found in direction {}, looking for a monitor", Math::toString(dir)); + Debug::log(LOG, "No window found in direction {}, looking for a monitor", arg); - if (*PMONITORFALLBACK && tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir))) + if (*PMONITORFALLBACK && tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg))) return {}; static auto PNOFALLBACK = CConfigValue("general:no_focus_fallback"); if (*PNOFALLBACK) - return {.success = false, .error = std::format("Nothing to focus to in direction {}", Math::toString(dir))}; + return {.success = false, .error = std::format("Nothing to focus to in direction {}", arg)}; - Log::logger->log(Log::DEBUG, "No monitor found in direction {}, getting the inverse edge", Math::toString(dir)); + Debug::log(LOG, "No monitor found in direction {}, getting the inverse edge", arg); - const auto PMONITOR = PLASTWINDOW->m_monitor.lock(); + const auto PMONITOR = PLASTWINDOW->m_pMonitor.lock(); if (!PMONITOR) return {.success = false, .error = "last window has no monitor?"}; - if (dir == Math::DIRECTION_LEFT || dir == Math::DIRECTION_RIGHT) { - if (STICKS(PLASTWINDOW->m_position.x, PMONITOR->m_position.x) && STICKS(PLASTWINDOW->m_size.x, PMONITOR->m_size.x)) + if (arg == 'l' || arg == 'r') { + if (STICKS(PLASTWINDOW->m_vPosition.x, PMONITOR->vecPosition.x) && STICKS(PLASTWINDOW->m_vSize.x, PMONITOR->vecSize.x)) return {.success = false, .error = "move does not make sense, would return back"}; - } else if (STICKS(PLASTWINDOW->m_position.y, PMONITOR->m_position.y) && STICKS(PLASTWINDOW->m_size.y, PMONITOR->m_size.y)) + } else if (STICKS(PLASTWINDOW->m_vPosition.y, PMONITOR->vecPosition.y) && STICKS(PLASTWINDOW->m_vSize.y, PMONITOR->vecSize.y)) return {.success = false, .error = "move does not make sense, would return back"}; CBox box = PMONITOR->logicalBox(); - switch (dir) { - case Math::DIRECTION_LEFT: + switch (arg) { + case 'l': box.x += box.w; box.w = 1; break; - case Math::DIRECTION_RIGHT: + case 'r': box.x -= 1; box.w = 1; break; - case Math::DIRECTION_UP: + case 'u': + case 't': box.y += box.h; box.h = 1; break; - case Math::DIRECTION_DOWN: + case 'd': + case 'b': box.y -= 1; box.h = 1; break; - default: break; } - const auto PWINDOWCANDIDATE = g_pCompositor->getWindowInDirection(box, PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace, - dir, PLASTWINDOW, PLASTWINDOW->m_isFloating); + const auto PWINDOWCANDIDATE = g_pCompositor->getWindowInDirection(box, PMONITOR->activeSpecialWorkspace ? PMONITOR->activeSpecialWorkspace : PMONITOR->activeWorkspace, arg, + PLASTWINDOW, PLASTWINDOW->m_bIsFloating); if (PWINDOWCANDIDATE) switchToWindow(PWINDOWCANDIDATE); @@ -1558,9 +1558,9 @@ SDispatchResult CKeybindManager::moveFocusTo(std::string args) { } SDispatchResult CKeybindManager::focusUrgentOrLast(std::string args) { - const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); - const auto PWINDOWURGENT = g_pCompositor->getUrgentWindow(); - const auto PWINDOWPREV = Desktop::focusState()->window() ? (HISTORY.size() < 2 ? nullptr : HISTORY[1].lock()) : (HISTORY.empty() ? nullptr : HISTORY[0].lock()); + const auto PWINDOWURGENT = g_pCompositor->getUrgentWindow(); + const auto PWINDOWPREV = g_pCompositor->m_pLastWindow.lock() ? (g_pCompositor->m_vWindowFocusHistory.size() < 2 ? nullptr : g_pCompositor->m_vWindowFocusHistory[1].lock()) : + (g_pCompositor->m_vWindowFocusHistory.empty() ? nullptr : g_pCompositor->m_vWindowFocusHistory[0].lock()); if (!PWINDOWURGENT && !PWINDOWPREV) return {.success = false, .error = "Window not found"}; @@ -1571,12 +1571,8 @@ SDispatchResult CKeybindManager::focusUrgentOrLast(std::string args) { } SDispatchResult CKeybindManager::focusCurrentOrLast(std::string args) { - const auto& HISTORY = Desktop::History::windowTracker()->fullHistory(); - - if (HISTORY.size() <= 1) - return {.success = false, .error = "History too short"}; - - const auto PWINDOWPREV = HISTORY[HISTORY.size() - 2].lock(); + const auto PWINDOWPREV = g_pCompositor->m_pLastWindow.lock() ? (g_pCompositor->m_vWindowFocusHistory.size() < 2 ? nullptr : g_pCompositor->m_vWindowFocusHistory[1].lock()) : + (g_pCompositor->m_vWindowFocusHistory.empty() ? nullptr : g_pCompositor->m_vWindowFocusHistory[0].lock()); if (!PWINDOWPREV) return {.success = false, .error = "Window not found"}; @@ -1587,8 +1583,15 @@ SDispatchResult CKeybindManager::focusCurrentOrLast(std::string args) { } SDispatchResult CKeybindManager::swapActive(std::string args) { - const auto PLASTWINDOW = Desktop::focusState()->window(); - PHLWINDOW PWINDOWTOCHANGETO = nullptr; + char arg = args[0]; + + if (!isDirection(args)) { + Debug::log(ERR, "Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg); + return {.success = false, .error = std::format("Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)}; + } + + Debug::log(LOG, "Swapping active window in direction {}", arg); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PLASTWINDOW) return {.success = false, .error = "Window to swap with not found"}; @@ -1596,26 +1599,19 @@ SDispatchResult CKeybindManager::swapActive(std::string args) { if (PLASTWINDOW->isFullscreen()) return {.success = false, .error = "Can't swap fullscreen window"}; - if (isDirection(args)) { - Math::eDirection dir = Math::fromChar(args[0]); - PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, dir); - } else - PWINDOWTOCHANGETO = g_pCompositor->getWindowByRegex(args); - - if (!PWINDOWTOCHANGETO || PWINDOWTOCHANGETO == PLASTWINDOW) { - Log::logger->log(Log::ERR, "Can't swap with {}, invalid window", args); - return {.success = false, .error = std::format("Can't swap with {}, invalid window", args)}; - } - - Log::logger->log(Log::DEBUG, "Swapping active window with {}", args); + const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg); + if (!PWINDOWTOCHANGETO) + return {.success = false, .error = "Window to swap with not found"}; updateRelativeCursorCoords(); - g_layoutManager->switchTargets(PLASTWINDOW->layoutTarget(), PWINDOWTOCHANGETO->layoutTarget(), true); + g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, PWINDOWTOCHANGETO); PLASTWINDOW->warpCursor(); + return {}; } SDispatchResult CKeybindManager::moveActiveTo(std::string args) { + char arg = args[0]; bool silent = args.ends_with(" silent"); if (silent) args = args.substr(0, args.length() - 7); @@ -1626,20 +1622,19 @@ SDispatchResult CKeybindManager::moveActiveTo(std::string args) { return {.success = false, .error = std::format("Monitor {} not found", args.substr(4))}; if (silent) - moveActiveToWorkspaceSilent(PNEWMONITOR->m_activeWorkspace->getConfigName()); + moveActiveToWorkspaceSilent(PNEWMONITOR->activeWorkspace->getConfigName()); else - moveActiveToWorkspace(PNEWMONITOR->m_activeWorkspace->getConfigName()); + moveActiveToWorkspace(PNEWMONITOR->activeWorkspace->getConfigName()); return {}; } - Math::eDirection dir = Math::fromChar(args[0]); - if (dir == Math::DIRECTION_DEFAULT) { - Log::logger->log(Log::ERR, "Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]); - return {.success = false, .error = std::format("Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0])}; + if (!isDirection(args)) { + Debug::log(ERR, "Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg); + return {.success = false, .error = std::format("Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)}; } - const auto PLASTWINDOW = Desktop::focusState()->window(); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PLASTWINDOW) return {.success = false, .error = "Window to move not found"}; @@ -1647,17 +1642,56 @@ SDispatchResult CKeybindManager::moveActiveTo(std::string args) { if (PLASTWINDOW->isFullscreen()) return {.success = false, .error = "Can't move fullscreen window"}; - updateRelativeCursorCoords(); + if (PLASTWINDOW->m_bIsFloating) { + std::optional vPosx, vPosy; + const auto PMONITOR = PLASTWINDOW->m_pMonitor.lock(); + const auto BORDERSIZE = PLASTWINDOW->getRealBorderSize(); - g_layoutManager->moveInDirection(PLASTWINDOW->layoutTarget(), args, silent); - if (!silent) - PLASTWINDOW->warpCursor(); + switch (arg) { + case 'l': vPosx = PMONITOR->vecReservedTopLeft.x + BORDERSIZE + PMONITOR->vecPosition.x; break; + case 'r': vPosx = PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PLASTWINDOW->m_vRealSize->goal().x - BORDERSIZE + PMONITOR->vecPosition.x; break; + case 't': + case 'u': vPosy = PMONITOR->vecReservedTopLeft.y + BORDERSIZE + PMONITOR->vecPosition.y; break; + case 'b': + case 'd': vPosy = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PLASTWINDOW->m_vRealSize->goal().y - BORDERSIZE + PMONITOR->vecPosition.y; break; + } + + *PLASTWINDOW->m_vRealPosition = Vector2D(vPosx.value_or(PLASTWINDOW->m_vRealPosition->goal().x), vPosy.value_or(PLASTWINDOW->m_vRealPosition->goal().y)); + + return {}; + } + + // If the window to change to is on the same workspace, switch them + const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg); + if (PWINDOWTOCHANGETO) { + updateRelativeCursorCoords(); + + g_pLayoutManager->getCurrentLayout()->moveWindowTo(PLASTWINDOW, args, silent); + if (!silent) + PLASTWINDOW->warpCursor(); + return {}; + } + + static auto PMONITORFALLBACK = CConfigValue("binds:window_direction_monitor_fallback"); + if (!*PMONITORFALLBACK) + return {}; + + // Otherwise, we always want to move to the next monitor in that direction + const auto PMONITORTOCHANGETO = g_pCompositor->getMonitorInDirection(arg); + if (!PMONITORTOCHANGETO) + return {.success = false, .error = "Nowhere to move active window to"}; + + const auto PWORKSPACE = PMONITORTOCHANGETO->activeWorkspace; + if (silent) + moveActiveToWorkspaceSilent(PWORKSPACE->getConfigName()); + else + moveActiveToWorkspace(PWORKSPACE->getConfigName()); return {}; } SDispatchResult CKeybindManager::toggleGroup(std::string args) { - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "Window not found"}; @@ -1665,43 +1699,102 @@ SDispatchResult CKeybindManager::toggleGroup(std::string args) { if (PWINDOW->isFullscreen()) g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE); - if (!PWINDOW->m_group) - PWINDOW->m_group = Desktop::View::CGroup::create({PWINDOW}); + if (PWINDOW->m_sGroupData.pNextWindow.expired()) + PWINDOW->createGroup(); else - PWINDOW->m_group->destroy(); + PWINDOW->destroyGroup(); return {}; } SDispatchResult CKeybindManager::changeGroupActive(std::string args) { - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "Window not found"}; - if (!PWINDOW->m_group) - return {.success = false, .error = "No group"}; + if (PWINDOW->m_sGroupData.pNextWindow.expired()) + return {.success = false, .error = "No next window in group"}; - if (PWINDOW->m_group->size() == 1) + if (PWINDOW->m_sGroupData.pNextWindow.lock() == PWINDOW) return {.success = false, .error = "Only one window in group"}; if (isNumber(args, false)) { // index starts from '1'; '0' means last window - try { - const int INDEX = std::stoi(args); - if (INDEX <= 0) - PWINDOW->m_group->setCurrent(PWINDOW->m_group->size() - 1); - else - PWINDOW->m_group->setCurrent(INDEX - 1); - } catch (...) { return {.success = false, .error = "invalid idx"}; } - + const int INDEX = std::stoi(args); + if (INDEX > PWINDOW->getGroupSize()) + return {.success = false, .error = "Index too big, there aren't that many windows in this group"}; + if (INDEX == 0) + PWINDOW->setGroupCurrent(PWINDOW->getGroupTail()); + else + PWINDOW->setGroupCurrent(PWINDOW->getGroupWindowByIndex(INDEX - 1)); return {}; } - if (args != "b" && args != "prev") - PWINDOW->m_group->moveCurrent(true); - else - PWINDOW->m_group->moveCurrent(false); + if (args != "b" && args != "prev") { + PWINDOW->setGroupCurrent(PWINDOW->m_sGroupData.pNextWindow.lock()); + } else { + PWINDOW->setGroupCurrent(PWINDOW->getGroupPrevious()); + } + + return {}; +} + +SDispatchResult CKeybindManager::toggleSplit(std::string args) { + SLayoutMessageHeader header; + header.pWindow = g_pCompositor->m_pLastWindow.lock(); + + if (!header.pWindow) + return {.success = false, .error = "Window not found"}; + + const auto PWORKSPACE = header.pWindow->m_pWorkspace; + + if (PWORKSPACE->m_bHasFullscreenWindow) + return {.success = false, .error = "Can't split windows that already split"}; + + g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "togglesplit"); + + return {}; +} + +SDispatchResult CKeybindManager::swapSplit(std::string args) { + SLayoutMessageHeader header; + header.pWindow = g_pCompositor->m_pLastWindow.lock(); + + if (!header.pWindow) + return {.success = false, .error = "Window not found"}; + + const auto PWORKSPACE = header.pWindow->m_pWorkspace; + + if (PWORKSPACE->m_bHasFullscreenWindow) + return {.success = false, .error = "Can't split windows that already split"}; + + g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "swapsplit"); + + return {}; +} + +SDispatchResult CKeybindManager::alterSplitRatio(std::string args) { + std::optional splitResult; + bool exact = false; + + if (args.starts_with("exact")) { + exact = true; + splitResult = getPlusMinusKeywordResult(args.substr(5), 0); + } else + splitResult = getPlusMinusKeywordResult(args, 0); + + if (!splitResult.has_value()) { + Debug::log(ERR, "Splitratio invalid in alterSplitRatio!"); + return {.success = false, .error = "Splitratio invalid in alterSplitRatio!"}; + } + + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); + + if (!PLASTWINDOW) + return {.success = false, .error = "Window not found"}; + + g_pLayoutManager->getCurrentLayout()->alterSplitRatio(PLASTWINDOW, splitResult.value(), exact); return {}; } @@ -1715,18 +1808,18 @@ SDispatchResult CKeybindManager::focusMonitor(std::string arg) { SDispatchResult CKeybindManager::moveCursorToCorner(std::string arg) { if (!isNumber(arg)) { - Log::logger->log(Log::ERR, "moveCursorToCorner, arg has to be a number."); + Debug::log(ERR, "moveCursorToCorner, arg has to be a number."); return {.success = false, .error = "moveCursorToCorner, arg has to be a number."}; } const auto CORNER = std::stoi(arg); if (CORNER < 0 || CORNER > 3) { - Log::logger->log(Log::ERR, "moveCursorToCorner, corner not 0 - 3."); + Debug::log(ERR, "moveCursorToCorner, corner not 0 - 3."); return {.success = false, .error = "moveCursorToCorner, corner not 0 - 3."}; } - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "Window not found"}; @@ -1734,20 +1827,20 @@ SDispatchResult CKeybindManager::moveCursorToCorner(std::string arg) { switch (CORNER) { case 0: // bottom left - g_pCompositor->warpCursorTo({PWINDOW->m_realPosition->value().x, PWINDOW->m_realPosition->value().y + PWINDOW->m_realSize->value().y}, true); + g_pCompositor->warpCursorTo({PWINDOW->m_vRealPosition->value().x, PWINDOW->m_vRealPosition->value().y + PWINDOW->m_vRealSize->value().y}, true); break; case 1: // bottom right - g_pCompositor->warpCursorTo({PWINDOW->m_realPosition->value().x + PWINDOW->m_realSize->value().x, PWINDOW->m_realPosition->value().y + PWINDOW->m_realSize->value().y}, - true); + g_pCompositor->warpCursorTo( + {PWINDOW->m_vRealPosition->value().x + PWINDOW->m_vRealSize->value().x, PWINDOW->m_vRealPosition->value().y + PWINDOW->m_vRealSize->value().y}, true); break; case 2: // top right - g_pCompositor->warpCursorTo({PWINDOW->m_realPosition->value().x + PWINDOW->m_realSize->value().x, PWINDOW->m_realPosition->value().y}, true); + g_pCompositor->warpCursorTo({PWINDOW->m_vRealPosition->value().x + PWINDOW->m_vRealSize->value().x, PWINDOW->m_vRealPosition->value().y}, true); break; case 3: // top left - g_pCompositor->warpCursorTo({PWINDOW->m_realPosition->value().x, PWINDOW->m_realPosition->value().y}, true); + g_pCompositor->warpCursorTo({PWINDOW->m_vRealPosition->value().x, PWINDOW->m_vRealPosition->value().y}, true); break; } @@ -1760,7 +1853,7 @@ SDispatchResult CKeybindManager::moveCursor(std::string args) { size_t i = args.find_first_of(' '); if (i == std::string::npos) { - Log::logger->log(Log::ERR, "moveCursor, takes 2 arguments."); + Debug::log(ERR, "moveCursor, takes 2 arguments."); return {.success = false, .error = "moveCursor, takes 2 arguments"}; } @@ -1768,11 +1861,11 @@ SDispatchResult CKeybindManager::moveCursor(std::string args) { y_str = args.substr(i + 1); if (!isNumber(x_str)) { - Log::logger->log(Log::ERR, "moveCursor, x argument has to be a number."); + Debug::log(ERR, "moveCursor, x argument has to be a number."); return {.success = false, .error = "moveCursor, x argument has to be a number."}; } if (!isNumber(y_str)) { - Log::logger->log(Log::ERR, "moveCursor, y argument has to be a number."); + Debug::log(ERR, "moveCursor, y argument has to be a number."); return {.success = false, .error = "moveCursor, y argument has to be a number."}; } @@ -1780,13 +1873,63 @@ SDispatchResult CKeybindManager::moveCursor(std::string args) { y = std::stoi(y_str); g_pCompositor->warpCursorTo({x, y}, true); - g_pInputManager->simulateMouseMovement(); return {}; } SDispatchResult CKeybindManager::workspaceOpt(std::string args) { - return {.success = false, .error = "workspaceopt is deprecated"}; + + // current workspace + const auto PWORKSPACE = g_pCompositor->m_pLastMonitor->activeWorkspace; + + if (!PWORKSPACE) + return {.success = false, .error = "Workspace not found"}; // ???? + + if (args == "allpseudo") { + PWORKSPACE->m_bDefaultPseudo = !PWORKSPACE->m_bDefaultPseudo; + + // apply + for (auto const& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsMapped || w->m_pWorkspace != PWORKSPACE) + continue; + + w->m_bIsPseudotiled = PWORKSPACE->m_bDefaultPseudo; + } + } else if (args == "allfloat") { + PWORKSPACE->m_bDefaultFloating = !PWORKSPACE->m_bDefaultFloating; + // apply + + // we make a copy because changeWindowFloatingMode might invalidate the iterator + std::vector ptrs(g_pCompositor->m_vWindows.begin(), g_pCompositor->m_vWindows.end()); + + for (auto const& w : ptrs) { + if (!w->m_bIsMapped || w->m_pWorkspace != PWORKSPACE || w->isHidden()) + continue; + + if (!w->m_bRequestsFloat && w->m_bIsFloating != PWORKSPACE->m_bDefaultFloating) { + const auto SAVEDPOS = w->m_vRealPosition->goal(); + const auto SAVEDSIZE = w->m_vRealSize->goal(); + + w->m_bIsFloating = PWORKSPACE->m_bDefaultFloating; + g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(w); + + if (PWORKSPACE->m_bDefaultFloating) { + w->m_vRealPosition->setValueAndWarp(SAVEDPOS); + w->m_vRealSize->setValueAndWarp(SAVEDSIZE); + *w->m_vRealSize = w->m_vRealSize->value() + Vector2D(4, 4); + *w->m_vRealPosition = w->m_vRealPosition->value() - Vector2D(2, 2); + } + } + } + } else { + Debug::log(ERR, "Invalid arg in workspaceOpt, opt \"{}\" doesn't exist.", args); + return {.success = false, .error = std::format("Invalid arg in workspaceOpt, opt \"{}\" doesn't exist.", args)}; + } + + // recalc mon + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(g_pCompositor->m_pLastMonitor->ID); + + return {}; } SDispatchResult CKeybindManager::renameWorkspace(std::string args) { @@ -1804,7 +1947,7 @@ SDispatchResult CKeybindManager::renameWorkspace(std::string args) { else return {.success = false, .error = "No such workspace"}; } catch (std::exception& e) { - Log::logger->log(Log::ERR, R"(Invalid arg in renameWorkspace, expected numeric id only or a numeric id and string name. "{}": "{}")", args, e.what()); + Debug::log(ERR, R"(Invalid arg in renameWorkspace, expected numeric id only or a numeric id and string name. "{}": "{}")", args, e.what()); return {.success = false, .error = std::format(R"(Invalid arg in renameWorkspace, expected numeric id only or a numeric id and string name. "{}": "{}")", args, e.what())}; } @@ -1814,7 +1957,7 @@ SDispatchResult CKeybindManager::renameWorkspace(std::string args) { SDispatchResult CKeybindManager::exitHyprland(std::string argz) { g_pConfigManager->dispatchExecShutdown(); - if (g_pCompositor->m_finalRequests) + if (g_pCompositor->m_bFinalRequests) return {}; // Exiting deferred until requests complete g_pCompositor->stopCompositor(); @@ -1825,14 +1968,14 @@ SDispatchResult CKeybindManager::moveCurrentWorkspaceToMonitor(std::string args) PHLMONITOR PMONITOR = g_pCompositor->getMonitorFromString(args); if (!PMONITOR) { - Log::logger->log(Log::ERR, "Ignoring moveCurrentWorkspaceToMonitor: monitor doesn't exist"); - return {.success = false, .error = "Ignoring moveCurrentWorkspaceToMonitor: monitor doesn't exist"}; + Debug::log(ERR, "Ignoring moveCurrentWorkspaceToMonitor: monitor doesnt exist"); + return {.success = false, .error = "Ignoring moveCurrentWorkspaceToMonitor: monitor doesnt exist"}; } // get the current workspace - const auto PCURRENTWORKSPACE = Desktop::focusState()->monitor()->m_activeWorkspace; + const auto PCURRENTWORKSPACE = g_pCompositor->m_pLastMonitor->activeWorkspace; if (!PCURRENTWORKSPACE) { - Log::logger->log(Log::ERR, "moveCurrentWorkspaceToMonitor invalid workspace!"); + Debug::log(ERR, "moveCurrentWorkspaceToMonitor invalid workspace!"); return {.success = false, .error = "moveCurrentWorkspaceToMonitor invalid workspace!"}; } @@ -1851,21 +1994,21 @@ SDispatchResult CKeybindManager::moveWorkspaceToMonitor(std::string args) { const auto PMONITOR = g_pCompositor->getMonitorFromString(monitor); if (!PMONITOR) { - Log::logger->log(Log::ERR, "Ignoring moveWorkspaceToMonitor: monitor doesn't exist"); - return {.success = false, .error = "Ignoring moveWorkspaceToMonitor: monitor doesn't exist"}; + Debug::log(ERR, "Ignoring moveWorkspaceToMonitor: monitor doesnt exist"); + return {.success = false, .error = "Ignoring moveWorkspaceToMonitor: monitor doesnt exist"}; } const auto WORKSPACEID = getWorkspaceIDNameFromString(workspace).id; if (WORKSPACEID == WORKSPACE_INVALID) { - Log::logger->log(Log::ERR, "moveWorkspaceToMonitor invalid workspace!"); + Debug::log(ERR, "moveWorkspaceToMonitor invalid workspace!"); return {.success = false, .error = "moveWorkspaceToMonitor invalid workspace!"}; } const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID); if (!PWORKSPACE) { - Log::logger->log(Log::ERR, "moveWorkspaceToMonitor workspace doesn't exist!"); + Debug::log(ERR, "moveWorkspaceToMonitor workspace doesn't exist!"); return {.success = false, .error = "moveWorkspaceToMonitor workspace doesn't exist!"}; } @@ -1875,44 +2018,44 @@ SDispatchResult CKeybindManager::moveWorkspaceToMonitor(std::string args) { } SDispatchResult CKeybindManager::focusWorkspaceOnCurrentMonitor(std::string args) { - auto [workspaceID, workspaceName, isAutoID] = getWorkspaceIDNameFromString(args); + auto [workspaceID, workspaceName] = getWorkspaceIDNameFromString(args); if (workspaceID == WORKSPACE_INVALID) { - Log::logger->log(Log::ERR, "focusWorkspaceOnCurrentMonitor invalid workspace!"); + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor invalid workspace!"); return {.success = false, .error = "focusWorkspaceOnCurrentMonitor invalid workspace!"}; } - const auto PCURRMONITOR = Desktop::focusState()->monitor(); + const auto PCURRMONITOR = g_pCompositor->m_pLastMonitor.lock(); if (!PCURRMONITOR) { - Log::logger->log(Log::ERR, "focusWorkspaceOnCurrentMonitor monitor doesn't exist!"); + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor monitor doesn't exist!"); return {.success = false, .error = "focusWorkspaceOnCurrentMonitor monitor doesn't exist!"}; } auto pWorkspace = g_pCompositor->getWorkspaceByID(workspaceID); if (!pWorkspace) { - pWorkspace = g_pCompositor->createNewWorkspace(workspaceID, PCURRMONITOR->m_id, workspaceName); + pWorkspace = g_pCompositor->createNewWorkspace(workspaceID, PCURRMONITOR->ID, workspaceName); // we can skip the moving, since it's already on the current monitor changeworkspace(pWorkspace->getConfigName()); return {}; } static auto PBACKANDFORTH = CConfigValue("binds:workspace_back_and_forth"); - const auto PREVWS = Desktop::History::workspaceTracker()->previousWorkspaceIDName(pWorkspace); + const auto PREVWS = pWorkspace->getPrevWorkspaceIDName(); if (*PBACKANDFORTH && PCURRMONITOR->activeWorkspaceID() == workspaceID && PREVWS.id != -1) { // Workspace to focus is previous workspace pWorkspace = g_pCompositor->getWorkspaceByID(PREVWS.id); if (!pWorkspace) - pWorkspace = g_pCompositor->createNewWorkspace(PREVWS.id, PCURRMONITOR->m_id, PREVWS.name); + pWorkspace = g_pCompositor->createNewWorkspace(PREVWS.id, PCURRMONITOR->ID, PREVWS.name); - workspaceID = pWorkspace->m_id; + workspaceID = pWorkspace->m_iID; } - if (pWorkspace->m_monitor != PCURRMONITOR) { - const auto POLDMONITOR = pWorkspace->m_monitor.lock(); + if (pWorkspace->m_pMonitor != PCURRMONITOR) { + const auto POLDMONITOR = pWorkspace->m_pMonitor.lock(); if (!POLDMONITOR) { // wat - Log::logger->log(Log::ERR, "focusWorkspaceOnCurrentMonitor old monitor doesn't exist!"); + Debug::log(ERR, "focusWorkspaceOnCurrentMonitor old monitor doesn't exist!"); return {.success = false, .error = "focusWorkspaceOnCurrentMonitor old monitor doesn't exist!"}; } if (POLDMONITOR->activeWorkspaceID() == workspaceID) { @@ -1929,53 +2072,35 @@ SDispatchResult CKeybindManager::focusWorkspaceOnCurrentMonitor(std::string args } SDispatchResult CKeybindManager::toggleSpecialWorkspace(std::string args) { - const auto& [workspaceID, workspaceName, isAutoID] = getWorkspaceIDNameFromString("special:" + args); + const auto& [workspaceID, workspaceName] = getWorkspaceIDNameFromString("special:" + args); if (workspaceID == WORKSPACE_INVALID || !g_pCompositor->isWorkspaceSpecial(workspaceID)) { - Log::logger->log(Log::ERR, "Invalid workspace passed to special"); + Debug::log(ERR, "Invalid workspace passed to special"); return {.success = false, .error = "Invalid workspace passed to special"}; } bool requestedWorkspaceIsAlreadyOpen = false; - const auto PMONITOR = Desktop::focusState()->monitor(); + const auto PMONITOR = g_pCompositor->m_pLastMonitor; auto specialOpenOnMonitor = PMONITOR->activeSpecialWorkspaceID(); - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { if (m->activeSpecialWorkspaceID() == workspaceID) { requestedWorkspaceIsAlreadyOpen = true; break; } } - updateRelativeCursorCoords(); - - PHLWORKSPACEREF focusedWorkspace; - if (requestedWorkspaceIsAlreadyOpen && specialOpenOnMonitor == workspaceID) { // already open on this monitor - Log::logger->log(Log::DEBUG, "Toggling special workspace {} to closed", workspaceID); + Debug::log(LOG, "Toggling special workspace {} to closed", workspaceID); PMONITOR->setSpecialWorkspace(nullptr); - - focusedWorkspace = PMONITOR->m_activeWorkspace; } else { - Log::logger->log(Log::DEBUG, "Toggling special workspace {} to open", workspaceID); + Debug::log(LOG, "Toggling special workspace {} to open", workspaceID); auto PSPECIALWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceID); if (!PSPECIALWORKSPACE) - PSPECIALWORKSPACE = g_pCompositor->createNewWorkspace(workspaceID, PMONITOR->m_id, workspaceName); + PSPECIALWORKSPACE = g_pCompositor->createNewWorkspace(workspaceID, PMONITOR->ID, workspaceName); PMONITOR->setSpecialWorkspace(PSPECIALWORKSPACE); - - focusedWorkspace = PSPECIALWORKSPACE; - } - - const static auto PWARPONTOGGLESPECIAL = CConfigValue("cursor:warp_on_toggle_special"); - - if (*PWARPONTOGGLESPECIAL > 0) { - auto PLAST = focusedWorkspace->getLastFocusedWindow(); - auto HLSurface = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.pointerFocus.lock()); - - if (PLAST && (!HLSurface || HLSurface->view()->type() == Desktop::View::VIEW_TYPE_WINDOW)) - PLAST->warpCursor(*PWARPONTOGGLESPECIAL == 2); } return {}; @@ -1984,8 +2109,8 @@ SDispatchResult CKeybindManager::toggleSpecialWorkspace(std::string args) { SDispatchResult CKeybindManager::forceRendererReload(std::string args) { bool overAgain = false; - for (auto const& m : g_pCompositor->m_monitors) { - if (!m->m_output) + for (auto const& m : g_pCompositor->m_vMonitors) { + if (!m->output) continue; auto rule = g_pConfigManager->getMonitorRuleFor(m); @@ -2002,7 +2127,7 @@ SDispatchResult CKeybindManager::forceRendererReload(std::string args) { } SDispatchResult CKeybindManager::resizeActive(std::string args) { - const auto PLASTWINDOW = Desktop::focusState()->window(); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PLASTWINDOW) return {.success = false, .error = "No window found"}; @@ -2010,21 +2135,21 @@ SDispatchResult CKeybindManager::resizeActive(std::string args) { if (PLASTWINDOW->isFullscreen()) return {.success = false, .error = "Window is fullscreen"}; - const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_realSize->goal()); + const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_vRealSize->goal()); if (SIZ.x < 1 || SIZ.y < 1) return {.success = false, .error = "Invalid size provided"}; - g_layoutManager->resizeTarget(SIZ - PLASTWINDOW->m_realSize->goal(), PLASTWINDOW->layoutTarget()); + g_pLayoutManager->getCurrentLayout()->resizeActiveWindow(SIZ - PLASTWINDOW->m_vRealSize->goal()); - if (PLASTWINDOW->m_realSize->goal().x > 1 && PLASTWINDOW->m_realSize->goal().y > 1) + if (PLASTWINDOW->m_vRealSize->goal().x > 1 && PLASTWINDOW->m_vRealSize->goal().y > 1) PLASTWINDOW->setHidden(false); return {}; } SDispatchResult CKeybindManager::moveActive(std::string args) { - const auto PLASTWINDOW = Desktop::focusState()->window(); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PLASTWINDOW) return {.success = false, .error = "No window found"}; @@ -2032,9 +2157,9 @@ SDispatchResult CKeybindManager::moveActive(std::string args) { if (PLASTWINDOW->isFullscreen()) return {.success = false, .error = "Window is fullscreen"}; - const auto POS = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_realPosition->goal()); + const auto POS = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_vRealPosition->goal()); - g_layoutManager->moveTarget(POS - PLASTWINDOW->m_realPosition->goal(), PLASTWINDOW->layoutTarget()); + g_pLayoutManager->getCurrentLayout()->moveActiveWindow(POS - PLASTWINDOW->m_vRealPosition->goal()); return {}; } @@ -2047,16 +2172,16 @@ SDispatchResult CKeybindManager::moveWindow(std::string args) { const auto PWINDOW = g_pCompositor->getWindowByRegex(WINDOWREGEX); if (!PWINDOW) { - Log::logger->log(Log::ERR, "moveWindow: no window"); + Debug::log(ERR, "moveWindow: no window"); return {.success = false, .error = "moveWindow: no window"}; } if (PWINDOW->isFullscreen()) return {.success = false, .error = "Window is fullscreen"}; - const auto POS = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_realPosition->goal()); + const auto POS = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_vRealPosition->goal()); - g_layoutManager->moveTarget(POS - PWINDOW->m_realPosition->goal(), PWINDOW->layoutTarget()); + g_pLayoutManager->getCurrentLayout()->moveActiveWindow(POS - PWINDOW->m_vRealPosition->goal(), PWINDOW); return {}; } @@ -2069,30 +2194,30 @@ SDispatchResult CKeybindManager::resizeWindow(std::string args) { const auto PWINDOW = g_pCompositor->getWindowByRegex(WINDOWREGEX); if (!PWINDOW) { - Log::logger->log(Log::ERR, "resizeWindow: no window"); + Debug::log(ERR, "resizeWindow: no window"); return {.success = false, .error = "resizeWindow: no window"}; } if (PWINDOW->isFullscreen()) return {.success = false, .error = "Window is fullscreen"}; - const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_realSize->goal()); + const auto SIZ = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_vRealSize->goal()); if (SIZ.x < 1 || SIZ.y < 1) return {.success = false, .error = "Invalid size provided"}; - g_layoutManager->resizeTarget(SIZ - PWINDOW->m_realSize->goal(), PWINDOW->layoutTarget(), Layout::CORNER_NONE); + g_pLayoutManager->getCurrentLayout()->resizeActiveWindow(SIZ - PWINDOW->m_vRealSize->goal(), CORNER_NONE, PWINDOW); - if (PWINDOW->m_realSize->goal().x > 1 && PWINDOW->m_realSize->goal().y > 1) + if (PWINDOW->m_vRealSize->goal().x > 1 && PWINDOW->m_vRealSize->goal().y > 1) PWINDOW->setHidden(false); return {}; } SDispatchResult CKeybindManager::circleNext(std::string arg) { - if (!Desktop::focusState()->window()) { + if (g_pCompositor->m_pLastWindow.expired()) { // if we have a clear focus, find the first window and get the next focusable. - const auto PWS = Desktop::focusState()->monitor()->m_activeWorkspace; + const auto PWS = g_pCompositor->m_pLastMonitor->activeWorkspace; if (PWS && PWS->getWindows() > 0) { const auto PWINDOW = PWS->getFirstWindow(); switchToWindow(PWINDOW); @@ -2103,36 +2228,18 @@ SDispatchResult CKeybindManager::circleNext(std::string arg) { CVarList args{arg, 0, 's', true}; - const auto PREV = args.contains("prev") || args.contains("p") || args.contains("last") || args.contains("l"); - std::optional floatStatus = {}; - if (args.contains("tile") || args.contains("tiled")) { - // if we want just tiled, and we are on a tiled window, use layoutmsg for layouts that support it - - if (!Desktop::focusState()->window()->m_isFloating) { - if (const auto SPACE = Desktop::focusState()->window()->layoutTarget()->space(); SPACE) { - - constexpr const std::array LAYOUTS_WITH_CYCLE_NEXT = { - &typeid(Layout::Tiled::CMonocleAlgorithm), - &typeid(Layout::Tiled::CMasterAlgorithm), - }; - - if (std::ranges::contains(LAYOUTS_WITH_CYCLE_NEXT, &typeid(*SPACE->algorithm()->tiledAlgo().get()))) { - CKeybindManager::layoutmsg(PREV ? "cyclenext, b" : "cyclenext"); - return {}; - } - } - } - } - - if (args.contains("float") || args.contains("floating")) + if (args.contains("tile") || args.contains("tiled")) + floatStatus = false; + else if (args.contains("float") || args.contains("floating")) floatStatus = true; const auto VISIBLE = args.contains("visible") || args.contains("v"); + const auto PREV = args.contains("prev") || args.contains("p") || args.contains("last") || args.contains("l"); const auto NEXT = args.contains("next") || args.contains("n"); // prev is default in classic alt+tab const auto HIST = args.contains("hist") || args.contains("h"); - const auto& w = HIST ? g_pCompositor->getWindowCycleHist(Desktop::focusState()->window(), true, floatStatus, VISIBLE, NEXT) : - g_pCompositor->getWindowCycle(Desktop::focusState()->window(), true, floatStatus, VISIBLE, PREV); + const auto& w = HIST ? g_pCompositor->getWindowCycleHist(g_pCompositor->m_pLastWindow, true, floatStatus, VISIBLE, NEXT) : + g_pCompositor->getWindowCycle(g_pCompositor->m_pLastWindow.lock(), true, floatStatus, VISIBLE, PREV); switchToWindow(w, HIST); @@ -2145,23 +2252,49 @@ SDispatchResult CKeybindManager::focusWindow(std::string regexp) { if (!PWINDOW) return {.success = false, .error = "No such window found"}; - Log::logger->log(Log::DEBUG, "Focusing to window name: {}", PWINDOW->m_title); + Debug::log(LOG, "Focusing to window name: {}", PWINDOW->m_szTitle); - const auto PWORKSPACE = PWINDOW->m_workspace; + const auto PWORKSPACE = PWINDOW->m_pWorkspace; if (!PWORKSPACE) { - Log::logger->log(Log::ERR, "BUG THIS: null workspace in focusWindow"); + Debug::log(ERR, "BUG THIS: null workspace in focusWindow"); return {.success = false, .error = "BUG THIS: null workspace in focusWindow"}; } updateRelativeCursorCoords(); - if (Desktop::focusState()->monitor() && Desktop::focusState()->monitor()->m_activeWorkspace != PWINDOW->m_workspace && - Desktop::focusState()->monitor()->m_activeSpecialWorkspace != PWINDOW->m_workspace) { - Log::logger->log(Log::DEBUG, "Fake executing workspace to move focus"); + if (g_pCompositor->m_pLastMonitor && g_pCompositor->m_pLastMonitor->activeWorkspace != PWINDOW->m_pWorkspace && + g_pCompositor->m_pLastMonitor->activeSpecialWorkspace != PWINDOW->m_pWorkspace) { + Debug::log(LOG, "Fake executing workspace to move focus"); changeworkspace(PWORKSPACE->getConfigName()); } - Desktop::focusState()->fullWindowFocus(PWINDOW, Desktop::FOCUS_REASON_KEYBIND, nullptr, false); + if (PWORKSPACE->m_bHasFullscreenWindow) { + const auto FSWINDOW = PWORKSPACE->getFullscreenWindow(); + const auto FSMODE = PWORKSPACE->m_efFullscreenMode; + + if (PWINDOW->m_bIsFloating) { + // don't make floating implicitly fs + if (!PWINDOW->m_bCreatedOverFullscreen) { + g_pCompositor->changeWindowZOrder(PWINDOW, true); + g_pCompositor->updateFullscreenFadeOnWorkspace(PWORKSPACE); + } + + g_pCompositor->focusWindow(PWINDOW); + } else { + if (FSWINDOW != PWINDOW && !PWINDOW->m_bPinned) + g_pCompositor->setWindowFullscreenClient(FSWINDOW, FSMODE_NONE); + + g_pCompositor->focusWindow(PWINDOW); + + if (FSWINDOW != PWINDOW && !PWINDOW->m_bPinned) + g_pCompositor->setWindowFullscreenClient(PWINDOW, FSMODE); + + // warp the position + size animation, otherwise it looks weird. + PWINDOW->m_vRealPosition->warp(); + PWINDOW->m_vRealSize->warp(); + } + } else + g_pCompositor->focusWindow(PWINDOW); PWINDOW->warpCursor(); @@ -2173,61 +2306,61 @@ SDispatchResult CKeybindManager::tagWindow(std::string args) { CVarList vars{args, 0, 's', true}; if (vars.size() == 1) - PWINDOW = Desktop::focusState()->window(); + PWINDOW = g_pCompositor->m_pLastWindow.lock(); else if (vars.size() == 2) PWINDOW = g_pCompositor->getWindowByRegex(vars[1]); else return {.success = false, .error = "Invalid number of arguments, expected 1 or 2 arguments"}; - if (PWINDOW && PWINDOW->m_ruleApplicator->m_tagKeeper.applyTag(vars[0])) { - PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TAG); - PWINDOW->updateDecorationValues(); + if (PWINDOW && PWINDOW->m_tags.applyTag(vars[0])) { + PWINDOW->updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW->m_pSelf.lock()); } return {}; } SDispatchResult CKeybindManager::toggleSwallow(std::string args) { - PHLWINDOWREF pWindow = Desktop::focusState()->window(); + PHLWINDOWREF pWindow = g_pCompositor->m_pLastWindow; - if (!valid(pWindow) || !valid(pWindow->m_swallowed)) + if (!valid(pWindow) || !valid(pWindow->m_pSwallowed)) return {}; - if (pWindow->m_swallowed->m_currentlySwallowed) { + if (pWindow->m_pSwallowed->m_bCurrentlySwallowed) { // Unswallow - pWindow->m_swallowed->m_currentlySwallowed = false; - pWindow->m_swallowed->setHidden(false); - g_layoutManager->newTarget(pWindow->m_swallowed->layoutTarget(), pWindow->m_workspace->m_space); + pWindow->m_pSwallowed->m_bCurrentlySwallowed = false; + pWindow->m_pSwallowed->setHidden(false); + g_pLayoutManager->getCurrentLayout()->onWindowCreated(pWindow->m_pSwallowed.lock()); } else { // Reswallow - pWindow->m_swallowed->m_currentlySwallowed = true; - pWindow->m_swallowed->setHidden(true); - g_layoutManager->removeTarget(pWindow->m_swallowed->layoutTarget()); + pWindow->m_pSwallowed->m_bCurrentlySwallowed = true; + pWindow->m_pSwallowed->setHidden(true); + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow->m_pSwallowed.lock()); } return {}; } SDispatchResult CKeybindManager::setSubmap(std::string submap) { - if (submap == "reset" || submap.empty()) { - m_currentSelectedSubmap.name = ""; - Log::logger->log(Log::DEBUG, "Reset active submap to the default one."); + if (submap == "reset" || submap == "") { + m_szCurrentSelectedSubmap = ""; + Debug::log(LOG, "Reset active submap to the default one."); g_pEventManager->postEvent(SHyprIPCEvent{"submap", ""}); - Event::bus()->m_events.keybinds.submap.emit(m_currentSelectedSubmap.name); + EMIT_HOOK_EVENT("submap", m_szCurrentSelectedSubmap); return {}; } - for (const auto& k : g_pKeybindManager->m_keybinds) { - if (k->submap.name == submap) { - m_currentSelectedSubmap.name = submap; - Log::logger->log(Log::DEBUG, "Changed keybind submap to {}", submap); + for (const auto& k : g_pKeybindManager->m_vKeybinds) { + if (k->submap == submap) { + m_szCurrentSelectedSubmap = submap; + Debug::log(LOG, "Changed keybind submap to {}", submap); g_pEventManager->postEvent(SHyprIPCEvent{"submap", submap}); - Event::bus()->m_events.keybinds.submap.emit(m_currentSelectedSubmap.name); + EMIT_HOOK_EVENT("submap", m_szCurrentSelectedSubmap); return {}; } } - Log::logger->log(Log::ERR, "Cannot set submap {}, submap doesn't exist (wasn't registered!)", submap); + Debug::log(ERR, "Cannot set submap {}, submap doesn't exist (wasn't registered!)", submap); return {.success = false, .error = std::format("Cannot set submap {}, submap doesn't exist (wasn't registered!)", submap)}; } @@ -2237,47 +2370,47 @@ SDispatchResult CKeybindManager::pass(std::string regexp) { const auto PWINDOW = g_pCompositor->getWindowByRegex(regexp); if (!PWINDOW) { - Log::logger->log(Log::ERR, "pass: window not found"); + Debug::log(ERR, "pass: window not found"); return {.success = false, .error = "pass: window not found"}; } - if (!g_pSeatManager->m_keyboard) { - Log::logger->log(Log::ERR, "No kb in pass?"); + if (!g_pSeatManager->keyboard) { + Debug::log(ERR, "No kb in pass?"); return {.success = false, .error = "No kb in pass?"}; } - const auto XWTOXW = PWINDOW->m_isX11 && Desktop::focusState()->window() && Desktop::focusState()->window()->m_isX11; - const auto LASTMOUSESURF = g_pSeatManager->m_state.pointerFocus.lock(); - const auto LASTKBSURF = g_pSeatManager->m_state.keyboardFocus.lock(); + const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsX11; + const auto LASTMOUSESURF = g_pSeatManager->state.pointerFocus.lock(); + const auto LASTKBSURF = g_pSeatManager->state.keyboardFocus.lock(); // pass all mf shit if (!XWTOXW) { - if (g_pKeybindManager->m_lastCode != 0) - g_pSeatManager->setKeyboardFocus(PWINDOW->wlSurface()->resource()); + if (g_pKeybindManager->m_uLastCode != 0) + g_pSeatManager->setKeyboardFocus(PWINDOW->m_pWLSurface->resource()); else - g_pSeatManager->setPointerFocus(PWINDOW->wlSurface()->resource(), {1, 1}); + g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface->resource(), {1, 1}); } - g_pSeatManager->sendKeyboardMods(g_pInputManager->getModsFromAllKBs(), 0, 0, 0); + g_pSeatManager->sendKeyboardMods(g_pInputManager->accumulateModsFromAllKBs(), 0, 0, 0); - if (g_pKeybindManager->m_passPressed == 1) { - if (g_pKeybindManager->m_lastCode != 0) - g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_timeLastMs, g_pKeybindManager->m_lastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + if (g_pKeybindManager->m_iPassPressed == 1) { + if (g_pKeybindManager->m_uLastCode != 0) + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); else - g_pSeatManager->sendPointerButton(g_pKeybindManager->m_timeLastMs, g_pKeybindManager->m_lastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); - } else if (g_pKeybindManager->m_passPressed == 0) - if (g_pKeybindManager->m_lastCode != 0) - g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_timeLastMs, g_pKeybindManager->m_lastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); + } else if (g_pKeybindManager->m_iPassPressed == 0) + if (g_pKeybindManager->m_uLastCode != 0) + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); else - g_pSeatManager->sendPointerButton(g_pKeybindManager->m_timeLastMs, g_pKeybindManager->m_lastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); else { // dynamic call of the dispatcher - if (g_pKeybindManager->m_lastCode != 0) { - g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_timeLastMs, g_pKeybindManager->m_lastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); - g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_timeLastMs, g_pKeybindManager->m_lastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + if (g_pKeybindManager->m_uLastCode != 0) { + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastCode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); } else { - g_pSeatManager->sendPointerButton(g_pKeybindManager->m_timeLastMs, g_pKeybindManager->m_lastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); - g_pSeatManager->sendPointerButton(g_pKeybindManager->m_timeLastMs, g_pKeybindManager->m_lastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_PRESSED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, g_pKeybindManager->m_uLastMouseCode, WL_POINTER_BUTTON_STATE_RELEASED); } } @@ -2287,19 +2420,19 @@ SDispatchResult CKeybindManager::pass(std::string regexp) { // Massive hack: // this will make g_pSeatManager NOT send the leave event to XWayland apps, provided we are not on an XWayland window already. // please kill me - if (PWINDOW->m_isX11) { - if (g_pKeybindManager->m_lastCode != 0) { - g_pSeatManager->m_state.keyboardFocus.reset(); - g_pSeatManager->m_state.keyboardFocusResource.reset(); + if (PWINDOW->m_bIsX11) { + if (g_pKeybindManager->m_uLastCode != 0) { + g_pSeatManager->state.keyboardFocus.reset(); + g_pSeatManager->state.keyboardFocusResource.reset(); } else { - g_pSeatManager->m_state.pointerFocus.reset(); - g_pSeatManager->m_state.pointerFocusResource.reset(); + g_pSeatManager->state.pointerFocus.reset(); + g_pSeatManager->state.pointerFocusResource.reset(); } } - const auto SL = PWINDOW->m_realPosition->goal() - g_pInputManager->getMouseCoordsInternal(); + const auto SL = PWINDOW->m_vRealPosition->goal() - g_pInputManager->getMouseCoordsInternal(); - if (g_pKeybindManager->m_lastCode != 0) + if (g_pKeybindManager->m_uLastCode != 0) g_pSeatManager->setKeyboardFocus(LASTKBSURF); else g_pSeatManager->setPointerFocus(LASTMOUSESURF, SL); @@ -2311,7 +2444,7 @@ SDispatchResult CKeybindManager::sendshortcut(std::string args) { // args=[,WINDOW_RULES] const auto ARGS = CVarList(args, 3); if (ARGS.size() != 3) { - Log::logger->log(Log::ERR, "sendshortcut: invalid args"); + Debug::log(ERR, "sendshortcut: invalid args"); return {.success = false, .error = "sendshortcut: invalid args"}; } @@ -2329,7 +2462,7 @@ SDispatchResult CKeybindManager::sendshortcut(std::string args) { keycode = std::stoi(KEY.substr(6)); isMouse = true; if (keycode < 272) { - Log::logger->log(Log::ERR, "sendshortcut: invalid mouse button"); + Debug::log(ERR, "sendshortcut: invalid mouse button"); return {.success = false, .error = "sendshortcut: invalid mouse button"}; } } else { @@ -2341,18 +2474,18 @@ SDispatchResult CKeybindManager::sendshortcut(std::string args) { const auto KEYSYM = xkb_keysym_from_name(KEY.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); keycode = 0; - const auto KB = g_pSeatManager->m_keyboard; + const auto KB = g_pSeatManager->keyboard; if (!KB) { - Log::logger->log(Log::ERR, "sendshortcut: no kb"); + Debug::log(ERR, "sendshortcut: no kb"); return {.success = false, .error = "sendshortcut: no kb"}; } - const auto KEYPAIRSTRING = std::format("{}{}", rc(KB.get()), KEY); + const auto KEYPAIRSTRING = std::format("{}{}", (uintptr_t)KB.get(), KEY); - if (!g_pKeybindManager->m_keyToCodeCache.contains(KEYPAIRSTRING)) { - xkb_keymap* km = KB->m_xkbKeymap; - xkb_state* ks = KB->m_xkbState; + if (!g_pKeybindManager->m_mKeyToCodeCache.contains(KEYPAIRSTRING)) { + xkb_keymap* km = KB->xkbKeymap; + xkb_state* ks = KB->xkbState; xkb_keycode_t keycode_min, keycode_max; keycode_min = xkb_keymap_min_keycode(km); @@ -2362,78 +2495,78 @@ SDispatchResult CKeybindManager::sendshortcut(std::string args) { xkb_keysym_t sym = xkb_state_key_get_one_sym(ks, kc); if (sym == KEYSYM) { - keycode = kc; - g_pKeybindManager->m_keyToCodeCache[KEYPAIRSTRING] = keycode; + keycode = kc; + g_pKeybindManager->m_mKeyToCodeCache[KEYPAIRSTRING] = keycode; } } if (!keycode) { - Log::logger->log(Log::ERR, "sendshortcut: key not found"); + Debug::log(ERR, "sendshortcut: key not found"); return {.success = false, .error = "sendshortcut: key not found"}; } } else - keycode = g_pKeybindManager->m_keyToCodeCache[KEYPAIRSTRING]; + keycode = g_pKeybindManager->m_mKeyToCodeCache[KEYPAIRSTRING]; } if (!keycode) { - Log::logger->log(Log::ERR, "sendshortcut: invalid key"); + Debug::log(ERR, "sendshortcut: invalid key"); return {.success = false, .error = "sendshortcut: invalid key"}; } const std::string regexp = ARGS[2]; PHLWINDOW PWINDOW = nullptr; - const auto LASTSURFACE = Desktop::focusState()->surface(); + const auto LASTSURFACE = g_pCompositor->m_pLastFocus.lock(); //if regexp is not empty, send shortcut to current window - //else, don't change focus - if (!regexp.empty()) { + //else, dont change focus + if (regexp != "") { PWINDOW = g_pCompositor->getWindowByRegex(regexp); if (!PWINDOW) { - Log::logger->log(Log::ERR, "sendshortcut: window not found"); + Debug::log(ERR, "sendshortcut: window not found"); return {.success = false, .error = "sendshortcut: window not found"}; } - if (!g_pSeatManager->m_keyboard) { - Log::logger->log(Log::ERR, "No kb in sendshortcut?"); + if (!g_pSeatManager->keyboard) { + Debug::log(ERR, "No kb in sendshortcut?"); return {.success = false, .error = "No kb in sendshortcut?"}; } if (!isMouse) - g_pSeatManager->setKeyboardFocus(PWINDOW->wlSurface()->resource()); + g_pSeatManager->setKeyboardFocus(PWINDOW->m_pWLSurface->resource()); else - g_pSeatManager->setPointerFocus(PWINDOW->wlSurface()->resource(), {1, 1}); + g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface->resource(), {1, 1}); } //copied the rest from pass and modified it // if wl -> xwl, activate destination - if (PWINDOW && PWINDOW->m_isX11 && Desktop::focusState()->window() && !Desktop::focusState()->window()->m_isX11) - g_pXWaylandManager->activateSurface(PWINDOW->wlSurface()->resource(), true); + if (PWINDOW && PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow && !g_pCompositor->m_pLastWindow->m_bIsX11) + g_pXWaylandManager->activateSurface(PWINDOW->m_pWLSurface->resource(), true); // if xwl -> xwl, send to current. Timing issues make this not work. - if (PWINDOW && PWINDOW->m_isX11 && Desktop::focusState()->window() && Desktop::focusState()->window()->m_isX11) + if (PWINDOW && PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->m_bIsX11) PWINDOW = nullptr; g_pSeatManager->sendKeyboardMods(MOD, 0, 0, 0); - if (g_pKeybindManager->m_passPressed == 1) { + if (g_pKeybindManager->m_iPassPressed == 1) { if (!isMouse) - g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_timeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); else - g_pSeatManager->sendPointerButton(g_pKeybindManager->m_timeLastMs, keycode, WL_POINTER_BUTTON_STATE_PRESSED); - } else if (g_pKeybindManager->m_passPressed == 0) { + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, keycode, WL_POINTER_BUTTON_STATE_PRESSED); + } else if (g_pKeybindManager->m_iPassPressed == 0) { if (!isMouse) - g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_timeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); else - g_pSeatManager->sendPointerButton(g_pKeybindManager->m_timeLastMs, keycode, WL_POINTER_BUTTON_STATE_RELEASED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, keycode, WL_POINTER_BUTTON_STATE_RELEASED); } else { // dynamic call of the dispatcher if (!isMouse) { - g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_timeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); - g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_timeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); } else { - g_pSeatManager->sendPointerButton(g_pKeybindManager->m_timeLastMs, keycode, WL_POINTER_BUTTON_STATE_PRESSED); - g_pSeatManager->sendPointerButton(g_pKeybindManager->m_timeLastMs, keycode, WL_POINTER_BUTTON_STATE_RELEASED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, keycode, WL_POINTER_BUTTON_STATE_PRESSED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, keycode, WL_POINTER_BUTTON_STATE_RELEASED); } } @@ -2442,17 +2575,17 @@ SDispatchResult CKeybindManager::sendshortcut(std::string args) { if (!PWINDOW) return {}; - if (PWINDOW->m_isX11) { //xwayland hack, see pass + if (PWINDOW->m_bIsX11) { //xwayland hack, see pass if (!isMouse) { - g_pSeatManager->m_state.keyboardFocus.reset(); - g_pSeatManager->m_state.keyboardFocusResource.reset(); + g_pSeatManager->state.keyboardFocus.reset(); + g_pSeatManager->state.keyboardFocusResource.reset(); } else { - g_pSeatManager->m_state.pointerFocus.reset(); - g_pSeatManager->m_state.pointerFocusResource.reset(); + g_pSeatManager->state.pointerFocus.reset(); + g_pSeatManager->state.pointerFocusResource.reset(); } } - const auto SL = PWINDOW->m_realPosition->goal() - g_pInputManager->getMouseCoordsInternal(); + const auto SL = PWINDOW->m_vRealPosition->goal() - g_pInputManager->getMouseCoordsInternal(); if (!isMouse) g_pSeatManager->setKeyboardFocus(LASTSURFACE); @@ -2463,9 +2596,9 @@ SDispatchResult CKeybindManager::sendshortcut(std::string args) { } SDispatchResult CKeybindManager::layoutmsg(std::string msg) { - auto ret = g_layoutManager->layoutMsg(msg); - if (!ret) - return {.success = false, .error = ret.error()}; + SLayoutMessageHeader hd = {g_pCompositor->m_pLastWindow.lock()}; + g_pLayoutManager->getCurrentLayout()->layoutMessage(hd, msg); + return {}; } @@ -2478,20 +2611,32 @@ SDispatchResult CKeybindManager::dpms(std::string arg) { if (arg.find_first_of(' ') != std::string::npos) port = arg.substr(arg.find_first_of(' ') + 1); - for (auto const& m : g_pCompositor->m_realMonitors) { - if (!m->m_enabled) - continue; + for (auto const& m : g_pCompositor->m_vMonitors) { - if (!port.empty() && m->m_name != port) + if (!port.empty() && m->szName != port) continue; if (isToggle) - enable = !m->m_dpmsStatus; + enable = !m->dpmsStatus; - m->setDPMS(enable); + m->output->state->resetExplicitFences(); + m->output->state->setEnabled(enable); + + m->dpmsStatus = enable; + + if (!m->state.commit()) { + Debug::log(ERR, "Couldn't commit output {}", m->szName); + res.success = false; + res.error = "Couldn't commit output {}"; + } + + if (enable) + g_pHyprRenderer->damageMonitor(m); + + m->events.dpmsChanged.emit(); } - g_pCompositor->m_dpmsStateOn = enable; + g_pCompositor->m_bDPMSStateON = enable; g_pPointerManager->recheckEnteredOutputs(); @@ -2502,14 +2647,14 @@ SDispatchResult CKeybindManager::swapnext(std::string arg) { PHLWINDOW toSwap = nullptr; - if (!Desktop::focusState()->window()) + if (g_pCompositor->m_pLastWindow.expired()) return {}; - const auto PLASTWINDOW = Desktop::focusState()->window(); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); const auto PLASTCYCLED = - validMapped(Desktop::focusState()->window()->m_lastCycledWindow) && Desktop::focusState()->window()->m_lastCycledWindow->m_workspace == PLASTWINDOW->m_workspace ? - Desktop::focusState()->window()->m_lastCycledWindow.lock() : + validMapped(g_pCompositor->m_pLastWindow->m_pLastCycledWindow) && g_pCompositor->m_pLastWindow->m_pLastCycledWindow->m_pWorkspace == PLASTWINDOW->m_pWorkspace ? + g_pCompositor->m_pLastWindow->m_pLastCycledWindow.lock() : nullptr; const bool NEED_PREV = arg == "last" || arg == "l" || arg == "prev" || arg == "p"; @@ -2519,11 +2664,11 @@ SDispatchResult CKeybindManager::swapnext(std::string arg) { if (toSwap == PLASTWINDOW) toSwap = g_pCompositor->getWindowCycle(PLASTWINDOW, true, std::nullopt, false, NEED_PREV); - g_layoutManager->switchTargets(PLASTWINDOW->layoutTarget(), toSwap->layoutTarget(), false); + g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, toSwap); - PLASTWINDOW->m_lastCycledWindow = toSwap; + PLASTWINDOW->m_pLastCycledWindow = toSwap; - Desktop::focusState()->fullWindowFocus(PLASTWINDOW, Desktop::FOCUS_REASON_KEYBIND); + g_pCompositor->focusWindow(PLASTWINDOW); return {}; } @@ -2553,38 +2698,36 @@ SDispatchResult CKeybindManager::pinActive(std::string args) { if (args != "active" && args.length() > 1) PWINDOW = g_pCompositor->getWindowByRegex(args); else - PWINDOW = Desktop::focusState()->window(); + PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) { - Log::logger->log(Log::ERR, "pin: window not found"); + Debug::log(ERR, "pin: window not found"); return {.success = false, .error = "pin: window not found"}; } - if (!PWINDOW->m_isFloating || PWINDOW->isFullscreen()) + if (!PWINDOW->m_bIsFloating || PWINDOW->isFullscreen()) return {.success = false, .error = "Window does not qualify to be pinned"}; - PWINDOW->m_pinned = !PWINDOW->m_pinned; + PWINDOW->m_bPinned = !PWINDOW->m_bPinned; - const auto PMONITOR = PWINDOW->m_monitor.lock(); + const auto PMONITOR = PWINDOW->m_pMonitor.lock(); if (!PMONITOR) { - Log::logger->log(Log::ERR, "pin: monitor not found"); + Debug::log(ERR, "pin: monitor not found"); return {.success = false, .error = "pin: window not found"}; } - PWINDOW->moveToWorkspace(PMONITOR->m_activeWorkspace); + PWINDOW->m_pWorkspace = PMONITOR->activeWorkspace; - PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_PINNED); + PWINDOW->updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); - const auto PWORKSPACE = PWINDOW->m_workspace; + const auto PWORKSPACE = PWINDOW->m_pWorkspace; - PWORKSPACE->m_lastFocusedWindow = - g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS); + PWORKSPACE->m_pLastFocusedWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS); - g_pEventManager->postEvent(SHyprIPCEvent{"pin", std::format("{:x},{}", rc(PWINDOW.get()), sc(PWINDOW->m_pinned))}); - Event::bus()->m_events.window.pin.emit(PWINDOW); - - g_pHyprRenderer->damageWindow(PWINDOW, true); + g_pEventManager->postEvent(SHyprIPCEvent{"pin", std::format("{:x},{}", (uintptr_t)PWINDOW.get(), (int)PWINDOW->m_bPinned)}); + EMIT_HOOK_EVENT("pin", PWINDOW); return {}; } @@ -2612,36 +2755,38 @@ SDispatchResult CKeybindManager::mouse(std::string args) { SDispatchResult CKeybindManager::changeMouseBindMode(const eMouseBindMode MODE) { if (MODE != MBIND_INVALID) { - if (g_layoutManager->dragController()->target()) + if (!g_pInputManager->currentlyDraggedWindow.expired() || g_pInputManager->dragMode != MBIND_INVALID) return {}; const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); - const PHLWINDOW PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); + const PHLWINDOW PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (!PWINDOW) return SDispatchResult{.passEvent = true}; - if (!PWINDOW->isFullscreen() && MODE == MBIND_MOVE) { - if (PWINDOW->checkInputOnDecos(INPUT_TYPE_DRAG_START, MOUSECOORDS)) - return SDispatchResult{.passEvent = false}; - } + if (!PWINDOW->isFullscreen() && MODE == MBIND_MOVE) + PWINDOW->checkInputOnDecos(INPUT_TYPE_DRAG_START, MOUSECOORDS); - g_layoutManager->beginDragTarget(PWINDOW->layoutTarget(), MODE); + if (g_pInputManager->currentlyDraggedWindow.expired()) + g_pInputManager->currentlyDraggedWindow = PWINDOW; + + g_pInputManager->dragMode = MODE; + + g_pLayoutManager->getCurrentLayout()->onBeginDragWindow(); } else { - if (!g_layoutManager->dragController()->target()) + if (g_pInputManager->currentlyDraggedWindow.expired() || g_pInputManager->dragMode == MBIND_INVALID) return {}; - g_layoutManager->endDragTarget(); + g_pLayoutManager->getCurrentLayout()->onEndDragWindow(); + g_pInputManager->dragMode = MODE; } return {}; } SDispatchResult CKeybindManager::bringActiveToTop(std::string args) { - if (Desktop::focusState()->window() && Desktop::focusState()->window()->m_isFloating) - g_pCompositor->changeWindowZOrder(Desktop::focusState()->window(), true); - - g_pInputManager->simulateMouseMovement(); + if (g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsFloating) + g_pCompositor->changeWindowZOrder(g_pCompositor->m_pLastWindow.lock(), true); return {}; } @@ -2651,11 +2796,11 @@ SDispatchResult CKeybindManager::alterZOrder(std::string args) { const auto POSITION = args.substr(0, args.find_first_of(',')); auto PWINDOW = g_pCompositor->getWindowByRegex(WINDOWREGEX); - if (!PWINDOW && Desktop::focusState()->window() && Desktop::focusState()->window()->m_isFloating) - PWINDOW = Desktop::focusState()->window(); + if (!PWINDOW && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsFloating) + PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) { - Log::logger->log(Log::ERR, "alterZOrder: no window"); + Debug::log(ERR, "alterZOrder: no window"); return {.success = false, .error = "alterZOrder: no window"}; } @@ -2664,7 +2809,7 @@ SDispatchResult CKeybindManager::alterZOrder(std::string args) { else if (POSITION == "bottom") g_pCompositor->changeWindowZOrder(PWINDOW, false); else { - Log::logger->log(Log::ERR, "alterZOrder: bad position: {}", POSITION); + Debug::log(ERR, "alterZOrder: bad position: {}", POSITION); return {.success = false, .error = "alterZOrder: bad position: {}"}; } @@ -2675,109 +2820,135 @@ SDispatchResult CKeybindManager::alterZOrder(std::string args) { SDispatchResult CKeybindManager::lockGroups(std::string args) { if (args == "lock" || args.empty() || args == "lockgroups") - g_pKeybindManager->m_groupsLocked = true; + g_pKeybindManager->m_bGroupsLocked = true; else if (args == "toggle") - g_pKeybindManager->m_groupsLocked = !g_pKeybindManager->m_groupsLocked; + g_pKeybindManager->m_bGroupsLocked = !g_pKeybindManager->m_bGroupsLocked; else - g_pKeybindManager->m_groupsLocked = false; + g_pKeybindManager->m_bGroupsLocked = false; - g_pEventManager->postEvent(SHyprIPCEvent{"lockgroups", g_pKeybindManager->m_groupsLocked ? "1" : "0"}); + g_pEventManager->postEvent(SHyprIPCEvent{"lockgroups", g_pKeybindManager->m_bGroupsLocked ? "1" : "0"}); g_pCompositor->updateAllWindowsAnimatedDecorationValues(); return {}; } SDispatchResult CKeybindManager::lockActiveGroup(std::string args) { - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "No window found"}; - if (!PWINDOW->m_group) + if (!PWINDOW->m_sGroupData.pNextWindow.lock()) return {.success = false, .error = "Not a group"}; - if (args == "lock") - PWINDOW->m_group->setLocked(true); - else if (args == "toggle") - PWINDOW->m_group->setLocked(!PWINDOW->m_group->locked()); - else - PWINDOW->m_group->setLocked(false); + const auto PHEAD = PWINDOW->getGroupHead(); - PWINDOW->updateDecorationValues(); + if (args == "lock") + PHEAD->m_sGroupData.locked = true; + else if (args == "toggle") + PHEAD->m_sGroupData.locked = !PHEAD->m_sGroupData.locked; + else + PHEAD->m_sGroupData.locked = false; + + g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); return {}; } void CKeybindManager::moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowInDirection) { - if (!pWindowInDirection->m_group || pWindowInDirection->m_group->denied()) + if (pWindow->m_sGroupData.deny) return; updateRelativeCursorCoords(); - if (pWindow->m_monitor != pWindowInDirection->m_monitor) { - pWindow->moveToWorkspace(pWindowInDirection->m_workspace); - pWindow->m_monitor = pWindowInDirection->m_monitor; + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); // This removes groupped property! + + if (pWindow->m_pMonitor != pWindowInDirection->m_pMonitor) { + pWindow->moveToWorkspace(pWindowInDirection->m_pWorkspace); + pWindow->m_pMonitor = pWindowInDirection->m_pMonitor; } - pWindowInDirection->m_group->add(pWindow); + static auto USECURRPOS = CConfigValue("group:insert_after_current"); + (*USECURRPOS ? pWindowInDirection : pWindowInDirection->getGroupTail())->insertWindowToGroup(pWindow); - pWindowInDirection->m_group->setCurrent(pWindow); + pWindowInDirection->setGroupCurrent(pWindow); pWindow->updateWindowDecos(); - Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND); + g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow); + g_pCompositor->focusWindow(pWindow); pWindow->warpCursor(); - g_pEventManager->postEvent(SHyprIPCEvent{"moveintogroup", std::format("{:x}", rc(pWindow.get()))}); + if (!pWindow->getDecorationByType(DECORATION_GROUPBAR)) + pWindow->addWindowDeco(makeUnique(pWindow)); + + g_pEventManager->postEvent(SHyprIPCEvent{"moveintogroup", std::format("{:x}", (uintptr_t)pWindow.get())}); } void CKeybindManager::moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string& dir) { static auto BFOCUSREMOVEDWINDOW = CConfigValue("group:focus_removed_window"); + const auto PWINDOWPREV = pWindow->getGroupPrevious(); + eDirection direction; - if (!pWindow->m_group) - return; - - WP group = pWindow->m_group; - - const auto direction = !dir.empty() ? Math::fromChar(dir[0]) : Math::DIRECTION_DEFAULT; - - pWindow->m_group->remove(pWindow, direction); - - if (*BFOCUSREMOVEDWINDOW || !group) { - Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND); - pWindow->warpCursor(); - } else { - Desktop::focusState()->fullWindowFocus(group->current(), Desktop::FOCUS_REASON_KEYBIND); - group->current()->warpCursor(); + switch (dir[0]) { + case 't': + case 'u': direction = DIRECTION_UP; break; + case 'd': + case 'b': direction = DIRECTION_DOWN; break; + case 'l': direction = DIRECTION_LEFT; break; + case 'r': direction = DIRECTION_RIGHT; break; + default: direction = DIRECTION_DEFAULT; } - g_pEventManager->postEvent(SHyprIPCEvent{"moveoutofgroup", std::format("{:x}", rc(pWindow.get()))}); + updateRelativeCursorCoords(); + + if (pWindow->m_sGroupData.pNextWindow.lock() == pWindow) { + pWindow->destroyGroup(); + } else { + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); + + const auto GROUPSLOCKEDPREV = g_pKeybindManager->m_bGroupsLocked; + g_pKeybindManager->m_bGroupsLocked = true; + + g_pLayoutManager->getCurrentLayout()->onWindowCreated(pWindow, direction); + + g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV; + } + + if (*BFOCUSREMOVEDWINDOW) { + g_pCompositor->focusWindow(pWindow); + pWindow->warpCursor(); + } else { + g_pCompositor->focusWindow(PWINDOWPREV); + PWINDOWPREV->warpCursor(); + } + + g_pEventManager->postEvent(SHyprIPCEvent{"moveoutofgroup", std::format("{:x}", (uintptr_t)pWindow.get())}); } SDispatchResult CKeybindManager::moveIntoGroup(std::string args) { + char arg = args[0]; + static auto PIGNOREGROUPLOCK = CConfigValue("binds:ignore_group_lock"); - if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked) + if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) return {}; - Math::eDirection dir = Math::fromChar(args[0]); - if (dir == Math::DIRECTION_DEFAULT) { - Log::logger->log(Log::ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]); - return {.success = false, .error = std::format("Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0])}; + if (!isDirection(args)) { + Debug::log(ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg); + return {.success = false, .error = std::format("Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)}; } - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); - if (!PWINDOW) + if (!PWINDOW || PWINDOW->m_sGroupData.deny) return {}; - auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, dir); + auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, arg); - if (!PWINDOWINDIR || !PWINDOWINDIR->m_group) + if (!PWINDOWINDIR || !PWINDOWINDIR->m_sGroupData.pNextWindow.lock()) return {}; - const auto GROUP = PWINDOWINDIR->m_group; - // Do not move window into locked group if binds:ignore_group_lock is false - if (!*PIGNOREGROUPLOCK && (GROUP->locked() || (PWINDOW->m_group && PWINDOW->m_group->locked()))) + if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_sGroupData.locked || (PWINDOW->m_sGroupData.pNextWindow.lock() && PWINDOW->getGroupHead()->m_sGroupData.locked))) return {}; moveWindowIntoGroup(PWINDOW, PWINDOWINDIR); @@ -2788,7 +2959,7 @@ SDispatchResult CKeybindManager::moveIntoGroup(std::string args) { SDispatchResult CKeybindManager::moveOutOfGroup(std::string args) { static auto PIGNOREGROUPLOCK = CConfigValue("binds:ignore_group_lock"); - if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked) + if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) return {.success = false, .error = "Groups locked"}; PHLWINDOW PWINDOW = nullptr; @@ -2796,12 +2967,12 @@ SDispatchResult CKeybindManager::moveOutOfGroup(std::string args) { if (args != "active" && args.length() > 1) PWINDOW = g_pCompositor->getWindowByRegex(args); else - PWINDOW = Desktop::focusState()->window(); + PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "No window found"}; - if (!PWINDOW->m_group) + if (!PWINDOW->m_sGroupData.pNextWindow.lock()) return {.success = false, .error = "Window not in a group"}; moveWindowOutOfGroup(PWINDOW); @@ -2810,62 +2981,62 @@ SDispatchResult CKeybindManager::moveOutOfGroup(std::string args) { } SDispatchResult CKeybindManager::moveWindowOrGroup(std::string args) { - static auto PIGNOREGROUPLOCK = CConfigValue("binds:ignore_group_lock"); + char arg = args[0]; - Math::eDirection dir = Math::fromChar(args[0]); - if (dir == Math::DIRECTION_DEFAULT) { - Log::logger->log(Log::ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]); - return {.success = false, .error = std::format("Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0])}; + static auto PIGNOREGROUPLOCK = CConfigValue("binds:ignore_group_lock"); + + if (!isDirection(args)) { + Debug::log(ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg); + return {.success = false, .error = std::format("Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)}; } - const auto PWINDOW = Desktop::focusState()->window(); + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PWINDOW) return {.success = false, .error = "No window found"}; if (PWINDOW->isFullscreen()) return {}; - if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked) { - g_layoutManager->moveInDirection(PWINDOW->layoutTarget(), args); + if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_bGroupsLocked) { + g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); return {}; } - const auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, dir); + const auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, arg); - const bool ISWINDOWGROUP = PWINDOW->m_group; - const bool ISWINDOWGROUPLOCKED = ISWINDOWGROUP && PWINDOW->m_group->locked(); - const bool ISWINDOWGROUPSINGLE = ISWINDOWGROUP && PWINDOW->m_group->size() == 1; - const bool ISWINDOWGROUPDENIED = ISWINDOWGROUP && PWINDOW->m_group->denied(); + const bool ISWINDOWGROUP = PWINDOW->m_sGroupData.pNextWindow; + const bool ISWINDOWGROUPLOCKED = ISWINDOWGROUP && PWINDOW->getGroupHead()->m_sGroupData.locked; + const bool ISWINDOWGROUPSINGLE = ISWINDOWGROUP && PWINDOW->m_sGroupData.pNextWindow.lock() == PWINDOW; updateRelativeCursorCoords(); - // note: PWINDOWINDIR is not null implies !PWINDOW->m_isFloating - if (PWINDOWINDIR && PWINDOWINDIR->m_group) { // target is group - if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->m_group->locked() || ISWINDOWGROUPLOCKED || ISWINDOWGROUPDENIED)) { - g_layoutManager->moveInDirection(PWINDOW->layoutTarget(), args); + // note: PWINDOWINDIR is not null implies !PWINDOW->m_bIsFloating + if (PWINDOWINDIR && PWINDOWINDIR->m_sGroupData.pNextWindow) { // target is group + if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_sGroupData.locked || ISWINDOWGROUPLOCKED || PWINDOW->m_sGroupData.deny)) { + g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); PWINDOW->warpCursor(); } else moveWindowIntoGroup(PWINDOW, PWINDOWINDIR); } else if (PWINDOWINDIR) { // target is regular window - if ((!*PIGNOREGROUPLOCK && ISWINDOWGROUPLOCKED) || !ISWINDOWGROUP || (ISWINDOWGROUPSINGLE && PWINDOW->m_groupRules & Desktop::View::GROUP_SET_ALWAYS)) { - g_layoutManager->moveInDirection(PWINDOW->layoutTarget(), args); + if ((!*PIGNOREGROUPLOCK && ISWINDOWGROUPLOCKED) || !ISWINDOWGROUP || (ISWINDOWGROUPSINGLE && PWINDOW->m_eGroupRules & GROUP_SET_ALWAYS)) { + g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); PWINDOW->warpCursor(); } else moveWindowOutOfGroup(PWINDOW, args); } else if ((*PIGNOREGROUPLOCK || !ISWINDOWGROUPLOCKED) && ISWINDOWGROUP) { // no target window moveWindowOutOfGroup(PWINDOW, args); } else if (!PWINDOWINDIR && !ISWINDOWGROUP) { // no target in dir and not in group - g_layoutManager->moveInDirection(PWINDOW->layoutTarget(), args); + g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); PWINDOW->warpCursor(); } - PWINDOW->updateDecorationValues(); + g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); return {}; } SDispatchResult CKeybindManager::setIgnoreGroupLock(std::string args) { - static auto PIGNOREGROUPLOCK = rc(g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock")); + static auto PIGNOREGROUPLOCK = (Hyprlang::INT* const*)g_pConfigManager->getConfigValuePtr("binds:ignore_group_lock"); if (args == "toggle") **PIGNOREGROUPLOCK = !**PIGNOREGROUPLOCK; @@ -2878,16 +3049,16 @@ SDispatchResult CKeybindManager::setIgnoreGroupLock(std::string args) { } SDispatchResult CKeybindManager::denyWindowFromGroup(std::string args) { - const auto PWINDOW = Desktop::focusState()->window(); - if (!PWINDOW || (PWINDOW && PWINDOW->m_group)) + const auto PWINDOW = g_pCompositor->m_pLastWindow.lock(); + if (!PWINDOW || (PWINDOW && PWINDOW->m_sGroupData.pNextWindow.lock())) return {}; if (args == "toggle") - PWINDOW->m_group->setDenied(!PWINDOW->m_group->denied()); + PWINDOW->m_sGroupData.deny = !PWINDOW->m_sGroupData.deny; else - PWINDOW->m_group->setDenied(args == "on"); + PWINDOW->m_sGroupData.deny = args == "on"; - PWINDOW->updateDecorationValues(); + g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); return {}; } @@ -2902,7 +3073,7 @@ SDispatchResult CKeybindManager::global(std::string args) { if (!PROTO::globalShortcuts->isTaken(APPID, NAME)) return {}; - PROTO::globalShortcuts->sendGlobalShortcutEvent(APPID, NAME, g_pKeybindManager->m_passPressed); + PROTO::globalShortcuts->sendGlobalShortcutEvent(APPID, NAME, g_pKeybindManager->m_iPassPressed); return {}; } @@ -2910,20 +3081,21 @@ SDispatchResult CKeybindManager::global(std::string args) { SDispatchResult CKeybindManager::moveGroupWindow(std::string args) { const auto BACK = args == "b" || args == "prev"; - const auto PLASTWINDOW = Desktop::focusState()->window(); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); if (!PLASTWINDOW) return {.success = false, .error = "No window found"}; - if (!PLASTWINDOW->m_group) + if (!PLASTWINDOW->m_sGroupData.pNextWindow.lock()) return {.success = false, .error = "Window not in a group"}; - const auto GROUP = PLASTWINDOW->m_group; + if ((!BACK && PLASTWINDOW->m_sGroupData.pNextWindow->m_sGroupData.head) || (BACK && PLASTWINDOW->m_sGroupData.head)) { + std::swap(PLASTWINDOW->m_sGroupData.head, PLASTWINDOW->m_sGroupData.pNextWindow->m_sGroupData.head); + std::swap(PLASTWINDOW->m_sGroupData.locked, PLASTWINDOW->m_sGroupData.pNextWindow->m_sGroupData.locked); + } else + PLASTWINDOW->switchWithWindowInGroup(BACK ? PLASTWINDOW->getGroupPrevious() : PLASTWINDOW->m_sGroupData.pNextWindow.lock()); - if (BACK) - GROUP->swapWithLast(); - else - GROUP->swapWithNext(); + PLASTWINDOW->updateWindowDecos(); return {}; } @@ -2936,46 +3108,13 @@ SDispatchResult CKeybindManager::event(std::string args) { #include #include -template -static void parsePropTrivial(Desktop::Types::COverridableVar& prop, const std::string& s) { - static_assert(std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v, - "Invalid type passed to parsePropTrivial"); - - if (s == "unset") { - prop.unset(Desktop::Types::PRIORITY_SET_PROP); - return; - } - - try { - if constexpr (std::is_same_v) { - if (s == "toggle") - prop.increment(true, Desktop::Types::PRIORITY_SET_PROP); - else - prop = Desktop::Types::COverridableVar(truthy(s), Desktop::Types::PRIORITY_SET_PROP); - } else if constexpr (std::is_same_v || std::is_same_v) { - if (s.starts_with("relative")) { - const auto VAL = std::stoi(s.substr(s.find(' ') + 1)); - prop.increment(VAL, Desktop::Types::PRIORITY_SET_PROP); - } else - prop = Desktop::Types::COverridableVar(std::stoull(s), Desktop::Types::PRIORITY_SET_PROP); - } else if constexpr (std::is_same_v) { - if (s.starts_with("relative")) { - const auto VAL = std::stof(s.substr(s.find(' ') + 1)); - prop.increment(VAL, Desktop::Types::PRIORITY_SET_PROP); - } else - prop = Desktop::Types::COverridableVar(std::stof(s), Desktop::Types::PRIORITY_SET_PROP); - } else if constexpr (std::is_same_v) - prop = Desktop::Types::COverridableVar(s, Desktop::Types::PRIORITY_SET_PROP); - } catch (...) { Log::logger->log(Log::ERR, "Hyprctl: parsePropTrivial: failed to parse setprop for {}", s); } -} - SDispatchResult CKeybindManager::setProp(std::string args) { CVarList vars(args, 3, ' '); if (vars.size() < 3) return {.success = false, .error = "Not enough args"}; - const auto PLASTWINDOW = Desktop::focusState()->window(); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); const auto PWINDOW = g_pCompositor->getWindowByRegex(vars[0]); if (!PWINDOW) @@ -2984,211 +3123,95 @@ SDispatchResult CKeybindManager::setProp(std::string args) { const auto PROP = vars[1]; const auto VAL = vars[2]; - bool noFocus = PWINDOW->m_ruleApplicator->noFocus().valueOrDefault(); + bool noFocus = PWINDOW->m_sWindowData.noFocus.valueOrDefault(); try { - if (PROP == "max_size") { - const auto SIZE = PWINDOW->calculateExpression(VAL); - if (!SIZE) { - Log::logger->log(Log::ERR, "failed to parse {} as an expression", VAL); - throw "failed to parse expression"; - } - PWINDOW->m_ruleApplicator->maxSizeOverride(Desktop::Types::COverridableVar(*SIZE, Desktop::Types::PRIORITY_SET_PROP)); - PWINDOW->clampWindowSize(std::nullopt, PWINDOW->m_ruleApplicator->maxSize().value()); + if (PROP == "animationstyle") { + PWINDOW->m_sWindowData.animationStyle = CWindowOverridableVar(VAL, PRIORITY_SET_PROP); + } else if (PROP == "maxsize") { + PWINDOW->m_sWindowData.maxSize = CWindowOverridableVar(configStringToVector2D(VAL), PRIORITY_SET_PROP); + PWINDOW->clampWindowSize(std::nullopt, PWINDOW->m_sWindowData.maxSize.value()); PWINDOW->setHidden(false); - } else if (PROP == "min_size") { - const auto SIZE = PWINDOW->calculateExpression(VAL); - if (!SIZE) { - Log::logger->log(Log::ERR, "failed to parse {} as an expression", VAL); - throw "failed to parse expression"; - } - PWINDOW->m_ruleApplicator->minSizeOverride(Desktop::Types::COverridableVar(*SIZE, Desktop::Types::PRIORITY_SET_PROP)); - PWINDOW->clampWindowSize(PWINDOW->m_ruleApplicator->minSize().value(), std::nullopt); + } else if (PROP == "minsize") { + PWINDOW->m_sWindowData.minSize = CWindowOverridableVar(configStringToVector2D(VAL), PRIORITY_SET_PROP); + PWINDOW->clampWindowSize(PWINDOW->m_sWindowData.minSize.value(), std::nullopt); PWINDOW->setHidden(false); - } else if (PROP == "active_border_color" || PROP == "inactive_border_color") { + } else if (PROP == "alpha") { + PWINDOW->m_sWindowData.alpha = CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_sWindowData.alpha.valueOrDefault().m_bOverride}, PRIORITY_SET_PROP); + } else if (PROP == "alphainactive") { + PWINDOW->m_sWindowData.alphaInactive = + CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_sWindowData.alphaInactive.valueOrDefault().m_bOverride}, PRIORITY_SET_PROP); + } else if (PROP == "alphafullscreen") { + PWINDOW->m_sWindowData.alphaFullscreen = + CWindowOverridableVar(SAlphaValue{std::stof(VAL), PWINDOW->m_sWindowData.alphaFullscreen.valueOrDefault().m_bOverride}, PRIORITY_SET_PROP); + } else if (PROP == "alphaoverride") { + PWINDOW->m_sWindowData.alpha = + CWindowOverridableVar(SAlphaValue{PWINDOW->m_sWindowData.alpha.valueOrDefault().m_fAlpha, (bool)configStringToInt(VAL).value_or(0)}, PRIORITY_SET_PROP); + } else if (PROP == "alphainactiveoverride") { + PWINDOW->m_sWindowData.alphaInactive = + CWindowOverridableVar(SAlphaValue{PWINDOW->m_sWindowData.alphaInactive.valueOrDefault().m_fAlpha, (bool)configStringToInt(VAL).value_or(0)}, PRIORITY_SET_PROP); + } else if (PROP == "alphafullscreenoverride") { + PWINDOW->m_sWindowData.alphaFullscreen = + CWindowOverridableVar(SAlphaValue{PWINDOW->m_sWindowData.alphaFullscreen.valueOrDefault().m_fAlpha, (bool)configStringToInt(VAL).value_or(0)}, PRIORITY_SET_PROP); + } else if (PROP == "activebordercolor" || PROP == "inactivebordercolor") { CGradientValueData colorData = {}; if (vars.size() > 4) { - for (int i = 3; i < sc(vars.size()); ++i) { + for (int i = 3; i < static_cast(vars.size()); ++i) { const auto TOKEN = vars[i]; if (TOKEN.ends_with("deg")) - colorData.m_angle = std::stoi(TOKEN.substr(0, TOKEN.size() - 3)) * (PI / 180.0); + colorData.m_fAngle = std::stoi(TOKEN.substr(0, TOKEN.size() - 3)) * (PI / 180.0); else configStringToInt(TOKEN).and_then([&colorData](const auto& e) { - colorData.m_colors.push_back(e); + colorData.m_vColors.push_back(e); return std::invoke_result_t(1); }); } } else if (VAL != "-1") configStringToInt(VAL).and_then([&colorData](const auto& e) { - colorData.m_colors.push_back(e); + colorData.m_vColors.push_back(e); return std::invoke_result_t(1); }); colorData.updateColorsOk(); - if (PROP == "active_border_color") - PWINDOW->m_ruleApplicator->activeBorderColorOverride(Desktop::Types::COverridableVar(colorData, Desktop::Types::PRIORITY_SET_PROP)); + if (PROP == "activebordercolor") + PWINDOW->m_sWindowData.activeBorderColor = CWindowOverridableVar(colorData, PRIORITY_SET_PROP); else - PWINDOW->m_ruleApplicator->inactiveBorderColorOverride(Desktop::Types::COverridableVar(colorData, Desktop::Types::PRIORITY_SET_PROP)); - } else if (PROP == "opacity") { - PWINDOW->m_ruleApplicator->alphaOverride(Desktop::Types::COverridableVar( - Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alpha().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP)); - } else if (PROP == "opacity_inactive") { - PWINDOW->m_ruleApplicator->alphaInactiveOverride(Desktop::Types::COverridableVar( - Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alphaInactive().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP)); - } else if (PROP == "opacity_fullscreen") { - PWINDOW->m_ruleApplicator->alphaFullscreenOverride(Desktop::Types::COverridableVar( - Desktop::Types::SAlphaValue{std::stof(VAL), PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().overridden}, Desktop::Types::PRIORITY_SET_PROP)); - } else if (PROP == "opacity_override") { - PWINDOW->m_ruleApplicator->alphaOverride(Desktop::Types::COverridableVar( - Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alpha().valueOrDefault().alpha, sc(configStringToInt(VAL).value_or(0))}, - Desktop::Types::PRIORITY_SET_PROP)); - } else if (PROP == "opacity_inactive_override") { - PWINDOW->m_ruleApplicator->alphaInactiveOverride(Desktop::Types::COverridableVar( - Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaInactive().valueOrDefault().alpha, sc(configStringToInt(VAL).value_or(0))}, - Desktop::Types::PRIORITY_SET_PROP)); - } else if (PROP == "opacity_fullscreen_override") { - PWINDOW->m_ruleApplicator->alphaFullscreenOverride(Desktop::Types::COverridableVar( - Desktop::Types::SAlphaValue{PWINDOW->m_ruleApplicator->alphaFullscreen().valueOrDefault().alpha, sc(configStringToInt(VAL).value_or(0))}, - Desktop::Types::PRIORITY_SET_PROP)); - } else if (PROP == "allows_input") - parsePropTrivial(PWINDOW->m_ruleApplicator->allowsInput(), VAL); - else if (PROP == "decorate") - parsePropTrivial(PWINDOW->m_ruleApplicator->decorate(), VAL); - else if (PROP == "focus_on_activate") - parsePropTrivial(PWINDOW->m_ruleApplicator->focusOnActivate(), VAL); - else if (PROP == "keep_aspect_ratio") - parsePropTrivial(PWINDOW->m_ruleApplicator->keepAspectRatio(), VAL); - else if (PROP == "nearest_neighbor") - parsePropTrivial(PWINDOW->m_ruleApplicator->nearestNeighbor(), VAL); - else if (PROP == "no_anim") - parsePropTrivial(PWINDOW->m_ruleApplicator->noAnim(), VAL); - else if (PROP == "no_blur") - parsePropTrivial(PWINDOW->m_ruleApplicator->noBlur(), VAL); - else if (PROP == "no_dim") - parsePropTrivial(PWINDOW->m_ruleApplicator->noDim(), VAL); - else if (PROP == "no_focus") - parsePropTrivial(PWINDOW->m_ruleApplicator->noFocus(), VAL); - else if (PROP == "no_max_size") - parsePropTrivial(PWINDOW->m_ruleApplicator->noMaxSize(), VAL); - else if (PROP == "no_shadow") - parsePropTrivial(PWINDOW->m_ruleApplicator->noShadow(), VAL); - else if (PROP == "no_shortcuts_inhibit") - parsePropTrivial(PWINDOW->m_ruleApplicator->noShortcutsInhibit(), VAL); - else if (PROP == "dim_around") - parsePropTrivial(PWINDOW->m_ruleApplicator->dimAround(), VAL); - else if (PROP == "opaque") - parsePropTrivial(PWINDOW->m_ruleApplicator->opaque(), VAL); - else if (PROP == "force_rgbx") - parsePropTrivial(PWINDOW->m_ruleApplicator->RGBX(), VAL); - else if (PROP == "sync_fullscreen") - parsePropTrivial(PWINDOW->m_ruleApplicator->syncFullscreen(), VAL); - else if (PROP == "immediate") - parsePropTrivial(PWINDOW->m_ruleApplicator->tearing(), VAL); - else if (PROP == "xray") - parsePropTrivial(PWINDOW->m_ruleApplicator->xray(), VAL); - else if (PROP == "render_unfocused") - parsePropTrivial(PWINDOW->m_ruleApplicator->renderUnfocused(), VAL); - else if (PROP == "no_follow_mouse") - parsePropTrivial(PWINDOW->m_ruleApplicator->noFollowMouse(), VAL); - else if (PROP == "no_screen_share") - parsePropTrivial(PWINDOW->m_ruleApplicator->noScreenShare(), VAL); - else if (PROP == "no_vrr") - parsePropTrivial(PWINDOW->m_ruleApplicator->noVRR(), VAL); - else if (PROP == "persistent_size") - parsePropTrivial(PWINDOW->m_ruleApplicator->persistentSize(), VAL); - else if (PROP == "stay_focused") - parsePropTrivial(PWINDOW->m_ruleApplicator->stayFocused(), VAL); - else if (PROP == "idle_inhibit") - parsePropTrivial(PWINDOW->m_ruleApplicator->idleInhibitMode(), VAL); - else if (PROP == "border_size") - parsePropTrivial(PWINDOW->m_ruleApplicator->borderSize(), VAL); - else if (PROP == "rounding") - parsePropTrivial(PWINDOW->m_ruleApplicator->rounding(), VAL); - else if (PROP == "rounding_power") - parsePropTrivial(PWINDOW->m_ruleApplicator->roundingPower(), VAL); - else if (PROP == "scroll_mouse") - parsePropTrivial(PWINDOW->m_ruleApplicator->scrollMouse(), VAL); - else if (PROP == "scroll_touchpad") - parsePropTrivial(PWINDOW->m_ruleApplicator->scrollTouchpad(), VAL); - else if (PROP == "animation") - parsePropTrivial(PWINDOW->m_ruleApplicator->animationStyle(), VAL); - else - return {.success = false, .error = "prop not found"}; - + PWINDOW->m_sWindowData.inactiveBorderColor = CWindowOverridableVar(colorData, PRIORITY_SET_PROP); + } else if (auto search = NWindowProperties::boolWindowProperties.find(PROP); search != NWindowProperties::boolWindowProperties.end()) { + auto pWindowDataElement = search->second(PWINDOW); + if (VAL == "toggle") + *pWindowDataElement = CWindowOverridableVar(!pWindowDataElement->valueOrDefault(), PRIORITY_SET_PROP); + else if (VAL == "unset") + pWindowDataElement->unset(PRIORITY_SET_PROP); + else + *pWindowDataElement = CWindowOverridableVar((bool)configStringToInt(VAL).value_or(0), PRIORITY_SET_PROP); + } else if (auto search = NWindowProperties::intWindowProperties.find(PROP); search != NWindowProperties::intWindowProperties.end()) { + if (VAL == "unset") + search->second(PWINDOW)->unset(PRIORITY_SET_PROP); + else if (const auto V = configStringToInt(VAL); V) + *(search->second(PWINDOW)) = CWindowOverridableVar((int)*V, PRIORITY_SET_PROP); + } else if (auto search = NWindowProperties::floatWindowProperties.find(PROP); search != NWindowProperties::floatWindowProperties.end()) { + if (VAL == "unset") + search->second(PWINDOW)->unset(PRIORITY_SET_PROP); + else { + const auto V = std::stof(VAL); + *(search->second(PWINDOW)) = CWindowOverridableVar(V, PRIORITY_SET_PROP); + } + } else + return {.success = false, .error = "Prop not found"}; } catch (std::exception& e) { return {.success = false, .error = std::format("Error parsing prop value: {}", std::string(e.what()))}; } g_pCompositor->updateAllWindowsAnimatedDecorationValues(); - if (PWINDOW->m_ruleApplicator->noFocus().valueOrDefault() != noFocus) { - // FIXME: what the fuck is going on here? -vax - Desktop::focusState()->rawWindowFocus(nullptr, Desktop::FOCUS_REASON_KEYBIND); - Desktop::focusState()->fullWindowFocus(PWINDOW, Desktop::FOCUS_REASON_KEYBIND); - Desktop::focusState()->fullWindowFocus(PLASTWINDOW, Desktop::FOCUS_REASON_KEYBIND); + if (!(PWINDOW->m_sWindowData.noFocus.valueOrDefault() == noFocus)) { + g_pCompositor->focusWindow(nullptr); + g_pCompositor->focusWindow(PWINDOW); + g_pCompositor->focusWindow(PLASTWINDOW); } - if (PROP == "no_vrr") - g_pConfigManager->ensureVRR(PWINDOW->m_monitor.lock()); - - for (auto const& m : g_pCompositor->m_monitors) { - if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(); - } + for (auto const& m : g_pCompositor->m_vMonitors) + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->ID); return {}; } - -SDispatchResult CKeybindManager::forceIdle(std::string args) { - std::optional duration = getPlusMinusKeywordResult(args, 0); - - if (!duration.has_value()) { - Log::logger->log(Log::ERR, "Duration invalid in forceIdle!"); - return {.success = false, .error = "Duration invalid in forceIdle!"}; - } - - PROTO::idle->setTimers(duration.value() * 1000.0); - - return {}; -} - -SDispatchResult CKeybindManager::sendkeystate(std::string args) { - // args=[,WINDOW_RULES] - const auto ARGS = CVarList(args, 4); - if (ARGS.size() != 4) { - Log::logger->log(Log::ERR, "sendkeystate: invalid args"); - return {.success = false, .error = "sendkeystate: invalid args"}; - } - - const auto STATE = ARGS[2]; - - if (STATE != "down" && STATE != "repeat" && STATE != "up") { - Log::logger->log(Log::ERR, "sendkeystate: invalid state, must be 'down', 'repeat', or 'up'"); - return {.success = false, .error = "sendkeystate: invalid state, must be 'down', 'repeat', or 'up'"}; - } - - std::string modifiedArgs = ARGS[0] + "," + ARGS[1] + "," + ARGS[3]; - - const int oldPassPressed = g_pKeybindManager->m_passPressed; - - if (STATE == "down") - g_pKeybindManager->m_passPressed = 1; - else if (STATE == "up") - g_pKeybindManager->m_passPressed = 0; - else if (STATE == "repeat") - g_pKeybindManager->m_passPressed = 1; - - auto result = sendshortcut(modifiedArgs); - - if (STATE == "repeat" && result.success) - result = sendshortcut(modifiedArgs); - - g_pKeybindManager->m_passPressed = oldPassPressed; - - if (!result.success && !result.error.empty()) { - size_t pos = result.error.find("sendshortcut:"); - if (pos != std::string::npos) - result.error = "sendkeystate:" + result.error.substr(pos + 13); - } - - return result; -} diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index 1f013606..712fd58d 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -8,7 +8,7 @@ #include #include "../devices/IPointer.hpp" #include "eventLoop/EventLoopTimer.hpp" -#include "../helpers/time/Timer.hpp" +#include "../helpers/Timer.hpp" class CInputManager; class CConfigManager; @@ -17,39 +17,28 @@ class IKeyboard; enum eMouseBindMode : int8_t; -struct SSubmap { - std::string name = ""; - std::string reset = ""; - bool operator==(const SSubmap& other) const { - return name == other.name; - } -}; - struct SKeybind { - std::string key = ""; - std::set sMkKeys = {}; - uint32_t keycode = 0; - bool catchAll = false; - uint32_t modmask = 0; - std::set sMkMods = {}; - std::string handler = ""; - std::string arg = ""; - bool locked = false; - SSubmap submap = {}; - std::string description = ""; - bool release = false; - bool repeat = false; - bool longPress = false; - bool mouse = false; - bool nonConsuming = false; - bool transparent = false; - bool ignoreMods = false; - bool multiKey = false; - bool hasDescription = false; - bool dontInhibit = false; - bool click = false; - bool drag = false; - bool submapUniversal = false; + std::string key = ""; + std::set sMkKeys = {}; + uint32_t keycode = 0; + bool catchAll = false; + uint32_t modmask = 0; + std::set sMkMods = {}; + std::string handler = ""; + std::string arg = ""; + bool locked = false; + std::string submap = ""; + std::string description = ""; + bool release = false; + bool repeat = false; + bool longPress = false; + bool mouse = false; + bool nonConsuming = false; + bool transparent = false; + bool ignoreMods = false; + bool multiKey = false; + bool hasDescription = false; + bool dontInhibit = false; // DO NOT INITIALIZE bool shadowed = false; @@ -72,8 +61,7 @@ struct SPressedKeyWithMods { uint32_t keycode = 0; uint32_t modmaskAtPressTime = 0; bool sent = false; - SSubmap submapAtPress = {}; - Vector2D mousePosAtPress = {}; + std::string submapAtPress = ""; }; struct SParsedKey { @@ -107,55 +95,52 @@ class CKeybindManager { uint32_t keycodeToModifier(xkb_keycode_t); void clearKeybinds(); void shadowKeybinds(const xkb_keysym_t& doesntHave = 0, const uint32_t doesntHaveCode = 0); - SSubmap getCurrentSubmap(); + std::string getCurrentSubmap(); - std::unordered_map> m_dispatchers; + std::unordered_map> m_mDispatchers; - bool m_groupsLocked = false; + bool m_bGroupsLocked = false; - std::vector> m_keybinds; + std::vector> m_vKeybinds; - //since we can't find keycode through keyname in xkb: + //since we cant find keycode through keyname in xkb: //on sendshortcut call, we once search for keyname (e.g. "g") the correct keycode (e.g. 42) //and cache it in this map to make sendshortcut calls faster //we also store the keyboard pointer (in the string) to differentiate between different keyboard (layouts) - std::unordered_map m_keyToCodeCache; + std::unordered_map m_mKeyToCodeCache; static SDispatchResult changeMouseBindMode(const eMouseBindMode mode); private: - std::vector m_pressedKeys; + std::vector m_dPressedKeys; - inline static SSubmap m_currentSelectedSubmap = {}; + inline static std::string m_szCurrentSelectedSubmap = ""; - std::vector> m_activeKeybinds; - WP m_lastLongPressKeybind; + std::vector> m_vActiveKeybinds; + WP m_pLastLongPressKeybind; + SP m_pLongPressTimer, m_pRepeatKeyTimer; - SP m_longPressTimer; - SP m_repeatKeyTimer; - uint32_t m_repeatKeyRate = 50; + uint32_t m_uTimeLastMs = 0; + uint32_t m_uLastCode = 0; + uint32_t m_uLastMouseCode = 0; - uint32_t m_timeLastMs = 0; - uint32_t m_lastCode = 0; - uint32_t m_lastMouseCode = 0; + std::vector> m_vPressedSpecialBinds; - std::vector> m_pressedSpecialBinds; + int m_iPassPressed = -1; // used for pass - int m_passPressed = -1; // used for pass + CTimer m_tScrollTimer; - CTimer m_scrollTimer; + SDispatchResult handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool); - SDispatchResult handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool, SP); - - std::set m_mkKeys = {}; - std::set m_mkMods = {}; + std::set m_sMkKeys = {}; + std::set m_sMkMods = {}; eMultiKeyCase mkBindMatches(const SP); eMultiKeyCase mkKeysymSetMatches(const std::set, const std::set); bool handleInternalKeybinds(xkb_keysym_t); bool handleVT(xkb_keysym_t); - xkb_state* m_xkbTranslationState = nullptr; + xkb_state* m_pXKBTranslationState = nullptr; void updateXKBTranslationState(); bool ensureMouseBindState(); @@ -163,10 +148,9 @@ class CKeybindManager { static bool tryMoveFocusToMonitor(PHLMONITOR monitor); static void moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string& dir = ""); static void moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowInDirection); - - static void switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool forceFSCycle = false); - static uint64_t spawnRawProc(std::string, PHLWORKSPACE pInitialWorkspace, const std::string& execRuleToken = ""); - static std::optional spawnWithRules(std::string, PHLWORKSPACE pInitialWorkspace); + static void switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool preserveFocusHistory = false); + static uint64_t spawnRawProc(std::string, PHLWORKSPACE pInitialWorkspace); + static uint64_t spawnWithRules(std::string, PHLWORKSPACE pInitialWorkspace); // -------------- Dispatchers -------------- // static SDispatchResult closeActive(std::string); @@ -194,7 +178,10 @@ class CKeybindManager { static SDispatchResult swapActive(std::string); static SDispatchResult toggleGroup(std::string); static SDispatchResult changeGroupActive(std::string); + static SDispatchResult alterSplitRatio(std::string); static SDispatchResult focusMonitor(std::string); + static SDispatchResult toggleSplit(std::string); + static SDispatchResult swapSplit(std::string); static SDispatchResult moveCursorToCorner(std::string); static SDispatchResult moveCursor(std::string); static SDispatchResult workspaceOpt(std::string); @@ -216,7 +203,6 @@ class CKeybindManager { static SDispatchResult setSubmap(std::string); static SDispatchResult pass(std::string); static SDispatchResult sendshortcut(std::string); - static SDispatchResult sendkeystate(std::string); static SDispatchResult layoutmsg(std::string); static SDispatchResult dpms(std::string); static SDispatchResult swapnext(std::string); @@ -236,7 +222,6 @@ class CKeybindManager { static SDispatchResult global(std::string); static SDispatchResult event(std::string); static SDispatchResult setProp(std::string); - static SDispatchResult forceIdle(std::string); friend class CCompositor; friend class CInputManager; diff --git a/src/managers/LayoutManager.cpp b/src/managers/LayoutManager.cpp new file mode 100644 index 00000000..70c2d2a3 --- /dev/null +++ b/src/managers/LayoutManager.cpp @@ -0,0 +1,60 @@ +#include "LayoutManager.hpp" + +CLayoutManager::CLayoutManager() { + m_vLayouts.emplace_back(std::make_pair<>("dwindle", &m_cDwindleLayout)); + m_vLayouts.emplace_back(std::make_pair<>("master", &m_cMasterLayout)); +} + +IHyprLayout* CLayoutManager::getCurrentLayout() { + return m_vLayouts[m_iCurrentLayoutID].second; +} + +void CLayoutManager::switchToLayout(std::string layout) { + for (size_t i = 0; i < m_vLayouts.size(); ++i) { + if (m_vLayouts[i].first == layout) { + if (i == (size_t)m_iCurrentLayoutID) + return; + + getCurrentLayout()->onDisable(); + m_iCurrentLayoutID = i; + getCurrentLayout()->onEnable(); + return; + } + } + + Debug::log(ERR, "Unknown layout!"); +} + +bool CLayoutManager::addLayout(const std::string& name, IHyprLayout* layout) { + if (std::find_if(m_vLayouts.begin(), m_vLayouts.end(), [&](const auto& other) { return other.first == name || other.second == layout; }) != m_vLayouts.end()) + return false; + + m_vLayouts.emplace_back(std::make_pair<>(name, layout)); + + Debug::log(LOG, "Added new layout {} at {:x}", name, (uintptr_t)layout); + + return true; +} + +bool CLayoutManager::removeLayout(IHyprLayout* layout) { + const auto IT = std::find_if(m_vLayouts.begin(), m_vLayouts.end(), [&](const auto& other) { return other.second == layout; }); + + if (IT == m_vLayouts.end() || IT->first == "dwindle" || IT->first == "master") + return false; + + if (m_iCurrentLayoutID == IT - m_vLayouts.begin()) + switchToLayout("dwindle"); + + Debug::log(LOG, "Removed a layout {} at {:x}", IT->first, (uintptr_t)layout); + + std::erase(m_vLayouts, *IT); + + return true; +} + +std::vector CLayoutManager::getAllLayoutNames() { + std::vector results(m_vLayouts.size()); + for (size_t i = 0; i < m_vLayouts.size(); ++i) + results[i] = m_vLayouts[i].first; + return results; +} diff --git a/src/managers/LayoutManager.hpp b/src/managers/LayoutManager.hpp new file mode 100644 index 00000000..63e54baa --- /dev/null +++ b/src/managers/LayoutManager.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "../layout/DwindleLayout.hpp" +#include "../layout/MasterLayout.hpp" + +class CLayoutManager { + public: + CLayoutManager(); + + IHyprLayout* getCurrentLayout(); + + void switchToLayout(std::string); + + bool addLayout(const std::string& name, IHyprLayout* layout); + bool removeLayout(IHyprLayout* layout); + std::vector getAllLayoutNames(); + + private: + enum eHyprLayouts : uint8_t { + LAYOUT_DWINDLE = 0, + LAYOUT_MASTER + }; + + int m_iCurrentLayoutID = LAYOUT_DWINDLE; + + CHyprDwindleLayout m_cDwindleLayout; + CHyprMasterLayout m_cMasterLayout; + std::vector> m_vLayouts; +}; + +inline UP g_pLayoutManager; diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 7256e176..e4f24230 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -1,48 +1,44 @@ #include "PointerManager.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" -#include "../config/ConfigManager.hpp" #include "../protocols/PointerGestures.hpp" #include "../protocols/RelativePointer.hpp" #include "../protocols/FractionalScale.hpp" #include "../protocols/IdleNotify.hpp" #include "../protocols/core/Compositor.hpp" #include "../protocols/core/Seat.hpp" -#include "debug/log/Logger.hpp" #include "eventLoop/EventLoopManager.hpp" #include "../render/pass/TexPassElement.hpp" #include "../managers/input/InputManager.hpp" +#include "../managers/HookSystemManager.hpp" #include "../render/Renderer.hpp" #include "../render/OpenGL.hpp" -#include "../desktop/state/FocusState.hpp" #include "SeatManager.hpp" -#include "../helpers/time/Time.hpp" -#include "../helpers/Drm.hpp" -#include "../event/EventBus.hpp" -#include #include #include #include -#include -#include #include using namespace Hyprutils::Utils; CPointerManager::CPointerManager() { - m_hooks.monitorAdded = Event::bus()->m_events.monitor.added.listen([this](PHLMONITOR monitor) { + hooks.monitorAdded = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any data) { + auto PMONITOR = std::any_cast(data); + onMonitorLayoutChange(); - monitor->m_events.modeChanged.listenStatic([this] { g_pEventLoopManager->doLater([this]() { onMonitorLayoutChange(); }); }); - monitor->m_events.disconnect.listenStatic([this] { g_pEventLoopManager->doLater([this]() { onMonitorLayoutChange(); }); }); - monitor->m_events.destroy.listenStatic([this] { - if (g_pCompositor && !g_pCompositor->m_isShuttingDown) - std::erase_if(m_monitorStates, [](const auto& other) { return other->monitor.expired(); }); - }); + PMONITOR->events.modeChanged.registerStaticListener([this](void* owner, std::any data) { g_pEventLoopManager->doLater([this]() { onMonitorLayoutChange(); }); }, nullptr); + PMONITOR->events.disconnect.registerStaticListener([this](void* owner, std::any data) { g_pEventLoopManager->doLater([this]() { onMonitorLayoutChange(); }); }, nullptr); + PMONITOR->events.destroy.registerStaticListener( + [this](void* owner, std::any data) { + if (g_pCompositor && !g_pCompositor->m_bIsShuttingDown) + std::erase_if(monitorStates, [](const auto& other) { return other->monitor.expired(); }); + }, + nullptr); }); - m_hooks.monitorPreRender = Event::bus()->m_events.monitor.preCommit.listen([this](PHLMONITOR monitor) { - auto state = stateFor(monitor); + hooks.monitorPreRender = g_pHookSystem->hookDynamic("preMonitorCommit", [this](void* self, SCallbackInfo& info, std::any data) { + auto state = stateFor(std::any_cast(data)); if (!state) return; @@ -51,14 +47,14 @@ CPointerManager::CPointerManager() { } void CPointerManager::lockSoftwareAll() { - for (auto const& state : m_monitorStates) + for (auto const& state : monitorStates) state->softwareLocks++; updateCursorBackend(); } void CPointerManager::unlockSoftwareAll() { - for (auto const& state : m_monitorStates) + for (auto const& state : monitorStates) state->softwareLocks--; updateCursorBackend(); @@ -75,10 +71,8 @@ void CPointerManager::lockSoftwareForMonitor(PHLMONITOR mon) { void CPointerManager::unlockSoftwareForMonitor(PHLMONITOR mon) { auto const state = stateFor(mon); state->softwareLocks--; - if (state->softwareLocks < 0) { + if (state->softwareLocks < 0) state->softwareLocks = 0; - Log::logger->log(Log::WARN, "Unlocking SW for monitor while it's not locked"); - } if (state->softwareLocks == 0) updateCursorBackend(); @@ -86,42 +80,32 @@ void CPointerManager::unlockSoftwareForMonitor(PHLMONITOR mon) { bool CPointerManager::softwareLockedFor(PHLMONITOR mon) { auto const state = stateFor(mon); - return state->softwareLocks > 0 || (state->hardwareFailed && hasCursor() && g_pHyprRenderer->shouldRenderCursor()); -} - -bool CPointerManager::hasVisibleHWCursor(PHLMONITOR pMonitor) { - auto const state = stateFor(pMonitor); - return state->softwareLocks == 0 && !state->hardwareFailed && hasCursor() && g_pHyprRenderer->shouldRenderCursor(); + return state->softwareLocks > 0 || state->hardwareFailed; } Vector2D CPointerManager::position() { - return m_pointerPos; -} - -Vector2D CPointerManager::hotspot() { - return m_currentCursorImage.hotspot; + return pointerPos; } bool CPointerManager::hasCursor() { - return m_currentCursorImage.pBuffer || m_currentCursorImage.surface; + return currentCursorImage.pBuffer || currentCursorImage.surface; } SP CPointerManager::stateFor(PHLMONITOR mon) { - auto it = std::ranges::find_if(m_monitorStates, [mon](const auto& other) { return other->monitor == mon; }); - if (it == m_monitorStates.end()) - return m_monitorStates.emplace_back(makeShared(mon)); + auto it = std::find_if(monitorStates.begin(), monitorStates.end(), [mon](const auto& other) { return other->monitor == mon; }); + if (it == monitorStates.end()) + return monitorStates.emplace_back(makeShared(mon)); return *it; } void CPointerManager::setCursorBuffer(SP buf, const Vector2D& hotspot, const float& scale) { damageIfSoftware(); - if (buf == m_currentCursorImage.pBuffer) { - if (hotspot != m_currentCursorImage.hotspot || scale != m_currentCursorImage.scale) { - m_currentCursorImage.hotspot = hotspot; - m_currentCursorImage.scale = scale; + if (buf == currentCursorImage.pBuffer) { + if (hotspot != currentCursorImage.hotspot || scale != currentCursorImage.scale) { + currentCursorImage.hotspot = hotspot; + currentCursorImage.scale = scale; updateCursorBackend(); damageIfSoftware(); - m_events.cursorChanged.emit(); } return; @@ -130,28 +114,26 @@ void CPointerManager::setCursorBuffer(SP buf, const Vector2 resetCursorImage(false); if (buf) { - m_currentCursorImage.size = buf->size; - m_currentCursorImage.pBuffer = buf; + currentCursorImage.size = buf->size; + currentCursorImage.pBuffer = buf; } - m_currentCursorImage.hotspot = hotspot; - m_currentCursorImage.scale = scale; + currentCursorImage.hotspot = hotspot; + currentCursorImage.scale = scale; updateCursorBackend(); damageIfSoftware(); - m_events.cursorChanged.emit(); } -void CPointerManager::setCursorSurface(SP surf, const Vector2D& hotspot) { +void CPointerManager::setCursorSurface(SP surf, const Vector2D& hotspot) { damageIfSoftware(); - if (surf == m_currentCursorImage.surface) { - if (hotspot != m_currentCursorImage.hotspot || (surf && surf->resource() ? surf->resource()->m_current.scale : 1.F) != m_currentCursorImage.scale) { - m_currentCursorImage.hotspot = hotspot; - m_currentCursorImage.scale = surf && surf->resource() ? surf->resource()->m_current.scale : 1.F; + if (surf == currentCursorImage.surface) { + if (hotspot != currentCursorImage.hotspot || (surf && surf->resource() ? surf->resource()->current.scale : 1.F) != currentCursorImage.scale) { + currentCursorImage.hotspot = hotspot; + currentCursorImage.scale = surf && surf->resource() ? surf->resource()->current.scale : 1.F; updateCursorBackend(); damageIfSoftware(); - m_events.cursorChanged.emit(); } return; @@ -160,34 +142,34 @@ void CPointerManager::setCursorSurface(SP surf, const resetCursorImage(false); if (surf) { - m_currentCursorImage.surface = surf; - m_currentCursorImage.scale = surf->resource()->m_current.scale; + currentCursorImage.surface = surf; + currentCursorImage.scale = surf->resource()->current.scale; surf->resource()->map(); - m_currentCursorImage.destroySurface = surf->m_events.destroy.listen([this] { resetCursorImage(); }); - m_currentCursorImage.commitSurface = surf->resource()->m_events.commit.listen([this] { + currentCursorImage.destroySurface = surf->events.destroy.registerListener([this](std::any data) { resetCursorImage(); }); + currentCursorImage.commitSurface = surf->resource()->events.commit.registerListener([this](std::any data) { damageIfSoftware(); - m_currentCursorImage.size = m_currentCursorImage.surface->resource()->m_current.texture ? m_currentCursorImage.surface->resource()->m_current.bufferSize : Vector2D{}; - m_currentCursorImage.scale = m_currentCursorImage.surface ? m_currentCursorImage.surface->resource()->m_current.scale : 1.F; + currentCursorImage.size = currentCursorImage.surface->resource()->current.texture ? currentCursorImage.surface->resource()->current.bufferSize : Vector2D{}; + currentCursorImage.scale = currentCursorImage.surface ? currentCursorImage.surface->resource()->current.scale : 1.F; recheckEnteredOutputs(); updateCursorBackend(); damageIfSoftware(); - m_events.cursorChanged.emit(); }); - if (surf->resource()->m_current.texture) { - m_currentCursorImage.size = surf->resource()->m_current.bufferSize; - surf->resource()->frame(Time::steadyNow()); + if (surf->resource()->current.texture) { + currentCursorImage.size = surf->resource()->current.bufferSize; + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + surf->resource()->frame(&now); } } - m_currentCursorImage.hotspot = hotspot; + currentCursorImage.hotspot = hotspot; recheckEnteredOutputs(); updateCursorBackend(); damageIfSoftware(); - m_events.cursorChanged.emit(); } void CPointerManager::recheckEnteredOutputs() { @@ -196,8 +178,8 @@ void CPointerManager::recheckEnteredOutputs() { auto box = getCursorBoxGlobal(); - for (auto const& s : m_monitorStates) { - if (s->monitor.expired() || s->monitor->isMirror() || !s->monitor->m_enabled) + for (auto const& s : monitorStates) { + if (s->monitor.expired() || s->monitor->isMirror() || !s->monitor->m_bEnabled) continue; const bool overlaps = box.overlaps(s->monitor->logicalBox()); @@ -205,25 +187,24 @@ void CPointerManager::recheckEnteredOutputs() { if (!s->entered && overlaps) { s->entered = true; - if (!m_currentCursorImage.surface) + if (!currentCursorImage.surface) continue; - m_currentCursorImage.surface->resource()->enter(s->monitor.lock()); - PROTO::fractional->sendScale(m_currentCursorImage.surface->resource(), s->monitor->m_scale); - g_pCompositor->setPreferredScaleForSurface(m_currentCursorImage.surface->resource(), s->monitor->m_scale); + currentCursorImage.surface->resource()->enter(s->monitor.lock()); + PROTO::fractional->sendScale(currentCursorImage.surface->resource(), s->monitor->scale); + g_pCompositor->setPreferredScaleForSurface(currentCursorImage.surface->resource(), s->monitor->scale); } else if (s->entered && !overlaps) { s->entered = false; // if we are using hw cursors, prevent // the cursor from being stuck at the last point. - if (!s->hardwareFailed && - (s->monitor->m_output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER)) + if (!s->hardwareFailed && (s->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER)) setHWCursorBuffer(s, nullptr); - if (!m_currentCursorImage.surface) + if (!currentCursorImage.surface) continue; - m_currentCursorImage.surface->resource()->leave(s->monitor.lock()); + currentCursorImage.surface->resource()->leave(s->monitor.lock()); } } } @@ -231,27 +212,27 @@ void CPointerManager::recheckEnteredOutputs() { void CPointerManager::resetCursorImage(bool apply) { damageIfSoftware(); - if (m_currentCursorImage.surface) { - for (auto const& m : g_pCompositor->m_monitors) { - m_currentCursorImage.surface->resource()->leave(m); + if (currentCursorImage.surface) { + for (auto const& m : g_pCompositor->m_vMonitors) { + currentCursorImage.surface->resource()->leave(m); } - m_currentCursorImage.surface->resource()->unmap(); + currentCursorImage.surface->resource()->unmap(); - m_currentCursorImage.destroySurface.reset(); - m_currentCursorImage.commitSurface.reset(); - m_currentCursorImage.surface.reset(); - } else if (m_currentCursorImage.pBuffer) - m_currentCursorImage.pBuffer = nullptr; + currentCursorImage.destroySurface.reset(); + currentCursorImage.commitSurface.reset(); + currentCursorImage.surface.reset(); + } else if (currentCursorImage.pBuffer) + currentCursorImage.pBuffer = nullptr; - if (m_currentCursorImage.bufferTex) - m_currentCursorImage.bufferTex = nullptr; + if (currentCursorImage.bufferTex) + currentCursorImage.bufferTex = nullptr; - m_currentCursorImage.scale = 1.F; - m_currentCursorImage.hotspot = {0, 0}; + currentCursorImage.scale = 1.F; + currentCursorImage.hotspot = {0, 0}; - for (auto const& s : m_monitorStates) { - if (s->monitor.expired() || s->monitor->isMirror() || !s->monitor->m_enabled) + for (auto const& s : monitorStates) { + if (s->monitor.expired() || s->monitor->isMirror() || !s->monitor->m_bEnabled) continue; s->entered = false; @@ -260,28 +241,26 @@ void CPointerManager::resetCursorImage(bool apply) { if (!apply) return; - for (auto const& ms : m_monitorStates) { - if (!ms->monitor || !ms->monitor->m_enabled || !ms->monitor->m_dpmsStatus) { - Log::logger->log(Log::TRACE, "Not updating hw cursors: disabled / dpms off display"); + for (auto const& ms : monitorStates) { + if (!ms->monitor || !ms->monitor->m_bEnabled || !ms->monitor->dpmsStatus) { + Debug::log(TRACE, "Not updating hw cursors: disabled / dpms off display"); continue; } if (ms->cursorFrontBuffer) { - if (ms->monitor->m_output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER) - ms->monitor->m_output->setCursor(nullptr, {}); + if (ms->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER) + ms->monitor->output->setCursor(nullptr, {}); ms->cursorFrontBuffer = nullptr; } } - - m_events.cursorChanged.emit(); } void CPointerManager::updateCursorBackend() { const auto CURSORBOX = getCursorBoxGlobal(); - for (auto const& m : g_pCompositor->m_monitors) { - if (!m->m_enabled || !m->m_dpmsStatus) { - Log::logger->log(Log::TRACE, "Not updating hw cursors: disabled / dpms off display"); + for (auto const& m : g_pCompositor->m_vMonitors) { + if (!m->m_bEnabled || !m->dpmsStatus) { + Debug::log(TRACE, "Not updating hw cursors: disabled / dpms off display"); continue; } @@ -296,7 +275,7 @@ void CPointerManager::updateCursorBackend() { } if (state->softwareLocks > 0 || g_pConfigManager->shouldUseSoftwareCursors(m) || !attemptHardwareCursor(state)) { - Log::logger->log(Log::TRACE, "Output {} rejected hardware cursors, falling back to sw", m->m_name); + Debug::log(TRACE, "Output {} rejected hardware cursors, falling back to sw", m->szName); state->box = getCursorBoxLogicalForMonitor(state->monitor.lock()); state->hardwareFailed = true; @@ -318,7 +297,7 @@ void CPointerManager::onCursorMoved() { const auto CURSORBOX = getCursorBoxGlobal(); bool recalc = false; - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { auto state = stateFor(m); state->box = getCursorBoxLogicalForMonitor(state->monitor.lock()); @@ -326,11 +305,11 @@ void CPointerManager::onCursorMoved() { auto CROSSES = !m->logicalBox().intersection(CURSORBOX).empty(); if (!CROSSES && state->cursorFrontBuffer) { - Log::logger->log(Log::TRACE, "onCursorMoved for output {}: cursor left the viewport, removing it from the backend", m->m_name); + Debug::log(TRACE, "onCursorMoved for output {}: cursor left the viewport, removing it from the backend", m->szName); setHWCursorBuffer(state, nullptr); continue; } else if (CROSSES && !state->cursorFrontBuffer) { - Log::logger->log(Log::TRACE, "onCursorMoved for output {}: cursor entered the output, but no front buffer, forcing recalc", m->m_name); + Debug::log(TRACE, "onCursorMoved for output {}: cursor entered the output, but no front buffer, forcing recalc", m->szName); recalc = true; } @@ -343,9 +322,9 @@ void CPointerManager::onCursorMoved() { continue; const auto CURSORPOS = getCursorPosForMonitor(m); - m->m_output->moveCursor(CURSORPOS, m->shouldSkipScheduleFrameOnMouseEvent()); + m->output->moveCursor(CURSORPOS, m->shouldSkipScheduleFrameOnMouseEvent()); - state->monitor->m_scanoutNeedsCursorUpdate = true; + state->monitor->scanoutNeedsCursorUpdate = true; } if (recalc) @@ -353,18 +332,18 @@ void CPointerManager::onCursorMoved() { } bool CPointerManager::attemptHardwareCursor(SP state) { - auto output = state->monitor->m_output; + auto output = state->monitor->output; if (!(output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER)) return false; const auto CURSORPOS = getCursorPosForMonitor(state->monitor.lock()); - state->monitor->m_output->moveCursor(CURSORPOS, state->monitor->shouldSkipScheduleFrameOnMouseEvent()); + state->monitor->output->moveCursor(CURSORPOS, state->monitor->shouldSkipScheduleFrameOnMouseEvent()); auto texture = getCurrentCursorTexture(); if (!texture) { - Log::logger->log(Log::TRACE, "[pointer] no texture for hw cursor -> hiding"); + Debug::log(TRACE, "[pointer] no texture for hw cursor -> hiding"); setHWCursorBuffer(state, nullptr); return true; } @@ -372,7 +351,7 @@ bool CPointerManager::attemptHardwareCursor(SPlog(Log::TRACE, "[pointer] hw cursor failed rendering"); + Debug::log(TRACE, "[pointer] hw cursor failed rendering"); setHWCursorBuffer(state, nullptr); return false; } @@ -380,7 +359,7 @@ bool CPointerManager::attemptHardwareCursor(SPlog(Log::TRACE, "[pointer] hw cursor failed applying, hiding"); + Debug::log(TRACE, "[pointer] hw cursor failed applying, hiding"); setHWCursorBuffer(state, nullptr); return false; } else @@ -390,14 +369,14 @@ bool CPointerManager::attemptHardwareCursor(SP state, SP buf) { - if (!(state->monitor->m_output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER)) + if (!(state->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER)) return false; const auto HOTSPOT = transformedHotspot(state->monitor.lock()); - Log::logger->log(Log::TRACE, "[pointer] hw transformed hotspot for {}: {}", state->monitor->m_name, HOTSPOT); + Debug::log(TRACE, "[pointer] hw transformed hotspot for {}: {}", state->monitor->szName, HOTSPOT); - if (!state->monitor->m_output->setCursor(buf, HOTSPOT)) + if (!state->monitor->output->setCursor(buf, HOTSPOT)) return false; state->cursorFrontBuffer = buf; @@ -405,14 +384,14 @@ bool CPointerManager::setHWCursorBuffer(SP state, SPmonitor->shouldSkipScheduleFrameOnMouseEvent()) g_pCompositor->scheduleFrameForMonitor(state->monitor.lock(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE); - state->monitor->m_scanoutNeedsCursorUpdate = true; + state->monitor->scanoutNeedsCursorUpdate = true; return true; } -SP CPointerManager::renderHWCursorBuffer(SP state, SP texture) { - auto maxSize = state->monitor->m_output->cursorPlaneSize(); - auto const& cursorSize = m_currentCursorImage.size; +SP CPointerManager::renderHWCursorBuffer(SP state, SP texture) { + auto maxSize = state->monitor->output->cursorPlaneSize(); + auto const& cursorSize = currentCursorImage.size; static auto PCPUBUFFER = CConfigValue("cursor:use_cpu_buffer"); @@ -423,20 +402,20 @@ SP CPointerManager::renderHWCursorBuffer(SP maxSize.x || cursorSize.y > maxSize.y) { - Log::logger->log(Log::TRACE, "hardware cursor too big! {} > {}", m_currentCursorImage.size, maxSize); + Debug::log(TRACE, "hardware cursor too big! {} > {}", currentCursorImage.size, maxSize); return nullptr; } } else maxSize = cursorSize; - if (!state->monitor->m_cursorSwapchain || maxSize != state->monitor->m_cursorSwapchain->currentOptions().size || - shouldUseCpuBuffer != (state->monitor->m_cursorSwapchain->getAllocator()->type() != Aquamarine::AQ_ALLOCATOR_TYPE_GBM)) { + if (!state->monitor->cursorSwapchain || maxSize != state->monitor->cursorSwapchain->currentOptions().size || + shouldUseCpuBuffer != (state->monitor->cursorSwapchain->getAllocator()->type() != Aquamarine::AQ_ALLOCATOR_TYPE_GBM)) { - if (!state->monitor->m_cursorSwapchain || shouldUseCpuBuffer != (state->monitor->m_cursorSwapchain->getAllocator()->type() != Aquamarine::AQ_ALLOCATOR_TYPE_GBM)) { + if (!state->monitor->cursorSwapchain || shouldUseCpuBuffer != (state->monitor->cursorSwapchain->getAllocator()->type() != Aquamarine::AQ_ALLOCATOR_TYPE_GBM)) { - auto allocator = state->monitor->m_output->getBackend()->preferredAllocator(); + auto allocator = state->monitor->output->getBackend()->preferredAllocator(); if (shouldUseCpuBuffer) { - for (const auto& a : state->monitor->m_output->getBackend()->getAllocators()) { + for (const auto& a : state->monitor->output->getBackend()->getAllocators()) { if (a->type() == Aquamarine::AQ_ALLOCATOR_TYPE_DRM_DUMB) { allocator = a; break; @@ -444,24 +423,24 @@ SP CPointerManager::renderHWCursorBuffer(SPmonitor->m_output->getBackend(); - auto primary = backend->getPrimary(); - state->monitor->m_cursorSwapchain = Aquamarine::CSwapchain::create(allocator, primary ? primary.lock() : backend); + auto backend = state->monitor->output->getBackend(); + auto primary = backend->getPrimary(); + state->monitor->cursorSwapchain = Aquamarine::CSwapchain::create(allocator, primary ? primary.lock() : backend); } - auto options = state->monitor->m_cursorSwapchain->currentOptions(); + auto options = state->monitor->cursorSwapchain->currentOptions(); options.size = maxSize; options.length = 2; options.scanout = true; options.cursor = true; - options.multigpu = !DRM::sameGpu(state->monitor->m_output->getBackend()->preferredAllocator()->drmFD(), g_pCompositor->m_drm.fd); + options.multigpu = state->monitor->output->getBackend()->preferredAllocator()->drmFD() != g_pCompositor->m_iDRMFD; // We do not set the format (unless shm). If it's unset (DRM_FORMAT_INVALID) then the swapchain will pick for us, // but if it's set, we don't wanna change it. if (shouldUseCpuBuffer) options.format = DRM_FORMAT_ARGB8888; - if (!state->monitor->m_cursorSwapchain->reconfigure(options)) { - Log::logger->log(Log::TRACE, "Failed to reconfigure cursor swapchain"); + if (!state->monitor->cursorSwapchain->reconfigure(options)) { + Debug::log(TRACE, "Failed to reconfigure cursor swapchain"); return nullptr; } } @@ -470,13 +449,13 @@ SP CPointerManager::renderHWCursorBuffer(SPcursorRendered) - state->monitor->m_cursorSwapchain->rollback(); + state->monitor->cursorSwapchain->rollback(); state->cursorRendered = true; - auto buf = state->monitor->m_cursorSwapchain->next(nullptr); + auto buf = state->monitor->cursorSwapchain->next(nullptr); if (!buf) { - Log::logger->log(Log::TRACE, "Failed to acquire a buffer from the cursor swapchain"); + Debug::log(TRACE, "Failed to acquire a buffer from the cursor swapchain"); return nullptr; } @@ -484,19 +463,19 @@ SP CPointerManager::renderHWCursorBuffer(SPdataCopy(); if (texData.empty()) { - if (m_currentCursorImage.surface && m_currentCursorImage.surface->resource()->m_role->role() == SURFACE_ROLE_CURSOR) { - const auto SURFACE = m_currentCursorImage.surface->resource(); + if (currentCursorImage.surface && currentCursorImage.surface->resource()->role->role() == SURFACE_ROLE_CURSOR) { + const auto SURFACE = currentCursorImage.surface->resource(); auto& shmBuffer = CCursorSurfaceRole::cursorPixelData(SURFACE); bool flipRB = false; - if (SURFACE->m_current.texture) { - Log::logger->log(Log::TRACE, "Cursor CPU surface: format {}, expecting AR24", NFormatUtils::drmFormatName(SURFACE->m_current.texture->m_drmFormat)); - if (SURFACE->m_current.texture->m_drmFormat == DRM_FORMAT_ABGR8888) { - Log::logger->log(Log::TRACE, "Cursor CPU surface format AB24, will flip. WARNING: this will break on big endian!"); + if (SURFACE->current.texture) { + Debug::log(TRACE, "Cursor CPU surface: format {}, expecting AR24", NFormatUtils::drmFormatName(SURFACE->current.texture->m_iDrmFormat)); + if (SURFACE->current.texture->m_iDrmFormat == DRM_FORMAT_ABGR8888) { + Debug::log(TRACE, "Cursor CPU surface format AB24, will flip. WARNING: this will break on big endian!"); flipRB = true; - } else if (SURFACE->m_current.texture->m_drmFormat != DRM_FORMAT_ARGB8888) { - Log::logger->log(Log::TRACE, "Cursor CPU surface format rejected, falling back to sw"); + } else if (SURFACE->current.texture->m_iDrmFormat != DRM_FORMAT_ARGB8888) { + Debug::log(TRACE, "Cursor CPU surface format rejected, falling back to sw"); return nullptr; } } @@ -504,7 +483,7 @@ SP CPointerManager::renderHWCursorBuffer(SPm_size.x * 4 * texture->m_size.y); + texData.resize(texture->m_vSize.x * 4 * texture->m_vSize.y); memset(texData.data(), 0x00, texData.size()); } @@ -514,7 +493,7 @@ SP CPointerManager::renderHWCursorBuffer(SPlog(Log::TRACE, "Cannot use dumb copy on dmabuf cursor buffers"); + Debug::log(TRACE, "Cannot use dumb copy on dmabuf cursor buffers"); return nullptr; } } @@ -524,14 +503,15 @@ SP CPointerManager::renderHWCursorBuffer(SPdmabuf(); auto [data, fmt, size] = buf->beginDataPtr(0); - auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, DMABUF.size.x, DMABUF.size.y); - auto CAIRODATASURFACE = cairo_image_surface_create_for_data(texData.data(), CAIRO_FORMAT_ARGB32, texture->m_size.x, texture->m_size.y, texture->m_size.x * 4); + auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, DMABUF.size.x, DMABUF.size.y); + auto CAIRODATASURFACE = + cairo_image_surface_create_for_data((unsigned char*)texData.data(), CAIRO_FORMAT_ARGB32, texture->m_vSize.x, texture->m_vSize.y, texture->m_vSize.x * 4); auto CAIRO = cairo_create(CAIROSURFACE); cairo_set_operator(CAIRO, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(CAIRO, 0, 0, 0, 0); - cairo_rectangle(CAIRO, 0, 0, texture->m_size.x, texture->m_size.y); + cairo_rectangle(CAIRO, 0, 0, texture->m_vSize.x, texture->m_vSize.y); cairo_fill(CAIRO); const auto PATTERNPRE = cairo_pattern_create_for_surface(CAIRODATASURFACE); @@ -539,27 +519,28 @@ SP CPointerManager::renderHWCursorBuffer(SPmonitor->m_transform; + const auto TR = state->monitor->transform; // we need to scale the cursor to the right size, because it might not be (esp with XCursor) - const auto SCALE = texture->m_size / (m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale); - const auto SX = SCALE.x, SY = SCALE.y; - const auto BW = sc(DMABUF.size.x), BH = sc(DMABUF.size.y); + const auto SCALE = texture->m_vSize / (currentCursorImage.size / currentCursorImage.scale * state->monitor->scale); + cairo_matrix_scale(&matrixPre, SCALE.x, SCALE.y); - // Cairo pattern matrix maps destination coords to source coords (inverse of visual transform). - // x_src = xx * x_dst + xy * y_dst + x0 - // y_src = yx * x_dst + yy * y_dst + y0 - // cairo_matrix_init(&m, xx, yx, xy, yy, x0, y0) - switch (TR) { - case WL_OUTPUT_TRANSFORM_NORMAL: - default: cairo_matrix_init(&matrixPre, SX, 0, 0, SY, 0, 0); break; - case WL_OUTPUT_TRANSFORM_90: cairo_matrix_init(&matrixPre, 0, SY, -SX, 0, SX * BW, 0); break; - case WL_OUTPUT_TRANSFORM_180: cairo_matrix_init(&matrixPre, -SX, 0, 0, -SY, SX * BW, SY * BH); break; - case WL_OUTPUT_TRANSFORM_270: cairo_matrix_init(&matrixPre, 0, -SY, SX, 0, 0, SY * BH); break; - case WL_OUTPUT_TRANSFORM_FLIPPED: cairo_matrix_init(&matrixPre, -SX, 0, 0, SY, SX * BW, 0); break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: cairo_matrix_init(&matrixPre, 0, SY, SX, 0, 0, 0); break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: cairo_matrix_init(&matrixPre, SX, 0, 0, -SY, 0, SY * BH); break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: cairo_matrix_init(&matrixPre, 0, -SY, -SX, 0, SX * BW, SY * BH); break; + if (TR) { + cairo_matrix_rotate(&matrixPre, M_PI_2 * (double)TR); + + // FIXME: this is wrong, and doesnt work for 5, 6 and 7. (flipped + rot) + // cba to do it rn, does anyone fucking use that?? + if (TR >= WL_OUTPUT_TRANSFORM_FLIPPED) { + cairo_matrix_scale(&matrixPre, -1, 1); + cairo_matrix_translate(&matrixPre, -DMABUF.size.x, 0); + } + + if (TR == 3 || TR == 7) + cairo_matrix_translate(&matrixPre, -DMABUF.size.x, 0); + else if (TR == 2 || TR == 6) + cairo_matrix_translate(&matrixPre, -DMABUF.size.x, -DMABUF.size.y); + else if (TR == 1 || TR == 5) + cairo_matrix_translate(&matrixPre, 0, -DMABUF.size.y); } cairo_pattern_set_matrix(PATTERNPRE, &matrixPre); @@ -570,7 +551,7 @@ SP CPointerManager::renderHWCursorBuffer(SP(cairo_image_surface_get_height(CAIROSURFACE)) * cairo_image_surface_get_stride(CAIROSURFACE)); + memcpy(data, cairo_image_surface_get_data(CAIROSURFACE), (size_t)cairo_image_surface_get_height(CAIROSURFACE) * cairo_image_surface_get_stride(CAIROSURFACE)); cairo_destroy(CAIRO); cairo_surface_destroy(CAIROSURFACE); @@ -582,112 +563,112 @@ SP CPointerManager::renderHWCursorBuffer(SPmakeEGLCurrent(); - g_pHyprOpenGL->m_renderData.pMonitor = state->monitor; + g_pHyprOpenGL->m_RenderData.pMonitor = state->monitor; - auto RBO = g_pHyprRenderer->getOrCreateRenderbuffer(buf, state->monitor->m_cursorSwapchain->currentOptions().format); + auto RBO = g_pHyprRenderer->getOrCreateRenderbuffer(buf, state->monitor->cursorSwapchain->currentOptions().format); if (!RBO) { - Log::logger->log(Log::TRACE, "Failed to create cursor RB with format {}, mod {}", buf->dmabuf().format, buf->dmabuf().modifier); + Debug::log(TRACE, "Failed to create cursor RB with format {}, mod {}", buf->dmabuf().format, buf->dmabuf().modifier); return nullptr; } RBO->bind(); - g_pHyprOpenGL->beginSimple(state->monitor.lock(), {0, 0, INT_MAX, INT_MAX}, RBO); - g_pHyprOpenGL->clear(CHyprColor{0.F, 0.F, 0.F, 0.F}); // ensure the RBO is zero initialized. + g_pHyprOpenGL->beginSimple(state->monitor.lock(), {0, 0, INT16_MAX, INT16_MAX}, RBO); + g_pHyprOpenGL->clear(CHyprColor{0.F, 0.F, 0.F, 0.F}); - CBox xbox = {{}, Vector2D{m_currentCursorImage.size / m_currentCursorImage.scale * state->monitor->m_scale}.round()}; - Log::logger->log(Log::TRACE, "[pointer] monitor: {}, size: {}, hw buf: {}, scale: {:.2f}, monscale: {:.2f}, xbox: {}", state->monitor->m_name, m_currentCursorImage.size, - cursorSize, m_currentCursorImage.scale, state->monitor->m_scale, xbox.size()); + CBox xbox = {{}, Vector2D{currentCursorImage.size / currentCursorImage.scale * state->monitor->scale}.round()}; + Debug::log(TRACE, "[pointer] monitor: {}, size: {}, hw buf: {}, scale: {:.2f}, monscale: {:.2f}, xbox: {}", state->monitor->szName, currentCursorImage.size, cursorSize, + currentCursorImage.scale, state->monitor->scale, xbox.size()); - g_pHyprOpenGL->renderTexture(texture, xbox, {.noCM = true}); + g_pHyprOpenGL->renderTexture(texture, xbox, 1.F); g_pHyprOpenGL->end(); - g_pHyprOpenGL->m_renderData.pMonitor.reset(); + glFlush(); + g_pHyprOpenGL->m_RenderData.pMonitor.reset(); + + g_pHyprRenderer->onRenderbufferDestroy(RBO.get()); return buf; } -void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, const Time::steady_tp& now, CRegion& damage, std::optional overridePos, bool forceRender) { +void CPointerManager::renderSoftwareCursorsFor(PHLMONITOR pMonitor, timespec* now, CRegion& damage, std::optional overridePos) { if (!hasCursor()) return; auto state = stateFor(pMonitor); - if (!state->hardwareFailed && state->softwareLocks == 0 && !forceRender) { - if (m_currentCursorImage.surface) - m_currentCursorImage.surface->resource()->frame(now); + if ((!state->hardwareFailed && state->softwareLocks == 0)) { + if (currentCursorImage.surface) + currentCursorImage.surface->resource()->frame(now); return; } - // don't render cursor if forced but we are already using sw cursors for the monitor - // otherwise we draw the cursor again for screencopy when using sw cursors - if (forceRender && (state->hardwareFailed || state->softwareLocks != 0)) - return; - auto box = state->box.copy(); if (overridePos.has_value()) { box.x = overridePos->x; box.y = overridePos->y; - - box.translate(-m_currentCursorImage.hotspot); } - if (box.intersection(CBox{{}, {pMonitor->m_size}}).empty()) + if (box.intersection(CBox{{}, {pMonitor->vecSize}}).empty()) return; auto texture = getCurrentCursorTexture(); if (!texture) return; - box.scale(pMonitor->m_scale); + box.scale(pMonitor->scale); box.x = std::round(box.x); box.y = std::round(box.y); CTexPassElement::SRenderData data; - data.tex = texture; - data.box = box.round(); + data.tex = texture; + data.box = box.round(); + data.syncTimeline = currentCursorImage.waitTimeline; + data.syncPoint = currentCursorImage.waitPoint; + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); - g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); + currentCursorImage.waitTimeline.reset(); + currentCursorImage.waitPoint = 0; - if (m_currentCursorImage.surface) - m_currentCursorImage.surface->resource()->frame(now); + if (currentCursorImage.surface) + currentCursorImage.surface->resource()->frame(now); } Vector2D CPointerManager::getCursorPosForMonitor(PHLMONITOR pMonitor) { - return CBox{m_pointerPos - pMonitor->m_position, {0, 0}} - .transform(Math::wlTransformToHyprutils(Math::invertTransform(pMonitor->m_transform)), pMonitor->m_transformedSize.x / pMonitor->m_scale, - pMonitor->m_transformedSize.y / pMonitor->m_scale) + return CBox{pointerPos - pMonitor->vecPosition, {0, 0}} + .transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->vecTransformedSize.x / pMonitor->scale, + pMonitor->vecTransformedSize.y / pMonitor->scale) .pos() * - pMonitor->m_scale; + pMonitor->scale; } Vector2D CPointerManager::transformedHotspot(PHLMONITOR pMonitor) { - if (!pMonitor->m_cursorSwapchain) + if (!pMonitor->cursorSwapchain) return {}; // doesn't matter, we have no hw cursor, and this is only for hw cursors - return CBox{m_currentCursorImage.hotspot * pMonitor->m_scale, {0, 0}} - .transform(Math::wlTransformToHyprutils(Math::invertTransform(pMonitor->m_transform)), pMonitor->m_cursorSwapchain->currentOptions().size.x, - pMonitor->m_cursorSwapchain->currentOptions().size.y) + return CBox{currentCursorImage.hotspot * pMonitor->scale, {0, 0}} + .transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->cursorSwapchain->currentOptions().size.x, + pMonitor->cursorSwapchain->currentOptions().size.y) .pos(); } CBox CPointerManager::getCursorBoxLogicalForMonitor(PHLMONITOR pMonitor) { - return getCursorBoxGlobal().translate(-pMonitor->m_position); + return getCursorBoxGlobal().translate(-pMonitor->vecPosition); } CBox CPointerManager::getCursorBoxGlobal() { - return CBox{m_pointerPos, m_currentCursorImage.size / m_currentCursorImage.scale}.translate(-m_currentCursorImage.hotspot); + return CBox{pointerPos, currentCursorImage.size / currentCursorImage.scale}.translate(-currentCursorImage.hotspot); } Vector2D CPointerManager::closestValid(const Vector2D& pos) { static auto PADDING = CConfigValue("cursor:hotspot_padding"); - auto CURSOR_PADDING = std::clamp(sc(*PADDING), 0, 100); + auto CURSOR_PADDING = std::clamp((int)*PADDING, 0, 100); CBox hotBox = {{pos.x - CURSOR_PADDING, pos.y - CURSOR_PADDING}, {2 * CURSOR_PADDING, 2 * CURSOR_PADDING}}; // static auto INSIDE_LAYOUT = [this](const CBox& box) -> bool { - for (auto const& b : m_currentMonitorLayout.monitorBoxes) { + for (auto const& b : currentMonitorLayout.monitorBoxes) { if (box.inside(b)) return true; } @@ -695,7 +676,7 @@ Vector2D CPointerManager::closestValid(const Vector2D& pos) { }; static auto INSIDE_LAYOUT_COORD = [this](const Vector2D& vec) -> bool { - for (auto const& b : m_currentMonitorLayout.monitorBoxes) { + for (auto const& b : currentMonitorLayout.monitorBoxes) { if (b.containsPoint(vec)) return true; } @@ -706,7 +687,7 @@ Vector2D CPointerManager::closestValid(const Vector2D& pos) { Vector2D leader; float distanceSq = __FLT_MAX__; - for (auto const& b : m_currentMonitorLayout.monitorBoxes) { + for (auto const& b : currentMonitorLayout.monitorBoxes) { auto p = b.closestPoint(vec); auto distSq = p.distanceSq(vec); @@ -756,33 +737,24 @@ Vector2D CPointerManager::closestValid(const Vector2D& pos) { } void CPointerManager::damageIfSoftware() { - if (g_pCompositor->m_unsafeState) - return; - auto b = getCursorBoxGlobal().expand(4); - for (auto const& mw : m_monitorStates) { - auto monitor = mw->monitor.lock(); - if (!monitor || !monitor->m_output || monitor->isMirror()) + for (auto const& mw : monitorStates) { + if (mw->monitor.expired() || !mw->monitor->output) continue; - auto usesSoftwareCursor = (mw->softwareLocks > 0 || mw->hardwareFailed || g_pConfigManager->shouldUseSoftwareCursors(monitor)); - if (!usesSoftwareCursor) - continue; - - auto shouldAddDamage = !monitor->shouldSkipScheduleFrameOnMouseEvent() && b.overlaps({monitor->m_position, monitor->m_size}); - if (!shouldAddDamage) - continue; - - CBox damageBox = b.copy().translate(-monitor->m_position).scale(monitor->m_scale).round(); - monitor->addDamage(damageBox); + if ((mw->softwareLocks > 0 || mw->hardwareFailed || g_pConfigManager->shouldUseSoftwareCursors(mw->monitor.lock())) && + b.overlaps({mw->monitor->vecPosition, mw->monitor->vecSize})) { + g_pHyprRenderer->damageBox(b, mw->monitor->shouldSkipScheduleFrameOnMouseEvent()); + break; + } } } void CPointerManager::warpTo(const Vector2D& logical) { damageIfSoftware(); - m_pointerPos = closestValid(logical); + pointerPos = closestValid(logical); if (!g_pInputManager->isLocked()) { recheckEnteredOutputs(); @@ -793,7 +765,7 @@ void CPointerManager::warpTo(const Vector2D& logical) { } void CPointerManager::move(const Vector2D& deltaLogical) { - const auto oldPos = m_pointerPos; + const auto oldPos = pointerPos; auto newPos = oldPos + Vector2D{std::isnan(deltaLogical.x) ? 0.0 : deltaLogical.x, std::isnan(deltaLogical.y) ? 0.0 : deltaLogical.y}; warpTo(newPos); @@ -809,8 +781,8 @@ void CPointerManager::warpAbsolute(Vector2D abs, SP dev) { abs.y = std::clamp(abs.y, 0.0, 1.0); // find x and y size of the entire space - const auto& MONITORS = g_pCompositor->m_monitors; - Vector2D topLeft = MONITORS.at(0)->m_position, bottomRight = MONITORS.at(0)->m_position + MONITORS.at(0)->m_size; + const auto& MONITORS = g_pCompositor->m_vMonitors; + Vector2D topLeft = MONITORS.at(0)->vecPosition, bottomRight = MONITORS.at(0)->vecPosition + MONITORS.at(0)->vecSize; for (size_t i = 1; i < MONITORS.size(); ++i) { const auto EXTENT = MONITORS[i]->logicalBox().extent(); const auto POS = MONITORS[i]->logicalBox().pos(); @@ -827,7 +799,7 @@ void CPointerManager::warpAbsolute(Vector2D abs, SP dev) { auto outputMappedArea = [&mappedArea](const std::string& output) { if (output == "current") { - if (const auto PLASTMONITOR = Desktop::focusState()->monitor(); PLASTMONITOR) + if (const auto PLASTMONITOR = g_pCompositor->m_pLastMonitor.lock(); PLASTMONITOR) return PLASTMONITOR->logicalBox(); } else if (const auto PMONITOR = g_pCompositor->getMonitorFromString(output); PMONITOR) return PMONITOR->logicalBox(); @@ -836,32 +808,32 @@ void CPointerManager::warpAbsolute(Vector2D abs, SP dev) { switch (dev->getType()) { case HID_TYPE_TABLET: { - CTablet* TAB = rc(dev.get()); - if (!TAB->m_boundOutput.empty()) { - mappedArea = outputMappedArea(TAB->m_boundOutput); - mappedArea.translate(TAB->m_boundBox.pos()); - } else if (TAB->m_absolutePos) { - mappedArea.x = TAB->m_boundBox.x; - mappedArea.y = TAB->m_boundBox.y; + CTablet* TAB = reinterpret_cast(dev.get()); + if (!TAB->boundOutput.empty()) { + mappedArea = outputMappedArea(TAB->boundOutput); + mappedArea.translate(TAB->boundBox.pos()); + } else if (TAB->absolutePos) { + mappedArea.x = TAB->boundBox.x; + mappedArea.y = TAB->boundBox.y; } else - mappedArea.translate(TAB->m_boundBox.pos()); + mappedArea.translate(TAB->boundBox.pos()); - if (!TAB->m_boundBox.empty()) { - mappedArea.w = TAB->m_boundBox.w; - mappedArea.h = TAB->m_boundBox.h; + if (!TAB->boundBox.empty()) { + mappedArea.w = TAB->boundBox.w; + mappedArea.h = TAB->boundBox.h; } break; } case HID_TYPE_TOUCH: { - ITouch* TOUCH = rc(dev.get()); - if (!TOUCH->m_boundOutput.empty()) - mappedArea = outputMappedArea(TOUCH->m_boundOutput); + ITouch* TOUCH = reinterpret_cast(dev.get()); + if (!TOUCH->boundOutput.empty()) + mappedArea = outputMappedArea(TOUCH->boundOutput); break; } case HID_TYPE_POINTER: { - IPointer* POINTER = rc(dev.get()); - if (!POINTER->m_boundOutput.empty()) - mappedArea = outputMappedArea(POINTER->m_boundOutput); + IPointer* POINTER = reinterpret_cast(dev.get()); + if (!POINTER->boundOutput.empty()) + mappedArea = outputMappedArea(POINTER->boundOutput); break; } default: break; @@ -870,10 +842,10 @@ void CPointerManager::warpAbsolute(Vector2D abs, SP dev) { damageIfSoftware(); if (std::isnan(abs.x) || std::isnan(abs.y)) { - m_pointerPos.x = std::isnan(abs.x) ? m_pointerPos.x : mappedArea.x + mappedArea.w * abs.x; - m_pointerPos.y = std::isnan(abs.y) ? m_pointerPos.y : mappedArea.y + mappedArea.h * abs.y; + pointerPos.x = std::isnan(abs.x) ? pointerPos.x : mappedArea.x + mappedArea.w * abs.x; + pointerPos.y = std::isnan(abs.y) ? pointerPos.y : mappedArea.y + mappedArea.h * abs.y; } else - m_pointerPos = mappedArea.pos() + mappedArea.size() * abs; + pointerPos = mappedArea.pos() + mappedArea.size() * abs; onCursorMoved(); recheckEnteredOutputs(); @@ -882,38 +854,34 @@ void CPointerManager::warpAbsolute(Vector2D abs, SP dev) { } void CPointerManager::onMonitorLayoutChange() { - m_currentMonitorLayout.monitorBoxes.clear(); - for (auto const& m : g_pCompositor->m_monitors) { - if (m->isMirror() || !m->m_enabled || !m->m_output) + currentMonitorLayout.monitorBoxes.clear(); + for (auto const& m : g_pCompositor->m_vMonitors) { + if (m->isMirror() || !m->m_bEnabled || !m->output) continue; - m_currentMonitorLayout.monitorBoxes.emplace_back(m->m_position, m->m_size); + currentMonitorLayout.monitorBoxes.emplace_back(m->vecPosition, m->vecSize); } damageIfSoftware(); - m_pointerPos = closestValid(m_pointerPos); + pointerPos = closestValid(pointerPos); updateCursorBackend(); recheckEnteredOutputs(); damageIfSoftware(); } -const CPointerManager::SCursorImage& CPointerManager::currentCursorImage() { - return m_currentCursorImage; -} - -SP CPointerManager::getCurrentCursorTexture() { - if (!m_currentCursorImage.pBuffer && (!m_currentCursorImage.surface || !m_currentCursorImage.surface->resource()->m_current.texture)) +SP CPointerManager::getCurrentCursorTexture() { + if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !currentCursorImage.surface->resource()->current.texture)) return nullptr; - if (m_currentCursorImage.pBuffer) { - if (!m_currentCursorImage.bufferTex) - m_currentCursorImage.bufferTex = g_pHyprRenderer->createTexture(m_currentCursorImage.pBuffer, true); - return m_currentCursorImage.bufferTex; + if (currentCursorImage.pBuffer) { + if (!currentCursorImage.bufferTex) + currentCursorImage.bufferTex = makeShared(currentCursorImage.pBuffer, true); + return currentCursorImage.bufferTex; } - return m_currentCursorImage.surface->resource()->m_current.texture; + return currentCursorImage.surface->resource()->current.texture; } void CPointerManager::attachPointer(SP pointer) { @@ -923,91 +891,136 @@ void CPointerManager::attachPointer(SP pointer) { static auto PMOUSEDPMS = CConfigValue("misc:mouse_move_enables_dpms"); // - auto listener = m_pointerListeners.emplace_back(makeShared()); + auto listener = pointerListeners.emplace_back(makeShared()); listener->pointer = pointer; - listener->destroy = pointer->m_events.destroy.listen([this] { detachPointer(nullptr); }); - listener->motion = pointer->m_pointerEvents.motion.listen([](const IPointer::SMotionEvent& event) { - g_pInputManager->onMouseMoved(event); + // clang-format off + listener->destroy = pointer->events.destroy.registerListener([this] (std::any d) { + detachPointer(nullptr); + }); + + listener->motion = pointer->pointerEvents.motion.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onMouseMoved(E); PROTO::idle->onActivity(); - if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS) - CKeybindManager::dpms("on"); + if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) + g_pKeybindManager->dpms("on"); }); - listener->motionAbsolute = pointer->m_pointerEvents.motionAbsolute.listen([](const IPointer::SMotionAbsoluteEvent& event) { - g_pInputManager->onMouseWarp(event); + listener->motionAbsolute = pointer->pointerEvents.motionAbsolute.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onMouseWarp(E); PROTO::idle->onActivity(); - if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS) - CKeybindManager::dpms("on"); + if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) + g_pKeybindManager->dpms("on"); }); - listener->button = pointer->m_pointerEvents.button.listen([](const IPointer::SButtonEvent& event) { - g_pInputManager->onMouseButton(event); - PROTO::idle->onActivity(); - }); + listener->button = pointer->pointerEvents.button.registerListener([] (std::any e) { + auto E = std::any_cast(e); - listener->axis = pointer->m_pointerEvents.axis.listen([weak = WP(pointer)](const IPointer::SAxisEvent& event) { - g_pInputManager->onMouseWheel(event, weak.lock()); - PROTO::idle->onActivity(); - }); - listener->frame = pointer->m_pointerEvents.frame.listen([] { g_pInputManager->onPointerFrame(); }); - - listener->swipeBegin = pointer->m_pointerEvents.swipeBegin.listen([](const IPointer::SSwipeBeginEvent& event) { - g_pInputManager->onSwipeBegin(event); - - PROTO::idle->onActivity(); - - if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS) - CKeybindManager::dpms("on"); - }); - - listener->swipeEnd = pointer->m_pointerEvents.swipeEnd.listen([](const IPointer::SSwipeEndEvent& event) { - g_pInputManager->onSwipeEnd(event); - PROTO::idle->onActivity(); - }); - - listener->swipeUpdate = pointer->m_pointerEvents.swipeUpdate.listen([](const IPointer::SSwipeUpdateEvent& event) { - g_pInputManager->onSwipeUpdate(event); - PROTO::idle->onActivity(); - }); - - listener->pinchBegin = pointer->m_pointerEvents.pinchBegin.listen([](const IPointer::SPinchBeginEvent& event) { - g_pInputManager->onPinchBegin(event); - - PROTO::idle->onActivity(); - - if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS) - CKeybindManager::dpms("on"); - }); - - listener->pinchEnd = pointer->m_pointerEvents.pinchEnd.listen([](const IPointer::SPinchEndEvent& event) { - g_pInputManager->onPinchEnd(event); + g_pInputManager->onMouseButton(E); PROTO::idle->onActivity(); }); - listener->pinchUpdate = pointer->m_pointerEvents.pinchUpdate.listen([](const IPointer::SPinchUpdateEvent& event) { - g_pInputManager->onPinchUpdate(event); + listener->axis = pointer->pointerEvents.axis.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onMouseWheel(E); PROTO::idle->onActivity(); }); - listener->holdBegin = pointer->m_pointerEvents.holdBegin.listen([](const IPointer::SHoldBeginEvent& event) { - PROTO::pointerGestures->holdBegin(event.timeMs, event.fingers); + listener->frame = pointer->pointerEvents.frame.registerListener([] (std::any e) { + bool shouldSkip = false; + if (!g_pSeatManager->mouse.expired() && g_pInputManager->isLocked()) { + auto PMONITOR = g_pCompositor->m_pLastMonitor.get(); + shouldSkip = PMONITOR && PMONITOR->shouldSkipScheduleFrameOnMouseEvent(); + } + g_pSeatManager->isPointerFrameSkipped = shouldSkip; + if (!g_pSeatManager->isPointerFrameSkipped) + g_pSeatManager->sendPointerFrame(); + }); + + listener->swipeBegin = pointer->pointerEvents.swipeBegin.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onSwipeBegin(E); + + PROTO::idle->onActivity(); + + if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) + g_pKeybindManager->dpms("on"); + }); + + listener->swipeEnd = pointer->pointerEvents.swipeEnd.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onSwipeEnd(E); + PROTO::idle->onActivity(); }); - listener->holdEnd = pointer->m_pointerEvents.holdEnd.listen([](const IPointer::SHoldEndEvent& event) { - PROTO::pointerGestures->holdEnd(event.timeMs, event.cancelled); + listener->swipeUpdate = pointer->pointerEvents.swipeUpdate.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onSwipeUpdate(E); + PROTO::idle->onActivity(); }); - Log::logger->log(Log::DEBUG, "Attached pointer {} to global", pointer->m_hlName); + listener->pinchBegin = pointer->pointerEvents.pinchBegin.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + PROTO::pointerGestures->pinchBegin(E.timeMs, E.fingers); + + PROTO::idle->onActivity(); + + if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) + g_pKeybindManager->dpms("on"); + }); + + listener->pinchEnd = pointer->pointerEvents.pinchEnd.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + PROTO::pointerGestures->pinchEnd(E.timeMs, E.cancelled); + + PROTO::idle->onActivity(); + }); + + listener->pinchUpdate = pointer->pointerEvents.pinchUpdate.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + PROTO::pointerGestures->pinchUpdate(E.timeMs, E.delta, E.scale, E.rotation); + + PROTO::idle->onActivity(); + }); + + listener->holdBegin = pointer->pointerEvents.holdBegin.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + PROTO::pointerGestures->holdBegin(E.timeMs, E.fingers); + + PROTO::idle->onActivity(); + }); + + listener->holdEnd = pointer->pointerEvents.holdEnd.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + PROTO::pointerGestures->holdEnd(E.timeMs, E.cancelled); + + PROTO::idle->onActivity(); + }); + // clang-format on + + Debug::log(LOG, "Attached pointer {} to global", pointer->hlName); } void CPointerManager::attachTouch(SP touch) { @@ -1017,38 +1030,52 @@ void CPointerManager::attachTouch(SP touch) { static auto PMOUSEDPMS = CConfigValue("misc:mouse_move_enables_dpms"); // - auto listener = m_touchListeners.emplace_back(makeShared()); + auto listener = touchListeners.emplace_back(makeShared()); listener->touch = touch; - listener->destroy = touch->m_events.destroy.listen([this] { detachTouch(nullptr); }); + // clang-format off + listener->destroy = touch->events.destroy.registerListener([this] (std::any d) { + detachTouch(nullptr); + }); - listener->down = touch->m_touchEvents.down.listen([](const ITouch::SDownEvent& event) { - g_pInputManager->onTouchDown(event); + listener->down = touch->touchEvents.down.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onTouchDown(E); PROTO::idle->onActivity(); - if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS) - CKeybindManager::dpms("on"); + if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) + g_pKeybindManager->dpms("on"); }); - listener->up = touch->m_touchEvents.up.listen([](const ITouch::SUpEvent& event) { - g_pInputManager->onTouchUp(event); + listener->up = touch->touchEvents.up.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onTouchUp(E); + PROTO::idle->onActivity(); }); - listener->motion = touch->m_touchEvents.motion.listen([](const ITouch::SMotionEvent& event) { - g_pInputManager->onTouchMove(event); + listener->motion = touch->touchEvents.motion.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onTouchMove(E); + PROTO::idle->onActivity(); }); - listener->cancel = touch->m_touchEvents.cancel.listen([] { + listener->cancel = touch->touchEvents.cancel.registerListener([] (std::any e) { // }); - listener->frame = touch->m_touchEvents.frame.listen([] { g_pSeatManager->sendTouchFrame(); }); + listener->frame = touch->touchEvents.frame.registerListener([] (std::any e) { + g_pSeatManager->sendTouchFrame(); + }); + // clang-format on - Log::logger->log(Log::DEBUG, "Attached touch {} to global", touch->m_hlName); + Debug::log(LOG, "Attached touch {} to global", touch->hlName); } void CPointerManager::attachTablet(SP tablet) { @@ -1058,58 +1085,71 @@ void CPointerManager::attachTablet(SP tablet) { static auto PMOUSEDPMS = CConfigValue("misc:mouse_move_enables_dpms"); // - auto listener = m_tabletListeners.emplace_back(makeShared()); + auto listener = tabletListeners.emplace_back(makeShared()); listener->tablet = tablet; - listener->destroy = tablet->m_events.destroy.listen([this] { detachTablet(nullptr); }); + // clang-format off + listener->destroy = tablet->events.destroy.registerListener([this] (std::any d) { + detachTablet(nullptr); + }); - listener->axis = tablet->m_tabletEvents.axis.listen([](const CTablet::SAxisEvent& event) { - g_pInputManager->onTabletAxis(event); + listener->axis = tablet->tabletEvents.axis.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onTabletAxis(E); PROTO::idle->onActivity(); - if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS) - CKeybindManager::dpms("on"); + if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) + g_pKeybindManager->dpms("on"); }); - listener->proximity = tablet->m_tabletEvents.proximity.listen([](const CTablet::SProximityEvent& event) { - g_pInputManager->onTabletProximity(event); + listener->proximity = tablet->tabletEvents.proximity.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onTabletProximity(E); + PROTO::idle->onActivity(); }); - listener->tip = tablet->m_tabletEvents.tip.listen([](const CTablet::STipEvent& event) { - g_pInputManager->onTabletTip(event); + listener->tip = tablet->tabletEvents.tip.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onTabletTip(E); PROTO::idle->onActivity(); - if (!g_pCompositor->m_dpmsStateOn && *PMOUSEDPMS) - CKeybindManager::dpms("on"); + if (!g_pCompositor->m_bDPMSStateON && *PMOUSEDPMS) + g_pKeybindManager->dpms("on"); }); - listener->button = tablet->m_tabletEvents.button.listen([](const CTablet::SButtonEvent& event) { - g_pInputManager->onTabletButton(event); + listener->button = tablet->tabletEvents.button.registerListener([] (std::any e) { + auto E = std::any_cast(e); + + g_pInputManager->onTabletButton(E); + PROTO::idle->onActivity(); }); // clang-format on - Log::logger->log(Log::DEBUG, "Attached tablet {} to global", tablet->m_hlName); + Debug::log(LOG, "Attached tablet {} to global", tablet->hlName); } void CPointerManager::detachPointer(SP pointer) { - std::erase_if(m_pointerListeners, [pointer](const auto& e) { return e->pointer.expired() || e->pointer == pointer; }); + std::erase_if(pointerListeners, [pointer](const auto& e) { return e->pointer.expired() || e->pointer == pointer; }); } void CPointerManager::detachTouch(SP touch) { - std::erase_if(m_touchListeners, [touch](const auto& e) { return e->touch.expired() || e->touch == touch; }); + std::erase_if(touchListeners, [touch](const auto& e) { return e->touch.expired() || e->touch == touch; }); } void CPointerManager::detachTablet(SP tablet) { - std::erase_if(m_tabletListeners, [tablet](const auto& e) { return e->tablet.expired() || e->tablet == tablet; }); + std::erase_if(tabletListeners, [tablet](const auto& e) { return e->tablet.expired() || e->tablet == tablet; }); } -void CPointerManager::damageCursor(PHLMONITOR pMonitor, bool skipFrameSchedule) { - for (auto const& mw : m_monitorStates) { +void CPointerManager::damageCursor(PHLMONITOR pMonitor) { + for (auto const& mw : monitorStates) { if (mw->monitor != pMonitor) continue; @@ -1118,12 +1158,31 @@ void CPointerManager::damageCursor(PHLMONITOR pMonitor, bool skipFrameSchedule) if (b.empty()) return; - g_pHyprRenderer->damageBox(b, skipFrameSchedule); + g_pHyprRenderer->damageBox(b); return; } } Vector2D CPointerManager::cursorSizeLogical() { - return m_currentCursorImage.size / m_currentCursorImage.scale; + return currentCursorImage.size / currentCursorImage.scale; +} + +void CPointerManager::storeMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { + storedTime = time; + storedDelta += delta; + storedUnaccel += deltaUnaccel; +} + +void CPointerManager::setStoredMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { + storedTime = time; + storedDelta = delta; + storedUnaccel = deltaUnaccel; +} + +void CPointerManager::sendStoredMovement() { + PROTO::relativePointer->sendRelativeMotion((uint64_t)storedTime * 1000, storedDelta, storedUnaccel); + storedTime = 0; + storedDelta = Vector2D{}; + storedUnaccel = Vector2D{}; } diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp index a4fe1971..d00b55ba 100644 --- a/src/managers/PointerManager.hpp +++ b/src/managers/PointerManager.hpp @@ -4,15 +4,13 @@ #include "../devices/ITouch.hpp" #include "../devices/Tablet.hpp" #include "../helpers/math/Math.hpp" -#include "../desktop/view/WLSurface.hpp" +#include "../desktop/WLSurface.hpp" #include "../helpers/sync/SyncTimeline.hpp" -#include "../helpers/time/Time.hpp" -#include "../helpers/signal/Signal.hpp" #include class CMonitor; class IHID; -class ITexture; +class CTexture; AQUAMARINE_FORWARD(IBuffer); @@ -41,7 +39,7 @@ class CPointerManager { void warpAbsolute(Vector2D abs, SP dev); void setCursorBuffer(SP buf, const Vector2D& hotspot, const float& scale); - void setCursorSurface(SP buf, const Vector2D& hotspot); + void setCursorSurface(SP buf, const Vector2D& hotspot); void resetCursorImage(bool apply = true); void lockSoftwareForMonitor(PHLMONITOR pMonitor); @@ -49,46 +47,23 @@ class CPointerManager { void lockSoftwareAll(); void unlockSoftwareAll(); bool softwareLockedFor(PHLMONITOR pMonitor); - bool hasVisibleHWCursor(PHLMONITOR pMonitor); - void renderSoftwareCursorsFor(PHLMONITOR pMonitor, const Time::steady_tp& now, CRegion& damage /* logical */, std::optional overridePos = {} /* monitor-local */, - bool forceRender = false); + void renderSoftwareCursorsFor(PHLMONITOR pMonitor, timespec* now, CRegion& damage /* logical */, std::optional overridePos = {} /* monitor-local */); // this is needed e.g. during screensharing where // the software cursors aren't locked during the cursor move, but they // are rendered later. - void damageCursor(PHLMONITOR pMonitor, bool skipFrameSchedule = false); + void damageCursor(PHLMONITOR pMonitor); // Vector2D position(); - Vector2D hotspot(); Vector2D cursorSizeLogical(); + void storeMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel); + void setStoredMovement(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel); + void sendStoredMovement(); void recheckEnteredOutputs(); - // returns the thing in global coords - CBox getCursorBoxGlobal(); - - struct SCursorImage { - SP pBuffer; - SP bufferTex; - WP surface; - - Vector2D hotspot; - Vector2D size; - float scale = 1.F; - - CHyprSignalListener destroySurface; - CHyprSignalListener commitSurface; - }; - - const SCursorImage& currentCursorImage(); - SP getCurrentCursorTexture(); - - struct { - CSignalT<> cursorChanged; - } m_events; - private: void recheckPointerPosition(); void onMonitorLayoutChange(); @@ -104,9 +79,13 @@ class CPointerManager { // returns the thing in device coordinates. Is NOT offset by the hotspot, relies on set_cursor with hotspot. Vector2D getCursorPosForMonitor(PHLMONITOR pMonitor); // returns the thing in logical coordinates of the monitor - CBox getCursorBoxLogicalForMonitor(PHLMONITOR pMonitor); + CBox getCursorBoxLogicalForMonitor(PHLMONITOR pMonitor); + // returns the thing in global coords + CBox getCursorBoxGlobal(); - Vector2D transformedHotspot(PHLMONITOR pMonitor); + Vector2D transformedHotspot(PHLMONITOR pMonitor); + + SP getCurrentCursorTexture(); struct SPointerListener { CHyprSignalListener destroy; @@ -129,7 +108,7 @@ class CPointerManager { WP pointer; }; - std::vector> m_pointerListeners; + std::vector> pointerListeners; struct STouchListener { CHyprSignalListener destroy; @@ -141,7 +120,7 @@ class CPointerManager { WP touch; }; - std::vector> m_touchListeners; + std::vector> touchListeners; struct STabletListener { CHyprSignalListener destroy; @@ -152,15 +131,32 @@ class CPointerManager { WP tablet; }; - std::vector> m_tabletListeners; + std::vector> tabletListeners; struct { std::vector monitorBoxes; - } m_currentMonitorLayout; + } currentMonitorLayout; - SCursorImage m_currentCursorImage; // TODO: support various sizes per-output so we can have pixel-perfect cursors + struct { + SP pBuffer; + SP bufferTex; + WP surface; - Vector2D m_pointerPos = {0, 0}; + Vector2D hotspot; + Vector2D size; + float scale = 1.F; + + CHyprSignalListener destroySurface; + CHyprSignalListener commitSurface; + SP waitTimeline = nullptr; + uint64_t waitPoint = 0; + } currentCursorImage; // TODO: support various sizes per-output so we can have pixel-perfect cursors + + Vector2D pointerPos = {0, 0}; + + uint64_t storedTime = 0; + Vector2D storedDelta = {0, 0}; + Vector2D storedUnaccel = {0, 0}; struct SMonitorPointerState { SMonitorPointerState(const PHLMONITOR& m) : monitor(m) {} @@ -178,16 +174,16 @@ class CPointerManager { SP cursorFrontBuffer; }; - std::vector> m_monitorStates; + std::vector> monitorStates; SP stateFor(PHLMONITOR mon); bool attemptHardwareCursor(SP state); - SP renderHWCursorBuffer(SP state, SP texture); + SP renderHWCursorBuffer(SP state, SP texture); bool setHWCursorBuffer(SP state, SP buf); struct { - CHyprSignalListener monitorAdded; - CHyprSignalListener monitorPreRender; - } m_hooks; + SP monitorAdded; + SP monitorPreRender; + } hooks; }; inline UP g_pPointerManager; diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index c13e6e48..06987dae 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -42,7 +42,6 @@ #include "../protocols/DRMSyncobj.hpp" #include "../protocols/Screencopy.hpp" #include "../protocols/ToplevelExport.hpp" -#include "../protocols/ToplevelMapping.hpp" #include "../protocols/TextInputV1.hpp" #include "../protocols/GlobalShortcuts.hpp" #include "../protocols/XDGDialog.hpp" @@ -50,8 +49,7 @@ #include "../protocols/SecurityContext.hpp" #include "../protocols/CTMControl.hpp" #include "../protocols/HyprlandSurface.hpp" -#include "../protocols/ImageCaptureSource.hpp" -#include "../protocols/ImageCopyCapture.hpp" + #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" #include "../protocols/core/Compositor.hpp" @@ -59,25 +57,17 @@ #include "../protocols/core/Output.hpp" #include "../protocols/core/Shm.hpp" #include "../protocols/ColorManagement.hpp" +#include "../protocols/XXColorManagement.hpp" +#include "../protocols/FrogColorManagement.hpp" #include "../protocols/ContentType.hpp" -#include "../protocols/XDGTag.hpp" -#include "../protocols/XDGBell.hpp" -#include "../protocols/ExtWorkspace.hpp" -#include "../protocols/ExtDataDevice.hpp" -#include "../protocols/PointerWarp.hpp" -#include "../protocols/Fifo.hpp" -#include "../protocols/CommitTiming.hpp" #include "../helpers/Monitor.hpp" -#include "../event/EventBus.hpp" #include "../render/Renderer.hpp" #include "../Compositor.hpp" #include "content-type-v1.hpp" #include -#include #include -#include // ******************************************************************************************** // * IMPORTANT: make sure to .reset() any protocol UP's you create! (put reset in destructor) * @@ -91,50 +81,53 @@ void CProtocolManager::onMonitorModeChange(PHLMONITOR pMonitor) { // mirrored outputs should have their global removed, as they are not physical parts of the // layout. - if (ISMIRROR && PROTO::outputs.contains(pMonitor->m_name)) - PROTO::outputs.at(pMonitor->m_name)->remove(); - else if (!ISMIRROR && (!PROTO::outputs.contains(pMonitor->m_name) || PROTO::outputs.at(pMonitor->m_name)->isDefunct())) { - if (PROTO::outputs.contains(pMonitor->m_name)) - PROTO::outputs.erase(pMonitor->m_name); - PROTO::outputs.emplace(pMonitor->m_name, makeShared(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->m_name), pMonitor->m_self.lock())); + if (ISMIRROR && PROTO::outputs.contains(pMonitor->szName)) + PROTO::outputs.at(pMonitor->szName)->remove(); + else if (!ISMIRROR && (!PROTO::outputs.contains(pMonitor->szName) || PROTO::outputs.at(pMonitor->szName)->isDefunct())) { + if (PROTO::outputs.contains(pMonitor->szName)) + PROTO::outputs.erase(pMonitor->szName); + PROTO::outputs.emplace(pMonitor->szName, makeShared(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock())); } if (PROTO::colorManagement && g_pCompositor->shouldChangePreferredImageDescription()) { - Log::logger->log(Log::ERR, "FIXME: color management protocol is enabled, need a preferred image description id"); + Debug::log(ERR, "FIXME: color management protocol is enabled, need a preferred image description id"); PROTO::colorManagement->onImagePreferredChanged(0); } } CProtocolManager::CProtocolManager() { - static const auto PENABLECM = CConfigValue("render:cm_enabled"); - static const auto PDEBUGCM = CConfigValue("debug:full_cm_proto"); - - static const auto PENABLECT = CConfigValue("render:commit_timing_enabled"); + static const auto PENABLEEXPLICIT = CConfigValue("render:explicit_sync"); + static const auto PENABLECM = CConfigValue("render:cm_enabled"); + static const auto PENABLEXXCM = CConfigValue("experimental:xx_color_management_v4"); + static const auto PDEBUGCM = CConfigValue("debug:full_cm_proto"); // Outputs are a bit dumb, we have to agree. - static auto P = Event::bus()->m_events.monitor.added.listen([this](PHLMONITOR M) { + static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) { + auto M = std::any_cast(param); + // ignore mirrored outputs. I don't think this will ever be hit as mirrors are applied after // this event is emitted iirc. // also ignore the fallback - if (M->isMirror() || M == g_pCompositor->m_unsafeOutput) + if (M->isMirror() || M == g_pCompositor->m_pUnsafeOutput) return; - if (PROTO::outputs.contains(M->m_name)) - PROTO::outputs.erase(M->m_name); + if (PROTO::outputs.contains(M->szName)) + PROTO::outputs.erase(M->szName); - auto ref = makeShared(&wl_output_interface, 4, std::format("WLOutput ({})", M->m_name), M->m_self.lock()); - PROTO::outputs.emplace(M->m_name, ref); - ref->m_self = ref; + auto ref = makeShared(&wl_output_interface, 4, std::format("WLOutput ({})", M->szName), M->self.lock()); + PROTO::outputs.emplace(M->szName, ref); + ref->self = ref; - m_modeChangeListeners[M->m_name] = M->m_events.modeChanged.listen([this, M] { onMonitorModeChange(M); }); + m_mModeChangeListeners[M->szName] = M->events.modeChanged.registerListener([M, this](std::any d) { onMonitorModeChange(M); }); }); - static auto P2 = Event::bus()->m_events.monitor.removed.listen([this](PHLMONITOR M) { - if (!PROTO::outputs.contains(M->m_name)) + static auto P2 = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) { + auto M = std::any_cast(param); + if (!PROTO::outputs.contains(M->szName)) return; - PROTO::outputs.at(M->m_name)->remove(); - m_modeChangeListeners.erase(M->m_name); + PROTO::outputs.at(M->szName)->remove(); + m_mModeChangeListeners.erase(M->szName); }); // Core @@ -142,14 +135,14 @@ CProtocolManager::CProtocolManager() { PROTO::data = makeUnique(&wl_data_device_manager_interface, 3, "WLDataDevice"); PROTO::compositor = makeUnique(&wl_compositor_interface, 6, "WLCompositor"); PROTO::subcompositor = makeUnique(&wl_subcompositor_interface, 1, "WLSubcompositor"); - PROTO::shm = makeUnique(&wl_shm_interface, 2, "WLSHM"); + PROTO::shm = makeUnique(&wl_shm_interface, 1, "WLSHM"); // Extensions PROTO::viewport = makeUnique(&wp_viewporter_interface, 1, "Viewporter"); PROTO::tearing = makeUnique(&wp_tearing_control_manager_v1_interface, 1, "TearingControl"); PROTO::fractional = makeUnique(&wp_fractional_scale_manager_v1_interface, 1, "FractionalScale"); PROTO::xdgOutput = makeUnique(&zxdg_output_manager_v1_interface, 3, "XDGOutput"); - PROTO::cursorShape = makeUnique(&wp_cursor_shape_manager_v1_interface, 2, "CursorShape"); + PROTO::cursorShape = makeUnique(&wp_cursor_shape_manager_v1_interface, 1, "CursorShape"); PROTO::idleInhibit = makeUnique(&zwp_idle_inhibit_manager_v1_interface, 1, "IdleInhibit"); PROTO::relativePointer = makeUnique(&zwp_relative_pointer_manager_v1_interface, 1, "RelativePointer"); PROTO::xdgDecoration = makeUnique(&zxdg_decoration_manager_v1_interface, 1, "XDGDecoration"); @@ -175,12 +168,13 @@ CProtocolManager::CProtocolManager() { PROTO::focusGrab = makeUnique(&hyprland_focus_grab_manager_v1_interface, 1, "FocusGrab"); PROTO::tablet = makeUnique(&zwp_tablet_manager_v2_interface, 1, "TabletV2"); PROTO::layerShell = makeUnique(&zwlr_layer_shell_v1_interface, 5, "LayerShell"); - PROTO::presentation = makeUnique(&wp_presentation_interface, 2, "Presentation"); - PROTO::xdgShell = makeUnique(&xdg_wm_base_interface, 7, "XDGShell"); + PROTO::presentation = makeUnique(&wp_presentation_interface, 1, "Presentation"); + PROTO::xdgShell = makeUnique(&xdg_wm_base_interface, 6, "XDGShell"); PROTO::dataWlr = makeUnique(&zwlr_data_control_manager_v1_interface, 2, "DataDeviceWlr"); PROTO::primarySelection = makeUnique(&zwp_primary_selection_device_manager_v1_interface, 1, "PrimarySelection"); PROTO::xwaylandShell = makeUnique(&xwayland_shell_v1_interface, 1, "XWaylandShell"); - PROTO::toplevelMapping = makeUnique(&hyprland_toplevel_mapping_manager_v1_interface, 1, "ToplevelMapping"); + PROTO::screencopy = makeUnique(&zwlr_screencopy_manager_v1_interface, 3, "Screencopy"); + PROTO::toplevelExport = makeUnique(&hyprland_toplevel_export_manager_v1_interface, 2, "ToplevelExport"); PROTO::globalShortcuts = makeUnique(&hyprland_global_shortcuts_manager_v1_interface, 1, "GlobalShortcuts"); PROTO::xdgDialog = makeUnique(&xdg_wm_dialog_v1_interface, 1, "XDGDialog"); PROTO::singlePixel = makeUnique(&wp_single_pixel_buffer_manager_v1_interface, 1, "SinglePixel"); @@ -188,51 +182,32 @@ CProtocolManager::CProtocolManager() { PROTO::ctm = makeUnique(&hyprland_ctm_control_manager_v1_interface, 2, "CTMControl"); PROTO::hyprlandSurface = makeUnique(&hyprland_surface_manager_v1_interface, 2, "HyprlandSurface"); PROTO::contentType = makeUnique(&wp_content_type_manager_v1_interface, 1, "ContentType"); - PROTO::xdgTag = makeUnique(&xdg_toplevel_tag_manager_v1_interface, 1, "XDGTag"); - PROTO::xdgBell = makeUnique(&xdg_system_bell_v1_interface, 1, "XDGBell"); - PROTO::extWorkspace = makeUnique(&ext_workspace_manager_v1_interface, 1, "ExtWorkspace"); - PROTO::extDataDevice = makeUnique(&ext_data_control_manager_v1_interface, 1, "ExtDataDevice"); - PROTO::pointerWarp = makeUnique(&wp_pointer_warp_v1_interface, 1, "PointerWarp"); - PROTO::fifo = makeUnique(&wp_fifo_manager_v1_interface, 1, "Fifo"); - - if (*PENABLECT) - PROTO::commitTiming = makeUnique(&wp_commit_timing_manager_v1_interface, 1, "CommitTiming"); - - // Screensharing Protocols - PROTO::screencopy = makeUnique(&zwlr_screencopy_manager_v1_interface, 3, "Screencopy"); - PROTO::toplevelExport = makeUnique(&hyprland_toplevel_export_manager_v1_interface, 2, "ToplevelExport"); - PROTO::imageCaptureSource = makeUnique(); // ctor inits actual protos, output and toplevel - PROTO::imageCopyCapture = makeUnique(&ext_image_copy_capture_manager_v1_interface, 1, "ImageCopyCapture"); if (*PENABLECM) PROTO::colorManagement = makeUnique(&wp_color_manager_v1_interface, 1, "ColorManagement", *PDEBUGCM); + if (*PENABLEXXCM && *PENABLECM) { + PROTO::xxColorManagement = makeUnique(&xx_color_manager_v4_interface, 1, "XXColorManagement"); + PROTO::frogColorManagement = makeUnique(&frog_color_management_factory_v1_interface, 1, "FrogColorManagement"); + } + // ! please read the top of this file before adding another protocol - for (auto const& b : g_pCompositor->m_aqBackend->getImplementations()) { + for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) { if (b->type() != Aquamarine::AQ_BACKEND_DRM) continue; - auto lease = makeShared(&wp_drm_lease_device_v1_interface, 1, "DRMLease", b); - if (lease->good()) - PROTO::lease.emplace(lease->getDeviceName(), lease); - else - lease.reset(); - - if (g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext && !PROTO::sync) { - if (g_pCompositor->supportsDrmSyncobjTimeline()) { - PROTO::sync = makeUnique(&wp_linux_drm_syncobj_manager_v1_interface, 1, "DRMSyncobj"); - Log::logger->log(Log::DEBUG, "DRM Syncobj Timeline support detected, enabling explicit sync protocol"); - } else - Log::logger->log(Log::WARN, "DRM Syncobj Timeline not supported, skipping explicit sync protocol"); - } + PROTO::lease = makeUnique(&wp_drm_lease_device_v1_interface, 1, "DRMLease"); + if (*PENABLEEXPLICIT) + PROTO::sync = makeUnique(&wp_linux_drm_syncobj_manager_v1_interface, 1, "DRMSyncobj"); + break; } - if (!g_pHyprOpenGL->getDRMFormats().empty()) { + if (g_pHyprOpenGL->getDRMFormats().size() > 0) { PROTO::mesaDRM = makeUnique(&wl_drm_interface, 2, "MesaDRM"); PROTO::linuxDma = makeUnique(&zwp_linux_dmabuf_v1_interface, 5, "LinuxDMABUF"); } else - Log::logger->log(Log::WARN, "ProtocolManager: Not binding linux-dmabuf and MesaDRM: DMABUF not available"); + Debug::log(WARN, "ProtocolManager: Not binding linux-dmabuf and MesaDRM: DMABUF not available"); } CProtocolManager::~CProtocolManager() { @@ -286,7 +261,6 @@ CProtocolManager::~CProtocolManager() { PROTO::xwaylandShell.reset(); PROTO::screencopy.reset(); PROTO::toplevelExport.reset(); - PROTO::toplevelMapping.reset(); PROTO::globalShortcuts.reset(); PROTO::xdgDialog.reset(); PROTO::singlePixel.reset(); @@ -295,18 +269,10 @@ CProtocolManager::~CProtocolManager() { PROTO::hyprlandSurface.reset(); PROTO::contentType.reset(); PROTO::colorManagement.reset(); - PROTO::xdgTag.reset(); - PROTO::xdgBell.reset(); - PROTO::extWorkspace.reset(); - PROTO::extDataDevice.reset(); - PROTO::pointerWarp.reset(); - PROTO::fifo.reset(); - PROTO::commitTiming.reset(); - PROTO::imageCaptureSource.reset(); + PROTO::xxColorManagement.reset(); + PROTO::frogColorManagement.reset(); - for (auto& [_, lease] : PROTO::lease) { - lease.reset(); - } + PROTO::lease.reset(); PROTO::sync.reset(); PROTO::mesaDRM.reset(); PROTO::linuxDma.reset(); @@ -345,6 +311,9 @@ bool CProtocolManager::isGlobalPrivileged(const wl_global* global) { PROTO::constraints->getGlobal(), PROTO::activation->getGlobal(), PROTO::idle->getGlobal(), + PROTO::ime->getGlobal(), + PROTO::virtualKeyboard->getGlobal(), + PROTO::virtualPointer->getGlobal(), PROTO::serverDecorationKDE->getGlobal(), PROTO::tablet->getGlobal(), PROTO::presentation->getGlobal(), @@ -352,17 +321,12 @@ bool CProtocolManager::isGlobalPrivileged(const wl_global* global) { PROTO::xdgDialog->getGlobal(), PROTO::singlePixel->getGlobal(), PROTO::primarySelection->getGlobal(), - PROTO::hyprlandSurface->getGlobal(), - PROTO::xdgTag->getGlobal(), - PROTO::xdgBell->getGlobal(), - PROTO::fifo->getGlobal(), - PROTO::commitTiming->getGlobal(), + PROTO::hyprlandSurface->getGlobal(), PROTO::sync ? PROTO::sync->getGlobal() : nullptr, PROTO::mesaDRM ? PROTO::mesaDRM->getGlobal() : nullptr, PROTO::linuxDma ? PROTO::linuxDma->getGlobal() : nullptr, - PROTO::colorManagement ? PROTO::colorManagement->getGlobal() : nullptr, }; // clang-format on - return std::ranges::find(ALLOWED_WHITELIST, global) == ALLOWED_WHITELIST.end(); + return std::find(ALLOWED_WHITELIST.begin(), ALLOWED_WHITELIST.end(), global) == ALLOWED_WHITELIST.end(); } diff --git a/src/managers/ProtocolManager.hpp b/src/managers/ProtocolManager.hpp index 86c6c0df..1ec8db4a 100644 --- a/src/managers/ProtocolManager.hpp +++ b/src/managers/ProtocolManager.hpp @@ -13,7 +13,7 @@ class CProtocolManager { bool isGlobalPrivileged(const wl_global* global); private: - std::unordered_map m_modeChangeListeners; + std::unordered_map m_mModeChangeListeners; void onMonitorModeChange(PHLMONITOR pMonitor); }; diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index a107dced..23db671a 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -2,38 +2,32 @@ #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" #include "../protocols/DataDeviceWlr.hpp" -#include "../protocols/ExtDataDevice.hpp" #include "../protocols/PrimarySelection.hpp" #include "../protocols/core/Compositor.hpp" -#include "../protocols/LayerShell.hpp" #include "../Compositor.hpp" -#include "../desktop/state/FocusState.hpp" #include "../devices/IKeyboard.hpp" -#include "../desktop/view/LayerSurface.hpp" +#include "../desktop/LayerSurface.hpp" #include "../managers/input/InputManager.hpp" +#include "../managers/HookSystemManager.hpp" #include "wlr-layer-shell-unstable-v1.hpp" #include -#include #include -#include - -using namespace Hyprutils::Utils; CSeatManager::CSeatManager() { - m_listeners.newSeatResource = PROTO::seat->m_events.newSeatResource.listen([this](const auto& resource) { onNewSeatResource(resource); }); + listeners.newSeatResource = PROTO::seat->events.newSeatResource.registerListener([this](std::any res) { onNewSeatResource(std::any_cast>(res)); }); } CSeatManager::SSeatResourceContainer::SSeatResourceContainer(SP res) : resource(res) { - listeners.destroy = res->m_events.destroy.listen( - [this] { std::erase_if(g_pSeatManager->m_seatResources, [this](const auto& e) { return e->resource.expired() || e->resource == resource; }); }); + listeners.destroy = res->events.destroy.registerListener( + [this](std::any data) { std::erase_if(g_pSeatManager->seatResources, [this](const auto& e) { return e->resource.expired() || e->resource == resource; }); }); } void CSeatManager::onNewSeatResource(SP resource) { - m_seatResources.emplace_back(makeShared(resource)); + seatResources.emplace_back(makeShared(resource)); } SP CSeatManager::containerForResource(SP seatResource) { - for (auto const& c : m_seatResources) { + for (auto const& c : seatResources) { if (c->resource == seatResource) return c; } @@ -49,7 +43,7 @@ uint32_t CSeatManager::nextSerial(SP seatResource) { ASSERT(container); - auto serial = wl_display_next_serial(g_pCompositor->m_wlDisplay); + auto serial = wl_display_next_serial(g_pCompositor->m_sWLDisplay); container->serials.emplace_back(serial); @@ -59,7 +53,7 @@ uint32_t CSeatManager::nextSerial(SP seatResource) { return serial; } -bool CSeatManager::serialValid(SP seatResource, uint32_t serial, bool erase) { +bool CSeatManager::serialValid(SP seatResource, uint32_t serial) { if (!seatResource) return false; @@ -69,8 +63,7 @@ bool CSeatManager::serialValid(SP seatResource, uint32_t serial for (auto it = container->serials.begin(); it != container->serials.end(); ++it) { if (*it == serial) { - if (erase) - container->serials.erase(it); + container->serials.erase(it); return true; } } @@ -83,108 +76,95 @@ void CSeatManager::updateCapabilities(uint32_t capabilities) { } void CSeatManager::setMouse(SP MAUZ) { - if (m_mouse == MAUZ) + if (mouse == MAUZ) return; - m_mouse = MAUZ; + mouse = MAUZ; } void CSeatManager::setKeyboard(SP KEEB) { - if (m_keyboard == KEEB) + if (keyboard == KEEB) return; - if (m_keyboard) - m_keyboard->m_active = false; - m_keyboard = KEEB; + if (keyboard) + keyboard->active = false; + keyboard = KEEB; if (KEEB) - KEEB->m_active = true; + KEEB->active = true; updateActiveKeyboardData(); } void CSeatManager::updateActiveKeyboardData() { - if (m_keyboard) - PROTO::seat->updateRepeatInfo(m_keyboard->m_repeatRate, m_keyboard->m_repeatDelay); + if (keyboard) + PROTO::seat->updateRepeatInfo(keyboard->repeatRate, keyboard->repeatDelay); PROTO::seat->updateKeymap(); } void CSeatManager::setKeyboardFocus(SP surf) { - if (m_state.keyboardFocus == surf) + if (state.keyboardFocus == surf) return; - if (!m_keyboard) { - Log::logger->log(Log::ERR, "BUG THIS: setKeyboardFocus without a valid keyboard set"); + if (!keyboard) { + Debug::log(ERR, "BUG THIS: setKeyboardFocus without a valid keyboard set"); return; } - m_listeners.keyboardSurfaceDestroy.reset(); + listeners.keyboardSurfaceDestroy.reset(); - if (m_state.keyboardFocusResource) { - auto client = m_state.keyboardFocusResource->client(); - for (auto const& s : m_seatResources) { + if (state.keyboardFocusResource) { + auto client = state.keyboardFocusResource->client(); + for (auto const& s : seatResources) { if (s->resource->client() != client) continue; - for (auto const& k : s->resource->m_keyboards) { + for (auto const& k : s->resource->keyboards) { if (!k) continue; - k->sendMods(0, m_keyboard->m_modifiersState.latched, m_keyboard->m_modifiersState.locked, m_keyboard->m_modifiersState.group); k->sendLeave(); } } } - m_state.keyboardFocusResource.reset(); - m_state.keyboardFocus = surf; + state.keyboardFocusResource.reset(); + state.keyboardFocus = surf; if (!surf) { - m_events.keyboardFocusChange.emit(); + events.keyboardFocusChange.emit(); return; } - wl_array keys; - wl_array_init(&keys); - CScopeGuard x([&keys] { wl_array_release(&keys); }); - - const auto& PRESSED = g_pInputManager->getKeysFromAllKBs(); - static_assert(std::is_same_v::value_type, uint32_t>, "Element type different from keycode type uint32_t"); - - const auto PRESSEDARRSIZE = PRESSED.size() * sizeof(uint32_t); - const auto PKEYS = wl_array_add(&keys, PRESSEDARRSIZE); - if (PKEYS) - memcpy(PKEYS, PRESSED.data(), PRESSEDARRSIZE); - auto client = surf->client(); - for (auto const& r : m_seatResources | std::views::reverse) { + for (auto const& r : seatResources | std::views::reverse) { if (r->resource->client() != client) continue; - m_state.keyboardFocusResource = r->resource; - for (auto const& k : r->resource->m_keyboards) { + state.keyboardFocusResource = r->resource; + for (auto const& k : r->resource->keyboards) { if (!k) continue; - k->sendEnter(surf, &keys); - k->sendMods(m_keyboard->m_modifiersState.depressed, m_keyboard->m_modifiersState.latched, m_keyboard->m_modifiersState.locked, m_keyboard->m_modifiersState.group); + k->sendEnter(surf); + k->sendMods(keyboard->modifiersState.depressed, keyboard->modifiersState.latched, keyboard->modifiersState.locked, keyboard->modifiersState.group); } } - m_listeners.keyboardSurfaceDestroy = surf->m_events.destroy.listen([this] { setKeyboardFocus(nullptr); }); + listeners.keyboardSurfaceDestroy = surf->events.destroy.registerListener([this](std::any d) { setKeyboardFocus(nullptr); }); - m_events.keyboardFocusChange.emit(); + events.keyboardFocusChange.emit(); } void CSeatManager::sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state_) { - if (!m_state.keyboardFocusResource) + if (!state.keyboardFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.keyboardFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.keyboardFocusResource->client()) continue; - for (auto const& k : s->resource->m_keyboards) { + for (auto const& k : s->resource->keyboards) { if (!k) continue; @@ -194,14 +174,14 @@ void CSeatManager::sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_ke } void CSeatManager::sendKeyboardMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { - if (!m_state.keyboardFocusResource) + if (!state.keyboardFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.keyboardFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.keyboardFocusResource->client()) continue; - for (auto const& k : s->resource->m_keyboards) { + for (auto const& k : s->resource->keyboards) { if (!k) continue; @@ -211,32 +191,32 @@ void CSeatManager::sendKeyboardMods(uint32_t depressed, uint32_t latched, uint32 } void CSeatManager::setPointerFocus(SP surf, const Vector2D& local) { - if (m_state.pointerFocus == surf) + if (state.pointerFocus == surf) return; if (PROTO::data->dndActive() && surf) { - if (m_state.dndPointerFocus == surf) + if (state.dndPointerFocus == surf) return; - Log::logger->log(Log::DEBUG, "[seatmgr] Refusing pointer focus during an active dnd, but setting dndPointerFocus"); - m_state.dndPointerFocus = surf; - m_events.dndPointerFocusChange.emit(); + Debug::log(LOG, "[seatmgr] Refusing pointer focus during an active dnd, but setting dndPointerFocus"); + state.dndPointerFocus = surf; + events.dndPointerFocusChange.emit(); return; } - if (!m_mouse) { - Log::logger->log(Log::ERR, "BUG THIS: setPointerFocus without a valid mouse set"); + if (!mouse) { + Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set"); return; } - m_listeners.pointerSurfaceDestroy.reset(); + listeners.pointerSurfaceDestroy.reset(); - if (m_state.pointerFocusResource) { - auto client = m_state.pointerFocusResource->client(); - for (auto const& s : m_seatResources) { + if (state.pointerFocusResource) { + auto client = state.pointerFocusResource->client(); + for (auto const& s : seatResources) { if (s->resource->client() != client) continue; - for (auto const& p : s->resource->m_pointers) { + for (auto const& p : s->resource->pointers) { if (!p) continue; @@ -245,27 +225,27 @@ void CSeatManager::setPointerFocus(SP surf, const Vector2D& } } - auto lastPointerFocusResource = m_state.pointerFocusResource; + auto lastPointerFocusResource = state.pointerFocusResource; - m_state.dndPointerFocus.reset(); - m_state.pointerFocusResource.reset(); - m_state.pointerFocus = surf; + state.dndPointerFocus.reset(); + state.pointerFocusResource.reset(); + state.pointerFocus = surf; if (!surf) { sendPointerFrame(lastPointerFocusResource); - m_events.pointerFocusChange.emit(); + events.pointerFocusChange.emit(); return; } - m_state.dndPointerFocus = surf; + state.dndPointerFocus = surf; auto client = surf->client(); - for (auto const& r : m_seatResources | std::views::reverse) { + for (auto const& r : seatResources | std::views::reverse) { if (r->resource->client() != client) continue; - m_state.pointerFocusResource = r->resource; - for (auto const& p : r->resource->m_pointers) { + state.pointerFocusResource = r->resource; + for (auto const& p : r->resource->pointers) { if (!p) continue; @@ -273,26 +253,26 @@ void CSeatManager::setPointerFocus(SP surf, const Vector2D& } } - if (m_state.pointerFocusResource != lastPointerFocusResource) + if (state.pointerFocusResource != lastPointerFocusResource) sendPointerFrame(lastPointerFocusResource); sendPointerFrame(); - m_listeners.pointerSurfaceDestroy = surf->m_events.destroy.listen([this] { setPointerFocus(nullptr, {}); }); + listeners.pointerSurfaceDestroy = surf->events.destroy.registerListener([this](std::any d) { setPointerFocus(nullptr, {}); }); - m_events.pointerFocusChange.emit(); - m_events.dndPointerFocusChange.emit(); + events.pointerFocusChange.emit(); + events.dndPointerFocusChange.emit(); } void CSeatManager::sendPointerMotion(uint32_t timeMs, const Vector2D& local) { - if (!m_state.pointerFocusResource) + if (!state.pointerFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.pointerFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.pointerFocusResource->client()) continue; - for (auto const& p : s->resource->m_pointers) { + for (auto const& p : s->resource->pointers) { if (!p) continue; @@ -300,18 +280,18 @@ void CSeatManager::sendPointerMotion(uint32_t timeMs, const Vector2D& local) { } } - m_lastLocalCoords = local; + lastLocalCoords = local; } void CSeatManager::sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_button_state state_) { - if (!m_state.pointerFocusResource || PROTO::data->dndActive()) + if (!state.pointerFocusResource || PROTO::data->dndActive()) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.pointerFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.pointerFocusResource->client()) continue; - for (auto const& p : s->resource->m_pointers) { + for (auto const& p : s->resource->pointers) { if (!p) continue; @@ -321,21 +301,21 @@ void CSeatManager::sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_b } void CSeatManager::sendPointerFrame() { - if (!m_state.pointerFocusResource) + if (!state.pointerFocusResource) return; - sendPointerFrame(m_state.pointerFocusResource); + sendPointerFrame(state.pointerFocusResource); } void CSeatManager::sendPointerFrame(WP pResource) { if (!pResource) return; - for (auto const& s : m_seatResources) { + for (auto const& s : seatResources) { if (s->resource->client() != pResource->client()) continue; - for (auto const& p : s->resource->m_pointers) { + for (auto const& p : s->resource->pointers) { if (!p) continue; @@ -346,14 +326,14 @@ void CSeatManager::sendPointerFrame(WP pResource) { void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, int32_t value120, wl_pointer_axis_source source, wl_pointer_axis_relative_direction relative) { - if (!m_state.pointerFocusResource) + if (!state.pointerFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.pointerFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.pointerFocusResource->client()) continue; - for (auto const& p : s->resource->m_pointers) { + for (auto const& p : s->resource->pointers) { if (!p) continue; @@ -373,18 +353,18 @@ void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double } void CSeatManager::sendTouchDown(SP surf, uint32_t timeMs, int32_t id, const Vector2D& local) { - m_listeners.touchSurfaceDestroy.reset(); + listeners.touchSurfaceDestroy.reset(); - m_state.touchFocusResource.reset(); - m_state.touchFocus = surf; + state.touchFocusResource.reset(); + state.touchFocus = surf; auto client = surf->client(); - for (auto const& r : m_seatResources | std::views::reverse) { + for (auto const& r : seatResources | std::views::reverse) { if (r->resource->client() != client) continue; - m_state.touchFocusResource = r->resource; - for (auto const& t : r->resource->m_touches) { + state.touchFocusResource = r->resource; + for (auto const& t : r->resource->touches) { if (!t) continue; @@ -392,25 +372,25 @@ void CSeatManager::sendTouchDown(SP surf, uint32_t timeMs, i } } - m_listeners.touchSurfaceDestroy = surf->m_events.destroy.listen([this, timeMs, id] { sendTouchUp(timeMs + 10, id); }); + listeners.touchSurfaceDestroy = surf->events.destroy.registerListener([this, timeMs, id](std::any d) { sendTouchUp(timeMs + 10, id); }); - m_touchLocks++; + touchLocks++; - if (m_touchLocks <= 1) - m_events.touchFocusChange.emit(); + if (touchLocks <= 1) + events.touchFocusChange.emit(); } void CSeatManager::sendTouchUp(uint32_t timeMs, int32_t id) { - if (!m_state.touchFocusResource || m_touchLocks <= 0) + if (!state.touchFocusResource || touchLocks <= 0) return; - auto client = m_state.touchFocusResource->client(); - for (auto const& r : m_seatResources | std::views::reverse) { + auto client = state.touchFocusResource->client(); + for (auto const& r : seatResources | std::views::reverse) { if (r->resource->client() != client) continue; - m_state.touchFocusResource = r->resource; - for (auto const& t : r->resource->m_touches) { + state.touchFocusResource = r->resource; + for (auto const& t : r->resource->touches) { if (!t) continue; @@ -418,21 +398,21 @@ void CSeatManager::sendTouchUp(uint32_t timeMs, int32_t id) { } } - m_touchLocks--; + touchLocks--; - if (m_touchLocks <= 0) - m_events.touchFocusChange.emit(); + if (touchLocks <= 0) + events.touchFocusChange.emit(); } void CSeatManager::sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& local) { - if (!m_state.touchFocusResource) + if (!state.touchFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.touchFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - for (auto const& t : s->resource->m_touches) { + for (auto const& t : s->resource->touches) { if (!t) continue; @@ -442,14 +422,14 @@ void CSeatManager::sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& } void CSeatManager::sendTouchFrame() { - if (!m_state.touchFocusResource) + if (!state.touchFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.touchFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - for (auto const& t : s->resource->m_touches) { + for (auto const& t : s->resource->touches) { if (!t) continue; @@ -459,14 +439,14 @@ void CSeatManager::sendTouchFrame() { } void CSeatManager::sendTouchCancel() { - if (!m_state.touchFocusResource) + if (!state.touchFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.touchFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - for (auto const& t : s->resource->m_touches) { + for (auto const& t : s->resource->touches) { if (!t) continue; @@ -476,14 +456,14 @@ void CSeatManager::sendTouchCancel() { } void CSeatManager::sendTouchShape(int32_t id, const Vector2D& shape) { - if (!m_state.touchFocusResource) + if (!state.touchFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.touchFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - for (auto const& t : s->resource->m_touches) { + for (auto const& t : s->resource->touches) { if (!t) continue; @@ -493,14 +473,14 @@ void CSeatManager::sendTouchShape(int32_t id, const Vector2D& shape) { } void CSeatManager::sendTouchOrientation(int32_t id, double angle) { - if (!m_state.touchFocusResource) + if (!state.touchFocusResource) return; - for (auto const& s : m_seatResources) { - if (s->resource->client() != m_state.touchFocusResource->client()) + for (auto const& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - for (auto const& t : s->resource->m_touches) { + for (auto const& t : s->resource->touches) { if (!t) continue; @@ -510,14 +490,14 @@ void CSeatManager::sendTouchOrientation(int32_t id, double angle) { } void CSeatManager::refocusGrab() { - if (!m_seatGrab) + if (!seatGrab) return; - if (!m_seatGrab->m_surfs.empty()) { + if (seatGrab->surfs.size() > 0) { // try to find a surf in focus first const auto MOUSE = g_pInputManager->getMouseCoordsInternal(); - for (auto const& s : m_seatGrab->m_surfs) { - auto hlSurf = Desktop::View::CWLSurface::fromResource(s.lock()); + for (auto const& s : seatGrab->surfs) { + auto hlSurf = CWLSurface::fromResource(s.lock()); if (!hlSurf) continue; @@ -528,34 +508,34 @@ void CSeatManager::refocusGrab() { if (!b->containsPoint(MOUSE)) continue; - if (m_seatGrab->m_keyboard) + if (seatGrab->keyboard) setKeyboardFocus(s.lock()); - if (m_seatGrab->m_pointer) + if (seatGrab->pointer) setPointerFocus(s.lock(), MOUSE - b->pos()); return; } - SP surf = m_seatGrab->m_surfs.at(0).lock(); - if (m_seatGrab->m_keyboard) + SP surf = seatGrab->surfs.at(0).lock(); + if (seatGrab->keyboard) setKeyboardFocus(surf); - if (m_seatGrab->m_pointer) + if (seatGrab->pointer) setPointerFocus(surf, {}); } } void CSeatManager::onSetCursor(SP seatResource, uint32_t serial, SP surf, const Vector2D& hotspot) { - if (!m_state.pointerFocusResource || !seatResource || seatResource->client() != m_state.pointerFocusResource->client()) { - Log::logger->log(Log::DEBUG, "[seatmgr] Rejecting a setCursor because the client ain't in focus"); + if (!state.pointerFocusResource || !seatResource || seatResource->client() != state.pointerFocusResource->client()) { + Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the client ain't in focus"); return; } // TODO: fix this. Probably should be done in the CWlPointer as the serial could be lost by us. // if (!serialValid(seatResource, serial)) { - // Log::logger->log(Log::DEBUG, "[seatmgr] Rejecting a setCursor because the serial is invalid"); + // Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the serial is invalid"); // return; // } - m_events.setCursor.emit(SSetCursorEvent{surf, hotspot}); + events.setCursor.emit(SSetCursorEvent{surf, hotspot}); } SP CSeatManager::seatResourceForClient(wl_client* client) { @@ -563,164 +543,107 @@ SP CSeatManager::seatResourceForClient(wl_client* client) { } void CSeatManager::setCurrentSelection(SP source) { - if (source == m_selection.currentSelection) { - Log::logger->log(Log::WARN, "[seat] duplicated setCurrentSelection?"); + if (source == selection.currentSelection) { + Debug::log(WARN, "[seat] duplicated setCurrentSelection?"); return; } - m_selection.destroySelection.reset(); + selection.destroySelection.reset(); - if (m_selection.currentSelection) - m_selection.currentSelection->cancelled(); + if (selection.currentSelection) + selection.currentSelection->cancelled(); if (!source) PROTO::data->setSelection(nullptr); - m_selection.currentSelection = source; + selection.currentSelection = source; if (source) { - m_selection.destroySelection = source->m_events.destroy.listen([this] { setCurrentSelection(nullptr); }); + selection.destroySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentSelection(nullptr); }); PROTO::data->setSelection(source); PROTO::dataWlr->setSelection(source, false); - PROTO::extDataDevice->setSelection(source, false); } - m_events.setSelection.emit(); + events.setSelection.emit(); } void CSeatManager::setCurrentPrimarySelection(SP source) { - if (source == m_selection.currentPrimarySelection) { - Log::logger->log(Log::WARN, "[seat] duplicated setCurrentPrimarySelection?"); + if (source == selection.currentPrimarySelection) { + Debug::log(WARN, "[seat] duplicated setCurrentPrimarySelection?"); return; } - m_selection.destroyPrimarySelection.reset(); + selection.destroyPrimarySelection.reset(); - if (m_selection.currentPrimarySelection) - m_selection.currentPrimarySelection->cancelled(); + if (selection.currentPrimarySelection) + selection.currentPrimarySelection->cancelled(); if (!source) PROTO::primarySelection->setSelection(nullptr); - m_selection.currentPrimarySelection = source; + selection.currentPrimarySelection = source; if (source) { - m_selection.destroyPrimarySelection = source->m_events.destroy.listen([this] { setCurrentPrimarySelection(nullptr); }); + selection.destroyPrimarySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentPrimarySelection(nullptr); }); PROTO::primarySelection->setSelection(source); PROTO::dataWlr->setSelection(source, true); - PROTO::extDataDevice->setSelection(source, true); } - m_events.setPrimarySelection.emit(); + events.setPrimarySelection.emit(); } void CSeatManager::setGrab(SP grab) { - if (m_seatGrab) { - auto oldGrab = m_seatGrab; + if (seatGrab) { + auto oldGrab = seatGrab; + seatGrab.reset(); + g_pInputManager->refocus(); - // Try to find the parent window or layer surface from the grab - PHLWINDOW parentWindow; - PHLLS parentLayer; - if (oldGrab && oldGrab->m_surfs.size()) { - // Try to find the surface that had focus when the grab ended - SP focusedSurf; - auto keyboardFocus = m_state.keyboardFocus.lock(); - auto pointerFocus = m_state.pointerFocus.lock(); + auto currentFocus = state.keyboardFocus.lock(); + auto refocus = !currentFocus; - // Check if keyboard or pointer focus is in the grab - for (auto const& s : oldGrab->m_surfs) { - auto surf = s.lock(); - if (surf && (surf == keyboardFocus || surf == pointerFocus)) { - focusedSurf = surf; - break; - } - } - - // Fall back to first surface if no focused surface found - if (!focusedSurf) - focusedSurf = oldGrab->m_surfs.front().lock(); - - if (focusedSurf) { - auto hlSurface = Desktop::View::CWLSurface::fromResource(focusedSurf); - if (hlSurface) { - auto popup = Desktop::View::CPopup::fromView(hlSurface->view()); - if (popup) { - auto t1Owner = popup->getT1Owner(); - if (t1Owner) { - parentWindow = Desktop::View::CWindow::fromView(t1Owner->view()); - if (!parentWindow) - parentLayer = Desktop::View::CLayerSurface::fromView(t1Owner->view()); - } - } - } - } - } - - m_seatGrab.reset(); - - if (parentLayer && parentLayer->m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) { - Desktop::focusState()->rawSurfaceFocus(parentLayer->wlSurface()->resource()); - } else { - static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); - if (*PFOLLOWMOUSE == 0 || *PFOLLOWMOUSE == 2 || *PFOLLOWMOUSE == 3) { - const auto PMONITOR = g_pCompositor->getMonitorFromCursor(); - - // If this was a popup grab, focus its parent window to maintain context - if (validMapped(parentWindow)) { - Desktop::focusState()->rawWindowFocus(parentWindow, Desktop::FOCUS_REASON_FFM); - Log::logger->log(Log::DEBUG, "[seatmgr] Refocused popup parent window {} (follow_mouse={})", parentWindow->m_title, *PFOLLOWMOUSE); - } else - g_pInputManager->refocusLastWindow(PMONITOR); - } else - g_pInputManager->refocus(); - } - - auto currentFocus = m_state.keyboardFocus.lock(); - auto refocus = !currentFocus; - - SP surf; - PHLLS layer; + SP surf; + PHLLS layer; if (!refocus) { - surf = Desktop::View::CWLSurface::fromResource(currentFocus); - layer = surf ? Desktop::View::CLayerSurface::fromView(surf->view()) : nullptr; + surf = CWLSurface::fromResource(currentFocus); + layer = surf ? surf->getLayer() : nullptr; } if (!refocus && !layer) { - auto popup = surf ? Desktop::View::CPopup::fromView(surf->view()) : nullptr; + auto popup = surf ? surf->getPopup() : nullptr; if (popup) { auto parent = popup->getT1Owner(); - layer = Desktop::View::CLayerSurface::fromView(parent->view()); + layer = parent->getLayer(); } } if (!refocus && layer) - refocus = layer->m_interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; + refocus = layer->interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; if (refocus) { - auto candidate = Desktop::focusState()->window(); + auto candidate = g_pCompositor->m_pLastWindow.lock(); if (candidate) - Desktop::focusState()->rawWindowFocus(candidate, Desktop::FOCUS_REASON_FFM); + g_pCompositor->focusWindow(candidate); } - if (oldGrab->m_onEnd) - oldGrab->m_onEnd(); + if (oldGrab->onEnd) + oldGrab->onEnd(); } if (!grab) return; - m_seatGrab = grab; + seatGrab = grab; refocusGrab(); } void CSeatManager::resendEnterEvents() { - SP kb = m_state.keyboardFocus.lock(); - SP pt = m_state.pointerFocus.lock(); + SP kb = state.keyboardFocus.lock(); + SP pt = state.pointerFocus.lock(); - auto last = m_lastLocalCoords; + auto last = lastLocalCoords; setKeyboardFocus(nullptr); setPointerFocus(nullptr, {}); @@ -730,23 +653,23 @@ void CSeatManager::resendEnterEvents() { } bool CSeatGrab::accepts(SP surf) { - return std::ranges::find(m_surfs, surf) != m_surfs.end(); + return std::find(surfs.begin(), surfs.end(), surf) != surfs.end(); } void CSeatGrab::add(SP surf) { - m_surfs.emplace_back(surf); + surfs.emplace_back(surf); } void CSeatGrab::remove(SP surf) { - std::erase(m_surfs, surf); - if ((m_keyboard && g_pSeatManager->m_state.keyboardFocus == surf) || (m_pointer && g_pSeatManager->m_state.pointerFocus == surf)) + std::erase(surfs, surf); + if ((keyboard && g_pSeatManager->state.keyboardFocus == surf) || (pointer && g_pSeatManager->state.pointerFocus == surf)) g_pSeatManager->refocusGrab(); } void CSeatGrab::setCallback(std::function onEnd_) { - m_onEnd = onEnd_; + onEnd = onEnd_; } void CSeatGrab::clear() { - m_surfs.clear(); + surfs.clear(); } diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index 21736e3a..32472aa3 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -33,12 +33,15 @@ class CSeatGrab { void setCallback(std::function onEnd_); void clear(); - bool m_keyboard = false; - bool m_pointer = false; + bool keyboard = false; + bool pointer = false; + bool touch = false; + + bool removeOnInput = true; // on hard input e.g. click outside, remove private: - std::vector> m_surfs; - std::function m_onEnd; + std::vector> surfs; + std::function onEnd; friend class CSeatManager; }; @@ -76,7 +79,7 @@ class CSeatManager { uint32_t nextSerial(SP seatResource); // pops the serial if it was valid, meaning it is consumed. - bool serialValid(SP seatResource, uint32_t serial, bool erase = true); + bool serialValid(SP seatResource, uint32_t serial); void onSetCursor(SP seatResource, uint32_t serial, SP surf, const Vector2D& hotspot); @@ -93,7 +96,7 @@ class CSeatManager { WP touchFocusResource; WP dndPointerFocus; - } m_state; + } state; struct SSetCursorEvent { SP surf = nullptr; @@ -101,31 +104,34 @@ class CSeatManager { }; struct { - CSignalT<> keyboardFocusChange; - CSignalT<> pointerFocusChange; - CSignalT<> dndPointerFocusChange; - CSignalT<> touchFocusChange; - CSignalT setCursor; - CSignalT<> setSelection; - CSignalT<> setPrimarySelection; - } m_events; + CSignal keyboardFocusChange; + CSignal pointerFocusChange; + CSignal dndPointerFocusChange; + CSignal touchFocusChange; + CSignal setCursor; // SSetCursorEvent + CSignal setSelection; + CSignal setPrimarySelection; + } events; struct { WP currentSelection; CHyprSignalListener destroySelection; WP currentPrimarySelection; CHyprSignalListener destroyPrimarySelection; - } m_selection; + } selection; void setCurrentSelection(SP source); void setCurrentPrimarySelection(SP source); // do not write to directly, use set... - WP m_mouse; - WP m_keyboard; + WP mouse; + WP keyboard; void setGrab(SP grab); // nullptr removes - SP m_seatGrab; + SP seatGrab; + + bool isPointerFrameSkipped = false; + bool isPointerFrameCommit = false; private: struct SSeatResourceContainer { @@ -139,7 +145,7 @@ class CSeatManager { } listeners; }; - std::vector> m_seatResources; + std::vector> seatResources; void onNewSeatResource(SP resource); SP containerForResource(SP seatResource); @@ -150,10 +156,10 @@ class CSeatManager { CHyprSignalListener keyboardSurfaceDestroy; CHyprSignalListener pointerSurfaceDestroy; CHyprSignalListener touchSurfaceDestroy; - } m_listeners; + } listeners; - Vector2D m_lastLocalCoords; - int m_touchLocks = 0; // we assume there aint like 20 touch devices at once... + Vector2D lastLocalCoords; + int touchLocks = 0; // we assume there aint like 20 touch devices at once... friend struct SSeatResourceContainer; friend class CSeatGrab; diff --git a/src/managers/SessionLockManager.cpp b/src/managers/SessionLockManager.cpp index e729ae95..40611199 100644 --- a/src/managers/SessionLockManager.cpp +++ b/src/managers/SessionLockManager.cpp @@ -3,19 +3,16 @@ #include "../config/ConfigValue.hpp" #include "../protocols/FractionalScale.hpp" #include "../protocols/SessionLock.hpp" +#include "../managers/SeatManager.hpp" #include "../render/Renderer.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../desktop/view/SessionLock.hpp" -#include "./managers/SeatManager.hpp" -#include "./managers/input/InputManager.hpp" -#include "./managers/eventLoop/EventLoopManager.hpp" +#include "../managers/input/InputManager.hpp" #include #include SSessionLockSurface::SSessionLockSurface(SP surface_) : surface(surface_) { pWlrSurface = surface->surface(); - listeners.map = surface_->m_events.map.listen([this] { + listeners.map = surface_->events.map.registerListener([this](std::any data) { mapped = true; g_pInputManager->simulateMouseMovement(); @@ -26,17 +23,17 @@ SSessionLockSurface::SSessionLockSurface(SP surface_) : sur g_pHyprRenderer->damageMonitor(PMONITOR); }); - listeners.destroy = surface_->m_events.destroy.listen([this] { - if (pWlrSurface == Desktop::focusState()->surface()) - Desktop::focusState()->surface().reset(); + listeners.destroy = surface_->events.destroy.registerListener([this](std::any data) { + if (pWlrSurface == g_pCompositor->m_pLastFocus) + g_pCompositor->m_pLastFocus.reset(); g_pSessionLockManager->removeSessionLockSurface(this); }); - listeners.commit = surface_->m_events.commit.listen([this] { + listeners.commit = surface_->events.commit.registerListener([this](std::any data) { const auto PMONITOR = g_pCompositor->getMonitorFromID(iMonitorID); - if (mapped && !Desktop::focusState()->surface()) + if (mapped && !g_pCompositor->m_pLastFocus) g_pInputManager->simulateMouseMovement(); if (PMONITOR) @@ -45,99 +42,60 @@ SSessionLockSurface::SSessionLockSurface(SP surface_) : sur } CSessionLockManager::CSessionLockManager() { - m_listeners.newLock = PROTO::sessionLock->m_events.newLock.listen([this](const auto& lock) { this->onNewSessionLock(lock); }); + listeners.newLock = PROTO::sessionLock->events.newLock.registerListener([this](std::any data) { this->onNewSessionLock(std::any_cast>(data)); }); } void CSessionLockManager::onNewSessionLock(SP pLock) { + static auto PALLOWRELOCK = CConfigValue("misc:allow_session_lock_restore"); if (PROTO::sessionLock->isLocked() && !*PALLOWRELOCK) { - LOGM(Log::DEBUG, "Cannot re-lock, misc:allow_session_lock_restore is disabled"); + Debug::log(LOG, "Cannot re-lock, misc:allow_session_lock_restore is disabled"); pLock->sendDenied(); return; } - if (m_sessionLock && !clientDenied() && !clientLocked()) - return; // Not allowing to relock in case the old lock is still in a limbo + Debug::log(LOG, "Session got locked by {:x}", (uintptr_t)pLock.get()); - LOGM(Log::DEBUG, "Session got locked by {:x}", (uintptr_t)pLock.get()); + m_pSessionLock = makeUnique(); + m_pSessionLock->lock = pLock; + m_pSessionLock->mLockTimer.reset(); - m_sessionLock = makeUnique(); - m_sessionLock->lock = pLock; - m_sessionLock->lockTimer.reset(); + m_pSessionLock->listeners.newSurface = pLock->events.newLockSurface.registerListener([this](std::any data) { + auto SURFACE = std::any_cast>(data); - m_sessionLock->listeners.newSurface = pLock->m_events.newLockSurface.listen([this](const SP& surface) { - const auto PMONITOR = surface->monitor(); + const auto PMONITOR = SURFACE->monitor(); - const auto NEWSURFACE = m_sessionLock->vSessionLockSurfaces.emplace_back(makeShared(surface)); - NEWSURFACE->iMonitorID = PMONITOR->m_id; - PROTO::fractional->sendScale(surface->surface(), PMONITOR->m_scale); - - g_pCompositor->m_otherViews.emplace_back(Desktop::View::CSessionLock::create(surface)); + const auto NEWSURFACE = m_pSessionLock->vSessionLockSurfaces.emplace_back(makeUnique(SURFACE)).get(); + NEWSURFACE->iMonitorID = PMONITOR->ID; + PROTO::fractional->sendScale(SURFACE->surface(), PMONITOR->scale); }); - m_sessionLock->listeners.unlock = pLock->m_events.unlockAndDestroy.listen([this] { - m_sessionLock.reset(); + m_pSessionLock->listeners.unlock = pLock->events.unlockAndDestroy.registerListener([this](std::any data) { + m_pSessionLock.reset(); g_pInputManager->refocus(); - for (auto const& m : g_pCompositor->m_monitors) + for (auto const& m : g_pCompositor->m_vMonitors) g_pHyprRenderer->damageMonitor(m); }); - m_sessionLock->listeners.destroy = pLock->m_events.destroyed.listen([this] { - m_sessionLock.reset(); - Desktop::focusState()->rawSurfaceFocus(nullptr); + m_pSessionLock->listeners.destroy = pLock->events.destroyed.registerListener([this](std::any data) { + m_pSessionLock.reset(); + g_pCompositor->focusSurface(nullptr); - for (auto const& m : g_pCompositor->m_monitors) + for (auto const& m : g_pCompositor->m_vMonitors) g_pHyprRenderer->damageMonitor(m); }); - Desktop::focusState()->rawSurfaceFocus(nullptr); + g_pCompositor->focusSurface(nullptr); g_pSeatManager->setGrab(nullptr); - const bool NOACTIVEMONS = std::ranges::all_of(g_pCompositor->m_monitors, [](const auto& m) { return !m->m_enabled || !m->m_dpmsStatus; }); - - if (NOACTIVEMONS || g_pCompositor->m_unsafeState) { - // Normally the locked event is sent after each output rendered a lock screen frame. - // When there are no active outputs, send it right away. - m_sessionLock->lock->sendLocked(); - m_sessionLock->hasSentLocked = true; - return; + // Normally the locked event is sent after each output rendered a lock screen frame. + // When there are no outputs, send it right away. + if (g_pCompositor->m_bUnsafeState) { + m_pSessionLock->lock->sendLocked(); + m_pSessionLock->m_hasSentLocked = true; } - - m_sessionLock->sendDeniedTimer = makeShared( - // Within this arbitrary amount of time, a session-lock client is expected to create and commit a lock surface for each output. If the client fails to do that, it will be denied. - std::chrono::seconds(5), - [](auto, auto) { - if (!g_pSessionLockManager || g_pSessionLockManager->clientLocked() || g_pSessionLockManager->clientDenied()) - return; - - if (!g_pSessionLockManager->m_sessionLock || !g_pSessionLockManager->m_sessionLock->lock) - return; - - if (g_pCompositor->m_unsafeState || !g_pCompositor->m_aqBackend->hasSession() || !g_pCompositor->m_aqBackend->session->active) { - // Because the session is inactive, there is a good reason for why the client did't manage to render to all outputs. - // We send locked, although this could lead to imperfect frames when we start to render again. - g_pSessionLockManager->m_sessionLock->lock->sendLocked(); - g_pSessionLockManager->m_sessionLock->hasSentLocked = true; - return; - } - - LOGM(Log::WARN, "Kicking lockscreen client, because it failed to render to all outputs within 5 seconds"); - g_pSessionLockManager->m_sessionLock->lock->sendDenied(); - g_pSessionLockManager->m_sessionLock->hasSentDenied = true; - }, - nullptr); - - g_pEventLoopManager->addTimer(m_sessionLock->sendDeniedTimer); -} - -void CSessionLockManager::removeSendDeniedTimer() { - if (!m_sessionLock || !m_sessionLock->sendDeniedTimer) - return; - - g_pEventLoopManager->removeTimer(m_sessionLock->sendDeniedTimer); - m_sessionLock->sendDeniedTimer.reset(); } bool CSessionLockManager::isSessionLocked() { @@ -145,10 +103,10 @@ bool CSessionLockManager::isSessionLocked() { } WP CSessionLockManager::getSessionLockSurfaceForMonitor(uint64_t id) { - if (!m_sessionLock) + if (!m_pSessionLock) return {}; - for (auto const& sls : m_sessionLock->vSessionLockSurfaces) { + for (auto const& sls : m_pSessionLock->vSessionLockSurfaces) { if (sls->iMonitorID == id) { if (sls->mapped) return sls; @@ -160,18 +118,30 @@ WP CSessionLockManager::getSessionLockSurfaceForMonitor(uin return {}; } +// We don't want the red screen to flash. +float CSessionLockManager::getRedScreenAlphaForMonitor(uint64_t id) { + if (!m_pSessionLock) + return 1.F; + + const auto& NOMAPPEDSURFACETIMER = m_pSessionLock->mMonitorsWithoutMappedSurfaceTimers.find(id); + + if (NOMAPPEDSURFACETIMER == m_pSessionLock->mMonitorsWithoutMappedSurfaceTimers.end()) { + m_pSessionLock->mMonitorsWithoutMappedSurfaceTimers.emplace(id, CTimer()); + m_pSessionLock->mMonitorsWithoutMappedSurfaceTimers[id].reset(); + return 0.f; + } + + return std::clamp(NOMAPPEDSURFACETIMER->second.getSeconds() - /* delay for screencopy */ 0.5f, 0.f, 1.f); +} + void CSessionLockManager::onLockscreenRenderedOnMonitor(uint64_t id) { - if (!m_sessionLock || m_sessionLock->hasSentLocked || m_sessionLock->hasSentDenied) + if (!m_pSessionLock || m_pSessionLock->m_hasSentLocked) return; - - m_sessionLock->lockedMonitors.emplace(id); - const bool LOCKED = - std::ranges::all_of(g_pCompositor->m_monitors, [this](auto m) { return !m->m_enabled || !m->m_dpmsStatus || m_sessionLock->lockedMonitors.contains(m->m_id); }); - - if (LOCKED && m_sessionLock->lock->good()) { - removeSendDeniedTimer(); - m_sessionLock->lock->sendLocked(); - m_sessionLock->hasSentLocked = true; + m_pSessionLock->m_lockedMonitors.emplace(id); + const bool LOCKED = std::ranges::all_of(g_pCompositor->m_vMonitors, [this](auto m) { return m_pSessionLock->m_lockedMonitors.contains(m->ID); }); + if (LOCKED && m_pSessionLock->lock->good()) { + m_pSessionLock->lock->sendLocked(); + m_pSessionLock->m_hasSentLocked = true; } } @@ -179,10 +149,10 @@ bool CSessionLockManager::isSurfaceSessionLock(SP pSurface) // TODO: this has some edge cases when it's wrong (e.g. destroyed lock but not yet surfaces) // but can be easily fixed when I rewrite wlr_surface - if (!m_sessionLock) + if (!m_pSessionLock) return false; - for (auto const& sls : m_sessionLock->vSessionLockSurfaces) { + for (auto const& sls : m_pSessionLock->vSessionLockSurfaces) { if (sls->surface->surface() == pSurface) return true; } @@ -190,41 +160,37 @@ bool CSessionLockManager::isSurfaceSessionLock(SP pSurface) return false; } -bool CSessionLockManager::anySessionLockSurfacesPresent() { - return m_sessionLock && std::ranges::any_of(m_sessionLock->vSessionLockSurfaces, [](const auto& surf) { return surf->mapped; }); -} - void CSessionLockManager::removeSessionLockSurface(SSessionLockSurface* pSLS) { - if (!m_sessionLock) + if (!m_pSessionLock) return; - std::erase_if(m_sessionLock->vSessionLockSurfaces, [&](const auto& other) { return pSLS == other.get(); }); + std::erase_if(m_pSessionLock->vSessionLockSurfaces, [&](const auto& other) { return pSLS == other.get(); }); - if (Desktop::focusState()->surface()) + if (g_pCompositor->m_pLastFocus) return; - for (auto const& sls : m_sessionLock->vSessionLockSurfaces) { + for (auto const& sls : m_pSessionLock->vSessionLockSurfaces) { if (!sls->mapped) continue; - Desktop::focusState()->rawSurfaceFocus(sls->surface->surface()); + g_pCompositor->focusSurface(sls->surface->surface()); break; } } -bool CSessionLockManager::clientLocked() { - return m_sessionLock && m_sessionLock->hasSentLocked; +bool CSessionLockManager::isSessionLockPresent() { + return m_pSessionLock && !m_pSessionLock->vSessionLockSurfaces.empty(); } -bool CSessionLockManager::clientDenied() { - return m_sessionLock && m_sessionLock->hasSentDenied; +bool CSessionLockManager::anySessionLockSurfacesPresent() { + return m_pSessionLock && std::ranges::any_of(m_pSessionLock->vSessionLockSurfaces, [](const auto& surf) { return surf->mapped; }); } bool CSessionLockManager::shallConsiderLockMissing() { - if (!m_sessionLock) - return true; + if (!m_pSessionLock) + return false; static auto LOCKDEAD_SCREEN_DELAY = CConfigValue("misc:lockdead_screen_delay"); - return m_sessionLock->lockTimer.getMillis() > *LOCKDEAD_SCREEN_DELAY; + return m_pSessionLock->mLockTimer.getMillis() > *LOCKDEAD_SCREEN_DELAY; } diff --git a/src/managers/SessionLockManager.hpp b/src/managers/SessionLockManager.hpp index efcaf09a..51d4cefb 100644 --- a/src/managers/SessionLockManager.hpp +++ b/src/managers/SessionLockManager.hpp @@ -1,9 +1,8 @@ #pragma once #include "../defines.hpp" -#include "../helpers/time/Timer.hpp" +#include "../helpers/Timer.hpp" #include "../helpers/signal/Signal.hpp" -#include "./eventLoop/EventLoopTimer.hpp" #include #include #include @@ -30,10 +29,10 @@ struct SSessionLockSurface { struct SSessionLock { WP lock; - CTimer lockTimer; - SP sendDeniedTimer; + CTimer mLockTimer; - std::vector> vSessionLockSurfaces; + std::vector> vSessionLockSurfaces; + std::unordered_map mMonitorsWithoutMappedSurfaceTimers; struct { CHyprSignalListener newSurface; @@ -41,9 +40,8 @@ struct SSessionLock { CHyprSignalListener destroy; } listeners; - bool hasSentLocked = false; - bool hasSentDenied = false; - std::unordered_set lockedMonitors; + bool m_hasSentLocked = false; + std::unordered_set m_lockedMonitors; }; class CSessionLockManager { @@ -53,9 +51,10 @@ class CSessionLockManager { WP getSessionLockSurfaceForMonitor(uint64_t); + float getRedScreenAlphaForMonitor(uint64_t); + bool isSessionLocked(); - bool clientLocked(); - bool clientDenied(); + bool isSessionLockPresent(); bool isSurfaceSessionLock(SP); bool anySessionLockSurfacesPresent(); @@ -66,14 +65,13 @@ class CSessionLockManager { bool shallConsiderLockMissing(); private: - UP m_sessionLock; + UP m_pSessionLock; struct { CHyprSignalListener newLock; - } m_listeners; + } listeners; void onNewSessionLock(SP pWlrLock); - void removeSendDeniedTimer(); }; inline UP g_pSessionLockManager; diff --git a/src/managers/TokenManager.cpp b/src/managers/TokenManager.cpp index 694830db..5efa2eb8 100644 --- a/src/managers/TokenManager.cpp +++ b/src/managers/TokenManager.cpp @@ -2,12 +2,12 @@ #include #include -CUUIDToken::CUUIDToken(const std::string& uuid_, std::any data_, Time::steady_dur expires) : m_data(data_), m_uuid(uuid_) { - m_expiresAt = Time::steadyNow() + expires; +CUUIDToken::CUUIDToken(const std::string& uuid_, std::any data_, std::chrono::steady_clock::duration expires) : data(data_), uuid(uuid_) { + expiresAt = std::chrono::steady_clock::now() + expires; } std::string CUUIDToken::getUUID() { - return m_uuid; + return uuid; } std::string CTokenManager::getRandomUUID() { @@ -15,36 +15,35 @@ std::string CTokenManager::getRandomUUID() { do { uuid_t uuid_; uuid_generate_random(uuid_); - uuid = std::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", sc(uuid_[0]), sc(uuid_[1]), - sc(uuid_[2]), sc(uuid_[3]), sc(uuid_[4]), sc(uuid_[5]), sc(uuid_[6]), sc(uuid_[7]), - sc(uuid_[8]), sc(uuid_[9]), sc(uuid_[10]), sc(uuid_[11]), sc(uuid_[12]), sc(uuid_[13]), - sc(uuid_[14]), sc(uuid_[15])); - } while (m_tokens.contains(uuid)); + uuid = std::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", (uint16_t)uuid_[0], (uint16_t)uuid_[1], + (uint16_t)uuid_[2], (uint16_t)uuid_[3], (uint16_t)uuid_[4], (uint16_t)uuid_[5], (uint16_t)uuid_[6], (uint16_t)uuid_[7], (uint16_t)uuid_[8], + (uint16_t)uuid_[9], (uint16_t)uuid_[10], (uint16_t)uuid_[11], (uint16_t)uuid_[12], (uint16_t)uuid_[13], (uint16_t)uuid_[14], (uint16_t)uuid_[15]); + } while (m_mTokens.contains(uuid)); return uuid; } -std::string CTokenManager::registerNewToken(std::any data, Time::steady_dur expires) { +std::string CTokenManager::registerNewToken(std::any data, std::chrono::steady_clock::duration expires) { std::string uuid = getRandomUUID(); - m_tokens[uuid] = makeShared(uuid, data, expires); + m_mTokens[uuid] = makeShared(uuid, data, expires); return uuid; } SP CTokenManager::getToken(const std::string& uuid) { // cleanup expired tokens - const auto NOW = Time::steadyNow(); - std::erase_if(m_tokens, [&NOW](const auto& el) { return el.second->m_expiresAt < NOW; }); + const auto NOW = std::chrono::steady_clock::now(); + std::erase_if(m_mTokens, [&NOW](const auto& el) { return el.second->expiresAt < NOW; }); - if (!m_tokens.contains(uuid)) + if (!m_mTokens.contains(uuid)) return {}; - return m_tokens.at(uuid); + return m_mTokens.at(uuid); } void CTokenManager::removeToken(SP token) { if (!token) return; - m_tokens.erase(token->m_uuid); + m_mTokens.erase(token->uuid); } diff --git a/src/managers/TokenManager.hpp b/src/managers/TokenManager.hpp index 10faee7b..aaf068bc 100644 --- a/src/managers/TokenManager.hpp +++ b/src/managers/TokenManager.hpp @@ -1,23 +1,24 @@ #pragma once +#include #include #include #include #include "../helpers/memory/Memory.hpp" -#include "../helpers/time/Time.hpp" class CUUIDToken { public: - CUUIDToken(const std::string& uuid_, std::any data_, Time::steady_dur expires); + CUUIDToken(const std::string& uuid_, std::any data_, std::chrono::steady_clock::duration expires); std::string getUUID(); - std::any m_data; + std::any data; private: - std::string m_uuid; - Time::steady_tp m_expiresAt; + std::string uuid; + + std::chrono::steady_clock::time_point expiresAt; friend class CTokenManager; }; @@ -31,7 +32,7 @@ class CTokenManager { void removeToken(SP token); private: - std::unordered_map> m_tokens; + std::unordered_map> m_mTokens; }; inline UP g_pTokenManager; \ No newline at end of file diff --git a/src/managers/VersionKeeperManager.cpp b/src/managers/VersionKeeperManager.cpp index b1d167fa..cc03a7b9 100644 --- a/src/managers/VersionKeeperManager.cpp +++ b/src/managers/VersionKeeperManager.cpp @@ -1,5 +1,5 @@ #include "VersionKeeperManager.hpp" -#include "../debug/log/Logger.hpp" +#include "../debug/Log.hpp" #include "../macros.hpp" #include "../version.h" #include "../helpers/MiscFunctions.hpp" @@ -26,32 +26,29 @@ CVersionKeeperManager::CVersionKeeperManager() { if (!DATAROOT) return; - auto LASTVER = NFsUtils::readFileAsString(*DATAROOT + "/" + VERSION_FILE_NAME); + const auto LASTVER = NFsUtils::readFileAsString(*DATAROOT + "/" + VERSION_FILE_NAME); - if (!LASTVER) { - NFsUtils::writeToFile(*DATAROOT + "/" + VERSION_FILE_NAME, "0.0.0"); - LASTVER = "0.0.0"; + if (!LASTVER) return; - } - if (!isMajorVersionOlderThanRunning(*LASTVER)) { - Log::logger->log(Log::DEBUG, "CVersionKeeperManager: Read version {} matches or is older than running major.", *LASTVER); + if (!isVersionOlderThanRunning(*LASTVER)) { + Debug::log(LOG, "CVersionKeeperManager: Read version {} matches or is older than running.", *LASTVER); return; } NFsUtils::writeToFile(*DATAROOT + "/" + VERSION_FILE_NAME, HYPRLAND_VERSION); if (*PNONOTIFY) { - Log::logger->log(Log::DEBUG, "CVersionKeeperManager: updated, but update news is disabled in the config :("); + Debug::log(LOG, "CVersionKeeperManager: updated, but update news is disabled in the config :("); return; } if (!NFsUtils::executableExistsInPath("hyprland-update-screen")) { - Log::logger->log(Log::ERR, "CVersionKeeperManager: hyprland-update-screen doesn't seem to exist, skipping notif about update..."); + Debug::log(ERR, "CVersionKeeperManager: hyprland-update-screen doesn't seem to exist, skipping notif about update..."); return; } - m_fired = true; + m_bFired = true; g_pEventLoopManager->doLater([]() { CProcess proc("hyprland-update-screen", {"--new-version", HYPRLAND_VERSION}); @@ -59,24 +56,28 @@ CVersionKeeperManager::CVersionKeeperManager() { }); } -bool CVersionKeeperManager::isMajorVersionOlderThanRunning(const std::string& ver) { +bool CVersionKeeperManager::isVersionOlderThanRunning(const std::string& ver) { const CVarList verStrings(ver, 0, '.', true); const int V1 = configStringToInt(verStrings[0]).value_or(0); const int V2 = configStringToInt(verStrings[1]).value_or(0); + const int V3 = configStringToInt(verStrings[2]).value_or(0); static const CVarList runningStrings(HYPRLAND_VERSION, 0, '.', true); static const int R1 = configStringToInt(runningStrings[0]).value_or(0); static const int R2 = configStringToInt(runningStrings[1]).value_or(0); + static const int R3 = configStringToInt(runningStrings[2]).value_or(0); if (R1 > V1) return true; if (R2 > V2) return true; + if (R3 > V3) + return true; return false; } bool CVersionKeeperManager::fired() { - return m_fired; + return m_bFired; } diff --git a/src/managers/VersionKeeperManager.hpp b/src/managers/VersionKeeperManager.hpp index 11bfb7df..250a36b8 100644 --- a/src/managers/VersionKeeperManager.hpp +++ b/src/managers/VersionKeeperManager.hpp @@ -10,9 +10,9 @@ class CVersionKeeperManager { bool fired(); private: - bool isMajorVersionOlderThanRunning(const std::string& ver); + bool isVersionOlderThanRunning(const std::string& ver); - bool m_fired = false; + bool m_bFired = false; }; inline UP g_pVersionKeeperMgr; \ No newline at end of file diff --git a/src/managers/WelcomeManager.cpp b/src/managers/WelcomeManager.cpp deleted file mode 100644 index 6faf58c3..00000000 --- a/src/managers/WelcomeManager.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "WelcomeManager.hpp" -#include "../Compositor.hpp" -#include "../debug/log/Logger.hpp" -#include "../config/ConfigValue.hpp" -#include "../helpers/fs/FsUtils.hpp" - -#include - -using namespace Hyprutils::OS; - -CWelcomeManager::CWelcomeManager() { - static auto PAUTOGEN = CConfigValue("autogenerated"); - - if (!*PAUTOGEN) { - Log::logger->log(Log::DEBUG, "[welcome] skipping, not autogen"); - return; - } - - if (g_pCompositor->m_safeMode) { - Log::logger->log(Log::DEBUG, "[welcome] skipping, safe mode"); - return; - } - - if (!NFsUtils::executableExistsInPath("hyprland-welcome")) { - Log::logger->log(Log::DEBUG, "[welcome] skipping, no welcome app"); - return; - } - - m_fired = true; - - CProcess welcome("hyprland-welcome", {}); - welcome.runAsync(); -} - -bool CWelcomeManager::fired() { - return m_fired; -} diff --git a/src/managers/WelcomeManager.hpp b/src/managers/WelcomeManager.hpp deleted file mode 100644 index 1f0535eb..00000000 --- a/src/managers/WelcomeManager.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "../helpers/memory/Memory.hpp" - -class CWelcomeManager { - public: - CWelcomeManager(); - - // whether the welcome screen was shown this boot. - bool fired(); - - private: - bool m_fired = false; -}; - -inline UP g_pWelcomeManager; \ No newline at end of file diff --git a/src/managers/XCursorManager.cpp b/src/managers/XCursorManager.cpp index 90cd2a32..0f0c9e80 100644 --- a/src/managers/XCursorManager.cpp +++ b/src/managers/XCursorManager.cpp @@ -1,9 +1,3 @@ -#define Time XTime__ -extern "C" { -#include -} -#undef Time - #include #include #include @@ -13,11 +7,9 @@ extern "C" { #include "config/ConfigValue.hpp" #include "helpers/CursorShapes.hpp" #include "../managers/CursorManager.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #include "XCursorManager.hpp" #include -#include -#include // clang-format off static std::vector HYPR_XCURSOR_PIXELS = { @@ -98,45 +90,45 @@ static std::vector HYPR_XCURSOR_PIXELS = { // clang-format on CXCursorManager::CXCursorManager() { - m_hyprCursor = makeShared(); + hyprCursor = makeShared(); SXCursorImage image; image.size = {32, 32}; image.hotspot = {3, 2}; image.pixels = HYPR_XCURSOR_PIXELS; image.delay = 0; - m_hyprCursor->images.push_back(image); - m_hyprCursor->shape = "left_ptr"; - m_defaultCursor = m_hyprCursor; + hyprCursor->images.push_back(image); + hyprCursor->shape = "left_ptr"; + defaultCursor = hyprCursor; } void CXCursorManager::loadTheme(std::string const& name, int size, float scale) { - if (m_lastLoadSize == (size * std::ceil(scale)) && m_themeName == name && m_lastLoadScale == scale) + if (lastLoadSize == (size * std::ceil(scale)) && themeName == name && lastLoadScale == scale) return; - m_lastLoadSize = size * std::ceil(scale); - m_lastLoadScale = scale; - m_themeName = name.empty() ? "default" : name; - m_defaultCursor.reset(); - m_cursors.clear(); + lastLoadSize = size * std::ceil(scale); + lastLoadScale = scale; + themeName = name.empty() ? "default" : name; + defaultCursor.reset(); + cursors.clear(); - auto paths = themePaths(m_themeName); + auto paths = themePaths(themeName); if (paths.empty()) { - Log::logger->log(Log::ERR, "XCursor librarypath is empty loading standard XCursors"); - m_cursors = loadStandardCursors(m_themeName, m_lastLoadSize); + Debug::log(ERR, "XCursor librarypath is empty loading standard XCursors"); + cursors = loadStandardCursors(themeName, lastLoadSize); } else { for (auto const& p : paths) { try { - auto dirCursors = loadAllFromDir(p, m_lastLoadSize); - std::ranges::copy_if(dirCursors, std::back_inserter(m_cursors), - [this](auto const& p) { return std::ranges::none_of(m_cursors, [&p](auto const& dp) { return dp->shape == p->shape; }); }); - } catch (std::exception& e) { Log::logger->log(Log::ERR, "XCursor path {} can't be loaded: threw error {}", p, e.what()); } + auto dirCursors = loadAllFromDir(p, lastLoadSize); + std::copy_if(dirCursors.begin(), dirCursors.end(), std::back_inserter(cursors), + [this](auto const& p) { return std::none_of(cursors.begin(), cursors.end(), [&p](auto const& dp) { return dp->shape == p->shape; }); }); + } catch (std::exception& e) { Debug::log(ERR, "XCursor path {} can't be loaded: threw error {}", p, e.what()); } } } - if (m_cursors.empty()) { - Log::logger->log(Log::ERR, "XCursor failed finding any shapes in theme \"{}\".", m_themeName); - m_defaultCursor = m_hyprCursor; + if (cursors.empty()) { + Debug::log(ERR, "XCursor failed finding any shapes in theme \"{}\".", themeName); + defaultCursor = hyprCursor; return; } @@ -145,15 +137,15 @@ void CXCursorManager::loadTheme(std::string const& name, int size, float scale) if (legacyName.empty()) continue; - auto it = std::ranges::find_if(m_cursors, [&legacyName](auto const& c) { return c->shape == legacyName; }); + auto it = std::find_if(cursors.begin(), cursors.end(), [&legacyName](auto const& c) { return c->shape == legacyName; }); - if (it == m_cursors.end()) { - Log::logger->log(Log::DEBUG, "XCursor failed to find a legacy shape with name {}, skipping", legacyName); + if (it == cursors.end()) { + Debug::log(LOG, "XCursor failed to find a legacy shape with name {}, skipping", legacyName); continue; } - if (std::ranges::any_of(m_cursors, [&shape](auto const& dp) { return dp->shape == shape; })) { - Log::logger->log(Log::DEBUG, "XCursor already has a shape {} loaded, skipping", shape); + if (std::any_of(cursors.begin(), cursors.end(), [&shape](auto const& dp) { return dp->shape == shape; })) { + Debug::log(LOG, "XCursor already has a shape {} loaded, skipping", shape); continue; } @@ -161,7 +153,7 @@ void CXCursorManager::loadTheme(std::string const& name, int size, float scale) cursor->images = it->get()->images; cursor->shape = shape; - m_cursors.emplace_back(cursor); + cursors.emplace_back(cursor); } syncGsettings(); @@ -169,32 +161,31 @@ void CXCursorManager::loadTheme(std::string const& name, int size, float scale) SP CXCursorManager::getShape(std::string const& shape, int size, float scale) { // monitor scaling changed etc, so reload theme with new size. - if ((size * std::ceil(scale)) != m_lastLoadSize || scale != m_lastLoadScale) - loadTheme(m_themeName, size, scale); + if ((size * std::ceil(scale)) != lastLoadSize || scale != lastLoadScale) + loadTheme(themeName, size, scale); // try to get an icon we know if we have one - for (auto const& c : m_cursors) { + for (auto const& c : cursors) { if (c->shape != shape) continue; return c; } - Log::logger->log(Log::WARN, "XCursor couldn't find shape {} , using default cursor instead", shape); - return m_defaultCursor; + Debug::log(WARN, "XCursor couldn't find shape {} , using default cursor instead", shape); + return defaultCursor; } -SP CXCursorManager::createCursor(std::string const& shape, void* ximages) { - auto xcursor = makeShared(); - XcursorImages* xImages = sc(ximages); +SP CXCursorManager::createCursor(std::string const& shape, XcursorImages* xImages) { + auto xcursor = makeShared(); for (int i = 0; i < xImages->nimage; i++) { auto xImage = xImages->images[i]; SXCursorImage image; - image.size = {sc(xImage->width), sc(xImage->height)}; - image.hotspot = {sc(xImage->xhot), sc(xImage->yhot)}; - image.pixels.resize(sc(xImage->width) * xImage->height); - std::memcpy(image.pixels.data(), xImage->pixels, sc(xImage->width) * xImage->height * sizeof(uint32_t)); + image.size = {(int)xImage->width, (int)xImage->height}; + image.hotspot = {(int)xImage->xhot, (int)xImage->yhot}; + image.pixels.resize((size_t)xImage->width * xImage->height); + std::memcpy(image.pixels.data(), xImage->pixels, (size_t)xImage->width * xImage->height * sizeof(uint32_t)); image.delay = xImage->delay; xcursor->images.emplace_back(image); @@ -222,7 +213,7 @@ std::set CXCursorManager::themePaths(std::string const& theme) { std::string line; std::vector themes; - Log::logger->log(Log::DEBUG, "XCursor parsing index.theme {}", indexTheme); + Debug::log(LOG, "XCursor parsing index.theme {}", indexTheme); while (std::getline(infile, line)) { if (line.empty()) @@ -291,12 +282,12 @@ std::set CXCursorManager::themePaths(std::string const& theme) { std::stringstream ss(path); std::string line; - Log::logger->log(Log::DEBUG, "XCursor scanning theme {}", t); + Debug::log(LOG, "XCursor scanning theme {}", t); while (std::getline(ss, line, ':')) { auto p = expandTilde(line + "/" + t + "/cursors"); if (std::filesystem::exists(p) && std::filesystem::is_directory(p)) { - Log::logger->log(Log::DEBUG, "XCursor using theme path {}", p); + Debug::log(LOG, "XCursor using theme path {}", p); paths.insert(p); } @@ -304,7 +295,7 @@ std::set CXCursorManager::themePaths(std::string const& theme) { if (std::filesystem::exists(inherit) && std::filesystem::is_regular_file(inherit)) { auto inheritThemes = getInheritThemes(inherit); for (auto const& i : inheritThemes) { - Log::logger->log(Log::DEBUG, "XCursor theme {} inherits {}", t, i); + Debug::log(LOG, "XCursor theme {} inherits {}", t, i); inherits.insert(i); } } @@ -397,10 +388,6 @@ std::string CXCursorManager::getLegacyShapeName(std::string const& shape) { return "left_ptr"; else if (shape == "zoom-out") return "left_ptr"; - else if (shape == "dnd-ask") - return "dnd-copy"; - else if (shape == "all-resize") - return "fleur"; return std::string(); }; @@ -497,11 +484,11 @@ std::vector> CXCursorManager::loadStandardCursors(std::string cons auto xImages = XcursorShapeLoadImages(i << 1 /* wtf xcursor? */, name.c_str(), size); if (!xImages) { - Log::logger->log(Log::WARN, "XCursor failed to find a shape with name {}, trying size 24.", shape); + Debug::log(WARN, "XCursor failed to find a shape with name {}, trying size 24.", shape); xImages = XcursorShapeLoadImages(i << 1 /* wtf xcursor? */, name.c_str(), 24); if (!xImages) { - Log::logger->log(Log::WARN, "XCursor failed to find a shape with name {}, skipping", shape); + Debug::log(WARN, "XCursor failed to find a shape with name {}, skipping", shape); continue; } } @@ -509,15 +496,15 @@ std::vector> CXCursorManager::loadStandardCursors(std::string cons auto cursor = createCursor(shape, xImages); newCursors.emplace_back(cursor); - if (!m_defaultCursor && (shape == "left_ptr" || shape == "arrow")) - m_defaultCursor = cursor; + if (!defaultCursor && (shape == "left_ptr" || shape == "arrow")) + defaultCursor = cursor; XcursorImagesDestroy(xImages); } // broken theme.. just set it. - if (!newCursors.empty() && !m_defaultCursor) - m_defaultCursor = newCursors.front(); + if (!newCursors.empty() && !defaultCursor) + defaultCursor = newCursors.front(); return newCursors; } @@ -529,13 +516,13 @@ std::vector> CXCursorManager::loadAllFromDir(std::string const& pa for (const auto& entry : std::filesystem::directory_iterator(path)) { std::error_code e1, e2; if ((!entry.is_regular_file(e1) && !entry.is_symlink(e2)) || e1 || e2) { - Log::logger->log(Log::WARN, "XCursor failed to load shape {}: {}", entry.path().stem().string(), e1 ? e1.message() : e2.message()); + Debug::log(WARN, "XCursor failed to load shape {}: {}", entry.path().stem().string(), e1 ? e1.message() : e2.message()); continue; } auto const& full = entry.path().string(); using PcloseType = int (*)(FILE*); - const std::unique_ptr f(fopen(full.c_str(), "r"), fclose); + const std::unique_ptr f(fopen(full.c_str(), "r"), static_cast(fclose)); if (!f) continue; @@ -543,11 +530,11 @@ std::vector> CXCursorManager::loadAllFromDir(std::string const& pa auto xImages = XcursorFileLoadImages(f.get(), size); if (!xImages) { - Log::logger->log(Log::WARN, "XCursor failed to load image {}, trying size 24.", full); + Debug::log(WARN, "XCursor failed to load image {}, trying size 24.", full); xImages = XcursorFileLoadImages(f.get(), 24); if (!xImages) { - Log::logger->log(Log::WARN, "XCursor failed to load image {}, skipping", full); + Debug::log(WARN, "XCursor failed to load image {}, skipping", full); continue; } } @@ -556,16 +543,16 @@ std::vector> CXCursorManager::loadAllFromDir(std::string const& pa auto cursor = createCursor(shape, xImages); newCursors.emplace_back(cursor); - if (!m_defaultCursor && (shape == "left_ptr" || shape == "arrow")) - m_defaultCursor = cursor; + if (!defaultCursor && (shape == "left_ptr" || shape == "arrow")) + defaultCursor = cursor; XcursorImagesDestroy(xImages); } } // broken theme.. just set it. - if (!newCursors.empty() && !m_defaultCursor) - m_defaultCursor = newCursors.front(); + if (!newCursors.empty() && !defaultCursor) + defaultCursor = newCursors.front(); return newCursors; } @@ -579,7 +566,7 @@ void CXCursorManager::syncGsettings() { auto* gSettingsSchemaSource = g_settings_schema_source_get_default(); if (!gSettingsSchemaSource) { - Log::logger->log(Log::WARN, "GSettings default schema source does not exist, can't sync GSettings"); + Debug::log(WARN, "GSettings default schema source does not exist, cant sync GSettings"); return false; } @@ -597,14 +584,14 @@ void CXCursorManager::syncGsettings() { using SettingValue = std::variant; auto setValue = [&checkParamExists](std::string const& paramName, const SettingValue& paramValue, std::string const& category) { if (!checkParamExists(paramName, category)) { - Log::logger->log(Log::WARN, "GSettings parameter doesn't exist {} in {}", paramName, category); + Debug::log(WARN, "GSettings parameter doesnt exist {} in {}", paramName, category); return; } auto* gsettings = g_settings_new(category.c_str()); if (!gsettings) { - Log::logger->log(Log::WARN, "GSettings failed to allocate new settings with category {}", category); + Debug::log(WARN, "GSettings failed to allocate new settings with category {}", category); return; } @@ -622,7 +609,7 @@ void CXCursorManager::syncGsettings() { g_object_unref(gsettings); }; - int unscaledSize = m_lastLoadSize / std::ceil(m_lastLoadScale); - setValue("cursor-theme", m_themeName, "org.gnome.desktop.interface"); + int unscaledSize = lastLoadSize / std::ceil(lastLoadScale); + setValue("cursor-theme", themeName, "org.gnome.desktop.interface"); setValue("cursor-size", unscaledSize, "org.gnome.desktop.interface"); } diff --git a/src/managers/XCursorManager.hpp b/src/managers/XCursorManager.hpp index 2535d8a0..3052206f 100644 --- a/src/managers/XCursorManager.hpp +++ b/src/managers/XCursorManager.hpp @@ -7,6 +7,10 @@ #include #include "helpers/memory/Memory.hpp" +extern "C" { +#include +} + // gangsta bootleg XCursor impl. adidas balkanized struct SXCursorImage { Hyprutils::Math::Vector2D size; @@ -30,16 +34,16 @@ class CXCursorManager { void syncGsettings(); private: - SP createCursor(std::string const& shape, void* /* XcursorImages* */ xImages); + SP createCursor(std::string const& shape, XcursorImages* xImages); std::set themePaths(std::string const& theme); std::string getLegacyShapeName(std::string const& shape); std::vector> loadStandardCursors(std::string const& name, int size); std::vector> loadAllFromDir(std::string const& path, int size); - int m_lastLoadSize = 0; - float m_lastLoadScale = 0; - std::string m_themeName = ""; - SP m_defaultCursor; - SP m_hyprCursor; - std::vector> m_cursors; + int lastLoadSize = 0; + float lastLoadScale = 0; + std::string themeName = ""; + SP defaultCursor; + SP hyprCursor; + std::vector> cursors; }; diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp index 1fca293a..63b14962 100644 --- a/src/managers/XWaylandManager.cpp +++ b/src/managers/XWaylandManager.cpp @@ -1,8 +1,7 @@ #include "XWaylandManager.hpp" #include "../Compositor.hpp" -#include "../desktop/state/FocusState.hpp" +#include "../events/Events.hpp" #include "../config/ConfigValue.hpp" -#include "../helpers/Monitor.hpp" #include "../protocols/XDGShell.hpp" #include "../protocols/core/Compositor.hpp" #include "../xwayland/XWayland.hpp" @@ -21,60 +20,60 @@ CHyprXWaylandManager::~CHyprXWaylandManager() { } SP CHyprXWaylandManager::getWindowSurface(PHLWINDOW pWindow) { - return pWindow ? pWindow->wlSurface()->resource() : nullptr; + return pWindow ? pWindow->m_pWLSurface->resource() : nullptr; } void CHyprXWaylandManager::activateSurface(SP pSurface, bool activate) { if (!pSurface) return; - auto HLSurface = Desktop::View::CWLSurface::fromResource(pSurface); + auto HLSurface = CWLSurface::fromResource(pSurface); if (!HLSurface) { - Log::logger->log(Log::TRACE, "CHyprXWaylandManager::activateSurface on non-desktop surface, ignoring"); + Debug::log(TRACE, "CHyprXWaylandManager::activateSurface on non-desktop surface, ignoring"); return; } - const auto PWINDOW = Desktop::View::CWindow::fromView(HLSurface->view()); + const auto PWINDOW = HLSurface->getWindow(); if (!PWINDOW) { - Log::logger->log(Log::TRACE, "CHyprXWaylandManager::activateSurface on non-window surface, ignoring"); + Debug::log(TRACE, "CHyprXWaylandManager::activateSurface on non-window surface, ignoring"); return; } - if (PWINDOW->m_isX11) { - if (PWINDOW->m_xwaylandSurface) { + if (PWINDOW->m_bIsX11) { + if (PWINDOW->m_pXWaylandSurface) { if (activate) { - PWINDOW->m_xwaylandSurface->setMinimized(false); - PWINDOW->m_xwaylandSurface->restackToTop(); + PWINDOW->m_pXWaylandSurface->setMinimized(false); + PWINDOW->m_pXWaylandSurface->restackToTop(); } - PWINDOW->m_xwaylandSurface->activate(activate); + PWINDOW->m_pXWaylandSurface->activate(activate); } - } else if (PWINDOW->m_xdgSurface && PWINDOW->m_xdgSurface->m_toplevel) - PWINDOW->m_xdgSurface->m_toplevel->setActive(activate); + } else if (PWINDOW->m_pXDGSurface && PWINDOW->m_pXDGSurface->toplevel) + PWINDOW->m_pXDGSurface->toplevel->setActive(activate); } void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) { - if (pWindow->m_isX11) { + if (pWindow->m_bIsX11) { if (activate) { pWindow->sendWindowSize(true); // update xwayland output pos - pWindow->m_xwaylandSurface->setMinimized(false); + pWindow->m_pXWaylandSurface->setMinimized(false); if (!pWindow->isX11OverrideRedirect()) - pWindow->m_xwaylandSurface->restackToTop(); + pWindow->m_pXWaylandSurface->restackToTop(); } - pWindow->m_xwaylandSurface->activate(activate); + pWindow->m_pXWaylandSurface->activate(activate); - } else if (pWindow->m_xdgSurface && pWindow->m_xdgSurface->m_toplevel) - pWindow->m_xdgSurface->m_toplevel->setActive(activate); + } else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->setActive(activate); if (activate) { - Desktop::focusState()->surface() = getWindowSurface(pWindow); - Desktop::focusState()->window() = pWindow; + g_pCompositor->m_pLastFocus = getWindowSurface(pWindow); + g_pCompositor->m_pLastWindow = pWindow; } - if (!pWindow->m_pinned) - pWindow->m_workspace->m_lastFocusedWindow = pWindow; + if (!pWindow->m_bPinned) + pWindow->m_pWorkspace->m_pLastFocusedWindow = pWindow; } CBox CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow) { @@ -83,61 +82,50 @@ CBox CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow) { CBox box; - if (pWindow->m_isX11) - box = pWindow->m_xwaylandSurface->m_geometry; - else if (pWindow->m_xdgSurface) - box = pWindow->m_xdgSurface->m_current.geometry; - - Vector2D MINSIZE = pWindow->minSize().value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}); - Vector2D MAXSIZE = pWindow->maxSize().value_or(Math::VECTOR2D_MAX).clamp(MINSIZE + Vector2D{1, 1}); - - Vector2D oldSize = box.size(); - box.w = std::clamp(box.w, MINSIZE.x, MAXSIZE.x); - box.h = std::clamp(box.h, MINSIZE.y, MAXSIZE.y); - box.translate((oldSize - box.size()) / 2.F); + if (pWindow->m_bIsX11) + box = pWindow->m_pXWaylandSurface->geometry; + else if (pWindow->m_pXDGSurface) + box = pWindow->m_pXDGSurface->current.geometry; return box; } void CHyprXWaylandManager::sendCloseWindow(PHLWINDOW pWindow) { - if (pWindow->m_isX11) - pWindow->m_xwaylandSurface->close(); - else if (pWindow->m_xdgSurface && pWindow->m_xdgSurface->m_toplevel) - pWindow->m_xdgSurface->m_toplevel->close(); + if (pWindow->m_bIsX11) + pWindow->m_pXWaylandSurface->close(); + else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->close(); } bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) { - if (pWindow->m_isX11) { - for (const auto& a : pWindow->m_xwaylandSurface->m_atoms) + if (pWindow->m_bIsX11) { + for (const auto& a : pWindow->m_pXWaylandSurface->atoms) if (a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DIALOG"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_SPLASH"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLBAR"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_UTILITY"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLTIP"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DOCK"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"] || a == HYPRATOMS["_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"]) { if (a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"]) - pWindow->m_X11ShouldntFocus = true; + pWindow->m_bX11ShouldntFocus = true; if (a != HYPRATOMS["_NET_WM_WINDOW_TYPE_DIALOG"]) - pWindow->m_noInitialFocus = true; + pWindow->m_bNoInitialFocus = true; return true; } - if (pWindow->isModal() || pWindow->m_xwaylandSurface->m_transient || - (pWindow->m_xwaylandSurface->m_role.contains("task_dialog") || pWindow->m_xwaylandSurface->m_role.contains("pop-up")) || pWindow->m_xwaylandSurface->m_overrideRedirect) + if (pWindow->isModal() || pWindow->m_pXWaylandSurface->transient || + (pWindow->m_pXWaylandSurface->role.contains("task_dialog") || pWindow->m_pXWaylandSurface->role.contains("pop-up")) || pWindow->m_pXWaylandSurface->overrideRedirect) return true; - const auto SIZEHINTS = pWindow->m_xwaylandSurface->m_sizeHints.get(); - if (pWindow->m_xwaylandSurface->m_transient || pWindow->m_xwaylandSurface->m_parent || - (SIZEHINTS && SIZEHINTS->min_width > 0 && SIZEHINTS->min_height > 0 && SIZEHINTS->max_width > 0 && SIZEHINTS->max_height > 0 && - (SIZEHINTS->min_width == SIZEHINTS->max_width) && (SIZEHINTS->min_height == SIZEHINTS->max_height))) + const auto SIZEHINTS = pWindow->m_pXWaylandSurface->sizeHints.get(); + if (pWindow->m_pXWaylandSurface->transient || pWindow->m_pXWaylandSurface->parent || + (SIZEHINTS && (SIZEHINTS->min_width == SIZEHINTS->max_width) && (SIZEHINTS->min_height == SIZEHINTS->max_height))) return true; } else { - if (!pWindow->m_xdgSurface || !pWindow->m_xdgSurface->m_toplevel) - return false; + const auto PSTATE = pending ? &pWindow->m_pXDGSurface->toplevel->pending : &pWindow->m_pXDGSurface->toplevel->current; - const auto PSTATE = pending ? &pWindow->m_xdgSurface->m_toplevel->m_pending : &pWindow->m_xdgSurface->m_toplevel->m_current; - if (pWindow->m_xdgSurface->m_toplevel->m_parent || + if (pWindow->m_pXDGSurface->toplevel->parent || (PSTATE->minSize.x != 0 && PSTATE->minSize.y != 0 && (PSTATE->minSize.x == PSTATE->maxSize.x || PSTATE->minSize.y == PSTATE->maxSize.y))) return true; } @@ -146,31 +134,31 @@ bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) { } void CHyprXWaylandManager::checkBorders(PHLWINDOW pWindow) { - if (!pWindow->m_isX11) + if (!pWindow->m_bIsX11) return; - for (auto const& a : pWindow->m_xwaylandSurface->m_atoms) { + for (auto const& a : pWindow->m_pXWaylandSurface->atoms) { if (a == HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_NOTIFICATION"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_COMBO"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_SPLASH"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLTIP"]) { - pWindow->m_X11DoesntWantBorders = true; + pWindow->m_bX11DoesntWantBorders = true; return; } } if (pWindow->isX11OverrideRedirect()) - pWindow->m_X11DoesntWantBorders = true; + pWindow->m_bX11DoesntWantBorders = true; } void CHyprXWaylandManager::setWindowFullscreen(PHLWINDOW pWindow, bool fullscreen) { if (!pWindow) return; - if (pWindow->m_isX11) - pWindow->m_xwaylandSurface->setFullscreen(fullscreen); - else if (pWindow->m_xdgSurface && pWindow->m_xdgSurface->m_toplevel) - pWindow->m_xdgSurface->m_toplevel->setFullscreen(fullscreen); + if (pWindow->m_bIsX11) + pWindow->m_pXWaylandSurface->setFullscreen(fullscreen); + else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) + pWindow->m_pXDGSurface->toplevel->setFullscreen(fullscreen); } Vector2D CHyprXWaylandManager::waylandToXWaylandCoords(const Vector2D& coord) { @@ -178,10 +166,10 @@ Vector2D CHyprXWaylandManager::waylandToXWaylandCoords(const Vector2D& coord) { PHLMONITOR pMonitor = nullptr; double bestDistance = __FLT_MAX__; - for (const auto& m : g_pCompositor->m_monitors) { - const auto SIZ = *PXWLFORCESCALEZERO ? m->m_transformedSize : m->m_size; + for (const auto& m : g_pCompositor->m_vMonitors) { + const auto SIZ = *PXWLFORCESCALEZERO ? m->vecTransformedSize : m->vecSize; - double distance = vecToRectDistanceSquared(coord, {m->m_position.x, m->m_position.y}, {m->m_position.x + SIZ.x - 1, m->m_position.y + SIZ.y - 1}); + double distance = vecToRectDistanceSquared(coord, {m->vecPosition.x, m->vecPosition.y}, {m->vecPosition.x + SIZ.x - 1, m->vecPosition.y + SIZ.y - 1}); if (distance < bestDistance) { bestDistance = distance; @@ -193,12 +181,12 @@ Vector2D CHyprXWaylandManager::waylandToXWaylandCoords(const Vector2D& coord) { return Vector2D{}; // get local coords - Vector2D result = coord - pMonitor->m_position; + Vector2D result = coord - pMonitor->vecPosition; // if scaled, scale if (*PXWLFORCESCALEZERO) - result *= pMonitor->m_scale; + result *= pMonitor->scale; // add pos - result += pMonitor->m_xwaylandPosition; + result += pMonitor->vecXWaylandPosition; return result; } @@ -209,11 +197,11 @@ Vector2D CHyprXWaylandManager::xwaylandToWaylandCoords(const Vector2D& coord) { PHLMONITOR pMonitor = nullptr; double bestDistance = __FLT_MAX__; - for (const auto& m : g_pCompositor->m_monitors) { - const auto SIZ = *PXWLFORCESCALEZERO ? m->m_transformedSize : m->m_size; + for (const auto& m : g_pCompositor->m_vMonitors) { + const auto SIZ = *PXWLFORCESCALEZERO ? m->vecTransformedSize : m->vecSize; double distance = - vecToRectDistanceSquared(coord, {m->m_xwaylandPosition.x, m->m_xwaylandPosition.y}, {m->m_xwaylandPosition.x + SIZ.x - 1, m->m_xwaylandPosition.y + SIZ.y - 1}); + vecToRectDistanceSquared(coord, {m->vecXWaylandPosition.x, m->vecXWaylandPosition.y}, {m->vecXWaylandPosition.x + SIZ.x - 1, m->vecXWaylandPosition.y + SIZ.y - 1}); if (distance < bestDistance) { bestDistance = distance; @@ -225,12 +213,12 @@ Vector2D CHyprXWaylandManager::xwaylandToWaylandCoords(const Vector2D& coord) { return Vector2D{}; // get local coords - Vector2D result = coord - pMonitor->m_xwaylandPosition; + Vector2D result = coord - pMonitor->vecXWaylandPosition; // if scaled, unscale if (*PXWLFORCESCALEZERO) - result /= pMonitor->m_scale; + result /= pMonitor->scale; // add pos - result += pMonitor->m_position; + result += pMonitor->vecPosition; return result; } diff --git a/src/managers/XWaylandManager.hpp b/src/managers/XWaylandManager.hpp index a26b7b68..e4f1e17a 100644 --- a/src/managers/XWaylandManager.hpp +++ b/src/managers/XWaylandManager.hpp @@ -1,9 +1,10 @@ #pragma once #include "../defines.hpp" -#include "../desktop/DesktopTypes.hpp" #include +class CWindow; // because clangd +typedef SP PHLWINDOW; class CWLSurfaceResource; class CHyprXWaylandManager { diff --git a/src/managers/animation/AnimationManager.cpp b/src/managers/animation/AnimationManager.cpp deleted file mode 100644 index c4b921cb..00000000 --- a/src/managers/animation/AnimationManager.cpp +++ /dev/null @@ -1,367 +0,0 @@ -#include "AnimationManager.hpp" -#include "../../Compositor.hpp" -#include "../../config/ConfigManager.hpp" -#include "../../desktop/DesktopTypes.hpp" -#include "../../helpers/AnimatedVariable.hpp" -#include "../../macros.hpp" -#include "../../config/ConfigValue.hpp" -#include "../../desktop/view/Window.hpp" -#include "../../desktop/view/LayerSurface.hpp" -#include "../eventLoop/EventLoopManager.hpp" -#include "../../helpers/varlist/VarList.hpp" -#include "../../render/Renderer.hpp" -#include "../../event/EventBus.hpp" - -#include -#include -#include - -static int wlTick(SP self, void* data) { - if (g_pAnimationManager) - g_pAnimationManager->frameTick(); - - return 0; -} - -CHyprAnimationManager::CHyprAnimationManager() { - m_animationTimer = makeShared(std::chrono::microseconds(500), wlTick, nullptr); - if (g_pEventLoopManager) // null in --verify-config mode - g_pEventLoopManager->addTimer(m_animationTimer); - - addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0)); -} - -template -static void updateVariable(CAnimatedVariable& av, const float POINTY, bool warp = false) { - if (warp || av.value() == av.goal()) { - av.warp(true, false); - return; - } - - const auto DELTA = av.goal() - av.begun(); - av.value() = av.begun() + DELTA * POINTY; -} - -static void updateColorVariable(CAnimatedVariable& av, const float POINTY, bool warp) { - if (warp || av.value() == av.goal()) { - av.warp(true, false); - return; - } - - // convert both to OkLab, then lerp that, and convert back. - // This is not as fast as just lerping rgb, but it's WAY more precise... - // Use the CHyprColor cache for OkLab - - const auto& L1 = av.begun().asOkLab(); - const auto& L2 = av.goal().asOkLab(); - - static const auto lerp = [](const float one, const float two, const float progress) -> float { return one + ((two - one) * progress); }; - - const Hyprgraphics::CColor lerped = Hyprgraphics::CColor::SOkLab{ - .l = lerp(L1.l, L2.l, POINTY), - .a = lerp(L1.a, L2.a, POINTY), - .b = lerp(L1.b, L2.b, POINTY), - }; - - av.value() = {lerped, lerp(av.begun().a, av.goal().a, POINTY)}; -} - -template -static void handleUpdate(CAnimatedVariable& av, bool warp) { - PHLWINDOW PWINDOW = av.m_Context.pWindow.lock(); - PHLWORKSPACE PWORKSPACE = av.m_Context.pWorkspace.lock(); - PHLLS PLAYER = av.m_Context.pLayer.lock(); - PHLMONITOR PMONITOR = nullptr; - bool animationsDisabled = warp; - - if (PWINDOW) { - if (av.m_Context.eDamagePolicy == AVARDAMAGE_ENTIRE) - g_pHyprRenderer->damageWindow(PWINDOW); - else if (av.m_Context.eDamagePolicy == AVARDAMAGE_BORDER) { - const auto PDECO = PWINDOW->getDecorationByType(DECORATION_BORDER); - PDECO->damageEntire(); - } else if (av.m_Context.eDamagePolicy == AVARDAMAGE_SHADOW) { - const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW); - PDECO->damageEntire(); - } - - PMONITOR = PWINDOW->m_monitor.lock(); - if (!PMONITOR) - return; - - animationsDisabled = PWINDOW->m_ruleApplicator->noAnim().valueOr(animationsDisabled); - } else if (PWORKSPACE) { - PMONITOR = PWORKSPACE->m_monitor.lock(); - if (!PMONITOR) - return; - - // don't damage the whole monitor on workspace change, unless it's a special workspace, because dim/blur etc - if (PWORKSPACE->m_isSpecialWorkspace) - g_pHyprRenderer->damageMonitor(PMONITOR); - - // TODO: just make this into a damn callback already vax... - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_isMapped || w->isHidden() || w->m_workspace != PWORKSPACE) - continue; - - if (w->m_isFloating && !w->m_pinned) { - // still doing the full damage hack for floating because sometimes when the window - // goes through multiple monitors the last rendered frame is missing damage somehow?? - const CBox windowBoxNoOffset = w->getFullWindowBoundingBox(); - const CBox monitorBox = {PMONITOR->m_position, PMONITOR->m_size}; - if (windowBoxNoOffset.intersection(monitorBox) != windowBoxNoOffset) // on edges between multiple monitors - g_pHyprRenderer->damageWindow(w, true); - } - - if (PWORKSPACE->m_isSpecialWorkspace) - g_pHyprRenderer->damageWindow(w, true); // hack for special too because it can cross multiple monitors - } - - // damage any workspace window that is on any monitor - for (auto const& w : g_pCompositor->m_windows) { - if (!validMapped(w) || w->m_workspace != PWORKSPACE || w->m_pinned) - continue; - - g_pHyprRenderer->damageWindow(w); - } - } else if (PLAYER) { - // "some fucking layers miss 1 pixel???" -- vaxry - CBox expandBox = CBox{PLAYER->m_realPosition->value(), PLAYER->m_realSize->value()}; - expandBox.expand(5); - g_pHyprRenderer->damageBox(expandBox); - - PMONITOR = g_pCompositor->getMonitorFromVector(PLAYER->m_realPosition->goal() + PLAYER->m_realSize->goal() / 2.F); - if (!PMONITOR) - return; - animationsDisabled = animationsDisabled || PLAYER->m_ruleApplicator->noanim().valueOrDefault(); - } - - const auto SPENT = av.getPercent(); - const auto PBEZIER = g_pAnimationManager->getBezier(av.getBezierName()); - const auto POINTY = PBEZIER->getYForPoint(SPENT); - const bool WARP = animationsDisabled || SPENT >= 1.f; - - if constexpr (std::same_as) - updateColorVariable(av, POINTY, WARP); - else - updateVariable(av, POINTY, WARP); - - av.onUpdate(); - - switch (av.m_Context.eDamagePolicy) { - case AVARDAMAGE_ENTIRE: { - if (PWINDOW) { - PWINDOW->updateWindowDecos(); - g_pHyprRenderer->damageWindow(PWINDOW); - } else if (PWORKSPACE) { - for (auto const& w : g_pCompositor->m_windows) { - if (!validMapped(w) || w->m_workspace != PWORKSPACE) - continue; - - w->updateWindowDecos(); - - // damage any workspace window that is on any monitor - if (!w->m_pinned) - g_pHyprRenderer->damageWindow(w); - } - } else if (PLAYER) { - if (PLAYER->m_layer <= 1) - g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); - - // some fucking layers miss 1 pixel??? - CBox expandBox = CBox{PLAYER->m_realPosition->value(), PLAYER->m_realSize->value()}; - expandBox.expand(5); - g_pHyprRenderer->damageBox(expandBox); - } - break; - } - case AVARDAMAGE_BORDER: { - RASSERT(PWINDOW, "Tried to AVARDAMAGE_BORDER a non-window AVAR!"); - - const auto PDECO = PWINDOW->getDecorationByType(DECORATION_BORDER); - PDECO->damageEntire(); - - break; - } - case AVARDAMAGE_SHADOW: { - RASSERT(PWINDOW, "Tried to AVARDAMAGE_SHADOW a non-window AVAR!"); - - const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW); - - PDECO->damageEntire(); - - break; - } - default: { - break; - } - } - - // manually schedule a frame - if (PMONITOR && !PMONITOR->inFullscreenMode()) - g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_ANIMATION); -} - -void CHyprAnimationManager::tick() { - static std::chrono::time_point lastTick = std::chrono::high_resolution_clock::now(); - m_lastTickTimeMs = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - lastTick).count() / 1000.0; - lastTick = std::chrono::high_resolution_clock::now(); - - static auto PANIMENABLED = CConfigValue("animations:enabled"); - - if (!m_vActiveAnimatedVariables.empty()) { - const auto CPY = m_vActiveAnimatedVariables; - - for (const auto& PAV : CPY) { - if (!PAV) - continue; - - // lock this value while we are doing handleUpdate to avoid a UAF if an update callback destroys it - const auto LOCK = PAV.lock(); - - // for disabled anims just warp - bool warp = !*PANIMENABLED || !PAV->enabled(); - - switch (PAV->m_Type) { - case AVARTYPE_FLOAT: { - auto pTypedAV = dc*>(PAV.get()); - RASSERT(pTypedAV, "Failed to upcast animated float"); - handleUpdate(*pTypedAV, warp); - } break; - case AVARTYPE_VECTOR: { - auto pTypedAV = dc*>(PAV.get()); - RASSERT(pTypedAV, "Failed to upcast animated Vector2D"); - handleUpdate(*pTypedAV, warp); - } break; - case AVARTYPE_COLOR: { - auto pTypedAV = dc*>(PAV.get()); - RASSERT(pTypedAV, "Failed to upcast animated CHyprColor"); - handleUpdate(*pTypedAV, warp); - } break; - default: UNREACHABLE(); - } - } - } - - tickDone(); -} - -void CHyprAnimationManager::frameTick() { - onTicked(); - - if (!shouldTickForNext()) - return; - - if UNLIKELY (!g_pCompositor->m_sessionActive || g_pCompositor->m_unsafeState || - !std::ranges::any_of(g_pCompositor->m_monitors, [](const auto& mon) { return mon->m_enabled && mon->m_output; })) - return; - - if (!m_lastTickValid || m_lastTickTimer.getMillis() >= 1.0f) { - m_lastTickTimer.reset(); - m_lastTickValid = true; - - tick(); - Event::bus()->m_events.tick.emit(); - } - - if (shouldTickForNext()) - scheduleTick(); -} - -void CHyprAnimationManager::scheduleTick() { - if (m_tickScheduled) - return; - - m_tickScheduled = true; - - if (!m_animationTimer || !g_pEventLoopManager) { - m_tickScheduled = false; - return; - } - - m_animationTimer->updateTimeout(std::chrono::milliseconds(1)); -} - -void CHyprAnimationManager::onTicked() { - m_tickScheduled = false; -} - -void CHyprAnimationManager::resetTickState() { - m_lastTickValid = false; - m_tickScheduled = false; -} - -std::string CHyprAnimationManager::styleValidInConfigVar(const std::string& config, const std::string& style) { - if (config.starts_with("window")) { - if (style.starts_with("slide") || style == "gnome" || style == "gnomed") - return ""; - else if (style.starts_with("popin")) { - // try parsing - float minPerc = 0.f; - if (style.find('%') != std::string::npos) { - try { - auto percstr = style.substr(style.find_last_of(' ')); - minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); - } catch (std::exception& e) { return "invalid minperc"; } - - return ""; - } - - minPerc; // fix warning - - return ""; - } - - return "unknown style"; - } else if (config.starts_with("workspaces") || config.starts_with("specialWorkspace")) { - if (style == "slide" || style == "slidevert" || style == "fade") - return ""; - else if (style.starts_with("slide")) { - // try parsing - float movePerc = 0.f; - if (style.find('%') != std::string::npos) { - try { - auto percstr = style.substr(style.find_last_of(' ') + 1); - movePerc = std::stoi(percstr.substr(0, percstr.length() - 1)); - } catch (std::exception& e) { return "invalid movePerc"; } - - return ""; - } - - movePerc; // fix warning - - return ""; - } - - return "unknown style"; - } else if (config == "borderangle") { - if (style == "loop" || style == "once") - return ""; - return "unknown style"; - } else if (config.starts_with("layers")) { - if (style.empty() || style == "fade" || style == "slide") - return ""; - else if (style.starts_with("popin")) { - // try parsing - float minPerc = 0.f; - if (style.find('%') != std::string::npos) { - try { - auto percstr = style.substr(style.find_last_of(' ')); - minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); - } catch (std::exception& e) { return "invalid minperc"; } - - return ""; - } - - minPerc; // fix warning - - return ""; - } - return ""; - return "unknown style"; - } else { - return "animation has no styles"; - } - - return ""; -} diff --git a/src/managers/animation/DesktopAnimationManager.cpp b/src/managers/animation/DesktopAnimationManager.cpp deleted file mode 100644 index 2c450add..00000000 --- a/src/managers/animation/DesktopAnimationManager.cpp +++ /dev/null @@ -1,517 +0,0 @@ -#include "DesktopAnimationManager.hpp" - -#include - -#include "../../desktop/view/LayerSurface.hpp" -#include "../../desktop/view/Window.hpp" -#include "../../desktop/view/Group.hpp" -#include "../../desktop/Workspace.hpp" - -#include "../../config/ConfigManager.hpp" -#include "../../Compositor.hpp" -#include "desktop/DesktopTypes.hpp" -#include "wlr-layer-shell-unstable-v1.hpp" - -void CDesktopAnimationManager::startAnimation(PHLWINDOW pWindow, eAnimationType type, bool force) { - const bool CLOSE = type == ANIMATION_TYPE_OUT; - - if (CLOSE) - *pWindow->m_alpha = 0.F; - else { - pWindow->m_alpha->setValueAndWarp(0.F); - *pWindow->m_alpha = 1.F; - } - - if (!CLOSE) { - pWindow->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn")); - pWindow->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsIn")); - pWindow->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn")); - } else { - pWindow->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut")); - pWindow->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("windowsOut")); - pWindow->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeOut")); - } - - std::string ANIMSTYLE = pWindow->m_realPosition->getStyle(); - std::ranges::transform(ANIMSTYLE, ANIMSTYLE.begin(), ::tolower); - - CVarList animList(ANIMSTYLE, 0, 's'); - - // if the window is not being animated, that means the layout set a fixed size for it, don't animate. - if (!pWindow->m_realPosition->isBeingAnimated() && !pWindow->m_realSize->isBeingAnimated() && !force) - return; - - // if the animation is disabled and we are leaving, ignore the anim to prevent the snapshot being fucked - if (!pWindow->m_realPosition->enabled() && !force) - return; - - if (pWindow->m_ruleApplicator->animationStyle().hasValue()) { - const auto STYLE = pWindow->m_ruleApplicator->animationStyle().value(); - // the window has config'd special anim - if (STYLE.starts_with("slide")) { - CVarList animList2(STYLE, 0, 's'); - animationSlide(pWindow, animList2[1], CLOSE); - } else if (STYLE == "gnomed" || STYLE == "gnome") - animationGnomed(pWindow, CLOSE); - else { - // anim popin, fallback - - float minPerc = 0.f; - if (STYLE.find("%") != std::string::npos) { - try { - auto percstr = STYLE.substr(STYLE.find_last_of(' ')); - minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); - } catch (std::exception& e) { - ; // oops - } - } - - animationPopin(pWindow, CLOSE, minPerc / 100.f); - } - } else { - if (animList[0] == "slide") - animationSlide(pWindow, animList[1], CLOSE); - else if (animList[0] == "gnomed" || animList[0] == "gnome") - animationGnomed(pWindow, CLOSE); - else { - // anim popin, fallback - - float minPerc = 0.f; - if (!ANIMSTYLE.starts_with("%")) { - try { - auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ')); - minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); - } catch (std::exception& e) { - ; // oops - } - } - - animationPopin(pWindow, CLOSE, minPerc / 100.f); - } - } -} - -void CDesktopAnimationManager::startAnimation(PHLLS ls, eAnimationType type, bool instant) { - const bool IN = type == ANIMATION_TYPE_IN; - - if (IN) { - ls->m_alpha->setValueAndWarp(0.F); - *ls->m_alpha = 1.F; - } else - *ls->m_alpha = 0.F; - - if (IN) { - ls->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn")); - ls->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersIn")); - ls->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn")); - } else { - ls->m_realPosition->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut")); - ls->m_realSize->setConfig(g_pConfigManager->getAnimationPropertyConfig("layersOut")); - ls->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeLayersOut")); - } - - const auto ANIMSTYLE = ls->m_ruleApplicator->animationStyle().valueOr(ls->m_realPosition->getStyle()); - if (ANIMSTYLE.starts_with("slide")) { - // get closest edge - const auto MIDDLE = ls->m_geometry.middle(); - - const auto PMONITOR = g_pCompositor->getMonitorFromVector(MIDDLE); - - if (!PMONITOR) { // can rarely happen on exit - ls->m_alpha->setValueAndWarp(IN ? 1.F : 0.F); - return; - } - - int force = -1; - - CVarList args(ANIMSTYLE, 0, 's'); - if (args.size() > 1) { - const auto ARG2 = args[1]; - if (ARG2 == "top") - force = 0; - else if (ARG2 == "bottom") - force = 1; - else if (ARG2 == "left") - force = 2; - else if (ARG2 == "right") - force = 3; - } - - const std::array 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}, - }; - - float closest = std::numeric_limits::max(); - int leader = force; - if (leader == -1) { - for (size_t i = 0; i < 4; ++i) { - float dist = MIDDLE.distance(edgePoints[i]); - if (dist < closest) { - leader = i; - closest = dist; - } - } - } - - ls->m_realSize->setValueAndWarp(ls->m_geometry.size()); - - Vector2D prePos; - - switch (leader) { - case 0: - // TOP - prePos = {ls->m_geometry.x, PMONITOR->m_position.y - ls->m_geometry.h}; - break; - case 1: - // BOTTOM - prePos = {ls->m_geometry.x, PMONITOR->m_position.y + PMONITOR->m_size.y}; - break; - case 2: - // LEFT - prePos = {PMONITOR->m_position.x - ls->m_geometry.w, ls->m_geometry.y}; - break; - case 3: - // RIGHT - prePos = {PMONITOR->m_position.x + PMONITOR->m_size.x, ls->m_geometry.y}; - break; - default: UNREACHABLE(); - } - - if (IN) { - ls->m_realPosition->setValueAndWarp(prePos); - *ls->m_realPosition = ls->m_geometry.pos(); - } else { - ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos()); - *ls->m_realPosition = prePos; - } - - } else if (ANIMSTYLE.starts_with("popin")) { - float minPerc = 0.f; - if (ANIMSTYLE.find("%") != std::string::npos) { - try { - auto percstr = ANIMSTYLE.substr(ANIMSTYLE.find_last_of(' ')); - minPerc = std::stoi(percstr.substr(0, percstr.length() - 1)); - } catch (std::exception& e) { - ; // oops - } - } - - minPerc *= 0.01; - - const auto GOALSIZE = (ls->m_geometry.size() * minPerc).clamp({5, 5}); - const auto GOALPOS = ls->m_geometry.pos() + (ls->m_geometry.size() - GOALSIZE) / 2.f; - - ls->m_alpha->setValueAndWarp(IN ? 0.f : 1.f); - *ls->m_alpha = IN ? 1.f : 0.f; - - if (IN) { - ls->m_realSize->setValueAndWarp(GOALSIZE); - ls->m_realPosition->setValueAndWarp(GOALPOS); - *ls->m_realSize = ls->m_geometry.size(); - *ls->m_realPosition = ls->m_geometry.pos(); - } else { - ls->m_realSize->setValueAndWarp(ls->m_geometry.size()); - ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos()); - *ls->m_realSize = GOALSIZE; - *ls->m_realPosition = GOALPOS; - } - } else { - // fade - ls->m_realPosition->setValueAndWarp(ls->m_geometry.pos()); - ls->m_realSize->setValueAndWarp(ls->m_geometry.size()); - *ls->m_alpha = IN ? 1.f : 0.f; - } - - if (instant) { - ls->m_realPosition->warp(); - ls->m_realSize->warp(); - ls->m_alpha->warp(); - } -} - -void CDesktopAnimationManager::startAnimation(PHLWORKSPACE ws, eAnimationType type, bool left, bool instant) { - const bool IN = type == ANIMATION_TYPE_IN; - - if (!instant) { - const std::string ANIMNAME = std::format("{}{}", ws->m_isSpecialWorkspace ? "specialWorkspace" : "workspaces", IN ? "In" : "Out"); - - ws->m_alpha->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME)); - ws->m_renderOffset->setConfig(g_pConfigManager->getAnimationPropertyConfig(ANIMNAME)); - } - static auto PWORKSPACEGAP = CConfigValue("general:gaps_workspaces"); - const auto PMONITOR = ws->m_monitor.lock(); - const auto ANIMSTYLE = ws->m_alpha->getStyle(); - float movePerc = 100.f; - // inverted for some reason. TODO: fix the cause - bool vert = ANIMSTYLE.starts_with("slidevert") || ANIMSTYLE.starts_with("slidefadevert"); - - // set floating windows offset callbacks - ws->m_renderOffset->setUpdateCallback([weak = PHLWORKSPACEREF{ws}](auto) { - if (!weak) - return; - - for (auto const& w : g_pCompositor->m_windows) { - if (!validMapped(w) || w->workspaceID() != weak->m_id) - continue; - - w->onWorkspaceAnimUpdate(); - }; - }); - - CVarList args(ANIMSTYLE, 0, 's'); - if (args.size() > 1) { - const auto ARG2 = args[1]; - if (ARG2 == "top") { - left = false; - vert = true; - } else if (ARG2 == "bottom") { - left = true; - vert = true; - } else if (ARG2 == "left") { - left = false; - vert = false; - } else if (ARG2 == "right") { - left = true; - vert = false; - } - } - - const auto percstr = args[args.size() - 1]; - if (percstr.ends_with('%')) { - try { - movePerc = std::stoi(percstr.substr(0, percstr.length() - 1)); - } catch (std::exception& e) { Log::logger->log(Log::ERR, "Error in startAnim: invalid percentage"); } - } - - if (ANIMSTYLE.starts_with("slidefade")) { - - ws->m_alpha->setValueAndWarp(1.f); - ws->m_renderOffset->setValueAndWarp(Vector2D(0, 0)); - - if (vert) { - if (IN) { - ws->m_alpha->setValueAndWarp(0.f); - ws->m_renderOffset->setValueAndWarp(Vector2D(0.0, (left ? PMONITOR->m_size.y : -PMONITOR->m_size.y) * (movePerc / 100.f))); - *ws->m_alpha = 1.f; - *ws->m_renderOffset = Vector2D(0, 0); - } else { - ws->m_alpha->setValueAndWarp(1.f); - *ws->m_alpha = 0.f; - *ws->m_renderOffset = Vector2D(0.0, (left ? -PMONITOR->m_size.y : PMONITOR->m_size.y) * (movePerc / 100.f)); - } - } else { - if (IN) { - ws->m_alpha->setValueAndWarp(0.f); - ws->m_renderOffset->setValueAndWarp(Vector2D((left ? PMONITOR->m_size.x : -PMONITOR->m_size.x) * (movePerc / 100.f), 0.0)); - *ws->m_alpha = 1.f; - *ws->m_renderOffset = Vector2D(0, 0); - } else { - ws->m_alpha->setValueAndWarp(1.f); - *ws->m_alpha = 0.f; - *ws->m_renderOffset = Vector2D((left ? -PMONITOR->m_size.x : PMONITOR->m_size.x) * (movePerc / 100.f), 0.0); - } - } - } else if (ANIMSTYLE == "fade") { - ws->m_renderOffset->setValueAndWarp(Vector2D(0, 0)); // fix a bug, if switching from slide -> fade. - - if (IN) { - ws->m_alpha->setValueAndWarp(0.f); - *ws->m_alpha = 1.f; - } else { - ws->m_alpha->setValueAndWarp(1.f); - *ws->m_alpha = 0.f; - } - } else if (vert) { - const auto YDISTANCE = (PMONITOR->m_size.y + *PWORKSPACEGAP) * (movePerc / 100.f); - ws->m_alpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. - - if (IN) { - ws->m_renderOffset->setValueAndWarp(Vector2D(0.0, left ? YDISTANCE : -YDISTANCE)); - *ws->m_renderOffset = Vector2D(0, 0); - } else { - *ws->m_renderOffset = Vector2D(0.0, left ? -YDISTANCE : YDISTANCE); - } - - } else { - // fallback is slide - const auto XDISTANCE = (PMONITOR->m_size.x + *PWORKSPACEGAP) * (movePerc / 100.f); - ws->m_alpha->setValueAndWarp(1.f); // fix a bug, if switching from fade -> slide. - - if (IN) { - ws->m_renderOffset->setValueAndWarp(Vector2D(left ? XDISTANCE : -XDISTANCE, 0.0)); - *ws->m_renderOffset = Vector2D(0, 0); - } else { - *ws->m_renderOffset = Vector2D(left ? -XDISTANCE : XDISTANCE, 0.0); - } - } - - if (ws->m_isSpecialWorkspace) { - // required for open/close animations - if (IN) { - ws->m_alpha->setValueAndWarp(0.f); - *ws->m_alpha = 1.f; - } else { - ws->m_alpha->setValueAndWarp(1.f); - *ws->m_alpha = 0.f; - } - } - - if (instant) { - ws->m_renderOffset->warp(); - ws->m_alpha->warp(); - } -} - -void CDesktopAnimationManager::animationPopin(PHLWINDOW pWindow, bool close, float minPerc) { - const auto GOALPOS = pWindow->m_realPosition->goal(); - const auto GOALSIZE = pWindow->m_realSize->goal(); - - if (!close) { - pWindow->m_realSize->setValue((GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y})); - pWindow->m_realPosition->setValue(GOALPOS + GOALSIZE / 2.f - pWindow->m_realSize->value() / 2.f); - } else { - *pWindow->m_realSize = (GOALSIZE * minPerc).clamp({5, 5}, {GOALSIZE.x, GOALSIZE.y}); - *pWindow->m_realPosition = GOALPOS + GOALSIZE / 2.f - pWindow->m_realSize->goal() / 2.f; - } -} - -void CDesktopAnimationManager::animationSlide(PHLWINDOW pWindow, std::string force, bool close) { - pWindow->m_realSize->warp(false); // size we preserve in slide - - const auto GOALPOS = pWindow->m_realPosition->goal(); - const auto GOALSIZE = pWindow->m_realSize->goal(); - - const auto PMONITOR = pWindow->m_monitor.lock(); - - if (!PMONITOR) - return; // unsafe state most likely - - Vector2D posOffset; - - if (!force.empty()) { - if (force == "bottom") - posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y + PMONITOR->m_size.y); - else if (force == "left") - posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0); - else if (force == "right") - posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0); - else - posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y - GOALSIZE.y); - - if (!close) - pWindow->m_realPosition->setValue(posOffset); - else - *pWindow->m_realPosition = posOffset; - - return; - } - - const auto MIDPOINT = GOALPOS + GOALSIZE / 2.f; - const auto MONBOX = PMONITOR->logicalBox(); - - // find the closest edge to midpoint - // CSS style, top right bottom left - std::array distances = { - MIDPOINT.y - MONBOX.y, // - MONBOX.x + MONBOX.w - MIDPOINT.x, // - MONBOX.y + MONBOX.h - MIDPOINT.y, // - MIDPOINT.x - MONBOX.x, // - }; - - const auto MIN_DIST = std::min({distances[0], distances[1], distances[2], distances[3]}); - if (MIN_DIST == distances[2]) - posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y + PMONITOR->m_size.y); - else if (MIN_DIST == distances[3]) - posOffset = GOALPOS - Vector2D(GOALSIZE.x, 0.0); - else if (MIN_DIST == distances[1]) - posOffset = GOALPOS + Vector2D(GOALSIZE.x, 0.0); - else - posOffset = Vector2D(GOALPOS.x, PMONITOR->m_position.y - GOALSIZE.y); - - if (!close) - pWindow->m_realPosition->setValue(posOffset); - else - *pWindow->m_realPosition = posOffset; -} - -void CDesktopAnimationManager::animationGnomed(PHLWINDOW pWindow, bool close) { - const auto GOALPOS = pWindow->m_realPosition->goal(); - const auto GOALSIZE = pWindow->m_realSize->goal(); - - if (close) { - *pWindow->m_realPosition = GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F}; - *pWindow->m_realSize = Vector2D{GOALSIZE.x, 0.F}; - } else { - pWindow->m_realPosition->setValueAndWarp(GOALPOS + Vector2D{0.F, GOALSIZE.y / 2.F}); - pWindow->m_realSize->setValueAndWarp(Vector2D{GOALSIZE.x, 0.F}); - *pWindow->m_realPosition = GOALPOS; - *pWindow->m_realSize = GOALSIZE; - } -} - -void CDesktopAnimationManager::setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnimationType type) { - if (!ws) - return; - - const auto FULLSCREEN = type == ANIMATION_TYPE_IN; - - const auto FSWINDOW = ws->getFullscreenWindow(); - - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace == ws) { - - if (w->m_fadingOut || w->m_pinned || w->isFullscreen()) - continue; - - if (!FULLSCREEN) - *w->m_alpha = 1.F; - else if (!w->isFullscreen()) { - const bool CREATED_OVER_FS = w->m_createdOverFullscreen; - const bool IS_IN_GROUP_OF_FS = FSWINDOW && FSWINDOW->m_group && FSWINDOW->m_group->has(w); - *w->m_alpha = !CREATED_OVER_FS && !IS_IN_GROUP_OF_FS ? 0.f : 1.f; - } - } - } - - const auto PMONITOR = ws->m_monitor.lock(); - - if (ws->m_id == PMONITOR->activeWorkspaceID() || ws->m_id == PMONITOR->activeSpecialWorkspaceID()) { - for (auto const& ls : PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { - if (!ls->m_fadingOut && !ls->m_aboveFullscreen) - *ls->m_alpha = FULLSCREEN && ws->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f; - } - } -} - -void CDesktopAnimationManager::setFullscreenFloatingFade(PHLWINDOW pWindow, float fade) { - if (pWindow->m_fadingOut || !pWindow->m_isFloating) - return; - - *pWindow->m_alpha = fade; -} - -void CDesktopAnimationManager::overrideFullscreenFadeAmount(PHLWORKSPACE ws, float fade, PHLWINDOW exclude) { - for (auto const& w : g_pCompositor->m_windows) { - if (w == exclude) - continue; - - if (w->m_workspace == ws) { - if (w->m_fadingOut || w->m_pinned || w->isFullscreen()) - continue; - - *w->m_alpha = fade; - } - } - - const auto PMONITOR = ws->m_monitor.lock(); - - if (ws->m_id == PMONITOR->activeWorkspaceID() || ws->m_id == PMONITOR->activeSpecialWorkspaceID()) { - for (auto const& ls : PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { - if (!ls->m_fadingOut) - *ls->m_alpha = fade; - } - } -} diff --git a/src/managers/animation/DesktopAnimationManager.hpp b/src/managers/animation/DesktopAnimationManager.hpp deleted file mode 100644 index fa86425e..00000000 --- a/src/managers/animation/DesktopAnimationManager.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "../../helpers/memory/Memory.hpp" -#include "../../desktop/DesktopTypes.hpp" -#include - -class CDesktopAnimationManager { - public: - enum eAnimationType : uint8_t { - ANIMATION_TYPE_IN = 0, - ANIMATION_TYPE_OUT, - }; - - void startAnimation(PHLWINDOW w, eAnimationType type, bool force = false); - void startAnimation(PHLLS ls, eAnimationType type, bool instant = false); - void startAnimation(PHLWORKSPACE ws, eAnimationType type, bool left = true, bool instant = false); - - void setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnimationType type); - void setFullscreenFloatingFade(PHLWINDOW pWindow, float fade); - void overrideFullscreenFadeAmount(PHLWORKSPACE ws, float fade, PHLWINDOW exclude = nullptr); - - private: - void animationPopin(PHLWINDOW w, bool close = false, float minPerc = 0.f); - void animationSlide(PHLWINDOW w, std::string force = "", bool close = false); - void animationGnomed(PHLWINDOW w, bool close = false); -}; - -inline UP g_pDesktopAnimationManager = makeUnique(); diff --git a/src/managers/cursor/CursorShapeOverrideController.cpp b/src/managers/cursor/CursorShapeOverrideController.cpp deleted file mode 100644 index 45064277..00000000 --- a/src/managers/cursor/CursorShapeOverrideController.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "CursorShapeOverrideController.hpp" - -#include - -using namespace Cursor; - -void CShapeOverrideController::setOverride(const std::string& name, eCursorShapeOverrideGroup group) { - if (m_overrides[group] == name) - return; - - m_overrides[group] = name; - - recheckOverridesResendIfChanged(); -} - -void CShapeOverrideController::unsetOverride(eCursorShapeOverrideGroup group) { - if (m_overrides[group].empty()) - return; - - m_overrides[group] = ""; - - recheckOverridesResendIfChanged(); -} - -void CShapeOverrideController::recheckOverridesResendIfChanged() { - for (const auto& s : m_overrides | std::views::reverse) { - if (s.empty()) - continue; - - if (s == m_overrideShape) - return; - - m_overrideShape = s; - m_events.overrideChanged.emit(s); - return; - } - - if (m_overrideShape.empty()) - return; - - m_overrideShape = ""; - m_events.overrideChanged.emit(""); -} diff --git a/src/managers/cursor/CursorShapeOverrideController.hpp b/src/managers/cursor/CursorShapeOverrideController.hpp deleted file mode 100644 index eca2ccf7..00000000 --- a/src/managers/cursor/CursorShapeOverrideController.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "../../helpers/memory/Memory.hpp" -#include "../../helpers/signal/Signal.hpp" - -#include -#include - -namespace Cursor { - enum eCursorShapeOverrideGroup : uint8_t { - // unknown group - lowest priority - CURSOR_OVERRIDE_UNKNOWN = 0, - // window edges for resizing from edge - CURSOR_OVERRIDE_WINDOW_EDGE, - // Drag and drop - CURSOR_OVERRIDE_DND, - // special action: Interactive::CDrag, kill, etc. - CURSOR_OVERRIDE_SPECIAL_ACTION, - - // - CURSOR_OVERRIDE_END, - }; - - class CShapeOverrideController { - public: - CShapeOverrideController() = default; - ~CShapeOverrideController() = default; - - CShapeOverrideController(const CShapeOverrideController&) = delete; - CShapeOverrideController(CShapeOverrideController&) = delete; - CShapeOverrideController(CShapeOverrideController&&) = delete; - - void setOverride(const std::string& name, eCursorShapeOverrideGroup group); - void unsetOverride(eCursorShapeOverrideGroup group); - - struct { - // if string is empty, override was cleared - CSignalT overrideChanged; - } m_events; - - private: - void recheckOverridesResendIfChanged(); - - std::array m_overrides; - std::string m_overrideShape; - }; - - inline UP overrideController = makeUnique(); -}; \ No newline at end of file diff --git a/src/managers/eventLoop/EventLoopManager.cpp b/src/managers/eventLoop/EventLoopManager.cpp index e38474aa..83bdf4a0 100644 --- a/src/managers/eventLoop/EventLoopManager.cpp +++ b/src/managers/eventLoop/EventLoopManager.cpp @@ -1,5 +1,5 @@ #include "EventLoopManager.hpp" -#include "../../debug/log/Logger.hpp" +#include "../../debug/Log.hpp" #include "../../Compositor.hpp" #include "../../config/ConfigWatcher.hpp" @@ -16,42 +16,33 @@ using namespace Hyprutils::OS; #define TIMESPEC_NSEC_PER_SEC 1000000000L CEventLoopManager::CEventLoopManager(wl_display* display, wl_event_loop* wlEventLoop) { - m_timers.timerfd = CFileDescriptor{timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)}; - m_wayland.loop = wlEventLoop; - m_wayland.display = display; + m_sTimers.timerfd = CFileDescriptor{timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)}; + m_sWayland.loop = wlEventLoop; + m_sWayland.display = display; } CEventLoopManager::~CEventLoopManager() { - for (auto const& [_, eventSourceData] : m_aqEventSources) { + for (auto const& [_, eventSourceData] : aqEventSources) { wl_event_source_remove(eventSourceData.eventSource); } - m_readableWaiters.clear(); - - if (m_wayland.eventSource) - wl_event_source_remove(m_wayland.eventSource); - if (m_idle.eventSource) - wl_event_source_remove(m_idle.eventSource); + if (m_sWayland.eventSource) + wl_event_source_remove(m_sWayland.eventSource); + if (m_sIdle.eventSource) + wl_event_source_remove(m_sIdle.eventSource); if (m_configWatcherInotifySource) wl_event_source_remove(m_configWatcherInotifySource); } static int timerWrite(int fd, uint32_t mask, void* data) { - if (!CFileDescriptor::isReadable(fd)) - Log::logger->log(Log::ERR, "timerWrite: triggered a non readable event on fd : {}", fd); - else { - uint64_t expirations; - read(fd, &expirations, sizeof(expirations)); - } - g_pEventLoopManager->onTimerFire(); - return 0; + return 1; } static int aquamarineFDWrite(int fd, uint32_t mask, void* data) { - auto POLLFD = sc(data); + auto POLLFD = (Aquamarine::SPollFD*)data; POLLFD->onSignal(); - return 0; + return 1; } static int configWatcherWrite(int fd, uint32_t mask, void* data) { @@ -59,95 +50,41 @@ static int configWatcherWrite(int fd, uint32_t mask, void* data) { return 0; } -static int handleWaiterFD(int fd, uint32_t mask, void* data) { - auto waiter = sc(data); - - if (!waiter) { - Log::logger->log(Log::ERR, "handleWaiterFD: failed casting waiter"); - return 0; - } - - if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { - Log::logger->log(Log::ERR, "handleWaiterFD: readable waiter error"); - g_pEventLoopManager->onFdReadableFail(waiter); - return 0; - } - - if (mask & WL_EVENT_READABLE) - g_pEventLoopManager->onFdReadable(waiter); - - return 0; -} - -void CEventLoopManager::onFdReadable(SReadableWaiter* waiter) { - auto it = std::ranges::find_if(m_readableWaiters, [waiter](const UP& w) { return waiter == w.get() && w->fd == waiter->fd && w->source == waiter->source; }); - - // ??? - if (it == m_readableWaiters.end()) - return; - - if (waiter->source) { // remove even_source if fn() somehow causes a reentry - wl_event_source_remove(waiter->source); - waiter->source = nullptr; - } - - UP taken = std::move(*it); - m_readableWaiters.erase(it); - - if (taken->fn) - taken->fn(); -} - -void CEventLoopManager::onFdReadableFail(SReadableWaiter* waiter) { - auto it = std::ranges::find_if(m_readableWaiters, [waiter](const UP& w) { return waiter == w.get() && w->fd == waiter->fd && w->source == waiter->source; }); - - // ??? - if (it == m_readableWaiters.end()) - return; - - m_readableWaiters.erase(it); -} - void CEventLoopManager::enterLoop() { - m_wayland.eventSource = wl_event_loop_add_fd(m_wayland.loop, m_timers.timerfd.get(), WL_EVENT_READABLE, timerWrite, nullptr); + m_sWayland.eventSource = wl_event_loop_add_fd(m_sWayland.loop, m_sTimers.timerfd.get(), WL_EVENT_READABLE, timerWrite, nullptr); if (const auto& FD = g_pConfigWatcher->getInotifyFD(); FD.isValid()) - m_configWatcherInotifySource = wl_event_loop_add_fd(m_wayland.loop, FD.get(), WL_EVENT_READABLE, configWatcherWrite, nullptr); + m_configWatcherInotifySource = wl_event_loop_add_fd(m_sWayland.loop, FD.get(), WL_EVENT_READABLE, configWatcherWrite, nullptr); syncPollFDs(); - m_listeners.pollFDsChanged = g_pCompositor->m_aqBackend->events.pollFDsChanged.listen([this] { syncPollFDs(); }); + m_sListeners.pollFDsChanged = g_pCompositor->m_pAqBackend->events.pollFDsChanged.registerListener([this](std::any d) { syncPollFDs(); }); // if we have a session, dispatch it to get the pending input devices - if (g_pCompositor->m_aqBackend->hasSession()) - g_pCompositor->m_aqBackend->session->dispatchPendingEventsAsync(); + if (g_pCompositor->m_pAqBackend->hasSession()) + g_pCompositor->m_pAqBackend->session->dispatchPendingEventsAsync(); - wl_display_run(m_wayland.display); + wl_display_run(m_sWayland.display); - Log::logger->log(Log::DEBUG, "Kicked off the event loop! :("); + Debug::log(LOG, "Kicked off the event loop! :("); } void CEventLoopManager::onTimerFire() { - const auto CPY = m_timers.timers; - for (auto const& t : CPY) { - if (t.strongRef() > 2 /* if it's 2, it was lost. Don't call it. */ && t->passed() && !t->cancelled()) + for (auto const& t : m_sTimers.timers) { + if (t.strongRef() > 1 /* if it's 1, it was lost. Don't call it. */ && t->passed() && !t->cancelled()) t->call(t); } - scheduleRecalc(); + nudgeTimers(); } void CEventLoopManager::addTimer(SP timer) { - if (std::ranges::contains(m_timers.timers, timer)) - return; - m_timers.timers.emplace_back(timer); - scheduleRecalc(); + m_sTimers.timers.push_back(timer); + nudgeTimers(); } void CEventLoopManager::removeTimer(SP timer) { - if (!std::ranges::contains(m_timers.timers, timer)) - return; - std::erase_if(m_timers.timers, [timer](const auto& t) { return timer == t; }); - scheduleRecalc(); + std::erase_if(m_sTimers.timers, [timer](const auto& t) { return timer == t; }); + nudgeTimers(); } static void timespecAddNs(timespec* pTimespec, int64_t delta) { @@ -156,34 +93,20 @@ static void timespecAddNs(timespec* pTimespec, int64_t delta) { pTimespec->tv_sec += delta_s_high; - pTimespec->tv_nsec += delta_ns_low; + pTimespec->tv_nsec += (long)delta_ns_low; if (pTimespec->tv_nsec >= TIMESPEC_NSEC_PER_SEC) { pTimespec->tv_nsec -= TIMESPEC_NSEC_PER_SEC; ++pTimespec->tv_sec; } } -void CEventLoopManager::scheduleRecalc() { - // do not do it instantly, do it later. Avoid recursive access to the timer - // vector, it could be catastrophic if we modify it while iterating - - if (m_timers.recalcScheduled) - return; - - m_timers.recalcScheduled = true; - - doLater([this] { nudgeTimers(); }); -} - void CEventLoopManager::nudgeTimers() { - m_timers.recalcScheduled = false; - // remove timers that have gone missing - std::erase_if(m_timers.timers, [](const auto& t) { return t.strongRef() <= 1; }); + std::erase_if(m_sTimers.timers, [](const auto& t) { return t.strongRef() <= 1; }); long nextTimerUs = 10L * 1000 * 1000; // 10s - for (auto const& t : m_timers.timers) { + for (auto const& t : m_sTimers.timers) { if (auto const& µs = t->leftUs(); µs < nextTimerUs) nextTimerUs = µs; } @@ -196,44 +119,34 @@ void CEventLoopManager::nudgeTimers() { itimerspec ts = {.it_value = now}; - timerfd_settime(m_timers.timerfd.get(), TFD_TIMER_ABSTIME, &ts, nullptr); + timerfd_settime(m_sTimers.timerfd.get(), TFD_TIMER_ABSTIME, &ts, nullptr); } void CEventLoopManager::doLater(const std::function& fn) { - m_idle.fns.emplace_back(fn); + m_sIdle.fns.emplace_back(fn); - if (m_idle.eventSource) + if (m_sIdle.eventSource) return; - m_idle.eventSource = wl_event_loop_add_idle( - m_wayland.loop, + m_sIdle.eventSource = wl_event_loop_add_idle( + m_sWayland.loop, [](void* data) { - auto IDLE = sc(data); - auto fns = std::move(IDLE->fns); + auto IDLE = (CEventLoopManager::SIdleData*)data; + auto cpy = IDLE->fns; IDLE->fns.clear(); IDLE->eventSource = nullptr; - for (auto& f : fns) { - if (f) - f(); + for (auto const& c : cpy) { + if (c) + c(); } }, - &m_idle); -} - -void CEventLoopManager::doOnReadable(CFileDescriptor fd, std::function&& fn) { - if (!fd.isValid() || fd.isReadable()) { - fn(); - return; - } - - auto& waiter = m_readableWaiters.emplace_back(makeUnique(nullptr, std::move(fd), std::move(fn))); - waiter->source = wl_event_loop_add_fd(g_pEventLoopManager->m_wayland.loop, waiter->fd.get(), WL_EVENT_READABLE, ::handleWaiterFD, waiter.get()); + &m_sIdle); } void CEventLoopManager::syncPollFDs() { - auto aqPollFDs = g_pCompositor->m_aqBackend->getPollFDs(); + auto aqPollFDs = g_pCompositor->m_pAqBackend->getPollFDs(); - std::erase_if(m_aqEventSources, [&](const auto& item) { + std::erase_if(aqEventSources, [&](const auto& item) { auto const& [fd, eventSourceData] = item; // If no pollFD has the same fd, remove this event source @@ -245,8 +158,8 @@ void CEventLoopManager::syncPollFDs() { return shouldRemove; }); - for (auto& fd : aqPollFDs | std::views::filter([&](SP fd) { return !m_aqEventSources.contains(fd->fd); })) { - auto eventSource = wl_event_loop_add_fd(m_wayland.loop, fd->fd, WL_EVENT_READABLE, aquamarineFDWrite, fd.get()); - m_aqEventSources[fd->fd] = {.pollFD = fd, .eventSource = eventSource}; + for (auto& fd : aqPollFDs | std::views::filter([&](SP fd) { return !aqEventSources.contains(fd->fd); })) { + auto eventSource = wl_event_loop_add_fd(m_sWayland.loop, fd->fd, WL_EVENT_READABLE, aquamarineFDWrite, fd.get()); + aqEventSources[fd->fd] = {.pollFD = fd, .eventSource = eventSource}; } } diff --git a/src/managers/eventLoop/EventLoopManager.hpp b/src/managers/eventLoop/EventLoopManager.hpp index 7999dc59..ebeb2160 100644 --- a/src/managers/eventLoop/EventLoopManager.hpp +++ b/src/managers/eventLoop/EventLoopManager.hpp @@ -5,7 +5,7 @@ #include #include #include -#include "../../helpers/signal/Signal.hpp" +#include "helpers/signal/Signal.hpp" #include #include "EventLoopTimer.hpp" @@ -27,8 +27,8 @@ class CEventLoopManager { void onTimerFire(); - // schedules a recalc of the timers - void scheduleRecalc(); + // recalculates timers + void nudgeTimers(); // schedules a function to run later, aka in a wayland idle event. void doLater(const std::function& fn); @@ -38,39 +38,9 @@ class CEventLoopManager { std::vector> fns; }; - struct SReadableWaiter { - wl_event_source* source; - Hyprutils::OS::CFileDescriptor fd; - std::function fn; - - SReadableWaiter(wl_event_source* src, Hyprutils::OS::CFileDescriptor f, std::function func) : source(src), fd(std::move(f)), fn(std::move(func)) {} - - ~SReadableWaiter() { - if (source) { - wl_event_source_remove(source); - source = nullptr; - } - } - - // copy - SReadableWaiter(const SReadableWaiter&) = delete; - SReadableWaiter& operator=(const SReadableWaiter&) = delete; - - // move - SReadableWaiter(SReadableWaiter&& other) noexcept = default; - SReadableWaiter& operator=(SReadableWaiter&& other) noexcept = default; - }; - - // schedule function to when fd is readable (WL_EVENT_READABLE / POLLIN), - // takes ownership of fd - void doOnReadable(Hyprutils::OS::CFileDescriptor fd, std::function&& fn); - void onFdReadable(SReadableWaiter* waiter); - void onFdReadableFail(SReadableWaiter* waiter); - private: // Manages the event sources after AQ pollFDs change. void syncPollFDs(); - void nudgeTimers(); struct SEventSourceData { SP pollFD; @@ -81,26 +51,23 @@ class CEventLoopManager { wl_event_loop* loop = nullptr; wl_display* display = nullptr; wl_event_source* eventSource = nullptr; - } m_wayland; + } m_sWayland; struct { std::vector> timers; Hyprutils::OS::CFileDescriptor timerfd; - bool recalcScheduled = false; - } m_timers; + } m_sTimers; - SIdleData m_idle; - std::map m_aqEventSources; - std::vector> m_readableWaiters; + SIdleData m_sIdle; + std::map aqEventSources; struct { CHyprSignalListener pollFDsChanged; - } m_listeners; + } m_sListeners; wl_event_source* m_configWatcherInotifySource = nullptr; - friend class CAsyncDialogBox; - friend class CMainLoopExecutor; + friend class CSyncTimeline; }; inline UP g_pEventLoopManager; diff --git a/src/managers/eventLoop/EventLoopTimer.cpp b/src/managers/eventLoop/EventLoopTimer.cpp index fa86c861..b8943b7c 100644 --- a/src/managers/eventLoop/EventLoopTimer.cpp +++ b/src/managers/eventLoop/EventLoopTimer.cpp @@ -1,55 +1,55 @@ #include "EventLoopTimer.hpp" #include #include "EventLoopManager.hpp" -#include "../../helpers/time/Time.hpp" -CEventLoopTimer::CEventLoopTimer(std::optional timeout, std::function self, void* data)> cb_, void* data_) : m_cb(cb_), m_data(data_) { +CEventLoopTimer::CEventLoopTimer(std::optional timeout, std::function self, void* data)> cb_, void* data_) : + cb(cb_), data(data_) { if (!timeout.has_value()) - m_expires.reset(); + expires.reset(); else - m_expires = Time::steadyNow() + *timeout; + expires = std::chrono::steady_clock::now() + *timeout; } -void CEventLoopTimer::updateTimeout(std::optional timeout) { +void CEventLoopTimer::updateTimeout(std::optional timeout) { if (!timeout.has_value()) { - m_expires.reset(); - g_pEventLoopManager->scheduleRecalc(); + expires.reset(); + g_pEventLoopManager->nudgeTimers(); return; } - m_expires = Time::steadyNow() + *timeout; + expires = std::chrono::steady_clock::now() + *timeout; - g_pEventLoopManager->scheduleRecalc(); + g_pEventLoopManager->nudgeTimers(); } bool CEventLoopTimer::passed() { - if (!m_expires.has_value()) + if (!expires.has_value()) return false; - return Time::steadyNow() > *m_expires; + return std::chrono::steady_clock::now() > *expires; } void CEventLoopTimer::cancel() { - m_wasCancelled = true; - m_expires.reset(); + wasCancelled = true; + expires.reset(); } bool CEventLoopTimer::cancelled() { - return m_wasCancelled; + return wasCancelled; } void CEventLoopTimer::call(SP self) { - m_expires.reset(); - m_cb(self, m_data); + expires.reset(); + cb(self, data); } float CEventLoopTimer::leftUs() { - if (!m_expires.has_value()) + if (!expires.has_value()) return std::numeric_limits::max(); - return std::chrono::duration_cast(*m_expires - Time::steadyNow()).count(); + return std::chrono::duration_cast(*expires - std::chrono::steady_clock::now()).count(); } bool CEventLoopTimer::armed() { - return m_expires.has_value(); + return expires.has_value(); } diff --git a/src/managers/eventLoop/EventLoopTimer.hpp b/src/managers/eventLoop/EventLoopTimer.hpp index d093cd6e..e5a8c5e3 100644 --- a/src/managers/eventLoop/EventLoopTimer.hpp +++ b/src/managers/eventLoop/EventLoopTimer.hpp @@ -5,15 +5,14 @@ #include #include "../../helpers/memory/Memory.hpp" -#include "../../helpers/time/Time.hpp" class CEventLoopTimer { public: - CEventLoopTimer(std::optional timeout, std::function self, void* data)> cb_, void* data_); + CEventLoopTimer(std::optional timeout, std::function self, void* data)> cb_, void* data_); // if not specified, disarms. // if specified, arms. - void updateTimeout(std::optional timeout); + void updateTimeout(std::optional timeout); void cancel(); bool passed(); @@ -26,8 +25,8 @@ class CEventLoopTimer { void call(SP self); private: - std::function self, void* data)> m_cb; - void* m_data = nullptr; - std::optional m_expires; - bool m_wasCancelled = false; + std::function self, void* data)> cb; + void* data = nullptr; + std::optional expires; + bool wasCancelled = false; }; diff --git a/src/managers/input/IdleInhibitor.cpp b/src/managers/input/IdleInhibitor.cpp index 02eefc1d..67837c17 100644 --- a/src/managers/input/IdleInhibitor.cpp +++ b/src/managers/input/IdleInhibitor.cpp @@ -5,52 +5,52 @@ #include "../../protocols/core/Compositor.hpp" void CInputManager::newIdleInhibitor(std::any inhibitor) { - const auto PINHIBIT = m_idleInhibitors.emplace_back(makeUnique()).get(); + const auto PINHIBIT = m_vIdleInhibitors.emplace_back(makeUnique()).get(); PINHIBIT->inhibitor = std::any_cast>(inhibitor); - Log::logger->log(Log::DEBUG, "New idle inhibitor registered for surface {:x}", rc(PINHIBIT->inhibitor->m_surface.get())); + Debug::log(LOG, "New idle inhibitor registered for surface {:x}", (uintptr_t)PINHIBIT->inhibitor->surface.get()); - PINHIBIT->inhibitor->m_listeners.destroy = PINHIBIT->inhibitor->m_resource->m_events.destroy.listen([this, PINHIBIT] { - std::erase_if(m_idleInhibitors, [PINHIBIT](const auto& other) { return other.get() == PINHIBIT; }); + PINHIBIT->inhibitor->listeners.destroy = PINHIBIT->inhibitor->resource->events.destroy.registerListener([this, PINHIBIT](std::any data) { + std::erase_if(m_vIdleInhibitors, [PINHIBIT](const auto& other) { return other.get() == PINHIBIT; }); recheckIdleInhibitorStatus(); }); - auto WLSurface = Desktop::View::CWLSurface::fromResource(PINHIBIT->inhibitor->m_surface.lock()); + auto WLSurface = CWLSurface::fromResource(PINHIBIT->inhibitor->surface.lock()); if (!WLSurface) { - Log::logger->log(Log::DEBUG, "Inhibitor has no HL Surface attached to it, likely meaning it's a non-desktop element. Assuming it's visible."); + Debug::log(LOG, "Inhibitor has no HL Surface attached to it, likely meaning it's a non-desktop element. Assuming it's visible."); PINHIBIT->nonDesktop = true; recheckIdleInhibitorStatus(); return; } - PINHIBIT->surfaceDestroyListener = - WLSurface->m_events.destroy.listen([this, PINHIBIT] { std::erase_if(m_idleInhibitors, [PINHIBIT](const auto& other) { return other.get() == PINHIBIT; }); }); + PINHIBIT->surfaceDestroyListener = WLSurface->events.destroy.registerListener( + [this, PINHIBIT](std::any data) { std::erase_if(m_vIdleInhibitors, [PINHIBIT](const auto& other) { return other.get() == PINHIBIT; }); }); recheckIdleInhibitorStatus(); } void CInputManager::recheckIdleInhibitorStatus() { - for (auto const& ii : m_idleInhibitors) { + for (auto const& ii : m_vIdleInhibitors) { if (ii->nonDesktop) { PROTO::idle->setInhibit(true); return; } - auto WLSurface = Desktop::View::CWLSurface::fromResource(ii->inhibitor->m_surface.lock()); + auto WLSurface = CWLSurface::fromResource(ii->inhibitor->surface.lock()); - if (!WLSurface || !WLSurface->view()) + if (!WLSurface) continue; - if (WLSurface->view()->aliveAndVisible()) { + if (WLSurface->visible()) { PROTO::idle->setInhibit(true); return; } } // check manual user-set inhibitors - for (auto const& w : g_pCompositor->m_windows) { + for (auto const& w : g_pCompositor->m_vWindows) { if (isWindowInhibiting(w)) { PROTO::idle->setInhibit(true); return; @@ -61,35 +61,35 @@ void CInputManager::recheckIdleInhibitorStatus() { } bool CInputManager::isWindowInhibiting(const PHLWINDOW& w, bool onlyHl) { - if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_ALWAYS) + if (w->m_eIdleInhibitMode == IDLEINHIBIT_ALWAYS) return true; - if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_FOCUS && g_pCompositor->isWindowActive(w)) + if (w->m_eIdleInhibitMode == IDLEINHIBIT_FOCUS && g_pCompositor->isWindowActive(w)) return true; - if (w->m_ruleApplicator->idleInhibitMode().valueOrDefault() == Desktop::Rule::IDLEINHIBIT_FULLSCREEN && w->isFullscreen() && w->m_workspace && w->m_workspace->isVisible()) + if (w->m_eIdleInhibitMode == IDLEINHIBIT_FULLSCREEN && w->isFullscreen() && w->m_pWorkspace && w->m_pWorkspace->isVisible()) return true; if (onlyHl) return false; - for (auto const& ii : m_idleInhibitors) { + for (auto const& ii : m_vIdleInhibitors) { if (ii->nonDesktop || !ii->inhibitor) continue; bool isInhibiting = false; - w->wlSurface()->resource()->breadthfirst( + w->m_pWLSurface->resource()->breadthfirst( [&ii](SP surf, const Vector2D& pos, void* data) { - if (ii->inhibitor->m_surface != surf) + if (ii->inhibitor->surface != surf) return; - auto WLSurface = Desktop::View::CWLSurface::fromResource(surf); + auto WLSurface = CWLSurface::fromResource(surf); - if (!WLSurface || !WLSurface->view()) + if (!WLSurface) return; - if (WLSurface->view()->aliveAndVisible()) - *sc(data) = true; + if (WLSurface->visible()) + *(bool*)data = true; }, &isInhibiting); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 9195536f..8ca8c21d 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -2,18 +2,14 @@ #include "../../Compositor.hpp" #include #include -#include #include -#include #include "../../config/ConfigValue.hpp" -#include "../../config/ConfigManager.hpp" -#include "../../desktop/view/WLSurface.hpp" -#include "../../desktop/state/FocusState.hpp" +#include "../../desktop/Window.hpp" +#include "../../desktop/LayerSurface.hpp" #include "../../protocols/CursorShape.hpp" #include "../../protocols/IdleInhibit.hpp" #include "../../protocols/RelativePointer.hpp" #include "../../protocols/PointerConstraints.hpp" -#include "../../protocols/PointerGestures.hpp" #include "../../protocols/IdleNotify.hpp" #include "../../protocols/SessionLock.hpp" #include "../../protocols/InputMethodV2.hpp" @@ -35,80 +31,60 @@ #include "../../managers/SeatManager.hpp" #include "../../managers/KeybindManager.hpp" #include "../../render/Renderer.hpp" +#include "../../managers/HookSystemManager.hpp" #include "../../managers/EventManager.hpp" -#include "../../managers/permissions/DynamicPermissionManager.hpp" - -#include "../../helpers/time/Time.hpp" -#include "../../helpers/MiscFunctions.hpp" - -#include "../../layout/LayoutManager.hpp" - -#include "../../event/EventBus.hpp" - -#include "trackpad/TrackpadGestures.hpp" -#include "../cursor/CursorShapeOverrideController.hpp" +#include "../../managers/LayoutManager.hpp" #include CInputManager::CInputManager() { - m_listeners.setCursorShape = PROTO::cursorShape->m_events.setShape.listen([this](const CCursorShapeProtocol::SSetShapeEvent& event) { - if (!g_pSeatManager->m_state.pointerFocusResource) - return; - - if (wl_resource_get_client(event.pMgr->resource()) != g_pSeatManager->m_state.pointerFocusResource->client()) - return; - - Log::logger->log(Log::DEBUG, "cursorImage request: shape {} -> {}", sc(event.shape), event.shapeName); - - m_cursorSurfaceInfo.wlSurface->unassign(); - m_cursorSurfaceInfo.vHotspot = {}; - m_cursorSurfaceInfo.name = event.shapeName; - m_cursorSurfaceInfo.hidden = false; - + m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) { if (!cursorImageUnlocked()) return; - g_pHyprRenderer->setCursorFromName(m_cursorSurfaceInfo.name); - }); + auto event = std::any_cast(data); - m_listeners.newIdleInhibitor = PROTO::idleInhibit->m_events.newIdleInhibitor.listen([this](const auto& data) { newIdleInhibitor(data); }); - - m_listeners.newVirtualKeyboard = PROTO::virtualKeyboard->m_events.newKeyboard.listen([this](const auto& keyboard) { - newVirtualKeyboard(keyboard); - updateCapabilities(); - }); - - m_listeners.newVirtualMouse = PROTO::virtualPointer->m_events.newPointer.listen([this](const auto& mouse) { - newVirtualMouse(mouse); - updateCapabilities(); - }); - - m_listeners.setCursor = g_pSeatManager->m_events.setCursor.listen([this](const auto& event) { processMouseRequest(event); }); - - m_listeners.overrideChanged = Cursor::overrideController->m_events.overrideChanged.listen([this](const std::string& shape) { - if (shape.empty()) { - m_cursorImageOverridden = false; - restoreCursorIconToApp(); + if (!g_pSeatManager->state.pointerFocusResource) return; - } - m_cursorImageOverridden = true; - g_pHyprRenderer->setCursorFromName(shape); + if (wl_resource_get_client(event.pMgr->resource()) != g_pSeatManager->state.pointerFocusResource->client()) + return; + + Debug::log(LOG, "cursorImage request: shape {} -> {}", (uint32_t)event.shape, event.shapeName); + + m_sCursorSurfaceInfo.wlSurface->unassign(); + m_sCursorSurfaceInfo.vHotspot = {}; + m_sCursorSurfaceInfo.name = event.shapeName; + m_sCursorSurfaceInfo.hidden = false; + + m_sCursorSurfaceInfo.inUse = true; + g_pHyprRenderer->setCursorFromName(m_sCursorSurfaceInfo.name); }); - m_cursorSurfaceInfo.wlSurface = Desktop::View::CWLSurface::create(); + m_sListeners.newIdleInhibitor = PROTO::idleInhibit->events.newIdleInhibitor.registerListener([this](std::any data) { this->newIdleInhibitor(data); }); + m_sListeners.newVirtualKeyboard = PROTO::virtualKeyboard->events.newKeyboard.registerListener([this](std::any data) { + this->newVirtualKeyboard(std::any_cast>(data)); + updateCapabilities(); + }); + m_sListeners.newVirtualMouse = PROTO::virtualPointer->events.newPointer.registerListener([this](std::any data) { + this->newVirtualMouse(std::any_cast>(data)); + updateCapabilities(); + }); + m_sListeners.setCursor = g_pSeatManager->events.setCursor.registerListener([this](std::any d) { this->processMouseRequest(d); }); + + m_sCursorSurfaceInfo.wlSurface = CWLSurface::create(); } CInputManager::~CInputManager() { - m_constraints.clear(); - m_keyboards.clear(); - m_pointers.clear(); - m_touches.clear(); - m_tablets.clear(); - m_tabletTools.clear(); - m_tabletPads.clear(); - m_idleInhibitors.clear(); - m_switches.clear(); + m_vConstraints.clear(); + m_vKeyboards.clear(); + m_vPointers.clear(); + m_vTouches.clear(); + m_vTablets.clear(); + m_vTabletTools.clear(); + m_vTabletPads.clear(); + m_vIdleInhibitors.clear(); + m_lSwitches.clear(); } void CInputManager::onMouseMoved(IPointer::SMotionEvent e) { @@ -118,12 +94,12 @@ void CInputManager::onMouseMoved(IPointer::SMotionEvent e) { Vector2D unaccel = e.unaccel; if (e.device) { - if (e.device->m_isTouchpad) { - if (e.device->m_flipX) { + if (e.device->isTouchpad) { + if (e.device->flipX) { delta.x = -delta.x; unaccel.x = -unaccel.x; } - if (e.device->m_flipY) { + if (e.device->flipY) { delta.y = -delta.y; unaccel.y = -unaccel.y; } @@ -132,23 +108,26 @@ void CInputManager::onMouseMoved(IPointer::SMotionEvent e) { const auto DELTA = *PNOACCEL == 1 ? unaccel : delta; + if (g_pSeatManager->isPointerFrameSkipped) + g_pPointerManager->storeMovement((uint64_t)e.timeMs, DELTA, unaccel); + else + g_pPointerManager->setStoredMovement((uint64_t)e.timeMs, DELTA, unaccel); + + PROTO::relativePointer->sendRelativeMotion((uint64_t)e.timeMs * 1000, DELTA, unaccel); + if (e.mouse) recheckMouseWarpOnMouseInput(); - PROTO::relativePointer->sendRelativeMotion(sc(e.timeMs) * 1000, delta, unaccel); g_pPointerManager->move(DELTA); mouseMoveUnified(e.timeMs, false, e.mouse); - m_lastCursorMovement.reset(); + m_tmrLastCursorMovement.reset(); - m_lastInputTouch = false; - m_lastInputTablet = false; + m_bLastInputTouch = false; if (e.mouse) - m_lastMousePos = getMouseCoordsInternal(); - - g_pSeatManager->sendPointerFrame(); + m_vLastMousePos = getMouseCoordsInternal(); } void CInputManager::onMouseWarp(IPointer::SMotionAbsoluteEvent e) { @@ -156,56 +135,46 @@ void CInputManager::onMouseWarp(IPointer::SMotionAbsoluteEvent e) { mouseMoveUnified(e.timeMs); - m_lastCursorMovement.reset(); + m_tmrLastCursorMovement.reset(); - m_lastInputTouch = false; - m_lastInputTablet = false; + m_bLastInputTouch = false; } void CInputManager::simulateMouseMovement() { - m_lastCursorPosFloored = m_lastCursorPosFloored - Vector2D(1, 1); // hack: force the mouseMoveUnified to report without making this a refocus. - mouseMoveUnified(Time::millis(Time::steadyNow())); + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + m_vLastCursorPosFloored = m_vLastCursorPosFloored - Vector2D(1, 1); // hack: force the mouseMoveUnified to report without making this a refocus. + mouseMoveUnified(now.tv_sec * 1000 + now.tv_nsec / 10000000); } void CInputManager::sendMotionEventsToFocused() { - if (!Desktop::focusState()->surface() || isConstrained()) + if (!g_pCompositor->m_pLastFocus || isConstrained()) return; - const auto SURF = Desktop::focusState()->surface(); + // todo: this sucks ass + const auto PWINDOW = g_pCompositor->getWindowFromSurface(g_pCompositor->m_pLastFocus.lock()); + const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus.lock()); - if (!SURF) - return; + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); - const auto HLSurf = Desktop::View::CWLSurface::fromResource(SURF); + const auto LOCAL = getMouseCoordsInternal() - (PWINDOW ? PWINDOW->m_vRealPosition->goal() : (PLS ? Vector2D{PLS->geometry.x, PLS->geometry.y} : Vector2D{})); - if (!HLSurf || !HLSurf->view()) - return; + m_bEmptyFocusCursorSet = false; - const auto VIEW = HLSurf->view(); - - if (!VIEW->aliveAndVisible()) - return; - - const auto BOX = HLSurf->getSurfaceBoxGlobal(); - - if (!BOX) - return; - - m_emptyFocusCursorSet = false; - - g_pSeatManager->setPointerFocus(Desktop::focusState()->surface(), getMouseCoordsInternal().floor() - BOX->pos()); + g_pSeatManager->setPointerFocus(g_pCompositor->m_pLastFocus.lock(), LOCAL); } -void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, std::optional overridePos) { - m_lastInputMouse = mouse; +void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse) { + m_bLastInputMouse = mouse; - if (!g_pCompositor->m_readyToProcess || g_pCompositor->m_isShuttingDown || g_pCompositor->m_unsafeState) + if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown || g_pCompositor->m_bUnsafeState) return; - Vector2D const mouseCoords = overridePos.value_or(getMouseCoordsInternal()); + Vector2D const mouseCoords = getMouseCoordsInternal(); auto const MOUSECOORDSFLOORED = mouseCoords.floor(); - if (MOUSECOORDSFLOORED == m_lastCursorPosFloored && !refocus) + if (MOUSECOORDSFLOORED == m_vLastCursorPosFloored && !refocus) return; static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); @@ -216,48 +185,45 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st static auto PMOUSEFOCUSMON = CConfigValue("misc:mouse_move_focuses_monitor"); static auto PRESIZEONBORDER = CConfigValue("general:resize_on_border"); static auto PRESIZECURSORICON = CConfigValue("general:hover_icon_on_border"); + static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); const auto FOLLOWMOUSE = *PFOLLOWONDND && PROTO::data->dndActive() ? 1 : *PFOLLOWMOUSE; - if (FOLLOWMOUSE == 1 && m_lastCursorMovement.getSeconds() < 0.5) - m_mousePosDelta += MOUSECOORDSFLOORED.distance(m_lastCursorPosFloored); + if (FOLLOWMOUSE == 1 && m_tmrLastCursorMovement.getSeconds() < 0.5) + m_fMousePosDelta += MOUSECOORDSFLOORED.distance(m_vLastCursorPosFloored); else - m_mousePosDelta = 0; + m_fMousePosDelta = 0; - m_foundSurfaceToFocus.reset(); - m_foundLSToFocus.reset(); - m_foundWindowToFocus.reset(); + m_pFoundSurfaceToFocus.reset(); + m_pFoundLSToFocus.reset(); + m_pFoundWindowToFocus.reset(); SP foundSurface; Vector2D surfaceCoords; Vector2D surfacePos = Vector2D(-1337, -1337); PHLWINDOW pFoundWindow; PHLLS pFoundLayerSurface; - const auto FOCUS_REASON = refocus ? Desktop::FOCUS_REASON_CLICK : Desktop::FOCUS_REASON_FFM; - Event::SCallbackInfo info; - Event::bus()->m_events.input.mouse.move.emit(MOUSECOORDSFLOORED, info); - if (info.cancelled) - return; + EMIT_HOOK_EVENT_CANCELLABLE("mouseMove", MOUSECOORDSFLOORED); - m_lastCursorPosFloored = MOUSECOORDSFLOORED; + m_vLastCursorPosFloored = MOUSECOORDSFLOORED; - const auto PMONITOR = isLocked() && Desktop::focusState()->monitor() ? Desktop::focusState()->monitor() : g_pCompositor->getMonitorFromCursor(); + const auto PMONITOR = isLocked() && g_pCompositor->m_pLastMonitor ? g_pCompositor->m_pLastMonitor.lock() : g_pCompositor->getMonitorFromCursor(); // this can happen if there are no displays hooked up to Hyprland if (PMONITOR == nullptr) return; - if (PMONITOR->m_cursorZoom->value() != 1.f) + if (*PZOOMFACTOR != 1.f) g_pHyprRenderer->damageMonitor(PMONITOR); bool skipFrameSchedule = PMONITOR->shouldSkipScheduleFrameOnMouseEvent(); - if (!PMONITOR->m_solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && g_pPointerManager->softwareLockedFor(PMONITOR->m_self.lock()) && !skipFrameSchedule) + if (!PMONITOR->solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && g_pPointerManager->softwareLockedFor(PMONITOR->self.lock()) && !skipFrameSchedule) g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE); // constraints - if (!g_pSeatManager->m_mouse.expired() && isConstrained()) { - const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface()); + if (!g_pSeatManager->mouse.expired() && isConstrained()) { + const auto SURF = CWLSurface::fromResource(g_pCompositor->m_pLastFocus.lock()); const auto CONSTRAINT = SURF ? SURF->constraint() : nullptr; if (CONSTRAINT) { @@ -268,102 +234,69 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st const auto RG = CONSTRAINT->logicConstraintRegion(); const auto CLOSEST = RG.closestPoint(mouseCoords); const auto BOX = SURF->getSurfaceBoxGlobal(); - const auto WINDOW = Desktop::View::CWindow::fromView(SURF->view()); - const auto CLOSESTLOCAL = (CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{})) * (WINDOW ? WINDOW->m_X11SurfaceScaledBy : 1.0); + const auto CLOSESTLOCAL = (CLOSEST - (BOX.has_value() ? BOX->pos() : Vector2D{})) * (SURF->getWindow() ? SURF->getWindow()->m_fX11SurfaceScaledBy : 1.0); g_pCompositor->warpCursorTo(CLOSEST, true); g_pSeatManager->sendPointerMotion(time, CLOSESTLOCAL); - PROTO::relativePointer->sendRelativeMotion(sc(time) * 1000, {}, {}); + PROTO::relativePointer->sendRelativeMotion((uint64_t)time * 1000, {}, {}); } return; } else - Log::logger->log(Log::ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", rc(SURF.get()), - rc(CONSTRAINT.get())); + Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF.get(), (uintptr_t)CONSTRAINT.get()); } - if (PMONITOR != Desktop::focusState()->monitor() && (*PMOUSEFOCUSMON || refocus) && m_forcedFocus.expired()) - Desktop::focusState()->rawMonitorFocus(PMONITOR); + if (PMONITOR != g_pCompositor->m_pLastMonitor && (*PMOUSEFOCUSMON || refocus) && m_pForcedFocus.expired()) + g_pCompositor->setActiveMonitor(PMONITOR); - // check for windows that have focus priority like our permission popups - pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, Desktop::View::FOCUS_PRIORITY); - if (pFoundWindow) - foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords); + if (g_pSessionLockManager->isSessionLocked()) { + const auto PSESSIONLOCKSURFACE = g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->ID); + surfacePos = PMONITOR->vecPosition; - if (!foundSurface && g_pSessionLockManager->isSessionLocked()) { - - // set keyboard focus on session lock surface regardless of layers - const auto PSESSIONLOCKSURFACE = g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->m_id); - const auto foundLockSurface = PSESSIONLOCKSURFACE ? PSESSIONLOCKSURFACE->surface->surface() : nullptr; - - Desktop::focusState()->rawSurfaceFocus(foundLockSurface); - - // search for interactable abovelock surfaces for pointer focus, or use session lock surface if not found - for (auto& lsl : PMONITOR->m_layerSurfaceLayers | std::views::reverse) { - foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &lsl, &surfaceCoords, &pFoundLayerSurface, true); - - if (foundSurface) - break; - } - - if (!foundSurface) { - surfaceCoords = mouseCoords - PMONITOR->m_position; - foundSurface = foundLockSurface; - } - - if (refocus) { - m_foundLSToFocus = pFoundLayerSurface; - m_foundWindowToFocus = pFoundWindow; - m_foundSurfaceToFocus = foundSurface; - } - - g_pSeatManager->setPointerFocus(foundSurface, surfaceCoords); - g_pSeatManager->sendPointerMotion(time, surfaceCoords); + foundSurface = PSESSIONLOCKSURFACE ? PSESSIONLOCKSURFACE->surface->surface() : nullptr; + g_pCompositor->focusSurface(foundSurface); + const auto SURFACELOCAL = mouseCoords - surfacePos; + g_pSeatManager->setPointerFocus(foundSurface, SURFACELOCAL); + g_pSeatManager->sendPointerMotion(time, SURFACELOCAL); return; } - PHLWINDOW forcedFocus = m_forcedFocus.lock(); + PHLWINDOW forcedFocus = m_pForcedFocus.lock(); if (!forcedFocus) forcedFocus = g_pCompositor->getForceFocus(); - if (forcedFocus && !foundSurface) { + if (forcedFocus) { pFoundWindow = forcedFocus; - surfacePos = pFoundWindow->m_realPosition->value(); - foundSurface = pFoundWindow->wlSurface()->resource(); + surfacePos = pFoundWindow->m_vRealPosition->value(); + foundSurface = pFoundWindow->m_pWLSurface->resource(); } // if we are holding a pointer button, // and we're not dnd-ing, don't refocus. Keep focus on last surface. - if (!PROTO::data->dndActive() && !m_currentlyHeldButtons.empty() && Desktop::focusState()->surface() && Desktop::focusState()->surface()->m_mapped && - g_pSeatManager->m_state.pointerFocus && !m_hardInput) { - foundSurface = g_pSeatManager->m_state.pointerFocus.lock(); + if (!PROTO::data->dndActive() && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && g_pSeatManager->state.pointerFocus && !m_bHardInput) { + foundSurface = g_pSeatManager->state.pointerFocus.lock(); // IME popups aren't desktop-like elements // TODO: make them. - CInputPopup* foundPopup = m_relay.popupFromSurface(foundSurface); + CInputPopup* foundPopup = m_sIMERelay.popupFromSurface(foundSurface); if (foundPopup) { - surfacePos = foundPopup->globalBox().pos(); - m_focusHeldByButtons = true; - m_refocusHeldByButtons = refocus; + surfacePos = foundPopup->globalBox().pos(); + m_bFocusHeldByButtons = true; + m_bRefocusHeldByButtons = refocus; } else { - auto HLSurface = Desktop::View::CWLSurface::fromResource(foundSurface); + auto HLSurface = CWLSurface::fromResource(foundSurface); if (HLSurface) { const auto BOX = HLSurface->getSurfaceBoxGlobal(); if (BOX) { - const auto PWINDOW = Desktop::View::CWindow::fromView(HLSurface->view()); - const auto LS = Desktop::View::CLayerSurface::fromView(HLSurface->view()); + const auto PWINDOW = HLSurface->getWindow(); surfacePos = BOX->pos(); - - if (PWINDOW) - pFoundWindow = PWINDOW; - else if (LS) - pFoundLayerSurface = LS; - + pFoundLayerSurface = HLSurface->getLayer(); + pFoundWindow = !PWINDOW || PWINDOW->isHidden() ? g_pCompositor->m_pLastWindow.lock() : PWINDOW; } else // reset foundSurface, find one normally foundSurface = nullptr; } else // reset foundSurface, find one normally @@ -371,29 +304,18 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st } } - g_layoutManager->moveMouse(getMouseCoordsInternal()); - - // forced above all - if (!g_pInputManager->m_exclusiveLSes.empty()) { - if (!foundSurface) - foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &g_pInputManager->m_exclusiveLSes, &surfaceCoords, &pFoundLayerSurface); - - if (!foundSurface) { - foundSurface = (*g_pInputManager->m_exclusiveLSes.begin())->wlSurface()->resource(); - surfacePos = (*g_pInputManager->m_exclusiveLSes.begin())->m_realPosition->goal(); - } - } + g_pLayoutManager->getCurrentLayout()->onMouseMove(getMouseCoordsInternal()); if (!foundSurface) foundSurface = g_pCompositor->vectorToLayerPopupSurface(mouseCoords, PMONITOR, &surfaceCoords, &pFoundLayerSurface); // overlays are above fullscreen if (!foundSurface) - foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &surfaceCoords, &pFoundLayerSurface); + foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &surfaceCoords, &pFoundLayerSurface); // also IME popups if (!foundSurface) { - auto popup = g_pInputManager->m_relay.popupFromCoords(mouseCoords); + auto popup = g_pInputManager->m_sIMERelay.popupFromCoords(mouseCoords); if (popup) { foundSurface = popup->getSurface(); surfacePos = popup->globalBox().pos(); @@ -402,51 +324,41 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st // also top layers if (!foundSurface) - foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &surfaceCoords, &pFoundLayerSurface); + foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &surfaceCoords, &pFoundLayerSurface); - // then, we check if the workspace doesn't have a fullscreen window - const auto PWORKSPACE = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace; - const auto PWINDOWIDEAL = g_pCompositor->vectorToWindowUnified(mouseCoords, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); - if (PWORKSPACE->m_hasFullscreenWindow && PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) { - const auto IS_LS_UNFOCUSABLE = pFoundLayerSurface && - (pFoundLayerSurface->m_layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP || - (pFoundLayerSurface->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP && !pFoundLayerSurface->m_aboveFullscreen)); + // then, we check if the workspace doesnt have a fullscreen window + const auto PWORKSPACE = PMONITOR->activeWorkspace; + const auto PWINDOWIDEAL = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); + if (PWORKSPACE->m_bHasFullscreenWindow && !foundSurface && PWORKSPACE->m_efFullscreenMode == FSMODE_FULLSCREEN) { + pFoundWindow = PWORKSPACE->getFullscreenWindow(); - if (IS_LS_UNFOCUSABLE) { - foundSurface = nullptr; - pFoundLayerSurface = nullptr; + if (!pFoundWindow) { + // what the fuck, somehow happens occasionally?? + PWORKSPACE->m_bHasFullscreenWindow = false; + return; + } - pFoundWindow = PWORKSPACE->getFullscreenWindow(); + if (PWINDOWIDEAL && + ((PWINDOWIDEAL->m_bIsFloating && PWINDOWIDEAL->m_bCreatedOverFullscreen) /* floating over fullscreen */ + || (PMONITOR->activeSpecialWorkspace == PWINDOWIDEAL->m_pWorkspace) /* on an open special workspace */)) + pFoundWindow = PWINDOWIDEAL; - if (!pFoundWindow) { - // what the fuck, somehow happens occasionally?? - PWORKSPACE->m_hasFullscreenWindow = false; - return; - } - - if (PWINDOWIDEAL && - ((PWINDOWIDEAL->m_isFloating && (PWINDOWIDEAL->m_createdOverFullscreen || PWINDOWIDEAL->m_pinned)) /* floating over fullscreen or pinned */ - || (PMONITOR->m_activeSpecialWorkspace == PWINDOWIDEAL->m_workspace) /* on an open special workspace */)) - pFoundWindow = PWINDOWIDEAL; - - if (!pFoundWindow->m_isX11) { - foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords); - surfacePos = Vector2D(-1337, -1337); - } else { - foundSurface = pFoundWindow->wlSurface()->resource(); - surfacePos = pFoundWindow->m_realPosition->value(); - } + if (!pFoundWindow->m_bIsX11) { + foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords); + surfacePos = Vector2D(-1337, -1337); + } else { + foundSurface = pFoundWindow->m_pWLSurface->resource(); + surfacePos = pFoundWindow->m_vRealPosition->value(); } } // then windows if (!foundSurface) { - if (PWORKSPACE->m_hasFullscreenWindow && PWORKSPACE->m_fullscreenMode == FSMODE_MAXIMIZED) { + if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FSMODE_MAXIMIZED) { if (!foundSurface) { - if (PMONITOR->m_activeSpecialWorkspace) { + if (PMONITOR->activeSpecialWorkspace) { if (pFoundWindow != PWINDOWIDEAL) - pFoundWindow = - g_pCompositor->vectorToWindowUnified(mouseCoords, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); + pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (pFoundWindow && !pFoundWindow->onSpecialWorkspace()) { pFoundWindow = PWORKSPACE->getFullscreenWindow(); @@ -454,16 +366,15 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st } else { // if we have a maximized window, allow focusing on a bar or something if in reserved area. if (g_pCompositor->isPointOnReservedArea(mouseCoords, PMONITOR)) { - foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &surfaceCoords, + foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &surfaceCoords, &pFoundLayerSurface); } if (!foundSurface) { if (pFoundWindow != PWINDOWIDEAL) - pFoundWindow = - g_pCompositor->vectorToWindowUnified(mouseCoords, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); + pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); - if (!(pFoundWindow && (pFoundWindow->m_isFloating && (pFoundWindow->m_createdOverFullscreen || pFoundWindow->m_pinned)))) + if (!(pFoundWindow && pFoundWindow->m_bIsFloating && pFoundWindow->m_bCreatedOverFullscreen)) pFoundWindow = PWORKSPACE->getFullscreenWindow(); } } @@ -471,44 +382,45 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st } else { if (pFoundWindow != PWINDOWIDEAL) - pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); + pFoundWindow = g_pCompositor->vectorToWindowUnified(mouseCoords, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); } if (pFoundWindow) { - if (!pFoundWindow->m_isX11) { + if (!pFoundWindow->m_bIsX11) { foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords); if (!foundSurface) { - foundSurface = pFoundWindow->wlSurface()->resource(); - surfacePos = pFoundWindow->m_realPosition->value(); + foundSurface = pFoundWindow->m_pWLSurface->resource(); + surfacePos = pFoundWindow->m_vRealPosition->value(); } } else { - foundSurface = pFoundWindow->wlSurface()->resource(); - surfacePos = pFoundWindow->m_realPosition->value(); + foundSurface = pFoundWindow->m_pWLSurface->resource(); + surfacePos = pFoundWindow->m_vRealPosition->value(); } } } // then surfaces below if (!foundSurface) - foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &surfaceCoords, &pFoundLayerSurface); + foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &surfaceCoords, &pFoundLayerSurface); if (!foundSurface) - foundSurface = g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords, &pFoundLayerSurface); + foundSurface = + g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords, &pFoundLayerSurface); - if (g_pPointerManager->softwareLockedFor(PMONITOR->m_self.lock()) > 0 && !skipFrameSchedule) - g_pCompositor->scheduleFrameForMonitor(Desktop::focusState()->monitor(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE); + if (g_pPointerManager->softwareLockedFor(PMONITOR->self.lock()) > 0 && !skipFrameSchedule) + g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.lock(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE); // FIXME: This will be disabled during DnD operations because we do not exactly follow the spec // xdg-popup grabs should be keyboard-only, while they are absolute in our case... - if (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(foundSurface) && !PROTO::data->dndActive()) { - if (m_hardInput || refocus) { + if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(foundSurface) && !PROTO::data->dndActive()) { + if (m_bHardInput || refocus) { g_pSeatManager->setGrab(nullptr); return; // setGrab will refocus } else { // we need to grab the last surface. - foundSurface = g_pSeatManager->m_state.pointerFocus.lock(); + foundSurface = g_pSeatManager->state.pointerFocus.lock(); - auto HLSurface = Desktop::View::CWLSurface::fromResource(foundSurface); + auto HLSurface = CWLSurface::fromResource(foundSurface); if (HLSurface) { const auto BOX = HLSurface->getSurfaceBoxGlobal(); @@ -520,51 +432,68 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st } if (!foundSurface) { - if (!m_emptyFocusCursorSet) { - g_pHyprRenderer->setCursorFromName("left_ptr"); - m_emptyFocusCursorSet = true; + if (!m_bEmptyFocusCursorSet) { + if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) { + m_eBorderIconDirection = BORDERICON_NONE; + unsetCursorImage(); + } + + // TODO: maybe wrap? + if (m_ecbClickBehavior == CLICKMODE_KILL) + setCursorImageOverride("crosshair"); + else + setCursorImageOverride("left_ptr"); + + m_bEmptyFocusCursorSet = true; } g_pSeatManager->setPointerFocus(nullptr, {}); - if (refocus || !Desktop::focusState()->window()) // if we are forcing a refocus, and we don't find a surface, clear the kb focus too! - Desktop::focusState()->rawWindowFocus(nullptr, FOCUS_REASON); + if (refocus || g_pCompositor->m_pLastWindow.expired()) // if we are forcing a refocus, and we don't find a surface, clear the kb focus too! + g_pCompositor->focusWindow(nullptr); return; } - m_emptyFocusCursorSet = false; + m_bEmptyFocusCursorSet = false; Vector2D surfaceLocal = surfacePos == Vector2D(-1337, -1337) ? surfaceCoords : mouseCoords - surfacePos; - if (pFoundWindow && pFoundWindow->m_isX11) // for x11 force scale zero - surfaceLocal = surfaceLocal * pFoundWindow->m_X11SurfaceScaledBy; + if (pFoundWindow && !pFoundWindow->m_bIsX11 && surfacePos != Vector2D(-1337, -1337)) { + // calc for oversized windows... fucking bullshit. + CBox geom = pFoundWindow->m_pXDGSurface->current.geometry; + + surfaceLocal = mouseCoords - surfacePos + geom.pos(); + } + + if (pFoundWindow && pFoundWindow->m_bIsX11) // for x11 force scale zero + surfaceLocal = surfaceLocal * pFoundWindow->m_fX11SurfaceScaledBy; bool allowKeyboardRefocus = true; - if (!refocus && Desktop::focusState()->surface()) { - const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(Desktop::focusState()->surface()); + if (!refocus && g_pCompositor->m_pLastFocus) { + const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus.lock()); - if (PLS && PLS->m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) + if (PLS && PLS->layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) allowKeyboardRefocus = false; } // set the values for use if (refocus) { - m_foundLSToFocus = pFoundLayerSurface; - m_foundWindowToFocus = pFoundWindow; - m_foundSurfaceToFocus = foundSurface; + m_pFoundLSToFocus = pFoundLayerSurface; + m_pFoundWindowToFocus = pFoundWindow; + m_pFoundSurfaceToFocus = foundSurface; } - if (g_layoutManager->dragController()->target() && pFoundWindow != g_layoutManager->dragController()->target()) { + if (currentlyDraggedWindow.lock() && pFoundWindow != currentlyDraggedWindow) { g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); return; } - if (pFoundWindow && foundSurface == pFoundWindow->wlSurface()->resource() && !m_cursorImageOverridden) { + if (pFoundWindow && foundSurface == pFoundWindow->m_pWLSurface->resource() && !m_bCursorImageOverridden) { const auto BOX = pFoundWindow->getWindowMainSurfaceBox(); - if (VECNOTINRECT(mouseCoords, BOX.x, BOX.y, BOX.x + BOX.width, BOX.y + BOX.height)) - g_pHyprRenderer->setCursorFromName("left_ptr"); + if (!VECINRECT(mouseCoords, BOX.x, BOX.y, BOX.x + BOX.width, BOX.y + BOX.height)) + setCursorImageOverride("left_ptr"); else restoreCursorIconToApp(); } @@ -572,75 +501,67 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st if (pFoundWindow) { // change cursor icon if hovering over border if (*PRESIZEONBORDER && *PRESIZECURSORICON) { - if (!pFoundWindow->isFullscreen() && !pFoundWindow->hasPopupAt(mouseCoords)) + if (!pFoundWindow->isFullscreen() && !pFoundWindow->hasPopupAt(mouseCoords)) { setCursorIconOnBorder(pFoundWindow); - else if (m_borderIconDirection != BORDERICON_NONE) { - m_borderIconDirection = BORDERICON_NONE; - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); + } else if (m_eBorderIconDirection != BORDERICON_NONE) { + unsetCursorImage(); } - } else if (m_borderIconDirection != BORDERICON_NONE) { - m_borderIconDirection = BORDERICON_NONE; - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); } if (FOLLOWMOUSE != 1 && !refocus) { - if (pFoundWindow != Desktop::focusState()->window() && Desktop::focusState()->window() && - ((pFoundWindow->m_isFloating && *PFLOATBEHAVIOR == 2) || (Desktop::focusState()->window()->m_isFloating != pFoundWindow->m_isFloating && *PFLOATBEHAVIOR != 0))) { + if (pFoundWindow != g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow.lock() && + ((pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR == 2) || (g_pCompositor->m_pLastWindow->m_bIsFloating != pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR != 0))) { // enter if change floating style if (FOLLOWMOUSE != 3 && allowKeyboardRefocus) - Desktop::focusState()->rawWindowFocus(pFoundWindow, FOCUS_REASON, foundSurface); + g_pCompositor->focusWindow(pFoundWindow, foundSurface); g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); } else if (FOLLOWMOUSE == 2 || FOLLOWMOUSE == 3) g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); - if (pFoundWindow == Desktop::focusState()->window()) + if (pFoundWindow == g_pCompositor->m_pLastWindow) g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); - if (FOLLOWMOUSE != 0 || pFoundWindow == Desktop::focusState()->window()) + if (FOLLOWMOUSE != 0 || pFoundWindow == g_pCompositor->m_pLastWindow) g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); - if (g_pSeatManager->m_state.pointerFocus == foundSurface) + if (g_pSeatManager->state.pointerFocus == foundSurface) g_pSeatManager->sendPointerMotion(time, surfaceLocal); - m_lastFocusOnLS = false; + m_bLastFocusOnLS = false; return; // don't enter any new surfaces } else { - if (allowKeyboardRefocus && ((FOLLOWMOUSE != 3 && (*PMOUSEREFOCUS || m_lastMouseFocus.lock() != pFoundWindow)) || refocus)) { - if (m_lastMouseFocus.lock() != pFoundWindow || Desktop::focusState()->window() != pFoundWindow || Desktop::focusState()->surface() != foundSurface || refocus) { - m_lastMouseFocus = pFoundWindow; + if (allowKeyboardRefocus && ((FOLLOWMOUSE != 3 && (*PMOUSEREFOCUS || m_pLastMouseFocus.lock() != pFoundWindow)) || refocus)) { + if (m_pLastMouseFocus.lock() != pFoundWindow || g_pCompositor->m_pLastWindow.lock() != pFoundWindow || g_pCompositor->m_pLastFocus != foundSurface || refocus) { + m_pLastMouseFocus = pFoundWindow; // TODO: this looks wrong. When over a popup, it constantly is switching. // Temp fix until that's figured out. Otherwise spams windowrule lookups and other shit. - if (m_lastMouseFocus.lock() != pFoundWindow || Desktop::focusState()->window() != pFoundWindow) { - if (m_mousePosDelta > *PFOLLOWMOUSETHRESHOLD || refocus) { - const bool hasNoFollowMouse = pFoundWindow && pFoundWindow->m_ruleApplicator->noFollowMouse().valueOrDefault(); - - if (refocus || !hasNoFollowMouse) - Desktop::focusState()->rawWindowFocus(pFoundWindow, FOCUS_REASON, foundSurface); - } + if (m_pLastMouseFocus.lock() != pFoundWindow || g_pCompositor->m_pLastWindow.lock() != pFoundWindow) { + if (m_fMousePosDelta > *PFOLLOWMOUSETHRESHOLD || refocus) + g_pCompositor->focusWindow(pFoundWindow, foundSurface); } else - Desktop::focusState()->rawSurfaceFocus(foundSurface, pFoundWindow); + g_pCompositor->focusSurface(foundSurface, pFoundWindow); } } } - if (g_pSeatManager->m_state.keyboardFocus == nullptr) - Desktop::focusState()->rawWindowFocus(pFoundWindow, FOCUS_REASON, foundSurface); + if (g_pSeatManager->state.keyboardFocus == nullptr) + g_pCompositor->focusWindow(pFoundWindow, foundSurface); - m_lastFocusOnLS = false; + m_bLastFocusOnLS = false; } else { - if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_borderIconDirection != BORDERICON_NONE) { - m_borderIconDirection = BORDERICON_NONE; - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); + if (*PRESIZEONBORDER && *PRESIZECURSORICON && m_eBorderIconDirection != BORDERICON_NONE) { + m_eBorderIconDirection = BORDERICON_NONE; + unsetCursorImage(); } - if (pFoundLayerSurface && (pFoundLayerSurface->m_layerSurface->m_current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) && FOLLOWMOUSE != 3 && - (allowKeyboardRefocus || pFoundLayerSurface->m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)) { - Desktop::focusState()->rawSurfaceFocus(foundSurface); + if (pFoundLayerSurface && (pFoundLayerSurface->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) && FOLLOWMOUSE != 3 && + (allowKeyboardRefocus || pFoundLayerSurface->layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE)) { + g_pCompositor->focusSurface(foundSurface); } if (pFoundLayerSurface) - m_lastFocusOnLS = true; + m_bLastFocusOnLS = true; } g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal); @@ -648,108 +569,126 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st } void CInputManager::onMouseButton(IPointer::SButtonEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.input.mouse.button.emit(e, info); - if (info.cancelled) - return; + EMIT_HOOK_EVENT_CANCELLABLE("mouseButton", e); if (e.mouse) recheckMouseWarpOnMouseInput(); - m_lastCursorMovement.reset(); + m_tmrLastCursorMovement.reset(); if (e.state == WL_POINTER_BUTTON_STATE_PRESSED) { - m_currentlyHeldButtons.push_back(e.button); + m_lCurrentlyHeldButtons.push_back(e.button); } else { - if (std::ranges::find_if(m_currentlyHeldButtons, [&](const auto& other) { return other == e.button; }) == m_currentlyHeldButtons.end()) + if (std::find_if(m_lCurrentlyHeldButtons.begin(), m_lCurrentlyHeldButtons.end(), [&](const auto& other) { return other == e.button; }) == m_lCurrentlyHeldButtons.end()) return; - std::erase_if(m_currentlyHeldButtons, [&](const auto& other) { return other == e.button; }); + std::erase_if(m_lCurrentlyHeldButtons, [&](const auto& other) { return other == e.button; }); } - switch (m_clickBehavior) { + switch (m_ecbClickBehavior) { case CLICKMODE_DEFAULT: processMouseDownNormal(e); break; case CLICKMODE_KILL: processMouseDownKill(e); break; default: break; } - if (m_focusHeldByButtons && m_currentlyHeldButtons.empty() && e.state == WL_POINTER_BUTTON_STATE_RELEASED) { - if (m_refocusHeldByButtons) + if (m_bFocusHeldByButtons && m_lCurrentlyHeldButtons.empty() && e.state == WL_POINTER_BUTTON_STATE_RELEASED) { + if (m_bRefocusHeldByButtons) refocus(); else simulateMouseMovement(); - m_focusHeldByButtons = false; - m_refocusHeldByButtons = false; + m_bFocusHeldByButtons = false; + m_bRefocusHeldByButtons = false; } - - g_pSeatManager->sendPointerFrame(); } -void CInputManager::processMouseRequest(const CSeatManager::SSetCursorEvent& event) { - Log::logger->log(Log::DEBUG, "cursorImage request: surface {:x}", rc(event.surf.get())); - - if (event.surf != m_cursorSurfaceInfo.wlSurface->resource()) { - m_cursorSurfaceInfo.wlSurface->unassign(); - - if (event.surf) - m_cursorSurfaceInfo.wlSurface->assign(event.surf); - } - - if (event.surf) { - m_cursorSurfaceInfo.vHotspot = event.hotspot; - m_cursorSurfaceInfo.hidden = false; - } else { - m_cursorSurfaceInfo.vHotspot = {}; - m_cursorSurfaceInfo.hidden = true; - } - - m_cursorSurfaceInfo.name = ""; - +void CInputManager::processMouseRequest(std::any E) { if (!cursorImageUnlocked()) return; - g_pHyprRenderer->setCursorSurface(m_cursorSurfaceInfo.wlSurface, event.hotspot.x, event.hotspot.y); + auto e = std::any_cast(E); + + Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e.surf.get()); + + if (e.surf != m_sCursorSurfaceInfo.wlSurface->resource()) { + m_sCursorSurfaceInfo.wlSurface->unassign(); + + if (e.surf) + m_sCursorSurfaceInfo.wlSurface->assign(e.surf); + } + + if (e.surf) { + m_sCursorSurfaceInfo.vHotspot = e.hotspot; + m_sCursorSurfaceInfo.hidden = false; + } else { + m_sCursorSurfaceInfo.vHotspot = {}; + m_sCursorSurfaceInfo.hidden = true; + } + + m_sCursorSurfaceInfo.name = ""; + + m_sCursorSurfaceInfo.inUse = true; + g_pHyprRenderer->setCursorSurface(m_sCursorSurfaceInfo.wlSurface, e.hotspot.x, e.hotspot.y); } void CInputManager::restoreCursorIconToApp() { - if (m_cursorSurfaceInfo.hidden) { + if (m_sCursorSurfaceInfo.inUse) + return; + + if (m_sCursorSurfaceInfo.hidden) { g_pHyprRenderer->setCursorSurface(nullptr, 0, 0); return; } - if (m_cursorSurfaceInfo.name.empty()) { - if (m_cursorSurfaceInfo.wlSurface->exists()) - g_pHyprRenderer->setCursorSurface(m_cursorSurfaceInfo.wlSurface, m_cursorSurfaceInfo.vHotspot.x, m_cursorSurfaceInfo.vHotspot.y); - } else - g_pHyprRenderer->setCursorFromName(m_cursorSurfaceInfo.name); + if (m_sCursorSurfaceInfo.name.empty()) { + if (m_sCursorSurfaceInfo.wlSurface->exists()) + g_pHyprRenderer->setCursorSurface(m_sCursorSurfaceInfo.wlSurface, m_sCursorSurfaceInfo.vHotspot.x, m_sCursorSurfaceInfo.vHotspot.y); + } else { + g_pHyprRenderer->setCursorFromName(m_sCursorSurfaceInfo.name); + } + + m_sCursorSurfaceInfo.inUse = true; +} + +void CInputManager::setCursorImageOverride(const std::string& name) { + if (m_bCursorImageOverridden) + return; + + m_sCursorSurfaceInfo.inUse = false; + g_pHyprRenderer->setCursorFromName(name); } bool CInputManager::cursorImageUnlocked() { - return !m_cursorImageOverridden; + if (m_ecbClickBehavior == CLICKMODE_KILL) + return false; + + if (m_bCursorImageOverridden) + return false; + + return true; } eClickBehaviorMode CInputManager::getClickMode() { - return m_clickBehavior; + return m_ecbClickBehavior; } void CInputManager::setClickMode(eClickBehaviorMode mode) { switch (mode) { case CLICKMODE_DEFAULT: - Log::logger->log(Log::DEBUG, "SetClickMode: DEFAULT"); - m_clickBehavior = CLICKMODE_DEFAULT; - g_pHyprRenderer->setCursorFromName("left_ptr", true); + Debug::log(LOG, "SetClickMode: DEFAULT"); + m_ecbClickBehavior = CLICKMODE_DEFAULT; + g_pHyprRenderer->setCursorFromName("left_ptr"); break; case CLICKMODE_KILL: - Log::logger->log(Log::DEBUG, "SetClickMode: KILL"); - m_clickBehavior = CLICKMODE_KILL; + Debug::log(LOG, "SetClickMode: KILL"); + m_ecbClickBehavior = CLICKMODE_KILL; // remove constraints g_pInputManager->unconstrainMouse(); refocus(); // set cursor - Cursor::overrideController->setOverride("crosshair", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); + g_pHyprRenderer->setCursorFromName("crosshair"); break; default: break; } @@ -770,16 +709,16 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { return; const auto mouseCoords = g_pInputManager->getMouseCoordsInternal(); - const auto w = g_pCompositor->vectorToWindowUnified(mouseCoords, Desktop::View::ALLOW_FLOATING | Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS); + const auto w = g_pCompositor->vectorToWindowUnified(mouseCoords, ALLOW_FLOATING | RESERVED_EXTENTS | INPUT_EXTENTS); - if (w && !m_lastFocusOnLS && !g_pSessionLockManager->isSessionLocked() && w->checkInputOnDecos(INPUT_TYPE_BUTTON, mouseCoords, e)) + if (w && !m_bLastFocusOnLS && w->checkInputOnDecos(INPUT_TYPE_BUTTON, mouseCoords, e)) return; // clicking on border triggers resize // TODO detect click on LS properly - if (*PRESIZEONBORDER && !g_pSessionLockManager->isSessionLocked() && !m_lastFocusOnLS && e.state == WL_POINTER_BUTTON_STATE_PRESSED && (!w || !w->isX11OverrideRedirect())) { + if (*PRESIZEONBORDER && !m_bLastFocusOnLS && e.state == WL_POINTER_BUTTON_STATE_PRESSED && (!w || !w->isX11OverrideRedirect())) { if (w && !w->isFullscreen()) { - const CBox real = {w->m_realPosition->value().x, w->m_realPosition->value().y, w->m_realSize->value().x, w->m_realSize->value().y}; + const CBox real = {w->m_vRealPosition->value().x, w->m_vRealPosition->value().y, w->m_vRealSize->value().x, w->m_vRealSize->value().y}; const CBox grab = {real.x - BORDER_GRAB_AREA, real.y - BORDER_GRAB_AREA, real.width + 2 * BORDER_GRAB_AREA, real.height + 2 * BORDER_GRAB_AREA}; if ((grab.containsPoint(mouseCoords) && (!real.containsPoint(mouseCoords) || w->isInCurvedCorner(mouseCoords.x, mouseCoords.y))) && !w->hasPopupAt(mouseCoords)) { @@ -794,62 +733,56 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { if (*PFOLLOWMOUSE == 3) // don't refocus on full loose break; - if ((g_pSeatManager->m_mouse.expired() || !isConstrained()) /* No constraints */ - && (w && Desktop::focusState()->window() != w) /* window should change */) { + if ((g_pSeatManager->mouse.expired() || !isConstrained()) /* No constraints */ + && (w && g_pCompositor->m_pLastWindow.lock() != w) /* window should change */) { // a bit hacky // if we only pressed one button, allow us to refocus. m_lCurrentlyHeldButtons.size() > 0 will stick the focus - if (m_currentlyHeldButtons.size() == 1) { - const auto COPY = m_currentlyHeldButtons; - m_currentlyHeldButtons.clear(); + if (m_lCurrentlyHeldButtons.size() == 1) { + const auto COPY = m_lCurrentlyHeldButtons; + m_lCurrentlyHeldButtons.clear(); refocus(); - m_currentlyHeldButtons = COPY; + m_lCurrentlyHeldButtons = COPY; } else refocus(); } // if clicked on a floating window make it top - if (!g_pSeatManager->m_state.pointerFocus) + if (!g_pSeatManager->state.pointerFocus) break; - auto HLSurf = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.pointerFocus.lock()); + auto HLSurf = CWLSurface::fromResource(g_pSeatManager->state.pointerFocus.lock()); - // pointerFocus can target a surface without a Desktop::View (e.g. IME popups), so view() may be null. - const auto PVIEW = HLSurf ? HLSurf->view() : nullptr; - if (PVIEW && PVIEW->type() == Desktop::View::VIEW_TYPE_WINDOW) - g_pCompositor->changeWindowZOrder(dynamicPointerCast(PVIEW), true); + if (HLSurf && HLSurf->getWindow()) + g_pCompositor->changeWindowZOrder(HLSurf->getWindow(), true); break; } case WL_POINTER_BUTTON_STATE_RELEASED: break; } - // notify app if we didn't handle it + // notify app if we didnt handle it g_pSeatManager->sendPointerButton(e.timeMs, e.button, e.state); - if (const auto PMON = g_pCompositor->getMonitorFromVector(mouseCoords); PMON != Desktop::focusState()->monitor() && PMON) - Desktop::focusState()->rawMonitorFocus(PMON); + if (const auto PMON = g_pCompositor->getMonitorFromVector(mouseCoords); PMON != g_pCompositor->m_pLastMonitor && PMON) + g_pCompositor->setActiveMonitor(PMON); - if (g_pSeatManager->m_seatGrab && e.state == WL_POINTER_BUTTON_STATE_PRESSED) { - m_hardInput = true; + if (g_pSeatManager->seatGrab && e.state == WL_POINTER_BUTTON_STATE_PRESSED) { + m_bHardInput = true; simulateMouseMovement(); - m_hardInput = false; + m_bHardInput = false; } } void CInputManager::processMouseDownKill(const IPointer::SButtonEvent& e) { switch (e.state) { case WL_POINTER_BUTTON_STATE_PRESSED: { - const auto PWINDOW = - g_pCompositor->vectorToWindowUnified(getMouseCoordsInternal(), Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); + const auto PWINDOW = g_pCompositor->vectorToWindowUnified(getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (!PWINDOW) { - Log::logger->log(Log::ERR, "Cannot kill invalid window!"); + Debug::log(ERR, "Cannot kill invalid window!"); break; } - g_pEventManager->postEvent(SHyprIPCEvent({.event = "kill", .data = std::format("{:x}", rc(PWINDOW.m_data))})); - Event::bus()->m_events.window.kill.emit(PWINDOW); - // kill the mf kill(PWINDOW->getPID(), SIGKILL); break; @@ -859,11 +792,10 @@ void CInputManager::processMouseDownKill(const IPointer::SButtonEvent& e) { } // reset click behavior mode - m_clickBehavior = CLICKMODE_DEFAULT; - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION); + m_ecbClickBehavior = CLICKMODE_DEFAULT; } -void CInputManager::onMouseWheel(IPointer::SAxisEvent e, SP pointer) { +void CInputManager::onMouseWheel(IPointer::SAxisEvent e) { static auto POFFWINDOWAXIS = CConfigValue("input:off_window_axis_events"); static auto PINPUTSCROLLFACTOR = CConfigValue("input:scroll_factor"); static auto PTOUCHPADSCROLLFACTOR = CConfigValue("input:touchpad:scroll_factor"); @@ -873,13 +805,8 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e, SP pointer) { const bool ISTOUCHPADSCROLL = *PTOUCHPADSCROLLFACTOR <= 0.f || e.source == WL_POINTER_AXIS_SOURCE_FINGER; auto factor = ISTOUCHPADSCROLL ? *PTOUCHPADSCROLLFACTOR : *PINPUTSCROLLFACTOR; - if (pointer && pointer->m_scrollFactor.has_value()) - factor = *pointer->m_scrollFactor; - - Event::SCallbackInfo info; - Event::bus()->m_events.input.mouse.axis.emit(e, info); - if (info.cancelled) - return; + const auto EMAP = std::unordered_map{{"event", e}}; + EMIT_HOOK_EVENT_CANCELLABLE("mouseAxis", EMAP); if (e.mouse) recheckMouseWarpOnMouseInput(); @@ -889,9 +816,9 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e, SP pointer) { if (!passEvent) return; - if (!m_lastFocusOnLS) { + if (!m_bLastFocusOnLS) { const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); - const auto PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); + const auto PWINDOW = g_pCompositor->vectorToWindowUnified(MOUSECOORDS, RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING); if (PWINDOW) { if (PWINDOW->checkInputOnDecos(INPUT_TYPE_AXIS, MOUSECOORDS, e)) @@ -915,17 +842,13 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e, SP pointer) { } } - if (g_pSeatManager->m_state.pointerFocus) { - const auto PCURRWINDOW = g_pCompositor->getWindowFromSurface(g_pSeatManager->m_state.pointerFocus.lock()); + if (g_pSeatManager->state.pointerFocus) { + const auto PCURRWINDOW = g_pCompositor->getWindowFromSurface(g_pSeatManager->state.pointerFocus.lock()); if (*PFOLLOWMOUSE == 1 && PCURRWINDOW && PWINDOW != PCURRWINDOW) simulateMouseMovement(); } - - if (!ISTOUCHPADSCROLL && PWINDOW->isScrollMouseOverridden()) - factor = PWINDOW->getScrollMouse(); - else if (ISTOUCHPADSCROLL && PWINDOW->isScrollTouchpadOverridden()) - factor = PWINDOW->getScrollTouchpad(); + factor = ISTOUCHPADSCROLL ? PWINDOW->getScrollTouchpad() : PWINDOW->getScrollMouse(); } } @@ -939,24 +862,24 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e, SP pointer) { const int interval = factor != 0 ? std::round(120 * (1 / factor)) : 120; // reset the accumulator when timeout is reached or direction/axis has changed - if (std::signbit(e.deltaDiscrete) != m_scrollWheelState.lastEventSign || e.axis != m_scrollWheelState.lastEventAxis || - e.timeMs - m_scrollWheelState.lastEventTime > 500 /* 500ms taken from libinput default timeout */) { + if (std::signbit(e.deltaDiscrete) != m_ScrollWheelState.lastEventSign || e.axis != m_ScrollWheelState.lastEventAxis || + e.timeMs - m_ScrollWheelState.lastEventTime > 500 /* 500ms taken from libinput default timeout */) { - m_scrollWheelState.accumulatedScroll = 0; + m_ScrollWheelState.accumulatedScroll = 0; // send 1 discrete on first event for responsiveness discrete = std::copysign(1, e.deltaDiscrete); } else discrete = 0; - for (int ac = m_scrollWheelState.accumulatedScroll; ac >= interval; ac -= interval) { + for (int ac = m_ScrollWheelState.accumulatedScroll; ac >= interval; ac -= interval) { discrete += std::copysign(1, e.deltaDiscrete); - m_scrollWheelState.accumulatedScroll -= interval; + m_ScrollWheelState.accumulatedScroll -= interval; } - m_scrollWheelState.lastEventSign = std::signbit(e.deltaDiscrete); - m_scrollWheelState.lastEventAxis = e.axis; - m_scrollWheelState.lastEventTime = e.timeMs; - m_scrollWheelState.accumulatedScroll += std::abs(e.deltaDiscrete); + m_ScrollWheelState.lastEventSign = std::signbit(e.deltaDiscrete); + m_ScrollWheelState.lastEventAxis = e.axis; + m_ScrollWheelState.lastEventTime = e.timeMs; + m_ScrollWheelState.accumulatedScroll += std::abs(e.deltaDiscrete); delta = 15.0 * discrete * factor; } @@ -966,110 +889,93 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e, SP pointer) { int32_t deltaDiscrete = std::abs(discrete) != 0 && std::abs(discrete) < 1 ? std::copysign(1, discrete) : std::round(discrete); g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, delta, deltaDiscrete, value120, e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); - - const bool deferPointerFrame = e.source == WL_POINTER_AXIS_SOURCE_FINGER || e.source == WL_POINTER_AXIS_SOURCE_CONTINUOUS; - if (deferPointerFrame) { - m_pointerAxisFramePending = true; - return; - } - - m_pointerAxisFramePending = false; - g_pSeatManager->sendPointerFrame(); -} - -void CInputManager::onPointerFrame() { - if (!m_pointerAxisFramePending) - return; - - m_pointerAxisFramePending = false; - g_pSeatManager->sendPointerFrame(); } Vector2D CInputManager::getMouseCoordsInternal() { return g_pPointerManager->position(); } -void CInputManager::newKeyboard(SP keeb) { - const auto PNEWKEYBOARD = m_keyboards.emplace_back(keeb); - - setupKeyboard(PNEWKEYBOARD); - - Log::logger->log(Log::DEBUG, "New keyboard created, pointers Hypr: {:x}", rc(PNEWKEYBOARD.get())); -} - void CInputManager::newKeyboard(SP keyboard) { - const auto PNEWKEYBOARD = m_keyboards.emplace_back(CKeyboard::create(keyboard)); + const auto PNEWKEYBOARD = m_vKeyboards.emplace_back(CKeyboard::create(keyboard)); setupKeyboard(PNEWKEYBOARD); - Log::logger->log(Log::DEBUG, "New keyboard created, pointers Hypr: {:x} and AQ: {:x}", rc(PNEWKEYBOARD.get()), rc(keyboard.get())); + Debug::log(LOG, "New keyboard created, pointers Hypr: {:x} and AQ: {:x}", (uintptr_t)PNEWKEYBOARD.get(), (uintptr_t)keyboard.get()); } void CInputManager::newVirtualKeyboard(SP keyboard) { - const auto PNEWKEYBOARD = m_keyboards.emplace_back(CVirtualKeyboard::create(keyboard)); + const auto PNEWKEYBOARD = m_vKeyboards.emplace_back(CVirtualKeyboard::create(keyboard)); setupKeyboard(PNEWKEYBOARD); - Log::logger->log(Log::DEBUG, "New virtual keyboard created at {:x}", rc(PNEWKEYBOARD.get())); + Debug::log(LOG, "New virtual keyboard created at {:x}", (uintptr_t)PNEWKEYBOARD.get()); } void CInputManager::setupKeyboard(SP keeb) { static auto PDPMS = CConfigValue("misc:key_press_enables_dpms"); - m_hids.emplace_back(keeb); + m_vHIDs.emplace_back(keeb); try { - keeb->m_hlName = getNameForNewDevice(keeb->m_deviceName); + keeb->hlName = getNameForNewDevice(keeb->deviceName); } catch (std::exception& e) { - Log::logger->log(Log::ERR, "Keyboard had no name???"); // logic error + Debug::log(ERR, "Keyboard had no name???"); // logic error } - keeb->m_events.destroy.listenStatic([this, keeb = keeb.get()] { - auto PKEEB = keeb->m_self.lock(); + keeb->events.destroy.registerStaticListener( + [this](void* owner, std::any data) { + auto PKEEB = ((IKeyboard*)owner)->self.lock(); - if (!PKEEB) - return; + if (!PKEEB) + return; - destroyKeyboard(PKEEB); - Log::logger->log(Log::DEBUG, "Destroyed keyboard {:x}", rc(keeb)); - }); + destroyKeyboard(PKEEB); + Debug::log(LOG, "Destroyed keyboard {:x}", (uintptr_t)owner); + }, + keeb.get()); - keeb->m_keyboardEvents.key.listenStatic([this, keeb = keeb.get()](const IKeyboard::SKeyEvent& event) { - auto PKEEB = keeb->m_self.lock(); + keeb->keyboardEvents.key.registerStaticListener( + [this](void* owner, std::any data) { + auto PKEEB = ((IKeyboard*)owner)->self.lock(); - onKeyboardKey(event, PKEEB); + onKeyboardKey(data, PKEEB); - if (PKEEB->m_enabled) - PROTO::idle->onActivity(); + if (PKEEB->enabled) + PROTO::idle->onActivity(); - if (PKEEB->m_enabled && *PDPMS && !g_pCompositor->m_dpmsStateOn) - g_pKeybindManager->dpms("on"); - }); + if (PKEEB->enabled && *PDPMS && !g_pCompositor->m_bDPMSStateON) + g_pKeybindManager->dpms("on"); + }, + keeb.get()); - keeb->m_keyboardEvents.modifiers.listenStatic([this, keeb = keeb.get()] { - auto PKEEB = keeb->m_self.lock(); + keeb->keyboardEvents.modifiers.registerStaticListener( + [this](void* owner, std::any data) { + auto PKEEB = ((IKeyboard*)owner)->self.lock(); - onKeyboardMod(PKEEB); + onKeyboardMod(PKEEB); - if (PKEEB->m_enabled) - PROTO::idle->onActivity(); + if (PKEEB->enabled) + PROTO::idle->onActivity(); - if (PKEEB->m_enabled && *PDPMS && !g_pCompositor->m_dpmsStateOn) - g_pKeybindManager->dpms("on"); - }); + if (PKEEB->enabled && *PDPMS && !g_pCompositor->m_bDPMSStateON) + g_pKeybindManager->dpms("on"); + }, + keeb.get()); - keeb->m_keyboardEvents.keymap.listenStatic([keeb = keeb.get()] { - auto PKEEB = keeb->m_self.lock(); - const auto LAYOUT = PKEEB->getActiveLayout(); + keeb->keyboardEvents.keymap.registerStaticListener( + [](void* owner, std::any data) { + auto PKEEB = ((IKeyboard*)owner)->self.lock(); + const auto LAYOUT = PKEEB->getActiveLayout(); - if (PKEEB == g_pSeatManager->m_keyboard) { - g_pSeatManager->updateActiveKeyboardData(); - g_pKeybindManager->m_keyToCodeCache.clear(); - } + if (PKEEB == g_pSeatManager->keyboard) { + g_pSeatManager->updateActiveKeyboardData(); + g_pKeybindManager->m_mKeyToCodeCache.clear(); + } - g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", PKEEB->m_hlName + "," + LAYOUT}); - Event::bus()->m_events.input.keyboard.layout.emit(PKEEB, LAYOUT); - }); + g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", PKEEB->hlName + "," + LAYOUT}); + EMIT_HOOK_EVENT("activeLayout", (std::vector{PKEEB, LAYOUT})); + }, + keeb.get()); disableAllKeyboards(false); @@ -1078,25 +984,21 @@ void CInputManager::setupKeyboard(SP keeb) { g_pSeatManager->setKeyboard(keeb); keeb->updateLEDs(); - - // in case m_lastFocus was set without a keyboard - if (m_keyboards.size() == 1 && Desktop::focusState()->surface()) - g_pSeatManager->setKeyboardFocus(Desktop::focusState()->surface()); } void CInputManager::setKeyboardLayout() { - for (auto const& k : m_keyboards) + for (auto const& k : m_vKeyboards) applyConfigToKeyboard(k); g_pKeybindManager->updateXKBTranslationState(); } void CInputManager::applyConfigToKeyboard(SP pKeyboard) { - auto devname = pKeyboard->m_hlName; + auto devname = pKeyboard->hlName; const auto HASCONFIG = g_pConfigManager->deviceConfigExists(devname); - Log::logger->log(Log::DEBUG, "ApplyConfigToKeyboard for \"{}\", hasconfig: {}", devname, sc(HASCONFIG)); + Debug::log(LOG, "ApplyConfigToKeyboard for \"{}\", hasconfig: {}", devname, (int)HASCONFIG); const auto REPEATRATE = g_pConfigManager->getDeviceInt(devname, "repeat_rate", "input:repeat_rate"); const auto REPEATDELAY = g_pConfigManager->getDeviceInt(devname, "repeat_delay", "input:repeat_delay"); @@ -1111,44 +1013,16 @@ void CInputManager::applyConfigToKeyboard(SP pKeyboard) { const auto VARIANT = g_pConfigManager->getDeviceString(devname, "kb_variant", "input:kb_variant"); const auto OPTIONS = g_pConfigManager->getDeviceString(devname, "kb_options", "input:kb_options"); - const auto ENABLED = HASCONFIG ? g_pConfigManager->getDeviceInt(devname, "enabled") : true; - const auto ALLOWBINDS = HASCONFIG ? g_pConfigManager->getDeviceInt(devname, "keybinds") : true; + const auto ENABLED = HASCONFIG ? g_pConfigManager->getDeviceInt(devname, "enabled") : true; - pKeyboard->m_enabled = ENABLED; - pKeyboard->m_resolveBindsBySym = RESOLVEBINDSBYSYM; - pKeyboard->m_allowBinds = ALLOWBINDS; - - const auto PERM = g_pDynamicPermissionManager->clientPermissionModeWithString(-1, pKeyboard->m_hlName, PERMISSION_TYPE_KEYBOARD); - - if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { - - // disallow while pending - pKeyboard->m_allowed = false; - - const auto PROMISE = g_pDynamicPermissionManager->promiseFor(-1, pKeyboard->m_hlName, PERMISSION_TYPE_KEYBOARD); - if (!PROMISE) - Log::logger->log(Log::ERR, "BUG THIS: No promise for client permission for keyboard"); - else { - PROMISE->then([k = WP{pKeyboard}](SP> r) { - if (r->hasError()) { - Log::logger->log(Log::ERR, "BUG THIS: No permission returned for keyboard"); - return; - } - - if (!k) - return; - - k->m_allowed = r->result() == PERMISSION_RULE_ALLOW_MODE_ALLOW; - }); - } - } else - pKeyboard->m_allowed = PERM == PERMISSION_RULE_ALLOW_MODE_ALLOW; + pKeyboard->enabled = ENABLED; + pKeyboard->resolveBindsBySym = RESOLVEBINDSBYSYM; try { - if (NUMLOCKON == pKeyboard->m_numlockOn && REPEATDELAY == pKeyboard->m_repeatDelay && REPEATRATE == pKeyboard->m_repeatRate && RULES == pKeyboard->m_currentRules.rules && - MODEL == pKeyboard->m_currentRules.model && LAYOUT == pKeyboard->m_currentRules.layout && VARIANT == pKeyboard->m_currentRules.variant && - OPTIONS == pKeyboard->m_currentRules.options && FILEPATH == pKeyboard->m_xkbFilePath) { - Log::logger->log(Log::DEBUG, "Not applying config to keyboard, it did not change."); + if (NUMLOCKON == pKeyboard->numlockOn && REPEATDELAY == pKeyboard->repeatDelay && REPEATRATE == pKeyboard->repeatRate && RULES != "" && + RULES == pKeyboard->currentRules.rules && MODEL == pKeyboard->currentRules.model && LAYOUT == pKeyboard->currentRules.layout && + VARIANT == pKeyboard->currentRules.variant && OPTIONS == pKeyboard->currentRules.options && FILEPATH == pKeyboard->xkbFilePath) { + Debug::log(LOG, "Not applying config to keyboard, it did not change."); return; } } catch (std::exception& e) { @@ -1156,97 +1030,93 @@ void CInputManager::applyConfigToKeyboard(SP pKeyboard) { // we can ignore those and just apply } - pKeyboard->m_repeatRate = std::max(0, REPEATRATE); - pKeyboard->m_repeatDelay = std::max(0, REPEATDELAY); - pKeyboard->m_numlockOn = NUMLOCKON; - pKeyboard->m_xkbFilePath = FILEPATH; + pKeyboard->repeatRate = std::max(0, REPEATRATE); + pKeyboard->repeatDelay = std::max(0, REPEATDELAY); + pKeyboard->numlockOn = NUMLOCKON; + pKeyboard->xkbFilePath = FILEPATH; + pKeyboard->setKeymap(IKeyboard::SStringRuleNames{LAYOUT, MODEL, VARIANT, OPTIONS, RULES}); const auto LAYOUTSTR = pKeyboard->getActiveLayout(); - g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", pKeyboard->m_hlName + "," + LAYOUTSTR}); - Event::bus()->m_events.input.keyboard.layout.emit(pKeyboard, LAYOUTSTR); + g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", pKeyboard->hlName + "," + LAYOUTSTR}); + EMIT_HOOK_EVENT("activeLayout", (std::vector{pKeyboard, LAYOUTSTR})); - Log::logger->log(Log::DEBUG, "Set the keyboard layout to {} and variant to {} for keyboard \"{}\"", pKeyboard->m_currentRules.layout, pKeyboard->m_currentRules.variant, - pKeyboard->m_hlName); + Debug::log(LOG, "Set the keyboard layout to {} and variant to {} for keyboard \"{}\"", pKeyboard->currentRules.layout, pKeyboard->currentRules.variant, pKeyboard->hlName); } void CInputManager::newVirtualMouse(SP mouse) { - const auto PMOUSE = m_pointers.emplace_back(CVirtualPointer::create(mouse)); + const auto PMOUSE = m_vPointers.emplace_back(CVirtualPointer::create(mouse)); setupMouse(PMOUSE); - Log::logger->log(Log::DEBUG, "New virtual mouse created"); -} - -void CInputManager::newMouse(SP mouse) { - m_pointers.emplace_back(mouse); - - setupMouse(mouse); - - Log::logger->log(Log::DEBUG, "New mouse created, pointer Hypr: {:x}", rc(mouse.get())); + Debug::log(LOG, "New virtual mouse created"); } void CInputManager::newMouse(SP mouse) { - const auto PMOUSE = m_pointers.emplace_back(CMouse::create(mouse)); + const auto PMOUSE = m_vPointers.emplace_back(CMouse::create(mouse)); setupMouse(PMOUSE); - Log::logger->log(Log::DEBUG, "New mouse created, pointer AQ: {:x}", rc(mouse.get())); + Debug::log(LOG, "New mouse created, pointer AQ: {:x}", (uintptr_t)mouse.get()); } void CInputManager::setupMouse(SP mauz) { - m_hids.emplace_back(mauz); + m_vHIDs.emplace_back(mauz); try { - mauz->m_hlName = getNameForNewDevice(mauz->m_deviceName); + mauz->hlName = getNameForNewDevice(mauz->deviceName); } catch (std::exception& e) { - Log::logger->log(Log::ERR, "Mouse had no name???"); // logic error + Debug::log(ERR, "Mouse had no name???"); // logic error } if (mauz->aq() && mauz->aq()->getLibinputHandle()) { const auto LIBINPUTDEV = mauz->aq()->getLibinputHandle(); - Log::logger->log(Log::DEBUG, "New mouse has libinput sens {:.2f} ({:.2f}) with accel profile {} ({})", libinput_device_config_accel_get_speed(LIBINPUTDEV), - libinput_device_config_accel_get_default_speed(LIBINPUTDEV), sc(libinput_device_config_accel_get_profile(LIBINPUTDEV)), - sc(libinput_device_config_accel_get_default_profile(LIBINPUTDEV))); + Debug::log(LOG, "New mouse has libinput sens {:.2f} ({:.2f}) with accel profile {} ({})", libinput_device_config_accel_get_speed(LIBINPUTDEV), + libinput_device_config_accel_get_default_speed(LIBINPUTDEV), (int)libinput_device_config_accel_get_profile(LIBINPUTDEV), + (int)libinput_device_config_accel_get_default_profile(LIBINPUTDEV)); } g_pPointerManager->attachPointer(mauz); - mauz->m_connected = true; + mauz->connected = true; setPointerConfigs(); - mauz->m_events.destroy.listenStatic([this, PMOUSE = mauz.get()] { destroyPointer(PMOUSE->m_self.lock()); }); + mauz->events.destroy.registerStaticListener( + [this](void* mouse, std::any data) { + const auto PMOUSE = (IPointer*)mouse; + + if (!PMOUSE) + return; + + destroyPointer(PMOUSE->self.lock()); + }, + mauz.get()); g_pSeatManager->setMouse(mauz); - m_lastCursorMovement.reset(); + m_tmrLastCursorMovement.reset(); } void CInputManager::setPointerConfigs() { - for (auto const& m : m_pointers) { - auto devname = m->m_hlName; + for (auto const& m : m_vPointers) { + auto devname = m->hlName; const auto HASCONFIG = g_pConfigManager->deviceConfigExists(devname); if (HASCONFIG) { const auto ENABLED = g_pConfigManager->getDeviceInt(devname, "enabled"); - if (ENABLED && !m->m_connected) { + if (ENABLED && !m->connected) { g_pPointerManager->attachPointer(m); - m->m_connected = true; - } else if (!ENABLED && m->m_connected) { + m->connected = true; + } else if (!ENABLED && m->connected) { g_pPointerManager->detachPointer(m); - m->m_connected = false; + m->connected = false; } } - if (g_pConfigManager->deviceConfigExplicitlySet(devname, "scroll_factor")) - m->m_scrollFactor = std::clamp(g_pConfigManager->getDeviceFloat(devname, "scroll_factor", "input:scroll_factor"), 0.F, 100.F); - else - m->m_scrollFactor = std::nullopt; - if (m->aq() && m->aq()->getLibinputHandle()) { const auto LIBINPUTDEV = m->aq()->getLibinputHandle(); @@ -1271,16 +1141,16 @@ void CInputManager::setPointerConfigs() { libinput_device_config_middle_emulation_set_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); const auto TAP_MAP = g_pConfigManager->getDeviceString(devname, "tap_button_map", "input:touchpad:tap_button_map"); - if (TAP_MAP.empty() || TAP_MAP == "lrm") + if (TAP_MAP == "" || TAP_MAP == "lrm") libinput_device_config_tap_set_button_map(LIBINPUTDEV, LIBINPUT_CONFIG_TAP_MAP_LRM); else if (TAP_MAP == "lmr") libinput_device_config_tap_set_button_map(LIBINPUTDEV, LIBINPUT_CONFIG_TAP_MAP_LMR); else - Log::logger->log(Log::WARN, "Tap button mapping unknown"); + Debug::log(WARN, "Tap button mapping unknown"); } const auto SCROLLMETHOD = g_pConfigManager->getDeviceString(devname, "scroll_method", "input:scroll_method"); - if (SCROLLMETHOD.empty()) { + if (SCROLLMETHOD == "") { libinput_device_config_scroll_set_method(LIBINPUTDEV, libinput_device_config_scroll_get_default_method(LIBINPUTDEV)); } else if (SCROLLMETHOD == "no_scroll") { libinput_device_config_scroll_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_SCROLL_NO_SCROLL); @@ -1291,7 +1161,7 @@ void CInputManager::setPointerConfigs() { } else if (SCROLLMETHOD == "on_button_down") { libinput_device_config_scroll_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN); } else { - Log::logger->log(Log::WARN, "Scroll method unknown"); + Debug::log(WARN, "Scroll method unknown"); } if (g_pConfigManager->getDeviceInt(devname, "tap-and-drag", "input:touchpad:tap-and-drag") == 0) @@ -1299,10 +1169,10 @@ void CInputManager::setPointerConfigs() { else libinput_device_config_tap_set_drag_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_DRAG_ENABLED); - const auto TAP_DRAG_LOCK = g_pConfigManager->getDeviceInt(devname, "drag_lock", "input:touchpad:drag_lock"); - if (TAP_DRAG_LOCK >= 0 && TAP_DRAG_LOCK <= 2) { - libinput_device_config_tap_set_drag_lock_enabled(LIBINPUTDEV, sc(TAP_DRAG_LOCK)); - } + if (g_pConfigManager->getDeviceInt(devname, "drag_lock", "input:touchpad:drag_lock") == 0) + libinput_device_config_tap_set_drag_lock_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_DRAG_LOCK_DISABLED); + else + libinput_device_config_tap_set_drag_lock_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED); if (libinput_device_config_tap_get_finger_count(LIBINPUTDEV)) // this is for tapping (like on a laptop) libinput_device_config_tap_set_enabled(LIBINPUTDEV, @@ -1318,26 +1188,17 @@ void CInputManager::setPointerConfigs() { libinput_device_config_scroll_set_natural_scroll_enabled(LIBINPUTDEV, g_pConfigManager->getDeviceInt(devname, "natural_scroll", "input:natural_scroll")); } - if (libinput_device_config_3fg_drag_get_finger_count(LIBINPUTDEV) >= 3) { - const auto DRAG_3FG_STATE = sc(g_pConfigManager->getDeviceInt(devname, "drag_3fg", "input:touchpad:drag_3fg")); - libinput_device_config_3fg_drag_set_enabled(LIBINPUTDEV, DRAG_3FG_STATE); - } - if (libinput_device_config_dwt_is_available(LIBINPUTDEV)) { - const auto DWT = sc(g_pConfigManager->getDeviceInt(devname, "disable_while_typing", "input:touchpad:disable_while_typing") != 0); + const auto DWT = + static_cast(g_pConfigManager->getDeviceInt(devname, "disable_while_typing", "input:touchpad:disable_while_typing") != 0); libinput_device_config_dwt_set_enabled(LIBINPUTDEV, DWT); } const auto LIBINPUTSENS = std::clamp(g_pConfigManager->getDeviceFloat(devname, "sensitivity", "input:sensitivity"), -1.f, 1.f); libinput_device_config_accel_set_speed(LIBINPUTDEV, LIBINPUTSENS); - if (libinput_device_config_rotation_is_available(LIBINPUTDEV)) { - const auto ROTATION = std::clamp(g_pConfigManager->getDeviceInt(devname, "rotation", "input:rotation"), 0, 359); - libinput_device_config_rotation_set_angle(LIBINPUTDEV, ROTATION); - } - - m->m_flipX = g_pConfigManager->getDeviceInt(devname, "flip_x", "input:touchpad:flip_x") != 0; - m->m_flipY = g_pConfigManager->getDeviceInt(devname, "flip_y", "input:touchpad:flip_y") != 0; + m->flipX = g_pConfigManager->getDeviceInt(devname, "flip_x", "input:touchpad:flip_x") != 0; + m->flipY = g_pConfigManager->getDeviceInt(devname, "flip_y", "input:touchpad:flip_y") != 0; const auto ACCELPROFILE = g_pConfigManager->getDeviceString(devname, "accel_profile", "input:accel_profile"); const auto SCROLLPOINTS = g_pConfigManager->getDeviceString(devname, "scroll_points", "input:scroll_points"); @@ -1370,15 +1231,15 @@ void CInputManager::setPointerConfigs() { } libinput_config_accel_set_points(CONFIG, LIBINPUT_ACCEL_TYPE_SCROLL, scrollStep, scrollPoints.size(), scrollPoints.data()); - } catch (std::exception& e) { Log::logger->log(Log::ERR, "Invalid values in scroll_points"); } + } catch (std::exception& e) { Debug::log(ERR, "Invalid values in scroll_points"); } } libinput_config_accel_set_points(CONFIG, LIBINPUT_ACCEL_TYPE_MOTION, accelStep, accelPoints.size(), accelPoints.data()); libinput_device_config_accel_apply(LIBINPUTDEV, CONFIG); libinput_config_accel_destroy(CONFIG); - } catch (std::exception& e) { Log::logger->log(Log::ERR, "Invalid values in custom accel profile"); } + } catch (std::exception& e) { Debug::log(ERR, "Invalid values in custom accel profile"); } } else { - Log::logger->log(Log::WARN, "Unknown acceleration profile, falling back to default"); + Debug::log(WARN, "Unknown acceleration profile, falling back to default"); } const auto SCROLLBUTTON = g_pConfigManager->getDeviceInt(devname, "scroll_button", "input:scroll_button"); @@ -1390,24 +1251,24 @@ void CInputManager::setPointerConfigs() { libinput_device_config_scroll_set_button_lock(LIBINPUTDEV, SCROLLBUTTONLOCK == 0 ? LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED : LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED); - Log::logger->log(Log::DEBUG, "Applied config to mouse {}, sens {:.2f}", m->m_hlName, LIBINPUTSENS); + Debug::log(LOG, "Applied config to mouse {}, sens {:.2f}", m->hlName, LIBINPUTSENS); } } } static void removeFromHIDs(WP hid) { - std::erase_if(g_pInputManager->m_hids, [hid](const auto& e) { return e.expired() || e == hid; }); + std::erase_if(g_pInputManager->m_vHIDs, [hid](const auto& e) { return e.expired() || e == hid; }); g_pInputManager->updateCapabilities(); } void CInputManager::destroyKeyboard(SP pKeyboard) { - Log::logger->log(Log::DEBUG, "Keyboard at {:x} removed", rc(pKeyboard.get())); + Debug::log(LOG, "Keyboard at {:x} removed", (uintptr_t)pKeyboard.get()); - std::erase_if(m_keyboards, [pKeyboard](const auto& other) { return other == pKeyboard; }); + std::erase_if(m_vKeyboards, [pKeyboard](const auto& other) { return other == pKeyboard; }); - if (!m_keyboards.empty()) { + if (m_vKeyboards.size() > 0) { bool found = false; - for (auto const& k : m_keyboards | std::views::reverse) { + for (auto const& k : m_vKeyboards | std::views::reverse) { if (!k) continue; @@ -1425,52 +1286,52 @@ void CInputManager::destroyKeyboard(SP pKeyboard) { } void CInputManager::destroyPointer(SP mouse) { - Log::logger->log(Log::DEBUG, "Pointer at {:x} removed", rc(mouse.get())); + Debug::log(LOG, "Pointer at {:x} removed", (uintptr_t)mouse.get()); - std::erase_if(m_pointers, [mouse](const auto& other) { return other == mouse; }); + std::erase_if(m_vPointers, [mouse](const auto& other) { return other == mouse; }); - g_pSeatManager->setMouse(!m_pointers.empty() ? m_pointers.front() : nullptr); + g_pSeatManager->setMouse(m_vPointers.size() > 0 ? m_vPointers.front() : nullptr); - if (!g_pSeatManager->m_mouse.expired()) + if (!g_pSeatManager->mouse.expired()) unconstrainMouse(); removeFromHIDs(mouse); } void CInputManager::destroyTouchDevice(SP touch) { - Log::logger->log(Log::DEBUG, "Touch device at {:x} removed", rc(touch.get())); + Debug::log(LOG, "Touch device at {:x} removed", (uintptr_t)touch.get()); - std::erase_if(m_touches, [touch](const auto& other) { return other == touch; }); + std::erase_if(m_vTouches, [touch](const auto& other) { return other == touch; }); removeFromHIDs(touch); } void CInputManager::destroyTablet(SP tablet) { - Log::logger->log(Log::DEBUG, "Tablet device at {:x} removed", rc(tablet.get())); + Debug::log(LOG, "Tablet device at {:x} removed", (uintptr_t)tablet.get()); - std::erase_if(m_tablets, [tablet](const auto& other) { return other == tablet; }); + std::erase_if(m_vTablets, [tablet](const auto& other) { return other == tablet; }); removeFromHIDs(tablet); } void CInputManager::destroyTabletTool(SP tool) { - Log::logger->log(Log::DEBUG, "Tablet tool at {:x} removed", rc(tool.get())); + Debug::log(LOG, "Tablet tool at {:x} removed", (uintptr_t)tool.get()); - std::erase_if(m_tabletTools, [tool](const auto& other) { return other == tool; }); + std::erase_if(m_vTabletTools, [tool](const auto& other) { return other == tool; }); removeFromHIDs(tool); } void CInputManager::destroyTabletPad(SP pad) { - Log::logger->log(Log::DEBUG, "Tablet pad at {:x} removed", rc(pad.get())); + Debug::log(LOG, "Tablet pad at {:x} removed", (uintptr_t)pad.get()); - std::erase_if(m_tabletPads, [pad](const auto& other) { return other == pad; }); + std::erase_if(m_vTabletPads, [pad](const auto& other) { return other == pad; }); removeFromHIDs(pad); } void CInputManager::updateKeyboardsLeds(SP pKeyboard) { - if (!pKeyboard || pKeyboard->isVirtual()) + if (!pKeyboard) return; std::optional leds = pKeyboard->getLEDs(); @@ -1478,69 +1339,33 @@ void CInputManager::updateKeyboardsLeds(SP pKeyboard) { if (!leds.has_value()) return; - for (auto const& k : m_keyboards) { + for (auto const& k : m_vKeyboards) { k->updateLEDs(leds.value()); } } -void CInputManager::onKeyboardKey(const IKeyboard::SKeyEvent& event, SP pKeyboard) { - if (!pKeyboard->m_enabled || !pKeyboard->m_allowed) +void CInputManager::onKeyboardKey(std::any event, SP pKeyboard) { + if (!pKeyboard->enabled) return; - const bool DISALLOWACTION = pKeyboard->isVirtual() && shouldIgnoreVirtualKeyboard(pKeyboard); + const bool DISALLOWACTION = pKeyboard->isVirtual() && shouldIgnoreVirtualKeyboard(pKeyboard); - const auto IME = m_relay.m_inputMethod.lock(); - const bool HASIME = IME && IME->hasGrab(); - const bool USEIME = HASIME && !DISALLOWACTION; + const auto EMAP = std::unordered_map{{"keyboard", pKeyboard}, {"event", event}}; + EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP); - Event::SCallbackInfo info; - Event::bus()->m_events.input.keyboard.key.emit(event, info); - if (info.cancelled) - return; + bool passEvent = DISALLOWACTION || g_pKeybindManager->onKeyEvent(event, pKeyboard); - bool passEvent = DISALLOWACTION; - - if (!DISALLOWACTION) - passEvent = g_pKeybindManager->onKeyEvent(event, pKeyboard); + auto e = std::any_cast(event); if (passEvent) { - auto state = event.state; - auto pressed = state == WL_KEYBOARD_KEY_STATE_PRESSED; + const auto IME = m_sIMERelay.m_pIME.lock(); - // use merged keys states when sending to ime or when sending to seat with no ime - // if passing from ime, send keys directly without merging - if (USEIME || !HASIME) { - const auto ANYPRESSED = shareKeyFromAllKBs(event.keycode, pressed); - - // do not turn released event into pressed event (when one keyboard has a key released but some - // other keyboard still has the key pressed) - // maybe we should keep track of pressed keys for inputs like m_pressed for seat outputs below, - // to avoid duplicate pressed events, but this should work well enough - if (!pressed && ANYPRESSED) - return; - - pressed = ANYPRESSED; - state = pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED; - } - - if (USEIME) { + if (IME && IME->hasGrab() && !DISALLOWACTION) { IME->setKeyboard(pKeyboard); - IME->sendKey(event.timeMs, event.keycode, state); + IME->sendKey(e.timeMs, e.keycode, e.state); } else { - const auto CONTAINS = std::ranges::contains(m_pressed, event.keycode); - - if (CONTAINS && pressed) - return; - if (!CONTAINS && !pressed) - return; - - if (CONTAINS) - std::erase(m_pressed, event.keycode); - else - m_pressed.emplace_back(event.keycode); - g_pSeatManager->setKeyboard(pKeyboard); - g_pSeatManager->sendKeyboardKey(event.timeMs, event.keycode, state); + g_pSeatManager->sendKeyboardKey(e.timeMs, e.keycode, e.state); } updateKeyboardsLeds(pKeyboard); @@ -1548,26 +1373,19 @@ void CInputManager::onKeyboardKey(const IKeyboard::SKeyEvent& event, SP pKeyboard) { - if (!pKeyboard->m_enabled) + if (!pKeyboard->enabled) return; const bool DISALLOWACTION = pKeyboard->isVirtual() && shouldIgnoreVirtualKeyboard(pKeyboard); - const auto IME = m_relay.m_inputMethod.lock(); - const bool HASIME = IME && IME->hasGrab(); - const bool USEIME = HASIME && !DISALLOWACTION; + const auto ALLMODS = accumulateModsFromAllKBs(); - auto MODS = pKeyboard->m_modifiersState; + auto MODS = pKeyboard->modifiersState; + MODS.depressed = ALLMODS; - // use merged mods states when sending to ime or when sending to seat with no ime - // if passing from ime, send mods directly without merging - if (USEIME || !HASIME) { - const auto ALLMODS = shareModsFromAllKBs(MODS.depressed); - MODS.depressed = ALLMODS; - m_lastMods = MODS.depressed; // for hyprland keybinds use; not for sending to seat - } + const auto IME = m_sIMERelay.m_pIME.lock(); - if (USEIME) { + if (IME && IME->hasGrab() && !DISALLOWACTION) { IME->setKeyboard(pKeyboard); IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group); } else { @@ -1577,48 +1395,35 @@ void CInputManager::onKeyboardMod(SP pKeyboard) { updateKeyboardsLeds(pKeyboard); - if (pKeyboard->m_modifiersState.group != pKeyboard->m_activeLayout) { - pKeyboard->m_activeLayout = pKeyboard->m_modifiersState.group; + if (pKeyboard->modifiersState.group != pKeyboard->activeLayout) { + pKeyboard->activeLayout = pKeyboard->modifiersState.group; const auto LAYOUT = pKeyboard->getActiveLayout(); - Log::logger->log(Log::DEBUG, "LAYOUT CHANGED TO {} GROUP {}", LAYOUT, MODS.group); + Debug::log(LOG, "LAYOUT CHANGED TO {} GROUP {}", LAYOUT, MODS.group); - g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", pKeyboard->m_hlName + "," + LAYOUT}); - Event::bus()->m_events.input.keyboard.layout.emit(pKeyboard, LAYOUT); + g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", pKeyboard->hlName + "," + LAYOUT}); + EMIT_HOOK_EVENT("activeLayout", (std::vector{pKeyboard, LAYOUT})); } } bool CInputManager::shouldIgnoreVirtualKeyboard(SP pKeyboard) { - if (!pKeyboard) - return true; - if (!pKeyboard->isVirtual()) return false; - const auto CLIENT = pKeyboard->getClient(); + CVirtualKeyboard* vk = (CVirtualKeyboard*)pKeyboard.get(); - const auto DISALLOWACTION = CLIENT && !m_relay.m_inputMethod.expired() && m_relay.m_inputMethod->grabClient() == CLIENT; - - if (DISALLOWACTION) - pKeyboard->setShareStatesAuto(false); - - return DISALLOWACTION; + return !pKeyboard || (!m_sIMERelay.m_pIME.expired() && m_sIMERelay.m_pIME->grabClient() == vk->getClient()); } -void CInputManager::refocus(std::optional overridePos) { - mouseMoveUnified(0, true, false, overridePos); +void CInputManager::refocus() { + mouseMoveUnified(0, true); } -bool CInputManager::refocusLastWindow(PHLMONITOR pMonitor) { - if (!m_exclusiveLSes.empty()) { - Log::logger->log(Log::DEBUG, "CInputManager::refocusLastWindow: ignoring, exclusive LS present."); - return false; - } - +void CInputManager::refocusLastWindow(PHLMONITOR pMonitor) { if (!pMonitor) { refocus(); - return true; + return; } Vector2D surfaceCoords; @@ -1627,44 +1432,46 @@ bool CInputManager::refocusLastWindow(PHLMONITOR pMonitor) { g_pInputManager->releaseAllMouseButtons(); + // first try for an exclusive layer + if (!m_dExclusiveLSes.empty()) + foundSurface = m_dExclusiveLSes[m_dExclusiveLSes.size() - 1]->surface->resource(); + // then any surfaces above windows on the same monitor if (!foundSurface) { - foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &surfaceCoords, &pFoundLayerSurface); - if (pFoundLayerSurface && pFoundLayerSurface->m_interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) + if (pFoundLayerSurface && pFoundLayerSurface->interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) foundSurface = nullptr; } if (!foundSurface) { - foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &surfaceCoords, &pFoundLayerSurface); - if (pFoundLayerSurface && pFoundLayerSurface->m_interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) + if (pFoundLayerSurface && pFoundLayerSurface->interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) foundSurface = nullptr; } - if (!foundSurface && Desktop::focusState()->window() && Desktop::focusState()->window()->m_workspace && Desktop::focusState()->window()->m_workspace->isVisibleNotCovered()) { + if (!foundSurface && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_pWorkspace && g_pCompositor->m_pLastWindow->m_pWorkspace->isVisibleNotCovered()) { // then the last focused window if we're on the same workspace as it - const auto PLASTWINDOW = Desktop::focusState()->window(); - Desktop::focusState()->fullWindowFocus(PLASTWINDOW, Desktop::FOCUS_REASON_FFM); + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); + g_pCompositor->focusWindow(PLASTWINDOW); } else { // otherwise fall back to a normal refocus. - if (foundSurface && !foundSurface->m_hlSurface->keyboardFocusable()) { - const auto PLASTWINDOW = Desktop::focusState()->window(); - Desktop::focusState()->fullWindowFocus(PLASTWINDOW, Desktop::FOCUS_REASON_FFM); + if (foundSurface && !foundSurface->hlSurface->keyboardFocusable()) { + const auto PLASTWINDOW = g_pCompositor->m_pLastWindow.lock(); + g_pCompositor->focusWindow(PLASTWINDOW); } refocus(); } - - return true; } void CInputManager::unconstrainMouse() { - if (g_pSeatManager->m_mouse.expired()) + if (g_pSeatManager->mouse.expired()) return; - for (auto const& c : m_constraints) { + for (auto const& c : m_vConstraints) { const auto C = c.lock(); if (!C) @@ -1678,9 +1485,9 @@ void CInputManager::unconstrainMouse() { } bool CInputManager::isConstrained() { - return std::ranges::any_of(m_constraints, [](auto const& c) { + return std::any_of(m_vConstraints.begin(), m_vConstraints.end(), [](auto const& c) { const auto constraint = c.lock(); - return constraint && constraint->isActive() && constraint->owner()->resource() == Desktop::focusState()->surface(); + return constraint && constraint->isActive() && constraint->owner()->resource() == g_pCompositor->m_pLastFocus; }); } @@ -1688,7 +1495,7 @@ bool CInputManager::isLocked() { if (!isConstrained()) return false; - const auto SURF = Desktop::View::CWLSurface::fromResource(Desktop::focusState()->surface()); + const auto SURF = CWLSurface::fromResource(g_pCompositor->m_pLastFocus.lock()); const auto CONSTRAINT = SURF ? SURF->constraint() : nullptr; return CONSTRAINT && CONSTRAINT->isLocked(); @@ -1697,7 +1504,7 @@ bool CInputManager::isLocked() { void CInputManager::updateCapabilities() { uint32_t caps = 0; - for (auto const& h : m_hids) { + for (auto const& h : m_vHIDs) { if (h.expired()) continue; @@ -1705,52 +1512,18 @@ void CInputManager::updateCapabilities() { } g_pSeatManager->updateCapabilities(caps); - m_capabilities = caps; + m_uiCapabilities = caps; } -const std::vector& CInputManager::getKeysFromAllKBs() { - return m_pressed; -} +uint32_t CInputManager::accumulateModsFromAllKBs() { -uint32_t CInputManager::getModsFromAllKBs() { - return m_lastMods; -} - -bool CInputManager::shareKeyFromAllKBs(uint32_t key, bool pressed) { - bool finalState = pressed; - - if (finalState) - return finalState; - - for (auto const& kb : m_keyboards) { - if (!kb->shareStates()) - continue; + uint32_t finalMask = 0; + for (auto const& kb : m_vKeyboards) { if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb)) continue; - if (!kb->m_enabled) - continue; - - const bool PRESSED = kb->getPressed(key); - if (PRESSED) - return PRESSED; - } - - return finalState; -} - -uint32_t CInputManager::shareModsFromAllKBs(uint32_t depressed) { - uint32_t finalMask = depressed; - - for (auto const& kb : m_keyboards) { - if (!kb->shareStates()) - continue; - - if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb)) - continue; - - if (!kb->m_enabled) + if (!kb->enabled) continue; finalMask |= kb->getModifiers(); @@ -1761,37 +1534,39 @@ uint32_t CInputManager::shareModsFromAllKBs(uint32_t depressed) { void CInputManager::disableAllKeyboards(bool virt) { - for (auto const& k : m_keyboards) { + for (auto const& k : m_vKeyboards) { if (k->isVirtual() != virt) continue; - k->m_active = false; + k->active = false; } } void CInputManager::newTouchDevice(SP pDevice) { - const auto PNEWDEV = m_touches.emplace_back(CTouchDevice::create(pDevice)); - m_hids.emplace_back(PNEWDEV); + const auto PNEWDEV = m_vTouches.emplace_back(CTouchDevice::create(pDevice)); + m_vHIDs.emplace_back(PNEWDEV); try { - PNEWDEV->m_hlName = getNameForNewDevice(PNEWDEV->m_deviceName); + PNEWDEV->hlName = getNameForNewDevice(PNEWDEV->deviceName); } catch (std::exception& e) { - Log::logger->log(Log::ERR, "Touch Device had no name???"); // logic error + Debug::log(ERR, "Touch Device had no name???"); // logic error } setTouchDeviceConfigs(PNEWDEV); g_pPointerManager->attachTouch(PNEWDEV); - PNEWDEV->m_events.destroy.listenStatic([this, dev = PNEWDEV.get()] { - auto PDEV = dev->m_self.lock(); + PNEWDEV->events.destroy.registerStaticListener( + [this](void* owner, std::any data) { + auto PDEV = ((ITouch*)owner)->self.lock(); - if (!PDEV) - return; + if (!PDEV) + return; - destroyTouchDevice(PDEV); - }); + destroyTouchDevice(PDEV); + }, + PNEWDEV.get()); - Log::logger->log(Log::DEBUG, "New touch device added at {:x}", rc(PNEWDEV.get())); + Debug::log(LOG, "New touch device added at {:x}", (uintptr_t)PNEWDEV.get()); } void CInputManager::setTouchDeviceConfigs(SP dev) { @@ -1799,20 +1574,20 @@ void CInputManager::setTouchDeviceConfigs(SP dev) { if (PTOUCHDEV->aq() && PTOUCHDEV->aq()->getLibinputHandle()) { const auto LIBINPUTDEV = PTOUCHDEV->aq()->getLibinputHandle(); - const auto ENABLED = g_pConfigManager->getDeviceInt(PTOUCHDEV->m_hlName, "enabled", "input:touchdevice:enabled"); + const auto ENABLED = g_pConfigManager->getDeviceInt(PTOUCHDEV->hlName, "enabled", "input:touchdevice:enabled"); const auto mode = ENABLED ? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED : LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; if (libinput_device_config_send_events_get_mode(LIBINPUTDEV) != mode) libinput_device_config_send_events_set_mode(LIBINPUTDEV, mode); if (libinput_device_config_calibration_has_matrix(LIBINPUTDEV)) { - Log::logger->log(Log::DEBUG, "Setting calibration matrix for device {}", PTOUCHDEV->m_hlName); + Debug::log(LOG, "Setting calibration matrix for device {}", PTOUCHDEV->hlName); // default value of transform being -1 means it's unset. - const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->m_hlName, "transform", "input:touchdevice:transform"), -1, 7); + const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(PTOUCHDEV->hlName, "transform", "input:touchdevice:transform"), -1, 7); if (ROTATION > -1) libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]); } - auto output = g_pConfigManager->getDeviceString(PTOUCHDEV->m_hlName, "output", "input:touchdevice:output"); + auto output = g_pConfigManager->getDeviceString(PTOUCHDEV->hlName, "output", "input:touchdevice:output"); bool bound = !output.empty() && output != STRVAL_EMPTY; const bool AUTODETECT = output == "[[Auto]]"; if (!bound && AUTODETECT) { @@ -1823,13 +1598,13 @@ void CInputManager::setTouchDeviceConfigs(SP dev) { // bound = true; // } } - PTOUCHDEV->m_boundOutput = bound ? output : ""; - const auto PMONITOR = bound ? g_pCompositor->getMonitorFromName(output) : nullptr; + PTOUCHDEV->boundOutput = bound ? output : ""; + const auto PMONITOR = bound ? g_pCompositor->getMonitorFromName(output) : nullptr; if (PMONITOR) { - Log::logger->log(Log::DEBUG, "Binding touch device {} to output {}", PTOUCHDEV->m_hlName, PMONITOR->m_name); + Debug::log(LOG, "Binding touch device {} to output {}", PTOUCHDEV->hlName, PMONITOR->szName); // wlr_cursor_map_input_to_output(g_pCompositor->m_sWLRCursor, &PTOUCHDEV->wlr()->base, PMONITOR->output); } else if (bound) - Log::logger->log(Log::ERR, "Failed to bind touch device {} to output '{}': monitor not found", PTOUCHDEV->m_hlName, output); + Debug::log(ERR, "Failed to bind touch device {} to output '{}': monitor not found", PTOUCHDEV->hlName, output); } }; @@ -1838,22 +1613,22 @@ void CInputManager::setTouchDeviceConfigs(SP dev) { return; } - for (auto const& m : m_touches) { + for (auto const& m : m_vTouches) { setConfig(m); } } void CInputManager::setTabletConfigs() { - for (auto const& t : m_tablets) { + for (auto const& t : m_vTablets) { if (t->aq()->getLibinputHandle()) { - const auto NAME = t->m_hlName; + const auto NAME = t->hlName; const auto LIBINPUTDEV = t->aq()->getLibinputHandle(); const auto RELINPUT = g_pConfigManager->getDeviceInt(NAME, "relative_input", "input:tablet:relative_input"); - t->m_relativeInput = RELINPUT; + t->relativeInput = RELINPUT; const int ROTATION = std::clamp(g_pConfigManager->getDeviceInt(NAME, "transform", "input:tablet:transform"), -1, 7); - Log::logger->log(Log::DEBUG, "Setting calibration matrix for device {}", NAME); + Debug::log(LOG, "Setting calibration matrix for device {}", NAME); if (ROTATION > -1) libinput_device_config_calibration_set_matrix(LIBINPUTDEV, MATRICES[ROTATION]); @@ -1864,60 +1639,77 @@ void CInputManager::setTabletConfigs() { const auto OUTPUT = g_pConfigManager->getDeviceString(NAME, "output", "input:tablet:output"); if (OUTPUT != STRVAL_EMPTY) { - Log::logger->log(Log::DEBUG, "Binding tablet {} to output {}", NAME, OUTPUT); - t->m_boundOutput = OUTPUT; + Debug::log(LOG, "Binding tablet {} to output {}", NAME, OUTPUT); + t->boundOutput = OUTPUT; } else - t->m_boundOutput = ""; + t->boundOutput = ""; const auto REGION_POS = g_pConfigManager->getDeviceVec(NAME, "region_position", "input:tablet:region_position"); const auto REGION_SIZE = g_pConfigManager->getDeviceVec(NAME, "region_size", "input:tablet:region_size"); - t->m_boundBox = {REGION_POS, REGION_SIZE}; + t->boundBox = {REGION_POS, REGION_SIZE}; const auto ABSOLUTE_REGION_POS = g_pConfigManager->getDeviceInt(NAME, "absolute_region_position", "input:tablet:absolute_region_position"); - t->m_absolutePos = ABSOLUTE_REGION_POS; + t->absolutePos = ABSOLUTE_REGION_POS; const auto ACTIVE_AREA_SIZE = g_pConfigManager->getDeviceVec(NAME, "active_area_size", "input:tablet:active_area_size"); const auto ACTIVE_AREA_POS = g_pConfigManager->getDeviceVec(NAME, "active_area_position", "input:tablet:active_area_position"); if (ACTIVE_AREA_SIZE.x != 0 || ACTIVE_AREA_SIZE.y != 0) { - // Rotations with an odd index (90 and 270 degrees, and their flipped variants) swap the X and Y axes. - // Use swapped dimensions when the axes are rotated, otherwise keep the original ones. - const Vector2D effectivePhysicalSize = (ROTATION % 2) ? Vector2D{t->aq()->physicalSize.y, t->aq()->physicalSize.x} : t->aq()->physicalSize; - - // Scale the active area coordinates into normalized space (0–1) using the effective dimensions. - t->m_activeArea = CBox{ACTIVE_AREA_POS.x / effectivePhysicalSize.x, ACTIVE_AREA_POS.y / effectivePhysicalSize.y, - (ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / effectivePhysicalSize.x, (ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / effectivePhysicalSize.y}; + t->activeArea = CBox{ACTIVE_AREA_POS.x / t->aq()->physicalSize.x, ACTIVE_AREA_POS.y / t->aq()->physicalSize.y, + (ACTIVE_AREA_POS.x + ACTIVE_AREA_SIZE.x) / t->aq()->physicalSize.x, (ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t->aq()->physicalSize.y}; } } } } void CInputManager::newSwitch(SP pDevice) { - const auto PNEWDEV = &m_switches.emplace_back(); + const auto PNEWDEV = &m_lSwitches.emplace_back(); PNEWDEV->pDevice = pDevice; - Log::logger->log(Log::DEBUG, "New switch with name \"{}\" added", pDevice->getName()); + Debug::log(LOG, "New switch with name \"{}\" added", pDevice->getName()); - PNEWDEV->listeners.destroy = pDevice->events.destroy.listen([this, PNEWDEV] { destroySwitch(PNEWDEV); }); + PNEWDEV->listeners.destroy = pDevice->events.destroy.registerListener([this, PNEWDEV](std::any d) { destroySwitch(PNEWDEV); }); - PNEWDEV->listeners.fire = pDevice->events.fire.listen([PNEWDEV](const Aquamarine::ISwitch::SFireEvent& event) { + PNEWDEV->listeners.fire = pDevice->events.fire.registerListener([PNEWDEV](std::any d) { const auto NAME = PNEWDEV->pDevice->getName(); + const auto E = std::any_cast(d); - Log::logger->log(Log::DEBUG, "Switch {} fired, triggering binds.", NAME); + Debug::log(LOG, "Switch {} fired, triggering binds.", NAME); g_pKeybindManager->onSwitchEvent(NAME); - if (event.enable) { - Log::logger->log(Log::DEBUG, "Switch {} turn on, triggering binds.", NAME); + if (E.enable) { + Debug::log(LOG, "Switch {} turn on, triggering binds.", NAME); g_pKeybindManager->onSwitchOnEvent(NAME); } else { - Log::logger->log(Log::DEBUG, "Switch {} turn off, triggering binds.", NAME); + Debug::log(LOG, "Switch {} turn off, triggering binds.", NAME); g_pKeybindManager->onSwitchOffEvent(NAME); } }); } void CInputManager::destroySwitch(SSwitchDevice* pDevice) { - m_switches.remove(*pDevice); + m_lSwitches.remove(*pDevice); +} + +void CInputManager::setCursorImageUntilUnset(std::string name) { + g_pHyprRenderer->setCursorFromName(name); + m_bCursorImageOverridden = true; + m_sCursorSurfaceInfo.inUse = false; +} + +void CInputManager::unsetCursorImage() { + if (!m_bCursorImageOverridden) + return; + + m_bCursorImageOverridden = false; + restoreCursorIconToApp(); +} + +std::string CInputManager::deviceNameToInternalString(std::string in) { + std::replace(in.begin(), in.end(), ' ', '-'); + std::replace(in.begin(), in.end(), '\n', '-'); + std::transform(in.begin(), in.end(), in.begin(), ::tolower); + return in; } std::string CInputManager::getNameForNewDevice(std::string internalName) { @@ -1927,32 +1719,39 @@ std::string CInputManager::getNameForNewDevice(std::string internalName) { auto makeNewName = [&]() { return (proposedNewName.empty() ? "unknown-device" : proposedNewName) + (dupeno == 0 ? "" : ("-" + std::to_string(dupeno))); }; - while (std::ranges::find_if(m_hids, [&](const auto& other) { return other->m_hlName == makeNewName(); }) != m_hids.end()) + while (std::find_if(m_vHIDs.begin(), m_vHIDs.end(), [&](const auto& other) { return other->hlName == makeNewName(); }) != m_vHIDs.end()) dupeno++; return makeNewName(); } void CInputManager::releaseAllMouseButtons() { - const auto buttonsCopy = m_currentlyHeldButtons; + const auto buttonsCopy = m_lCurrentlyHeldButtons; if (PROTO::data->dndActive()) return; + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + for (auto const& mb : buttonsCopy) { - g_pSeatManager->sendPointerButton(Time::millis(Time::steadyNow()), mb, WL_POINTER_BUTTON_STATE_RELEASED); + g_pSeatManager->sendPointerButton(now.tv_sec * 1000 + now.tv_nsec / 1000000, mb, WL_POINTER_BUTTON_STATE_RELEASED); } - m_currentlyHeldButtons.clear(); + m_lCurrentlyHeldButtons.clear(); } void CInputManager::setCursorIconOnBorder(PHLWINDOW w) { - // ignore X11 OR windows, they shouldn't be touched - if (w->m_isX11 && w->isX11OverrideRedirect()) { - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); + // do not override cursor icons set by mouse binds + if (g_pInputManager->currentlyDraggedWindow.expired()) { + m_eBorderIconDirection = BORDERICON_NONE; return; } + // ignore X11 OR windows, they shouldn't be touched + if (w->m_bIsX11 && w->isX11OverrideRedirect()) + return; + static auto PEXTENDBORDERGRAB = CConfigValue("general:extend_border_grab_area"); const int BORDERSIZE = w->getRealBorderSize(); const int ROUNDING = w->rounding(); @@ -1967,13 +1766,13 @@ void CInputManager::setCursorIconOnBorder(PHLWINDOW w) { if (w->hasPopupAt(mouseCoords)) direction = BORDERICON_NONE; - else if (!boxFullGrabInput.containsPoint(mouseCoords) || (!m_currentlyHeldButtons.empty() && !g_layoutManager->dragController()->target())) + else if (!boxFullGrabInput.containsPoint(mouseCoords) || (!m_lCurrentlyHeldButtons.empty() && currentlyDraggedWindow.expired())) direction = BORDERICON_NONE; else { bool onDeco = false; - for (auto const& wd : w->m_windowDecorations) { + for (auto const& wd : w->m_dWindowDecorations) { if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT)) continue; @@ -2027,93 +1826,27 @@ void CInputManager::setCursorIconOnBorder(PHLWINDOW w) { } } - if (direction == m_borderIconDirection) + if (direction == m_eBorderIconDirection) return; - m_borderIconDirection = direction; + m_eBorderIconDirection = direction; switch (direction) { - case BORDERICON_NONE: Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; - case BORDERICON_UP: Cursor::overrideController->setOverride("top_side", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; - case BORDERICON_DOWN: Cursor::overrideController->setOverride("bottom_side", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; - case BORDERICON_LEFT: Cursor::overrideController->setOverride("left_side", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; - case BORDERICON_RIGHT: Cursor::overrideController->setOverride("right_side", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; - case BORDERICON_UP_LEFT: Cursor::overrideController->setOverride("top_left_corner", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; - case BORDERICON_DOWN_LEFT: Cursor::overrideController->setOverride("bottom_left_corner", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; - case BORDERICON_UP_RIGHT: Cursor::overrideController->setOverride("top_right_corner", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; - case BORDERICON_DOWN_RIGHT: Cursor::overrideController->setOverride("bottom_right_corner", Cursor::CURSOR_OVERRIDE_WINDOW_EDGE); break; + case BORDERICON_NONE: unsetCursorImage(); break; + case BORDERICON_UP: setCursorImageUntilUnset("top_side"); break; + case BORDERICON_DOWN: setCursorImageUntilUnset("bottom_side"); break; + case BORDERICON_LEFT: setCursorImageUntilUnset("left_side"); break; + case BORDERICON_RIGHT: setCursorImageUntilUnset("right_side"); break; + case BORDERICON_UP_LEFT: setCursorImageUntilUnset("top_left_corner"); break; + case BORDERICON_DOWN_LEFT: setCursorImageUntilUnset("bottom_left_corner"); break; + case BORDERICON_UP_RIGHT: setCursorImageUntilUnset("top_right_corner"); break; + case BORDERICON_DOWN_RIGHT: setCursorImageUntilUnset("bottom_right_corner"); break; } } void CInputManager::recheckMouseWarpOnMouseInput() { static auto PWARPFORNONMOUSE = CConfigValue("cursor:warp_back_after_non_mouse_input"); - if (!m_lastInputMouse && *PWARPFORNONMOUSE) - g_pPointerManager->warpTo(m_lastMousePos); -} - -void CInputManager::onSwipeBegin(IPointer::SSwipeBeginEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.gesture.swipe.begin.emit(e, info); - if (info.cancelled) - return; - - g_pTrackpadGestures->gestureBegin(e); - - PROTO::pointerGestures->swipeBegin(e.timeMs, e.fingers); -} - -void CInputManager::onSwipeUpdate(IPointer::SSwipeUpdateEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.gesture.swipe.update.emit(e, info); - if (info.cancelled) - return; - - g_pTrackpadGestures->gestureUpdate(e); - - PROTO::pointerGestures->swipeUpdate(e.timeMs, e.delta); -} - -void CInputManager::onSwipeEnd(IPointer::SSwipeEndEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.gesture.swipe.end.emit(e, info); - if (info.cancelled) - return; - - g_pTrackpadGestures->gestureEnd(e); - - PROTO::pointerGestures->swipeEnd(e.timeMs, e.cancelled); -} - -void CInputManager::onPinchBegin(IPointer::SPinchBeginEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.gesture.pinch.begin.emit(e, info); - if (info.cancelled) - return; - - g_pTrackpadGestures->gestureBegin(e); - - PROTO::pointerGestures->pinchBegin(e.timeMs, e.fingers); -} - -void CInputManager::onPinchUpdate(IPointer::SPinchUpdateEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.gesture.pinch.update.emit(e, info); - if (info.cancelled) - return; - - g_pTrackpadGestures->gestureUpdate(e); - - PROTO::pointerGestures->pinchUpdate(e.timeMs, e.delta, e.scale, e.rotation); -} - -void CInputManager::onPinchEnd(IPointer::SPinchEndEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.gesture.pinch.end.emit(e, info); - if (info.cancelled) - return; - - g_pTrackpadGestures->gestureEnd(e); - - PROTO::pointerGestures->pinchEnd(e.timeMs, e.cancelled); + if (!m_bLastInputMouse && *PWARPFORNONMOUSE) + g_pPointerManager->warpTo(m_vLastMousePos); } diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index ca235a23..7774a6db 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -4,18 +4,16 @@ #include #include #include "../../helpers/WLClasses.hpp" -#include "../../helpers/time/Timer.hpp" +#include "../../helpers/Timer.hpp" #include "InputMethodRelay.hpp" #include "../../helpers/signal/Signal.hpp" -#include "../../desktop/view/WLSurface.hpp" #include "../../devices/IPointer.hpp" #include "../../devices/ITouch.hpp" -#include "../../devices/IKeyboard.hpp" #include "../../devices/Tablet.hpp" #include "../SessionLockManager.hpp" -#include "../SeatManager.hpp" class CPointerConstraint; +class CWindow; class CIdleInhibitor; class CVirtualKeyboardV1Resource; class CVirtualPointerV1Resource; @@ -90,15 +88,12 @@ class CInputManager { void onMouseMoved(IPointer::SMotionEvent); void onMouseWarp(IPointer::SMotionAbsoluteEvent); void onMouseButton(IPointer::SButtonEvent); - void onMouseWheel(IPointer::SAxisEvent, SP pointer = nullptr); - void onPointerFrame(); - void onKeyboardKey(const IKeyboard::SKeyEvent&, SP); + void onMouseWheel(IPointer::SAxisEvent); + void onKeyboardKey(std::any, SP); void onKeyboardMod(SP); - void newKeyboard(SP); void newKeyboard(SP); void newVirtualKeyboard(SP); - void newMouse(SP); void newMouse(SP); void newVirtualMouse(SP); void newTouchDevice(SP); @@ -118,8 +113,8 @@ class CInputManager { bool isLocked(); Vector2D getMouseCoordsInternal(); - void refocus(std::optional overridePos = std::nullopt); - bool refocusLastWindow(PHLMONITOR pMonitor); + void refocus(); + void refocusLastWindow(PHLMONITOR pMonitor); void simulateMouseMovement(); void sendMotionEventsToFocused(); @@ -133,7 +128,7 @@ class CInputManager { void setClickMode(eClickBehaviorMode); eClickBehaviorMode getClickMode(); - void processMouseRequest(const CSeatManager::SSetCursorEvent& event); + void processMouseRequest(std::any e); void onTouchDown(ITouch::SDownEvent); void onTouchUp(ITouch::SUpEvent); @@ -143,74 +138,79 @@ class CInputManager { void onSwipeEnd(IPointer::SSwipeEndEvent); void onSwipeUpdate(IPointer::SSwipeUpdateEvent); - void onPinchBegin(IPointer::SPinchBeginEvent); - void onPinchUpdate(IPointer::SPinchUpdateEvent); - void onPinchEnd(IPointer::SPinchEndEvent); - void onTabletAxis(CTablet::SAxisEvent); void onTabletProximity(CTablet::SProximityEvent); void onTabletTip(CTablet::STipEvent); void onTabletButton(CTablet::SButtonEvent); - STouchData m_touchData; + STouchData m_sTouchData; + + // for dragging floating windows + PHLWINDOWREF currentlyDraggedWindow; + eMouseBindMode dragMode = MBIND_INVALID; + bool m_bWasDraggingWindow = false; // for refocus to be forced - PHLWINDOWREF m_forcedFocus; + PHLWINDOWREF m_pForcedFocus; - std::vector> m_keyboards; - std::vector> m_pointers; - std::vector> m_touches; - std::vector> m_tablets; - std::vector> m_tabletTools; - std::vector> m_tabletPads; - std::vector> m_hids; // general container for all HID devices connected to the input manager. + std::vector> m_vKeyboards; + std::vector> m_vPointers; + std::vector> m_vTouches; + std::vector> m_vTablets; + std::vector> m_vTabletTools; + std::vector> m_vTabletPads; + std::vector> m_vHIDs; // general container for all HID devices connected to the input manager. // Switches - std::list m_switches; + std::list m_lSwitches; // Exclusive layer surfaces - std::vector m_exclusiveLSes; + std::vector m_dExclusiveLSes; // constraints - std::vector> m_constraints; + std::vector> m_vConstraints; // void newIdleInhibitor(std::any); void recheckIdleInhibitorStatus(); bool isWindowInhibiting(const PHLWINDOW& pWindow, bool onlyHl = true); - CTimer m_lastCursorMovement; + SSwipeGesture m_sActiveSwipe; - CInputMethodRelay m_relay; + CTimer m_tmrLastCursorMovement; + + CInputMethodRelay m_sIMERelay; // for shared mods - const std::vector& getKeysFromAllKBs(); - uint32_t getModsFromAllKBs(); + uint32_t accumulateModsFromAllKBs(); // for virtual keyboards: whether we should respect them as normal ones - bool shouldIgnoreVirtualKeyboard(SP); + bool shouldIgnoreVirtualKeyboard(SP); + // for special cursors that we choose + void setCursorImageUntilUnset(std::string); + void unsetCursorImage(); + + std::string deviceNameToInternalString(std::string); std::string getNameForNewDevice(std::string); void releaseAllMouseButtons(); // for some bugs in follow mouse 0 - bool m_lastFocusOnLS = false; + bool m_bLastFocusOnLS = false; + bool m_bLastFocusOnIMEPopup = false; // for hard input e.g. clicks - bool m_hardInput = false; + bool m_bHardInput = false; // for hiding cursor on touch - bool m_lastInputTouch = false; - - // for hiding cursor on tablet - bool m_lastInputTablet = false; + bool m_bLastInputTouch = false; // for tracking mouse refocus - PHLWINDOWREF m_lastMouseFocus; + PHLWINDOWREF m_pLastMouseFocus; // - bool m_emptyFocusCursorSet = false; + bool m_bEmptyFocusCursorSet = false; private: // Listeners @@ -220,15 +220,14 @@ class CInputManager { CHyprSignalListener newVirtualKeyboard; CHyprSignalListener newVirtualMouse; CHyprSignalListener setCursor; - CHyprSignalListener overrideChanged; - } m_listeners; + } m_sListeners; - bool m_cursorImageOverridden = false; - eBorderIconDirection m_borderIconDirection = BORDERICON_NONE; + bool m_bCursorImageOverridden = false; + eBorderIconDirection m_eBorderIconDirection = BORDERICON_NONE; // for click behavior override - eClickBehaviorMode m_clickBehavior = CLICKMODE_DEFAULT; - Vector2D m_lastCursorPosFloored = Vector2D(); + eClickBehaviorMode m_ecbClickBehavior = CLICKMODE_DEFAULT; + Vector2D m_vLastCursorPosFloored = Vector2D(); void setupKeyboard(SP keeb); void setupMouse(SP mauz); @@ -240,9 +239,9 @@ class CInputManager { void disableAllKeyboards(bool virt = false); - uint32_t m_capabilities = 0; + uint32_t m_uiCapabilities = 0; - void mouseMoveUnified(uint32_t, bool refocus = false, bool mouse = false, std::optional overridePos = std::nullopt); + void mouseMoveUnified(uint32_t, bool refocus = false, bool mouse = false); void recheckMouseWarpOnMouseInput(); SP ensureTabletToolPresent(SP); @@ -250,21 +249,21 @@ class CInputManager { void applyConfigToKeyboard(SP); // this will be set after a refocus() - WP m_foundSurfaceToFocus; - PHLLSREF m_foundLSToFocus; - PHLWINDOWREF m_foundWindowToFocus; + WP m_pFoundSurfaceToFocus; + PHLLSREF m_pFoundLSToFocus; + PHLWINDOWREF m_pFoundWindowToFocus; // used for warping back after non-mouse input - Vector2D m_lastMousePos = {}; - double m_mousePosDelta = 0; - bool m_lastInputMouse = true; + Vector2D m_vLastMousePos = {}; + double m_fMousePosDelta = 0; + bool m_bLastInputMouse = true; // for holding focus on buttons held - bool m_focusHeldByButtons = false; - bool m_refocusHeldByButtons = false; + bool m_bFocusHeldByButtons = false; + bool m_bRefocusHeldByButtons = false; // for releasing mouse buttons - std::list m_currentlyHeldButtons; + std::list m_lCurrentlyHeldButtons; // idle inhibitors struct SIdleInhibitor { @@ -272,18 +271,27 @@ class CInputManager { bool nonDesktop = false; CHyprSignalListener surfaceDestroyListener; }; - std::vector> m_idleInhibitors; + std::vector> m_vIdleInhibitors; - void setBorderCursorIcon(eBorderIconDirection); - void setCursorIconOnBorder(PHLWINDOW w); + // swipe + void beginWorkspaceSwipe(); + void updateWorkspaceSwipe(double); + void endWorkspaceSwipe(); + + void setBorderCursorIcon(eBorderIconDirection); + void setCursorIconOnBorder(PHLWINDOW w); + + // temporary. Obeys setUntilUnset. + void setCursorImageOverride(const std::string& name); // cursor surface struct { - bool hidden = false; // null surface = hidden - SP wlSurface; - Vector2D vHotspot; - std::string name; // if not empty, means set by name. - } m_cursorSurfaceInfo; + bool hidden = false; // null surface = hidden + SP wlSurface; + Vector2D vHotspot; + std::string name; // if not empty, means set by name. + bool inUse = false; + } m_sCursorSurfaceInfo; void restoreCursorIconToApp(); // no-op if restored @@ -293,17 +301,10 @@ class CInputManager { bool lastEventAxis = false; uint32_t lastEventTime = 0; uint32_t accumulatedScroll = 0; - } m_scrollWheelState; - bool m_pointerAxisFramePending = false; - - bool shareKeyFromAllKBs(uint32_t key, bool pressed); - uint32_t shareModsFromAllKBs(uint32_t depressed); - std::vector m_pressed; - uint32_t m_lastMods = 0; + } m_ScrollWheelState; friend class CKeybindManager; - friend class Desktop::View::CWLSurface; - friend class CWorkspaceSwipeGesture; + friend class CWLSurface; }; inline UP g_pInputManager; diff --git a/src/managers/input/InputMethodPopup.cpp b/src/managers/input/InputMethodPopup.cpp index 9a891213..db08c026 100644 --- a/src/managers/input/InputMethodPopup.cpp +++ b/src/managers/input/InputMethodPopup.cpp @@ -7,30 +7,30 @@ #include "../../helpers/Monitor.hpp" #include "../../render/Renderer.hpp" -CInputPopup::CInputPopup(SP popup_) : m_popup(popup_) { - m_listeners.commit = popup_->m_events.commit.listen([this] { onCommit(); }); - m_listeners.map = popup_->m_events.map.listen([this] { onMap(); }); - m_listeners.unmap = popup_->m_events.unmap.listen([this] { onUnmap(); }); - m_listeners.destroy = popup_->m_events.destroy.listen([this] { onDestroy(); }); - m_surface = Desktop::View::CWLSurface::create(); - m_surface->assign(popup_->surface()); +CInputPopup::CInputPopup(SP popup_) : popup(popup_) { + listeners.commit = popup_->events.commit.registerListener([this](std::any d) { onCommit(); }); + listeners.map = popup_->events.map.registerListener([this](std::any d) { onMap(); }); + listeners.unmap = popup_->events.unmap.registerListener([this](std::any d) { onUnmap(); }); + listeners.destroy = popup_->events.destroy.registerListener([this](std::any d) { onDestroy(); }); + surface = CWLSurface::create(); + surface->assign(popup_->surface()); } -SP CInputPopup::queryOwner() { - const auto FOCUSED = g_pInputManager->m_relay.getFocusedTextInput(); +SP CInputPopup::queryOwner() { + const auto FOCUSED = g_pInputManager->m_sIMERelay.getFocusedTextInput(); if (!FOCUSED) return nullptr; - return Desktop::View::CWLSurface::fromResource(FOCUSED->focusedSurface()); + return CWLSurface::fromResource(FOCUSED->focusedSurface()); } void CInputPopup::onDestroy() { - g_pInputManager->m_relay.removePopup(this); + g_pInputManager->m_sIMERelay.removePopup(this); } void CInputPopup::onMap() { - Log::logger->log(Log::DEBUG, "Mapped an IME Popup"); + Debug::log(LOG, "Mapped an IME Popup"); updateBox(); damageEntire(); @@ -40,11 +40,11 @@ void CInputPopup::onMap() { if (!PMONITOR) return; - PROTO::fractional->sendScale(m_surface->resource(), PMONITOR->m_scale); + PROTO::fractional->sendScale(surface->resource(), PMONITOR->scale); } void CInputPopup::onUnmap() { - Log::logger->log(Log::DEBUG, "Unmapped an IME Popup"); + Debug::log(LOG, "Unmapped an IME Popup"); damageEntire(); } @@ -57,7 +57,7 @@ void CInputPopup::damageEntire() { const auto OWNER = queryOwner(); if (!OWNER) { - Log::logger->log(Log::ERR, "BUG THIS: No owner in imepopup::damageentire"); + Debug::log(ERR, "BUG THIS: No owner in imepopup::damageentire"); return; } CBox box = globalBox(); @@ -68,20 +68,20 @@ void CInputPopup::damageSurface() { const auto OWNER = queryOwner(); if (!OWNER) { - Log::logger->log(Log::ERR, "BUG THIS: No owner in imepopup::damagesurface"); + Debug::log(ERR, "BUG THIS: No owner in imepopup::damagesurface"); return; } Vector2D pos = globalBox().pos(); - g_pHyprRenderer->damageSurface(m_surface->resource(), pos.x, pos.y); + g_pHyprRenderer->damageSurface(surface->resource(), pos.x, pos.y); } void CInputPopup::updateBox() { - if (!m_popup->m_mapped) + if (!popup->mapped) return; const auto OWNER = queryOwner(); - const auto PFOCUSEDTI = g_pInputManager->m_relay.getFocusedTextInput(); + const auto PFOCUSEDTI = g_pInputManager->m_sIMERelay.getFocusedTextInput(); if (!PFOCUSEDTI) return; @@ -99,50 +99,43 @@ void CInputPopup::updateBox() { if (!cursorRect) { Vector2D coords = OWNER ? OWNER->getSurfaceBoxGlobal().value_or(CBox{0, 0, 500, 500}).pos() : Vector2D{0, 0}; parentBox = {coords, {500, 500}}; - cursorBoxParent = {0, 0, sc(parentBox.w), sc(parentBox.h)}; + cursorBoxParent = {0, 0, (int)parentBox.w, (int)parentBox.h}; } - Vector2D currentPopupSize = m_surface->getViewporterCorrectedSize() / m_surface->resource()->m_current.scale; + Vector2D currentPopupSize = surface->getViewporterCorrectedSize() / surface->resource()->current.scale; PHLMONITOR pMonitor = g_pCompositor->getMonitorFromVector(parentBox.middle()); Vector2D popupOffset(0, 0); - if (parentBox.y + cursorBoxParent.y + cursorBoxParent.height + currentPopupSize.y > pMonitor->m_position.y + pMonitor->m_size.y) + if (parentBox.y + cursorBoxParent.y + cursorBoxParent.height + currentPopupSize.y > pMonitor->vecPosition.y + pMonitor->vecSize.y) popupOffset.y -= currentPopupSize.y; else popupOffset.y = cursorBoxParent.height; - double popupOverflow = parentBox.x + cursorBoxParent.x + currentPopupSize.x - (pMonitor->m_position.x + pMonitor->m_size.x); + double popupOverflow = parentBox.x + cursorBoxParent.x + currentPopupSize.x - (pMonitor->vecPosition.x + pMonitor->vecSize.x); if (popupOverflow > 0) popupOffset.x -= popupOverflow; CBox cursorBoxLocal({-popupOffset.x, -popupOffset.y}, cursorBoxParent.size()); - m_popup->sendInputRectangle(cursorBoxLocal); + popup->sendInputRectangle(cursorBoxLocal); - CBox popupBoxParent(cursorBoxParent.pos() + popupOffset, currentPopupSize); - const bool boxChanged = popupBoxParent != m_lastBoxLocal; - if (boxChanged) - damageEntire(); // damage the old location before updating - - m_lastBoxLocal = popupBoxParent; - - // Since a redraw request is not always sent when only the position is updated, - // a manual redraw may be required in some cases. - if (boxChanged) + CBox popupBoxParent(cursorBoxParent.pos() + popupOffset, currentPopupSize); + if (popupBoxParent != lastBoxLocal) { damageEntire(); - + lastBoxLocal = popupBoxParent; + } damageSurface(); - if (const auto PM = g_pCompositor->getMonitorFromCursor(); PM && PM->m_id != m_lastMonitor) { - const auto PML = g_pCompositor->getMonitorFromID(m_lastMonitor); + if (const auto PM = g_pCompositor->getMonitorFromCursor(); PM && PM->ID != lastMonitor) { + const auto PML = g_pCompositor->getMonitorFromID(lastMonitor); if (PML) - m_surface->resource()->leave(PML->m_self.lock()); + surface->resource()->leave(PML->self.lock()); - m_surface->resource()->enter(PM->m_self.lock()); + surface->resource()->enter(PM->self.lock()); - m_lastMonitor = PM->m_id; + lastMonitor = PM->ID; } } @@ -150,12 +143,12 @@ CBox CInputPopup::globalBox() { const auto OWNER = queryOwner(); if (!OWNER) { - Log::logger->log(Log::ERR, "BUG THIS: No owner in imepopup::globalbox"); + Debug::log(ERR, "BUG THIS: No owner in imepopup::globalbox"); return {}; } CBox parentBox = OWNER->getSurfaceBoxGlobal().value_or(CBox{0, 0, 500, 500}); - return m_lastBoxLocal.copy().translate(parentBox.pos()); + return lastBoxLocal.copy().translate(parentBox.pos()); } bool CInputPopup::isVecInPopup(const Vector2D& point) { @@ -163,5 +156,5 @@ bool CInputPopup::isVecInPopup(const Vector2D& point) { } SP CInputPopup::getSurface() { - return m_surface->resource(); + return surface->resource(); } diff --git a/src/managers/input/InputMethodPopup.hpp b/src/managers/input/InputMethodPopup.hpp index a7014973..53c11cff 100644 --- a/src/managers/input/InputMethodPopup.hpp +++ b/src/managers/input/InputMethodPopup.hpp @@ -1,6 +1,6 @@ #pragma once -#include "../../desktop/view/WLSurface.hpp" +#include "../../desktop/WLSurface.hpp" #include "../../macros.hpp" #include "../../helpers/math/Math.hpp" #include "../../helpers/signal/Signal.hpp" @@ -22,22 +22,22 @@ class CInputPopup { void onCommit(); private: - SP queryOwner(); - void updateBox(); + SP queryOwner(); + void updateBox(); - void onDestroy(); - void onMap(); - void onUnmap(); + void onDestroy(); + void onMap(); + void onUnmap(); - WP m_popup; - SP m_surface; - CBox m_lastBoxLocal; - MONITORID m_lastMonitor = MONITOR_INVALID; + WP popup; + SP surface; + CBox lastBoxLocal; + MONITORID lastMonitor = MONITOR_INVALID; struct { CHyprSignalListener map; CHyprSignalListener unmap; CHyprSignalListener destroy; CHyprSignalListener commit; - } m_listeners; + } listeners; }; diff --git a/src/managers/input/InputMethodRelay.cpp b/src/managers/input/InputMethodRelay.cpp index 27fd80b6..5f6c4354 100644 --- a/src/managers/input/InputMethodRelay.cpp +++ b/src/managers/input/InputMethodRelay.cpp @@ -1,86 +1,84 @@ #include "InputMethodRelay.hpp" -#include "../../desktop/state/FocusState.hpp" -#include "../../event/EventBus.hpp" +#include "InputManager.hpp" +#include "../../Compositor.hpp" #include "../../protocols/TextInputV3.hpp" #include "../../protocols/TextInputV1.hpp" #include "../../protocols/InputMethodV2.hpp" #include "../../protocols/core/Compositor.hpp" +#include "../../managers/HookSystemManager.hpp" CInputMethodRelay::CInputMethodRelay() { - static auto P = Event::bus()->m_events.input.keyboard.focus.listen([&](SP surf) { onKeyboardFocus(surf); }); + static auto P = + g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast>(param)); }); - m_listeners.newTIV3 = PROTO::textInputV3->m_events.newTextInput.listen([this](const auto& input) { onNewTextInput(input); }); - m_listeners.newTIV1 = PROTO::textInputV1->m_events.newTextInput.listen([this](const auto& input) { onNewTextInput(input); }); - m_listeners.newIME = PROTO::ime->m_events.newIME.listen([this](const auto& ime) { onNewIME(ime); }); + listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(std::any_cast>(ti)); }); + listeners.newTIV1 = PROTO::textInputV1->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(std::any_cast>(ti)); }); + listeners.newIME = PROTO::ime->events.newIME.registerListener([this](std::any ime) { onNewIME(std::any_cast>(ime)); }); } void CInputMethodRelay::onNewIME(SP pIME) { - if (!m_inputMethod.expired()) { - Log::logger->log(Log::ERR, "Cannot register 2 IMEs at once!"); + if (!m_pIME.expired()) { + Debug::log(ERR, "Cannot register 2 IMEs at once!"); pIME->unavailable(); return; } - m_inputMethod = pIME; + m_pIME = pIME; - m_listeners.commitIME = pIME->m_events.onCommit.listen([this] { + listeners.commitIME = pIME->events.onCommit.registerListener([this](std::any d) { const auto PTI = getFocusedTextInput(); if (!PTI) { - Log::logger->log(Log::DEBUG, "No focused TextInput on IME Commit"); + Debug::log(LOG, "No focused TextInput on IME Commit"); return; } - PTI->updateIMEState(m_inputMethod.lock()); + PTI->updateIMEState(m_pIME.lock()); }); - m_listeners.destroyIME = pIME->m_events.destroy.listen([this] { + listeners.destroyIME = pIME->events.destroy.registerListener([this](std::any d) { const auto PTI = getFocusedTextInput(); - Log::logger->log(Log::DEBUG, "IME Destroy"); + Debug::log(LOG, "IME Destroy"); if (PTI) PTI->leave(); - m_inputMethod.reset(); + m_pIME.reset(); }); - m_listeners.newPopup = pIME->m_events.newPopup.listen([this](const SP& popup) { - m_inputMethodPopups.emplace_back(makeUnique(popup)); - Log::logger->log(Log::DEBUG, "New input popup"); + listeners.newPopup = pIME->events.newPopup.registerListener([this](std::any d) { + m_vIMEPopups.emplace_back(makeUnique(std::any_cast>(d))); + + Debug::log(LOG, "New input popup"); }); - if (!Desktop::focusState()->surface()) + if (!g_pCompositor->m_pLastFocus) return; - for (auto const& ti : m_textInputs) { - if (ti->client() != Desktop::focusState()->surface()->client()) + for (auto const& ti : m_vTextInputs) { + if (ti->client() != g_pCompositor->m_pLastFocus->client()) continue; if (ti->isV3()) - ti->enter(Desktop::focusState()->surface()); + ti->enter(g_pCompositor->m_pLastFocus.lock()); else - ti->onEnabled(Desktop::focusState()->surface()); + ti->onEnabled(g_pCompositor->m_pLastFocus.lock()); } } void CInputMethodRelay::removePopup(CInputPopup* pPopup) { - std::erase_if(m_inputMethodPopups, [pPopup](const auto& other) { return other.get() == pPopup; }); + std::erase_if(m_vIMEPopups, [pPopup](const auto& other) { return other.get() == pPopup; }); } CTextInput* CInputMethodRelay::getFocusedTextInput() { - if (!Desktop::focusState()->surface()) + if (!g_pCompositor->m_pLastFocus) return nullptr; - for (auto const& ti : m_textInputs) { - if (ti->focusedSurface() == Desktop::focusState()->surface() && ti->isEnabled()) - return ti.get(); - } - - for (auto const& ti : m_textInputs) { - if (ti->focusedSurface() == Desktop::focusState()->surface()) + for (auto const& ti : m_vTextInputs) { + if (ti->focusedSurface() == g_pCompositor->m_pLastFocus) return ti.get(); } @@ -88,58 +86,58 @@ CTextInput* CInputMethodRelay::getFocusedTextInput() { } void CInputMethodRelay::onNewTextInput(WP tiv3) { - m_textInputs.emplace_back(makeUnique(tiv3)); + m_vTextInputs.emplace_back(makeUnique(tiv3)); } void CInputMethodRelay::onNewTextInput(WP pTIV1) { - m_textInputs.emplace_back(makeUnique(pTIV1)); + m_vTextInputs.emplace_back(makeUnique(pTIV1)); } void CInputMethodRelay::removeTextInput(CTextInput* pInput) { - std::erase_if(m_textInputs, [pInput](const auto& other) { return other.get() == pInput; }); + std::erase_if(m_vTextInputs, [pInput](const auto& other) { return other.get() == pInput; }); } void CInputMethodRelay::updateAllPopups() { - for (auto const& p : m_inputMethodPopups) { + for (auto const& p : m_vIMEPopups) { p->onCommit(); } } void CInputMethodRelay::activateIME(CTextInput* pInput, bool shouldCommit) { - if (m_inputMethod.expired()) + if (m_pIME.expired()) return; - m_inputMethod->activate(); + m_pIME->activate(); if (shouldCommit) commitIMEState(pInput); } void CInputMethodRelay::deactivateIME(CTextInput* pInput, bool shouldCommit) { - if (m_inputMethod.expired()) + if (m_pIME.expired()) return; - m_inputMethod->deactivate(); + m_pIME->deactivate(); if (shouldCommit) commitIMEState(pInput); } void CInputMethodRelay::commitIMEState(CTextInput* pInput) { - if (m_inputMethod.expired()) + if (m_pIME.expired()) return; - pInput->commitStateToIME(m_inputMethod.lock()); + pInput->commitStateToIME(m_pIME.lock()); } void CInputMethodRelay::onKeyboardFocus(SP pSurface) { - if (m_inputMethod.expired()) + if (m_pIME.expired()) return; - if (pSurface == m_lastKbFocus) + if (pSurface == m_pLastKbFocus) return; - m_lastKbFocus = pSurface; + m_pLastKbFocus = pSurface; - for (auto const& ti : m_textInputs) { + for (auto const& ti : m_vTextInputs) { if (!ti->focusedSurface()) continue; @@ -149,7 +147,7 @@ void CInputMethodRelay::onKeyboardFocus(SP pSurface) { if (!pSurface) return; - for (auto const& ti : m_textInputs) { + for (auto const& ti : m_vTextInputs) { if (!ti->isV3()) continue; @@ -161,7 +159,7 @@ void CInputMethodRelay::onKeyboardFocus(SP pSurface) { } CInputPopup* CInputMethodRelay::popupFromCoords(const Vector2D& point) { - for (auto const& p : m_inputMethodPopups) { + for (auto const& p : m_vIMEPopups) { if (p->isVecInPopup(point)) return p.get(); } @@ -170,7 +168,7 @@ CInputPopup* CInputMethodRelay::popupFromCoords(const Vector2D& point) { } CInputPopup* CInputMethodRelay::popupFromSurface(const SP surface) { - for (auto const& p : m_inputMethodPopups) { + for (auto const& p : m_vIMEPopups) { if (p->getSurface() == surface) return p.get(); } diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp index cb631b12..602a939a 100644 --- a/src/managers/input/InputMethodRelay.hpp +++ b/src/managers/input/InputMethodRelay.hpp @@ -37,13 +37,13 @@ class CInputMethodRelay { void updateAllPopups(); - WP m_inputMethod; + WP m_pIME; private: - std::vector> m_textInputs; - std::vector> m_inputMethodPopups; + std::vector> m_vTextInputs; + std::vector> m_vIMEPopups; - WP m_lastKbFocus; + WP m_pLastKbFocus; struct { CHyprSignalListener newTIV3; @@ -52,7 +52,7 @@ class CInputMethodRelay { CHyprSignalListener commitIME; CHyprSignalListener destroyIME; CHyprSignalListener newPopup; - } m_listeners; + } listeners; friend class CHyprRenderer; friend class CInputManager; diff --git a/src/managers/input/Swipe.cpp b/src/managers/input/Swipe.cpp new file mode 100644 index 00000000..b5282e3c --- /dev/null +++ b/src/managers/input/Swipe.cpp @@ -0,0 +1,355 @@ +#include "InputManager.hpp" +#include "../../Compositor.hpp" +#include "../../desktop/LayerSurface.hpp" +#include "../../config/ConfigValue.hpp" +#include "../../managers/HookSystemManager.hpp" +#include "../../render/Renderer.hpp" + +void CInputManager::onSwipeBegin(IPointer::SSwipeBeginEvent e) { + static auto PSWIPE = CConfigValue("gestures:workspace_swipe"); + static auto PSWIPEFINGERS = CConfigValue("gestures:workspace_swipe_fingers"); + static auto PSWIPEMINFINGERS = CConfigValue("gestures:workspace_swipe_min_fingers"); + static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); + + EMIT_HOOK_EVENT_CANCELLABLE("swipeBegin", e); + + if ((!*PSWIPEMINFINGERS && e.fingers != *PSWIPEFINGERS) || (*PSWIPEMINFINGERS && e.fingers < *PSWIPEFINGERS) || *PSWIPE == 0 || g_pSessionLockManager->isSessionLocked()) + return; + + int onMonitor = 0; + for (auto const& w : g_pCompositor->m_vWorkspaces) { + if (w->m_pMonitor == g_pCompositor->m_pLastMonitor && !g_pCompositor->isWorkspaceSpecial(w->m_iID)) + onMonitor++; + } + + if (onMonitor < 2 && !*PSWIPENEW) + return; // disallow swiping when there's 1 workspace on a monitor + + beginWorkspaceSwipe(); +} + +void CInputManager::beginWorkspaceSwipe() { + const auto PWORKSPACE = g_pCompositor->m_pLastMonitor->activeWorkspace; + + Debug::log(LOG, "Starting a swipe from {}", PWORKSPACE->m_szName); + + m_sActiveSwipe.pWorkspaceBegin = PWORKSPACE; + m_sActiveSwipe.delta = 0; + m_sActiveSwipe.pMonitor = g_pCompositor->m_pLastMonitor; + m_sActiveSwipe.avgSpeed = 0; + m_sActiveSwipe.speedPoints = 0; + + if (PWORKSPACE->m_bHasFullscreenWindow) { + for (auto const& ls : g_pCompositor->m_pLastMonitor->m_aLayerSurfaceLayers[2]) { + *ls->alpha = 1.f; + } + } +} + +void CInputManager::onSwipeEnd(IPointer::SSwipeEndEvent e) { + EMIT_HOOK_EVENT_CANCELLABLE("swipeEnd", e); + + if (!m_sActiveSwipe.pWorkspaceBegin) + return; // no valid swipe + endWorkspaceSwipe(); +} + +void CInputManager::endWorkspaceSwipe() { + static auto PSWIPEPERC = CConfigValue("gestures:workspace_swipe_cancel_ratio"); + static auto PSWIPEDIST = CConfigValue("gestures:workspace_swipe_distance"); + static auto PSWIPEFORC = CConfigValue("gestures:workspace_swipe_min_speed_to_force"); + static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); + static auto PSWIPEUSER = CConfigValue("gestures:workspace_swipe_use_r"); + static auto PWORKSPACEGAP = CConfigValue("general:gaps_workspaces"); + const auto ANIMSTYLE = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->getStyle(); + const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert"); + + // commit + auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id; + auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id; + const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, (int64_t)1LL, (int64_t)UINT32_MAX); + + // If we've been swiping off the right end with PSWIPENEW enabled, there is + // no workspace there yet, and we need to choose an ID for a new one now. + // With multiple monitors, it might not be appropriate to choose one more + // than the ID of the workspace we're swiping from, because that ID might + // just be on another monitor. It's also not just the smallest unused ID, + // because that could be a gap in the existing workspace numbers, and it'd + // be counterintuitive to swipe rightwards onto a new workspace and end up + // left of where we started. Instead, it's one more than the greatest + // workspace ID that currently exists. + if (workspaceIDRight <= m_sActiveSwipe.pWorkspaceBegin->m_iID && *PSWIPENEW) { + WORKSPACEID maxWorkspace = 0; + for (const auto& ws : g_pCompositor->m_vWorkspaces) { + maxWorkspace = std::max(maxWorkspace, ws->m_iID); + } + workspaceIDRight = maxWorkspace + 1; + } + + auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); // not guaranteed if PSWIPENEW || PSWIPENUMBER + auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); // not guaranteed if PSWIPENUMBER + + const auto RENDEROFFSETMIDDLE = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->value(); + const auto XDISTANCE = m_sActiveSwipe.pMonitor->vecSize.x + *PWORKSPACEGAP; + const auto YDISTANCE = m_sActiveSwipe.pMonitor->vecSize.y + *PWORKSPACEGAP; + + PHLWORKSPACE pSwitchedTo = nullptr; + + if ((abs(m_sActiveSwipe.delta) < SWIPEDISTANCE * *PSWIPEPERC && (*PSWIPEFORC == 0 || (*PSWIPEFORC != 0 && m_sActiveSwipe.avgSpeed < *PSWIPEFORC))) || + abs(m_sActiveSwipe.delta) < 2) { + // revert + if (abs(m_sActiveSwipe.delta) < 2) { + if (PWORKSPACEL) + PWORKSPACEL->m_vRenderOffset->setValueAndWarp(Vector2D(0, 0)); + if (PWORKSPACER) + PWORKSPACER->m_vRenderOffset->setValueAndWarp(Vector2D(0, 0)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(0, 0)); + } else { + if (m_sActiveSwipe.delta < 0) { + // to left + + if (PWORKSPACEL) { + if (VERTANIMS) + *PWORKSPACEL->m_vRenderOffset = Vector2D{0.0, -YDISTANCE}; + else + *PWORKSPACEL->m_vRenderOffset = Vector2D{-XDISTANCE, 0.0}; + } + } else if (PWORKSPACER) { + // to right + if (VERTANIMS) + *PWORKSPACER->m_vRenderOffset = Vector2D{0.0, YDISTANCE}; + else + *PWORKSPACER->m_vRenderOffset = Vector2D{XDISTANCE, 0.0}; + } + + *m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset = Vector2D(); + } + + pSwitchedTo = m_sActiveSwipe.pWorkspaceBegin; + } else if (m_sActiveSwipe.delta < 0) { + // switch to left + const auto RENDEROFFSET = PWORKSPACEL ? PWORKSPACEL->m_vRenderOffset->value() : Vector2D(); + + if (PWORKSPACEL) + m_sActiveSwipe.pMonitor->changeWorkspace(workspaceIDLeft); + else { + m_sActiveSwipe.pMonitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDLeft, m_sActiveSwipe.pMonitor->ID)); + PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); + } + + PWORKSPACEL->m_vRenderOffset->setValue(RENDEROFFSET); + PWORKSPACEL->m_fAlpha->setValueAndWarp(1.f); + + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValue(RENDEROFFSETMIDDLE); + if (VERTANIMS) + *m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset = Vector2D(0.0, YDISTANCE); + else + *m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset = Vector2D(XDISTANCE, 0.0); + m_sActiveSwipe.pWorkspaceBegin->m_fAlpha->setValueAndWarp(1.f); + + g_pInputManager->unconstrainMouse(); + + Debug::log(LOG, "Ended swipe to the left"); + + pSwitchedTo = PWORKSPACEL; + } else { + // switch to right + const auto RENDEROFFSET = PWORKSPACER ? PWORKSPACER->m_vRenderOffset->value() : Vector2D(); + + if (PWORKSPACER) + m_sActiveSwipe.pMonitor->changeWorkspace(workspaceIDRight); + else { + m_sActiveSwipe.pMonitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDRight, m_sActiveSwipe.pMonitor->ID)); + PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); + } + + PWORKSPACER->m_vRenderOffset->setValue(RENDEROFFSET); + PWORKSPACER->m_fAlpha->setValueAndWarp(1.f); + + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValue(RENDEROFFSETMIDDLE); + if (VERTANIMS) + *m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset = Vector2D(0.0, -YDISTANCE); + else + *m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset = Vector2D(-XDISTANCE, 0.0); + m_sActiveSwipe.pWorkspaceBegin->m_fAlpha->setValueAndWarp(1.f); + + g_pInputManager->unconstrainMouse(); + + Debug::log(LOG, "Ended swipe to the right"); + + pSwitchedTo = PWORKSPACER; + } + m_sActiveSwipe.pWorkspaceBegin->rememberPrevWorkspace(pSwitchedTo); + + g_pHyprRenderer->damageMonitor(m_sActiveSwipe.pMonitor.lock()); + + if (PWORKSPACEL) + PWORKSPACEL->m_bForceRendering = false; + if (PWORKSPACER) + PWORKSPACER->m_bForceRendering = false; + m_sActiveSwipe.pWorkspaceBegin->m_bForceRendering = false; + + m_sActiveSwipe.pWorkspaceBegin = nullptr; + m_sActiveSwipe.initialDirection = 0; + + g_pInputManager->refocus(); + + // apply alpha + for (auto const& ls : g_pCompositor->m_pLastMonitor->m_aLayerSurfaceLayers[2]) { + *ls->alpha = pSwitchedTo->m_bHasFullscreenWindow && pSwitchedTo->m_efFullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f; + } +} + +void CInputManager::onSwipeUpdate(IPointer::SSwipeUpdateEvent e) { + EMIT_HOOK_EVENT_CANCELLABLE("swipeUpdate", e); + + if (!m_sActiveSwipe.pWorkspaceBegin) + return; + static auto PSWIPEINVR = CConfigValue("gestures:workspace_swipe_invert"); + const auto ANIMSTYLE = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->getStyle(); + const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert"); + + const double delta = m_sActiveSwipe.delta + (VERTANIMS ? (*PSWIPEINVR ? -e.delta.y : e.delta.y) : (*PSWIPEINVR ? -e.delta.x : e.delta.x)); + updateWorkspaceSwipe(delta); +} + +void CInputManager::updateWorkspaceSwipe(double delta) { + static auto PSWIPEDIST = CConfigValue("gestures:workspace_swipe_distance"); + static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); + static auto PSWIPEDIRLOCK = CConfigValue("gestures:workspace_swipe_direction_lock"); + static auto PSWIPEDIRLOCKTHRESHOLD = CConfigValue("gestures:workspace_swipe_direction_lock_threshold"); + static auto PSWIPEFOREVER = CConfigValue("gestures:workspace_swipe_forever"); + static auto PSWIPEUSER = CConfigValue("gestures:workspace_swipe_use_r"); + static auto PWORKSPACEGAP = CConfigValue("general:gaps_workspaces"); + + const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, (int64_t)1LL, (int64_t)UINT32_MAX); + const auto XDISTANCE = m_sActiveSwipe.pMonitor->vecSize.x + *PWORKSPACEGAP; + const auto YDISTANCE = m_sActiveSwipe.pMonitor->vecSize.y + *PWORKSPACEGAP; + const auto ANIMSTYLE = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->getStyle(); + const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert"); + const double d = m_sActiveSwipe.delta - delta; + m_sActiveSwipe.delta = delta; + + m_sActiveSwipe.avgSpeed = (m_sActiveSwipe.avgSpeed * m_sActiveSwipe.speedPoints + abs(d)) / (m_sActiveSwipe.speedPoints + 1); + m_sActiveSwipe.speedPoints++; + + auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id; + auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id; + + if ((workspaceIDLeft == WORKSPACE_INVALID || workspaceIDRight == WORKSPACE_INVALID || workspaceIDLeft == m_sActiveSwipe.pWorkspaceBegin->m_iID) && !*PSWIPENEW) { + m_sActiveSwipe.pWorkspaceBegin = nullptr; // invalidate the swipe + return; + } + + m_sActiveSwipe.pWorkspaceBegin->m_bForceRendering = true; + + m_sActiveSwipe.delta = std::clamp(m_sActiveSwipe.delta, (double)-SWIPEDISTANCE, (double)SWIPEDISTANCE); + + if ((m_sActiveSwipe.pWorkspaceBegin->m_iID == workspaceIDLeft && *PSWIPENEW && (m_sActiveSwipe.delta < 0)) || + (m_sActiveSwipe.delta > 0 && m_sActiveSwipe.pWorkspaceBegin->getWindows() == 0 && workspaceIDRight <= m_sActiveSwipe.pWorkspaceBegin->m_iID) || + (m_sActiveSwipe.delta < 0 && m_sActiveSwipe.pWorkspaceBegin->m_iID <= workspaceIDLeft)) { + + m_sActiveSwipe.delta = 0; + return; + } + + if (*PSWIPEDIRLOCK) { + if (m_sActiveSwipe.initialDirection != 0 && m_sActiveSwipe.initialDirection != (m_sActiveSwipe.delta < 0 ? -1 : 1)) + m_sActiveSwipe.delta = 0; + else if (m_sActiveSwipe.initialDirection == 0 && abs(m_sActiveSwipe.delta) > *PSWIPEDIRLOCKTHRESHOLD) + m_sActiveSwipe.initialDirection = m_sActiveSwipe.delta < 0 ? -1 : 1; + } + + if (m_sActiveSwipe.delta < 0) { + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDLeft); + + if (workspaceIDLeft > m_sActiveSwipe.pWorkspaceBegin->m_iID || !PWORKSPACE) { + if (*PSWIPENEW) { + g_pHyprRenderer->damageMonitor(m_sActiveSwipe.pMonitor.lock()); + + if (VERTANIMS) + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(0.0, ((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * YDISTANCE)); + else + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * XDISTANCE, 0.0)); + + m_sActiveSwipe.pWorkspaceBegin->updateWindowDecos(); + return; + } + m_sActiveSwipe.delta = 0; + return; + } + + PWORKSPACE->m_bForceRendering = true; + PWORKSPACE->m_fAlpha->setValueAndWarp(1.f); + + if (workspaceIDLeft != workspaceIDRight && workspaceIDRight != m_sActiveSwipe.pWorkspaceBegin->m_iID) { + const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); + + if (PWORKSPACER) { + PWORKSPACER->m_bForceRendering = false; + PWORKSPACER->m_fAlpha->setValueAndWarp(0.f); + } + } + + if (VERTANIMS) { + PWORKSPACE->m_vRenderOffset->setValueAndWarp(Vector2D(0.0, ((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * YDISTANCE - YDISTANCE)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(0.0, ((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * YDISTANCE)); + } else { + PWORKSPACE->m_vRenderOffset->setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * XDISTANCE - XDISTANCE, 0.0)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * XDISTANCE, 0.0)); + } + + PWORKSPACE->updateWindowDecos(); + } else { + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDRight); + + if (workspaceIDRight < m_sActiveSwipe.pWorkspaceBegin->m_iID || !PWORKSPACE) { + if (*PSWIPENEW) { + g_pHyprRenderer->damageMonitor(m_sActiveSwipe.pMonitor.lock()); + + if (VERTANIMS) + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(0.0, ((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * YDISTANCE)); + else + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * XDISTANCE, 0.0)); + + m_sActiveSwipe.pWorkspaceBegin->updateWindowDecos(); + return; + } + m_sActiveSwipe.delta = 0; + return; + } + + PWORKSPACE->m_bForceRendering = true; + PWORKSPACE->m_fAlpha->setValueAndWarp(1.f); + + if (workspaceIDLeft != workspaceIDRight && workspaceIDLeft != m_sActiveSwipe.pWorkspaceBegin->m_iID) { + const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); + + if (PWORKSPACEL) { + PWORKSPACEL->m_bForceRendering = false; + PWORKSPACEL->m_fAlpha->setValueAndWarp(0.f); + } + } + + if (VERTANIMS) { + PWORKSPACE->m_vRenderOffset->setValueAndWarp(Vector2D(0.0, ((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * YDISTANCE + YDISTANCE)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(0.0, ((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * YDISTANCE)); + } else { + PWORKSPACE->m_vRenderOffset->setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * XDISTANCE + XDISTANCE, 0.0)); + m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->setValueAndWarp(Vector2D(((-m_sActiveSwipe.delta) / SWIPEDISTANCE) * XDISTANCE, 0.0)); + } + + PWORKSPACE->updateWindowDecos(); + } + + g_pHyprRenderer->damageMonitor(m_sActiveSwipe.pMonitor.lock()); + + m_sActiveSwipe.pWorkspaceBegin->updateWindowDecos(); + + if (*PSWIPEFOREVER) { + if (abs(m_sActiveSwipe.delta) >= SWIPEDISTANCE) { + onSwipeEnd({}); + beginWorkspaceSwipe(); + } + } +} diff --git a/src/managers/input/Tablets.cpp b/src/managers/input/Tablets.cpp index a2fec15c..e1a2f2aa 100644 --- a/src/managers/input/Tablets.cpp +++ b/src/managers/input/Tablets.cpp @@ -1,21 +1,19 @@ #include "InputManager.hpp" -#include "../../desktop/view/Window.hpp" +#include "../../desktop/Window.hpp" #include "../../protocols/Tablet.hpp" #include "../../devices/Tablet.hpp" #include "../../managers/PointerManager.hpp" #include "../../managers/SeatManager.hpp" #include "../../protocols/PointerConstraints.hpp" -#include "../../protocols/core/DataDevice.hpp" -#include "../../event/EventBus.hpp" static void unfocusTool(SP tool) { if (!tool->getSurface()) return; tool->setSurface(nullptr); - if (tool->m_isDown) + if (tool->isDown) PROTO::tablet->up(tool); - for (auto const& b : tool->m_buttonsDown) { + for (auto const& b : tool->buttonsDown) { PROTO::tablet->buttonTool(tool, b, false); } PROTO::tablet->proximityOut(tool); @@ -30,17 +28,17 @@ static void focusTool(SP tool, SP tablet, SPsetSurface(surf); PROTO::tablet->proximityIn(tool, tablet, surf); - if (tool->m_isDown) + if (tool->isDown) PROTO::tablet->down(tool); - for (auto const& b : tool->m_buttonsDown) { + for (auto const& b : tool->buttonsDown) { PROTO::tablet->buttonTool(tool, b, true); } } static void refocusTablet(SP tab, SP tool, bool motion = false) { - const auto LASTHLSURFACE = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.pointerFocus.lock()); + const auto LASTHLSURFACE = CWLSurface::fromResource(g_pSeatManager->state.pointerFocus.lock()); - if (!LASTHLSURFACE || !tool->m_active) { + if (!LASTHLSURFACE || !tool->active) { if (tool->getSurface()) unfocusTool(tool); @@ -58,13 +56,11 @@ static void refocusTablet(SP tab, SP tool, bool motion = f const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal(); - focusTool(tool, tab, g_pSeatManager->m_state.pointerFocus.lock()); + focusTool(tool, tab, g_pSeatManager->state.pointerFocus.lock()); if (!motion) return; - const auto WINDOW = Desktop::View::CWindow::fromView(LASTHLSURFACE->view()); - if (LASTHLSURFACE->constraint() && tool->aq()->type != Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_MOUSE) { // cursor logic will completely break here as the cursor will be locked. // let's just "map" the desired position to the constraint area. @@ -72,13 +68,13 @@ static void refocusTablet(SP tab, SP tool, bool motion = f Vector2D local; // yes, this technically ignores any regions set by the app. Too bad! - if (WINDOW) - local = tool->m_absolutePos * WINDOW->m_realSize->goal(); + if (LASTHLSURFACE->getWindow()) + local = tool->absolutePos * LASTHLSURFACE->getWindow()->m_vRealSize->goal(); else - local = tool->m_absolutePos * BOX->size(); + local = tool->absolutePos * BOX->size(); - if (WINDOW && WINDOW->m_isX11) - local = local * WINDOW->m_X11SurfaceScaledBy; + if (LASTHLSURFACE->getWindow() && LASTHLSURFACE->getWindow()->m_bIsX11) + local = local * LASTHLSURFACE->getWindow()->m_fX11SurfaceScaledBy; PROTO::tablet->motion(tool, local); return; @@ -86,8 +82,8 @@ static void refocusTablet(SP tab, SP tool, bool motion = f auto local = CURSORPOS - BOX->pos(); - if (WINDOW && WINDOW->m_isX11) - local = local * WINDOW->m_X11SurfaceScaledBy; + if (LASTHLSURFACE->getWindow() && LASTHLSURFACE->getWindow()->m_bIsX11) + local = local * LASTHLSURFACE->getWindow()->m_fX11SurfaceScaledBy; PROTO::tablet->motion(tool, local); } @@ -107,15 +103,10 @@ static Vector2D transformToActiveRegion(const Vector2D pos, const CBox activeAre } void CInputManager::onTabletAxis(CTablet::SAxisEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.input.tablet.axis.emit(e, info); - if (info.cancelled) - return; - const auto PTAB = e.tablet; const auto PTOOL = ensureTabletToolPresent(e.tool); - if (PTOOL->m_active && (e.updatedAxes & (CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X | CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y))) { + if (PTOOL->active && (e.updatedAxes & (CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X | CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y))) { double x = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X) ? e.axis.x : NAN; double dx = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_X) ? e.axisDelta.x : NAN; double y = (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_Y) ? e.axis.y : NAN; @@ -130,24 +121,21 @@ void CInputManager::onTabletAxis(CTablet::SAxisEvent e) { } default: { if (!std::isnan(x)) - PTOOL->m_absolutePos.x = x; + PTOOL->absolutePos.x = x; if (!std::isnan(y)) - PTOOL->m_absolutePos.y = y; + PTOOL->absolutePos.y = y; - if (PTAB->m_relativeInput) + if (PTAB->relativeInput) g_pPointerManager->move(delta); else - g_pPointerManager->warpAbsolute(transformToActiveRegion({x, y}, PTAB->m_activeArea), PTAB); + g_pPointerManager->warpAbsolute(transformToActiveRegion({x, y}, PTAB->activeArea), PTAB); break; } } - m_lastInputTouch = false; - if (!PTOOL->m_isDown || PROTO::data->dndActive()) - simulateMouseMovement(); refocusTablet(PTAB, PTOOL, true); - m_lastCursorMovement.reset(); + m_tmrLastCursorMovement.reset(); } if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_PRESSURE) @@ -166,32 +154,24 @@ void CInputManager::onTabletAxis(CTablet::SAxisEvent e) { PROTO::tablet->wheel(PTOOL, e.wheelDelta); if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X) - PTOOL->m_tilt.x = e.tilt.x; + PTOOL->tilt.x = e.tilt.x; if (e.updatedAxes & CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y) - PTOOL->m_tilt.y = e.tilt.y; + PTOOL->tilt.y = e.tilt.y; if (e.updatedAxes & (CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_X | CTablet::eTabletToolAxes::HID_TABLET_TOOL_AXIS_TILT_Y)) - PROTO::tablet->tilt(PTOOL, PTOOL->m_tilt); + PROTO::tablet->tilt(PTOOL, PTOOL->tilt); } void CInputManager::onTabletTip(CTablet::STipEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.input.tablet.tip.emit(e, info); - if (info.cancelled) - return; - const auto PTAB = e.tablet; const auto PTOOL = ensureTabletToolPresent(e.tool); const auto POS = e.tip; - if (PTAB->m_relativeInput) + if (PTAB->relativeInput) g_pPointerManager->move({0, 0}); else - g_pPointerManager->warpAbsolute(transformToActiveRegion(POS, PTAB->m_activeArea), PTAB); - - if (e.in) - refocus(); + g_pPointerManager->warpAbsolute(transformToActiveRegion(POS, PTAB->activeArea), PTAB); refocusTablet(PTAB, PTOOL, true); @@ -200,125 +180,126 @@ void CInputManager::onTabletTip(CTablet::STipEvent e) { else PROTO::tablet->up(PTOOL); - PTOOL->m_isDown = e.in; + PTOOL->isDown = e.in; } void CInputManager::onTabletButton(CTablet::SButtonEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.input.tablet.button.emit(e, info); - if (info.cancelled) - return; - const auto PTOOL = ensureTabletToolPresent(e.tool); - if (e.down) - refocus(); - PROTO::tablet->buttonTool(PTOOL, e.button, e.down); if (e.down) - PTOOL->m_buttonsDown.push_back(e.button); + PTOOL->buttonsDown.push_back(e.button); else - std::erase(PTOOL->m_buttonsDown, e.button); + std::erase(PTOOL->buttonsDown, e.button); } void CInputManager::onTabletProximity(CTablet::SProximityEvent e) { - Event::SCallbackInfo info; - Event::bus()->m_events.input.tablet.proximity.emit(e, info); - if (info.cancelled) - return; - const auto PTAB = e.tablet; const auto PTOOL = ensureTabletToolPresent(e.tool); - PTOOL->m_active = e.in; + PTOOL->active = e.in; if (!e.in) { - m_lastInputTablet = false; if (PTOOL->getSurface()) unfocusTool(PTOOL); } else { - m_lastInputTablet = true; simulateMouseMovement(); refocusTablet(PTAB, PTOOL); } } void CInputManager::newTablet(SP pDevice) { - const auto PNEWTABLET = m_tablets.emplace_back(CTablet::create(pDevice)); - m_hids.emplace_back(PNEWTABLET); + const auto PNEWTABLET = m_vTablets.emplace_back(CTablet::create(pDevice)); + m_vHIDs.emplace_back(PNEWTABLET); try { - PNEWTABLET->m_hlName = g_pInputManager->getNameForNewDevice(pDevice->getName()); + PNEWTABLET->hlName = g_pInputManager->getNameForNewDevice(pDevice->getName()); } catch (std::exception& e) { - Log::logger->log(Log::ERR, "Tablet had no name???"); // logic error + Debug::log(ERR, "Tablet had no name???"); // logic error } g_pPointerManager->attachTablet(PNEWTABLET); - PNEWTABLET->m_events.destroy.listenStatic([this, tablet = PNEWTABLET.get()] { - auto TABLET = tablet->m_self; - destroyTablet(TABLET.lock()); - }); + PNEWTABLET->events.destroy.registerStaticListener( + [this](void* owner, std::any d) { + auto TABLET = ((CTablet*)owner)->self; + destroyTablet(TABLET.lock()); + }, + PNEWTABLET.get()); setTabletConfigs(); } SP CInputManager::ensureTabletToolPresent(SP pTool) { - for (auto const& t : m_tabletTools) { + for (auto const& t : m_vTabletTools) { if (t->aq() == pTool) return t; } - const auto PTOOL = m_tabletTools.emplace_back(CTabletTool::create(pTool)); - m_hids.emplace_back(PTOOL); + const auto PTOOL = m_vTabletTools.emplace_back(CTabletTool::create(pTool)); + m_vHIDs.emplace_back(PTOOL); try { - PTOOL->m_hlName = g_pInputManager->getNameForNewDevice(pTool->getName()); + PTOOL->hlName = g_pInputManager->getNameForNewDevice(pTool->getName()); } catch (std::exception& e) { - Log::logger->log(Log::ERR, "Tablet had no name???"); // logic error + Debug::log(ERR, "Tablet had no name???"); // logic error } - PTOOL->m_events.destroy.listenStatic([this, tool = PTOOL.get()] { - auto TOOL = tool->m_self; - destroyTabletTool(TOOL.lock()); - }); + PTOOL->events.destroy.registerStaticListener( + [this](void* owner, std::any d) { + auto TOOL = ((CTabletTool*)owner)->self; + destroyTabletTool(TOOL.lock()); + }, + PTOOL.get()); return PTOOL; } void CInputManager::newTabletPad(SP pDevice) { - const auto PNEWPAD = m_tabletPads.emplace_back(CTabletPad::create(pDevice)); - m_hids.emplace_back(PNEWPAD); + const auto PNEWPAD = m_vTabletPads.emplace_back(CTabletPad::create(pDevice)); + m_vHIDs.emplace_back(PNEWPAD); try { - PNEWPAD->m_hlName = g_pInputManager->getNameForNewDevice(pDevice->getName()); + PNEWPAD->hlName = g_pInputManager->getNameForNewDevice(pDevice->getName()); } catch (std::exception& e) { - Log::logger->log(Log::ERR, "Pad had no name???"); // logic error + Debug::log(ERR, "Pad had no name???"); // logic error } - PNEWPAD->m_events.destroy.listenStatic([this, pad = PNEWPAD.get()] { - auto PAD = pad->m_self; + // clang-format off + PNEWPAD->events.destroy.registerStaticListener([this](void* owner, std::any d) { + auto PAD = ((CTabletPad*)owner)->self; destroyTabletPad(PAD.lock()); - }); + }, PNEWPAD.get()); - PNEWPAD->m_padEvents.button.listenStatic([pad = PNEWPAD.get()](const CTabletPad::SButtonEvent& event) { - const auto PPAD = pad->m_self.lock(); + PNEWPAD->padEvents.button.registerStaticListener([](void* owner, std::any e) { + const auto E = std::any_cast(e); + const auto PPAD = ((CTabletPad*)owner)->self.lock(); - PROTO::tablet->mode(PPAD, 0, event.mode, event.timeMs); - PROTO::tablet->buttonPad(PPAD, event.button, event.timeMs, event.down); - }); + PROTO::tablet->mode(PPAD, 0, E.mode, E.timeMs); + PROTO::tablet->buttonPad(PPAD, E.button, E.timeMs, E.down); + }, PNEWPAD.get()); - PNEWPAD->m_padEvents.strip.listenStatic([pad = PNEWPAD.get()](const CTabletPad::SStripEvent& event) { - const auto PPAD = pad->m_self.lock(); - PROTO::tablet->strip(PPAD, event.strip, event.position, event.finger, event.timeMs); - }); + PNEWPAD->padEvents.strip.registerStaticListener([](void* owner, std::any e) { + const auto E = std::any_cast(e); + const auto PPAD = ((CTabletPad*)owner)->self.lock(); - PNEWPAD->m_padEvents.ring.listenStatic([pad = PNEWPAD.get()](const CTabletPad::SRingEvent& event) { - const auto PPAD = pad->m_self.lock(); - PROTO::tablet->ring(PPAD, event.ring, event.position, event.finger, event.timeMs); - }); + PROTO::tablet->strip(PPAD, E.strip, E.position, E.finger, E.timeMs); + }, PNEWPAD.get()); - PNEWPAD->m_padEvents.attach.listenStatic([pad = PNEWPAD.get()](const SP& tool) { pad->m_parent = tool; }); + PNEWPAD->padEvents.ring.registerStaticListener([](void* owner, std::any e) { + const auto E = std::any_cast(e); + const auto PPAD = ((CTabletPad*)owner)->self.lock(); + + PROTO::tablet->ring(PPAD, E.ring, E.position, E.finger, E.timeMs); + }, PNEWPAD.get()); + + PNEWPAD->padEvents.attach.registerStaticListener([](void* owner, std::any e) { + const auto PPAD = ((CTabletPad*)owner)->self.lock(); + const auto TOOL = std::any_cast>(e); + + PPAD->parent = TOOL; + }, PNEWPAD.get()); + // clang-format on } diff --git a/src/managers/input/TextInput.cpp b/src/managers/input/TextInput.cpp index be9a5d29..4b28d513 100644 --- a/src/managers/input/TextInput.cpp +++ b/src/managers/input/TextInput.cpp @@ -1,181 +1,187 @@ #include "TextInput.hpp" +#include "../../defines.hpp" #include "InputManager.hpp" #include "../../protocols/TextInputV1.hpp" -#include "../../desktop/state/FocusState.hpp" +#include "../../Compositor.hpp" #include "../../protocols/TextInputV3.hpp" #include "../../protocols/InputMethodV2.hpp" #include "../../protocols/core/Compositor.hpp" -CTextInput::CTextInput(WP ti) : m_v1Input(ti) { +CTextInput::CTextInput(WP ti) : pV1Input(ti) { initCallbacks(); } -CTextInput::CTextInput(WP ti) : m_v3Input(ti) { +CTextInput::CTextInput(WP ti) : pV3Input(ti) { initCallbacks(); } void CTextInput::initCallbacks() { if (isV3()) { - const auto INPUT = m_v3Input.lock(); + const auto INPUT = pV3Input.lock(); - m_listeners.enable = INPUT->m_events.enable.listen([this] { onEnabled(); }); - m_listeners.disable = INPUT->m_events.disable.listen([this] { onDisabled(); }); - m_listeners.commit = INPUT->m_events.onCommit.listen([this] { onCommit(); }); - m_listeners.reset = INPUT->m_events.reset.listen([this] { onReset(); }); - m_listeners.destroy = INPUT->m_events.destroy.listen([this] { destroy(); }); + listeners.enable = INPUT->events.enable.registerListener([this](std::any p) { onEnabled(); }); + listeners.disable = INPUT->events.disable.registerListener([this](std::any p) { onDisabled(); }); + listeners.commit = INPUT->events.onCommit.registerListener([this](std::any p) { onCommit(); }); + listeners.reset = INPUT->events.reset.registerListener([this](std::any p) { onReset(); }); + listeners.destroy = INPUT->events.destroy.registerListener([this](std::any p) { + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); + g_pInputManager->m_sIMERelay.removeTextInput(this); + if (!g_pInputManager->m_sIMERelay.getFocusedTextInput()) + g_pInputManager->m_sIMERelay.deactivateIME(this); + }); - if (Desktop::focusState()->surface() && Desktop::focusState()->surface()->client() == INPUT->client()) - enter(Desktop::focusState()->surface()); + if (!g_pCompositor->m_pLastFocus.expired() && g_pCompositor->m_pLastFocus->client() == INPUT->client()) + enter(g_pCompositor->m_pLastFocus.lock()); } else { - const auto INPUT = m_v1Input.lock(); + const auto INPUT = pV1Input.lock(); - m_listeners.enable = INPUT->m_events.enable.listen([this](const auto& surface) { onEnabled(surface); }); - m_listeners.disable = INPUT->m_events.disable.listen([this] { onDisabled(); }); - m_listeners.commit = INPUT->m_events.onCommit.listen([this] { onCommit(); }); - m_listeners.reset = INPUT->m_events.reset.listen([this] { onReset(); }); - m_listeners.destroy = INPUT->m_events.destroy.listen([this] { destroy(); }); + listeners.enable = INPUT->events.enable.registerListener([this](std::any p) { + const auto SURFACE = std::any_cast>(p); + onEnabled(SURFACE); + }); + listeners.disable = INPUT->events.disable.registerListener([this](std::any p) { onDisabled(); }); + listeners.commit = INPUT->events.onCommit.registerListener([this](std::any p) { onCommit(); }); + listeners.reset = INPUT->events.reset.registerListener([this](std::any p) { onReset(); }); + listeners.destroy = INPUT->events.destroy.registerListener([this](std::any p) { + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); + g_pInputManager->m_sIMERelay.removeTextInput(this); + if (!g_pInputManager->m_sIMERelay.getFocusedTextInput()) + g_pInputManager->m_sIMERelay.deactivateIME(this); + }); } } -void CTextInput::destroy() { - m_listeners.surfaceUnmap.reset(); - m_listeners.surfaceDestroy.reset(); - - g_pInputManager->m_relay.removeTextInput(this); - - if (!g_pInputManager->m_relay.getFocusedTextInput()) - g_pInputManager->m_relay.deactivateIME(nullptr, false); -} - void CTextInput::onEnabled(SP surfV1) { - Log::logger->log(Log::DEBUG, "TI ENABLE"); + Debug::log(LOG, "TI ENABLE"); - if (g_pInputManager->m_relay.m_inputMethod.expired()) { - // Log::logger->log(Log::WARN, "Enabling TextInput on no IME!"); + if (g_pInputManager->m_sIMERelay.m_pIME.expired()) { + // Debug::log(WARN, "Enabling TextInput on no IME!"); return; } // v1 only, map surface to PTI if (!isV3()) { - if (Desktop::focusState()->surface() != surfV1 || !m_v1Input->m_active) + if (g_pCompositor->m_pLastFocus != surfV1 || !pV1Input->active) return; enter(surfV1); } - g_pInputManager->m_relay.activateIME(this); + g_pInputManager->m_sIMERelay.activateIME(this); } void CTextInput::onDisabled() { - if (g_pInputManager->m_relay.m_inputMethod.expired()) { - // Log::logger->log(Log::WARN, "Disabling TextInput on no IME!"); + if (g_pInputManager->m_sIMERelay.m_pIME.expired()) { + // Debug::log(WARN, "Disabling TextInput on no IME!"); return; } if (!isV3()) leave(); - m_listeners.surfaceUnmap.reset(); - m_listeners.surfaceDestroy.reset(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); if (!focusedSurface()) return; - const auto PFOCUSEDTI = g_pInputManager->m_relay.getFocusedTextInput(); + const auto PFOCUSEDTI = g_pInputManager->m_sIMERelay.getFocusedTextInput(); if (!PFOCUSEDTI || PFOCUSEDTI != this) return; - g_pInputManager->m_relay.deactivateIME(this); + g_pInputManager->m_sIMERelay.deactivateIME(this); } void CTextInput::onReset() { - if (g_pInputManager->m_relay.m_inputMethod.expired()) + if (g_pInputManager->m_sIMERelay.m_pIME.expired()) return; if (!focusedSurface()) return; - const auto PFOCUSEDTI = g_pInputManager->m_relay.getFocusedTextInput(); + const auto PFOCUSEDTI = g_pInputManager->m_sIMERelay.getFocusedTextInput(); if (!PFOCUSEDTI || PFOCUSEDTI != this) return; - g_pInputManager->m_relay.deactivateIME(this, false); - g_pInputManager->m_relay.activateIME(this); + g_pInputManager->m_sIMERelay.deactivateIME(this, false); + g_pInputManager->m_sIMERelay.activateIME(this); } void CTextInput::onCommit() { - if (g_pInputManager->m_relay.m_inputMethod.expired()) { - // Log::logger->log(Log::WARN, "Committing TextInput on no IME!"); + if (g_pInputManager->m_sIMERelay.m_pIME.expired()) { + // Debug::log(WARN, "Committing TextInput on no IME!"); return; } - if (!(isV3() ? m_v3Input->m_current.enabled.value : m_v1Input->m_active)) { - Log::logger->log(Log::WARN, "Disabled TextInput commit?"); + if (!(isV3() ? pV3Input->current.enabled.value : pV1Input->active)) { + Debug::log(WARN, "Disabled TextInput commit?"); return; } - g_pInputManager->m_relay.commitIMEState(this); + g_pInputManager->m_sIMERelay.commitIMEState(this); } void CTextInput::setFocusedSurface(SP pSurface) { - if (pSurface == m_focusedSurface) + if (pSurface == pFocusedSurface) return; - m_focusedSurface = pSurface; + pFocusedSurface = pSurface; if (!pSurface) return; - m_listeners.surfaceUnmap.reset(); - m_listeners.surfaceDestroy.reset(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); - m_listeners.surfaceUnmap = pSurface->m_events.unmap.listen([this] { - Log::logger->log(Log::DEBUG, "Unmap TI owner1"); + listeners.surfaceUnmap = pSurface->events.unmap.registerListener([this](std::any d) { + Debug::log(LOG, "Unmap TI owner1"); - if (m_enterLocks) - m_enterLocks--; - m_focusedSurface.reset(); - m_listeners.surfaceUnmap.reset(); - m_listeners.surfaceDestroy.reset(); + if (enterLocks) + enterLocks--; + pFocusedSurface.reset(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); - if (isV3() && !m_v3Input.expired() && m_v3Input->m_current.enabled.value) { - m_v3Input->m_pending.enabled.value = false; - m_v3Input->m_pending.enabled.isDisablePending = false; - m_v3Input->m_pending.enabled.isEnablePending = false; - m_v3Input->m_current.enabled.value = false; + if (isV3() && !pV3Input.expired() && pV3Input->current.enabled.value) { + pV3Input->pending.enabled.value = false; + pV3Input->pending.enabled.isDisablePending = false; + pV3Input->pending.enabled.isEnablePending = false; + pV3Input->current.enabled.value = false; } - if (!g_pInputManager->m_relay.getFocusedTextInput()) - g_pInputManager->m_relay.deactivateIME(this); + if (!g_pInputManager->m_sIMERelay.getFocusedTextInput()) + g_pInputManager->m_sIMERelay.deactivateIME(this); }); - m_listeners.surfaceDestroy = pSurface->m_events.destroy.listen([this] { - Log::logger->log(Log::DEBUG, "Destroy TI owner1"); + listeners.surfaceDestroy = pSurface->events.destroy.registerListener([this](std::any d) { + Debug::log(LOG, "Destroy TI owner1"); - if (m_enterLocks) - m_enterLocks--; - m_focusedSurface.reset(); - m_listeners.surfaceUnmap.reset(); - m_listeners.surfaceDestroy.reset(); + if (enterLocks) + enterLocks--; + pFocusedSurface.reset(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); - if (isV3() && !m_v3Input.expired() && m_v3Input->m_current.enabled.value) { - m_v3Input->m_pending.enabled.value = false; - m_v3Input->m_pending.enabled.isDisablePending = false; - m_v3Input->m_pending.enabled.isEnablePending = false; - m_v3Input->m_current.enabled.value = false; + if (isV3() && !pV3Input.expired() && pV3Input->current.enabled.value) { + pV3Input->pending.enabled.value = false; + pV3Input->pending.enabled.isDisablePending = false; + pV3Input->pending.enabled.isEnablePending = false; + pV3Input->current.enabled.value = false; } - if (!g_pInputManager->m_relay.getFocusedTextInput()) - g_pInputManager->m_relay.deactivateIME(this); + if (!g_pInputManager->m_sIMERelay.getFocusedTextInput()) + g_pInputManager->m_sIMERelay.deactivateIME(this); }); } bool CTextInput::isV3() { - return m_v3Input && !m_v1Input; + return pV3Input && !pV1Input; } void CTextInput::enter(SP pSurface) { - if (!pSurface || !pSurface->m_mapped) + if (!pSurface || !pSurface->mapped) return; if (pSurface == focusedSurface()) @@ -184,17 +190,17 @@ void CTextInput::enter(SP pSurface) { if (focusedSurface()) leave(); - m_enterLocks++; - if (m_enterLocks != 1) { - Log::logger->log(Log::ERR, "BUG THIS: TextInput has != 1 locks in enter"); + enterLocks++; + if (enterLocks != 1) { + Debug::log(ERR, "BUG THIS: TextInput has != 1 locks in enter"); leave(); - m_enterLocks = 1; + enterLocks = 1; } if (isV3()) - m_v3Input->enter(pSurface); + pV3Input->enter(pSurface); else { - m_v1Input->enter(pSurface); + pV1Input->enter(pSurface); } setFocusedSurface(pSurface); @@ -204,106 +210,102 @@ void CTextInput::leave() { if (!focusedSurface()) return; - m_enterLocks--; - if (m_enterLocks != 0) { - Log::logger->log(Log::ERR, "BUG THIS: TextInput has != 0 locks in leave"); - m_enterLocks = 0; + enterLocks--; + if (enterLocks != 0) { + Debug::log(ERR, "BUG THIS: TextInput has != 0 locks in leave"); + enterLocks = 0; } if (isV3()) - m_v3Input->leave(focusedSurface()); + pV3Input->leave(focusedSurface()); else - m_v1Input->leave(); + pV1Input->leave(); setFocusedSurface(nullptr); - g_pInputManager->m_relay.deactivateIME(this); + g_pInputManager->m_sIMERelay.deactivateIME(this); } SP CTextInput::focusedSurface() { - return m_focusedSurface.lock(); + return pFocusedSurface.lock(); } wl_client* CTextInput::client() { - return isV3() ? m_v3Input->client() : m_v1Input->client(); + return isV3() ? pV3Input->client() : pV1Input->client(); } void CTextInput::commitStateToIME(SP ime) { - if (isV3() && !m_v3Input.expired()) { - const auto INPUT = m_v3Input.lock(); + if (isV3() && !pV3Input.expired()) { + const auto INPUT = pV3Input.lock(); - if (INPUT->m_current.surrounding.updated) - ime->surroundingText(INPUT->m_current.surrounding.text, INPUT->m_current.surrounding.cursor, INPUT->m_current.surrounding.anchor); + if (INPUT->current.surrounding.updated) + ime->surroundingText(INPUT->current.surrounding.text, INPUT->current.surrounding.cursor, INPUT->current.surrounding.anchor); - ime->textChangeCause(INPUT->m_current.cause); + ime->textChangeCause(INPUT->current.cause); - if (INPUT->m_current.contentType.updated) - ime->textContentType(INPUT->m_current.contentType.hint, INPUT->m_current.contentType.purpose); - } else if (!m_v1Input.expired()) { - const auto INPUT = m_v1Input.lock(); + if (INPUT->current.contentType.updated) + ime->textContentType(INPUT->current.contentType.hint, INPUT->current.contentType.purpose); + } else if (!pV1Input.expired()) { + const auto INPUT = pV1Input.lock(); - if (INPUT->m_pendingSurrounding.isPending) - ime->surroundingText(INPUT->m_pendingSurrounding.text, INPUT->m_pendingSurrounding.cursor, INPUT->m_pendingSurrounding.anchor); + if (INPUT->pendingSurrounding.isPending) + ime->surroundingText(INPUT->pendingSurrounding.text, INPUT->pendingSurrounding.cursor, INPUT->pendingSurrounding.anchor); ime->textChangeCause(ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD); - if (m_v1Input->m_pendingContentType.isPending) - ime->textContentType(sc(INPUT->m_pendingContentType.hint), sc(INPUT->m_pendingContentType.purpose)); + if (pV1Input->pendingContentType.isPending) + ime->textContentType((zwpTextInputV3ContentHint)INPUT->pendingContentType.hint, (zwpTextInputV3ContentPurpose)INPUT->pendingContentType.purpose); } - g_pInputManager->m_relay.updateAllPopups(); + g_pInputManager->m_sIMERelay.updateAllPopups(); ime->done(); } void CTextInput::updateIMEState(SP ime) { if (isV3()) { - const auto INPUT = m_v3Input.lock(); + const auto INPUT = pV3Input.lock(); - if (ime->m_current.preeditString.committed) - INPUT->preeditString(ime->m_current.preeditString.string, ime->m_current.preeditString.begin, ime->m_current.preeditString.end); + if (ime->current.preeditString.committed) + INPUT->preeditString(ime->current.preeditString.string, ime->current.preeditString.begin, ime->current.preeditString.end); - if (ime->m_current.committedString.committed) - INPUT->commitString(ime->m_current.committedString.string); + if (ime->current.committedString.committed) + INPUT->commitString(ime->current.committedString.string); - if (ime->m_current.deleteSurrounding.committed) - INPUT->deleteSurroundingText(ime->m_current.deleteSurrounding.before, ime->m_current.deleteSurrounding.after); + if (ime->current.deleteSurrounding.committed) + INPUT->deleteSurroundingText(ime->current.deleteSurrounding.before, ime->current.deleteSurrounding.after); INPUT->sendDone(); } else { - const auto INPUT = m_v1Input.lock(); + const auto INPUT = pV1Input.lock(); - if (ime->m_current.preeditString.committed) { - INPUT->preeditCursor(ime->m_current.preeditString.begin); - INPUT->preeditStyling(0, std::string(ime->m_current.preeditString.string).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT); - INPUT->preeditString(m_v1Input->m_serial, ime->m_current.preeditString.string.c_str(), ""); + if (ime->current.preeditString.committed) { + INPUT->preeditCursor(ime->current.preeditString.begin); + INPUT->preeditStyling(0, std::string(ime->current.preeditString.string).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT); + INPUT->preeditString(pV1Input->serial, ime->current.preeditString.string.c_str(), ""); } else { INPUT->preeditCursor(0); INPUT->preeditStyling(0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT); - INPUT->preeditString(m_v1Input->m_serial, "", ""); + INPUT->preeditString(pV1Input->serial, "", ""); } - if (ime->m_current.committedString.committed) - INPUT->commitString(m_v1Input->m_serial, ime->m_current.committedString.string.c_str()); + if (ime->current.committedString.committed) + INPUT->commitString(pV1Input->serial, ime->current.committedString.string.c_str()); - if (ime->m_current.deleteSurrounding.committed) { - INPUT->deleteSurroundingText(std::string(ime->m_current.preeditString.string).length() - ime->m_current.deleteSurrounding.before, - ime->m_current.deleteSurrounding.after + ime->m_current.deleteSurrounding.before); + if (ime->current.deleteSurrounding.committed) { + INPUT->deleteSurroundingText(std::string(ime->current.preeditString.string).length() - ime->current.deleteSurrounding.before, + ime->current.deleteSurrounding.after + ime->current.deleteSurrounding.before); - if (ime->m_current.preeditString.committed) - INPUT->commitString(m_v1Input->m_serial, ime->m_current.preeditString.string.c_str()); + if (ime->current.preeditString.committed) + INPUT->commitString(pV1Input->serial, ime->current.preeditString.string.c_str()); } } } bool CTextInput::hasCursorRectangle() { - return !isV3() || m_v3Input->m_current.box.updated; + return !isV3() || pV3Input->current.box.updated; } CBox CTextInput::cursorBox() { - return CBox{isV3() ? m_v3Input->m_current.box.cursorBox : m_v1Input->m_cursorRectangle}; -} - -bool CTextInput::isEnabled() { - return isV3() ? m_v3Input->m_current.enabled.value : true; + return CBox{isV3() ? pV3Input->current.box.cursorBox : pV1Input->cursorRectangle}; } diff --git a/src/managers/input/TextInput.hpp b/src/managers/input/TextInput.hpp index 798f31e9..37b471af 100644 --- a/src/managers/input/TextInput.hpp +++ b/src/managers/input/TextInput.hpp @@ -29,7 +29,6 @@ class CTextInput { void onCommit(); void onReset(); - bool isEnabled(); bool hasCursorRectangle(); CBox cursorBox(); @@ -39,12 +38,10 @@ class CTextInput { void setFocusedSurface(SP pSurface); void initCallbacks(); - void destroy(); - - WP m_focusedSurface; - int m_enterLocks = 0; - WP m_v3Input; - WP m_v1Input; + WP pFocusedSurface; + int enterLocks = 0; + WP pV3Input; + WP pV1Input; struct { CHyprSignalListener enable; @@ -54,5 +51,5 @@ class CTextInput { CHyprSignalListener destroy; CHyprSignalListener surfaceUnmap; CHyprSignalListener surfaceDestroy; - } m_listeners; + } listeners; }; diff --git a/src/managers/input/Touch.cpp b/src/managers/input/Touch.cpp index e45bfd28..a166a256 100644 --- a/src/managers/input/Touch.cpp +++ b/src/managers/input/Touch.cpp @@ -2,41 +2,35 @@ #include "../SessionLockManager.hpp" #include "../../protocols/SessionLock.hpp" #include "../../Compositor.hpp" -#include "../../desktop/view/LayerSurface.hpp" -#include "../../desktop/state/FocusState.hpp" +#include "../../desktop/LayerSurface.hpp" #include "../../config/ConfigValue.hpp" -#include "../../helpers/Monitor.hpp" #include "../../devices/ITouch.hpp" -#include "../../event/EventBus.hpp" #include "../SeatManager.hpp" -#include "debug/log/Logger.hpp" -#include "UnifiedWorkspaceSwipeGesture.hpp" +#include "managers/AnimationManager.hpp" +#include "../HookSystemManager.hpp" +#include "debug/Log.hpp" void CInputManager::onTouchDown(ITouch::SDownEvent e) { - m_lastInputTouch = true; + m_bLastInputTouch = true; static auto PSWIPETOUCH = CConfigValue("gestures:workspace_swipe_touch"); static auto PGAPSOUTDATA = CConfigValue("general:gaps_out"); - auto* const PGAPSOUT = sc((PGAPSOUTDATA.ptr())->getData()); + auto* const PGAPSOUT = (CCssGapData*)(PGAPSOUTDATA.ptr())->getData(); // TODO: WORKSPACERULE.gapsOut.value_or() - auto gapsOut = *PGAPSOUT; - static auto PBORDERSIZE = CConfigValue("general:border_size"); - static auto PSWIPEINVR = CConfigValue("gestures:workspace_swipe_touch_invert"); + auto gapsOut = *PGAPSOUT; + static auto PBORDERSIZE = CConfigValue("general:border_size"); + static auto PSWIPEINVR = CConfigValue("gestures:workspace_swipe_touch_invert"); + EMIT_HOOK_EVENT_CANCELLABLE("touchDown", e); - Event::SCallbackInfo info; - Event::bus()->m_events.input.touch.down.emit(e, info); - if (info.cancelled) - return; + auto PMONITOR = g_pCompositor->getMonitorFromName(!e.device->boundOutput.empty() ? e.device->boundOutput : ""); - auto PMONITOR = g_pCompositor->getMonitorFromName(!e.device->m_boundOutput.empty() ? e.device->m_boundOutput : ""); + PMONITOR = PMONITOR ? PMONITOR : g_pCompositor->m_pLastMonitor.lock(); - PMONITOR = PMONITOR ? PMONITOR : Desktop::focusState()->monitor(); + g_pCompositor->warpCursorTo({PMONITOR->vecPosition.x + e.pos.x * PMONITOR->vecSize.x, PMONITOR->vecPosition.y + e.pos.y * PMONITOR->vecSize.y}, true); - const auto TOUCH_COORDS = PMONITOR->m_position + (e.pos * PMONITOR->m_size); + refocus(); - refocus(TOUCH_COORDS); - - if (m_clickBehavior == CLICKMODE_KILL) { + if (m_ecbClickBehavior == CLICKMODE_KILL) { IPointer::SButtonEvent e; e.state = WL_POINTER_BUTTON_STATE_PRESSED; g_pInputManager->processMouseDownKill(e); @@ -44,145 +38,131 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) { } // Don't propagate new touches when a workspace swipe is in progress. - if (g_pUnifiedWorkspaceSwipe->isGestureInProgress()) { + if (m_sActiveSwipe.pWorkspaceBegin) { return; // TODO: Don't swipe if you touched a floating window. - } else if (*PSWIPETOUCH && (m_foundLSToFocus.expired() || m_foundLSToFocus->m_layer <= 1) && !g_pSessionLockManager->isSessionLocked()) { - const auto PWORKSPACE = PMONITOR->m_activeWorkspace; - const auto STYLE = PWORKSPACE->m_renderOffset->getStyle(); + } else if (*PSWIPETOUCH && (m_pFoundLSToFocus.expired() || m_pFoundLSToFocus->layer <= 1) && !g_pSessionLockManager->isSessionLocked()) { + const auto PWORKSPACE = PMONITOR->activeWorkspace; + const auto STYLE = PWORKSPACE->m_vRenderOffset->getStyle(); const bool VERTANIMS = STYLE == "slidevert" || STYLE.starts_with("slidefadevert"); - const double TARGETLEFT = ((VERTANIMS ? gapsOut.m_top : gapsOut.m_left) + *PBORDERSIZE) / (VERTANIMS ? PMONITOR->m_size.y : PMONITOR->m_size.x); - const double TARGETRIGHT = 1 - (((VERTANIMS ? gapsOut.m_bottom : gapsOut.m_right) + *PBORDERSIZE) / (VERTANIMS ? PMONITOR->m_size.y : PMONITOR->m_size.x)); + const double TARGETLEFT = ((VERTANIMS ? gapsOut.top : gapsOut.left) + *PBORDERSIZE) / (VERTANIMS ? PMONITOR->vecSize.y : PMONITOR->vecSize.x); + const double TARGETRIGHT = 1 - (((VERTANIMS ? gapsOut.bottom : gapsOut.right) + *PBORDERSIZE) / (VERTANIMS ? PMONITOR->vecSize.y : PMONITOR->vecSize.x)); const double POSITION = (VERTANIMS ? e.pos.y : e.pos.x); if (POSITION < TARGETLEFT || POSITION > TARGETRIGHT) { - g_pUnifiedWorkspaceSwipe->begin(); - g_pUnifiedWorkspaceSwipe->m_touchID = e.touchID; + beginWorkspaceSwipe(); + m_sActiveSwipe.touch_id = e.touchID; // Set the initial direction based on which edge you started from if (POSITION > 0.5) - g_pUnifiedWorkspaceSwipe->m_initialDirection = *PSWIPEINVR ? -1 : 1; + m_sActiveSwipe.initialDirection = *PSWIPEINVR ? -1 : 1; else - g_pUnifiedWorkspaceSwipe->m_initialDirection = *PSWIPEINVR ? 1 : -1; + m_sActiveSwipe.initialDirection = *PSWIPEINVR ? 1 : -1; return; } } - // could have abovelock surface, thus only use lock if no ls found - if (g_pSessionLockManager->isSessionLocked() && m_foundLSToFocus.expired()) { - m_touchData.touchFocusLockSurface = g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->m_id); - if (!m_touchData.touchFocusLockSurface) - Log::logger->log(Log::WARN, "The session is locked but can't find a lock surface"); + if (g_pSessionLockManager->isSessionLocked()) { + m_sTouchData.touchFocusLockSurface = g_pSessionLockManager->getSessionLockSurfaceForMonitor(PMONITOR->ID); + if (!m_sTouchData.touchFocusLockSurface) + Debug::log(WARN, "The session is locked but can't find a lock surface"); else - m_touchData.touchFocusSurface = m_touchData.touchFocusLockSurface->surface->surface(); + m_sTouchData.touchFocusSurface = m_sTouchData.touchFocusLockSurface->surface->surface(); } else { - m_touchData.touchFocusLockSurface.reset(); - m_touchData.touchFocusWindow = m_foundWindowToFocus; - m_touchData.touchFocusSurface = m_foundSurfaceToFocus; - m_touchData.touchFocusLS = m_foundLSToFocus; + m_sTouchData.touchFocusLockSurface.reset(); + m_sTouchData.touchFocusWindow = m_pFoundWindowToFocus; + m_sTouchData.touchFocusSurface = m_pFoundSurfaceToFocus; + m_sTouchData.touchFocusLS = m_pFoundLSToFocus; } Vector2D local; - if (m_touchData.touchFocusLockSurface) { - local = TOUCH_COORDS - PMONITOR->m_position; - m_touchData.touchSurfaceOrigin = TOUCH_COORDS - local; - } else if (!m_touchData.touchFocusWindow.expired()) { - if (m_touchData.touchFocusWindow->m_isX11) { - local = (TOUCH_COORDS - m_touchData.touchFocusWindow->m_realPosition->goal()) * m_touchData.touchFocusWindow->m_X11SurfaceScaledBy; - m_touchData.touchSurfaceOrigin = m_touchData.touchFocusWindow->m_realPosition->goal(); + if (m_sTouchData.touchFocusLockSurface) { + local = g_pInputManager->getMouseCoordsInternal() - PMONITOR->vecPosition; + m_sTouchData.touchSurfaceOrigin = g_pInputManager->getMouseCoordsInternal() - local; + } else if (!m_sTouchData.touchFocusWindow.expired()) { + if (m_sTouchData.touchFocusWindow->m_bIsX11) { + local = (g_pInputManager->getMouseCoordsInternal() - m_sTouchData.touchFocusWindow->m_vRealPosition->goal()) * m_sTouchData.touchFocusWindow->m_fX11SurfaceScaledBy; + m_sTouchData.touchSurfaceOrigin = m_sTouchData.touchFocusWindow->m_vRealPosition->goal(); } else { - g_pCompositor->vectorWindowToSurface(TOUCH_COORDS, m_touchData.touchFocusWindow.lock(), local); - m_touchData.touchSurfaceOrigin = TOUCH_COORDS - local; + g_pCompositor->vectorWindowToSurface(g_pInputManager->getMouseCoordsInternal(), m_sTouchData.touchFocusWindow.lock(), local); + m_sTouchData.touchSurfaceOrigin = g_pInputManager->getMouseCoordsInternal() - local; } - } else if (!m_touchData.touchFocusLS.expired()) { - PHLLS foundSurf; - Vector2D foundCoords; - auto surf = g_pCompositor->vectorToLayerPopupSurface(TOUCH_COORDS, PMONITOR, &foundCoords, &foundSurf); - if (surf) { - local = foundCoords; - m_touchData.touchFocusSurface = surf; - } else - local = TOUCH_COORDS - m_touchData.touchFocusLS->m_geometry.pos(); + } else if (!m_sTouchData.touchFocusLS.expired()) { + local = g_pInputManager->getMouseCoordsInternal() - m_sTouchData.touchFocusLS->geometry.pos(); - m_touchData.touchSurfaceOrigin = TOUCH_COORDS - local; + m_sTouchData.touchSurfaceOrigin = g_pInputManager->getMouseCoordsInternal() - local; } else return; // oops, nothing found. - g_pSeatManager->sendTouchDown(m_touchData.touchFocusSurface.lock(), e.timeMs, e.touchID, local); + g_pSeatManager->sendTouchDown(m_sTouchData.touchFocusSurface.lock(), e.timeMs, e.touchID, local); } void CInputManager::onTouchUp(ITouch::SUpEvent e) { - m_lastInputTouch = true; + m_bLastInputTouch = true; - Event::SCallbackInfo info; - Event::bus()->m_events.input.touch.up.emit(e, info); - if (info.cancelled) - return; - - if (g_pUnifiedWorkspaceSwipe->isGestureInProgress()) { + EMIT_HOOK_EVENT_CANCELLABLE("touchUp", e); + if (m_sActiveSwipe.pWorkspaceBegin) { // If there was a swipe from this finger, end it. - if (e.touchID == g_pUnifiedWorkspaceSwipe->m_touchID) - g_pUnifiedWorkspaceSwipe->end(); + if (e.touchID == m_sActiveSwipe.touch_id) + endWorkspaceSwipe(); return; } - if (m_touchData.touchFocusSurface) + if (m_sTouchData.touchFocusSurface) g_pSeatManager->sendTouchUp(e.timeMs, e.touchID); } void CInputManager::onTouchMove(ITouch::SMotionEvent e) { - m_lastInputTouch = true; + m_bLastInputTouch = true; - m_lastCursorMovement.reset(); - - Event::SCallbackInfo info; - Event::bus()->m_events.input.touch.motion.emit(e, info); - if (info.cancelled) - return; - - if (g_pUnifiedWorkspaceSwipe->isGestureInProgress()) { + EMIT_HOOK_EVENT_CANCELLABLE("touchMove", e); + if (m_sActiveSwipe.pWorkspaceBegin) { // Do nothing if this is using a different finger. - if (e.touchID != g_pUnifiedWorkspaceSwipe->m_touchID) + if (e.touchID != m_sActiveSwipe.touch_id) return; - const auto ANIMSTYLE = g_pUnifiedWorkspaceSwipe->m_workspaceBegin->m_renderOffset->getStyle(); + const auto ANIMSTYLE = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset->getStyle(); const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert"); static auto PSWIPEINVR = CConfigValue("gestures:workspace_swipe_touch_invert"); static auto PSWIPEDIST = CConfigValue("gestures:workspace_swipe_distance"); - const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc(1LL), sc(UINT32_MAX)); + const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, (int64_t)1LL, (int64_t)UINT32_MAX); // Handle the workspace swipe if there is one - if (g_pUnifiedWorkspaceSwipe->m_initialDirection == -1) { + if (m_sActiveSwipe.initialDirection == -1) { if (*PSWIPEINVR) // go from 0 to -SWIPEDISTANCE - g_pUnifiedWorkspaceSwipe->update(SWIPEDISTANCE * ((VERTANIMS ? e.pos.y : e.pos.x) - 1)); + updateWorkspaceSwipe(SWIPEDISTANCE * ((VERTANIMS ? e.pos.y : e.pos.x) - 1)); else // go from 0 to -SWIPEDISTANCE - g_pUnifiedWorkspaceSwipe->update(SWIPEDISTANCE * (-1 * (VERTANIMS ? e.pos.y : e.pos.x))); + updateWorkspaceSwipe(SWIPEDISTANCE * (-1 * (VERTANIMS ? e.pos.y : e.pos.x))); } else if (*PSWIPEINVR) // go from 0 to SWIPEDISTANCE - g_pUnifiedWorkspaceSwipe->update(SWIPEDISTANCE * (VERTANIMS ? e.pos.y : e.pos.x)); + updateWorkspaceSwipe(SWIPEDISTANCE * (VERTANIMS ? e.pos.y : e.pos.x)); else // go from 0 to SWIPEDISTANCE - g_pUnifiedWorkspaceSwipe->update(SWIPEDISTANCE * (1 - (VERTANIMS ? e.pos.y : e.pos.x))); + updateWorkspaceSwipe(SWIPEDISTANCE * (1 - (VERTANIMS ? e.pos.y : e.pos.x))); return; } - if (m_touchData.touchFocusLockSurface) { - const auto PMONITOR = g_pCompositor->getMonitorFromID(m_touchData.touchFocusLockSurface->iMonitorID); - const auto TOUCH_COORDS = PMONITOR->m_position + (e.pos * PMONITOR->m_size); - const auto LOCAL = TOUCH_COORDS - PMONITOR->m_position; - g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, LOCAL); - } else if (validMapped(m_touchData.touchFocusWindow)) { - const auto PMONITOR = m_touchData.touchFocusWindow->m_monitor.lock(); - const auto TOUCH_COORDS = PMONITOR->m_position + (e.pos * PMONITOR->m_size); - auto local = TOUCH_COORDS - m_touchData.touchSurfaceOrigin; - if (m_touchData.touchFocusWindow->m_isX11) - local = local * m_touchData.touchFocusWindow->m_X11SurfaceScaledBy; + if (m_sTouchData.touchFocusLockSurface) { + const auto PMONITOR = g_pCompositor->getMonitorFromID(m_sTouchData.touchFocusLockSurface->iMonitorID); + g_pCompositor->warpCursorTo({PMONITOR->vecPosition.x + e.pos.x * PMONITOR->vecSize.x, PMONITOR->vecPosition.y + e.pos.y * PMONITOR->vecSize.y}, true); + auto local = g_pInputManager->getMouseCoordsInternal() - PMONITOR->vecPosition; + g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, local); + } else if (validMapped(m_sTouchData.touchFocusWindow)) { + const auto PMONITOR = m_sTouchData.touchFocusWindow->m_pMonitor.lock(); + + g_pCompositor->warpCursorTo({PMONITOR->vecPosition.x + e.pos.x * PMONITOR->vecSize.x, PMONITOR->vecPosition.y + e.pos.y * PMONITOR->vecSize.y}, true); + + auto local = g_pInputManager->getMouseCoordsInternal() - m_sTouchData.touchSurfaceOrigin; + if (m_sTouchData.touchFocusWindow->m_bIsX11) + local = local * m_sTouchData.touchFocusWindow->m_fX11SurfaceScaledBy; g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, local); - } else if (validMapped(m_touchData.touchFocusLS)) { - const auto PMONITOR = m_touchData.touchFocusLS->m_monitor.lock(); - const auto TOUCH_COORDS = PMONITOR->m_position + (e.pos * PMONITOR->m_size); - const auto LOCAL = TOUCH_COORDS - m_touchData.touchSurfaceOrigin; + } else if (!m_sTouchData.touchFocusLS.expired()) { + const auto PMONITOR = m_sTouchData.touchFocusLS->monitor.lock(); - g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, LOCAL); + g_pCompositor->warpCursorTo({PMONITOR->vecPosition.x + e.pos.x * PMONITOR->vecSize.x, PMONITOR->vecPosition.y + e.pos.y * PMONITOR->vecSize.y}, true); + + const auto local = g_pInputManager->getMouseCoordsInternal() - m_sTouchData.touchSurfaceOrigin; + + g_pSeatManager->sendTouchMotion(e.timeMs, e.touchID, local); } } diff --git a/src/managers/input/UnifiedWorkspaceSwipeGesture.cpp b/src/managers/input/UnifiedWorkspaceSwipeGesture.cpp deleted file mode 100644 index c2c8a72a..00000000 --- a/src/managers/input/UnifiedWorkspaceSwipeGesture.cpp +++ /dev/null @@ -1,311 +0,0 @@ -#include "UnifiedWorkspaceSwipeGesture.hpp" - -#include "../../Compositor.hpp" -#include "../../desktop/state/FocusState.hpp" -#include "../../render/Renderer.hpp" -#include "InputManager.hpp" - -bool CUnifiedWorkspaceSwipeGesture::isGestureInProgress() { - return m_workspaceBegin; -} - -void CUnifiedWorkspaceSwipeGesture::begin() { - if (isGestureInProgress()) - return; - - const auto PWORKSPACE = Desktop::focusState()->monitor()->m_activeWorkspace; - - Log::logger->log(Log::DEBUG, "CUnifiedWorkspaceSwipeGesture::begin: Starting a swipe from {}", PWORKSPACE->m_name); - - m_workspaceBegin = PWORKSPACE; - m_delta = 0; - m_monitor = Desktop::focusState()->monitor(); - m_avgSpeed = 0; - m_speedPoints = 0; - - if (PWORKSPACE->m_hasFullscreenWindow) { - for (auto const& ls : Desktop::focusState()->monitor()->m_layerSurfaceLayers[2]) { - *ls->m_alpha = 1.f; - } - } -} - -void CUnifiedWorkspaceSwipeGesture::update(double delta) { - if (!isGestureInProgress()) - return; - - static auto PSWIPEDIST = CConfigValue("gestures:workspace_swipe_distance"); - static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); - static auto PSWIPEDIRLOCK = CConfigValue("gestures:workspace_swipe_direction_lock"); - static auto PSWIPEDIRLOCKTHRESHOLD = CConfigValue("gestures:workspace_swipe_direction_lock_threshold"); - static auto PSWIPEFOREVER = CConfigValue("gestures:workspace_swipe_forever"); - static auto PSWIPEUSER = CConfigValue("gestures:workspace_swipe_use_r"); - static auto PWORKSPACEGAP = CConfigValue("general:gaps_workspaces"); - - const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc(1LL), sc(UINT32_MAX)); - const auto XDISTANCE = m_monitor->m_size.x + *PWORKSPACEGAP; - const auto YDISTANCE = m_monitor->m_size.y + *PWORKSPACEGAP; - const auto ANIMSTYLE = m_workspaceBegin->m_renderOffset->getStyle(); - const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert"); - const double d = m_delta - delta; - m_delta = delta; - - m_avgSpeed = (m_avgSpeed * m_speedPoints + abs(d)) / (m_speedPoints + 1); - m_speedPoints++; - - auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id; - auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id; - - if ((workspaceIDLeft == WORKSPACE_INVALID || workspaceIDRight == WORKSPACE_INVALID || workspaceIDLeft == m_workspaceBegin->m_id) && !*PSWIPENEW) { - m_workspaceBegin = nullptr; // invalidate the swipe - return; - } - - m_workspaceBegin->m_forceRendering = true; - - m_delta = std::clamp(m_delta, sc(-SWIPEDISTANCE), sc(SWIPEDISTANCE)); - - if ((m_workspaceBegin->m_id == workspaceIDLeft && *PSWIPENEW && (m_delta < 0)) || - (m_delta > 0 && m_workspaceBegin->getWindows() == 0 && workspaceIDRight <= m_workspaceBegin->m_id) || (m_delta < 0 && m_workspaceBegin->m_id <= workspaceIDLeft)) { - - m_delta = 0; - g_pHyprRenderer->damageMonitor(m_monitor.lock()); - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, 0.0)); - return; - } - - if (*PSWIPEDIRLOCK) { - if (m_initialDirection != 0 && m_initialDirection != (m_delta < 0 ? -1 : 1)) - m_delta = 0; - else if (m_initialDirection == 0 && abs(m_delta) > *PSWIPEDIRLOCKTHRESHOLD) - m_initialDirection = m_delta < 0 ? -1 : 1; - } - - if (m_delta < 0) { - const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDLeft); - - if (workspaceIDLeft > m_workspaceBegin->m_id || !PWORKSPACE) { - if (*PSWIPENEW) { - g_pHyprRenderer->damageMonitor(m_monitor.lock()); - - if (VERTANIMS) - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE)); - else - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0)); - - m_workspaceBegin->updateWindowDecos(); - return; - } - m_delta = 0; - return; - } - - PWORKSPACE->m_forceRendering = true; - PWORKSPACE->m_alpha->setValueAndWarp(1.f); - - if (workspaceIDLeft != workspaceIDRight && workspaceIDRight != m_workspaceBegin->m_id) { - const auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); - - if (PWORKSPACER) { - PWORKSPACER->m_forceRendering = false; - PWORKSPACER->m_alpha->setValueAndWarp(0.f); - } - } - - if (VERTANIMS) { - PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE - YDISTANCE)); - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE)); - } else { - PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE - XDISTANCE, 0.0)); - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0)); - } - - PWORKSPACE->updateWindowDecos(); - } else { - const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceIDRight); - - if (workspaceIDRight < m_workspaceBegin->m_id || !PWORKSPACE) { - if (*PSWIPENEW) { - g_pHyprRenderer->damageMonitor(m_monitor.lock()); - - if (VERTANIMS) - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE)); - else - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0)); - - m_workspaceBegin->updateWindowDecos(); - return; - } - m_delta = 0; - return; - } - - PWORKSPACE->m_forceRendering = true; - PWORKSPACE->m_alpha->setValueAndWarp(1.f); - - if (workspaceIDLeft != workspaceIDRight && workspaceIDLeft != m_workspaceBegin->m_id) { - const auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); - - if (PWORKSPACEL) { - PWORKSPACEL->m_forceRendering = false; - PWORKSPACEL->m_alpha->setValueAndWarp(0.f); - } - } - - if (VERTANIMS) { - PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE + YDISTANCE)); - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0.0, ((-m_delta) / SWIPEDISTANCE) * YDISTANCE)); - } else { - PWORKSPACE->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE + XDISTANCE, 0.0)); - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(((-m_delta) / SWIPEDISTANCE) * XDISTANCE, 0.0)); - } - - PWORKSPACE->updateWindowDecos(); - } - - g_pHyprRenderer->damageMonitor(m_monitor.lock()); - - m_workspaceBegin->updateWindowDecos(); - - if (*PSWIPEFOREVER) { - if (abs(m_delta) >= SWIPEDISTANCE) { - end(); - begin(); - } - } -} - -void CUnifiedWorkspaceSwipeGesture::end() { - if (!isGestureInProgress()) - return; - - static auto PSWIPEPERC = CConfigValue("gestures:workspace_swipe_cancel_ratio"); - static auto PSWIPEDIST = CConfigValue("gestures:workspace_swipe_distance"); - static auto PSWIPEFORC = CConfigValue("gestures:workspace_swipe_min_speed_to_force"); - static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); - static auto PSWIPEUSER = CConfigValue("gestures:workspace_swipe_use_r"); - static auto PWORKSPACEGAP = CConfigValue("general:gaps_workspaces"); - const auto ANIMSTYLE = m_workspaceBegin->m_renderOffset->getStyle(); - const bool VERTANIMS = ANIMSTYLE == "slidevert" || ANIMSTYLE.starts_with("slidefadevert"); - - // commit - auto workspaceIDLeft = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r-1" : "m-1")).id; - auto workspaceIDRight = getWorkspaceIDNameFromString((*PSWIPEUSER ? "r+1" : "m+1")).id; - const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, sc(1LL), sc(UINT32_MAX)); - - // If we've been swiping off the right end with PSWIPENEW enabled, there is - // no workspace there yet, and we need to choose an ID for a new one now. - if (workspaceIDRight <= m_workspaceBegin->m_id && *PSWIPENEW) - workspaceIDRight = getWorkspaceIDNameFromString("r+1").id; - - auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); // not guaranteed if PSWIPENEW || PSWIPENUMBER - auto PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); // not guaranteed if PSWIPENUMBER - - const auto RENDEROFFSETMIDDLE = m_workspaceBegin->m_renderOffset->value(); - const auto XDISTANCE = m_monitor->m_size.x + *PWORKSPACEGAP; - const auto YDISTANCE = m_monitor->m_size.y + *PWORKSPACEGAP; - - PHLWORKSPACE pSwitchedTo = nullptr; - - if ((abs(m_delta) < SWIPEDISTANCE * *PSWIPEPERC && (*PSWIPEFORC == 0 || (*PSWIPEFORC != 0 && m_avgSpeed < *PSWIPEFORC))) || abs(m_delta) < 2) { - // revert - if (abs(m_delta) < 2) { - if (PWORKSPACEL) - PWORKSPACEL->m_renderOffset->setValueAndWarp(Vector2D(0, 0)); - if (PWORKSPACER) - PWORKSPACER->m_renderOffset->setValueAndWarp(Vector2D(0, 0)); - m_workspaceBegin->m_renderOffset->setValueAndWarp(Vector2D(0, 0)); - } else { - if (m_delta < 0) { - // to left - - if (PWORKSPACEL) { - if (VERTANIMS) - *PWORKSPACEL->m_renderOffset = Vector2D{0.0, -YDISTANCE}; - else - *PWORKSPACEL->m_renderOffset = Vector2D{-XDISTANCE, 0.0}; - } - } else if (PWORKSPACER) { - // to right - if (VERTANIMS) - *PWORKSPACER->m_renderOffset = Vector2D{0.0, YDISTANCE}; - else - *PWORKSPACER->m_renderOffset = Vector2D{XDISTANCE, 0.0}; - } - - *m_workspaceBegin->m_renderOffset = Vector2D(); - } - - pSwitchedTo = m_workspaceBegin; - } else if (m_delta < 0) { - // switch to left - const auto RENDEROFFSET = PWORKSPACEL ? PWORKSPACEL->m_renderOffset->value() : Vector2D(); - - if (PWORKSPACEL) - m_monitor->changeWorkspace(workspaceIDLeft); - else { - m_monitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDLeft, m_monitor->m_id)); - PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft); - } - - PWORKSPACEL->m_renderOffset->setValue(RENDEROFFSET); - PWORKSPACEL->m_alpha->setValueAndWarp(1.f); - - m_workspaceBegin->m_renderOffset->setValue(RENDEROFFSETMIDDLE); - if (VERTANIMS) - *m_workspaceBegin->m_renderOffset = Vector2D(0.0, YDISTANCE); - else - *m_workspaceBegin->m_renderOffset = Vector2D(XDISTANCE, 0.0); - m_workspaceBegin->m_alpha->setValueAndWarp(1.f); - - g_pInputManager->unconstrainMouse(); - - Log::logger->log(Log::DEBUG, "Ended swipe to the left"); - - pSwitchedTo = PWORKSPACEL; - } else { - // switch to right - const auto RENDEROFFSET = PWORKSPACER ? PWORKSPACER->m_renderOffset->value() : Vector2D(); - - if (PWORKSPACER) - m_monitor->changeWorkspace(workspaceIDRight); - else { - m_monitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDRight, m_monitor->m_id)); - PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); - } - - PWORKSPACER->m_renderOffset->setValue(RENDEROFFSET); - PWORKSPACER->m_alpha->setValueAndWarp(1.f); - - m_workspaceBegin->m_renderOffset->setValue(RENDEROFFSETMIDDLE); - if (VERTANIMS) - *m_workspaceBegin->m_renderOffset = Vector2D(0.0, -YDISTANCE); - else - *m_workspaceBegin->m_renderOffset = Vector2D(-XDISTANCE, 0.0); - m_workspaceBegin->m_alpha->setValueAndWarp(1.f); - - g_pInputManager->unconstrainMouse(); - - Log::logger->log(Log::DEBUG, "Ended swipe to the right"); - - pSwitchedTo = PWORKSPACER; - } - - g_pHyprRenderer->damageMonitor(m_monitor.lock()); - - if (PWORKSPACEL) - PWORKSPACEL->m_forceRendering = false; - if (PWORKSPACER) - PWORKSPACER->m_forceRendering = false; - m_workspaceBegin->m_forceRendering = false; - - m_workspaceBegin = nullptr; - m_initialDirection = 0; - - g_pInputManager->refocus(); - - // apply alpha - for (auto const& ls : Desktop::focusState()->monitor()->m_layerSurfaceLayers[2]) { - *ls->m_alpha = pSwitchedTo->m_hasFullscreenWindow && pSwitchedTo->m_fullscreenMode == FSMODE_FULLSCREEN ? 0.f : 1.f; - } -} diff --git a/src/managers/input/UnifiedWorkspaceSwipeGesture.hpp b/src/managers/input/UnifiedWorkspaceSwipeGesture.hpp deleted file mode 100644 index 4dbb6c5d..00000000 --- a/src/managers/input/UnifiedWorkspaceSwipeGesture.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "../../helpers/memory/Memory.hpp" -#include "../../desktop/DesktopTypes.hpp" - -class CUnifiedWorkspaceSwipeGesture { - public: - void begin(); - void update(double delta); - void end(); - - bool isGestureInProgress(); - - private: - PHLWORKSPACE m_workspaceBegin = nullptr; - PHLMONITORREF m_monitor; - - double m_delta = 0; - int m_initialDirection = 0; - float m_avgSpeed = 0; - int m_speedPoints = 0; - int m_touchID = 0; - - friend class CWorkspaceSwipeGesture; - friend class CInputManager; -}; - -inline UP g_pUnifiedWorkspaceSwipe = makeUnique(); diff --git a/src/managers/input/trackpad/GestureTypes.hpp b/src/managers/input/trackpad/GestureTypes.hpp deleted file mode 100644 index 89f69638..00000000 --- a/src/managers/input/trackpad/GestureTypes.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -enum eTrackpadGestureDirection : uint8_t { - TRACKPAD_GESTURE_DIR_NONE = 0, - TRACKPAD_GESTURE_DIR_SWIPE, - TRACKPAD_GESTURE_DIR_LEFT, - TRACKPAD_GESTURE_DIR_RIGHT, - TRACKPAD_GESTURE_DIR_UP, - TRACKPAD_GESTURE_DIR_DOWN, - TRACKPAD_GESTURE_DIR_VERTICAL, - TRACKPAD_GESTURE_DIR_HORIZONTAL, - TRACKPAD_GESTURE_DIR_PINCH, - TRACKPAD_GESTURE_DIR_PINCH_OUT, - TRACKPAD_GESTURE_DIR_PINCH_IN, -}; \ No newline at end of file diff --git a/src/managers/input/trackpad/TrackpadGestures.cpp b/src/managers/input/trackpad/TrackpadGestures.cpp deleted file mode 100644 index e054c2f9..00000000 --- a/src/managers/input/trackpad/TrackpadGestures.cpp +++ /dev/null @@ -1,250 +0,0 @@ -#include "TrackpadGestures.hpp" - -#include "../InputManager.hpp" -#include "../../../config/ConfigValue.hpp" -#include "../../../protocols/ShortcutsInhibit.hpp" - -#include - -void CTrackpadGestures::clearGestures() { - m_gestures.clear(); -} - -eTrackpadGestureDirection CTrackpadGestures::dirForString(const std::string_view& s) { - std::string lc = std::string{s}; - std::ranges::transform(lc, lc.begin(), ::tolower); - - if (lc == "swipe") - return TRACKPAD_GESTURE_DIR_SWIPE; - if (lc == "left" || lc == "l") - return TRACKPAD_GESTURE_DIR_LEFT; - if (lc == "right" || lc == "r") - return TRACKPAD_GESTURE_DIR_RIGHT; - if (lc == "up" || lc == "u" || lc == "top" || lc == "t") - return TRACKPAD_GESTURE_DIR_UP; - if (lc == "down" || lc == "d" || lc == "bottom" || lc == "b") - return TRACKPAD_GESTURE_DIR_DOWN; - if (lc == "horizontal" || lc == "horiz") - return TRACKPAD_GESTURE_DIR_HORIZONTAL; - if (lc == "vertical" || lc == "vert") - return TRACKPAD_GESTURE_DIR_VERTICAL; - if (lc == "pinch") - return TRACKPAD_GESTURE_DIR_PINCH; - if (lc == "pinchin" || lc == "zoomin") - return TRACKPAD_GESTURE_DIR_PINCH_IN; - if (lc == "pinchout" || lc == "zoomout") - return TRACKPAD_GESTURE_DIR_PINCH_OUT; - - return TRACKPAD_GESTURE_DIR_NONE; -} - -const char* CTrackpadGestures::stringForDir(eTrackpadGestureDirection dir) { - switch (dir) { - case TRACKPAD_GESTURE_DIR_HORIZONTAL: return "HORIZONTAL"; - case TRACKPAD_GESTURE_DIR_VERTICAL: return "VERTICAL"; - case TRACKPAD_GESTURE_DIR_LEFT: return "LEFT"; - case TRACKPAD_GESTURE_DIR_RIGHT: return "RIGHT"; - case TRACKPAD_GESTURE_DIR_UP: return "UP"; - case TRACKPAD_GESTURE_DIR_DOWN: return "DOWN"; - case TRACKPAD_GESTURE_DIR_SWIPE: return "SWIPE"; - case TRACKPAD_GESTURE_DIR_PINCH: return "PINCH"; - case TRACKPAD_GESTURE_DIR_PINCH_IN: return "PINCH_IN"; - case TRACKPAD_GESTURE_DIR_PINCH_OUT: return "PINCH_OUT"; - default: return "ERROR"; - } - return "ERROR"; -} - -std::expected CTrackpadGestures::addGesture(UP&& gesture, size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, - float deltaScale, bool disableInhibit) { - for (const auto& g : m_gestures) { - if (g->fingerCount != fingerCount) - continue; - - if (g->modMask != modMask) - continue; - - eTrackpadGestureDirection axis = TRACKPAD_GESTURE_DIR_NONE; - switch (direction) { - case TRACKPAD_GESTURE_DIR_UP: - case TRACKPAD_GESTURE_DIR_DOWN: - case TRACKPAD_GESTURE_DIR_VERTICAL: axis = TRACKPAD_GESTURE_DIR_VERTICAL; break; - case TRACKPAD_GESTURE_DIR_LEFT: - case TRACKPAD_GESTURE_DIR_RIGHT: - case TRACKPAD_GESTURE_DIR_HORIZONTAL: axis = TRACKPAD_GESTURE_DIR_HORIZONTAL; break; - case TRACKPAD_GESTURE_DIR_SWIPE: axis = TRACKPAD_GESTURE_DIR_SWIPE; break; - case TRACKPAD_GESTURE_DIR_PINCH: - case TRACKPAD_GESTURE_DIR_PINCH_IN: - case TRACKPAD_GESTURE_DIR_PINCH_OUT: axis = TRACKPAD_GESTURE_DIR_PINCH; break; - default: TRACKPAD_GESTURE_DIR_NONE; break; - } - - if (g->direction == axis || g->direction == direction || - ((axis == TRACKPAD_GESTURE_DIR_VERTICAL || axis == TRACKPAD_GESTURE_DIR_HORIZONTAL) && g->direction == TRACKPAD_GESTURE_DIR_SWIPE)) { - return std::unexpected( - std::format("Gesture will be overshadowed by a previous gesture. Previous {} shadows new {}", stringForDir(g->direction), stringForDir(direction))); - } - } - - m_gestures.emplace_back(makeShared(std::move(gesture), fingerCount, modMask, direction, deltaScale, disableInhibit)); - - return {}; -} - -std::expected CTrackpadGestures::removeGesture(size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, float deltaScale, - bool disableInhibit) { - const auto IT = std::ranges::find_if(m_gestures, [&](const auto& g) { - return g->fingerCount == fingerCount && g->direction == direction && g->modMask == modMask && g->deltaScale == deltaScale && g->disableInhibit == disableInhibit; - }); - - if (IT == m_gestures.end()) - return std::unexpected("Can't remove a non-existent gesture"); - - std::erase(m_gestures, *IT); - - return {}; -} - -void CTrackpadGestures::gestureBegin(const IPointer::SSwipeBeginEvent& e) { - if (m_activeGesture) { - Log::logger->log(Log::ERR, "CTrackpadGestures::gestureBegin (swipe) but m_activeGesture is already present"); - return; - } - - m_gestureFindFailed = false; - m_currentTotalDelta = {}; - - // nothing here. We need to wait for the first update to determine the delta. -} - -void CTrackpadGestures::gestureUpdate(const IPointer::SSwipeUpdateEvent& e) { - static auto PDISABLEINHIBIT = CConfigValue("binds:disable_keybind_grabbing"); - - if (m_gestureFindFailed) - return; - - m_currentTotalDelta += e.delta; - - // 5 was chosen because I felt like that's a good number. - if (!m_activeGesture && (std::abs(m_currentTotalDelta.x) < 5 && std::abs(m_currentTotalDelta.y) < 5)) { - Log::logger->log(Log::TRACE, "CTrackpadGestures::gestureUpdate (swipe): gesture delta too small to start considering, waiting"); - return; - } - - if (!m_activeGesture) { - // try to find a gesture that matches our current state - - auto direction = TRACKPAD_GESTURE_DIR_NONE; - auto axis = std::abs(m_currentTotalDelta.x) > std::abs(m_currentTotalDelta.y) ? TRACKPAD_GESTURE_DIR_HORIZONTAL : TRACKPAD_GESTURE_DIR_VERTICAL; - - if (axis == TRACKPAD_GESTURE_DIR_HORIZONTAL) - direction = m_currentTotalDelta.x < 0 ? TRACKPAD_GESTURE_DIR_LEFT : TRACKPAD_GESTURE_DIR_RIGHT; - else - direction = m_currentTotalDelta.y < 0 ? TRACKPAD_GESTURE_DIR_UP : TRACKPAD_GESTURE_DIR_DOWN; - - const auto MODS = g_pInputManager->getModsFromAllKBs(); - - for (const auto& g : m_gestures) { - if (g->direction != axis && g->direction != direction && g->direction != TRACKPAD_GESTURE_DIR_SWIPE) - continue; - - if (g->fingerCount != e.fingers) - continue; - - if (g->modMask != MODS) - continue; - - if (PROTO::shortcutsInhibit->isInhibited() && !*PDISABLEINHIBIT && !g->disableInhibit) - continue; - - m_activeGesture = g; - g->currentDirection = g->gesture->isDirectionSensitive() ? g->direction : direction; - m_activeGesture->gesture->begin({.swipe = &e, .direction = direction, .scale = g->deltaScale}); - break; - } - - if (!m_activeGesture) { - m_gestureFindFailed = true; - return; - } - } - - m_activeGesture->gesture->update({.swipe = &e, .direction = m_activeGesture->currentDirection, .scale = m_activeGesture->deltaScale}); -} - -void CTrackpadGestures::gestureEnd(const IPointer::SSwipeEndEvent& e) { - if (!m_activeGesture) - return; - - m_activeGesture->gesture->end({.swipe = &e, .direction = m_activeGesture->direction, .scale = m_activeGesture->deltaScale}); - - m_activeGesture.reset(); -} - -void CTrackpadGestures::gestureBegin(const IPointer::SPinchBeginEvent& e) { - if (m_activeGesture) { - Log::logger->log(Log::ERR, "CTrackpadGestures::gestureBegin (pinch) but m_activeGesture is already present"); - return; - } - - m_gestureFindFailed = false; - - // nothing here. We need to wait for the first update to determine the delta. -} - -void CTrackpadGestures::gestureUpdate(const IPointer::SPinchUpdateEvent& e) { - static auto PDISABLEINHIBIT = CConfigValue("binds:disable_keybind_grabbing"); - - if (m_gestureFindFailed) - return; - - // 0.1 was chosen because I felt like that's a good number. - if (!m_activeGesture && std::abs(e.scale - 1.F) < 0.1) { - Log::logger->log(Log::TRACE, "CTrackpadGestures::gestureUpdate (pinch): gesture delta too small to start considering, waiting"); - return; - } - - if (!m_activeGesture) { - // try to find a gesture that matches our current state - - auto direction = e.scale < 1.F ? TRACKPAD_GESTURE_DIR_PINCH_OUT : TRACKPAD_GESTURE_DIR_PINCH_IN; - auto axis = TRACKPAD_GESTURE_DIR_PINCH; - - const auto MODS = g_pInputManager->getModsFromAllKBs(); - - for (const auto& g : m_gestures) { - if (g->direction != axis && g->direction != direction) - continue; - - if (g->fingerCount != e.fingers) - continue; - - if (g->modMask != MODS) - continue; - - if (PROTO::shortcutsInhibit->isInhibited() && !*PDISABLEINHIBIT && !g->disableInhibit) - continue; - - m_activeGesture = g; - g->currentDirection = g->gesture->isDirectionSensitive() ? g->direction : direction; - m_activeGesture->gesture->begin({.pinch = &e, .direction = direction}); - break; - } - - if (!m_activeGesture) { - m_gestureFindFailed = true; - return; - } - } - - m_activeGesture->gesture->update({.pinch = &e, .direction = m_activeGesture->currentDirection}); -} - -void CTrackpadGestures::gestureEnd(const IPointer::SPinchEndEvent& e) { - if (!m_activeGesture) - return; - - m_activeGesture->gesture->end({.pinch = &e, .direction = m_activeGesture->direction}); - - m_activeGesture.reset(); -} diff --git a/src/managers/input/trackpad/TrackpadGestures.hpp b/src/managers/input/trackpad/TrackpadGestures.hpp deleted file mode 100644 index ecf11c40..00000000 --- a/src/managers/input/trackpad/TrackpadGestures.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "../../../devices/IPointer.hpp" - -#include "gestures/ITrackpadGesture.hpp" -#include "GestureTypes.hpp" - -#include -#include - -class CTrackpadGestures { - public: - void clearGestures(); - std::expected addGesture(UP&& gesture, size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, float deltaScale, - bool disableInhibit); - std::expected removeGesture(size_t fingerCount, eTrackpadGestureDirection direction, uint32_t modMask, float deltaScale, bool disableInhibit); - - void gestureBegin(const IPointer::SSwipeBeginEvent& e); - void gestureUpdate(const IPointer::SSwipeUpdateEvent& e); - void gestureEnd(const IPointer::SSwipeEndEvent& e); - - void gestureBegin(const IPointer::SPinchBeginEvent& e); - void gestureUpdate(const IPointer::SPinchUpdateEvent& e); - void gestureEnd(const IPointer::SPinchEndEvent& e); - - eTrackpadGestureDirection dirForString(const std::string_view& s); - const char* stringForDir(eTrackpadGestureDirection dir); - - private: - struct SGestureData { - UP gesture; - size_t fingerCount = 0; - uint32_t modMask = 0; - eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE; // configured dir - float deltaScale = 1.F; - bool disableInhibit = false; - eTrackpadGestureDirection currentDirection = TRACKPAD_GESTURE_DIR_NONE; // actual dir of that select swipe - }; - - std::vector> m_gestures; - - Vector2D m_currentTotalDelta = {}; - SP m_activeGesture = nullptr; - bool m_gestureFindFailed = false; -}; - -inline UP g_pTrackpadGestures = makeUnique(); diff --git a/src/managers/input/trackpad/gestures/CloseGesture.cpp b/src/managers/input/trackpad/gestures/CloseGesture.cpp deleted file mode 100644 index 0c37ee36..00000000 --- a/src/managers/input/trackpad/gestures/CloseGesture.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "CloseGesture.hpp" - -#include "../../../../Compositor.hpp" -#include "../../../../managers/animation/DesktopAnimationManager.hpp" -#include "../../../../render/Renderer.hpp" -#include "../../../../managers/eventLoop/EventLoopManager.hpp" -#include "../../../../managers/eventLoop/EventLoopTimer.hpp" -#include "../../../../config/ConfigValue.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../layout/target/Target.hpp" - -constexpr const float MAX_DISTANCE = 200.F; - -static std::vector> trackpadCloseTimers; - -// -static Vector2D lerpVal(const Vector2D& from, const Vector2D& to, const float& t) { - return Vector2D{ - from.x + ((to.x - from.x) * t), - from.y + ((to.y - from.y) * t), - }; -} - -static float lerpVal(const float& from, const float& to, const float& t) { - return from + ((to - from) * t); -} - -void CCloseTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ITrackpadGesture::begin(e); - - m_window = Desktop::focusState()->window(); - - if (!m_window) - return; - - m_alphaFrom = m_window->m_alpha->goal(); - m_posFrom = m_window->m_realPosition->goal(); - m_sizeFrom = m_window->m_realSize->goal(); - - g_pDesktopAnimationManager->startAnimation(m_window.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT, true); - *m_window->m_alpha = 0.f; - - m_alphaTo = m_window->m_alpha->goal(); - m_posTo = m_window->m_realPosition->goal(); - m_sizeTo = m_window->m_realSize->goal(); - - m_window->m_alpha->setValueAndWarp(m_alphaFrom); - m_window->m_realPosition->setValueAndWarp(m_posFrom); - m_window->m_realSize->setValueAndWarp(m_sizeFrom); - - m_lastDelta = 0.F; -} - -void CCloseTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) { - if (!m_window) - return; - - g_pHyprRenderer->damageWindow(m_window.lock()); - - m_lastDelta += distance(e); - - const auto FADEPERCENT = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F); - - m_window->m_alpha->setValueAndWarp(lerpVal(m_alphaFrom, m_alphaTo, FADEPERCENT)); - m_window->m_realPosition->setValueAndWarp(lerpVal(m_posFrom, m_posTo, FADEPERCENT)); - m_window->m_realSize->setValueAndWarp(lerpVal(m_sizeFrom, m_sizeTo, FADEPERCENT)); - - g_pDecorationPositioner->onWindowUpdate(m_window.lock()); - - g_pHyprRenderer->damageWindow(m_window.lock()); -} - -void CCloseTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) { - static const auto PTIMEOUT = CConfigValue("gestures:close_max_timeout"); - - if (!m_window) - return; - - const auto COMPLETION = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F); - - if (COMPLETION < 0.2F) { - // revert the animation - g_pHyprRenderer->damageWindow(m_window.lock()); - *m_window->m_alpha = m_alphaFrom; - *m_window->m_realPosition = m_posFrom; - *m_window->m_realSize = m_sizeFrom; - return; - } - - // commence. Close the window and restore our current state to avoid a harsh anim - const auto CURRENT_ALPHA = m_window->m_alpha->value(); - const auto CURRENT_POS = m_window->m_realPosition->value(); - const auto CURRENT_SIZE = m_window->m_realSize->value(); - - g_pCompositor->closeWindow(m_window.lock()); - - m_window->m_alpha->setValueAndWarp(CURRENT_ALPHA); - m_window->m_realPosition->setValueAndWarp(CURRENT_POS); - m_window->m_realSize->setValueAndWarp(CURRENT_SIZE); - - // this is a kinda hack, but oh well. - m_window->m_realPosition->setCallbackOnBegin( - [CURRENT_POS, window = m_window](auto) { - if (!window || !window->m_isMapped) - return; - - window->m_realPosition->setValueAndWarp(CURRENT_POS); - }, - false); - - m_window->m_realSize->setCallbackOnBegin( - [CURRENT_SIZE, window = m_window](auto) { - if (!window || !window->m_isMapped) - return; - - window->m_realSize->setValueAndWarp(CURRENT_SIZE); - }, - false); - - // we give windows 2s to close. If they don't, pop them back in. - auto timer = makeShared( - std::chrono::milliseconds(*PTIMEOUT), - [window = m_window](SP self, void* data) { - std::erase(trackpadCloseTimers, self); - - // if after 2 seconds the window is still alive and mapped, we revert our changes. - if (!window) - return; - - window->m_realPosition->setCallbackOnBegin(nullptr); - window->m_realSize->setCallbackOnBegin(nullptr); - - if (!window->m_isMapped) - return; - - window->layoutTarget()->recalc(); - window->updateDecorationValues(); - window->sendWindowSize(true); - *window->m_alpha = 1.F; - }, - nullptr); - trackpadCloseTimers.emplace_back(timer); - g_pEventLoopManager->addTimer(timer); - - m_window.reset(); -} diff --git a/src/managers/input/trackpad/gestures/CloseGesture.hpp b/src/managers/input/trackpad/gestures/CloseGesture.hpp deleted file mode 100644 index a8e18fd2..00000000 --- a/src/managers/input/trackpad/gestures/CloseGesture.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" - -#include "../../../../desktop/DesktopTypes.hpp" - -class CCloseTrackpadGesture : public ITrackpadGesture { - public: - CCloseTrackpadGesture() = default; - virtual ~CCloseTrackpadGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - private: - PHLWINDOWREF m_window; - - Vector2D m_posFrom, m_posTo, m_sizeFrom, m_sizeTo; - float m_alphaFrom = 0.F, m_alphaTo = 0.F; - - float m_lastDelta = 0.F; -}; diff --git a/src/managers/input/trackpad/gestures/CursorZoomGesture.cpp b/src/managers/input/trackpad/gestures/CursorZoomGesture.cpp deleted file mode 100644 index 97dfe158..00000000 --- a/src/managers/input/trackpad/gestures/CursorZoomGesture.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "CursorZoomGesture.hpp" - -#include "../../../../Compositor.hpp" -#include "../../../../helpers/Monitor.hpp" - -CCursorZoomTrackpadGesture::CCursorZoomTrackpadGesture(const std::string& first, const std::string& second) { - try { - m_zoomValue = std::stof(first); - } catch (...) { ; } - - if (second == "mult") - m_mode = MODE_MULT; -} - -void CCursorZoomTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ITrackpadGesture::begin(e); - - if (m_mode == MODE_TOGGLE) - m_zoomed = !m_zoomed; - - for (auto const& m : g_pCompositor->m_monitors) { - switch (m_mode) { - case MODE_TOGGLE: - static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); - *m->m_cursorZoom = m_zoomed ? m_zoomValue : *PZOOMFACTOR; - break; - case MODE_MULT: *m->m_cursorZoom = std::clamp(m->m_cursorZoom->goal() * m_zoomValue, 1.0F, 100.0F); break; - } - } -} - -void CCursorZoomTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) {} -void CCursorZoomTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {} diff --git a/src/managers/input/trackpad/gestures/CursorZoomGesture.hpp b/src/managers/input/trackpad/gestures/CursorZoomGesture.hpp deleted file mode 100644 index b53c81e9..00000000 --- a/src/managers/input/trackpad/gestures/CursorZoomGesture.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" - -class CCursorZoomTrackpadGesture : public ITrackpadGesture { - public: - CCursorZoomTrackpadGesture(const std::string& zoomLevel, const std::string& mode); - virtual ~CCursorZoomTrackpadGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - private: - float m_zoomValue = 1.0; - inline static bool m_zoomed = false; - - enum eMode : uint8_t { - MODE_TOGGLE = 0, - MODE_MULT, - }; - - eMode m_mode = MODE_TOGGLE; -}; diff --git a/src/managers/input/trackpad/gestures/DispatcherGesture.cpp b/src/managers/input/trackpad/gestures/DispatcherGesture.cpp deleted file mode 100644 index 4d76a671..00000000 --- a/src/managers/input/trackpad/gestures/DispatcherGesture.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "DispatcherGesture.hpp" - -#include "../../../../managers/KeybindManager.hpp" - -CDispatcherTrackpadGesture::CDispatcherTrackpadGesture(const std::string& dispatcher, const std::string& data) : m_dispatcher(dispatcher), m_data(data) { - ; -} - -void CDispatcherTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ; // intentionally blank -} - -void CDispatcherTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) { - ; // intentionally blank -} - -void CDispatcherTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) { - if (!g_pKeybindManager->m_dispatchers.contains(m_dispatcher)) - return; - - g_pKeybindManager->m_dispatchers.at(m_dispatcher)(m_data); -} diff --git a/src/managers/input/trackpad/gestures/DispatcherGesture.hpp b/src/managers/input/trackpad/gestures/DispatcherGesture.hpp deleted file mode 100644 index b15abbed..00000000 --- a/src/managers/input/trackpad/gestures/DispatcherGesture.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" - -class CDispatcherTrackpadGesture : public ITrackpadGesture { - public: - CDispatcherTrackpadGesture(const std::string& dispatcher, const std::string& data); - virtual ~CDispatcherTrackpadGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - private: - std::string m_dispatcher, m_data; -}; diff --git a/src/managers/input/trackpad/gestures/FloatGesture.cpp b/src/managers/input/trackpad/gestures/FloatGesture.cpp deleted file mode 100644 index 0849bfac..00000000 --- a/src/managers/input/trackpad/gestures/FloatGesture.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "FloatGesture.hpp" - -#include "../../../../render/Renderer.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../desktop/view/Window.hpp" -#include "../../../../layout/LayoutManager.hpp" -#include "../../../../layout/target/WindowTarget.hpp" - -constexpr const float MAX_DISTANCE = 250.F; - -// -static Vector2D lerpVal(const Vector2D& from, const Vector2D& to, const float& t) { - return Vector2D{ - from.x + ((to.x - from.x) * t), - from.y + ((to.y - from.y) * t), - }; -} - -CFloatTrackpadGesture::CFloatTrackpadGesture(const std::string_view& data) { - std::string lc = std::string{data}; - std::ranges::transform(lc, lc.begin(), ::tolower); - - if (lc.starts_with("float")) - m_mode = FLOAT_MODE_FLOAT; - else if (lc.starts_with("tile")) - m_mode = FLOAT_MODE_TILE; - else - m_mode = FLOAT_MODE_TOGGLE; -} - -void CFloatTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ITrackpadGesture::begin(e); - - m_window = Desktop::focusState()->window(); - - if (!m_window) - return; - - if ((m_window->m_isFloating && m_mode == FLOAT_MODE_FLOAT) || (!m_window->m_isFloating && m_mode == FLOAT_MODE_TILE)) { - m_window.reset(); - return; - } - - g_layoutManager->changeFloatingMode(m_window->layoutTarget()); - - m_posFrom = m_window->m_realPosition->begun(); - m_sizeFrom = m_window->m_realSize->begun(); - - m_posTo = m_window->m_realPosition->goal(); - m_sizeTo = m_window->m_realSize->goal(); - - m_lastDelta = 0.F; -} - -void CFloatTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) { - if (!m_window) - return; - - g_pHyprRenderer->damageWindow(m_window.lock()); - - m_lastDelta += distance(e); - - const auto FADEPERCENT = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F); - - m_window->m_realPosition->setValueAndWarp(lerpVal(m_posFrom, m_posTo, FADEPERCENT)); - m_window->m_realSize->setValueAndWarp(lerpVal(m_sizeFrom, m_sizeTo, FADEPERCENT)); - - g_pDecorationPositioner->onWindowUpdate(m_window.lock()); - - g_pHyprRenderer->damageWindow(m_window.lock()); -} - -void CFloatTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) { - if (!m_window) - return; - - const auto COMPLETION = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F); - - if (COMPLETION < 0.2F) { - // revert the animation - g_pHyprRenderer->damageWindow(m_window.lock()); - g_layoutManager->changeFloatingMode(m_window->layoutTarget()); - return; - } - - *m_window->m_realPosition = m_posTo; - *m_window->m_realSize = m_sizeTo; -} diff --git a/src/managers/input/trackpad/gestures/FloatGesture.hpp b/src/managers/input/trackpad/gestures/FloatGesture.hpp deleted file mode 100644 index 4132a1ac..00000000 --- a/src/managers/input/trackpad/gestures/FloatGesture.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" - -#include "../../../../desktop/DesktopTypes.hpp" - -class CFloatTrackpadGesture : public ITrackpadGesture { - public: - CFloatTrackpadGesture(const std::string_view& mode); - virtual ~CFloatTrackpadGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - private: - PHLWINDOWREF m_window; - - Vector2D m_posFrom, m_posTo, m_sizeFrom, m_sizeTo; - - float m_lastDelta = 0; - - enum eMode : uint8_t { - FLOAT_MODE_TOGGLE = 0, - FLOAT_MODE_FLOAT, - FLOAT_MODE_TILE, - }; - - eMode m_mode = FLOAT_MODE_TOGGLE; -}; diff --git a/src/managers/input/trackpad/gestures/FullscreenGesture.cpp b/src/managers/input/trackpad/gestures/FullscreenGesture.cpp deleted file mode 100644 index a219b685..00000000 --- a/src/managers/input/trackpad/gestures/FullscreenGesture.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "FullscreenGesture.hpp" - -#include "../../../../Compositor.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../render/Renderer.hpp" -#include "../../../animation/DesktopAnimationManager.hpp" - -constexpr const float MAX_DISTANCE = 250.F; - -// -static Vector2D lerpVal(const Vector2D& from, const Vector2D& to, const float& t) { - return Vector2D{ - from.x + ((to.x - from.x) * t), - from.y + ((to.y - from.y) * t), - }; -} - -CFullscreenTrackpadGesture::CFullscreenTrackpadGesture(const std::string_view& mode) { - std::string lc = std::string{mode}; - std::ranges::transform(lc, lc.begin(), ::tolower); - - if (lc.starts_with("fullscreen")) - m_mode = MODE_FULLSCREEN; - else if (lc.starts_with("maximize")) - m_mode = MODE_MAXIMIZE; - else - m_mode = MODE_FULLSCREEN; -} - -void CFullscreenTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ITrackpadGesture::begin(e); - - m_window = Desktop::focusState()->window(); - - if (!m_window) - return; - - m_posFrom = m_window->m_realPosition->goal(); - m_sizeFrom = m_window->m_realSize->goal(); - - m_originalMode = m_window->m_fullscreenState.internal; - - g_pCompositor->setWindowFullscreenInternal(m_window.lock(), m_window->m_fullscreenState.internal == FSMODE_NONE ? fsModeForMode(m_mode) : FSMODE_NONE); - - m_posTo = m_window->m_realPosition->goal(); - m_sizeTo = m_window->m_realSize->goal(); - - m_lastDelta = 0.F; -} - -void CFullscreenTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) { - if (!m_window) - return; - - g_pHyprRenderer->damageWindow(m_window.lock()); - - m_lastDelta += distance(e); - - const auto FADEPERCENT = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F); - - m_window->m_realPosition->setValueAndWarp(lerpVal(m_posFrom, m_posTo, FADEPERCENT)); - m_window->m_realSize->setValueAndWarp(lerpVal(m_sizeFrom, m_sizeTo, FADEPERCENT)); - - g_pDesktopAnimationManager->overrideFullscreenFadeAmount(m_window->m_workspace, m_originalMode == FSMODE_NONE ? 1.F - FADEPERCENT : FADEPERCENT, m_window.lock()); - - g_pDecorationPositioner->onWindowUpdate(m_window.lock()); - - g_pHyprRenderer->damageWindow(m_window.lock()); -} - -void CFullscreenTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) { - if (!m_window) - return; - - const auto COMPLETION = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F); - - if (COMPLETION < 0.2F) { - // revert the animation - g_pHyprRenderer->damageWindow(m_window.lock()); - g_pDesktopAnimationManager->overrideFullscreenFadeAmount(m_window->m_workspace, m_originalMode == FSMODE_NONE ? 1.F : 0.F, m_window.lock()); - g_pCompositor->setWindowFullscreenInternal(m_window.lock(), m_window->m_fullscreenState.internal == FSMODE_NONE ? m_originalMode : FSMODE_NONE); - return; - } - - *m_window->m_realPosition = m_posTo; - *m_window->m_realSize = m_sizeTo; - g_pDesktopAnimationManager->overrideFullscreenFadeAmount(m_window->m_workspace, m_originalMode == FSMODE_NONE ? 0.F : 1.F); -} - -eFullscreenMode CFullscreenTrackpadGesture::fsModeForMode(eMode mode) { - switch (mode) { - case MODE_FULLSCREEN: return FSMODE_FULLSCREEN; - case MODE_MAXIMIZE: return FSMODE_MAXIMIZED; - default: break; - } - return FSMODE_FULLSCREEN; -} diff --git a/src/managers/input/trackpad/gestures/FullscreenGesture.hpp b/src/managers/input/trackpad/gestures/FullscreenGesture.hpp deleted file mode 100644 index dd125e5b..00000000 --- a/src/managers/input/trackpad/gestures/FullscreenGesture.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" - -#include "../../../../desktop/DesktopTypes.hpp" -#include "../../../../desktop/Workspace.hpp" - -class CFullscreenTrackpadGesture : public ITrackpadGesture { - public: - CFullscreenTrackpadGesture(const std::string_view& mode); - virtual ~CFullscreenTrackpadGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - private: - PHLWINDOWREF m_window; - - Vector2D m_posFrom, m_posTo, m_sizeFrom, m_sizeTo; - - float m_lastDelta = 0; - - enum eMode : uint8_t { - MODE_FULLSCREEN = 0, - MODE_MAXIMIZE, - }; - - eMode m_mode = MODE_FULLSCREEN; - eFullscreenMode m_originalMode = FSMODE_NONE; - - eFullscreenMode fsModeForMode(eMode mode); -}; diff --git a/src/managers/input/trackpad/gestures/ITrackpadGesture.cpp b/src/managers/input/trackpad/gestures/ITrackpadGesture.cpp deleted file mode 100644 index afc96627..00000000 --- a/src/managers/input/trackpad/gestures/ITrackpadGesture.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "ITrackpadGesture.hpp" - -// scale the pinch "scale" to match our imaginary delta units -constexpr const float PINCH_DELTA_SCALE = 400.F; -constexpr const float PINCH_DELTA_SCALE_OUT_ADD = 1.6F; - -// -void ITrackpadGesture::begin(const STrackpadGestureBegin& e) { - m_lastPinchScale = 1.F; - m_scale = e.scale; -} - -float ITrackpadGesture::distance(const STrackpadGestureBegin& e) { - if (e.direction == TRACKPAD_GESTURE_DIR_LEFT || e.direction == TRACKPAD_GESTURE_DIR_RIGHT || e.direction == TRACKPAD_GESTURE_DIR_HORIZONTAL) - return m_scale * (e.direction == TRACKPAD_GESTURE_DIR_LEFT ? -e.swipe->delta.x : e.swipe->delta.x); - if (e.direction == TRACKPAD_GESTURE_DIR_UP || e.direction == TRACKPAD_GESTURE_DIR_DOWN || e.direction == TRACKPAD_GESTURE_DIR_VERTICAL) - return m_scale * (e.direction == TRACKPAD_GESTURE_DIR_UP ? -e.swipe->delta.y : e.swipe->delta.y); - if (e.direction == TRACKPAD_GESTURE_DIR_SWIPE) - return m_scale * (e.swipe->delta.size()); - if (e.direction == TRACKPAD_GESTURE_DIR_PINCH || e.direction == TRACKPAD_GESTURE_DIR_PINCH_IN || e.direction == TRACKPAD_GESTURE_DIR_PINCH_OUT) { - const auto Δ = m_lastPinchScale - e.pinch->scale; - m_lastPinchScale = e.pinch->scale; - return m_scale * ((e.direction == TRACKPAD_GESTURE_DIR_PINCH_IN ? -Δ : Δ * PINCH_DELTA_SCALE_OUT_ADD) * PINCH_DELTA_SCALE); - } - - return m_scale * (e.swipe ? e.swipe->delta.size() : e.pinch->delta.size()); -} - -float ITrackpadGesture::distance(const STrackpadGestureUpdate& e) { - return ITrackpadGesture::distance(STrackpadGestureBegin{ - .swipe = e.swipe, - .pinch = e.pinch, - .direction = e.direction, - .scale = e.scale, - }); -} - -bool ITrackpadGesture::isDirectionSensitive() { - return false; -} diff --git a/src/managers/input/trackpad/gestures/ITrackpadGesture.hpp b/src/managers/input/trackpad/gestures/ITrackpadGesture.hpp deleted file mode 100644 index a5108a2c..00000000 --- a/src/managers/input/trackpad/gestures/ITrackpadGesture.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "../../../../devices/IPointer.hpp" -#include "../GestureTypes.hpp" - -class ITrackpadGesture { - public: - virtual ~ITrackpadGesture() = default; - - struct STrackpadGestureBegin { - // this has update because we wait for the delta - const IPointer::SSwipeUpdateEvent* swipe = nullptr; - const IPointer::SPinchUpdateEvent* pinch = nullptr; - eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE; - float scale = 1.F; - }; - - struct STrackpadGestureUpdate { - const IPointer::SSwipeUpdateEvent* swipe = nullptr; - const IPointer::SPinchUpdateEvent* pinch = nullptr; - eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE; - float scale = 1.F; - }; - - struct STrackpadGestureEnd { - const IPointer::SSwipeEndEvent* swipe = nullptr; - const IPointer::SPinchEndEvent* pinch = nullptr; - eTrackpadGestureDirection direction = TRACKPAD_GESTURE_DIR_NONE; - float scale = 1.F; - }; - - virtual void begin(const STrackpadGestureBegin& e); - virtual void update(const STrackpadGestureUpdate& e) = 0; - virtual void end(const STrackpadGestureEnd& e) = 0; - - virtual float distance(const STrackpadGestureBegin& e); - virtual float distance(const STrackpadGestureUpdate& e); - - virtual bool isDirectionSensitive(); - - protected: - float m_lastPinchScale = 1.F, m_scale = 1.F; -}; \ No newline at end of file diff --git a/src/managers/input/trackpad/gestures/MoveGesture.cpp b/src/managers/input/trackpad/gestures/MoveGesture.cpp deleted file mode 100644 index 0dcc310f..00000000 --- a/src/managers/input/trackpad/gestures/MoveGesture.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "MoveGesture.hpp" - -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../desktop/view/Window.hpp" -#include "../../../../render/Renderer.hpp" -#include "../../../../layout/LayoutManager.hpp" - -void CMoveTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ITrackpadGesture::begin(e); - - m_window = Desktop::focusState()->window(); - m_lastDelta = {}; -} - -void CMoveTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) { - if (!m_window) - return; - - const auto DELTA = e.swipe ? e.swipe->delta : e.pinch->delta; - - if (m_window->m_isFloating) { - g_layoutManager->moveTarget(DELTA, m_window->layoutTarget()); - m_window->m_realSize->warp(); - m_window->m_realPosition->warp(); - return; - } - - // tiled window -> displace, then execute a move dispatcher on end. - - g_pHyprRenderer->damageWindow(m_window.lock()); - - // funny name but works on tiled too lmao - m_lastDelta += DELTA; - m_window->m_floatingOffset = (m_lastDelta * 0.5F).clamp(Vector2D{-100.F, -100.F}, Vector2D{100.F, 100.F}); - - g_pHyprRenderer->damageWindow(m_window.lock()); -} - -void CMoveTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) { - - if (!m_window) - return; - - if (m_window->m_isFloating || m_lastDelta.size() < 0.1F) - return; - - // tiled: attempt to move window in the given direction - - const auto WINDOWPOS = m_window->m_realPosition->goal() + m_window->m_floatingOffset; - - m_window->m_floatingOffset = {}; - - if (std::abs(m_lastDelta.x) > std::abs(m_lastDelta.y)) { - // horizontal - g_layoutManager->moveInDirection(m_window->layoutTarget(), m_lastDelta.x > 0 ? "r" : "l"); - } else { - // vertical - g_layoutManager->moveInDirection(m_window->layoutTarget(), m_lastDelta.y > 0 ? "b" : "t"); - } - - const auto GOAL = m_window->m_realPosition->goal(); - - m_window->m_realPosition->setValueAndWarp(WINDOWPOS); - *m_window->m_realPosition = GOAL; - - m_window.reset(); -} diff --git a/src/managers/input/trackpad/gestures/MoveGesture.hpp b/src/managers/input/trackpad/gestures/MoveGesture.hpp deleted file mode 100644 index 3a045561..00000000 --- a/src/managers/input/trackpad/gestures/MoveGesture.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" - -#include "../../../../desktop/DesktopTypes.hpp" - -class CMoveTrackpadGesture : public ITrackpadGesture { - public: - CMoveTrackpadGesture() = default; - virtual ~CMoveTrackpadGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - private: - PHLWINDOWREF m_window; - Vector2D m_lastDelta; -}; diff --git a/src/managers/input/trackpad/gestures/ResizeGesture.cpp b/src/managers/input/trackpad/gestures/ResizeGesture.cpp deleted file mode 100644 index ffc7704b..00000000 --- a/src/managers/input/trackpad/gestures/ResizeGesture.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "ResizeGesture.hpp" - -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../desktop/view/Window.hpp" -#include "../../../../render/Renderer.hpp" -#include "../../../../layout/LayoutManager.hpp" - -void CResizeTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ITrackpadGesture::begin(e); - - m_window = Desktop::focusState()->window(); -} - -void CResizeTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) { - if (!m_window) - return; - - g_pHyprRenderer->damageWindow(m_window.lock()); - - g_layoutManager->resizeTarget((e.swipe ? e.swipe->delta : e.pinch->delta), m_window->layoutTarget(), - Layout::cornerFromBox(m_window->getWindowMainSurfaceBox(), g_pInputManager->getMouseCoordsInternal())); - m_window->m_realSize->warp(); - m_window->m_realPosition->warp(); - - g_pHyprRenderer->damageWindow(m_window.lock()); -} - -void CResizeTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) { - m_window.reset(); -} diff --git a/src/managers/input/trackpad/gestures/ResizeGesture.hpp b/src/managers/input/trackpad/gestures/ResizeGesture.hpp deleted file mode 100644 index 0b47a224..00000000 --- a/src/managers/input/trackpad/gestures/ResizeGesture.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" - -#include "../../../../desktop/DesktopTypes.hpp" - -class CResizeTrackpadGesture : public ITrackpadGesture { - public: - CResizeTrackpadGesture() = default; - virtual ~CResizeTrackpadGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - private: - PHLWINDOWREF m_window; -}; diff --git a/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.cpp b/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.cpp deleted file mode 100644 index b3643c05..00000000 --- a/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "SpecialWorkspaceGesture.hpp" - -#include "../../../../Compositor.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../render/Renderer.hpp" - -#include -using namespace Hyprutils::Memory; - -constexpr const float MAX_DISTANCE = 150.F; - -// -static Vector2D lerpVal(const Vector2D& from, const Vector2D& to, const float& t) { - return Vector2D{ - from.x + ((to.x - from.x) * t), - from.y + ((to.y - from.y) * t), - }; -} - -static float lerpVal(const float& from, const float& to, const float& t) { - return from + ((to - from) * t); -} - -CSpecialWorkspaceGesture::CSpecialWorkspaceGesture(const std::string& workspaceName) : m_specialWorkspaceName(workspaceName) { - ; -} - -void CSpecialWorkspaceGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ITrackpadGesture::begin(e); - - m_specialWorkspace.reset(); - m_lastDelta = 0.F; - m_monitor.reset(); - - m_specialWorkspace = g_pCompositor->getWorkspaceByName("special:" + m_specialWorkspaceName); - - if (m_specialWorkspace) { - m_animatingOut = m_specialWorkspace->isVisible(); - m_monitor = m_animatingOut ? m_specialWorkspace->m_monitor : Desktop::focusState()->monitor(); - - if (!m_monitor) - return; - - if (!m_animatingOut) - m_monitor->setSpecialWorkspace(m_specialWorkspace); - } else { - m_monitor = Desktop::focusState()->monitor(); - - if (!m_monitor) - return; - - m_animatingOut = false; - - const auto& [workspaceID, workspaceName, isAutoID] = getWorkspaceIDNameFromString("special:" + m_specialWorkspaceName); - const auto WS = g_pCompositor->createNewWorkspace(workspaceID, m_monitor->m_id, workspaceName); - m_monitor->setSpecialWorkspace(WS); - m_specialWorkspace = WS; - } - - if (!m_specialWorkspace) - return; - - m_monitorDimFrom = m_monitor->m_specialFade->begun(); - m_monitorDimTo = m_monitor->m_specialFade->goal(); - m_workspaceAlphaFrom = m_specialWorkspace->m_alpha->begun(); - m_workspaceAlphaTo = m_specialWorkspace->m_alpha->goal(); - m_workspaceOffsetFrom = m_specialWorkspace->m_renderOffset->begun(); - m_workspaceOffsetTo = m_specialWorkspace->m_renderOffset->goal(); -} - -void CSpecialWorkspaceGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) { - if (!m_specialWorkspace || !m_monitor) - return; - - g_pHyprRenderer->damageMonitor(m_specialWorkspace->m_monitor.lock()); - - m_lastDelta += distance(e); - - const auto FADEPERCENT = m_animatingOut ? 1.F - std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F) : std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F); - - m_monitor->m_specialFade->setValueAndWarp(lerpVal(m_monitorDimFrom, m_monitorDimTo, FADEPERCENT)); - m_specialWorkspace->m_alpha->setValueAndWarp(lerpVal(m_workspaceAlphaFrom, m_workspaceAlphaTo, FADEPERCENT)); - m_specialWorkspace->m_renderOffset->setValueAndWarp(lerpVal(m_workspaceOffsetFrom, m_workspaceOffsetTo, FADEPERCENT)); -} - -void CSpecialWorkspaceGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) { - if (!m_specialWorkspace || !m_monitor) - return; - - const auto COMPLETION = std::clamp(m_lastDelta / MAX_DISTANCE, 0.F, 1.F); - - if (COMPLETION < 0.3F) { - // cancel the operation, which effectively means just flip the animation direction - // also flip goals if animating in - m_animatingOut = !m_animatingOut; - - if (m_animatingOut) { - m_workspaceOffsetTo = m_workspaceOffsetFrom; - m_workspaceAlphaTo = m_workspaceAlphaFrom; - m_monitorDimTo = m_monitorDimFrom; - } - } - - if (m_animatingOut) { - const auto CURR_WS_ALPHA = m_specialWorkspace->m_alpha->value(); - const auto CURR_WS_OFFSET = m_specialWorkspace->m_renderOffset->value(); - const auto CURR_MON_FADE = m_monitor->m_specialFade->value(); - - m_monitor->setSpecialWorkspace(nullptr); - - const auto GOAL_WS_ALPHA = m_specialWorkspace->m_alpha->goal(); - const auto GOAL_WS_OFFSET = m_specialWorkspace->m_renderOffset->goal(); - - m_monitor->m_specialFade->setValueAndWarp(CURR_MON_FADE); - m_specialWorkspace->m_alpha->setValueAndWarp(CURR_WS_ALPHA); - m_specialWorkspace->m_renderOffset->setValueAndWarp(CURR_WS_OFFSET); - - *m_monitor->m_specialFade = 0.F; - *m_specialWorkspace->m_alpha = GOAL_WS_ALPHA; - *m_specialWorkspace->m_renderOffset = GOAL_WS_OFFSET; - } else { - *m_monitor->m_specialFade = m_monitorDimTo; - *m_specialWorkspace->m_renderOffset = m_workspaceOffsetTo; - *m_specialWorkspace->m_alpha = m_workspaceAlphaTo; - } -} diff --git a/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp b/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp deleted file mode 100644 index 0cc62529..00000000 --- a/src/managers/input/trackpad/gestures/SpecialWorkspaceGesture.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" - -#include "../../../../desktop/DesktopTypes.hpp" - -class CSpecialWorkspaceGesture : public ITrackpadGesture { - public: - CSpecialWorkspaceGesture(const std::string& workspaceName); - virtual ~CSpecialWorkspaceGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - private: - std::string m_specialWorkspaceName; - PHLWORKSPACE m_specialWorkspace; - PHLMONITORREF m_monitor; - bool m_animatingOut = false; - float m_lastDelta = 0.F; - - // animated properties, kinda sucks - float m_monitorDimFrom = 0.F, m_monitorDimTo = 0.F; - float m_workspaceAlphaFrom = 0.F, m_workspaceAlphaTo = 0.F; - Vector2D m_workspaceOffsetFrom = {}, m_workspaceOffsetTo = {}; -}; diff --git a/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.cpp b/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.cpp deleted file mode 100644 index 0ccd2462..00000000 --- a/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "WorkspaceSwipeGesture.hpp" - -#include "../../../../Compositor.hpp" -#include "../../../../desktop/state/FocusState.hpp" -#include "../../../../render/Renderer.hpp" - -#include "../../UnifiedWorkspaceSwipeGesture.hpp" - -void CWorkspaceSwipeGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) { - ITrackpadGesture::begin(e); - - static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); - - if (g_pSessionLockManager->isSessionLocked() || g_pUnifiedWorkspaceSwipe->isGestureInProgress()) - return; - - int onMonitor = 0; - for (auto const& w : g_pCompositor->getWorkspaces()) { - if (w->m_monitor == Desktop::focusState()->monitor() && !g_pCompositor->isWorkspaceSpecial(w->m_id)) - onMonitor++; - } - - if (onMonitor < 2 && !*PSWIPENEW) - return; // disallow swiping when there's 1 workspace on a monitor - - g_pUnifiedWorkspaceSwipe->begin(); -} - -void CWorkspaceSwipeGesture::update(const ITrackpadGesture::STrackpadGestureUpdate& e) { - if (!g_pUnifiedWorkspaceSwipe->isGestureInProgress()) - return; - - const float DELTA = distance(e); - - static auto PSWIPEINVR = CConfigValue("gestures:workspace_swipe_invert"); - - const double D = g_pUnifiedWorkspaceSwipe->m_delta + (*PSWIPEINVR ? -DELTA : DELTA); - g_pUnifiedWorkspaceSwipe->update(D); -} - -void CWorkspaceSwipeGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) { - if (!g_pUnifiedWorkspaceSwipe->isGestureInProgress()) - return; - - g_pUnifiedWorkspaceSwipe->end(); -} - -bool CWorkspaceSwipeGesture::isDirectionSensitive() { - return true; -} diff --git a/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp b/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp deleted file mode 100644 index 203fc329..00000000 --- a/src/managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "ITrackpadGesture.hpp" -#include "../../../../desktop/DesktopTypes.hpp" - -class CWorkspaceSwipeGesture : public ITrackpadGesture { - public: - CWorkspaceSwipeGesture() = default; - virtual ~CWorkspaceSwipeGesture() = default; - - virtual void begin(const ITrackpadGesture::STrackpadGestureBegin& e); - virtual void update(const ITrackpadGesture::STrackpadGestureUpdate& e); - virtual void end(const ITrackpadGesture::STrackpadGestureEnd& e); - - virtual bool isDirectionSensitive(); -}; diff --git a/src/managers/permissions/DynamicPermissionManager.cpp b/src/managers/permissions/DynamicPermissionManager.cpp deleted file mode 100644 index d63a72a0..00000000 --- a/src/managers/permissions/DynamicPermissionManager.cpp +++ /dev/null @@ -1,363 +0,0 @@ -#include -#include "DynamicPermissionManager.hpp" -#include -#include -#include "../../Compositor.hpp" -#include "../../config/ConfigValue.hpp" -#include "../../helpers/MiscFunctions.hpp" -#include "../../i18n/Engine.hpp" - -#include -using namespace Hyprutils::String; - -#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) -#include -#endif - -static void clientDestroyInternal(struct wl_listener* listener, void* data) { - SDynamicPermissionRuleDestroyWrapper* wrap = wl_container_of(listener, wrap, listener); - CDynamicPermissionRule* rule = wrap->parent; - g_pDynamicPermissionManager->removeRulesForClient(rule->client()); -} - -CDynamicPermissionRule::CDynamicPermissionRule(const std::string& binaryPathRegex, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode) : - m_type(type), m_source(PERMISSION_RULE_SOURCE_CONFIG), m_binaryRegex(makeUnique(binaryPathRegex)), m_allowMode(defaultAllowMode) { - ; -} - -CDynamicPermissionRule::CDynamicPermissionRule(wl_client* const client, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode) : - m_type(type), m_source(PERMISSION_RULE_SOURCE_RUNTIME_USER), m_client(client), m_allowMode(defaultAllowMode) { - wl_list_init(&m_destroyWrapper.listener.link); - m_destroyWrapper.listener.notify = ::clientDestroyInternal; - m_destroyWrapper.parent = this; - wl_display_add_destroy_listener(g_pCompositor->m_wlDisplay, &m_destroyWrapper.listener); -} - -CDynamicPermissionRule::~CDynamicPermissionRule() { - if (m_client) { - wl_list_remove(&m_destroyWrapper.listener.link); - wl_list_init(&m_destroyWrapper.listener.link); - } - - if (m_dialogBox && m_dialogBox->isRunning()) - m_dialogBox->kill(); -} - -wl_client* CDynamicPermissionRule::client() const { - return m_client; -} - -static const char* permissionToString(eDynamicPermissionType type) { - switch (type) { - case PERMISSION_TYPE_UNKNOWN: return "PERMISSION_TYPE_UNKNOWN"; - case PERMISSION_TYPE_SCREENCOPY: return "PERMISSION_TYPE_SCREENCOPY"; - case PERMISSION_TYPE_PLUGIN: return "PERMISSION_TYPE_PLUGIN"; - case PERMISSION_TYPE_KEYBOARD: return "PERMISSION_TYPE_KEYBOARD"; - case PERMISSION_TYPE_CURSOR_POS: return "PERMISSION_TYPE_CURSOR_POS"; - } - - return "error"; -} - -static const char* specialPidToString(eSpecialPidTypes type) { - switch (type) { - case SPECIAL_PID_TYPE_CONFIG: return "config"; - default: return ""; - } -} - -void CDynamicPermissionManager::clearConfigPermissions() { - std::erase_if(m_rules, [](const auto& e) { return e->m_source == PERMISSION_RULE_SOURCE_CONFIG; }); -} - -void CDynamicPermissionManager::addConfigPermissionRule(const std::string& binaryName, eDynamicPermissionType type, eDynamicPermissionAllowMode mode) { - m_rules.emplace_back(SP(new CDynamicPermissionRule(binaryName, type, mode))); -} - -eDynamicPermissionAllowMode CDynamicPermissionManager::clientPermissionMode(wl_client* client, eDynamicPermissionType permission) { - - static auto PPERM = CConfigValue("ecosystem:enforce_permissions"); - - if (*PPERM == 0) - return PERMISSION_RULE_ALLOW_MODE_ALLOW; - - const auto LOOKUP = binaryNameForWlClient(client); - - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: checking permission {} for client {:x} (binary {})", permissionToString(permission), - rc(client), LOOKUP.has_value() ? LOOKUP.value() : "lookup failed: " + LOOKUP.error()); - - // first, check if we have the client + perm combo in our cache. - auto it = std::ranges::find_if(m_rules, [client, permission](const auto& e) { return e->m_client == client && e->m_type == permission; }); - if (it == m_rules.end()) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission not cached, checking binary name"); - - if (!LOOKUP.has_value()) - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: binary name check failed"); - else { - const auto BINNAME = LOOKUP.value().contains("/") ? LOOKUP.value().substr(LOOKUP.value().find_last_of('/') + 1) : LOOKUP.value(); - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: binary path {}, name {}", LOOKUP.value(), BINNAME); - - it = std::ranges::find_if(m_rules, [clientBinaryPath = LOOKUP.value(), permission](const auto& e) { - if (e->m_type != permission) - return false; // wrong perm - - if (!e->m_binaryPath.empty() && e->m_binaryPath == clientBinaryPath) - return true; // matches binary path - - if (!e->m_binaryRegex) - return false; // wl_client* rule - - // regex match - if (RE2::FullMatch(clientBinaryPath, *e->m_binaryRegex)) - return true; - - return false; - }); - - if (it == m_rules.end()) - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: no rule for binary"); - else { - if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ALLOW) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission allowed by config rule"); - return PERMISSION_RULE_ALLOW_MODE_ALLOW; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_DENY) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission denied by config rule"); - return PERMISSION_RULE_ALLOW_MODE_DENY; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_PENDING) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission pending by config rule"); - return PERMISSION_RULE_ALLOW_MODE_PENDING; - } else - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission ask by config rule"); - } - } - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ALLOW) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission allowed before by user"); - return PERMISSION_RULE_ALLOW_MODE_ALLOW; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_DENY) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission denied before by user"); - return PERMISSION_RULE_ALLOW_MODE_DENY; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_PENDING) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission pending before by user"); - return PERMISSION_RULE_ALLOW_MODE_PENDING; - } - - // if we are here, we need to ask, that's the fallback for all these (keyboards won't come here) - askForPermission(client, LOOKUP.value_or(""), permission); - - return PERMISSION_RULE_ALLOW_MODE_PENDING; -} - -eDynamicPermissionAllowMode CDynamicPermissionManager::clientPermissionModeWithString(pid_t pid, const std::string& str, eDynamicPermissionType permission) { - static auto PPERM = CConfigValue("ecosystem:enforce_permissions"); - - if (*PPERM == 0) - return PERMISSION_RULE_ALLOW_MODE_ALLOW; - - std::optional binaryName; - std::expected lookup; - - if (pid > 0) { - lookup = binaryNameForPid(pid); - - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: checking permission {} for key {} (binary {})", permissionToString(permission), str, - lookup.has_value() ? lookup.value() : "lookup failed: " + lookup.error()); - - if (lookup.has_value()) - binaryName = *lookup; - } else - binaryName = specialPidToString(sc(pid)); - - // first, check if we have the client + perm combo in our cache. - auto it = std::ranges::find_if(m_rules, [str, permission, pid](const auto& e) { return e->m_keyString == str && pid && pid == e->m_pid && e->m_type == permission; }); - if (it == m_rules.end()) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission not cached, checking key"); - - it = std::ranges::find_if(m_rules, [key = str, permission, &lookup](const auto& e) { - if (e->m_type != permission) - return false; // wrong perm - - if (!e->m_binaryRegex) - return false; // no regex - - // regex match - if (RE2::FullMatch(key, *e->m_binaryRegex) || (lookup.has_value() && RE2::FullMatch(lookup.value(), *e->m_binaryRegex))) - return true; - - return false; - }); - - if (it == m_rules.end()) - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: no rule for key"); - else { - if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ALLOW) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission allowed by config rule"); - return PERMISSION_RULE_ALLOW_MODE_ALLOW; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_DENY) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission denied by config rule"); - return PERMISSION_RULE_ALLOW_MODE_DENY; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_PENDING) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission pending by config rule"); - return PERMISSION_RULE_ALLOW_MODE_PENDING; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ASK) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission ask by config rule"); - askForPermission(nullptr, str, permission, pid); - return PERMISSION_RULE_ALLOW_MODE_PENDING; - } else - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission ask by config rule"); - } - - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_ALLOW) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission allowed before by user"); - return PERMISSION_RULE_ALLOW_MODE_ALLOW; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_DENY) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission denied before by user"); - return PERMISSION_RULE_ALLOW_MODE_DENY; - } else if ((*it)->m_allowMode == PERMISSION_RULE_ALLOW_MODE_PENDING) { - Log::logger->log(Log::TRACE, "CDynamicPermissionManager::clientHasPermission: permission pending before by user"); - return PERMISSION_RULE_ALLOW_MODE_PENDING; - } - - // keyboards are allow default - if (permission == PERMISSION_TYPE_KEYBOARD) - return PERMISSION_RULE_ALLOW_MODE_ALLOW; - - // if we are here, we need to ask. - askForPermission(nullptr, str, permission, pid); - - return PERMISSION_RULE_ALLOW_MODE_PENDING; -} - -void CDynamicPermissionManager::askForPermission(wl_client* client, const std::string& binaryPath, eDynamicPermissionType type, pid_t pid) { - auto rule = m_rules.emplace_back(SP(new CDynamicPermissionRule(client, type, PERMISSION_RULE_ALLOW_MODE_PENDING))); - - if (!client) - rule->m_keyString = binaryPath; - - rule->m_pid = pid; - - std::string appName = ""; - if (binaryPath.empty()) - appName = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, {{"wayland_id", std::format("{:x}", rc(client))}}); - else if (client) { - appName = binaryPath.contains("/") ? binaryPath.substr(binaryPath.find_last_of('/') + 1) : binaryPath; - } else { - if (pid < 0) - appName = specialPidToString(sc(pid)); - else { - const auto LOOKUP = binaryNameForPid(pid); - appName = LOOKUP.value_or(I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_UNKNOWN_NAME)); - } - } - - std::string description = ""; - switch (rule->m_type) { - case PERMISSION_TYPE_SCREENCOPY: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, {{"app", appName}}); break; - case PERMISSION_TYPE_CURSOR_POS: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_CURSOR_POS, {{"app", appName}}); break; - case PERMISSION_TYPE_PLUGIN: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_PLUGIN, {{"app", appName}, {"plugin", binaryPath}}); break; - case PERMISSION_TYPE_KEYBOARD: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_KEYBOARD, {{"keyboard", binaryPath}}); break; - case PERMISSION_TYPE_UNKNOWN: description = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_REQUEST_UNKNOWN, {{"app", appName}}); break; - } - - std::vector options; - const auto ALLOW = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW); - const auto ALLOW_AND_REMEMBER = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER); - const auto ALLOW_ONCE = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_ALLOW_ONCE); - const auto DENY = I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_DENY); - - if (!binaryPath.empty() && client) { - description += std::format("

{}", I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_PERSISTENCE_HINT)); - options = {DENY, ALLOW_AND_REMEMBER, ALLOW_ONCE}; - } else - options = {DENY, ALLOW}; - - rule->m_dialogBox = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_PERMISSION_TITLE), description, options); - rule->m_dialogBox->m_priority = true; - - if (!rule->m_dialogBox) { - Log::logger->log(Log::ERR, "CDynamicPermissionManager::askForPermission: hyprland-guiutils likely missing, cannot ask! Disabling permission control..."); - rule->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW; - return; - } - - rule->m_promise = rule->m_dialogBox->open(); - rule->m_promise->then([r = WP(rule), binaryPath, ALLOW, ALLOW_AND_REMEMBER, ALLOW_ONCE, DENY](SP> pr) { - if (!r) - return; - - if (pr->hasError()) { - // not reachable for now - Log::logger->log(Log::TRACE, "CDynamicPermissionRule: error spawning dialog box"); - if (r->m_promiseResolverForExternal) - r->m_promiseResolverForExternal->reject("error spawning dialog box"); - r->m_promiseResolverForExternal.reset(); - return; - } - - const std::string& result = pr->result(); - - Log::logger->log(Log::TRACE, "CDynamicPermissionRule: user returned {}", result); - - if (result.starts_with(ALLOW_ONCE)) - r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW; - else if (result.starts_with(DENY)) { - r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_DENY; - r->m_binaryPath = binaryPath; - } else if (result.starts_with(ALLOW_AND_REMEMBER)) { - r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW; - r->m_binaryPath = binaryPath; - } else if (result.starts_with(ALLOW)) - r->m_allowMode = PERMISSION_RULE_ALLOW_MODE_ALLOW; - - if (r->m_promiseResolverForExternal) - r->m_promiseResolverForExternal->resolve(r->m_allowMode); - - r->m_promise.reset(); - r->m_promiseResolverForExternal.reset(); - }); -} - -SP> CDynamicPermissionManager::promiseFor(wl_client* client, eDynamicPermissionType permission) { - auto rule = std::ranges::find_if(m_rules, [&client, &permission](const auto& e) { return e->m_client == client && e->m_type == permission; }); - if (rule == m_rules.end()) - return nullptr; - - if (!(*rule)->m_promise) - return nullptr; - - if ((*rule)->m_promiseResolverForExternal) - return nullptr; - - return CPromise::make([rule](SP> r) { (*rule)->m_promiseResolverForExternal = r; }); -} - -SP> CDynamicPermissionManager::promiseFor(const std::string& key, eDynamicPermissionType permission) { - auto rule = std::ranges::find_if(m_rules, [&key, &permission](const auto& e) { return e->m_keyString == key && e->m_type == permission; }); - if (rule == m_rules.end()) - return nullptr; - - if (!(*rule)->m_promise) - return nullptr; - - if ((*rule)->m_promiseResolverForExternal) - return nullptr; - - return CPromise::make([rule](SP> r) { (*rule)->m_promiseResolverForExternal = r; }); -} - -SP> CDynamicPermissionManager::promiseFor(pid_t pid, const std::string& key, eDynamicPermissionType permission) { - auto rule = std::ranges::find_if(m_rules, [&pid, &permission, &key](const auto& e) { return e->m_pid == pid && e->m_keyString == key && e->m_type == permission; }); - if (rule == m_rules.end()) - return nullptr; - - if (!(*rule)->m_promise) - return nullptr; - - if ((*rule)->m_promiseResolverForExternal) - return nullptr; - - return CPromise::make([rule](SP> r) { (*rule)->m_promiseResolverForExternal = r; }); -} - -void CDynamicPermissionManager::removeRulesForClient(wl_client* client) { - std::erase_if(m_rules, [client](const auto& e) { return e->m_client == client; }); -} diff --git a/src/managers/permissions/DynamicPermissionManager.hpp b/src/managers/permissions/DynamicPermissionManager.hpp deleted file mode 100644 index 423596c3..00000000 --- a/src/managers/permissions/DynamicPermissionManager.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include "../../macros.hpp" -#include "../../helpers/memory/Memory.hpp" -#include "../../helpers/AsyncDialogBox.hpp" -#include -#include -#include -#include "../../helpers/defer/Promise.hpp" - -// NOLINTNEXTLINE -namespace re2 { - class RE2; -}; - -enum eDynamicPermissionType : uint8_t { - PERMISSION_TYPE_UNKNOWN = 0, - PERMISSION_TYPE_SCREENCOPY, - PERMISSION_TYPE_PLUGIN, - PERMISSION_TYPE_KEYBOARD, - PERMISSION_TYPE_CURSOR_POS, -}; - -enum eDynamicPermissionRuleSource : uint8_t { - PERMISSION_RULE_SOURCE_UNKNOWN = 0, - PERMISSION_RULE_SOURCE_CONFIG, - PERMISSION_RULE_SOURCE_RUNTIME_USER, -}; - -enum eDynamicPermissionAllowMode : uint8_t { - PERMISSION_RULE_ALLOW_MODE_UNKNOWN = 0, - PERMISSION_RULE_ALLOW_MODE_DENY, - PERMISSION_RULE_ALLOW_MODE_ASK, - PERMISSION_RULE_ALLOW_MODE_ALLOW, - PERMISSION_RULE_ALLOW_MODE_PENDING, // popup is open -}; - -// NOLINTNEXTLINE -enum eSpecialPidTypes : int { - SPECIAL_PID_TYPE_CONFIG = -3, - SPECIAL_PID_TYPE_NONE = -2, -}; - -class CDynamicPermissionRule; - -struct SDynamicPermissionRuleDestroyWrapper { - wl_listener listener; - CDynamicPermissionRule* parent = nullptr; -}; - -class CDynamicPermissionRule { - public: - ~CDynamicPermissionRule(); - - wl_client* client() const; - - private: - // config rule - CDynamicPermissionRule(const std::string& binaryPathRegex, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode = PERMISSION_RULE_ALLOW_MODE_ASK); - // user rule - CDynamicPermissionRule(wl_client* const client, eDynamicPermissionType type, eDynamicPermissionAllowMode defaultAllowMode = PERMISSION_RULE_ALLOW_MODE_ASK); - - const eDynamicPermissionType m_type = PERMISSION_TYPE_UNKNOWN; - const eDynamicPermissionRuleSource m_source = PERMISSION_RULE_SOURCE_UNKNOWN; - wl_client* const m_client = nullptr; - std::string m_binaryPath = ""; - UP m_binaryRegex; - std::string m_keyString = ""; - pid_t m_pid = 0; - - eDynamicPermissionAllowMode m_allowMode = PERMISSION_RULE_ALLOW_MODE_ASK; - SP m_dialogBox; // for pending - SP> m_promise; // for pending - SP> m_promiseResolverForExternal; // for external promise - - SDynamicPermissionRuleDestroyWrapper m_destroyWrapper; - - friend class CDynamicPermissionManager; -}; - -class CDynamicPermissionManager { - public: - void clearConfigPermissions(); - void addConfigPermissionRule(const std::string& binaryPath, eDynamicPermissionType type, eDynamicPermissionAllowMode mode); - - // if the rule is "ask", or missing, will pop up a dialog and return false until the user agrees. - // (will continue returning false if the user does not agree, of course.) - eDynamicPermissionAllowMode clientPermissionMode(wl_client* client, eDynamicPermissionType permission); - - // for plugins for now. Pid 0 means unknown - eDynamicPermissionAllowMode clientPermissionModeWithString(pid_t pid, const std::string& str, eDynamicPermissionType permission); - - // get a promise for the result. Returns null if there already was one requested for the client. - // Returns null if state is not pending - SP> promiseFor(wl_client* client, eDynamicPermissionType permission); - SP> promiseFor(const std::string& str, eDynamicPermissionType permission); - SP> promiseFor(pid_t pid, const std::string& key, eDynamicPermissionType permission); - - void removeRulesForClient(wl_client* client); - - private: - void askForPermission(wl_client* client, const std::string& binaryName, eDynamicPermissionType type, pid_t pid = 0); - - // - std::vector> m_rules; -}; - -inline UP g_pDynamicPermissionManager; diff --git a/src/managers/screenshare/CursorshareSession.cpp b/src/managers/screenshare/CursorshareSession.cpp deleted file mode 100644 index 703832ab..00000000 --- a/src/managers/screenshare/CursorshareSession.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include "ScreenshareManager.hpp" -#include "../PointerManager.hpp" -#include "../../protocols/core/Seat.hpp" -#include "../permissions/DynamicPermissionManager.hpp" -#include "../../render/Renderer.hpp" - -using namespace Screenshare; - -CCursorshareSession::CCursorshareSession(wl_client* client, WP pointer) : m_client(client), m_pointer(pointer) { - m_listeners.pointerDestroyed = m_pointer->m_events.destroyed.listen([this] { stop(); }); - m_listeners.cursorChanged = g_pPointerManager->m_events.cursorChanged.listen([this] { - calculateConstraints(); - m_events.constraintsChanged.emit(); - - if (m_pendingFrame.pending) { - if (copy()) - return; - - LOGM(Log::ERR, "Failed to copy cursor image for cursor share"); - if (m_pendingFrame.callback) - m_pendingFrame.callback(RESULT_NOT_COPIED); - m_pendingFrame.pending = false; - return; - } - }); - - calculateConstraints(); -} - -CCursorshareSession::~CCursorshareSession() { - stop(); -} - -void CCursorshareSession::stop() { - if (m_stopped) - return; - m_stopped = true; - m_events.stopped.emit(); -} - -void CCursorshareSession::calculateConstraints() { - const auto& cursorImage = g_pPointerManager->currentCursorImage(); - m_constraintsChanged = true; - - // cursor is hidden, keep the previous constraints and render 0 alpha - if (!cursorImage.pBuffer) - return; - - // TODO: should cursor share have a format bit flip for RGBA? - if (auto attrs = cursorImage.pBuffer->shm(); attrs.success) { - m_format = attrs.format; - } else { - // we only have shm cursors - return; - } - - m_hotspot = cursorImage.hotspot; - m_bufferSize = cursorImage.size; -} - -// TODO: allow render to buffer without monitor and remove monitor param -eScreenshareError CCursorshareSession::share(PHLMONITOR monitor, SP buffer, FSourceBoxCallback sourceBoxCallback, FScreenshareCallback callback) { - if (m_stopped || m_pointer.expired() || m_bufferSize == Vector2D(0, 0)) - return ERROR_STOPPED; - - if UNLIKELY (!buffer || !buffer->m_resource || !buffer->m_resource->good()) { - LOGM(Log::ERR, "Client requested sharing to an invalid buffer"); - return ERROR_NO_BUFFER; - } - - if UNLIKELY (buffer->size != m_bufferSize) { - LOGM(Log::ERR, "Client requested sharing to an invalid buffer size"); - return ERROR_BUFFER_SIZE; - } - - uint32_t bufFormat; - if (buffer->dmabuf().success) - bufFormat = buffer->dmabuf().format; - else if (buffer->shm().success) - bufFormat = buffer->shm().format; - else { - LOGM(Log::ERR, "Client requested sharing to an invalid buffer"); - return ERROR_NO_BUFFER; - } - - if (bufFormat != m_format) { - LOGM(Log::ERR, "Invalid format {} in {:x}", bufFormat, (uintptr_t)this); - return ERROR_BUFFER_FORMAT; - } - - m_pendingFrame.pending = true; - m_pendingFrame.monitor = monitor; - m_pendingFrame.buffer = buffer; - m_pendingFrame.sourceBoxCallback = sourceBoxCallback; - m_pendingFrame.callback = callback; - - // nothing changed, then delay copy until contraints changed - if (!m_constraintsChanged) - return ERROR_NONE; - - if (!copy()) { - LOGM(Log::ERR, "Failed to copy cursor image for cursor share"); - callback(RESULT_NOT_COPIED); - m_pendingFrame.pending = false; - return ERROR_UNKNOWN; - } - - return ERROR_NONE; -} - -void CCursorshareSession::render() { - const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_client, PERMISSION_TYPE_CURSOR_POS); - - const auto& cursorImage = g_pPointerManager->currentCursorImage(); - - // TODO: implement a monitor independent render mode to buffer that does this in CHyprRenderer::begin() or something like that - g_pHyprOpenGL->m_renderData.transformDamage = false; - g_pHyprOpenGL->setViewport(0, 0, m_bufferSize.x, m_bufferSize.y); - - bool overlaps = g_pPointerManager->getCursorBoxGlobal().overlaps(m_pendingFrame.sourceBoxCallback()); - if (PERM != PERMISSION_RULE_ALLOW_MODE_ALLOW || !overlaps) { - // render black when not allowed - g_pHyprOpenGL->clear(Colors::BLACK); - } else if (!cursorImage.pBuffer || !cursorImage.surface || !cursorImage.bufferTex) { - // render clear when cursor is probably hidden - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); - } else { - // render cursor - CBox texbox = {{}, cursorImage.bufferTex->m_size}; - g_pHyprOpenGL->renderTexture(cursorImage.bufferTex, texbox, {}); - } - - g_pHyprOpenGL->m_renderData.blockScreenShader = true; -} - -bool CCursorshareSession::copy() { - if (!m_pendingFrame.callback || !m_pendingFrame.monitor || !m_pendingFrame.callback || !m_pendingFrame.sourceBoxCallback) - return false; - - // FIXME: this doesn't really make sense but just to be safe - m_pendingFrame.callback(RESULT_TIMESTAMP); - - g_pHyprRenderer->makeEGLCurrent(); - - CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - if (auto attrs = m_pendingFrame.buffer->dmabuf(); attrs.success) { - if (attrs.format != m_format) { - LOGM(Log::ERR, "Can't copy: invalid format"); - return false; - } - - if (!g_pHyprRenderer->beginRender(m_pendingFrame.monitor, fakeDamage, RENDER_MODE_TO_BUFFER, m_pendingFrame.buffer, nullptr, true)) { - LOGM(Log::ERR, "Can't copy: failed to begin rendering to dmabuf"); - return false; - } - - render(); - - g_pHyprRenderer->endRender([callback = m_pendingFrame.callback]() { - if (callback) - callback(RESULT_COPIED); - }); - } else if (auto attrs = m_pendingFrame.buffer->shm(); attrs.success) { - auto [bufData, fmt, bufLen] = m_pendingFrame.buffer->beginDataPtr(0); - const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(m_format); - - if (attrs.format != m_format || !PFORMAT) { - LOGM(Log::ERR, "Can't copy: invalid format"); - return false; - } - - auto outFB = g_pHyprRenderer->createFB(); - outFB->alloc(m_bufferSize.x, m_bufferSize.y, m_format); - - if (!g_pHyprRenderer->beginRender(m_pendingFrame.monitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, outFB, true)) { - LOGM(Log::ERR, "Can't copy: failed to begin rendering to shm"); - return false; - } - - render(); - - g_pHyprRenderer->endRender(); - - g_pHyprOpenGL->m_renderData.pMonitor = m_pendingFrame.monitor; - outFB->bind(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, GLFB(outFB)->getFBID()); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - int glFormat = PFORMAT->glFormat; - - if (glFormat == GL_RGBA) - glFormat = GL_BGRA_EXT; - - if (glFormat != GL_BGRA_EXT && glFormat != GL_RGB) { - if (PFORMAT->swizzle.has_value()) { - std::array RGBA = SWIZZLE_RGBA; - std::array BGRA = SWIZZLE_BGRA; - if (PFORMAT->swizzle == RGBA) - glFormat = GL_RGBA; - else if (PFORMAT->swizzle == BGRA) - glFormat = GL_BGRA_EXT; - else { - LOGM(Log::ERR, "Copied frame via shm might be broken or color flipped"); - glFormat = GL_RGBA; - } - } - } - - glReadPixels(0, 0, m_bufferSize.x, m_bufferSize.y, glFormat, PFORMAT->glType, bufData); - - g_pHyprOpenGL->m_renderData.pMonitor.reset(); - - m_pendingFrame.buffer->endDataPtr(); - GLFB(outFB)->unbind(); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - - m_pendingFrame.callback(RESULT_COPIED); - } else { - LOGM(Log::ERR, "Can't copy: invalid buffer type"); - return false; - } - - m_pendingFrame.pending = false; - m_constraintsChanged = false; - return true; -} - -DRMFormat CCursorshareSession::format() const { - return m_format; -} - -Vector2D CCursorshareSession::bufferSize() const { - return m_bufferSize; -} - -Vector2D CCursorshareSession::hotspot() const { - return m_hotspot; -} diff --git a/src/managers/screenshare/ScreenshareFrame.cpp b/src/managers/screenshare/ScreenshareFrame.cpp deleted file mode 100644 index d747ecee..00000000 --- a/src/managers/screenshare/ScreenshareFrame.cpp +++ /dev/null @@ -1,497 +0,0 @@ -#include "ScreenshareManager.hpp" -#include "../PointerManager.hpp" -#include "../input/InputManager.hpp" -#include "../permissions/DynamicPermissionManager.hpp" -#include "../../protocols/ColorManagement.hpp" -#include "../../protocols/XDGShell.hpp" -#include "../../Compositor.hpp" -#include "../../render/Renderer.hpp" -#include "../../render/OpenGL.hpp" -#include "../../helpers/Monitor.hpp" -#include "../../desktop/view/Window.hpp" -#include "../../desktop/state/FocusState.hpp" -#include - -using namespace Screenshare; - -CScreenshareFrame::CScreenshareFrame(WP session, bool overlayCursor, bool isFirst) : - m_session(session), m_bufferSize(m_session->bufferSize()), m_overlayCursor(overlayCursor), m_isFirst(isFirst) { - ; -} - -CScreenshareFrame::~CScreenshareFrame() { - if (m_failed || !m_shared) - return; - - if (!m_copied && m_callback) - m_callback(RESULT_NOT_COPIED); -} - -bool CScreenshareFrame::done() const { - if (m_session.expired() || m_session->m_stopped) - return true; - - if (m_session->m_type == SHARE_NONE || m_bufferSize == Vector2D(0, 0)) - return true; - - if (m_failed || m_copied) - return true; - - if (m_session->m_type == SHARE_MONITOR && !m_session->monitor()) - return true; - - if (m_session->m_type == SHARE_REGION && !m_session->monitor()) - return true; - - if (m_session->m_type == SHARE_WINDOW && (!m_session->monitor() || !validMapped(m_session->m_window))) - return true; - - if (!m_shared) - return false; - - if (!m_buffer || !m_buffer->m_resource || !m_buffer->m_resource->good()) - return true; - - if (!m_callback) - return true; - - return false; -} - -eScreenshareError CScreenshareFrame::share(SP buffer, const CRegion& clientDamage, FScreenshareCallback callback) { - if UNLIKELY (done()) - return ERROR_STOPPED; - - if UNLIKELY (!m_session->monitor() || !g_pCompositor->monitorExists(m_session->monitor())) { - LOGM(Log::ERR, "Client requested sharing of a monitor that is gone"); - m_failed = true; - return ERROR_STOPPED; - } - - if UNLIKELY (m_session->m_type == SHARE_WINDOW && !validMapped(m_session->m_window)) { - LOGM(Log::ERR, "Client requested sharing of window that is gone or not shareable!"); - m_failed = true; - return ERROR_STOPPED; - } - - if UNLIKELY (!buffer || !buffer->m_resource || !buffer->m_resource->good()) { - LOGM(Log::ERR, "Client requested sharing to an invalid buffer"); - return ERROR_NO_BUFFER; - } - - if UNLIKELY (buffer->size != m_bufferSize) { - LOGM(Log::ERR, "Client requested sharing to an invalid buffer size"); - return ERROR_BUFFER_SIZE; - } - - uint32_t bufFormat; - if (buffer->dmabuf().success) - bufFormat = buffer->dmabuf().format; - else if (buffer->shm().success) - bufFormat = buffer->shm().format; - else { - LOGM(Log::ERR, "Client requested sharing to an invalid buffer"); - return ERROR_NO_BUFFER; - } - - if (std::ranges::count_if(m_session->allowedFormats(), [&](const DRMFormat& format) { return format == bufFormat; }) == 0) { - LOGM(Log::ERR, "Invalid format {} in {:x}", bufFormat, (uintptr_t)this); - return ERROR_BUFFER_FORMAT; - } - - m_buffer = buffer; - m_callback = callback; - m_shared = true; - - // schedule a frame so that when a screenshare starts it isn't black until the output is updated - if (m_isFirst) { - g_pCompositor->scheduleFrameForMonitor(m_session->monitor(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); - g_pHyprRenderer->damageMonitor(m_session->monitor()); - } - - // TODO: add a damage ring for output damage since last shared frame - CRegion frameDamage = CRegion(0, 0, m_bufferSize.x, m_bufferSize.y); - - // copy everything on the first frame - if (m_isFirst) - m_damage = CRegion(0, 0, m_bufferSize.x, m_bufferSize.y); - else - m_damage = frameDamage.add(clientDamage); - - m_damage.intersect(0, 0, m_bufferSize.x, m_bufferSize.y); - - return ERROR_NONE; -} - -void CScreenshareFrame::copy() { - if (done()) - return; - - // tell client to send presented timestamp - // TODO: is this right? this is right after we commit to aq, not when page flip happens.. - m_callback(RESULT_TIMESTAMP); - - // store a snapshot before the permission popup so we don't break screenshots - const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_session->m_client, PERMISSION_TYPE_SCREENCOPY); - if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { - if (!m_session->m_tempFB || !m_session->m_tempFB->isAllocated()) - storeTempFB(); - - // don't copy a frame while allow is pending because screenshot tools will only take the first frame we give, which is empty - return; - } - - if (m_buffer->shm().success) - m_failed = !copyShm(); - else if (m_buffer->dmabuf().success) - m_failed = !copyDmabuf(); - - if (!m_failed) { - // screensharing has started again - m_session->screenshareEvents(true); - m_session->m_shareStopTimer->updateTimeout(std::chrono::milliseconds(500)); // check in half second - } else - m_callback(RESULT_NOT_COPIED); -} - -void CScreenshareFrame::renderMonitor() { - if ((m_session->m_type != SHARE_MONITOR && m_session->m_type != SHARE_REGION) || done()) - return; - - const auto PMONITOR = m_session->monitor(); - - auto TEXTURE = g_pHyprRenderer->createTexture(PMONITOR->m_output->state->state().buffer); - - const bool IS_CM_AWARE = PROTO::colorManagement && PROTO::colorManagement->isClientCMAware(m_session->m_client); - g_pHyprOpenGL->m_renderData.transformDamage = false; - g_pHyprOpenGL->m_renderData.noSimplify = true; - - // render monitor texture - CBox monbox = CBox{{}, PMONITOR->m_pixelSize} - .transform(Math::wlTransformToHyprutils(Math::invertTransform(PMONITOR->m_transform)), PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y) - .translate(-m_session->m_captureBox.pos()); // vvvv kinda ass-backwards but that's how I designed the renderer... sigh. - g_pHyprOpenGL->pushMonitorTransformEnabled(true); - g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTexture(TEXTURE, monbox, - { - .cmBackToSRGB = !IS_CM_AWARE, - .cmBackToSRGBSource = !IS_CM_AWARE ? PMONITOR : nullptr, - }); - g_pHyprOpenGL->setRenderModifEnabled(true); - g_pHyprOpenGL->popMonitorTransformEnabled(); - - // render black boxes for noscreenshare - auto hidePopups = [&](Vector2D popupBaseOffset) { - return [&, popupBaseOffset](WP popup, void*) { - if (!popup->wlSurface() || !popup->wlSurface()->resource() || !popup->visible()) - return; - - const auto popRel = popup->coordsRelativeToParent(); - popup->wlSurface()->resource()->breadthfirst( - [&](SP surf, const Vector2D& localOff, void*) { - const auto size = surf->m_current.size; - const auto surfBox = - CBox{popupBaseOffset + popRel + localOff, size}.translate(PMONITOR->m_position).scale(PMONITOR->m_scale).translate(-m_session->m_captureBox.pos()); - - if LIKELY (surfBox.w > 0 && surfBox.h > 0) - g_pHyprOpenGL->renderRect(surfBox, Colors::BLACK, {}); - }, - nullptr); - }; - }; - - for (auto const& l : g_pCompositor->m_layers) { - if (!l->m_ruleApplicator->noScreenShare().valueOrDefault()) - continue; - - if UNLIKELY (!l->visible()) - continue; - - const auto REALPOS = l->m_realPosition->value(); - const auto REALSIZE = l->m_realSize->value(); - - const auto noScreenShareBox = CBox{REALPOS.x, REALPOS.y, std::max(REALSIZE.x, 5.0), std::max(REALSIZE.y, 5.0)} - .translate(-PMONITOR->m_position) - .scale(PMONITOR->m_scale) - .translate(-m_session->m_captureBox.pos()); - - g_pHyprOpenGL->renderRect(noScreenShareBox, Colors::BLACK, {}); - - const auto geom = l->m_geometry; - const Vector2D popupBaseOffset = REALPOS - Vector2D{geom.pos().x, geom.pos().y}; - if (l->m_popupHead) - l->m_popupHead->breadthfirst(hidePopups(popupBaseOffset), nullptr); - } - - for (auto const& w : g_pCompositor->m_windows) { - if (!w->m_ruleApplicator->noScreenShare().valueOrDefault()) - continue; - - if (!g_pHyprRenderer->shouldRenderWindow(w, PMONITOR)) - continue; - - if (w->isHidden()) - continue; - - const auto PWORKSPACE = w->m_workspace; - - if UNLIKELY (!PWORKSPACE && !w->m_fadingOut && w->m_alpha->value() != 0.f) - continue; - - const auto renderOffset = PWORKSPACE && !w->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D{}; - const auto REALPOS = w->m_realPosition->value() + renderOffset; - const auto noScreenShareBox = CBox{REALPOS.x, REALPOS.y, std::max(w->m_realSize->value().x, 5.0), std::max(w->m_realSize->value().y, 5.0)} - .translate(-PMONITOR->m_position) - .scale(PMONITOR->m_scale) - .translate(-m_session->m_captureBox.pos()); - - // seems like rounding doesn't play well with how we manipulate the box position to render regions causing the window to leak through - const auto dontRound = m_session->m_captureBox.pos() != Vector2D() || w->isEffectiveInternalFSMode(FSMODE_FULLSCREEN); - const auto rounding = dontRound ? 0 : w->rounding() * PMONITOR->m_scale; - const auto roundingPower = dontRound ? 2.0f : w->roundingPower(); - - g_pHyprOpenGL->renderRect(noScreenShareBox, Colors::BLACK, {.round = rounding, .roundingPower = roundingPower}); - - if (w->m_isX11 || !w->m_popupHead) - continue; - - const auto geom = w->m_xdgSurface->m_current.geometry; - const Vector2D popupBaseOffset = REALPOS - Vector2D{geom.pos().x, geom.pos().y}; - - w->m_popupHead->breadthfirst(hidePopups(popupBaseOffset), nullptr); - } - - if (m_overlayCursor) { - CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - Vector2D cursorPos = g_pInputManager->getMouseCoordsInternal() - PMONITOR->m_position - m_session->m_captureBox.pos() / PMONITOR->m_scale; - g_pPointerManager->renderSoftwareCursorsFor(PMONITOR, Time::steadyNow(), fakeDamage, cursorPos, true); - } -} - -void CScreenshareFrame::renderWindow() { - if (m_session->m_type != SHARE_WINDOW || done()) - return; - - const auto PWINDOW = m_session->m_window.lock(); - const auto PMONITOR = m_session->monitor(); - - const auto NOW = Time::steadyNow(); - - // TODO: implement a monitor independent render mode to buffer that does this in CHyprRenderer::begin() or something like that - g_pHyprOpenGL->m_renderData.monitorProjection = Mat3x3::identity(); - g_pHyprOpenGL->m_renderData.projection = Mat3x3::outputProjection(m_bufferSize, HYPRUTILS_TRANSFORM_NORMAL); - g_pHyprOpenGL->m_renderData.transformDamage = false; - g_pHyprOpenGL->setViewport(0, 0, m_bufferSize.x, m_bufferSize.y); - - g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(PWINDOW); // block the feedback to avoid spamming the surface if it's visible - g_pHyprRenderer->renderWindow(PWINDOW, PMONITOR, NOW, false, RENDER_PASS_ALL, true, true); - g_pHyprRenderer->m_bBlockSurfaceFeedback = false; - - if (!m_overlayCursor) - return; - - auto pointerSurfaceResource = g_pSeatManager->m_state.pointerFocus.lock(); - - if (!pointerSurfaceResource) - return; - - auto pointerSurface = Desktop::View::CWLSurface::fromResource(pointerSurfaceResource); - if (!pointerSurface) - return; - - auto box = pointerSurface->getSurfaceBoxGlobal(); - if (!box.has_value() || box->intersection(m_session->m_window->getFullWindowBoundingBox()).empty()) - return; - - if (Desktop::focusState()->window() != m_session->m_window) - return; - - CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->m_self.lock(), NOW, fakeDamage, g_pInputManager->getMouseCoordsInternal() - PWINDOW->m_realPosition->value(), true); -} - -void CScreenshareFrame::render() { - const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_session->m_client, PERMISSION_TYPE_SCREENCOPY); - - if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); - return; - } - - bool windowShareDenied = m_session->m_type == SHARE_WINDOW && m_session->m_window->m_ruleApplicator && m_session->m_window->m_ruleApplicator->noScreenShare().valueOrDefault(); - if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY || windowShareDenied) { - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); - CBox texbox = CBox{m_bufferSize / 2.F, g_pHyprOpenGL->m_screencopyDeniedTexture->m_size}.translate(-g_pHyprOpenGL->m_screencopyDeniedTexture->m_size / 2.F); - g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_screencopyDeniedTexture, texbox, {}); - return; - } - - if (m_session->m_tempFB && m_session->m_tempFB->isAllocated()) { - CBox texbox = {{}, m_bufferSize}; - g_pHyprOpenGL->renderTexture(m_session->m_tempFB->getTexture(), texbox, {}); - m_session->m_tempFB->release(); - return; - } - - switch (m_session->m_type) { - case SHARE_REGION: // TODO: could this be better? this is how screencopy works - case SHARE_MONITOR: renderMonitor(); break; - case SHARE_WINDOW: renderWindow(); break; - case SHARE_NONE: - default: return; - } -} - -bool CScreenshareFrame::copyDmabuf() { - if (done()) - return false; - - if (!g_pHyprRenderer->beginRender(m_session->monitor(), m_damage, RENDER_MODE_TO_BUFFER, m_buffer, nullptr, true)) { - LOGM(Log::ERR, "Can't copy: failed to begin rendering to dma frame"); - return false; - } - - render(); - - g_pHyprOpenGL->m_renderData.blockScreenShader = true; - - g_pHyprRenderer->endRender([self = m_self]() { - if (!self || self.expired() || self->m_copied) - return; - - LOGM(Log::TRACE, "Copied frame via dma"); - self->m_callback(RESULT_COPIED); - self->m_copied = true; - }); - - return true; -} - -bool CScreenshareFrame::copyShm() { - if (done()) - return false; - - g_pHyprRenderer->makeEGLCurrent(); - - auto shm = m_buffer->shm(); - auto [pixelData, fmt, bufLen] = m_buffer->beginDataPtr(0); // no need for end, cuz it's shm - - const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(shm.format); - if (!PFORMAT) { - LOGM(Log::ERR, "Can't copy: failed to find a pixel format"); - return false; - } - - const auto PMONITOR = m_session->monitor(); - - auto outFB = g_pHyprRenderer->createFB(); - outFB->alloc(m_bufferSize.x, m_bufferSize.y, shm.format); - - if (!g_pHyprRenderer->beginRender(PMONITOR, m_damage, RENDER_MODE_FULL_FAKE, nullptr, outFB, true)) { - LOGM(Log::ERR, "Can't copy: failed to begin rendering"); - return false; - } - - render(); - - g_pHyprOpenGL->m_renderData.blockScreenShader = true; - - g_pHyprRenderer->endRender(); - - g_pHyprOpenGL->m_renderData.pMonitor = PMONITOR; - outFB->bind(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, GLFB(outFB)->getFBID()); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - uint32_t packStride = NFormatUtils::minStride(PFORMAT, m_bufferSize.x); - int glFormat = PFORMAT->glFormat; - - if (glFormat == GL_RGBA) - glFormat = GL_BGRA_EXT; - - if (glFormat != GL_BGRA_EXT && glFormat != GL_RGB) { - if (PFORMAT->swizzle.has_value()) { - std::array RGBA = SWIZZLE_RGBA; - std::array BGRA = SWIZZLE_BGRA; - if (PFORMAT->swizzle == RGBA) - glFormat = GL_RGBA; - else if (PFORMAT->swizzle == BGRA) - glFormat = GL_BGRA_EXT; - else { - LOGM(Log::ERR, "Copied frame via shm might be broken or color flipped"); - glFormat = GL_RGBA; - } - } - } - - // TODO: use pixel buffer object to not block cpu - if (packStride == sc(shm.stride)) { - m_damage.forEachRect([&](const auto& rect) { - int width = rect.x2 - rect.x1; - int height = rect.y2 - rect.y1; - glReadPixels(rect.x1, rect.y1, width, height, glFormat, PFORMAT->glType, pixelData); - }); - } else { - m_damage.forEachRect([&](const auto& rect) { - size_t width = rect.x2 - rect.x1; - size_t height = rect.y2 - rect.y1; - for (size_t i = rect.y1; i < height; ++i) { - glReadPixels(rect.x1, i, width, 1, glFormat, PFORMAT->glType, pixelData + (rect.x1 * PFORMAT->bytesPerBlock) + (i * shm.stride)); - } - }); - } - - GLFB(outFB)->unbind(); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - - g_pHyprOpenGL->m_renderData.pMonitor.reset(); - - if (!m_copied) { - LOGM(Log::TRACE, "Copied frame via shm"); - m_callback(RESULT_COPIED); - } - - return true; -} - -void CScreenshareFrame::storeTempFB() { - if (!m_session->m_tempFB) - m_session->m_tempFB = g_pHyprRenderer->createFB(); - m_session->m_tempFB->alloc(m_bufferSize.x, m_bufferSize.y); - - CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - - if (!g_pHyprRenderer->beginRender(m_session->monitor(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, m_session->m_tempFB, true)) { - LOGM(Log::ERR, "Can't copy: failed to begin rendering to temp fb"); - return; - } - - switch (m_session->m_type) { - case SHARE_REGION: // TODO: could this be better? this is how screencopy works - case SHARE_MONITOR: renderMonitor(); break; - case SHARE_WINDOW: renderWindow(); break; - case SHARE_NONE: - default: return; - } - - g_pHyprRenderer->endRender(); -} - -Vector2D CScreenshareFrame::bufferSize() const { - return m_bufferSize; -} - -wl_output_transform CScreenshareFrame::transform() const { - switch (m_session->m_type) { - case SHARE_REGION: - case SHARE_MONITOR: return m_session->monitor()->m_transform; - default: - case SHARE_WINDOW: return WL_OUTPUT_TRANSFORM_NORMAL; - } -} - -const CRegion& CScreenshareFrame::damage() const { - return m_damage; -} diff --git a/src/managers/screenshare/ScreenshareManager.cpp b/src/managers/screenshare/ScreenshareManager.cpp deleted file mode 100644 index 63f2bbbc..00000000 --- a/src/managers/screenshare/ScreenshareManager.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "ScreenshareManager.hpp" -#include "../../render/Renderer.hpp" -#include "../../Compositor.hpp" -#include "../../desktop/view/Window.hpp" -#include "../../protocols/core/Seat.hpp" - -using namespace Screenshare; - -CScreenshareManager::CScreenshareManager() { - ; -} - -void CScreenshareManager::onOutputCommit(PHLMONITOR monitor) { - std::erase_if(m_sessions, [&](const WP& session) { return session.expired(); }); - - // if no pending frames, and no sessions are sharing, then unblock ds - if (m_pendingFrames.empty()) { - for (const auto& session : m_sessions) { - if (!session->m_stopped && session->m_sharing) - return; - } - - g_pHyprRenderer->m_directScanoutBlocked = false; - return; // nothing to share - } - - std::ranges::for_each(m_pendingFrames, [&](WP& frame) { - if (frame.expired() || !frame->m_shared || frame->done()) - return; - - if (frame->m_session->monitor() != monitor) - return; - - if (frame->m_session->m_type == SHARE_WINDOW) { - CBox geometry = {frame->m_session->m_window->m_realPosition->value(), frame->m_session->m_window->m_realSize->value()}; - if (geometry.intersection({monitor->m_position, monitor->m_size}).empty()) - return; - } - - frame->copy(); - }); - - std::erase_if(m_pendingFrames, [&](const WP& frame) { return frame.expired(); }); -} - -UP CScreenshareManager::newSession(wl_client* client, PHLMONITOR monitor) { - if UNLIKELY (!monitor || !g_pCompositor->monitorExists(monitor)) { - LOGM(Log::ERR, "Client requested sharing of a monitor that is gone"); - return nullptr; - } - - UP session = UP(new CScreenshareSession(monitor, client)); - - session->m_self = session; - m_sessions.emplace_back(session); - - return session; -} - -UP CScreenshareManager::newSession(wl_client* client, PHLMONITOR monitor, CBox captureRegion) { - if UNLIKELY (!monitor || !g_pCompositor->monitorExists(monitor)) { - LOGM(Log::ERR, "Client requested sharing of a monitor that is gone"); - return nullptr; - } - - UP session = UP(new CScreenshareSession(monitor, captureRegion, client)); - - session->m_self = session; - m_sessions.emplace_back(session); - - return session; -} - -UP CScreenshareManager::newSession(wl_client* client, PHLWINDOW window) { - if UNLIKELY (!window || !window->m_isMapped) { - LOGM(Log::ERR, "Client requested sharing of window that is gone or not shareable!"); - return nullptr; - } - - UP session = UP(new CScreenshareSession(window, client)); - - session->m_self = session; - m_sessions.emplace_back(session); - - return session; -} - -UP CScreenshareManager::newCursorSession(wl_client* client, WP pointer) { - UP session = UP(new CCursorshareSession(client, pointer)); - - session->m_self = session; - m_cursorSessions.emplace_back(session); - - return session; -} - -WP CScreenshareManager::getManagedSession(wl_client* client, PHLMONITOR monitor) { - return getManagedSession(SHARE_MONITOR, client, monitor, nullptr, {}); -} - -WP CScreenshareManager::getManagedSession(wl_client* client, PHLMONITOR monitor, CBox captureBox) { - - return getManagedSession(SHARE_REGION, client, monitor, nullptr, captureBox); -} - -WP CScreenshareManager::getManagedSession(wl_client* client, PHLWINDOW window) { - return getManagedSession(SHARE_WINDOW, client, nullptr, window, {}); -} - -WP CScreenshareManager::getManagedSession(eScreenshareType type, wl_client* client, PHLMONITOR monitor, PHLWINDOW window, CBox captureBox) { - if (type == SHARE_NONE) - return {}; - - auto it = std::ranges::find_if(m_managedSessions, [&](const auto& session) { - if (session->m_session->m_client != client || session->m_session->m_type != type) - return false; - - switch (type) { - case SHARE_MONITOR: return session->m_session->m_monitor == monitor; - case SHARE_WINDOW: return session->m_session->m_window == window; - case SHARE_REGION: return session->m_session->m_monitor == monitor && session->m_session->m_captureBox == captureBox; - case SHARE_NONE: - default: return false; - } - - return false; - }); - - if (it == m_managedSessions.end()) { - UP session; - switch (type) { - case SHARE_MONITOR: session = UP(new CScreenshareSession(monitor, client)); break; - case SHARE_WINDOW: session = UP(new CScreenshareSession(window, client)); break; - case SHARE_REGION: session = UP(new CScreenshareSession(monitor, captureBox, client)); break; - case SHARE_NONE: - default: return {}; - } - - session->m_self = session; - m_sessions.emplace_back(session); - - it = m_managedSessions.emplace(m_managedSessions.end(), makeUnique(std::move(session))); - } - - auto& session = *it; - - session->stoppedListener = session->m_session->m_events.stopped.listen([session = WP(session)]() { - if (!session.expired()) - std::erase_if(Screenshare::mgr()->m_managedSessions, [&](const auto& s) { return s && s->m_session.get() == session->m_session.get(); }); - }); - - return session->m_session; -} - -bool CScreenshareManager::isOutputBeingSSd(PHLMONITOR monitor) { - return std::ranges::any_of(m_sessions, [monitor](const auto& s) { - if (!s) - return false; - return (s->m_type == SHARE_MONITOR || s->m_type == SHARE_REGION) && s->m_monitor == monitor; - }); -} - -CScreenshareManager::SManagedSession::SManagedSession(UP&& session) : m_session(std::move(session)) { - ; -} diff --git a/src/managers/screenshare/ScreenshareManager.hpp b/src/managers/screenshare/ScreenshareManager.hpp deleted file mode 100644 index 5a4ada5e..00000000 --- a/src/managers/screenshare/ScreenshareManager.hpp +++ /dev/null @@ -1,251 +0,0 @@ -#pragma once - -#include -#include "../../helpers/memory/Memory.hpp" -#include "../../protocols/types/Buffer.hpp" -#include "../../render/Framebuffer.hpp" -#include "../eventLoop/EventLoopTimer.hpp" -#include "../../render/Renderer.hpp" - -// TODO: do screenshare damage - -class CWLPointerResource; - -namespace Screenshare { - enum eScreenshareType : uint8_t { - SHARE_MONITOR, - SHARE_WINDOW, - SHARE_REGION, - SHARE_NONE - }; - - enum eScreenshareError : uint8_t { - ERROR_NONE, - ERROR_UNKNOWN, - ERROR_STOPPED, - ERROR_NO_BUFFER, - ERROR_BUFFER_SIZE, - ERROR_BUFFER_FORMAT - }; - - enum eScreenshareResult : uint8_t { - RESULT_COPIED, - RESULT_NOT_COPIED, - RESULT_TIMESTAMP, - }; - - using FScreenshareCallback = std::function; - using FSourceBoxCallback = std::function; - - class CScreenshareSession { - public: - CScreenshareSession(const CScreenshareSession&) = delete; - CScreenshareSession(CScreenshareSession&&) = delete; - ~CScreenshareSession(); - - UP nextFrame(bool overlayCursor); - void stop(); - - // constraints - const std::vector& allowedFormats() const; - Vector2D bufferSize() const; - PHLMONITOR monitor() const; // this will return the correct monitor based on type - - struct { - CSignalT<> stopped; - CSignalT<> constraintsChanged; - } m_events; - - private: - CScreenshareSession(PHLMONITOR monitor, wl_client* client); - CScreenshareSession(PHLMONITOR monitor, CBox captureRegion, wl_client* client); - CScreenshareSession(PHLWINDOW window, wl_client* client); - - WP m_self; - bool m_stopped = false; - - eScreenshareType m_type = SHARE_NONE; - PHLMONITORREF m_monitor; - PHLWINDOWREF m_window; - CBox m_captureBox = {}; // given capture area in logical coordinates (see xdg_output) - - wl_client* m_client = nullptr; - std::string m_name = ""; - - std::vector m_formats; - Vector2D m_bufferSize = Vector2D(0, 0); - - SP m_tempFB; - - SP m_shareStopTimer; - bool m_sharing = false; - - struct { - CHyprSignalListener monitorDestroyed; - CHyprSignalListener monitorModeChanged; - CHyprSignalListener windowDestroyed; - CHyprSignalListener windowSizeChanged; - CHyprSignalListener windowMonitorChanged; - } m_listeners; - - void screenshareEvents(bool started); - void calculateConstraints(); - void init(); - - friend class CScreenshareFrame; - friend class CScreenshareManager; - }; - - class CCursorshareSession { - public: - CCursorshareSession(const CCursorshareSession&) = delete; - CCursorshareSession(CCursorshareSession&&) = delete; - ~CCursorshareSession(); - - eScreenshareError share(PHLMONITOR monitor, SP buffer, FSourceBoxCallback sourceBoxCallback, FScreenshareCallback callback); - void stop(); - - // constraints - DRMFormat format() const; - Vector2D bufferSize() const; - Vector2D hotspot() const; - - struct { - CSignalT<> stopped; - CSignalT<> constraintsChanged; - } m_events; - - private: - CCursorshareSession(wl_client* client, WP pointer); - - WP m_self; - bool m_stopped = false; - bool m_constraintsChanged = true; - - wl_client* m_client = nullptr; - WP m_pointer; - - // constraints - DRMFormat m_format = 0 /* DRM_FORMAT_INVALID */; - Vector2D m_hotspot = Vector2D(0, 0); - Vector2D m_bufferSize = Vector2D(0, 0); - - struct { - bool pending = false; - PHLMONITOR monitor; - SP buffer; - FSourceBoxCallback sourceBoxCallback; - FScreenshareCallback callback; - } m_pendingFrame; - - struct { - CHyprSignalListener pointerDestroyed; - CHyprSignalListener cursorChanged; - } m_listeners; - - bool copy(); - void render(); - void calculateConstraints(); - - friend class CScreenshareFrame; - friend class CScreenshareManager; - }; - - class CScreenshareFrame { - public: - CScreenshareFrame(const CScreenshareFrame&) = delete; - CScreenshareFrame(CScreenshareFrame&&) = delete; - CScreenshareFrame(WP session, bool overlayCursor, bool isFirst); - ~CScreenshareFrame(); - - bool done() const; - eScreenshareError share(SP buffer, const CRegion& damage, FScreenshareCallback callback); - - Vector2D bufferSize() const; - wl_output_transform transform() const; // returns the transform applied by compositor on the buffer - const CRegion& damage() const; - - private: - WP m_self; - WP m_session; - FScreenshareCallback m_callback; - SP m_buffer; - Vector2D m_bufferSize = Vector2D(0, 0); - CRegion m_damage; // damage in buffer coords - bool m_shared = false, m_copied = false, m_failed = false; - bool m_overlayCursor = true; - bool m_isFirst = false; - - // - void copy(); - bool copyDmabuf(); - bool copyShm(); - - void render(); - void renderMonitor(); - void renderMonitorRegion(); - void renderWindow(); - - void storeTempFB(); - - friend class CScreenshareManager; - friend class CScreenshareSession; - }; - - class CScreenshareManager { - public: - CScreenshareManager(); - - UP newSession(wl_client* client, PHLMONITOR monitor); - UP newSession(wl_client* client, PHLMONITOR monitor, CBox captureRegion); - UP newSession(wl_client* client, PHLWINDOW window); - - WP getManagedSession(wl_client* client, PHLMONITOR monitor); - WP getManagedSession(wl_client* client, PHLMONITOR monitor, CBox captureBox); - WP getManagedSession(wl_client* client, PHLWINDOW window); - - UP newCursorSession(wl_client* client, WP pointer); - - void onOutputCommit(PHLMONITOR monitor); - bool isOutputBeingSSd(PHLMONITOR monitor); - - private: - std::vector> m_sessions; - std::vector> m_cursorSessions; - std::vector> m_pendingFrames; - - struct SManagedSession { - SManagedSession(UP&& session); - - UP m_session; - CHyprSignalListener stoppedListener; - }; - - std::vector> m_managedSessions; - WP getManagedSession(eScreenshareType type, wl_client* client, PHLMONITOR monitor, PHLWINDOW window, CBox captureBox); - - friend class CScreenshareSession; - }; - - inline UP& mgr() { - static UP manager = nullptr; - if (!manager && g_pHyprRenderer) { - Log::logger->log(Log::DEBUG, "Starting ScreenshareManager"); - manager = makeUnique(); - } - return manager; - } -} - -template <> -struct std::formatter : std::formatter { - auto format(const Screenshare::eScreenshareType& res, std::format_context& ctx) const { - switch (res) { - case Screenshare::SHARE_MONITOR: return formatter::format("monitor", ctx); - case Screenshare::SHARE_WINDOW: return formatter::format("window", ctx); - case Screenshare::SHARE_REGION: return formatter::format("region", ctx); - case Screenshare::SHARE_NONE: return formatter::format("ERR NONE", ctx); - } - return formatter::format("error", ctx); - } -}; diff --git a/src/managers/screenshare/ScreenshareSession.cpp b/src/managers/screenshare/ScreenshareSession.cpp deleted file mode 100644 index 2fddc431..00000000 --- a/src/managers/screenshare/ScreenshareSession.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "ScreenshareManager.hpp" -#include "../../render/OpenGL.hpp" -#include "../../Compositor.hpp" -#include "../../render/Renderer.hpp" -#include "../EventManager.hpp" -#include "../eventLoop/EventLoopManager.hpp" -#include "../../event/EventBus.hpp" - -using namespace Screenshare; - -CScreenshareSession::CScreenshareSession(PHLMONITOR monitor, wl_client* client) : m_type(SHARE_MONITOR), m_monitor(monitor), m_client(client) { - init(); -} - -CScreenshareSession::CScreenshareSession(PHLWINDOW window, wl_client* client) : m_type(SHARE_WINDOW), m_window(window), m_client(client) { - m_listeners.windowDestroyed = m_window->m_events.unmap.listen([this]() { stop(); }); - m_listeners.windowSizeChanged = m_window->m_events.resize.listen([this]() { - calculateConstraints(); - m_events.constraintsChanged.emit(); - }); - m_listeners.windowMonitorChanged = m_window->m_events.monitorChanged.listen([this]() { - m_listeners.monitorDestroyed = monitor()->m_events.disconnect.listen([this]() { stop(); }); - m_listeners.monitorModeChanged = monitor()->m_events.modeChanged.listen([this]() { - calculateConstraints(); - m_events.constraintsChanged.emit(); - }); - - calculateConstraints(); - m_events.constraintsChanged.emit(); - }); - - init(); -} - -CScreenshareSession::CScreenshareSession(PHLMONITOR monitor, CBox captureRegion, wl_client* client) : - m_type(SHARE_REGION), m_monitor(monitor), m_captureBox(captureRegion), m_client(client) { - init(); -} - -CScreenshareSession::~CScreenshareSession() { - stop(); - uintptr_t ptr = m_type == SHARE_WINDOW && !m_window.expired() ? (uintptr_t)m_window.get() : (m_monitor.expired() ? (uintptr_t)nullptr : (uintptr_t)m_monitor.get()); - LOGM(Log::TRACE, "Destroyed screenshare session for ({}): {}, {:x}", m_type, m_name, ptr); -} - -void CScreenshareSession::stop() { - if (m_stopped) - return; - m_stopped = true; - m_events.stopped.emit(); - - screenshareEvents(false); -} - -void CScreenshareSession::init() { - uintptr_t ptr = m_type == SHARE_WINDOW && !m_window.expired() ? (uintptr_t)m_window.get() : (m_monitor.expired() ? (uintptr_t)nullptr : (uintptr_t)m_monitor.get()); - LOGM(Log::TRACE, "Created screenshare session for ({}): {}, {:x}", m_type, m_name, ptr); - - m_shareStopTimer = makeShared( - std::chrono::milliseconds(500), - [this](SP self, void* data) { - // if this fires, then it's been half a second since the last frame, so we aren't sharing - screenshareEvents(false); - }, - nullptr); - - if (g_pEventLoopManager) - g_pEventLoopManager->addTimer(m_shareStopTimer); - - // scale capture box since it's in logical coords - m_captureBox.scale(monitor()->m_scale); - - m_listeners.monitorDestroyed = monitor()->m_events.disconnect.listen([this]() { stop(); }); - m_listeners.monitorModeChanged = monitor()->m_events.modeChanged.listen([this]() { - calculateConstraints(); - m_events.constraintsChanged.emit(); - }); - - calculateConstraints(); -} - -void CScreenshareSession::calculateConstraints() { - const auto PMONITOR = monitor(); - if (!PMONITOR) { - stop(); - return; - } - - // TODO: maybe support more that just monitor format in the future? - m_formats.clear(); - m_formats.push_back(NFormatUtils::alphaFormat(g_pHyprOpenGL->getPreferredReadFormat(PMONITOR))); - m_formats.push_back(g_pHyprOpenGL->getPreferredReadFormat(PMONITOR)); // some clients don't like alpha formats - - // TODO: hack, we can't bit flip so we'll format flip heh, GL_BGRA_EXT won't work here - for (auto& format : m_formats) { - if (format == DRM_FORMAT_XRGB2101010 || format == DRM_FORMAT_ARGB2101010) - format = DRM_FORMAT_XBGR2101010; - } - - switch (m_type) { - case SHARE_MONITOR: - m_bufferSize = PMONITOR->m_pixelSize; - m_name = PMONITOR->m_name; - break; - case SHARE_WINDOW: - m_bufferSize = (m_window->m_realSize->value() * PMONITOR->m_scale).round(); - m_name = m_window->m_title; - break; - case SHARE_REGION: - m_bufferSize = PMONITOR->m_transform % 2 == 0 ? m_captureBox.size() : Vector2D{m_captureBox.h, m_captureBox.w}; - m_name = PMONITOR->m_name; - break; - case SHARE_NONE: - default: - LOGM(Log::ERR, "Invalid share type?? This shouldn't happen"); - stop(); - return; - } - - LOGM(Log::TRACE, "constraints changed for {}", m_name); -} - -void CScreenshareSession::screenshareEvents(bool startSharing) { - if (startSharing && !m_sharing) { - m_sharing = true; - g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencast", .data = std::format("1,{}", m_type)}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencastv2", .data = std::format("1,{},{}", m_type, m_name)}); - LOGM(Log::INFO, "Started screenshare session for ({}): {}", m_type, m_name); - - Event::bus()->m_events.screenshare.state.emit(true, m_type, m_name); - } else if (!startSharing && m_sharing) { - m_sharing = false; - g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencast", .data = std::format("0,{}", m_type)}); - g_pEventManager->postEvent(SHyprIPCEvent{.event = "screencastv2", .data = std::format("0,{},{}", m_type, m_name)}); - LOGM(Log::INFO, "Stopped screenshare session for ({}): {}", m_type, m_name); - - Event::bus()->m_events.screenshare.state.emit(false, m_type, m_name); - } -} - -const std::vector& CScreenshareSession::allowedFormats() const { - return m_formats; -} - -Vector2D CScreenshareSession::bufferSize() const { - return m_bufferSize; -} - -PHLMONITOR CScreenshareSession::monitor() const { - if (m_type == SHARE_WINDOW && m_window.expired()) - return nullptr; - PHLMONITORREF mon = m_type == SHARE_WINDOW ? m_window->m_monitor : m_monitor; - return mon.expired() ? nullptr : mon.lock(); -} - -UP CScreenshareSession::nextFrame(bool overlayCursor) { - UP frame = makeUnique(m_self, overlayCursor, !m_sharing); - frame->m_self = frame; - - Screenshare::mgr()->m_pendingFrames.emplace_back(frame); - - // there is now a pending frame, so block ds - g_pHyprRenderer->m_directScanoutBlocked = true; - - return frame; -} diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000..3973dc4c --- /dev/null +++ b/src/meson.build @@ -0,0 +1,58 @@ +globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true) +src = globber.stdout().strip().split('\n') + +executable( + 'Hyprland', + src, + link_args: '-rdynamic', + cpp_pch: 'pch/pch.hpp', + dependencies: [ + server_protos, + aquamarine, + hyprcursor, + hyprgraphics, + hyprlang, + hyprutils, + dependency('gbm'), + dependency('xcursor'), + dependency('wayland-server'), + dependency('wayland-client'), + dependency('cairo'), + dependency('libdrm'), + dependency('egl'), + dependency('xkbcommon'), + dependency('libinput'), + dependency('re2'), + xcb_dep, + xcb_composite_dep, + xcb_errors_dep, + xcb_icccm_dep, + xcb_render_dep, + xcb_res_dep, + xcb_xfixes_dep, + backtrace_dep, + epoll_dep, + inotify_dep, + gio_dep, + tracy, + + # Try to find canihavesomecoffee's udis86 using pkgconfig + # vmt/udis86 does not provide a .pc file and won't be detected this way + # Falls back to using the subproject through udis86.wrap + dependency('udis86'), + + dependency('pixman-1'), + dependency('gl', 'opengl'), + dependency('threads'), + dependency('pango'), + dependency('pangocairo'), + dependency('uuid'), + ], + install: true, +) + +install_symlink( + 'hyprland', + install_dir: get_option('bindir'), + pointing_to: 'Hyprland', +) diff --git a/src/plugins/HookSystem.cpp b/src/plugins/HookSystem.cpp index f17a4556..9fa61561 100644 --- a/src/plugins/HookSystem.cpp +++ b/src/plugins/HookSystem.cpp @@ -1,5 +1,5 @@ #include "HookSystem.hpp" -#include "../debug/log/Logger.hpp" +#include "../debug/Log.hpp" #include "../helpers/varlist/VarList.hpp" #include "../managers/TokenManager.hpp" #include "../helpers/MiscFunctions.hpp" @@ -14,12 +14,12 @@ #include #include -CFunctionHook::CFunctionHook(HANDLE owner, void* source, void* destination) : m_source(source), m_destination(destination), m_owner(owner) { +CFunctionHook::CFunctionHook(HANDLE owner, void* source, void* destination) : m_pSource(source), m_pDestination(destination), m_pOwner(owner) { ; } CFunctionHook::~CFunctionHook() { - if (m_active) + if (m_bActive) unhook(); } @@ -33,7 +33,7 @@ CFunctionHook::SInstructionProbe CFunctionHook::getInstructionLenAt(void* start) size_t curOffset = 1; size_t insSize = 0; while (true) { - ud_set_input_buffer(&udis, sc(start), curOffset); + ud_set_input_buffer(&udis, (uint8_t*)start, curOffset); insSize = ud_disassemble(&udis); if (insSize != curOffset) break; @@ -57,7 +57,7 @@ CFunctionHook::SInstructionProbe CFunctionHook::probeMinimumJumpSize(void* start while (size <= min) { // find info about this instruction - auto probe = getInstructionLenAt(sc(start) + size); + auto probe = getInstructionLenAt((uint8_t*)start + size); sizes.push_back(probe.len); size += probe.len; instrs += probe.assembly + "\n"; @@ -70,7 +70,7 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr SAssembly returns; // analyze the code and fix what we know how to. - uint64_t currentAddress = rc(m_source); + uint64_t currentAddress = (uint64_t)m_pSource; // actually newline + 1 size_t lastAsmNewline = 0; // needle for destination binary @@ -83,7 +83,7 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr // copy original bytes to our finalBytes for (size_t i = 0; i < len; ++i) { - finalBytes[currentDestinationOffset + i] = *rc(currentAddress + i); + finalBytes[currentDestinationOffset + i] = *(char*)(currentAddress + i); } std::string code = probe.assembly.substr(lastAsmNewline, probe.assembly.find('\n', lastAsmNewline) - lastAsmNewline); @@ -106,14 +106,14 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr if (ADDREND == std::string::npos || ADDRSTART == std::string::npos) return {}; - const uint64_t PREDICTEDRIP = rc(m_landTrampolineAddr) + currentDestinationOffset + len; + const uint64_t PREDICTEDRIP = (uint64_t)m_pTrampolineAddr + currentDestinationOffset + len; const int32_t NEWRIPOFFSET = DESTINATION - PREDICTEDRIP; size_t ripOffset = 0; // find %rip usage offset from beginning for (int i = len - 4 /* 32-bit */; i > 0; --i) { - if (*rc(currentAddress + i) == OFFSET) { + if (*(int32_t*)(currentAddress + i) == OFFSET) { ripOffset = i; break; } @@ -123,7 +123,7 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr return {}; // fix offset in the final bytes. This doesn't care about endianness - *rc(&finalBytes[currentDestinationOffset + ripOffset]) = NEWRIPOFFSET; + *(int32_t*)&finalBytes[currentDestinationOffset + ripOffset] = NEWRIPOFFSET; currentDestinationOffset += len; } else { @@ -144,100 +144,80 @@ bool CFunctionHook::hook() { return false; #endif - if (g_pFunctionHookSystem->m_activeHooks.contains(rc(m_source))) { - // TODO: return actual error codes... - Log::logger->log(Log::ERR, "[functionhook] failed, function is already hooked"); - return false; - } - - // jmp rel32 - // offset for relative addr: 1 - static constexpr uint8_t RELATIVE_JMP_ADDRESS[] = {0xE9, 0x00, 0x00, 0x00, 0x00}; - static constexpr size_t RELATIVE_JMP_ADDRESS_OFFSET = 1; - // movabs $0,%rax | jmpq *rax + // movabs $0,%rax | jmpq *%rax + // offset for addr: 2 static constexpr uint8_t ABSOLUTE_JMP_ADDRESS[] = {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0}; static constexpr size_t ABSOLUTE_JMP_ADDRESS_OFFSET = 2; + // pushq %rax + static constexpr uint8_t PUSH_RAX[] = {0x50}; + // popq %rax + static constexpr uint8_t POP_RAX[] = {0x58}; // nop static constexpr uint8_t NOP = 0x90; - // alloc trampolines + // alloc trampoline const auto MAX_TRAMPOLINE_SIZE = HOOK_TRAMPOLINE_MAX_SIZE; // we will never need more. - m_launchTrampolineAddr = rc(g_pFunctionHookSystem->getAddressForTrampo()); - m_landTrampolineAddr = rc(g_pFunctionHookSystem->getAddressForTrampo()); + m_pTrampolineAddr = (void*)g_pFunctionHookSystem->getAddressForTrampo(); // probe instructions to be trampolin'd SInstructionProbe probe; try { - probe = probeMinimumJumpSize(m_source, sizeof(RELATIVE_JMP_ADDRESS)); + probe = probeMinimumJumpSize(m_pSource, sizeof(ABSOLUTE_JMP_ADDRESS) + sizeof(PUSH_RAX) + sizeof(POP_RAX)); } catch (std::exception& e) { return false; } const auto PROBEFIXEDASM = fixInstructionProbeRIPCalls(probe); - if (PROBEFIXEDASM.bytes.empty()) { - Log::logger->log(Log::ERR, "[functionhook] failed, unsupported asm / failed assembling:\n{}", probe.assembly); - return false; - } - - if (std::abs(rc(m_source) - rc(m_landTrampolineAddr)) > 2000000000 /* 2 GB */) { - Log::logger->log(Log::ERR, "[functionhook] failed, source and trampo are over 2GB apart"); + if (PROBEFIXEDASM.bytes.size() == 0) { + Debug::log(ERR, "[functionhook] failed, unsupported asm / failed assembling:\n{}", probe.assembly); return false; } const size_t HOOKSIZE = PROBEFIXEDASM.bytes.size(); const size_t ORIGSIZE = probe.len; - const auto TRAMPOLINE_SIZE = sizeof(RELATIVE_JMP_ADDRESS) + HOOKSIZE; + const auto TRAMPOLINE_SIZE = sizeof(ABSOLUTE_JMP_ADDRESS) + HOOKSIZE + sizeof(PUSH_RAX); if (TRAMPOLINE_SIZE > MAX_TRAMPOLINE_SIZE) { - Log::logger->log(Log::ERR, "[functionhook] failed, not enough space in trampo to alloc:\n{}", probe.assembly); + Debug::log(ERR, "[functionhook] failed, not enough space in trampo to alloc:\n{}", probe.assembly); return false; } - m_originalBytes.resize(ORIGSIZE); - memcpy(m_originalBytes.data(), m_source, ORIGSIZE); + m_pOriginalBytes = malloc(ORIGSIZE); + memcpy(m_pOriginalBytes, m_pSource, ORIGSIZE); - // populate land trampoline - memcpy(m_landTrampolineAddr, PROBEFIXEDASM.bytes.data(), HOOKSIZE); // first, original but fixed func bytes - memcpy(sc(m_landTrampolineAddr) + HOOKSIZE, RELATIVE_JMP_ADDRESS, sizeof(RELATIVE_JMP_ADDRESS)); // then, jump to source + // populate trampoline + memcpy(m_pTrampolineAddr, PROBEFIXEDASM.bytes.data(), HOOKSIZE); // first, original but fixed func bytes + memcpy((uint8_t*)m_pTrampolineAddr + HOOKSIZE, PUSH_RAX, sizeof(PUSH_RAX)); // then, pushq %rax + memcpy((uint8_t*)m_pTrampolineAddr + HOOKSIZE + sizeof(PUSH_RAX), ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); // then, jump to source - // populate short jump addr - *rc(sc(m_landTrampolineAddr) + TRAMPOLINE_SIZE - sizeof(RELATIVE_JMP_ADDRESS) + RELATIVE_JMP_ADDRESS_OFFSET) = - sc((sc(m_source) + probe.len) // jump to source + probe len (skip header) - - (sc(m_landTrampolineAddr) + TRAMPOLINE_SIZE) // from trampo + size - jmp (not - size because jmp is rel to rip after instr) - ); + // fixup trampoline addr + *(uint64_t*)((uint8_t*)m_pTrampolineAddr + TRAMPOLINE_SIZE - sizeof(ABSOLUTE_JMP_ADDRESS) + ABSOLUTE_JMP_ADDRESS_OFFSET) = + (uint64_t)((uint8_t*)m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS)); - // populate launch trampoline - memcpy(m_launchTrampolineAddr, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); // long jump to our hk - - // populate long jump addr - *rc(sc(m_launchTrampolineAddr) + ABSOLUTE_JMP_ADDRESS_OFFSET) = rc(m_destination); // long jump to hk fn - - // make short jump to launch trampoile + // make jump to hk const auto PAGESIZE_VAR = sysconf(_SC_PAGE_SIZE); - const uint8_t* PROTSTART = sc(m_source) - (rc(m_source) % PAGESIZE_VAR); - const size_t PROTLEN = std::ceil(sc(ORIGSIZE + (rc(m_source) - rc(PROTSTART))) / sc(PAGESIZE_VAR)) * PAGESIZE_VAR; - mprotect(const_cast(PROTSTART), PROTLEN, PROT_READ | PROT_WRITE | PROT_EXEC); - memcpy(m_source, RELATIVE_JMP_ADDRESS, sizeof(RELATIVE_JMP_ADDRESS)); + const uint8_t* PROTSTART = (uint8_t*)m_pSource - ((uint64_t)m_pSource % PAGESIZE_VAR); + const size_t PROTLEN = std::ceil((float)(ORIGSIZE + ((uint64_t)m_pSource - (uint64_t)PROTSTART)) / (float)PAGESIZE_VAR) * PAGESIZE_VAR; + mprotect((uint8_t*)PROTSTART, PROTLEN, PROT_READ | PROT_WRITE | PROT_EXEC); + memcpy((uint8_t*)m_pSource, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); - size_t currentOp = sizeof(RELATIVE_JMP_ADDRESS); - memset(sc(m_source) + currentOp, NOP, ORIGSIZE - currentOp); + // make popq %rax and NOP all remaining + memcpy((uint8_t*)m_pSource + sizeof(ABSOLUTE_JMP_ADDRESS), POP_RAX, sizeof(POP_RAX)); + size_t currentOp = sizeof(ABSOLUTE_JMP_ADDRESS) + sizeof(POP_RAX); + memset((uint8_t*)m_pSource + currentOp, NOP, ORIGSIZE - currentOp); - // populate short jump addr - *rc(sc(m_source) + RELATIVE_JMP_ADDRESS_OFFSET) = sc( // - rc(m_launchTrampolineAddr) // jump to the launch trampoline which jumps to hk - - (rc(m_source) + 5) // from source - ); + // fixup jump addr + *(uint64_t*)((uint8_t*)m_pSource + ABSOLUTE_JMP_ADDRESS_OFFSET) = (uint64_t)(m_pDestination); // revert mprot - mprotect(const_cast(PROTSTART), PROTLEN, PROT_READ | PROT_EXEC); + mprotect((uint8_t*)PROTSTART, PROTLEN, PROT_READ | PROT_EXEC); - // set original addr to land trampo addr - m_original = m_landTrampolineAddr; + // set original addr to trampo addr + m_pOriginal = m_pTrampolineAddr; - m_active = true; - m_hookLen = ORIGSIZE; - - g_pFunctionHookSystem->m_activeHooks.emplace(rc(m_source)); + m_bActive = true; + m_iHookLen = ORIGSIZE; + m_iTrampoLen = TRAMPOLINE_SIZE; return true; } @@ -248,42 +228,41 @@ bool CFunctionHook::unhook() { return false; #endif - if (!m_active) + if (!m_bActive) return false; - g_pFunctionHookSystem->m_activeHooks.erase(rc(m_source)); - // allow write to src - mprotect(sc(m_source) - rc(m_source) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC); + mprotect((uint8_t*)m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC); // write back original bytes - memcpy(m_source, m_originalBytes.data(), m_hookLen); + memcpy(m_pSource, m_pOriginalBytes, m_iHookLen); // revert mprot - mprotect(sc(m_source) - rc(m_source) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_EXEC); + mprotect((uint8_t*)m_pSource - ((uint64_t)m_pSource) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_EXEC); // reset vars - m_active = false; - m_hookLen = 0; - m_landTrampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem - m_launchTrampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem - m_original = nullptr; - m_originalBytes.clear(); + m_bActive = false; + m_iHookLen = 0; + m_iTrampoLen = 0; + m_pTrampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem + m_pOriginalBytes = nullptr; + + free(m_pOriginalBytes); return true; } CFunctionHook* CHookSystem::initHook(HANDLE owner, void* source, void* destination) { - return m_hooks.emplace_back(makeUnique(owner, source, destination)).get(); + return m_vHooks.emplace_back(makeUnique(owner, source, destination)).get(); } bool CHookSystem::removeHook(CFunctionHook* hook) { - std::erase_if(m_hooks, [&](const auto& other) { return other.get() == hook; }); + std::erase_if(m_vHooks, [&](const auto& other) { return other.get() == hook; }); return true; // todo: make false if not found } void CHookSystem::removeAllHooksFrom(HANDLE handle) { - std::erase_if(m_hooks, [&](const auto& other) { return other->m_owner == handle; }); + std::erase_if(m_vHooks, [&](const auto& other) { return other->m_pOwner == handle; }); } static uintptr_t seekNewPageAddr() { @@ -300,7 +279,7 @@ static uintptr_t seekNewPageAddr() { uint64_t start = 0, end = 0; if (props[0].empty()) { - Log::logger->log(Log::WARN, "seekNewPageAddr: unexpected line in self maps"); + Debug::log(WARN, "seekNewPageAddr: unexpected line in self maps"); continue; } @@ -310,11 +289,11 @@ static uintptr_t seekNewPageAddr() { start = std::stoull(startEnd[0], nullptr, 16); end = std::stoull(startEnd[1], nullptr, 16); } catch (std::exception& e) { - Log::logger->log(Log::WARN, "seekNewPageAddr: unexpected line in self maps: {}", line); + Debug::log(WARN, "seekNewPageAddr: unexpected line in self maps: {}", line); continue; } - Log::logger->log(Log::DEBUG, "seekNewPageAddr: page 0x{:x} - 0x{:x}", start, end); + Debug::log(LOG, "seekNewPageAddr: page 0x{:x} - 0x{:x}", start, end); if (lastStart == 0) { lastStart = start; @@ -322,18 +301,21 @@ static uintptr_t seekNewPageAddr() { continue; } - if (!anchoredToHyprland && line.contains("Hyprland")) { - Log::logger->log(Log::DEBUG, "seekNewPageAddr: Anchored to hyprland at 0x{:x}", start); - anchoredToHyprland = true; - } else if (start - lastEnd > PAGESIZE_VAR * 2) { - if (!anchoredToHyprland) { - Log::logger->log(Log::DEBUG, "seekNewPageAddr: skipping gap 0x{:x}-0x{:x}, not anchored to Hyprland code pages yet.", lastEnd, start); + if (start - lastEnd > PAGESIZE_VAR * 2) { + if (!line.contains("Hyprland") && !anchoredToHyprland) { + Debug::log(LOG, "seekNewPageAddr: skipping gap 0x{:x}-0x{:x}, not anchored to Hyprland code pages yet.", lastEnd, start); lastStart = start; lastEnd = end; continue; + } else if (!anchoredToHyprland) { + Debug::log(LOG, "seekNewPageAddr: Anchored to hyprland at 0x{:x}", start); + anchoredToHyprland = true; + lastStart = start; + lastEnd = end; + continue; } - Log::logger->log(Log::DEBUG, "seekNewPageAddr: found gap: 0x{:x}-0x{:x} ({} bytes)", lastEnd, start, start - lastEnd); + Debug::log(LOG, "seekNewPageAddr: found gap: 0x{:x}-0x{:x} ({} bytes)", lastEnd, start, start - lastEnd); MAPS.close(); return lastEnd; } @@ -352,7 +334,7 @@ uint64_t CHookSystem::getAddressForTrampo() { // Nobody will hook 100k times, and even if, that's only 6.4 MB. Nothing. SAllocatedPage* page = nullptr; - for (auto& p : m_pages) { + for (auto& p : pages) { if (p.used + HOOK_TRAMPOLINE_MAX_SIZE > p.len) continue; @@ -361,27 +343,27 @@ uint64_t CHookSystem::getAddressForTrampo() { } if (!page) - page = &m_pages.emplace_back(); + page = &pages.emplace_back(); if (!page->addr) { // allocate it - Log::logger->log(Log::DEBUG, "getAddressForTrampo: Allocating new page for hooks"); + Debug::log(LOG, "getAddressForTrampo: Allocating new page for hooks"); const uint64_t PAGESIZE_VAR = sysconf(_SC_PAGE_SIZE); const auto BASEPAGEADDR = seekNewPageAddr(); for (int attempt = 0; attempt < 2; ++attempt) { for (int i = 0; i <= 2; ++i) { const auto PAGEADDR = BASEPAGEADDR + i * PAGESIZE_VAR; - page->addr = rc(mmap(rc(PAGEADDR), PAGESIZE_VAR, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + page->addr = (uint64_t)mmap((void*)PAGEADDR, PAGESIZE_VAR, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); page->len = PAGESIZE_VAR; page->used = 0; - Log::logger->log(Log::DEBUG, "Attempted to allocate 0x{:x}, got 0x{:x}", PAGEADDR, page->addr); + Debug::log(LOG, "Attempted to allocate 0x{:x}, got 0x{:x}", PAGEADDR, page->addr); - if (page->addr == rc(MAP_FAILED)) + if (page->addr == (uint64_t)MAP_FAILED) continue; if (page->addr != PAGEADDR && attempt == 0) { - munmap(rc(page->addr), PAGESIZE_VAR); + munmap((void*)page->addr, PAGESIZE_VAR); page->addr = 0; page->len = 0; continue; @@ -398,7 +380,7 @@ uint64_t CHookSystem::getAddressForTrampo() { page->used += HOOK_TRAMPOLINE_MAX_SIZE; - Log::logger->log(Log::DEBUG, "getAddressForTrampo: Returning addr 0x{:x} for page at 0x{:x}", ADDRFORCONSUMER, page->addr); + Debug::log(LOG, "getAddressForTrampo: Returning addr 0x{:x} for page at 0x{:x}", ADDRFORCONSUMER, page->addr); return ADDRFORCONSUMER; } diff --git a/src/plugins/HookSystem.hpp b/src/plugins/HookSystem.hpp index 3431e8c8..93ca4ff5 100644 --- a/src/plugins/HookSystem.hpp +++ b/src/plugins/HookSystem.hpp @@ -2,12 +2,10 @@ #include #include -#include -#include #include "../helpers/memory/Memory.hpp" #define HANDLE void* -#define HOOK_TRAMPOLINE_MAX_SIZE 32 +#define HOOK_TRAMPOLINE_MAX_SIZE 64 class CFunctionHook { public: @@ -22,18 +20,19 @@ class CFunctionHook { CFunctionHook& operator=(const CFunctionHook&) = delete; CFunctionHook& operator=(CFunctionHook&&) = delete; - void* m_original = nullptr; + void* m_pOriginal = nullptr; private: - void* m_source = nullptr; - void* m_launchTrampolineAddr = nullptr; - void* m_landTrampolineAddr = nullptr; - void* m_destination = nullptr; - size_t m_hookLen = 0; - HANDLE m_owner = nullptr; - bool m_active = false; + void* m_pSource = nullptr; + void* m_pFunctionAddr = nullptr; + void* m_pTrampolineAddr = nullptr; + void* m_pDestination = nullptr; + size_t m_iHookLen = 0; + size_t m_iTrampoLen = 0; + HANDLE m_pOwner = nullptr; + bool m_bActive = false; - std::vector m_originalBytes; + void* m_pOriginalBytes = nullptr; struct SInstructionProbe { size_t len = 0; @@ -61,7 +60,7 @@ class CHookSystem { void removeAllHooksFrom(HANDLE handle); private: - std::vector> m_hooks; + std::vector> m_vHooks; uint64_t getAddressForTrampo(); @@ -71,8 +70,7 @@ class CHookSystem { uint64_t used = 0; }; - std::vector m_pages; - std::unordered_set m_activeHooks; + std::vector pages; friend class CFunctionHook; }; diff --git a/src/plugins/PluginAPI.cpp b/src/plugins/PluginAPI.cpp index 5f89da53..92f043fd 100644 --- a/src/plugins/PluginAPI.cpp +++ b/src/plugins/PluginAPI.cpp @@ -2,11 +2,11 @@ #include "../Compositor.hpp" #include "../debug/HyprCtl.hpp" #include "../plugins/PluginSystem.hpp" +#include "../managers/HookSystemManager.hpp" +#include "../managers/LayoutManager.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" #include "../config/ConfigManager.hpp" #include "../debug/HyprNotificationOverlay.hpp" -#include "../layout/target/Target.hpp" -#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp" #include #include @@ -17,18 +17,7 @@ #include APICALL const char* __hyprland_api_get_hash() { - static auto stripPatch = [](const char* ver) -> std::string { - std::string_view v = ver; - if (!v.contains('.')) - return std::string{v}; - - return std::string{v.substr(0, v.find_last_of('.'))}; - }; - - static const std::string ver = (std::string{GIT_COMMIT_HASH} + "_aq_" + stripPatch(AQUAMARINE_VERSION) + "_hu_" + stripPatch(HYPRUTILS_VERSION) + "_hg_" + - stripPatch(HYPRGRAPHICS_VERSION) + "_hc_" + stripPatch(HYPRCURSOR_VERSION) + "_hlg_" + stripPatch(HYPRLANG_VERSION)); - - return ver.c_str(); + return GIT_COMMIT_HASH; } APICALL SP HyprlandAPI::registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn) { @@ -37,9 +26,9 @@ APICALL SP HyprlandAPI::registerCallbackDynamic(HANDLE handle, if (!PLUGIN) return nullptr; - //auto PFN = g_pHookSystem->hookDynamic(event, fn, handle); - //PLUGIN->m_registeredCallbacks.emplace_back(std::make_pair<>(event, WP(PFN))); - return nullptr; + auto PFN = g_pHookSystem->hookDynamic(event, fn, handle); + PLUGIN->registeredCallbacks.emplace_back(std::make_pair<>(event, WP(PFN))); + return PFN; } APICALL bool HyprlandAPI::unregisterCallback(HANDLE handle, SP fn) { @@ -48,8 +37,8 @@ APICALL bool HyprlandAPI::unregisterCallback(HANDLE handle, SP if (!PLUGIN) return false; - //g_pHookSystem->unhook(fn); - // std::erase_if(PLUGIN->m_registeredCallbacks, [&](const auto& other) { return other.second.lock() == fn; }); + g_pHookSystem->unhook(fn); + std::erase_if(PLUGIN->registeredCallbacks, [&](const auto& other) { return other.second.lock() == fn; }); return true; } @@ -62,44 +51,25 @@ APICALL std::string HyprlandAPI::invokeHyprctlCommand(const std::string& call, c } APICALL bool HyprlandAPI::addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout) { - return false; + auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); + + if (!PLUGIN) + return false; + + PLUGIN->registeredLayouts.push_back(layout); + + return g_pLayoutManager->addLayout(name, layout); } APICALL bool HyprlandAPI::removeLayout(HANDLE handle, IHyprLayout* layout) { - return false; -} - -APICALL bool HyprlandAPI::addTiledAlgo(HANDLE handle, const std::string& name, const std::type_info* typeInfo, std::function()>&& factory) { auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); if (!PLUGIN) return false; - PLUGIN->m_registeredAlgos.emplace_back(name); + std::erase(PLUGIN->registeredLayouts, layout); - return Layout::Supplementary::algoMatcher()->registerTiledAlgo(name, typeInfo, std::move(factory)); -} - -APICALL bool HyprlandAPI::addFloatingAlgo(HANDLE handle, const std::string& name, const std::type_info* typeInfo, std::function()>&& factory) { - auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); - - if (!PLUGIN) - return false; - - PLUGIN->m_registeredAlgos.emplace_back(name); - - return Layout::Supplementary::algoMatcher()->registerFloatingAlgo(name, typeInfo, std::move(factory)); -} - -APICALL bool HyprlandAPI::removeAlgo(HANDLE handle, const std::string& name) { - auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); - - if (!PLUGIN) - return false; - - std::erase(PLUGIN->m_registeredAlgos, name); - - return Layout::Supplementary::algoMatcher()->unregisterAlgo(name); + return g_pLayoutManager->removeLayout(layout); } APICALL bool HyprlandAPI::reloadConfig() { @@ -124,7 +94,7 @@ APICALL CFunctionHook* HyprlandAPI::createFunctionHook(HANDLE handle, const void if (!PLUGIN) return nullptr; - return g_pFunctionHookSystem->initHook(handle, const_cast(source), const_cast(destination)); + return g_pFunctionHookSystem->initHook(handle, (void*)source, (void*)destination); } APICALL bool HyprlandAPI::removeFunctionHook(HANDLE handle, CFunctionHook* hook) { @@ -145,11 +115,11 @@ APICALL bool HyprlandAPI::addWindowDecoration(HANDLE handle, PHLWINDOW pWindow, if (!validMapped(pWindow)) return false; - PLUGIN->m_registeredDecorations.push_back(pDecoration.get()); + PLUGIN->registeredDecorations.push_back(pDecoration.get()); pWindow->addWindowDeco(std::move(pDecoration)); - pWindow->layoutTarget()->recalc(); + g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow); return true; } @@ -160,8 +130,8 @@ APICALL bool HyprlandAPI::removeWindowDecoration(HANDLE handle, IHyprWindowDecor if (!PLUGIN) return false; - for (auto const& w : g_pCompositor->m_windows) { - for (auto const& d : w->m_windowDecorations) { + for (auto const& w : g_pCompositor->m_vWindows) { + for (auto const& d : w->m_dWindowDecorations) { if (d.get() == pDecoration) { w->removeWindowDeco(pDecoration); return true; @@ -175,7 +145,7 @@ APICALL bool HyprlandAPI::removeWindowDecoration(HANDLE handle, IHyprWindowDecor APICALL bool HyprlandAPI::addConfigValue(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value) { auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); - if (!g_pPluginSystem->m_allowConfigVars) + if (!g_pPluginSystem->m_bAllowConfigVars) return false; if (!PLUGIN) @@ -191,7 +161,7 @@ APICALL bool HyprlandAPI::addConfigValue(HANDLE handle, const std::string& name, APICALL bool HyprlandAPI::addConfigKeyword(HANDLE handle, const std::string& name, Hyprlang::PCONFIGHANDLERFUNC fn, Hyprlang::SHandlerOptions opts) { auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle); - if (!g_pPluginSystem->m_allowConfigVars) + if (!g_pPluginSystem->m_bAllowConfigVars) return false; if (!PLUGIN) @@ -228,9 +198,9 @@ APICALL bool HyprlandAPI::addDispatcher(HANDLE handle, const std::string& name, if (!PLUGIN) return false; - PLUGIN->m_registeredDispatchers.push_back(name); + PLUGIN->registeredDispatchers.push_back(name); - g_pKeybindManager->m_dispatchers[name] = [handler](std::string arg1) -> SDispatchResult { + g_pKeybindManager->m_mDispatchers[name] = [handler](std::string arg1) -> SDispatchResult { handler(arg1); return {}; }; @@ -244,9 +214,9 @@ APICALL bool HyprlandAPI::addDispatcherV2(HANDLE handle, const std::string& name if (!PLUGIN) return false; - PLUGIN->m_registeredDispatchers.push_back(name); + PLUGIN->registeredDispatchers.push_back(name); - g_pKeybindManager->m_dispatchers[name] = handler; + g_pKeybindManager->m_mDispatchers[name] = handler; return true; } @@ -257,8 +227,8 @@ APICALL bool HyprlandAPI::removeDispatcher(HANDLE handle, const std::string& nam if (!PLUGIN) return false; - std::erase_if(g_pKeybindManager->m_dispatchers, [&](const auto& other) { return other.first == name; }); - std::erase_if(PLUGIN->m_registeredDispatchers, [&](const auto& other) { return other == name; }); + std::erase_if(g_pKeybindManager->m_mDispatchers, [&](const auto& other) { return other.first == name; }); + std::erase_if(PLUGIN->registeredDispatchers, [&](const auto& other) { return other == name; }); return true; } @@ -331,7 +301,7 @@ APICALL std::vector HyprlandAPI::findFunctionsByName(HANDLE hand #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); @@ -369,11 +339,11 @@ APICALL std::vector HyprlandAPI::findFunctionsByName(HANDLE hand }; if (SYMBOLS.empty()) { - Log::logger->log(Log::ERR, R"(Unable to search for function "{}": no symbols found in binary (is "{}" in path?))", name, + Debug::log(ERR, R"(Unable to search for function "{}": no symbols found in binary (is "{}" in path?))", name, #ifdef __clang__ - "llvm-nm" + "llvm-nm" #else - "nm" + "nm" #endif ); return {}; @@ -415,7 +385,7 @@ APICALL SP HyprlandAPI::registerHyprCtlCommand(HANDLE handle, S return nullptr; auto PTR = g_pHyprCtl->registerCommand(cmd); - PLUGIN->m_registeredHyprctlCommands.push_back(PTR); + PLUGIN->registeredHyprctlCommands.push_back(PTR); return PTR; } @@ -426,7 +396,7 @@ APICALL bool HyprlandAPI::unregisterHyprCtlCommand(HANDLE handle, SPm_registeredHyprctlCommands, [&](const auto& other) { return !other || other == cmd; }); + std::erase(PLUGIN->registeredHyprctlCommands, cmd); g_pHyprCtl->unregisterCommand(cmd); return true; diff --git a/src/plugins/PluginAPI.hpp b/src/plugins/PluginAPI.hpp index 77bb9926..0f333294 100644 --- a/src/plugins/PluginAPI.hpp +++ b/src/plugins/PluginAPI.hpp @@ -29,15 +29,14 @@ Feel like the API is missing something you'd like to use in your plugin? Open an #include #include #include -#include #include -using PLUGIN_DESCRIPTION_INFO = struct { +typedef struct { std::string name; std::string description; std::string author; std::string version; -}; +} PLUGIN_DESCRIPTION_INFO; struct SFunctionMatch { void* address = nullptr; @@ -68,16 +67,10 @@ struct SVersionInfo { #endif class IHyprLayout; +class CWindow; class IHyprWindowDecoration; struct SConfigValue; -class Hypr_dummyClass {}; - -namespace Layout { - class ITiledAlgorithm; - class IFloatingAlgorithm; -}; - -using HOOK_CALLBACK_FN = Hypr_dummyClass; +class CWindow; /* These methods are for the plugin to implement @@ -90,7 +83,7 @@ using HOOK_CALLBACK_FN = Hypr_dummyClass; This function should not be modified, see the example plugin. */ -using PPLUGIN_API_VERSION_FUNC = REQUIRED std::string (*)(); +typedef REQUIRED std::string (*PPLUGIN_API_VERSION_FUNC)(); #define PLUGIN_API_VERSION pluginAPIVersion #define PLUGIN_API_VERSION_FUNC_STR "pluginAPIVersion" @@ -100,7 +93,7 @@ using PPLUGIN_API_VERSION_FUNC = REQUIRED std::string (*)(); Keep in mind this is executed synchronously, and as such any blocking calls to hyprland might hang. (e.g. system("hyprctl ...")) */ -using PPLUGIN_INIT_FUNC = REQUIRED PLUGIN_DESCRIPTION_INFO (*)(HANDLE); +typedef REQUIRED PLUGIN_DESCRIPTION_INFO (*PPLUGIN_INIT_FUNC)(HANDLE); #define PLUGIN_INIT pluginInit #define PLUGIN_INIT_FUNC_STR "pluginInit" @@ -110,7 +103,7 @@ using PPLUGIN_INIT_FUNC = REQUIRED PLUGIN_DESCRIPTION_INFO (*)(HANDLE); Hooks are unloaded after exit. */ -using PPLUGIN_EXIT_FUNC = OPTIONAL void (*)(); +typedef OPTIONAL void (*PPLUGIN_EXIT_FUNC)(); #define PLUGIN_EXIT pluginExit #define PLUGIN_EXIT_FUNC_STR "pluginExit" @@ -143,7 +136,7 @@ namespace HyprlandAPI { /* Get a config value. - Please see the header or https://hypr.land/hyprlang/ for docs regarding Hyprlang types. + Please see the header or https://hyprland.org/hyprlang/ for docs regarding Hyprlang types. returns: a pointer to the config value struct, which is guaranteed to be valid for the life of this plugin, unless another `addConfigValue` is called afterwards. nullptr on error. @@ -151,8 +144,6 @@ namespace HyprlandAPI { APICALL Hyprlang::CConfigValue* getConfigValue(HANDLE handle, const std::string& name); /* - Deprecated: doesn't do anything anymore, use Event::bus() - Register a dynamic (function) callback to a selected event. Pointer will be free'd by Hyprland on unregisterCallback(). @@ -160,7 +151,7 @@ namespace HyprlandAPI { WARNING: Losing this pointer will unregister the callback! */ - APICALL [[deprecated]] [[nodiscard]] SP registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn); + APICALL [[nodiscard]] SP registerCallbackDynamic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN fn); /* Unregisters a callback. If the callback was dynamic, frees the memory. @@ -182,26 +173,15 @@ namespace HyprlandAPI { Adds a layout to Hyprland. returns: true on success. False otherwise. - - deprecated: addTiledAlgo, addFloatingAlgo */ - APICALL [[deprecated]] bool addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout); + APICALL bool addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout); /* Removes an added layout from Hyprland. returns: true on success. False otherwise. - - deprecated: V2 removeAlgo */ - APICALL [[deprecated]] bool removeLayout(HANDLE handle, IHyprLayout* layout); - - /* - Algorithm fns. Used for registering and removing. Return success. - */ - APICALL bool addTiledAlgo(HANDLE handle, const std::string& name, const std::type_info* typeInfo, std::function()>&& factory); - APICALL bool addFloatingAlgo(HANDLE handle, const std::string& name, const std::type_info* typeInfo, std::function()>&& factory); - APICALL bool removeAlgo(HANDLE handle, const std::string& name); + APICALL bool removeLayout(HANDLE handle, IHyprLayout* layout); /* Queues a config reload. Does not take effect immediately. @@ -329,7 +309,7 @@ namespace HyprlandAPI { // NOLINTBEGIN /* - Get the descriptive string this plugin/server was compiled with. + Get the hash this plugin/server was compiled with. This function will end up in both hyprland and any/all plugins, and can be found by a simple dlsym() @@ -339,18 +319,7 @@ namespace HyprlandAPI { */ APICALL EXPORT const char* __hyprland_api_get_hash(); APICALL inline EXPORT const char* __hyprland_api_get_client_hash() { - static auto stripPatch = [](const char* ver) -> std::string { - std::string_view v = ver; - if (!v.contains('.')) - return std::string{v}; - - return std::string{v.substr(0, v.find_last_of('.'))}; - }; - - static const std::string ver = (std::string{GIT_COMMIT_HASH} + "_aq_" + stripPatch(AQUAMARINE_VERSION) + "_hu_" + stripPatch(HYPRUTILS_VERSION) + "_hg_" + - stripPatch(HYPRGRAPHICS_VERSION) + "_hc_" + stripPatch(HYPRCURSOR_VERSION) + "_hlg_" + stripPatch(HYPRLANG_VERSION)); - - return ver.c_str(); + return GIT_COMMIT_HASH; } // NOLINTEND diff --git a/src/plugins/PluginSystem.cpp b/src/plugins/PluginSystem.cpp index 3bd8f473..87b9e8ef 100644 --- a/src/plugins/PluginSystem.cpp +++ b/src/plugins/PluginSystem.cpp @@ -3,139 +3,90 @@ #include #include #include "../config/ConfigManager.hpp" -#include "../debug/HyprCtl.hpp" +#include "../managers/LayoutManager.hpp" +#include "../managers/HookSystemManager.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" -#include "../managers/permissions/DynamicPermissionManager.hpp" -#include "../debug/HyprNotificationOverlay.hpp" -#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp" -#include "../i18n/Engine.hpp" CPluginSystem::CPluginSystem() { g_pFunctionHookSystem = makeUnique(); } -SP> CPluginSystem::loadPlugin(const std::string& path, eSpecialPidTypes pidType) { +CPlugin* CPluginSystem::loadPlugin(const std::string& path) { - pid_t pid = 0; + m_szLastError = ""; - if (g_pHyprCtl->m_currentRequestParams.pid > 0) - pid = g_pHyprCtl->m_currentRequestParams.pid; - - return CPromise::make([path, pid, pidType, this](SP> resolver) { - const auto PERM = g_pDynamicPermissionManager->clientPermissionModeWithString(pidType != SPECIAL_PID_TYPE_NONE ? pidType : pid, path, PERMISSION_TYPE_PLUGIN); - if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) { - Log::logger->log(Log::DEBUG, "CPluginSystem: Waiting for user confirmation to load {}", path); - - auto promise = g_pDynamicPermissionManager->promiseFor(pid, path, PERMISSION_TYPE_PLUGIN); - if (!promise) { // already awaiting or something? - resolver->reject("Failed to get a promise for permission"); - return; - } - - promise->then([this, path, resolver](SP> result) { - if (result->hasError()) { - Log::logger->log(Log::ERR, "CPluginSystem: Error spawning permission prompt"); - resolver->reject("Error spawning permission prompt"); - return; - } - - if (result->result() != PERMISSION_RULE_ALLOW_MODE_ALLOW) { - Log::logger->log(Log::ERR, "CPluginSystem: Rejecting plugin load of {}, user denied", path); - resolver->reject("user denied"); - return; - } - - Log::logger->log(Log::DEBUG, "CPluginSystem: Loading {}, user allowed", path); - - const auto RESULT = loadPluginInternal(path); - if (RESULT.has_value()) - resolver->resolve(RESULT.value()); - else - resolver->reject(RESULT.error()); - }); - return; - } else if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { - Log::logger->log(Log::DEBUG, "CPluginSystem: Rejecting plugin load, permission is disabled"); - resolver->reject("permission is disabled"); - return; - } - - const auto RESULT = loadPluginInternal(path); - if (RESULT.has_value()) - resolver->resolve(RESULT.value()); - else - resolver->reject(RESULT.error()); - }); -} - -std::expected CPluginSystem::loadPluginInternal(const std::string& path) { if (getPluginByPath(path)) { - Log::logger->log(Log::ERR, " [PluginSystem] Cannot load a plugin twice!"); - return std::unexpected("Cannot load a plugin twice!"); + m_szLastError = "Cannot load a plugin twice!"; + Debug::log(ERR, " [PluginSystem] Cannot load a plugin twice!"); + return nullptr; } - auto* const PLUGIN = m_loadedPlugins.emplace_back(makeUnique()).get(); + auto* const PLUGIN = m_vLoadedPlugins.emplace_back(makeUnique()).get(); - PLUGIN->m_path = path; + PLUGIN->path = path; HANDLE MODULE = dlopen(path.c_str(), RTLD_LAZY); if (!MODULE) { std::string strerr = dlerror(); - Log::logger->log(Log::ERR, " [PluginSystem] Plugin {} could not be loaded: {}", path, strerr); - m_loadedPlugins.pop_back(); - return std::unexpected(std::format("Plugin {} could not be loaded: {}", path, strerr)); + m_szLastError = std::format("Plugin {} could not be loaded: {}", path, strerr); + Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded: {}", path, strerr); + m_vLoadedPlugins.pop_back(); + return nullptr; } - PLUGIN->m_handle = MODULE; + PLUGIN->m_pHandle = MODULE; - PPLUGIN_API_VERSION_FUNC apiVerFunc = rc(dlsym(MODULE, PLUGIN_API_VERSION_FUNC_STR)); - PPLUGIN_INIT_FUNC initFunc = rc(dlsym(MODULE, PLUGIN_INIT_FUNC_STR)); + PPLUGIN_API_VERSION_FUNC apiVerFunc = (PPLUGIN_API_VERSION_FUNC)dlsym(MODULE, PLUGIN_API_VERSION_FUNC_STR); + PPLUGIN_INIT_FUNC initFunc = (PPLUGIN_INIT_FUNC)dlsym(MODULE, PLUGIN_INIT_FUNC_STR); if (!apiVerFunc || !initFunc) { - Log::logger->log(Log::ERR, " [PluginSystem] Plugin {} could not be loaded. (No apiver/init func)", path); + m_szLastError = std::format("Plugin {} could not be loaded: {}", path, "missing apiver/init func"); + Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded. (No apiver/init func)", path); dlclose(MODULE); - m_loadedPlugins.pop_back(); - return std::unexpected(std::format("Plugin {} could not be loaded: {}", path, "missing apiver/init func")); + m_vLoadedPlugins.pop_back(); + return nullptr; } const std::string PLUGINAPIVER = apiVerFunc(); if (PLUGINAPIVER != HYPRLAND_API_VERSION) { - Log::logger->log(Log::ERR, " [PluginSystem] Plugin {} could not be loaded. (API version mismatch)", path); + m_szLastError = std::format("Plugin {} could not be loaded: {}", path, "API version mismatch"); + Debug::log(ERR, " [PluginSystem] Plugin {} could not be loaded. (API version mismatch)", path); dlclose(MODULE); - m_loadedPlugins.pop_back(); - return std::unexpected(std::format("Plugin {} could not be loaded: {}", path, "API version mismatch")); + m_vLoadedPlugins.pop_back(); + return nullptr; } PLUGIN_DESCRIPTION_INFO PLUGINDATA; try { - if (!setjmp(m_pluginFaultJumpBuf)) { - m_allowConfigVars = true; - PLUGINDATA = initFunc(MODULE); + if (!setjmp(m_jbPluginFaultJumpBuf)) { + m_bAllowConfigVars = true; + PLUGINDATA = initFunc(MODULE); } else { // this module crashed. throw std::runtime_error("received a fatal signal"); } } catch (std::exception& e) { - m_allowConfigVars = false; - Log::logger->log(Log::ERR, " [PluginSystem] Plugin {} (Handle {:x}) crashed in init. Unloading.", path, rc(MODULE)); + m_bAllowConfigVars = false; + m_szLastError = std::format("Plugin {} could not be loaded: plugin crashed/threw in main: {}", path, e.what()); + Debug::log(ERR, " [PluginSystem] Plugin {} (Handle {:x}) crashed in init. Unloading.", path, (uintptr_t)MODULE); unloadPlugin(PLUGIN, true); // Plugin could've already hooked/done something - return std::unexpected(std::format("Plugin {} could not be loaded: plugin crashed/threw in main: {}", path, e.what())); + return nullptr; } - m_allowConfigVars = false; + m_bAllowConfigVars = false; - PLUGIN->m_author = PLUGINDATA.author; - PLUGIN->m_description = PLUGINDATA.description; - PLUGIN->m_version = PLUGINDATA.version; - PLUGIN->m_name = PLUGINDATA.name; + PLUGIN->author = PLUGINDATA.author; + PLUGIN->description = PLUGINDATA.description; + PLUGIN->version = PLUGINDATA.version; + PLUGIN->name = PLUGINDATA.name; g_pEventLoopManager->doLater([] { g_pConfigManager->reload(); }); - Log::logger->log(Log::DEBUG, R"( [PluginSystem] Plugin {} loaded. Handle: {:x}, path: "{}", author: "{}", description: "{}", version: "{}")", PLUGINDATA.name, - rc(MODULE), path, PLUGINDATA.author, PLUGINDATA.description, PLUGINDATA.version); + Debug::log(LOG, R"( [PluginSystem] Plugin {} loaded. Handle: {:x}, path: "{}", author: "{}", description: "{}", version: "{}")", PLUGINDATA.name, (uintptr_t)MODULE, path, + PLUGINDATA.author, PLUGINDATA.description, PLUGINDATA.version); return PLUGIN; } @@ -145,102 +96,88 @@ void CPluginSystem::unloadPlugin(const CPlugin* plugin, bool eject) { return; if (!eject) { - PPLUGIN_EXIT_FUNC exitFunc = rc(dlsym(plugin->m_handle, PLUGIN_EXIT_FUNC_STR)); + PPLUGIN_EXIT_FUNC exitFunc = (PPLUGIN_EXIT_FUNC)dlsym(plugin->m_pHandle, PLUGIN_EXIT_FUNC_STR); if (exitFunc) exitFunc(); } - // for (auto const& [k, v] : plugin->m_registeredCallbacks) { - // if (const auto SHP = v.lock()) - // g_pHookSystem->unhook(SHP); - // } - - for (const auto& l : plugin->m_registeredAlgos) { - Layout::Supplementary::algoMatcher()->unregisterAlgo(l); + for (auto const& [k, v] : plugin->registeredCallbacks) { + if (const auto SHP = v.lock()) + g_pHookSystem->unhook(SHP); } - g_pFunctionHookSystem->removeAllHooksFrom(plugin->m_handle); + const auto ls = plugin->registeredLayouts; + for (auto const& l : ls) + g_pLayoutManager->removeLayout(l); - const auto rd = plugin->m_registeredDecorations; + g_pFunctionHookSystem->removeAllHooksFrom(plugin->m_pHandle); + + const auto rd = plugin->registeredDecorations; for (auto const& d : rd) - HyprlandAPI::removeWindowDecoration(plugin->m_handle, d); + HyprlandAPI::removeWindowDecoration(plugin->m_pHandle, d); - const auto rdi = plugin->m_registeredDispatchers; + const auto rdi = plugin->registeredDispatchers; for (auto const& d : rdi) - HyprlandAPI::removeDispatcher(plugin->m_handle, d); + HyprlandAPI::removeDispatcher(plugin->m_pHandle, d); - const auto rhc = plugin->m_registeredHyprctlCommands; - for (auto const& c : rhc) { - if (const auto sp = c.lock()) - HyprlandAPI::unregisterHyprCtlCommand(plugin->m_handle, sp); - } + const auto rhc = plugin->registeredHyprctlCommands; + for (auto const& c : rhc) + HyprlandAPI::unregisterHyprCtlCommand(plugin->m_pHandle, c); - g_pConfigManager->removePluginConfig(plugin->m_handle); + g_pConfigManager->removePluginConfig(plugin->m_pHandle); // save these two for dlclose and a log, // as erase_if will kill the pointer - const auto PLNAME = plugin->m_name; - const auto PLHANDLE = plugin->m_handle; + const auto PLNAME = plugin->name; + const auto PLHANDLE = plugin->m_pHandle; - std::erase_if(m_loadedPlugins, [&](const auto& other) { return other->m_handle == PLHANDLE; }); + std::erase_if(m_vLoadedPlugins, [&](const auto& other) { return other->m_pHandle == PLHANDLE; }); dlclose(PLHANDLE); - Log::logger->log(Log::DEBUG, " [PluginSystem] Plugin {} unloaded.", PLNAME); + Debug::log(LOG, " [PluginSystem] Plugin {} unloaded.", PLNAME); // reload config to fix some stuf like e.g. unloadedPluginVars g_pEventLoopManager->doLater([] { g_pConfigManager->reload(); }); } void CPluginSystem::unloadAllPlugins() { - for (auto const& p : m_loadedPlugins | std::views::reverse) + for (auto const& p : m_vLoadedPlugins | std::views::reverse) unloadPlugin(p.get(), false); // Unload remaining plugins gracefully } -void CPluginSystem::updateConfigPlugins(const std::vector& plugins, bool& changed) { - if (m_lastConfigPlugins == plugins) - return; - - m_lastConfigPlugins = plugins; +std::vector CPluginSystem::updateConfigPlugins(const std::vector& plugins, bool& changed) { + std::vector failures; // unload all plugins that are no longer present - for (auto const& p : m_loadedPlugins | std::views::reverse) { - if (!p->m_loadedWithConfig || std::ranges::find(plugins, p->m_path) != plugins.end()) - continue; - - Log::logger->log(Log::DEBUG, "Unloading plugin {} which is no longer present in config", p->m_path); - unloadPlugin(p.get(), false); - changed = true; + for (auto const& p : m_vLoadedPlugins | std::views::reverse) { + if (p->m_bLoadedWithConfig && std::find(plugins.begin(), plugins.end(), p->path) == plugins.end()) { + Debug::log(LOG, "Unloading plugin {} which is no longer present in config", p->path); + unloadPlugin(p.get(), false); + changed = true; + } } // load all new plugins for (auto const& path : plugins) { - if (std::ranges::find_if(m_loadedPlugins, [&](const auto& other) { return other->m_path == path; }) != m_loadedPlugins.end()) - continue; + if (std::find_if(m_vLoadedPlugins.begin(), m_vLoadedPlugins.end(), [&](const auto& other) { return other->path == path; }) == m_vLoadedPlugins.end()) { + Debug::log(LOG, "Loading plugin {} which is now present in config", path); + const auto plugin = loadPlugin(path); - Log::logger->log(Log::DEBUG, "Loading plugin {} which is now present in config", path); - - changed = true; - - loadPlugin(path, SPECIAL_PID_TYPE_CONFIG)->then([path](SP> result) { - if (result->hasError()) { - const auto NAME = path.contains('/') ? path.substr(path.find_last_of('/') + 1) : path; - Log::logger->log(Log::ERR, "CPluginSystem::updateConfigPlugins: failed to load plugin {}: {}", NAME, result->error()); - g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, {{"name", NAME}, {"error", result->error()}}), - CHyprColor{0, 0, 0, 0}, 5000, ICON_ERROR); - return; - } - - result->result()->m_loadedWithConfig = true; - - Log::logger->log(Log::DEBUG, "CPluginSystem::updateConfigPlugins: loaded {}", path); - }); + if (plugin) { + plugin->m_bLoadedWithConfig = true; + changed = true; + } else + failures.push_back(path); + } } + + return failures; } CPlugin* CPluginSystem::getPluginByPath(const std::string& path) { - for (auto const& p : m_loadedPlugins) { - if (p->m_path == path) + for (auto const& p : m_vLoadedPlugins) { + if (p->path == path) return p.get(); } @@ -248,8 +185,8 @@ CPlugin* CPluginSystem::getPluginByPath(const std::string& path) { } CPlugin* CPluginSystem::getPluginByHandle(HANDLE handle) { - for (auto const& p : m_loadedPlugins) { - if (p->m_handle == handle) + for (auto const& p : m_vLoadedPlugins) { + if (p->m_pHandle == handle) return p.get(); } @@ -257,18 +194,18 @@ CPlugin* CPluginSystem::getPluginByHandle(HANDLE handle) { } std::vector CPluginSystem::getAllPlugins() { - std::vector results(m_loadedPlugins.size()); - for (size_t i = 0; i < m_loadedPlugins.size(); ++i) - results[i] = m_loadedPlugins[i].get(); + std::vector results(m_vLoadedPlugins.size()); + for (size_t i = 0; i < m_vLoadedPlugins.size(); ++i) + results[i] = m_vLoadedPlugins[i].get(); return results; } size_t CPluginSystem::pluginCount() { - return m_loadedPlugins.size(); + return m_vLoadedPlugins.size(); } void CPluginSystem::sigGetPlugins(CPlugin** data, size_t len) { - for (size_t i = 0; i < std::min(m_loadedPlugins.size(), len); i++) { - data[i] = m_loadedPlugins[i].get(); + for (size_t i = 0; i < std::min(m_vLoadedPlugins.size(), len); i++) { + data[i] = m_vLoadedPlugins[i].get(); } } diff --git a/src/plugins/PluginSystem.hpp b/src/plugins/PluginSystem.hpp index 85afaefa..95841bef 100644 --- a/src/plugins/PluginSystem.hpp +++ b/src/plugins/PluginSystem.hpp @@ -1,57 +1,52 @@ #pragma once #include "../defines.hpp" -#include "../helpers/defer/Promise.hpp" -#include "../helpers/time/Timer.hpp" #include "PluginAPI.hpp" -#include "../managers/permissions/DynamicPermissionManager.hpp" #include -#include class IHyprWindowDecoration; class CPlugin { public: - std::string m_name = ""; - std::string m_description = ""; - std::string m_author = ""; - std::string m_version = ""; + std::string name = ""; + std::string description = ""; + std::string author = ""; + std::string version = ""; - std::string m_path = ""; + std::string path = ""; - bool m_loadedWithConfig = false; + bool m_bLoadedWithConfig = false; - HANDLE m_handle = nullptr; + HANDLE m_pHandle = nullptr; - std::vector m_registeredDecorations; - //std::vector>> m_registeredCallbacks; - std::vector m_registeredDispatchers; - std::vector> m_registeredHyprctlCommands; - std::vector m_registeredAlgos; + std::vector registeredLayouts; + std::vector registeredDecorations; + std::vector>> registeredCallbacks; + std::vector registeredDispatchers; + std::vector> registeredHyprctlCommands; }; class CPluginSystem { public: CPluginSystem(); - SP> loadPlugin(const std::string& path, eSpecialPidTypes pidType = SPECIAL_PID_TYPE_NONE); - void unloadPlugin(const CPlugin* plugin, bool eject = false); - void unloadAllPlugins(); - void updateConfigPlugins(const std::vector& plugins, bool& changed); - CPlugin* getPluginByPath(const std::string& path); - CPlugin* getPluginByHandle(HANDLE handle); - std::vector getAllPlugins(); - size_t pluginCount(); - void sigGetPlugins(CPlugin** data, size_t len); + CPlugin* loadPlugin(const std::string& path); + void unloadPlugin(const CPlugin* plugin, bool eject = false); + void unloadAllPlugins(); + std::vector updateConfigPlugins(const std::vector& plugins, bool& changed); + CPlugin* getPluginByPath(const std::string& path); + CPlugin* getPluginByHandle(HANDLE handle); + std::vector getAllPlugins(); + size_t pluginCount(); + void sigGetPlugins(CPlugin** data, size_t len); - bool m_allowConfigVars = false; + bool m_bAllowConfigVars = false; + std::string m_szLastError = ""; private: - std::vector> m_loadedPlugins; - std::vector m_lastConfigPlugins; - jmp_buf m_pluginFaultJumpBuf; + std::vector> m_vLoadedPlugins; - std::expected loadPluginInternal(const std::string& path); + jmp_buf m_jbPluginFaultJumpBuf; }; inline UP g_pPluginSystem; diff --git a/src/protocols/AlphaModifier.cpp b/src/protocols/AlphaModifier.cpp index a4ebc635..60037632 100644 --- a/src/protocols/AlphaModifier.cpp +++ b/src/protocols/AlphaModifier.cpp @@ -1,61 +1,61 @@ #include "AlphaModifier.hpp" -#include "../desktop/view/WLSurface.hpp" +#include "../desktop/WLSurface.hpp" #include "../render/Renderer.hpp" #include "alpha-modifier-v1.hpp" #include "core/Compositor.hpp" -CAlphaModifier::CAlphaModifier(UP&& resource, SP surface) : m_surface(surface) { +CAlphaModifier::CAlphaModifier(SP resource, SP surface) : m_pSurface(surface) { setResource(std::move(resource)); } bool CAlphaModifier::good() { - return m_resource->resource(); + return m_pResource->resource(); } -void CAlphaModifier::setResource(UP&& resource) { - m_resource = std::move(resource); +void CAlphaModifier::setResource(SP resource) { + m_pResource = std::move(resource); - if UNLIKELY (!m_resource->resource()) + if UNLIKELY (!m_pResource->resource()) return; - m_resource->setDestroy([this](CWpAlphaModifierSurfaceV1* resource) { destroy(); }); - m_resource->setOnDestroy([this](CWpAlphaModifierSurfaceV1* resource) { destroy(); }); + m_pResource->setDestroy([this](CWpAlphaModifierSurfaceV1* resource) { destroy(); }); + m_pResource->setOnDestroy([this](CWpAlphaModifierSurfaceV1* resource) { destroy(); }); - m_resource->setSetMultiplier([this](CWpAlphaModifierSurfaceV1* resource, uint32_t alpha) { - if (!m_surface) { - m_resource->error(WP_ALPHA_MODIFIER_SURFACE_V1_ERROR_NO_SURFACE, "set_multiplier called for destroyed wl_surface"); + m_pResource->setSetMultiplier([this](CWpAlphaModifierSurfaceV1* resource, uint32_t alpha) { + if (!m_pSurface) { + m_pResource->error(WP_ALPHA_MODIFIER_SURFACE_V1_ERROR_NO_SURFACE, "set_multiplier called for destroyed wl_surface"); return; } - m_alpha = alpha / sc(UINT32_MAX); + m_fAlpha = alpha / (float)UINT32_MAX; }); - m_listeners.surfaceCommitted = m_surface->m_events.commit.listen([this] { - auto surface = Desktop::View::CWLSurface::fromResource(m_surface.lock()); + listeners.surfaceCommitted = m_pSurface->events.commit.registerListener([this](std::any data) { + auto surface = CWLSurface::fromResource(m_pSurface.lock()); - if (surface && surface->m_alphaModifier != m_alpha) { - surface->m_alphaModifier = m_alpha; - auto box = surface->getSurfaceBoxGlobal(); + if (surface && surface->m_fAlphaModifier != m_fAlpha) { + surface->m_fAlphaModifier = m_fAlpha; + auto box = surface->getSurfaceBoxGlobal(); if (box.has_value()) g_pHyprRenderer->damageBox(*box); - if (!m_resource) + if (!m_pResource) PROTO::alphaModifier->destroyAlphaModifier(this); } }); - m_listeners.surfaceDestroyed = m_surface->m_events.destroy.listen([this] { - if (!m_resource) + listeners.surfaceDestroyed = m_pSurface->events.destroy.registerListener([this](std::any data) { + if (!m_pResource) PROTO::alphaModifier->destroyAlphaModifier(this); }); } void CAlphaModifier::destroy() { - m_resource.reset(); - m_alpha = 1.F; + m_pResource.reset(); + m_fAlpha = 1.F; - if (!m_surface) + if (!m_pSurface) PROTO::alphaModifier->destroyAlphaModifier(this); } @@ -64,7 +64,7 @@ CAlphaModifierProtocol::CAlphaModifierProtocol(const wl_interface* iface, const } void CAlphaModifierProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CWpAlphaModifierV1* manager) { destroyManager(manager); }); RESOURCE->setDestroy([this](CWpAlphaModifierV1* manager) { destroyManager(manager); }); @@ -72,33 +72,33 @@ void CAlphaModifierProtocol::bindManager(wl_client* client, void* data, uint32_t } void CAlphaModifierProtocol::destroyManager(CWpAlphaModifierV1* manager) { - std::erase_if(m_managers, [&](const auto& p) { return p.get() == manager; }); + std::erase_if(m_vManagers, [&](const auto& p) { return p.get() == manager; }); } void CAlphaModifierProtocol::destroyAlphaModifier(CAlphaModifier* modifier) { - std::erase_if(m_alphaModifiers, [&](const auto& entry) { return entry.second.get() == modifier; }); + std::erase_if(m_mAlphaModifiers, [&](const auto& entry) { return entry.second.get() == modifier; }); } void CAlphaModifierProtocol::getSurface(CWpAlphaModifierV1* manager, uint32_t id, SP surface) { CAlphaModifier* alphaModifier = nullptr; - auto iter = std::ranges::find_if(m_alphaModifiers, [&](const auto& entry) { return entry.second->m_surface == surface; }); + auto iter = std::find_if(m_mAlphaModifiers.begin(), m_mAlphaModifiers.end(), [&](const auto& entry) { return entry.second->m_pSurface == surface; }); - if (iter != m_alphaModifiers.end()) { - if (iter->second->m_resource) { - LOGM(Log::ERR, "AlphaModifier already present for surface {:x}", (uintptr_t)surface.get()); + if (iter != m_mAlphaModifiers.end()) { + if (iter->second->m_pResource) { + LOGM(ERR, "AlphaModifier already present for surface {:x}", (uintptr_t)surface.get()); manager->error(WP_ALPHA_MODIFIER_V1_ERROR_ALREADY_CONSTRUCTED, "AlphaModifier already present"); return; } else { - iter->second->setResource(makeUnique(manager->client(), manager->version(), id)); + iter->second->setResource(makeShared(manager->client(), manager->version(), id)); alphaModifier = iter->second.get(); } } else { - alphaModifier = m_alphaModifiers.emplace(surface, makeUnique(makeUnique(manager->client(), manager->version(), id), surface)) + alphaModifier = m_mAlphaModifiers.emplace(surface, makeUnique(makeShared(manager->client(), manager->version(), id), surface)) .first->second.get(); } if UNLIKELY (!alphaModifier->good()) { manager->noMemory(); - m_alphaModifiers.erase(surface); + m_mAlphaModifiers.erase(surface); } } diff --git a/src/protocols/AlphaModifier.hpp b/src/protocols/AlphaModifier.hpp index 8dc370a3..9a7873ae 100644 --- a/src/protocols/AlphaModifier.hpp +++ b/src/protocols/AlphaModifier.hpp @@ -11,22 +11,22 @@ class CAlphaModifierProtocol; class CAlphaModifier { public: - CAlphaModifier(UP&& resource_, SP surface); + CAlphaModifier(SP resource_, SP surface); bool good(); - void setResource(UP&& resource); + void setResource(SP resource); private: - UP m_resource; - WP m_surface; - float m_alpha = 1.0; + SP m_pResource; + WP m_pSurface; + float m_fAlpha = 1.0; void destroy(); struct { CHyprSignalListener surfaceCommitted; CHyprSignalListener surfaceDestroyed; - } m_listeners; + } listeners; friend class CAlphaModifierProtocol; }; @@ -43,8 +43,8 @@ class CAlphaModifierProtocol : public IWaylandProtocol { void getSurface(CWpAlphaModifierV1* manager, uint32_t id, SP surface); // - std::vector> m_managers; - std::unordered_map, UP> m_alphaModifiers; + std::vector> m_vManagers; + std::unordered_map, UP> m_mAlphaModifiers; friend class CAlphaModifier; }; diff --git a/src/protocols/CTMControl.cpp b/src/protocols/CTMControl.cpp index f94792dc..322f95e2 100644 --- a/src/protocols/CTMControl.cpp +++ b/src/protocols/CTMControl.cpp @@ -3,21 +3,19 @@ #include "../render/Renderer.hpp" #include "core/Output.hpp" #include "../config/ConfigValue.hpp" -#include "../config/ConfigManager.hpp" -#include "managers/animation/AnimationManager.hpp" +#include "managers/AnimationManager.hpp" #include "../helpers/Monitor.hpp" -#include "../helpers/MiscFunctions.hpp" -CHyprlandCTMControlResource::CHyprlandCTMControlResource(UP&& resource_) : m_resource(std::move(resource_)) { +CHyprlandCTMControlResource::CHyprlandCTMControlResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CHyprlandCtmControlManagerV1* pMgr) { PROTO::ctm->destroyResource(this); }); - m_resource->setOnDestroy([this](CHyprlandCtmControlManagerV1* pMgr) { PROTO::ctm->destroyResource(this); }); + resource->setDestroy([this](CHyprlandCtmControlManagerV1* pMgr) { PROTO::ctm->destroyResource(this); }); + resource->setOnDestroy([this](CHyprlandCtmControlManagerV1* pMgr) { PROTO::ctm->destroyResource(this); }); - m_resource->setSetCtmForOutput([this](CHyprlandCtmControlManagerV1* r, wl_resource* output, wl_fixed_t mat0, wl_fixed_t mat1, wl_fixed_t mat2, wl_fixed_t mat3, wl_fixed_t mat4, - wl_fixed_t mat5, wl_fixed_t mat6, wl_fixed_t mat7, wl_fixed_t mat8) { - if (m_blocked) + resource->setSetCtmForOutput([this](CHyprlandCtmControlManagerV1* r, wl_resource* output, wl_fixed_t mat0, wl_fixed_t mat1, wl_fixed_t mat2, wl_fixed_t mat3, wl_fixed_t mat4, + wl_fixed_t mat5, wl_fixed_t mat6, wl_fixed_t mat7, wl_fixed_t mat8) { + if (blocked) return; const auto OUTPUTRESOURCE = CWLOutputResource::fromResource(output); @@ -25,7 +23,7 @@ CHyprlandCTMControlResource::CHyprlandCTMControlResource(UPm_monitor.lock(); + const auto PMONITOR = OUTPUTRESOURCE->monitor.lock(); if UNLIKELY (!PMONITOR) return; // ?!?! @@ -34,52 +32,52 @@ CHyprlandCTMControlResource::CHyprlandCTMControlResource(UPerror(HYPRLAND_CTM_CONTROL_MANAGER_V1_ERROR_INVALID_MATRIX, "a matrix component was invalid"); + if (el < 0.F) { + resource->error(HYPRLAND_CTM_CONTROL_MANAGER_V1_ERROR_INVALID_MATRIX, "a matrix component was < 0"); return; } } - m_ctms[PMONITOR->m_name] = MAT; + ctms[PMONITOR->szName] = MAT; - LOGM(Log::DEBUG, "CTM set for output {}: {}", PMONITOR->m_name, m_ctms.at(PMONITOR->m_name).toString()); + LOGM(LOG, "CTM set for output {}: {}", PMONITOR->szName, ctms.at(PMONITOR->szName).toString()); }); - m_resource->setCommit([this](CHyprlandCtmControlManagerV1* r) { - if (m_blocked) + resource->setCommit([this](CHyprlandCtmControlManagerV1* r) { + if (blocked) return; - LOGM(Log::DEBUG, "Committing ctms to outputs"); + LOGM(LOG, "Committing ctms to outputs"); - for (auto& m : g_pCompositor->m_monitors) { - if (!m_ctms.contains(m->m_name)) { + for (auto& m : g_pCompositor->m_vMonitors) { + if (!ctms.contains(m->szName)) { PROTO::ctm->setCTM(m, Mat3x3::identity()); continue; } - PROTO::ctm->setCTM(m, m_ctms.at(m->m_name)); + PROTO::ctm->setCTM(m, ctms.at(m->szName)); } }); } void CHyprlandCTMControlResource::block() { - m_blocked = true; + blocked = true; - if (m_resource->version() >= 2) - m_resource->sendBlocked(); + if (resource->version() >= 2) + resource->sendBlocked(); } CHyprlandCTMControlResource::~CHyprlandCTMControlResource() { - if (m_blocked) + if (blocked) return; - for (auto& m : g_pCompositor->m_monitors) { + for (auto& m : g_pCompositor->m_vMonitors) { PROTO::ctm->setCTM(m, Mat3x3::identity()); } } bool CHyprlandCTMControlResource::good() { - return m_resource->resource(); + return resource->resource(); } CHyprlandCTMControlProtocol::CHyprlandCTMControlProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -87,35 +85,31 @@ CHyprlandCTMControlProtocol::CHyprlandCTMControlProtocol(const wl_interface* ifa } void CHyprlandCTMControlProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto& RESOURCE = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } - if (m_manager) + if (m_pManager) RESOURCE->block(); else - m_manager = RESOURCE; + m_pManager = RESOURCE; - LOGM(Log::DEBUG, "New CTM Manager at 0x{:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New CTM Manager at 0x{:x}", (uintptr_t)RESOURCE.get()); } void CHyprlandCTMControlProtocol::destroyResource(CHyprlandCTMControlResource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == res; }); } bool CHyprlandCTMControlProtocol::isCTMAnimationEnabled() { static auto PENABLEANIM = CConfigValue("render:ctm_animation"); - if (*PENABLEANIM == 2 /* auto */) { - if (!g_pHyprRenderer->isNvidia()) - return true; - // CTM animations are bugged on versions below. - return isNvidiaDriverVersionAtLeast(575); - } + if (*PENABLEANIM == 2) + return !g_pHyprRenderer->isNvidia(); return *PENABLEANIM; } @@ -129,12 +123,12 @@ void CHyprlandCTMControlProtocol::setCTM(PHLMONITOR monitor, const Mat3x3& ctm) return; } - std::erase_if(m_ctmDatas, [](const auto& el) { return !el.first; }); + std::erase_if(m_mCTMDatas, [](const auto& el) { return !el.first; }); - if (!m_ctmDatas.contains(monitor)) - m_ctmDatas[monitor] = makeUnique(); + if (!m_mCTMDatas.contains(monitor)) + m_mCTMDatas[monitor] = makeUnique(); - auto& data = m_ctmDatas.at(monitor); + auto& data = m_mCTMDatas.at(monitor); data->ctmFrom = data->ctmTo; data->ctmTo = ctm; @@ -145,9 +139,9 @@ void CHyprlandCTMControlProtocol::setCTM(PHLMONITOR monitor, const Mat3x3& ctm) monitor->setCTM(data->ctmFrom); data->progress->setUpdateCallback([monitor = PHLMONITORREF{monitor}, this](auto) { - if (!monitor || !m_ctmDatas.contains(monitor)) + if (!monitor || !m_mCTMDatas.contains(monitor)) return; - auto& data = m_ctmDatas.at(monitor); + auto& data = m_mCTMDatas.at(monitor); const auto from = data->ctmFrom.getMatrix(); const auto to = data->ctmTo.getMatrix(); const auto PROGRESS = data->progress->getPercent(); @@ -163,12 +157,11 @@ void CHyprlandCTMControlProtocol::setCTM(PHLMONITOR monitor, const Mat3x3& ctm) }); data->progress->setCallbackOnEnd([monitor = PHLMONITORREF{monitor}, this](auto) { - if (!monitor || !m_ctmDatas.contains(monitor)) { - if (monitor) - monitor->setCTM(Mat3x3::identity()); + if (!monitor || !m_mCTMDatas.contains(monitor)) { + monitor->setCTM(Mat3x3::identity()); return; } - auto& data = m_ctmDatas.at(monitor); + auto& data = m_mCTMDatas.at(monitor); monitor->setCTM(data->ctmTo); }); } diff --git a/src/protocols/CTMControl.hpp b/src/protocols/CTMControl.hpp index 2e3a7673..eb54a3aa 100644 --- a/src/protocols/CTMControl.hpp +++ b/src/protocols/CTMControl.hpp @@ -12,17 +12,17 @@ class CMonitor; class CHyprlandCTMControlResource { public: - CHyprlandCTMControlResource(UP&& resource_); + CHyprlandCTMControlResource(SP resource_); ~CHyprlandCTMControlResource(); bool good(); void block(); private: - UP m_resource; + SP resource; - std::unordered_map m_ctms; - bool m_blocked = false; + std::unordered_map ctms; + bool blocked = false; }; class CHyprlandCTMControlProtocol : public IWaylandProtocol { @@ -38,8 +38,8 @@ class CHyprlandCTMControlProtocol : public IWaylandProtocol { bool isCTMAnimationEnabled(); // - std::vector> m_managers; - WP m_manager; + std::vector> m_vManagers; + WP m_pManager; // struct SCTMData { @@ -47,7 +47,7 @@ class CHyprlandCTMControlProtocol : public IWaylandProtocol { Mat3x3 ctmFrom = Mat3x3::identity(), ctmTo = Mat3x3::identity(); PHLANIMVAR progress; }; - std::map> m_ctmDatas; + std::map> m_mCTMDatas; friend class CHyprlandCTMControlResource; }; diff --git a/src/protocols/ColorManagement.cpp b/src/protocols/ColorManagement.cpp index b9c3143b..b6aee17b 100644 --- a/src/protocols/ColorManagement.cpp +++ b/src/protocols/ColorManagement.cpp @@ -3,12 +3,12 @@ #include "color-management-v1.hpp" #include "../helpers/Monitor.hpp" #include "core/Output.hpp" -#include "../helpers/cm/ColorManagement.hpp" +#include "types/ColorManagement.hpp" #include using namespace NColorManagement; -const auto PRIMARIES_SCALE = 1000000.0f; +static uint64_t lastImageID = 0; // FIXME use for deduplication CColorManager::CColorManager(SP resource) : m_resource(resource) { if UNLIKELY (!good()) @@ -17,13 +17,13 @@ CColorManager::CColorManager(SP resource) : m_resource(resour m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC); m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES); m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES); - m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB); - m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES); - m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME); if (PROTO::colorManagement->m_debug) { m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_ICC_V2_V4); m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_TF_POWER); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_EXTENDED_TARGET_VOLUME); + m_resource->sendSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_WINDOWS_SCRGB); } m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_SRGB); @@ -35,7 +35,10 @@ CColorManager::CColorManager(SP resource) : m_resource(resour m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DCI_P3); m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_DISPLAY_P3); m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_ADOBE_RGB); - m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_CIE1931_XYZ); + + if (PROTO::colorManagement->m_debug) { + m_resource->sendSupportedPrimariesNamed(WP_COLOR_MANAGER_V1_PRIMARIES_CIE1931_XYZ); + } m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB); m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22); @@ -43,13 +46,16 @@ CColorManager::CColorManager(SP resource) : m_resource(resour m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR); m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ); m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG); - m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886); - m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240); - m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100); - m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316); - m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC); - m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB); - m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428); + + if (PROTO::colorManagement->m_debug) { + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB); + m_resource->sendSupportedTfNamed(WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428); + } m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL); if (PROTO::colorManagement->m_debug) { @@ -59,119 +65,140 @@ CColorManager::CColorManager(SP resource) : m_resource(resour m_resource->sendSupportedIntent(WP_COLOR_MANAGER_V1_RENDER_INTENT_RELATIVE_BPC); } - m_resource->setDestroy([](CWpColorManagerV1* r) { LOGM(Log::TRACE, "Destroy WP_color_manager at {:x} (generated default)", (uintptr_t)r); }); + m_resource->setDestroy([](CWpColorManagerV1* r) { LOGM(TRACE, "Destroy WP_color_manager at {:x} (generated default)", (uintptr_t)r); }); m_resource->setGetOutput([](CWpColorManagerV1* r, uint32_t id, wl_resource* output) { - LOGM(Log::TRACE, "Get output for id={}, output={}", id, (uintptr_t)output); + LOGM(TRACE, "Get output for id={}, output={}", id, (uintptr_t)output); const auto OUTPUTRESOURCE = CWLOutputResource::fromResource(output); - const auto RESOURCE = PROTO::colorManagement->m_outputs.emplace_back( - makeShared(makeShared(r->client(), r->version(), id), OUTPUTRESOURCE)); - - if UNLIKELY (!RESOURCE->good()) { - r->noMemory(); - PROTO::colorManagement->m_outputs.pop_back(); + if UNLIKELY (!OUTPUTRESOURCE) { + r->error(-1, "Invalid output (2)"); return; } - RESOURCE->m_self = RESOURCE; + const auto PMONITOR = OUTPUTRESOURCE->monitor.lock(); + + if UNLIKELY (!PMONITOR) { + r->error(-1, "Invalid output (2)"); + return; + } + + const auto RESOURCE = + PROTO::colorManagement->m_vOutputs.emplace_back(makeShared(makeShared(r->client(), r->version(), id), PMONITOR)); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::colorManagement->m_vOutputs.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; }); m_resource->setGetSurface([](CWpColorManagerV1* r, uint32_t id, wl_resource* surface) { - LOGM(Log::TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); + LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); auto SURF = CWLSurfaceResource::fromResource(surface); if (!SURF) { - LOGM(Log::ERR, "No surface for resource {}", (uintptr_t)surface); + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); r->error(-1, "Invalid surface (2)"); return; } - if (SURF->m_colorManagement) { + if (SURF->colorManagement) { r->error(WP_COLOR_MANAGER_V1_ERROR_SURFACE_EXISTS, "CM Surface already exists"); return; } const auto RESOURCE = - PROTO::colorManagement->m_surfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF)); + PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_surfaces.pop_back(); + PROTO::colorManagement->m_vSurfaces.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - SURF->m_colorManagement = RESOURCE; + SURF->colorManagement = RESOURCE; }); m_resource->setGetSurfaceFeedback([](CWpColorManagerV1* r, uint32_t id, wl_resource* surface) { - LOGM(Log::TRACE, "Get feedback surface for id={}, surface={}", id, (uintptr_t)surface); + LOGM(TRACE, "Get feedback surface for id={}, surface={}", id, (uintptr_t)surface); auto SURF = CWLSurfaceResource::fromResource(surface); if (!SURF) { - LOGM(Log::ERR, "No surface for resource {}", (uintptr_t)surface); + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); r->error(-1, "Invalid surface (2)"); return; } - const auto RESOURCE = PROTO::colorManagement->m_feedbackSurfaces.emplace_back( + const auto RESOURCE = PROTO::colorManagement->m_vFeedbackSurfaces.emplace_back( makeShared(makeShared(r->client(), r->version(), id), SURF)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_feedbackSurfaces.pop_back(); + PROTO::colorManagement->m_vFeedbackSurfaces.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; }); m_resource->setCreateIccCreator([](CWpColorManagerV1* r, uint32_t id) { - LOGM(Log::WARN, "New ICC creator for id={} (unsupported)", id); + LOGM(WARN, "New ICC creator for id={} (unsupported)", id); if (!PROTO::colorManagement->m_debug) { r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported"); return; } const auto RESOURCE = - PROTO::colorManagement->m_iccCreators.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + PROTO::colorManagement->m_vIccCreators.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_iccCreators.pop_back(); + PROTO::colorManagement->m_vIccCreators.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; }); m_resource->setCreateParametricCreator([](CWpColorManagerV1* r, uint32_t id) { - LOGM(Log::TRACE, "New parametric creator for id={}", id); + LOGM(TRACE, "New parametric creator for id={}", id); - const auto RESOURCE = PROTO::colorManagement->m_parametricCreators.emplace_back( + const auto RESOURCE = PROTO::colorManagement->m_vParametricCreators.emplace_back( makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_parametricCreators.pop_back(); + PROTO::colorManagement->m_vParametricCreators.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; }); m_resource->setCreateWindowsScrgb([](CWpColorManagerV1* r, uint32_t id) { - LOGM(Log::WARN, "New Windows scRGB description id={}", id); + LOGM(WARN, "New Windows scRGB description id={} (unsupported)", id); + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Windows scRGB profiles are not supported"); + return; + } - const auto RESOURCE = PROTO::colorManagement->m_imageDescriptions.emplace_back( + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( makeShared(makeShared(r->client(), r->version(), id), false)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_imageDescriptions.pop_back(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - RESOURCE->m_settings = SCRGB_IMAGE_DESCRIPTION; - - RESOURCE->resource()->sendReady(RESOURCE->m_settings->id()); + RESOURCE->self = RESOURCE; + RESOURCE->settings.id = ++lastImageID; + RESOURCE->settings.windowsScRGB = true; + RESOURCE->settings.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB; + RESOURCE->settings.primariesNameSet = true; + RESOURCE->settings.primaries = NColorPrimaries::BT709; + RESOURCE->settings.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR; + RESOURCE->settings.luminances.reference = 203; + RESOURCE->resource()->sendReady(RESOURCE->settings.id); }); m_resource->setOnDestroy([this](CWpColorManagerV1* r) { PROTO::colorManagement->destroyResource(this); }); @@ -183,40 +210,38 @@ bool CColorManager::good() { return m_resource->resource(); } -wl_client* CColorManager::client() { - return m_resource->client(); -} - -CColorManagementOutput::CColorManagementOutput(SP resource, WP output) : m_resource(resource), m_output(output) { +CColorManagementOutput::CColorManagementOutput(SP resource, WP monitor) : m_resource(resource), m_monitor(monitor) { if UNLIKELY (!good()) return; - m_client = m_resource->client(); + pClient = m_resource->client(); m_resource->setDestroy([this](CWpColorManagementOutputV1* r) { PROTO::colorManagement->destroyResource(this); }); m_resource->setOnDestroy([this](CWpColorManagementOutputV1* r) { PROTO::colorManagement->destroyResource(this); }); m_resource->setGetImageDescription([this](CWpColorManagementOutputV1* r, uint32_t id) { - LOGM(Log::TRACE, "Get image description for output={}, id={}", (uintptr_t)r, id); + LOGM(TRACE, "Get image description for output={}, id={}", (uintptr_t)r, id); - if (m_imageDescription.valid()) - PROTO::colorManagement->destroyResource(m_imageDescription.get()); + if (imageDescription.valid()) + PROTO::colorManagement->destroyResource(imageDescription.get()); - const auto RESOURCE = PROTO::colorManagement->m_imageDescriptions.emplace_back( + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( makeShared(makeShared(r->client(), r->version(), id), true)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_imageDescriptions.pop_back(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - if (!m_output || !m_output->m_monitor.valid()) + RESOURCE->self = RESOURCE; + if (!m_monitor.valid()) RESOURCE->m_resource->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_NO_OUTPUT, "No output"); else { - RESOURCE->m_settings = m_output->m_monitor->m_imageDescription; - RESOURCE->m_resource->sendReady(RESOURCE->m_settings->id()); + RESOURCE->settings = m_monitor->imageDescription; + if (RESOURCE->settings.id) + RESOURCE->settings.id = ++lastImageID; + RESOURCE->m_resource->sendReady(RESOURCE->settings.id); // FIXME: create correct id } }); } @@ -226,29 +251,32 @@ bool CColorManagementOutput::good() { } wl_client* CColorManagementOutput::client() { - return m_client; + return pClient; } -CColorManagementSurface::CColorManagementSurface(SP resource, SP surface_) : m_surface(surface_), m_resource(resource) { +CColorManagementSurface::CColorManagementSurface(SP surface_) : surface(surface_) { + // only for frog cm untill wayland cm is adopted +} + +CColorManagementSurface::CColorManagementSurface(SP resource, SP surface_) : surface(surface_), m_resource(resource) { if UNLIKELY (!good()) return; - m_client = m_resource->client(); - m_imageDescription = DEFAULT_IMAGE_DESCRIPTION; + pClient = m_resource->client(); m_resource->setDestroy([this](CWpColorManagementSurfaceV1* r) { - LOGM(Log::TRACE, "Destroy wp cm surface {}", (uintptr_t)m_surface); + LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); PROTO::colorManagement->destroyResource(this); }); m_resource->setOnDestroy([this](CWpColorManagementSurfaceV1* r) { - LOGM(Log::TRACE, "Destroy wp cm surface {}", (uintptr_t)m_surface); + LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); PROTO::colorManagement->destroyResource(this); }); m_resource->setSetImageDescription([this](CWpColorManagementSurfaceV1* r, wl_resource* image_description, uint32_t render_intent) { - LOGM(Log::TRACE, "Set image description for surface={}, desc={}, intent={}", (uintptr_t)r, (uintptr_t)image_description, render_intent); + LOGM(TRACE, "Set image description for surface={}, desc={}, intent={}", (uintptr_t)r, (uintptr_t)image_description, render_intent); - const auto PO = sc(wl_resource_get_user_data(image_description)); + const auto PO = (CWpImageDescriptionV1*)wl_resource_get_user_data(image_description); if (!PO) { // FIXME check validity r->error(WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION, "Image description creation failed"); return; @@ -258,19 +286,19 @@ CColorManagementSurface::CColorManagementSurface(SP return; } - const auto imageDescription = - std::ranges::find_if(PROTO::colorManagement->m_imageDescriptions, [&](const auto& other) { return other->resource()->resource() == image_description; }); - if (imageDescription == PROTO::colorManagement->m_imageDescriptions.end()) { + const auto imageDescription = std::find_if(PROTO::colorManagement->m_vImageDescriptions.begin(), PROTO::colorManagement->m_vImageDescriptions.end(), + [&](const auto& other) { return other->resource()->resource() == image_description; }); + if (imageDescription == PROTO::colorManagement->m_vImageDescriptions.end()) { r->error(WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION, "Image description not found"); return; } setHasImageDescription(true); - m_imageDescription = imageDescription->get()->m_settings; + m_imageDescription = imageDescription->get()->settings; }); m_resource->setUnsetImageDescription([this](CWpColorManagementSurfaceV1* r) { - LOGM(Log::TRACE, "Unset image description for surface={}", (uintptr_t)r); - m_imageDescription = DEFAULT_IMAGE_DESCRIPTION; + LOGM(TRACE, "Unset image description for surface={}", (uintptr_t)r); + m_imageDescription = SImageDescription{}; setHasImageDescription(false); }); } @@ -280,14 +308,13 @@ bool CColorManagementSurface::good() { } wl_client* CColorManagementSurface::client() { - return m_client; + return pClient; } const SImageDescription& CColorManagementSurface::imageDescription() { if (!hasImageDescription()) - LOGM(Log::WARN, "Reading imageDescription while none set. Returns default or empty values"); - - return m_imageDescription->value(); + LOGM(WARN, "Reading imageDescription while none set. Returns default or empty values"); + return m_imageDescription; } bool CColorManagementSurface::hasImageDescription() { @@ -304,103 +331,91 @@ const hdr_output_metadata& CColorManagementSurface::hdrMetadata() { } void CColorManagementSurface::setHDRMetadata(const hdr_output_metadata& metadata) { - m_hdrMetadata = metadata; - m_lastImageDescription = m_imageDescription; - m_needsNewMetadata = false; + m_hdrMetadata = metadata; + m_needsNewMetadata = false; } bool CColorManagementSurface::needsHdrMetadataUpdate() { - if (!m_needsNewMetadata) - return false; - if (m_imageDescription == m_lastImageDescription) - m_needsNewMetadata = false; return m_needsNewMetadata; } -bool CColorManagementSurface::isHDR() { - return m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ || m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_HLG || - isWindowsScRGB(); -} - -bool CColorManagementSurface::isWindowsScRGB() { - return m_imageDescription->value().windowsScRGB || - // autodetect scRGB, might be incorrect - (m_imageDescription->value().primariesNamed == CM_PRIMARIES_SRGB && m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_EXT_LINEAR); -} - CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP resource, SP surface_) : - m_surface(surface_), m_resource(resource) { + surface(surface_), m_resource(resource) { if UNLIKELY (!good()) return; - m_client = m_resource->client(); + pClient = m_resource->client(); m_resource->setDestroy([this](CWpColorManagementSurfaceFeedbackV1* r) { - LOGM(Log::TRACE, "Destroy wp cm feedback surface {}", (uintptr_t)m_surface); + LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface); + if (m_currentPreferred.valid()) + PROTO::colorManagement->destroyResource(m_currentPreferred.get()); PROTO::colorManagement->destroyResource(this); }); m_resource->setOnDestroy([this](CWpColorManagementSurfaceFeedbackV1* r) { - LOGM(Log::TRACE, "Destroy wp cm feedback surface {}", (uintptr_t)m_surface); + LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface); + if (m_currentPreferred.valid()) + PROTO::colorManagement->destroyResource(m_currentPreferred.get()); PROTO::colorManagement->destroyResource(this); }); m_resource->setGetPreferred([this](CWpColorManagementSurfaceFeedbackV1* r, uint32_t id) { - LOGM(Log::TRACE, "Get preferred for id {}", id); + LOGM(TRACE, "Get preferred for id {}", id); - if (m_surface.expired()) { - r->error(WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_ERROR_INERT, "Surface is inert"); - return; - } + if (m_currentPreferred.valid()) + PROTO::colorManagement->destroyResource(m_currentPreferred.get()); - const auto RESOURCE = PROTO::colorManagement->m_imageDescriptions.emplace_back( + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( makeShared(makeShared(r->client(), r->version(), id), true)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_imageDescriptions.pop_back(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - RESOURCE->m_settings = m_surface->getPreferredImageDescription(); + RESOURCE->self = RESOURCE; + m_currentPreferred = RESOURCE; - RESOURCE->resource()->sendReady(RESOURCE->m_settings->id()); + m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription(); + if (!m_currentPreferred->settings.id) + m_currentPreferred->settings.id = ++lastImageID; + + RESOURCE->resource()->sendReady(++lastImageID); // FIXME: create correct id }); m_resource->setGetPreferredParametric([this](CWpColorManagementSurfaceFeedbackV1* r, uint32_t id) { - LOGM(Log::TRACE, "Get preferred for id {}", id); + LOGM(TRACE, "Get preferred for id {}", id); - if (m_surface.expired()) { - r->error(WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_ERROR_INERT, "Surface is inert"); + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Parametric descriptions are not supported"); return; } - const auto RESOURCE = PROTO::colorManagement->m_imageDescriptions.emplace_back( + if (m_currentPreferred.valid()) + PROTO::colorManagement->destroyResource(m_currentPreferred.get()); + + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( makeShared(makeShared(r->client(), r->version(), id), true)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_imageDescriptions.pop_back(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - RESOURCE->m_settings = m_surface->getPreferredImageDescription(); - m_currentPreferredId = RESOURCE->m_settings->id(); + RESOURCE->self = RESOURCE; + m_currentPreferred = RESOURCE; - RESOURCE->resource()->sendReady(m_currentPreferredId); + m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription(); + if (!PROTO::colorManagement->m_debug && m_currentPreferred->settings.icc.fd) { + LOGM(ERR, "FIXME: parse icc profile"); + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported"); + return; + } + + RESOURCE->resource()->sendReady(++lastImageID); // FIXME: create correct id }); - - m_listeners.enter = m_surface->m_events.enter.listen([this](const auto& monitor) { onPreferredChanged(); }); - m_listeners.leave = m_surface->m_events.leave.listen([this](const auto& monitor) { onPreferredChanged(); }); -} - -void CColorManagementFeedbackSurface::onPreferredChanged() { - if (m_surface->m_enteredOutputs.size() == 1) { - const auto newId = m_surface->getPreferredImageDescription()->id(); - if (m_currentPreferredId != newId) - m_resource->sendPreferredChanged(newId); - } } bool CColorManagementFeedbackSurface::good() { @@ -408,54 +423,55 @@ bool CColorManagementFeedbackSurface::good() { } wl_client* CColorManagementFeedbackSurface::client() { - return m_client; + return pClient; } CColorManagementIccCreator::CColorManagementIccCreator(SP resource) : m_resource(resource) { if UNLIKELY (!good()) return; // - m_client = m_resource->client(); + pClient = m_resource->client(); m_resource->setOnDestroy([this](CWpImageDescriptionCreatorIccV1* r) { PROTO::colorManagement->destroyResource(this); }); m_resource->setCreate([this](CWpImageDescriptionCreatorIccV1* r, uint32_t id) { - LOGM(Log::TRACE, "Create image description from icc for id {}", id); + LOGM(TRACE, "Create image description from icc for id {}", id); // FIXME actually check completeness - if (m_icc.fd < 0 || !m_icc.length) { + if (settings.icc.fd < 0 || !settings.icc.length) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings"); return; } - const auto RESOURCE = PROTO::colorManagement->m_imageDescriptions.emplace_back( + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( makeShared(makeShared(r->client(), r->version(), id), false)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_imageDescriptions.pop_back(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); return; } - LOGM(Log::ERR, "FIXME: Parse icc file {}({},{}) for id {}", m_icc.fd, m_icc.offset, m_icc.length, id); + LOGM(ERR, "FIXME: Parse icc file {}({},{}) for id {}", settings.icc.fd, settings.icc.offset, settings.icc.length, id); // FIXME actually check support - if (m_icc.fd < 0 || !m_icc.length) { + if (settings.icc.fd < 0 || !settings.icc.length) { RESOURCE->resource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported"); return; } - RESOURCE->m_self = RESOURCE; - RESOURCE->m_settings = CImageDescription::from(m_settings); - RESOURCE->resource()->sendReady(RESOURCE->m_settings->id()); + RESOURCE->self = RESOURCE; + RESOURCE->settings = settings; + settings.id = ++lastImageID; + RESOURCE->resource()->sendReady(settings.id); // FIXME: create correct id PROTO::colorManagement->destroyResource(this); }); m_resource->setSetIccFile([this](CWpImageDescriptionCreatorIccV1* r, int fd, uint32_t offset, uint32_t length) { - m_icc.fd = fd; - m_icc.offset = offset; - m_icc.length = length; + settings.icc.fd = fd; + settings.icc.offset = offset; + settings.icc.length = length; }); } @@ -464,58 +480,51 @@ bool CColorManagementIccCreator::good() { } wl_client* CColorManagementIccCreator::client() { - return m_client; + return pClient; } CColorManagementParametricCreator::CColorManagementParametricCreator(SP resource) : m_resource(resource) { if UNLIKELY (!good()) return; // - m_client = m_resource->client(); + pClient = m_resource->client(); m_resource->setOnDestroy([this](CWpImageDescriptionCreatorParamsV1* r) { PROTO::colorManagement->destroyResource(this); }); m_resource->setCreate([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t id) { - LOGM(Log::TRACE, "Create image description from params for id {}", id); + LOGM(TRACE, "Create image description from params for id {}", id); // FIXME actually check completeness - if (!m_valuesSet) { + if (!valuesSet) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET, "Missing required settings"); return; } - const auto RESOURCE = PROTO::colorManagement->m_imageDescriptions.emplace_back( + const auto RESOURCE = PROTO::colorManagement->m_vImageDescriptions.emplace_back( makeShared(makeShared(r->client(), r->version(), id), false)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::colorManagement->m_imageDescriptions.pop_back(); + PROTO::colorManagement->m_vImageDescriptions.pop_back(); return; } // FIXME actually check support - if (!m_valuesSet) { + if (!valuesSet) { RESOURCE->resource()->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED, "unsupported"); return; } - if ((m_valuesSet & PC_TF) && !(m_valuesSet & PC_LUMINANCES)) { - m_settings.luminances = { - .min = m_settings.getTFMinLuminance(), - .max = m_settings.getTFMaxLuminance(), - .reference = m_settings.getTFRefLuminance(), - }; - } - - RESOURCE->m_self = RESOURCE; - RESOURCE->m_settings = CImageDescription::from(m_settings); - RESOURCE->resource()->sendReady(RESOURCE->m_settings->id()); + RESOURCE->self = RESOURCE; + RESOURCE->settings = settings; + settings.id = ++lastImageID; + RESOURCE->resource()->sendReady(settings.id); // FIXME: create correct id PROTO::colorManagement->destroyResource(this); }); m_resource->setSetTfNamed([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t tf) { - LOGM(Log::TRACE, "Set image description transfer function to {}", tf); - if (m_valuesSet & PC_TF) { + LOGM(TRACE, "Set image description transfer function to {}", tf); + if (valuesSet & PC_TF) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Transfer function already set"); return; } @@ -527,22 +536,15 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SPerror(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF, "Unsupported transfer function"); return; } - m_settings.transferFunction = convertTransferFunction(sc(tf)); - m_valuesSet |= PC_TF; + settings.transferFunction = convertTransferFunction((wpColorManagerV1TransferFunction)tf); + valuesSet |= PC_TF; }); m_resource->setSetTfPower([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t eexp) { - LOGM(Log::TRACE, "Set image description tf power to {}", eexp); - if (m_valuesSet & PC_TF_POWER) { + LOGM(TRACE, "Set image description tf power to {}", eexp); + if (valuesSet & PC_TF_POWER) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Transfer function power already set"); return; } @@ -550,16 +552,16 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SPerror(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "TF power is not supported"); return; } - m_settings.transferFunctionPower = eexp / 10000.0f; - if (m_settings.transferFunctionPower < 1.0 || m_settings.transferFunctionPower > 10.0) { + settings.transferFunctionPower = eexp / 10000.0f; + if (settings.transferFunctionPower < 1.0 || settings.transferFunctionPower > 10.0) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF, "Power should be between 1.0 and 10.0"); return; } - m_valuesSet |= PC_TF_POWER; + valuesSet |= PC_TF_POWER; }); m_resource->setSetPrimariesNamed([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t primaries) { - LOGM(Log::TRACE, "Set image description primaries by name {}", primaries); - if (m_valuesSet & PC_PRIMARIES) { + LOGM(TRACE, "Set image description primaries by name {}", primaries); + if (valuesSet & PC_PRIMARIES) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Primaries already set"); return; } @@ -571,7 +573,6 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP(primaries)); - m_settings.primaries = getPrimaries(m_settings.primariesNamed); - m_valuesSet |= PC_PRIMARIES; + settings.primariesNameSet = true; + settings.primariesNamed = convertPrimaries((wpColorManagerV1Primaries)primaries); + settings.primaries = getPrimaries(settings.primariesNamed); + valuesSet |= PC_PRIMARIES; }); m_resource->setSetPrimaries( [this](CWpImageDescriptionCreatorParamsV1* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { - LOGM(Log::TRACE, "Set image description primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y); - if (m_valuesSet & PC_PRIMARIES) { + LOGM(TRACE, "Set image description primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y); + if (valuesSet & PC_PRIMARIES) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Primaries already set"); return; } - m_settings.primariesNameSet = false; - m_settings.primaries = SPCPRimaries{.red = {.x = r_x / PRIMARIES_SCALE, .y = r_y / PRIMARIES_SCALE}, - .green = {.x = g_x / PRIMARIES_SCALE, .y = g_y / PRIMARIES_SCALE}, - .blue = {.x = b_x / PRIMARIES_SCALE, .y = b_y / PRIMARIES_SCALE}, - .white = {.x = w_x / PRIMARIES_SCALE, .y = w_y / PRIMARIES_SCALE}}; - m_valuesSet |= PC_PRIMARIES; + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Custom primaries aren't supported"); + return; + } + settings.primariesNameSet = false; + settings.primaries = SPCPRimaries{.red = {.x = r_x / 1000000.0f, .y = r_y / 1000000.0f}, + .green = {.x = g_x / 1000000.0f, .y = g_y / 1000000.0f}, + .blue = {.x = b_x / 1000000.0f, .y = b_y / 1000000.0f}, + .white = {.x = w_x / 1000000.0f, .y = w_y / 1000000.0f}}; + valuesSet |= PC_PRIMARIES; }); m_resource->setSetLuminances([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) { auto min = min_lum / 10000.0f; - LOGM(Log::TRACE, "Set image description luminances to {} - {} ({})", min, max_lum, reference_lum); - if (m_valuesSet & PC_LUMINANCES) { + LOGM(TRACE, "Set image description luminances to {} - {} ({})", min, max_lum, reference_lum); + if (valuesSet & PC_LUMINANCES) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Luminances already set"); return; } - if (max_lum <= min || reference_lum <= min) { + if (max_lum < reference_lum || reference_lum <= min) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, "Invalid luminances"); return; } - m_settings.luminances = SImageDescription::SPCLuminances{.min = min, .max = max_lum, .reference = reference_lum}; - m_valuesSet |= PC_LUMINANCES; + settings.luminances = SImageDescription::SPCLuminances{.min = min, .max = max_lum, .reference = reference_lum}; + valuesSet |= PC_LUMINANCES; }); m_resource->setSetMasteringDisplayPrimaries( [this](CWpImageDescriptionCreatorParamsV1* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { - LOGM(Log::TRACE, "Set image description mastering primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y); - if (m_valuesSet & PC_MASTERING_PRIMARIES) { + LOGM(TRACE, "Set image description mastering primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y); + if (valuesSet & PC_MASTERING_PRIMARIES) { r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Mastering primaries already set"); return; } - - m_settings.masteringPrimaries = SPCPRimaries{.red = {.x = r_x / PRIMARIES_SCALE, .y = r_y / PRIMARIES_SCALE}, - .green = {.x = g_x / PRIMARIES_SCALE, .y = g_y / PRIMARIES_SCALE}, - .blue = {.x = b_x / PRIMARIES_SCALE, .y = b_y / PRIMARIES_SCALE}, - .white = {.x = w_x / PRIMARIES_SCALE, .y = w_y / PRIMARIES_SCALE}}; - m_valuesSet |= PC_MASTERING_PRIMARIES; + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Mastering primaries are not supported"); + return; + } + settings.masteringPrimaries = SPCPRimaries{.red = {.x = r_x / 1000000.0f, .y = r_y / 1000000.0f}, + .green = {.x = g_x / 1000000.0f, .y = g_y / 1000000.0f}, + .blue = {.x = b_x / 1000000.0f, .y = b_y / 1000000.0f}, + .white = {.x = w_x / 1000000.0f, .y = w_y / 1000000.0f}}; + valuesSet |= PC_MASTERING_PRIMARIES; // FIXME: // If a compositor additionally supports target color volume exceeding the primary color volume, it must advertise wp_color_manager_v1.feature.extended_target_volume. @@ -636,7 +644,7 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SPsetSetMasteringLuminance([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t min_lum, uint32_t max_lum) { auto min = min_lum / 10000.0f; - LOGM(Log::TRACE, "Set image description mastering luminances to {} - {}", min, max_lum); + LOGM(TRACE, "Set image description mastering luminances to {} - {}", min, max_lum); // if (valuesSet & PC_MASTERING_LUMINANCES) { // r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Mastering luminances already set"); // return; @@ -645,27 +653,30 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SPerror(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE, "Invalid luminances"); return; } - - m_settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum}; - m_valuesSet |= PC_MASTERING_LUMINANCES; + if (!PROTO::colorManagement->m_debug) { + r->error(WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "Mastering luminances are not supported"); + return; + } + settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum}; + valuesSet |= PC_MASTERING_LUMINANCES; }); m_resource->setSetMaxCll([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t max_cll) { - LOGM(Log::TRACE, "Set image description max content light level to {}", max_cll); + LOGM(TRACE, "Set image description max content light level to {}", max_cll); // if (valuesSet & PC_CLL) { // r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Max CLL already set"); // return; // } - m_settings.maxCLL = max_cll; - m_valuesSet |= PC_CLL; + settings.maxCLL = max_cll; + valuesSet |= PC_CLL; }); m_resource->setSetMaxFall([this](CWpImageDescriptionCreatorParamsV1* r, uint32_t max_fall) { - LOGM(Log::TRACE, "Set image description max frame-average light level to {}", max_fall); + LOGM(TRACE, "Set image description max frame-average light level to {}", max_fall); // if (valuesSet & PC_FALL) { // r->error(WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET, "Max FALL already set"); // return; // } - m_settings.maxFALL = max_fall; - m_valuesSet |= PC_FALL; + settings.maxFALL = max_fall; + valuesSet |= PC_FALL; }); } @@ -674,7 +685,7 @@ bool CColorManagementParametricCreator::good() { } wl_client* CColorManagementParametricCreator::client() { - return m_client; + return pClient; } CColorManagementImageDescription::CColorManagementImageDescription(SP resource, bool allowGetInformation) : @@ -682,19 +693,19 @@ CColorManagementImageDescription::CColorManagementImageDescription(SPclient(); + pClient = m_resource->client(); m_resource->setDestroy([this](CWpImageDescriptionV1* r) { PROTO::colorManagement->destroyResource(this); }); m_resource->setOnDestroy([this](CWpImageDescriptionV1* r) { PROTO::colorManagement->destroyResource(this); }); m_resource->setGetInformation([this](CWpImageDescriptionV1* r, uint32_t id) { - LOGM(Log::TRACE, "Get image information for image={}, id={}", (uintptr_t)r, id); + LOGM(TRACE, "Get image information for image={}, id={}", (uintptr_t)r, id); if (!m_allowGetInformation) { r->error(WP_IMAGE_DESCRIPTION_V1_ERROR_NO_INFORMATION, "Image descriptions doesn't allow get_information request"); return; } - auto RESOURCE = makeShared(makeShared(r->client(), r->version(), id), m_settings->value()); + auto RESOURCE = makeShared(makeShared(r->client(), r->version(), id), settings); if UNLIKELY (!RESOURCE->good()) r->noMemory(); @@ -709,7 +720,7 @@ bool CColorManagementImageDescription::good() { } wl_client* CColorManagementImageDescription::client() { - return m_client; + return pClient; } SP CColorManagementImageDescription::resource() { @@ -717,55 +728,33 @@ SP CColorManagementImageDescription::resource() { } CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP resource, const SImageDescription& settings_) : - m_resource(resource), m_settings(settings_) { + m_resource(resource), settings(settings_) { if UNLIKELY (!good()) return; - m_client = m_resource->client(); + pClient = m_resource->client(); - const auto toProto = [](float value) { return sc(std::round(value * PRIMARIES_SCALE)); }; + const auto toProto = [](float value) { return int32_t(std::round(value * 10000)); }; - // FIXME: - // if (m_icc.fd >= 0) - // m_resource->sendIccFile(m_icc.fd, m_icc.length); + if (settings.icc.fd >= 0) + m_resource->sendIccFile(settings.icc.fd, settings.icc.length); // send preferred client paramateres - m_resource->sendPrimaries(toProto(m_settings.primaries.red.x), toProto(m_settings.primaries.red.y), toProto(m_settings.primaries.green.x), - toProto(m_settings.primaries.green.y), toProto(m_settings.primaries.blue.x), toProto(m_settings.primaries.blue.y), - toProto(m_settings.primaries.white.x), toProto(m_settings.primaries.white.y)); + m_resource->sendPrimaries(toProto(settings.primaries.red.x), toProto(settings.primaries.red.y), toProto(settings.primaries.green.x), toProto(settings.primaries.green.y), + toProto(settings.primaries.blue.x), toProto(settings.primaries.blue.y), toProto(settings.primaries.white.x), toProto(settings.primaries.white.y)); + if (settings.primariesNameSet) + m_resource->sendPrimariesNamed(settings.primariesNamed); + m_resource->sendTfPower(std::round(settings.transferFunctionPower * 10000)); + m_resource->sendTfNamed(settings.transferFunction); + m_resource->sendLuminances(std::round(settings.luminances.min * 10000), settings.luminances.max, settings.luminances.reference); - if (m_settings.primariesNameSet) - m_resource->sendPrimariesNamed(m_settings.primariesNamed); - - m_resource->sendTfNamed(m_settings.transferFunction); - - if (m_settings.transferFunctionPower != 1.0f) - m_resource->sendTfPower(std::round(m_settings.transferFunctionPower * 10000)); - - m_resource->sendLuminances(std::round(m_settings.luminances.min * 10000), m_settings.luminances.max, m_settings.luminances.reference); - - const auto& targetPrimaries = ( // - m_settings.masteringPrimaries.red.x != 0 || m_settings.masteringPrimaries.red.y != 0 || // - m_settings.masteringPrimaries.green.x != 0 || m_settings.masteringPrimaries.green.y != 0 || // - m_settings.masteringPrimaries.blue.x != 0 || m_settings.masteringPrimaries.blue.y != 0) ? - m_settings.masteringPrimaries : - m_settings.primaries; - - m_resource->sendTargetPrimaries( // - toProto(targetPrimaries.red.x), toProto(targetPrimaries.red.y), // - toProto(targetPrimaries.green.x), toProto(targetPrimaries.green.y), // - toProto(targetPrimaries.blue.x), toProto(targetPrimaries.blue.y), // - toProto(targetPrimaries.white.x), toProto(targetPrimaries.white.y)); - - if (m_settings.masteringLuminances.max > 0) - m_resource->sendTargetLuminance(std::round(m_settings.masteringLuminances.min * 10000), m_settings.masteringLuminances.max); - else - m_resource->sendTargetLuminance(std::round(m_settings.luminances.min * 10000), m_settings.luminances.max); - - if (m_settings.maxCLL > 0 || m_settings.maxFALL > 0) { - m_resource->sendTargetMaxCll(m_settings.maxCLL); - m_resource->sendTargetMaxFall(m_settings.maxFALL); - } + // send expexted display paramateres + m_resource->sendTargetPrimaries(toProto(settings.masteringPrimaries.red.x), toProto(settings.masteringPrimaries.red.y), toProto(settings.masteringPrimaries.green.x), + toProto(settings.masteringPrimaries.green.y), toProto(settings.masteringPrimaries.blue.x), toProto(settings.masteringPrimaries.blue.y), + toProto(settings.masteringPrimaries.white.x), toProto(settings.masteringPrimaries.white.y)); + m_resource->sendTargetLuminance(std::round(settings.masteringLuminances.min * 10000), settings.masteringLuminances.max); + m_resource->sendTargetMaxCll(settings.maxCLL); + m_resource->sendTargetMaxFall(settings.maxFALL); m_resource->sendDone(); } @@ -775,7 +764,7 @@ bool CColorManagementImageDescriptionInfo::good() { } wl_client* CColorManagementImageDescriptionInfo::client() { - return m_client; + return pClient; } CColorManagementProtocol::CColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name, bool debug) : @@ -784,61 +773,54 @@ CColorManagementProtocol::CColorManagementProtocol(const wl_interface* iface, co } void CColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } - LOGM(Log::TRACE, "New WP_color_manager at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(TRACE, "New WP_color_manager at {:x}", (uintptr_t)RESOURCE.get()); } void CColorManagementProtocol::onImagePreferredChanged(uint32_t preferredId) { - for (auto const& feedback : m_feedbackSurfaces) { + for (auto const& feedback : m_vFeedbackSurfaces) { feedback->m_resource->sendPreferredChanged(preferredId); } } void CColorManagementProtocol::onMonitorImageDescriptionChanged(WP monitor) { - for (auto const& output : m_outputs) { - if (output->m_output && output->m_output->m_monitor == monitor) + for (auto const& output : m_vOutputs) { + if (output->m_monitor == monitor) output->m_resource->sendImageDescriptionChanged(); } - // recheck feedbacks - for (auto const& feedback : m_feedbackSurfaces) - feedback->onPreferredChanged(); -} - -bool CColorManagementProtocol::isClientCMAware(wl_client* client) { - return std::ranges::any_of(m_managers, [client](const auto& m) { return m->client() == client; }); } void CColorManagementProtocol::destroyResource(CColorManager* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CColorManagementProtocol::destroyResource(CColorManagementOutput* resource) { - std::erase_if(m_outputs, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vOutputs, [&](const auto& other) { return other.get() == resource; }); } void CColorManagementProtocol::destroyResource(CColorManagementSurface* resource) { - std::erase_if(m_surfaces, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); } void CColorManagementProtocol::destroyResource(CColorManagementFeedbackSurface* resource) { - std::erase_if(m_feedbackSurfaces, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vFeedbackSurfaces, [&](const auto& other) { return other.get() == resource; }); } void CColorManagementProtocol::destroyResource(CColorManagementIccCreator* resource) { - std::erase_if(m_iccCreators, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vIccCreators, [&](const auto& other) { return other.get() == resource; }); } void CColorManagementProtocol::destroyResource(CColorManagementParametricCreator* resource) { - std::erase_if(m_parametricCreators, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vParametricCreators, [&](const auto& other) { return other.get() == resource; }); } void CColorManagementProtocol::destroyResource(CColorManagementImageDescription* resource) { - std::erase_if(m_imageDescriptions, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vImageDescriptions, [&](const auto& other) { return other.get() == resource; }); } diff --git a/src/protocols/ColorManagement.hpp b/src/protocols/ColorManagement.hpp index 7cdab37d..55219b8f 100644 --- a/src/protocols/ColorManagement.hpp +++ b/src/protocols/ColorManagement.hpp @@ -7,7 +7,7 @@ #include "../helpers/Monitor.hpp" #include "core/Compositor.hpp" #include "color-management-v1.hpp" -#include "../helpers/cm/ColorManagement.hpp" +#include "types/ColorManagement.hpp" class CColorManager; class CColorManagementOutput; @@ -18,8 +18,7 @@ class CColorManager { public: CColorManager(SP resource); - bool good(); - wl_client* client(); + bool good(); private: SP m_resource; @@ -27,18 +26,18 @@ class CColorManager { class CColorManagementOutput { public: - CColorManagementOutput(SP resource, WP output); + CColorManagementOutput(SP resource, WP monitor); bool good(); wl_client* client(); - WP m_self; - WP m_imageDescription; + WP self; + WP imageDescription; private: SP m_resource; - wl_client* m_client = nullptr; - WP m_output; + wl_client* pClient = nullptr; + WP m_monitor; friend class CColorManagementProtocol; friend class CColorManagementImageDescription; @@ -46,13 +45,14 @@ class CColorManagementOutput { class CColorManagementSurface { public: + CColorManagementSurface(SP surface_); // temporary interface for frog CM CColorManagementSurface(SP resource, SP surface_); bool good(); wl_client* client(); - WP m_self; - WP m_surface; + WP self; + WP surface; const NColorManagement::SImageDescription& imageDescription(); bool hasImageDescription(); @@ -60,17 +60,17 @@ class CColorManagementSurface { const hdr_output_metadata& hdrMetadata(); void setHDRMetadata(const hdr_output_metadata& metadata); bool needsHdrMetadataUpdate(); - bool isHDR(); - bool isWindowsScRGB(); private: SP m_resource; - wl_client* m_client = nullptr; - NColorManagement::PImageDescription m_imageDescription; - NColorManagement::PImageDescription m_lastImageDescription; + wl_client* pClient = nullptr; + NColorManagement::SImageDescription m_imageDescription; bool m_hasImageDescription = false; bool m_needsNewMetadata = false; hdr_output_metadata m_hdrMetadata; + + friend class CXXColorManagementSurface; + friend class CFrogColorManagementSurface; }; class CColorManagementFeedbackSurface { @@ -80,21 +80,14 @@ class CColorManagementFeedbackSurface { bool good(); wl_client* client(); - WP m_self; - WP m_surface; + WP self; + WP surface; private: SP m_resource; - wl_client* m_client = nullptr; + wl_client* pClient = nullptr; - uint32_t m_currentPreferredId = 0; - - struct { - CHyprSignalListener enter; - CHyprSignalListener leave; - } m_listeners; - - void onPreferredChanged(); + WP m_currentPreferred; friend class CColorManagementProtocol; }; @@ -106,21 +99,13 @@ class CColorManagementIccCreator { bool good(); wl_client* client(); - WP m_self; + WP self; - NColorManagement::SImageDescription m_settings; - struct SIccFile { - int fd = -1; - uint32_t length = 0; - uint32_t offset = 0; - bool operator==(const SIccFile& i2) const { - return fd == i2.fd; - } - } m_icc; + NColorManagement::SImageDescription settings; private: SP m_resource; - wl_client* m_client = nullptr; + wl_client* pClient = nullptr; }; class CColorManagementParametricCreator { @@ -130,9 +115,9 @@ class CColorManagementParametricCreator { bool good(); wl_client* client(); - WP m_self; + WP self; - NColorManagement::SImageDescription m_settings; + NColorManagement::SImageDescription settings; private: enum eValuesSet : uint32_t { // NOLINT @@ -147,8 +132,8 @@ class CColorManagementParametricCreator { }; SP m_resource; - wl_client* m_client = nullptr; - uint32_t m_valuesSet = 0; // enum eValuesSet + wl_client* pClient = nullptr; + uint32_t valuesSet = 0; // enum eValuesSet }; class CColorManagementImageDescription { @@ -159,13 +144,13 @@ class CColorManagementImageDescription { wl_client* client(); SP resource(); - WP m_self; + WP self; - NColorManagement::PImageDescription m_settings; + NColorManagement::SImageDescription settings; private: SP m_resource; - wl_client* m_client = nullptr; + wl_client* pClient = nullptr; bool m_allowGetInformation = false; friend class CColorManagementOutput; @@ -180,8 +165,8 @@ class CColorManagementImageDescriptionInfo { private: SP m_resource; - wl_client* m_client = nullptr; - NColorManagement::SImageDescription m_settings; + wl_client* pClient = nullptr; + NColorManagement::SImageDescription settings; }; class CColorManagementProtocol : public IWaylandProtocol { @@ -193,8 +178,6 @@ class CColorManagementProtocol : public IWaylandProtocol { void onImagePreferredChanged(uint32_t preferredId); void onMonitorImageDescriptionChanged(WP monitor); - bool isClientCMAware(wl_client* client); - private: void destroyResource(CColorManager* resource); void destroyResource(CColorManagementOutput* resource); @@ -204,13 +187,13 @@ class CColorManagementProtocol : public IWaylandProtocol { void destroyResource(CColorManagementParametricCreator* resource); void destroyResource(CColorManagementImageDescription* resource); - std::vector> m_managers; - std::vector> m_outputs; - std::vector> m_surfaces; - std::vector> m_feedbackSurfaces; - std::vector> m_iccCreators; - std::vector> m_parametricCreators; - std::vector> m_imageDescriptions; + std::vector> m_vManagers; + std::vector> m_vOutputs; + std::vector> m_vSurfaces; + std::vector> m_vFeedbackSurfaces; + std::vector> m_vIccCreators; + std::vector> m_vParametricCreators; + std::vector> m_vImageDescriptions; bool m_debug = false; friend class CColorManager; @@ -220,6 +203,9 @@ class CColorManagementProtocol : public IWaylandProtocol { friend class CColorManagementIccCreator; friend class CColorManagementParametricCreator; friend class CColorManagementImageDescription; + + friend class CXXColorManagementSurface; + friend class CFrogColorManagementSurface; }; namespace PROTO { diff --git a/src/protocols/CommitTiming.cpp b/src/protocols/CommitTiming.cpp deleted file mode 100644 index 9cc2da83..00000000 --- a/src/protocols/CommitTiming.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "CommitTiming.hpp" -#include "core/Compositor.hpp" -#include "../managers/eventLoop/EventLoopManager.hpp" -#include "../managers/eventLoop/EventLoopTimer.hpp" - -CCommitTimerResource::CCommitTimerResource(UP&& resource_, SP surface) : m_resource(std::move(resource_)), m_surface(surface) { - if UNLIKELY (!m_resource->resource()) - return; - - m_resource->setData(this); - m_resource->setDestroy([this](CWpCommitTimerV1* r) { PROTO::commitTiming->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpCommitTimerV1* r) { PROTO::commitTiming->destroyResource(this); }); - - m_resource->setSetTimestamp([this](CWpCommitTimerV1* r, uint32_t tvHi, uint32_t tvLo, uint32_t tvNsec) { - if (!m_surface) { - r->error(WP_COMMIT_TIMER_V1_ERROR_SURFACE_DESTROYED, "Surface was gone"); - return; - } - - if (m_surface->m_pending.pendingTimeout.has_value()) { - r->error(WP_COMMIT_TIMER_V1_ERROR_TIMESTAMP_EXISTS, "Timestamp is already set"); - return; - } - - const auto delay = Time::till({.tv_sec = (((uint64_t)tvHi) << 32) | (uint64_t)tvLo, .tv_nsec = tvNsec}); - - if (delay.count() <= 0) { - m_surface->m_pending.pendingTimeout.reset(); - } else - m_surface->m_pending.pendingTimeout = delay; - }); - - m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit2.listen([this](auto state) { - if (!state || !state->pendingTimeout.has_value() || !m_surface || m_surface->isTearing()) - return; - - m_surface->m_stateQueue.lock(state, LOCK_REASON_TIMER); - - if (!state->timer) { - state->timer = makeShared( - state->pendingTimeout, - [surface = m_surface, state](SP self, void* data) { - if (!surface || !state) - return; - - surface->m_stateQueue.unlock(state, LOCK_REASON_TIMER); - }, - nullptr); - g_pEventLoopManager->addTimer(state->timer); - } else - state->timer->updateTimeout(state->pendingTimeout); - - state->pendingTimeout.reset(); - }); -} - -bool CCommitTimerResource::good() { - return m_resource->resource(); -} - -CCommitTimingManagerResource::CCommitTimingManagerResource(UP&& resource_) : m_resource(std::move(resource_)) { - if UNLIKELY (!m_resource->resource()) - return; - - m_resource->setData(this); - m_resource->setDestroy([this](CWpCommitTimingManagerV1* r) { PROTO::commitTiming->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpCommitTimingManagerV1* r) { PROTO::commitTiming->destroyResource(this); }); - - m_resource->setGetTimer([](CWpCommitTimingManagerV1* r, uint32_t id, wl_resource* surfResource) { - if (!surfResource) { - r->error(-1, "No resource for commit timing"); - return; - } - - auto surf = CWLSurfaceResource::fromResource(surfResource); - - if (!surf) { - r->error(-1, "No surface for commit timing"); - return; - } - - if (surf->m_commitTimer) { - r->error(WP_COMMIT_TIMING_MANAGER_V1_ERROR_COMMIT_TIMER_EXISTS, "Surface already has a commit timing"); - return; - } - - const auto& RESOURCE = PROTO::commitTiming->m_timers.emplace_back(makeUnique(makeUnique(r->client(), r->version(), id), surf)); - - if (!RESOURCE->good()) { - r->noMemory(); - PROTO::commitTiming->m_timers.pop_back(); - return; - } - - surf->m_commitTimer = RESOURCE; - }); -} - -CCommitTimingManagerResource::~CCommitTimingManagerResource() { - ; -} - -bool CCommitTimingManagerResource::good() { - return m_resource->resource(); -} - -CCommitTimingProtocol::CCommitTimingProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; -} - -void CCommitTimingProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))).get(); - - if (!RESOURCE->good()) { - wl_client_post_no_memory(client); - m_managers.pop_back(); - return; - } -} - -void CCommitTimingProtocol::destroyResource(CCommitTimingManagerResource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); -} - -void CCommitTimingProtocol::destroyResource(CCommitTimerResource* res) { - std::erase_if(m_timers, [&](const auto& other) { return other.get() == res; }); -} diff --git a/src/protocols/CommitTiming.hpp b/src/protocols/CommitTiming.hpp deleted file mode 100644 index b5a1de93..00000000 --- a/src/protocols/CommitTiming.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include "WaylandProtocol.hpp" -#include "commit-timing-v1.hpp" - -#include "../helpers/signal/Signal.hpp" -#include "helpers/time/Time.hpp" - -class CWLSurfaceResource; -class CEventLoopTimer; - -class CCommitTimerResource { - public: - CCommitTimerResource(UP&& resource_, SP surface); - - bool good(); - - private: - UP m_resource; - WP m_surface; - - struct { - CHyprSignalListener surfaceStateCommit; - } m_listeners; - - friend class CCommitTimingProtocol; - friend class CCommitTimingManagerResource; -}; - -class CCommitTimingManagerResource { - public: - CCommitTimingManagerResource(UP&& resource_); - ~CCommitTimingManagerResource(); - - bool good(); - - private: - UP m_resource; -}; - -class CCommitTimingProtocol : public IWaylandProtocol { - public: - CCommitTimingProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - private: - void destroyResource(CCommitTimingManagerResource* resource); - void destroyResource(CCommitTimerResource* resource); - - // - std::vector> m_managers; - std::vector> m_timers; - - friend class CCommitTimingManagerResource; - friend class CCommitTimerResource; -}; - -namespace PROTO { - inline UP commitTiming; -}; diff --git a/src/protocols/ContentType.cpp b/src/protocols/ContentType.cpp index acae218c..0dd5481a 100644 --- a/src/protocols/ContentType.cpp +++ b/src/protocols/ContentType.cpp @@ -6,34 +6,34 @@ CContentTypeManager::CContentTypeManager(SP resource) : if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CWpContentTypeManagerV1* r) { PROTO::contentType->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpContentTypeManagerV1* r) { PROTO::contentType->destroyResource(this); }); + resource->setDestroy([](CWpContentTypeManagerV1* r) {}); + resource->setOnDestroy([this](CWpContentTypeManagerV1* r) { PROTO::contentType->destroyResource(this); }); - m_resource->setGetSurfaceContentType([](CWpContentTypeManagerV1* r, uint32_t id, wl_resource* surface) { - LOGM(Log::TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); + resource->setGetSurfaceContentType([](CWpContentTypeManagerV1* r, uint32_t id, wl_resource* surface) { + LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); auto SURF = CWLSurfaceResource::fromResource(surface); if (!SURF) { - LOGM(Log::ERR, "No surface for resource {}", (uintptr_t)surface); + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); r->error(-1, "Invalid surface (2)"); return; } - if (SURF->m_contentType) { + if (SURF->colorManagement) { r->error(WP_CONTENT_TYPE_MANAGER_V1_ERROR_ALREADY_CONSTRUCTED, "CT manager already exists"); return; } - const auto RESOURCE = PROTO::contentType->m_contentTypes.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + const auto RESOURCE = PROTO::contentType->m_vContentTypes.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::contentType->m_contentTypes.pop_back(); + PROTO::contentType->m_vContentTypes.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - SURF->m_contentType = RESOURCE; + SURF->contentType = RESOURCE; }); } @@ -42,19 +42,19 @@ bool CContentTypeManager::good() { } CContentType::CContentType(WP surface) { - m_destroy = surface->m_events.destroy.listen([this] { PROTO::contentType->destroyResource(this); }); + destroy = surface->events.destroy.registerListener([this](std::any d) { PROTO::contentType->destroyResource(this); }); } CContentType::CContentType(SP resource) : m_resource(resource) { if UNLIKELY (!good()) return; - m_client = m_resource->client(); + m_pClient = resource->client(); - m_resource->setDestroy([this](CWpContentTypeV1* r) { PROTO::contentType->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpContentTypeV1* r) { PROTO::contentType->destroyResource(this); }); + resource->setDestroy([this](CWpContentTypeV1* r) { PROTO::contentType->destroyResource(this); }); + resource->setOnDestroy([this](CWpContentTypeV1* r) { PROTO::contentType->destroyResource(this); }); - m_resource->setSetContentType([this](CWpContentTypeV1* r, wpContentTypeV1Type type) { m_value = NContentType::fromWP(type); }); + resource->setSetContentType([this](CWpContentTypeV1* r, wpContentTypeV1Type type) { value = NContentType::fromWP(type); }); } bool CContentType::good() { @@ -62,7 +62,7 @@ bool CContentType::good() { } wl_client* CContentType::client() { - return m_client; + return m_pClient; } CContentTypeProtocol::CContentTypeProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -70,26 +70,26 @@ CContentTypeProtocol::CContentTypeProtocol(const wl_interface* iface, const int& } void CContentTypeProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } SP CContentTypeProtocol::getContentType(WP surface) { - if (surface->m_contentType.valid()) - return surface->m_contentType.lock(); + if (surface->contentType.valid()) + return surface->contentType.lock(); - return m_contentTypes.emplace_back(makeShared(surface)); + return m_vContentTypes.emplace_back(makeShared(surface)); } void CContentTypeProtocol::destroyResource(CContentTypeManager* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CContentTypeProtocol::destroyResource(CContentType* resource) { - std::erase_if(m_contentTypes, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vContentTypes, [&](const auto& other) { return other.get() == resource; }); } diff --git a/src/protocols/ContentType.hpp b/src/protocols/ContentType.hpp index 4fc9bee5..4c0c445f 100644 --- a/src/protocols/ContentType.hpp +++ b/src/protocols/ContentType.hpp @@ -22,15 +22,15 @@ class CContentType { bool good(); wl_client* client(); - NContentType::eContentType m_value = NContentType::CONTENT_TYPE_NONE; + NContentType::eContentType value = NContentType::CONTENT_TYPE_NONE; - WP m_self; + WP self; private: SP m_resource; - wl_client* m_client = nullptr; + wl_client* m_pClient = nullptr; - CHyprSignalListener m_destroy; + CHyprSignalListener destroy; friend class CContentTypeProtocol; }; @@ -47,8 +47,8 @@ class CContentTypeProtocol : public IWaylandProtocol { void destroyResource(CContentTypeManager* resource); void destroyResource(CContentType* resource); - std::vector> m_managers; - std::vector> m_contentTypes; + std::vector> m_vManagers; + std::vector> m_vContentTypes; friend class CContentTypeManager; friend class CContentType; diff --git a/src/protocols/CursorShape.cpp b/src/protocols/CursorShape.cpp index 0583da1a..d38d5a8c 100644 --- a/src/protocols/CursorShape.cpp +++ b/src/protocols/CursorShape.cpp @@ -7,15 +7,15 @@ CCursorShapeProtocol::CCursorShapeProtocol(const wl_interface* iface, const int& } void CCursorShapeProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [res](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [res](const auto& other) { return other->resource() == res; }); } void CCursorShapeProtocol::onDeviceResourceDestroy(wl_resource* res) { - std::erase_if(m_devices, [res](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vDevices, [res](const auto& other) { return other->resource() == res; }); } void CCursorShapeProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CWpCursorShapeManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CWpCursorShapeManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -32,8 +32,8 @@ void CCursorShapeProtocol::onGetTabletToolV2(CWpCursorShapeManagerV1* pMgr, uint } void CCursorShapeProtocol::createCursorShapeDevice(CWpCursorShapeManagerV1* pMgr, uint32_t id, wl_resource* resource) { - const auto CLIENT = pMgr->client(); - const auto& RESOURCE = m_devices.emplace_back(makeUnique(CLIENT, pMgr->version(), id)); + const auto CLIENT = pMgr->client(); + const auto RESOURCE = m_vDevices.emplace_back(makeShared(CLIENT, pMgr->version(), id)); RESOURCE->setOnDestroy([this](CWpCursorShapeDeviceV1* p) { this->onDeviceResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CWpCursorShapeDeviceV1* p) { this->onDeviceResourceDestroy(p->resource()); }); @@ -41,7 +41,7 @@ void CCursorShapeProtocol::createCursorShapeDevice(CWpCursorShapeManagerV1* pMgr } void CCursorShapeProtocol::onSetShape(CWpCursorShapeDeviceV1* pMgr, uint32_t serial, wpCursorShapeDeviceV1Shape shape) { - if UNLIKELY (sc(shape) == 0 || sc(shape) >= CURSOR_SHAPE_NAMES.size()) { + if UNLIKELY ((uint32_t)shape == 0 || (uint32_t)shape >= CURSOR_SHAPE_NAMES.size()) { pMgr->error(WP_CURSOR_SHAPE_DEVICE_V1_ERROR_INVALID_SHAPE, "The shape is invalid"); return; } @@ -51,5 +51,5 @@ void CCursorShapeProtocol::onSetShape(CWpCursorShapeDeviceV1* pMgr, uint32_t ser event.shape = shape; event.shapeName = CURSOR_SHAPE_NAMES.at(shape); - m_events.setShape.emit(event); -} + events.setShape.emit(event); +} \ No newline at end of file diff --git a/src/protocols/CursorShape.hpp b/src/protocols/CursorShape.hpp index d4a130e9..37b7e6c8 100644 --- a/src/protocols/CursorShape.hpp +++ b/src/protocols/CursorShape.hpp @@ -18,8 +18,8 @@ class CCursorShapeProtocol : public IWaylandProtocol { }; struct { - CSignalT setShape; - } m_events; + CSignal setShape; + } events; private: void onManagerResourceDestroy(wl_resource* res); @@ -32,10 +32,10 @@ class CCursorShapeProtocol : public IWaylandProtocol { void createCursorShapeDevice(CWpCursorShapeManagerV1* pMgr, uint32_t id, wl_resource* resource); // - std::vector> m_devices; - std::vector> m_managers; + std::vector> m_vDevices; + std::vector> m_vManagers; }; namespace PROTO { inline UP cursorShape; -}; +}; \ No newline at end of file diff --git a/src/protocols/DRMLease.cpp b/src/protocols/DRMLease.cpp index 2da7b04a..e70f0441 100644 --- a/src/protocols/DRMLease.cpp +++ b/src/protocols/DRMLease.cpp @@ -1,343 +1,314 @@ #include "DRMLease.hpp" #include "../Compositor.hpp" #include "../helpers/Monitor.hpp" -#include "drm-lease-v1.hpp" #include "managers/eventLoop/EventLoopManager.hpp" -#include "protocols/WaylandProtocol.hpp" -#include #include #include using namespace Hyprutils::OS; -CDRMLeaseResource::CDRMLeaseResource(SP resource_, SP request) : m_resource(resource_) { +CDRMLeaseResource::CDRMLeaseResource(SP resource_, SP request) : resource(resource_) { if UNLIKELY (!good()) return; - m_parent = request->m_parent; - m_requested = request->m_requested; + resource->setOnDestroy([this](CWpDrmLeaseV1* r) { PROTO::lease->destroyResource(this); }); + resource->setDestroy([this](CWpDrmLeaseV1* r) { PROTO::lease->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpDrmLeaseV1* r) { - if (m_parent && PROTO::lease.contains(m_parent->m_deviceName)) - PROTO::lease.at(m_parent->m_deviceName)->destroyResource(this); - }); - m_resource->setDestroy([this](CWpDrmLeaseV1* r) { - if (m_parent && PROTO::lease.contains(m_parent->m_deviceName)) - PROTO::lease.at(m_parent->m_deviceName)->destroyResource(this); - }); + parent = request->parent; + requested = request->requested; - for (auto const& m : m_requested) { - if (!m->m_monitor || m->m_monitor->m_isBeingLeased) { - LOGM(Log::ERR, "Rejecting lease: no monitor or monitor is being leased for {}", (m->m_monitor ? m->m_monitor->m_name : "null")); - m_resource->sendFinished(); + for (auto const& m : requested) { + if (!m->monitor || m->monitor->isBeingLeased) { + LOGM(ERR, "Rejecting lease: no monitor or monitor is being leased for {}", (m->monitor ? m->monitor->szName : "null")); + resource->sendFinished(); return; } } // grant the lease if it is seemingly valid - LOGM(Log::DEBUG, "Leasing outputs: {}", [this]() { + LOGM(LOG, "Leasing outputs: {}", [this]() { std::string roll; - for (auto const& o : m_requested) { - roll += std::format("{} ", o->m_monitor->m_name); + for (auto const& o : requested) { + roll += std::format("{} ", o->monitor->szName); } return roll; }()); std::vector> outputs; // reserve to avoid reallocations - outputs.reserve(m_requested.size()); + outputs.reserve(requested.size()); - for (auto const& m : m_requested) { - outputs.emplace_back(m->m_monitor->m_output); + for (auto const& m : requested) { + outputs.emplace_back(m->monitor->output); } auto aqlease = Aquamarine::CDRMLease::create(outputs); if (!aqlease) { - LOGM(Log::ERR, "Rejecting lease: backend failed to alloc a lease"); - m_resource->sendFinished(); + LOGM(ERR, "Rejecting lease: backend failed to alloc a lease"); + resource->sendFinished(); return; } - m_lease = aqlease; + lease = aqlease; - for (auto const& m : m_requested) { - m->m_monitor->m_isBeingLeased = true; + for (auto const& m : requested) { + m->monitor->isBeingLeased = true; } - m_listeners.destroyLease = m_lease->events.destroy.listen([this] { - for (auto const& m : m_requested) { - if (m && m->m_monitor) - m->m_monitor->m_isBeingLeased = false; + listeners.destroyLease = lease->events.destroy.registerListener([this](std::any d) { + for (auto const& m : requested) { + if (m && m->monitor) + m->monitor->isBeingLeased = false; } - m_resource->sendFinished(); - LOGM(Log::DEBUG, "Revoking lease for fd {}", m_lease->leaseFD); + resource->sendFinished(); + LOGM(LOG, "Revoking lease for fd {}", lease->leaseFD); }); - LOGM(Log::DEBUG, "Granting lease, sending fd {}", m_lease->leaseFD); + LOGM(LOG, "Granting lease, sending fd {}", lease->leaseFD); - m_resource->sendLeaseFd(m_lease->leaseFD); + resource->sendLeaseFd(lease->leaseFD); - close(m_lease->leaseFD); + close(lease->leaseFD); } bool CDRMLeaseResource::good() { - return m_resource->resource(); + return resource->resource(); } CDRMLeaseResource::~CDRMLeaseResource() { // destroy in this order to ensure listener gets called - m_lease.reset(); - m_listeners.destroyLease.reset(); + lease.reset(); + listeners.destroyLease.reset(); } -CDRMLeaseRequestResource::CDRMLeaseRequestResource(WP parent_, SP resource_) : m_parent(parent_), m_resource(resource_) { +CDRMLeaseRequestResource::CDRMLeaseRequestResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWpDrmLeaseRequestV1* r) { - if (m_parent && PROTO::lease.contains(m_parent->m_deviceName)) - PROTO::lease.at(m_parent->m_deviceName)->destroyResource(this); - }); + resource->setOnDestroy([this](CWpDrmLeaseRequestV1* r) { PROTO::lease->destroyResource(this); }); - m_resource->setRequestConnector([this](CWpDrmLeaseRequestV1* r, wl_resource* conn) { + resource->setRequestConnector([this](CWpDrmLeaseRequestV1* r, wl_resource* conn) { if (!conn) { - m_resource->error(-1, "Null connector"); + resource->error(-1, "Null connector"); return; } auto CONNECTOR = CDRMLeaseConnectorResource::fromResource(conn); - if (std::ranges::find(m_requested, CONNECTOR) != m_requested.end()) { - m_resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "Connector already requested"); + if (std::find(requested.begin(), requested.end(), CONNECTOR) != requested.end()) { + resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "Connector already requested"); return; } - auto& lease = PROTO::lease.at(m_parent->m_deviceName); + // TODO: when (if) we add multi, make sure this is from the correct device. - if (std::ranges::find(lease->m_connectors.begin(), lease->m_connectors.end(), CONNECTOR) == lease->m_connectors.end()) { - m_resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_WRONG_DEVICE, "Connector requested for wrong device"); - return; - } - - m_requested.emplace_back(CONNECTOR); + requested.emplace_back(CONNECTOR); }); - m_resource->setSubmit([this](CWpDrmLeaseRequestV1* r, uint32_t id) { - if (m_requested.empty()) { - m_resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "No connectors added"); + resource->setSubmit([this](CWpDrmLeaseRequestV1* r, uint32_t id) { + if (requested.empty()) { + resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "No connectors added"); return; } - auto RESOURCE = makeShared(makeShared(m_resource->client(), m_resource->version(), id), m_self.lock()); + auto RESOURCE = makeShared(makeShared(resource->client(), resource->version(), id), self.lock()); if UNLIKELY (!RESOURCE) { - m_resource->noMemory(); + resource->noMemory(); return; } - PROTO::lease.at(m_parent->m_deviceName)->m_leases.emplace_back(RESOURCE); + PROTO::lease->m_vLeases.emplace_back(RESOURCE); - // per protocol, after submit, this is dead. - PROTO::lease.at(m_parent->m_deviceName)->destroyResource(this); + // per protcol, after submit, this is dead. + PROTO::lease->destroyResource(this); }); } bool CDRMLeaseRequestResource::good() { - return m_resource->resource(); + return resource->resource(); } SP CDRMLeaseConnectorResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CDRMLeaseConnectorResource*)(((CWpDrmLeaseConnectorV1*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } -CDRMLeaseConnectorResource::CDRMLeaseConnectorResource(WP parent_, SP resource_, PHLMONITOR monitor_) : - m_parent(parent_), m_monitor(monitor_), m_resource(resource_) { +CDRMLeaseConnectorResource::CDRMLeaseConnectorResource(SP resource_, PHLMONITOR monitor_) : monitor(monitor_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWpDrmLeaseConnectorV1* r) { - if (m_parent && PROTO::lease.contains(m_parent->m_deviceName)) - PROTO::lease.at(m_parent->m_deviceName)->destroyResource(this); - }); - m_resource->setDestroy([this](CWpDrmLeaseConnectorV1* r) { - if (m_parent && PROTO::lease.contains(m_parent->m_deviceName)) - PROTO::lease.at(m_parent->m_deviceName)->destroyResource(this); - }); + resource->setOnDestroy([this](CWpDrmLeaseConnectorV1* r) { PROTO::lease->destroyResource(this); }); + resource->setDestroy([this](CWpDrmLeaseConnectorV1* r) { PROTO::lease->destroyResource(this); }); - m_resource->setData(this); + resource->setData(this); - m_listeners.destroyMonitor = m_monitor->m_events.destroy.listen([this] { - m_resource->sendWithdrawn(); - m_dead = true; + listeners.destroyMonitor = monitor->events.destroy.registerListener([this](std::any d) { + resource->sendWithdrawn(); + dead = true; }); } bool CDRMLeaseConnectorResource::good() { - return m_resource->resource(); + return resource->resource(); } void CDRMLeaseConnectorResource::sendData() { - m_resource->sendName(m_monitor->m_name.c_str()); - m_resource->sendDescription(m_monitor->m_description.c_str()); + resource->sendName(monitor->szName.c_str()); + resource->sendDescription(monitor->szDescription.c_str()); - auto AQDRMOutput = sc(m_monitor->m_output.get()); - m_resource->sendConnectorId(AQDRMOutput->getConnectorID()); + auto AQDRMOutput = (Aquamarine::CDRMOutput*)monitor->output.get(); + resource->sendConnectorId(AQDRMOutput->getConnectorID()); - m_resource->sendDone(); + resource->sendDone(); } -CDRMLeaseDeviceResource::CDRMLeaseDeviceResource(std::string deviceName_, SP resource_) : m_deviceName(deviceName_), m_resource(resource_) { +CDRMLeaseDeviceResource::CDRMLeaseDeviceResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWpDrmLeaseDeviceV1* r) { - if (PROTO::lease.contains(m_deviceName)) - PROTO::lease.at(m_deviceName)->destroyResource(this); - }); - m_resource->setRelease([this](CWpDrmLeaseDeviceV1* r) { - if (PROTO::lease.contains(m_deviceName)) - PROTO::lease.at(m_deviceName)->destroyResource(this); - }); + resource->setOnDestroy([this](CWpDrmLeaseDeviceV1* r) { PROTO::lease->destroyResource(this); }); + resource->setRelease([this](CWpDrmLeaseDeviceV1* r) { PROTO::lease->destroyResource(this); }); - m_resource->setCreateLeaseRequest([this](CWpDrmLeaseDeviceV1* r, uint32_t id) { - auto RESOURCE = makeShared(m_self, makeShared(m_resource->client(), m_resource->version(), id)); + resource->setCreateLeaseRequest([this](CWpDrmLeaseDeviceV1* r, uint32_t id) { + auto RESOURCE = makeShared(makeShared(resource->client(), resource->version(), id)); if UNLIKELY (!RESOURCE) { - m_resource->noMemory(); + resource->noMemory(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - PROTO::lease.at(m_deviceName)->m_requests.emplace_back(RESOURCE); + PROTO::lease->m_vRequests.emplace_back(RESOURCE); - LOGM(Log::DEBUG, "New lease request {}", id); + LOGM(LOG, "New lease request {}", id); - RESOURCE->m_parent = m_self; + RESOURCE->parent = self; }); - CFileDescriptor fd{PROTO::lease.at(m_deviceName)->m_backend.get()->getNonMasterFD()}; + CFileDescriptor fd{((Aquamarine::CDRMBackend*)PROTO::lease->primaryDevice->backend.get())->getNonMasterFD()}; if (!fd.isValid()) { - LOGM(Log::ERR, "Failed to dup fd in lease"); + LOGM(ERR, "Failed to dup fd in lease"); return; } - LOGM(Log::DEBUG, "Sending DRMFD {} to new lease device", fd.get()); - m_resource->sendDrmFd(fd.get()); + LOGM(LOG, "Sending DRMFD {} to new lease device", fd.get()); + resource->sendDrmFd(fd.get()); - for (auto const& m : PROTO::lease.at(m_deviceName)->m_offeredOutputs) { + for (auto const& m : PROTO::lease->primaryDevice->offeredOutputs) { if (m) sendConnector(m.lock()); } - m_resource->sendDone(); + resource->sendDone(); } bool CDRMLeaseDeviceResource::good() { - return m_resource->resource(); + return resource->resource(); } void CDRMLeaseDeviceResource::sendConnector(PHLMONITOR monitor) { - if (std::ranges::find_if(m_connectorsSent, [monitor](const auto& e) { return e && !e->m_dead && e->m_monitor == monitor; }) != m_connectorsSent.end()) + if (std::find_if(connectorsSent.begin(), connectorsSent.end(), [monitor](const auto& e) { return e && !e->dead && e->monitor == monitor; }) != connectorsSent.end()) return; - auto RESOURCE = makeShared(m_self, makeShared(m_resource->client(), m_resource->version(), 0), monitor); + auto RESOURCE = makeShared(makeShared(resource->client(), resource->version(), 0), monitor); if UNLIKELY (!RESOURCE) { - m_resource->noMemory(); + resource->noMemory(); return; } - RESOURCE->m_parent = m_self; - RESOURCE->m_self = RESOURCE; + RESOURCE->parent = self; + RESOURCE->self = RESOURCE; - LOGM(Log::DEBUG, "Sending new connector {}", monitor->m_name); + LOGM(LOG, "Sending new connector {}", monitor->szName); - m_connectorsSent.emplace_back(RESOURCE); - PROTO::lease.at(m_deviceName)->m_connectors.emplace_back(RESOURCE); + connectorsSent.emplace_back(RESOURCE); + PROTO::lease->m_vConnectors.emplace_back(RESOURCE); - m_resource->sendConnector(RESOURCE->m_resource.get()); + resource->sendConnector(RESOURCE->resource.get()); RESOURCE->sendData(); } -CDRMLeaseProtocol::CDRMLeaseProtocol(const wl_interface* iface, const int& ver, const std::string& name, SP backend_) : - IWaylandProtocol(iface, ver, name) { - if (backend_->type() != Aquamarine::AQ_BACKEND_DRM) - return; +CDRMLeaseDevice::CDRMLeaseDevice(SP drmBackend) : backend(drmBackend) { + auto drm = (Aquamarine::CDRMBackend*)drmBackend.get(); - m_backend = sc(backend_.get())->self.lock(); - m_deviceName = m_backend->gpuName; - - CFileDescriptor fd{m_backend->getNonMasterFD()}; + CFileDescriptor fd{drm->getNonMasterFD()}; if (!fd.isValid()) { - LOGM(Log::ERR, "Failed to dup fd for drm node {}", m_deviceName); + LOGM(ERR, "Failed to dup fd for drm node {}", drm->gpuName); return; } - m_success = true; + success = true; + name = drm->gpuName; +} + +CDRMLeaseProtocol::CDRMLeaseProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) { + if (b->type() != Aquamarine::AQ_BACKEND_DRM) + continue; + + auto drm = ((Aquamarine::CDRMBackend*)b.get())->self.lock(); + + primaryDevice = makeShared(drm); + + if (primaryDevice->success) + break; + } + + if (!primaryDevice || !primaryDevice->success) + g_pEventLoopManager->doLater([]() { PROTO::lease.reset(); }); } void CDRMLeaseProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(m_deviceName, makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; } void CDRMLeaseProtocol::destroyResource(CDRMLeaseDeviceResource* resource) { - std::erase_if(m_managers, [resource](const auto& e) { return e.get() == resource; }); + std::erase_if(m_vManagers, [resource](const auto& e) { return e.get() == resource; }); } void CDRMLeaseProtocol::destroyResource(CDRMLeaseConnectorResource* resource) { - for (const auto& m : m_managers) { - std::erase_if(m->m_connectorsSent, [resource](const auto& e) { return e.expired() || e->m_dead || e.get() == resource; }); + for (const auto& m : m_vManagers) { + std::erase_if(m->connectorsSent, [resource](const auto& e) { return e.expired() || e->dead || e.get() == resource; }); } - std::erase_if(m_connectors, [resource](const auto& e) { return e.get() == resource; }); + std::erase_if(m_vConnectors, [resource](const auto& e) { return e.get() == resource; }); } void CDRMLeaseProtocol::destroyResource(CDRMLeaseRequestResource* resource) { - std::erase_if(m_requests, [resource](const auto& e) { return e.get() == resource; }); + std::erase_if(m_vRequests, [resource](const auto& e) { return e.get() == resource; }); } void CDRMLeaseProtocol::destroyResource(CDRMLeaseResource* resource) { - std::erase_if(m_leases, [resource](const auto& e) { return e.get() == resource; }); + std::erase_if(m_vLeases, [resource](const auto& e) { return e.get() == resource; }); } void CDRMLeaseProtocol::offer(PHLMONITOR monitor) { - std::erase_if(m_offeredOutputs, [](const auto& e) { return e.expired(); }); - if (std::ranges::find(m_offeredOutputs.begin(), m_offeredOutputs.end(), monitor) != m_offeredOutputs.end()) + std::erase_if(primaryDevice->offeredOutputs, [](const auto& e) { return e.expired(); }); + if (std::find(primaryDevice->offeredOutputs.begin(), primaryDevice->offeredOutputs.end(), monitor) != primaryDevice->offeredOutputs.end()) return; - if (monitor->m_output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM) + if (monitor->output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM) return; - if (monitor->m_output->getBackend() != m_backend) { - LOGM(Log::ERR, "Monitor {} cannot be leased: lease is for a different device", monitor->m_name); + if (monitor->output->getBackend() != primaryDevice->backend) { + LOGM(ERR, "Monitor {} cannot be leased: primaryDevice lease is for a different device", monitor->szName); return; } - m_offeredOutputs.emplace_back(monitor); + primaryDevice->offeredOutputs.emplace_back(monitor); - for (auto const& m : m_managers) { + for (auto const& m : m_vManagers) { m->sendConnector(monitor); - m->m_resource->sendDone(); + m->resource->sendDone(); } } - -std::string CDRMLeaseProtocol::getDeviceName() { - return m_deviceName; -} - -SP CDRMLeaseProtocol::getBackend() { - return m_backend; -} - -bool CDRMLeaseProtocol::good() { - return m_success; -} diff --git a/src/protocols/DRMLease.hpp b/src/protocols/DRMLease.hpp index 404fc29d..c7849149 100644 --- a/src/protocols/DRMLease.hpp +++ b/src/protocols/DRMLease.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include "WaylandProtocol.hpp" @@ -27,84 +26,92 @@ class CDRMLeaseResource { bool good(); - WP m_parent; - std::vector> m_requested; - SP m_lease; + WP parent; + std::vector> requested; + SP lease; + + int leaseFD = -1; struct { CHyprSignalListener destroyLease; - } m_listeners; + } listeners; private: - SP m_resource; + SP resource; }; class CDRMLeaseRequestResource { public: - CDRMLeaseRequestResource(WP parent_, SP resource_); + CDRMLeaseRequestResource(SP resource_); bool good(); - WP m_parent; - WP m_self; - std::vector> m_requested; + WP parent; + WP self; + std::vector> requested; private: - SP m_resource; + SP resource; }; class CDRMLeaseConnectorResource { public: - CDRMLeaseConnectorResource(WP parent_, SP resource_, PHLMONITOR monitor_); + CDRMLeaseConnectorResource(SP resource_, PHLMONITOR monitor_); static SP fromResource(wl_resource*); bool good(); void sendData(); - WP m_self; - WP m_parent; - PHLMONITORREF m_monitor; - bool m_dead = false; + WP self; + WP parent; + PHLMONITORREF monitor; + bool dead = false; private: - SP m_resource; + SP resource; struct { CHyprSignalListener destroyMonitor; - } m_listeners; + } listeners; friend class CDRMLeaseDeviceResource; }; class CDRMLeaseDeviceResource { public: - CDRMLeaseDeviceResource(std::string deviceName, SP resource_); + CDRMLeaseDeviceResource(SP resource_); bool good(); void sendConnector(PHLMONITOR monitor); - std::vector> m_connectorsSent; + std::vector> connectorsSent; - WP m_self; - std::string m_deviceName; + WP self; private: - SP m_resource; + SP resource; friend class CDRMLeaseProtocol; }; +class CDRMLeaseDevice { + public: + CDRMLeaseDevice(SP drmBackend); + + std::string name = ""; + bool success = false; + SP backend; + + std::vector offeredOutputs; +}; + class CDRMLeaseProtocol : public IWaylandProtocol { public: - CDRMLeaseProtocol(const wl_interface* iface, const int& ver, const std::string& name, SP backend); + CDRMLeaseProtocol(const wl_interface* iface, const int& ver, const std::string& name); - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - void offer(PHLMONITOR monitor); - - SP getBackend(); - std::string getDeviceName(); - bool good(); + void offer(PHLMONITOR monitor); private: void destroyResource(CDRMLeaseDeviceResource* resource); @@ -113,15 +120,12 @@ class CDRMLeaseProtocol : public IWaylandProtocol { void destroyResource(CDRMLeaseResource* resource); // - std::vector> m_managers; - std::vector> m_connectors; - std::vector> m_requests; - std::vector> m_leases; + std::vector> m_vManagers; + std::vector> m_vConnectors; + std::vector> m_vRequests; + std::vector> m_vLeases; - std::string m_deviceName = ""; - bool m_success = false; - SP m_backend; - std::vector m_offeredOutputs; + SP primaryDevice; friend class CDRMLeaseDeviceResource; friend class CDRMLeaseConnectorResource; @@ -130,5 +134,5 @@ class CDRMLeaseProtocol : public IWaylandProtocol { }; namespace PROTO { - inline std::unordered_map> lease; + inline UP lease; }; diff --git a/src/protocols/DRMSyncobj.cpp b/src/protocols/DRMSyncobj.cpp index 821796d0..5238d1fb 100644 --- a/src/protocols/DRMSyncobj.cpp +++ b/src/protocols/DRMSyncobj.cpp @@ -21,18 +21,18 @@ WP CDRMSyncPointState::timeline() { UP CDRMSyncPointState::createSyncRelease() { if (m_releaseTaken) - Log::logger->log(Log::ERR, "CDRMSyncPointState: creating a sync releaser on an already created SyncRelease"); + Debug::log(ERR, "CDRMSyncPointState: creating a sync releaser on an already created SyncRelease"); m_releaseTaken = true; return makeUnique(m_timeline, m_point); } -bool CDRMSyncPointState::addWaiter(std::function&& waiter) { +bool CDRMSyncPointState::addWaiter(const std::function& waiter) { m_acquireCommitted = true; - return m_timeline->addWaiter(std::move(waiter), m_point, 0u); + return m_timeline->addWaiter(waiter, m_point, 0u); } -bool CDRMSyncPointState::committed() { +bool CDRMSyncPointState::comitted() { return m_acquireCommitted; } @@ -45,97 +45,151 @@ void CDRMSyncPointState::signal() { } CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(UP&& resource_, SP surface_) : - m_surface(surface_), m_resource(std::move(resource_)) { + surface(surface_), resource(std::move(resource_)) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setOnDestroy([this](CWpLinuxDrmSyncobjSurfaceV1* r) { PROTO::sync->destroyResource(this); }); - m_resource->setDestroy([this](CWpLinuxDrmSyncobjSurfaceV1* r) { PROTO::sync->destroyResource(this); }); + resource->setOnDestroy([this](CWpLinuxDrmSyncobjSurfaceV1* r) { PROTO::sync->destroyResource(this); }); + resource->setDestroy([this](CWpLinuxDrmSyncobjSurfaceV1* r) { PROTO::sync->destroyResource(this); }); - m_resource->setSetAcquirePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) { - if (!m_surface) { - m_resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone"); + resource->setSetAcquirePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) { + if (!surface) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone"); return; } - auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_); - m_pendingAcquire = {timeline->m_timeline, (sc(hi) << 32) | sc(lo)}; + auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_); + pendingAcquire = {timeline->timeline, ((uint64_t)hi << 32) | (uint64_t)lo}; }); - m_resource->setSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) { - if (!m_surface) { - m_resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone"); + resource->setSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) { + if (!surface) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone"); return; } - auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_); - m_pendingRelease = {timeline->m_timeline, (sc(hi) << 32) | sc(lo)}; + auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_); + pendingRelease = {timeline->timeline, ((uint64_t)hi << 32) | (uint64_t)lo}; }); - m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit.listen([this](auto state) { - if (!state->updated.bits.buffer || !state->buffer) { - if (m_pendingAcquire.timeline() || m_pendingRelease.timeline()) { - m_resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer"); - state->rejected = true; - } + listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) { + if (!surface->pending.buffer && surface->pending.newBuffer && !surface->pending.texture) { + removeAllWaiters(); + surface->commitPendingState(surface->pending); + return; // null buffer attached. + } + + if (!surface->pending.buffer && !surface->pending.newBuffer && surface->current.buffer) { + surface->current.bufferDamage.clear(); + surface->current.damage.clear(); + surface->commitPendingState(surface->current); + return; // no new buffer, but we still have current around and a commit happend, commit current again. + } + + if (!surface->pending.buffer && !surface->pending.newBuffer && !surface->current.buffer) { + surface->commitPendingState(surface->pending); // no pending buffer, no current buffer. probably first commit return; } - if (!m_pendingAcquire.timeline()) { - m_resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing acquire timeline"); - state->rejected = true; - return; + if (pendingAcquire.timeline()) { + surface->pending.buffer->acquire = makeUnique(std::move(pendingAcquire)); + pendingAcquire = {}; } - if (!m_pendingRelease.timeline()) { - m_resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, "Missing release timeline"); - state->rejected = true; - return; + if (pendingRelease.timeline()) { + surface->pending.buffer->release = makeUnique(std::move(pendingRelease)); + pendingRelease = {}; } - if (m_pendingAcquire.timeline() == m_pendingRelease.timeline() && m_pendingAcquire.point() >= m_pendingRelease.point()) { - m_resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS, "Acquire and release points are on the same timeline, and acquire >= release"); - state->rejected = true; + if (protocolError()) return; - } - state->updated.bits.acquire = true; - state->acquire = m_pendingAcquire; - m_surface->m_stateQueue.lock(state, LOCK_REASON_FENCE); - m_pendingAcquire = {}; + const auto& state = pendingStates.emplace_back(makeShared(surface->pending)); + surface->pending.damage.clear(); + surface->pending.bufferDamage.clear(); + surface->pending.newBuffer = false; + surface->pending.buffer.reset(); - state->buffer->addReleasePoint(m_pendingRelease); - m_pendingRelease = {}; + state->buffer->buffer->syncReleaser = state->buffer->release->createSyncRelease(); + state->buffer->acquire->addWaiter([this, surf = surface, wp = CWeakPointer(*std::prev(pendingStates.end()))] { + if (!surf) + return; + + surf->commitPendingState(*wp.lock()); + std::erase(pendingStates, wp); + }); }); } +void CDRMSyncobjSurfaceResource::removeAllWaiters() { + for (auto& s : pendingStates) { + if (s && s->buffer && s->buffer->acquire) + s->buffer->acquire->timeline()->removeAllWaiters(); + } + + pendingStates.clear(); +} + +CDRMSyncobjSurfaceResource::~CDRMSyncobjSurfaceResource() { + removeAllWaiters(); +} + +bool CDRMSyncobjSurfaceResource::protocolError() { + if (!surface->pending.buffer) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer"); + surface->pending.rejected = true; + return true; + } + + if (!surface->pending.buffer->acquire || !surface->pending.buffer->acquire->timeline()) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing acquire timeline"); + surface->pending.rejected = true; + return true; + } + + if (!surface->pending.buffer->release || !surface->pending.buffer->release->timeline()) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT, "Missing release timeline"); + surface->pending.rejected = true; + return true; + } + + if (surface->pending.buffer->acquire->timeline() == surface->pending.buffer->release->timeline()) { + if (surface->pending.buffer->acquire->point() >= surface->pending.buffer->release->point()) { + resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_CONFLICTING_POINTS, "Acquire and release points are on the same timeline, and acquire >= release"); + surface->pending.rejected = true; + return true; + } + } + + return false; +} + bool CDRMSyncobjSurfaceResource::good() { - return m_resource->resource(); + return resource->resource(); } -CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(UP&& resource_, CFileDescriptor&& fd_) : - m_fd(std::move(fd_)), m_resource(std::move(resource_)) { +CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(UP&& resource_, CFileDescriptor&& fd_) : fd(std::move(fd_)), resource(std::move(resource_)) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setOnDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); }); - m_resource->setDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); }); + resource->setOnDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); }); + resource->setDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); }); - m_timeline = CSyncTimeline::create(PROTO::sync->m_drmFD, std::move(m_fd)); + timeline = CSyncTimeline::create(PROTO::sync->drmFD, std::move(fd)); - if (!m_timeline) { - m_resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, "Timeline failed importing"); + if (!timeline) { + resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, "Timeline failed importing"); return; } } WP CDRMSyncobjTimelineResource::fromResource(wl_resource* res) { - for (const auto& r : PROTO::sync->m_timelines) { - if (r && r->m_resource && r->m_resource->resource() == res) + for (const auto& r : PROTO::sync->m_vTimelines) { + if (r && r->resource && r->resource->resource() == res) return r; } @@ -143,94 +197,83 @@ WP CDRMSyncobjTimelineResource::fromResource(wl_res } bool CDRMSyncobjTimelineResource::good() { - return m_resource->resource(); + return resource->resource(); } -CDRMSyncobjManagerResource::CDRMSyncobjManagerResource(UP&& resource_) : m_resource(std::move(resource_)) { +CDRMSyncobjManagerResource::CDRMSyncobjManagerResource(UP&& resource_) : resource(std::move(resource_)) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWpLinuxDrmSyncobjManagerV1* r) { PROTO::sync->destroyResource(this); }); - m_resource->setDestroy([this](CWpLinuxDrmSyncobjManagerV1* r) { PROTO::sync->destroyResource(this); }); + resource->setOnDestroy([this](CWpLinuxDrmSyncobjManagerV1* r) { PROTO::sync->destroyResource(this); }); + resource->setDestroy([this](CWpLinuxDrmSyncobjManagerV1* r) { PROTO::sync->destroyResource(this); }); - m_resource->setGetSurface([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, wl_resource* surf) { + resource->setGetSurface([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, wl_resource* surf) { if UNLIKELY (!surf) { - m_resource->error(-1, "Invalid surface"); + resource->error(-1, "Invalid surface"); return; } auto SURF = CWLSurfaceResource::fromResource(surf); if UNLIKELY (!SURF) { - m_resource->error(-1, "Invalid surface (2)"); + resource->error(-1, "Invalid surface (2)"); return; } - if UNLIKELY (SURF->m_syncobj) { - m_resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_SURFACE_EXISTS, "Surface already has a syncobj attached"); + if UNLIKELY (SURF->syncobj) { + resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_SURFACE_EXISTS, "Surface already has a syncobj attached"); return; } - const auto& RESOURCE = PROTO::sync->m_surfaces.emplace_back( - makeUnique(makeUnique(m_resource->client(), m_resource->version(), id), SURF)); + const auto& RESOURCE = PROTO::sync->m_vSurfaces.emplace_back( + makeUnique(makeUnique(resource->client(), resource->version(), id), SURF)); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::sync->m_surfaces.pop_back(); + resource->noMemory(); + PROTO::sync->m_vSurfaces.pop_back(); return; } - SURF->m_syncobj = RESOURCE; + SURF->syncobj = RESOURCE; - LOGM(Log::DEBUG, "New linux_syncobj at {:x} for surface {:x}", (uintptr_t)RESOURCE.get(), (uintptr_t)SURF.get()); + LOGM(LOG, "New linux_syncobj at {:x} for surface {:x}", (uintptr_t)RESOURCE.get(), (uintptr_t)SURF.get()); }); - m_resource->setImportTimeline([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, int32_t fd) { - const auto& RESOURCE = PROTO::sync->m_timelines.emplace_back( - makeUnique(makeUnique(m_resource->client(), m_resource->version(), id), CFileDescriptor{fd})); + resource->setImportTimeline([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, int32_t fd) { + const auto& RESOURCE = PROTO::sync->m_vTimelines.emplace_back( + makeUnique(makeUnique(resource->client(), resource->version(), id), CFileDescriptor{fd})); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::sync->m_timelines.pop_back(); + resource->noMemory(); + PROTO::sync->m_vTimelines.pop_back(); return; } - LOGM(Log::DEBUG, "New linux_drm_timeline at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New linux_drm_timeline at {:x}", (uintptr_t)RESOURCE.get()); }); } bool CDRMSyncobjManagerResource::good() { - return m_resource->resource(); + return resource->resource(); } -CDRMSyncobjProtocol::CDRMSyncobjProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - if (g_pCompositor->m_drmRenderNode.syncObjSupport) - m_drmFD = g_pCompositor->m_drmRenderNode.fd; - else if (g_pCompositor->m_drm.syncobjSupport) - m_drmFD = g_pCompositor->m_drm.fd; - else { - LOGM(Log::ERR, "CDRMSyncobjProtocol: no nodes support explicit sync?"); - return; - } - - LOGM(Log::DEBUG, "CDRMSyncobjProtocol: using fd {}", m_drmFD); -} +CDRMSyncobjProtocol::CDRMSyncobjProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name), drmFD(g_pCompositor->m_iDRMFD) {} void CDRMSyncobjProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto& RESOURCE = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))); + const auto& RESOURCE = m_vManagers.emplace_back(makeUnique(makeUnique(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjManagerResource* resource) { - std::erase_if(m_managers, [resource](const auto& e) { return e.get() == resource; }); + std::erase_if(m_vManagers, [resource](const auto& e) { return e.get() == resource; }); } void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjTimelineResource* resource) { - std::erase_if(m_timelines, [resource](const auto& e) { return e.get() == resource; }); + std::erase_if(m_vTimelines, [resource](const auto& e) { return e.get() == resource; }); } void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjSurfaceResource* resource) { - std::erase_if(m_surfaces, [resource](const auto& e) { return e.get() == resource; }); + std::erase_if(m_vSurfaces, [resource](const auto& e) { return e.get() == resource; }); } diff --git a/src/protocols/DRMSyncobj.hpp b/src/protocols/DRMSyncobj.hpp index f91ace4c..d2259a34 100644 --- a/src/protocols/DRMSyncobj.hpp +++ b/src/protocols/DRMSyncobj.hpp @@ -5,11 +5,14 @@ #include "../helpers/sync/SyncReleaser.hpp" #include "linux-drm-syncobj-v1.hpp" #include "../helpers/signal/Signal.hpp" +#include "types/SurfaceState.hpp" #include +#include class CWLSurfaceResource; class CDRMSyncobjTimelineResource; class CSyncTimeline; +struct SSurfaceState; class CDRMSyncPointState { public: @@ -20,16 +23,11 @@ class CDRMSyncPointState { const uint64_t& point(); WP timeline(); Hyprutils::Memory::CUniquePointer createSyncRelease(); - bool addWaiter(std::function&& waiter); - bool committed(); + bool addWaiter(const std::function& waiter); + bool comitted(); Hyprutils::OS::CFileDescriptor exportAsFD(); void signal(); - // - operator bool() const { - return m_timeline; - } - private: SP m_timeline = {}; uint64_t m_point = 0; @@ -40,19 +38,23 @@ class CDRMSyncPointState { class CDRMSyncobjSurfaceResource { public: CDRMSyncobjSurfaceResource(UP&& resource_, SP surface_); + ~CDRMSyncobjSurfaceResource(); + bool protocolError(); bool good(); private: - WP m_surface; - UP m_resource; + void removeAllWaiters(); + WP surface; + UP resource; - CDRMSyncPointState m_pendingAcquire; - CDRMSyncPointState m_pendingRelease; + CDRMSyncPointState pendingAcquire; + CDRMSyncPointState pendingRelease; + std::vector> pendingStates; struct { - CHyprSignalListener surfaceStateCommit; - } m_listeners; + CHyprSignalListener surfacePrecommit; + } listeners; }; class CDRMSyncobjTimelineResource { @@ -63,11 +65,11 @@ class CDRMSyncobjTimelineResource { bool good(); - Hyprutils::OS::CFileDescriptor m_fd; - SP m_timeline; + Hyprutils::OS::CFileDescriptor fd; + SP timeline; private: - UP m_resource; + UP resource; }; class CDRMSyncobjManagerResource { @@ -78,7 +80,7 @@ class CDRMSyncobjManagerResource { bool good(); private: - UP m_resource; + UP resource; }; class CDRMSyncobjProtocol : public IWaylandProtocol { @@ -94,12 +96,12 @@ class CDRMSyncobjProtocol : public IWaylandProtocol { void destroyResource(CDRMSyncobjSurfaceResource* resource); // - std::vector> m_managers; - std::vector> m_timelines; - std::vector> m_surfaces; + std::vector> m_vManagers; + std::vector> m_vTimelines; + std::vector> m_vSurfaces; // - int m_drmFD = -1; + int drmFD = -1; friend class CDRMSyncobjManagerResource; friend class CDRMSyncobjTimelineResource; diff --git a/src/protocols/DataDeviceWlr.cpp b/src/protocols/DataDeviceWlr.cpp index d29106e0..3da5afd6 100644 --- a/src/protocols/DataDeviceWlr.cpp +++ b/src/protocols/DataDeviceWlr.cpp @@ -4,227 +4,227 @@ #include "core/Seat.hpp" using namespace Hyprutils::OS; -CWLRDataOffer::CWLRDataOffer(SP resource_, SP source_) : m_source(source_), m_resource(resource_) { +CWLRDataOffer::CWLRDataOffer(SP resource_, SP source_) : source(source_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); }); - m_resource->setReceive([this](CZwlrDataControlOfferV1* r, const char* mime, int32_t fd) { + resource->setReceive([this](CZwlrDataControlOfferV1* r, const char* mime, int32_t fd) { CFileDescriptor sendFd{fd}; - if (!m_source) { - LOGM(Log::WARN, "Possible bug: Receive on an offer w/o a source"); + if (!source) { + LOGM(WARN, "Possible bug: Receive on an offer w/o a source"); return; } - if (m_dead) { - LOGM(Log::WARN, "Possible bug: Receive on an offer that's dead"); + if (dead) { + LOGM(WARN, "Possible bug: Receive on an offer that's dead"); return; } - LOGM(Log::DEBUG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get()); + LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get()); - m_source->send(mime, std::move(sendFd)); + source->send(mime, std::move(sendFd)); }); } bool CWLRDataOffer::good() { - return m_resource->resource(); + return resource->resource(); } void CWLRDataOffer::sendData() { - if UNLIKELY (!m_source) + if UNLIKELY (!source) return; - for (auto const& m : m_source->mimes()) { - m_resource->sendOffer(m.c_str()); + for (auto const& m : source->mimes()) { + resource->sendOffer(m.c_str()); } } -CWLRDataSource::CWLRDataSource(SP resource_, SP device_) : m_device(device_), m_resource(resource_) { +CWLRDataSource::CWLRDataSource(SP resource_, SP device_) : device(device_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setDestroy([this](CZwlrDataControlSourceV1* r) { - m_events.destroy.emit(); + resource->setDestroy([this](CZwlrDataControlSourceV1* r) { + events.destroy.emit(); PROTO::dataWlr->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrDataControlSourceV1* r) { - m_events.destroy.emit(); + resource->setOnDestroy([this](CZwlrDataControlSourceV1* r) { + events.destroy.emit(); PROTO::dataWlr->destroyResource(this); }); - m_resource->setOffer([this](CZwlrDataControlSourceV1* r, const char* mime) { m_mimeTypes.emplace_back(mime); }); + resource->setOffer([this](CZwlrDataControlSourceV1* r, const char* mime) { mimeTypes.emplace_back(mime); }); } CWLRDataSource::~CWLRDataSource() { - m_events.destroy.emit(); + events.destroy.emit(); } SP CWLRDataSource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CWLRDataSource*)(((CZwlrDataControlSourceV1*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } bool CWLRDataSource::good() { - return m_resource->resource(); + return resource->resource(); } std::vector CWLRDataSource::mimes() { - return m_mimeTypes; + return mimeTypes; } void CWLRDataSource::send(const std::string& mime, CFileDescriptor fd) { - if (std::ranges::find(m_mimeTypes, mime) == m_mimeTypes.end()) { - LOGM(Log::ERR, "Compositor/App bug: CWLRDataSource::sendAskSend with non-existent mime"); + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CWLRDataSource::sendAskSend with non-existent mime"); return; } - m_resource->sendSend(mime.c_str(), fd.get()); + resource->sendSend(mime.c_str(), fd.get()); } void CWLRDataSource::accepted(const std::string& mime) { - if (std::ranges::find(m_mimeTypes, mime) == m_mimeTypes.end()) - LOGM(Log::ERR, "Compositor/App bug: CWLRDataSource::sendAccepted with non-existent mime"); + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) + LOGM(ERR, "Compositor/App bug: CWLRDataSource::sendAccepted with non-existent mime"); // wlr has no accepted } void CWLRDataSource::cancelled() { - m_resource->sendCancelled(); + resource->sendCancelled(); } void CWLRDataSource::error(uint32_t code, const std::string& msg) { - m_resource->error(code, msg); + resource->error(code, msg); } -CWLRDataDevice::CWLRDataDevice(SP resource_) : m_resource(resource_) { +CWLRDataDevice::CWLRDataDevice(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_client = m_resource->client(); + pClient = resource->client(); - m_resource->setDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); }); - m_resource->setSetSelection([](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) { + resource->setSetSelection([](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) { auto source = sourceR ? CWLRDataSource::fromResource(sourceR) : CSharedPointer{}; if (!source) { - LOGM(Log::DEBUG, "wlr reset selection received"); + LOGM(LOG, "wlr reset selection received"); g_pSeatManager->setCurrentSelection(nullptr); return; } if (source && source->used()) - LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); source->markUsed(); - LOGM(Log::DEBUG, "wlr manager requests selection to {:x}", (uintptr_t)source.get()); + LOGM(LOG, "wlr manager requests selection to {:x}", (uintptr_t)source.get()); g_pSeatManager->setCurrentSelection(source); }); - m_resource->setSetPrimarySelection([](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) { + resource->setSetPrimarySelection([](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) { auto source = sourceR ? CWLRDataSource::fromResource(sourceR) : CSharedPointer{}; if (!source) { - LOGM(Log::DEBUG, "wlr reset primary selection received"); + LOGM(LOG, "wlr reset primary selection received"); g_pSeatManager->setCurrentPrimarySelection(nullptr); return; } if (source && source->used()) - LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); source->markUsed(); - LOGM(Log::DEBUG, "wlr manager requests primary selection to {:x}", (uintptr_t)source.get()); + LOGM(LOG, "wlr manager requests primary selection to {:x}", (uintptr_t)source.get()); g_pSeatManager->setCurrentPrimarySelection(source); }); } bool CWLRDataDevice::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CWLRDataDevice::client() { - return m_client; + return pClient; } void CWLRDataDevice::sendInitialSelections() { - PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->m_selection.currentSelection.lock(), false); - PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->m_selection.currentPrimarySelection.lock(), true); + PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->selection.currentSelection.lock(), false); + PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->selection.currentPrimarySelection.lock(), true); } void CWLRDataDevice::sendDataOffer(SP offer) { - m_resource->sendDataOffer(offer->m_resource.get()); + resource->sendDataOffer(offer->resource.get()); } void CWLRDataDevice::sendSelection(SP selection) { - m_resource->sendSelection(selection->m_resource.get()); + resource->sendSelection(selection->resource.get()); } void CWLRDataDevice::sendPrimarySelection(SP selection) { - m_resource->sendPrimarySelection(selection->m_resource.get()); + resource->sendPrimarySelection(selection->resource.get()); } -CWLRDataControlManagerResource::CWLRDataControlManagerResource(SP resource_) : m_resource(resource_) { +CWLRDataControlManagerResource::CWLRDataControlManagerResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); }); - m_resource->setGetDataDevice([this](CZwlrDataControlManagerV1* r, uint32_t id, wl_resource* seat) { - const auto RESOURCE = PROTO::dataWlr->m_devices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + resource->setGetDataDevice([this](CZwlrDataControlManagerV1* r, uint32_t id, wl_resource* seat) { + const auto RESOURCE = PROTO::dataWlr->m_vDevices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::dataWlr->m_devices.pop_back(); + PROTO::dataWlr->m_vDevices.pop_back(); return; } RESOURCE->self = RESOURCE; - m_device = RESOURCE; + device = RESOURCE; - for (auto const& s : m_sources) { + for (auto const& s : sources) { if (!s) continue; - s->m_device = RESOURCE; + s->device = RESOURCE; } RESOURCE->sendInitialSelections(); - LOGM(Log::DEBUG, "New wlr data device bound at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New wlr data device bound at {:x}", (uintptr_t)RESOURCE.get()); }); - m_resource->setCreateDataSource([this](CZwlrDataControlManagerV1* r, uint32_t id) { - std::erase_if(m_sources, [](const auto& e) { return e.expired(); }); + resource->setCreateDataSource([this](CZwlrDataControlManagerV1* r, uint32_t id) { + std::erase_if(sources, [](const auto& e) { return e.expired(); }); const auto RESOURCE = - PROTO::dataWlr->m_sources.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_device.lock())); + PROTO::dataWlr->m_vSources.emplace_back(makeShared(makeShared(r->client(), r->version(), id), device.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::dataWlr->m_sources.pop_back(); + PROTO::dataWlr->m_vSources.pop_back(); return; } - if (!m_device) - LOGM(Log::WARN, "New data source before a device was created"); + if (!device) + LOGM(WARN, "New data source before a device was created"); - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - m_sources.emplace_back(RESOURCE); + sources.emplace_back(RESOURCE); - LOGM(Log::DEBUG, "New wlr data source bound at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New wlr data source bound at {:x}", (uintptr_t)RESOURCE.get()); }); } bool CWLRDataControlManagerResource::good() { - return m_resource->resource(); + return resource->resource(); } CDataDeviceWLRProtocol::CDataDeviceWLRProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -232,53 +232,53 @@ CDataDeviceWLRProtocol::CDataDeviceWLRProtocol(const wl_interface* iface, const } void CDataDeviceWLRProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } - LOGM(Log::DEBUG, "New wlr_data_control_manager at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New wlr_data_control_manager at {:x}", (uintptr_t)RESOURCE.get()); } void CDataDeviceWLRProtocol::destroyResource(CWLRDataControlManagerResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CDataDeviceWLRProtocol::destroyResource(CWLRDataSource* resource) { - std::erase_if(m_sources, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; }); } void CDataDeviceWLRProtocol::destroyResource(CWLRDataDevice* resource) { - std::erase_if(m_devices, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; }); } void CDataDeviceWLRProtocol::destroyResource(CWLRDataOffer* resource) { - std::erase_if(m_offers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; }); } void CDataDeviceWLRProtocol::sendSelectionToDevice(SP dev, SP sel, bool primary) { if (!sel) { if (primary) - dev->m_resource->sendPrimarySelectionRaw(nullptr); + dev->resource->sendPrimarySelectionRaw(nullptr); else - dev->m_resource->sendSelectionRaw(nullptr); + dev->resource->sendSelectionRaw(nullptr); return; } - const auto OFFER = m_offers.emplace_back(makeShared(makeShared(dev->m_resource->client(), dev->m_resource->version(), 0), sel)); + const auto OFFER = m_vOffers.emplace_back(makeShared(makeShared(dev->resource->client(), dev->resource->version(), 0), sel)); if (!OFFER->good()) { - dev->m_resource->noMemory(); - m_offers.pop_back(); + dev->resource->noMemory(); + m_vOffers.pop_back(); return; } - OFFER->m_primary = primary; + OFFER->primary = primary; - LOGM(Log::DEBUG, "New {}offer {:x} for data source {:x}", primary ? "primary " : " ", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); + LOGM(LOG, "New {}offer {:x} for data source {:x}", primary ? "primary " : " ", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); dev->sendDataOffer(OFFER); OFFER->sendData(); @@ -289,34 +289,34 @@ void CDataDeviceWLRProtocol::sendSelectionToDevice(SP dev, SP source, bool primary) { - for (auto const& o : m_offers) { - if (o->m_source && o->m_source->hasDnd()) + for (auto const& o : m_vOffers) { + if (o->source && o->source->hasDnd()) continue; - if (o->m_primary != primary) + if (o->primary != primary) continue; - o->m_dead = true; + o->dead = true; } if (!source) { - LOGM(Log::DEBUG, "resetting {}selection", primary ? "primary " : " "); + LOGM(LOG, "resetting {}selection", primary ? "primary " : " "); - for (auto const& d : m_devices) { + for (auto const& d : m_vDevices) { sendSelectionToDevice(d, nullptr, primary); } return; } - LOGM(Log::DEBUG, "New {}selection for data source {:x}", primary ? "primary" : "", (uintptr_t)source.get()); + LOGM(LOG, "New {}selection for data source {:x}", primary ? "primary" : "", (uintptr_t)source.get()); - for (auto const& d : m_devices) { + for (auto const& d : m_vDevices) { sendSelectionToDevice(d, source, primary); } } SP CDataDeviceWLRProtocol::dataDeviceForClient(wl_client* c) { - auto it = std::ranges::find_if(m_devices, [c](const auto& e) { return e->client() == c; }); - if (it == m_devices.end()) + auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; }); + if (it == m_vDevices.end()) return nullptr; return *it; } diff --git a/src/protocols/DataDeviceWlr.hpp b/src/protocols/DataDeviceWlr.hpp index 00f704e9..7f14b320 100644 --- a/src/protocols/DataDeviceWlr.hpp +++ b/src/protocols/DataDeviceWlr.hpp @@ -19,13 +19,13 @@ class CWLRDataOffer { bool good(); void sendData(); - bool m_dead = false; - bool m_primary = false; + bool dead = false; + bool primary = false; - WP m_source; + WP source; private: - SP m_resource; + SP resource; friend class CWLRDataDevice; }; @@ -44,12 +44,12 @@ class CWLRDataSource : public IDataSource { virtual void cancelled(); virtual void error(uint32_t code, const std::string& msg); - std::vector m_mimeTypes; - WP m_self; - WP m_device; + std::vector mimeTypes; + WP self; + WP device; private: - SP m_resource; + SP resource; }; class CWLRDataDevice { @@ -67,8 +67,8 @@ class CWLRDataDevice { WP self; private: - SP m_resource; - wl_client* m_client = nullptr; + SP resource; + wl_client* pClient = nullptr; friend class CDataDeviceWLRProtocol; }; @@ -79,11 +79,11 @@ class CWLRDataControlManagerResource { bool good(); - WP m_device; - std::vector> m_sources; + WP device; + std::vector> sources; private: - SP m_resource; + SP resource; }; class CDataDeviceWLRProtocol : public IWaylandProtocol { @@ -99,10 +99,10 @@ class CDataDeviceWLRProtocol : public IWaylandProtocol { void destroyResource(CWLRDataOffer* resource); // - std::vector> m_managers; - std::vector> m_sources; - std::vector> m_devices; - std::vector> m_offers; + std::vector> m_vManagers; + std::vector> m_vSources; + std::vector> m_vDevices; + std::vector> m_vOffers; // void setSelection(SP source, bool primary); diff --git a/src/protocols/ExtDataDevice.cpp b/src/protocols/ExtDataDevice.cpp deleted file mode 100644 index 6ab83ab4..00000000 --- a/src/protocols/ExtDataDevice.cpp +++ /dev/null @@ -1,322 +0,0 @@ -#include "ExtDataDevice.hpp" -#include -#include "../managers/SeatManager.hpp" -#include "core/Seat.hpp" -using namespace Hyprutils::OS; - -CExtDataOffer::CExtDataOffer(SP resource_, SP source_) : m_source(source_), m_resource(resource_) { - if UNLIKELY (!good()) - return; - - m_resource->setDestroy([this](CExtDataControlOfferV1* r) { PROTO::extDataDevice->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtDataControlOfferV1* r) { PROTO::extDataDevice->destroyResource(this); }); - - m_resource->setReceive([this](CExtDataControlOfferV1* r, const char* mime, int32_t fd) { - CFileDescriptor sendFd{fd}; - if (!m_source) { - LOGM(Log::WARN, "Possible bug: Receive on an offer w/o a source"); - return; - } - - if (m_dead) { - LOGM(Log::WARN, "Possible bug: Receive on an offer that's dead"); - return; - } - - LOGM(Log::DEBUG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get()); - - m_source->send(mime, std::move(sendFd)); - }); -} - -bool CExtDataOffer::good() { - return m_resource->resource(); -} - -void CExtDataOffer::sendData() { - if UNLIKELY (!m_source) - return; - - for (auto const& m : m_source->mimes()) { - m_resource->sendOffer(m.c_str()); - } -} - -CExtDataSource::CExtDataSource(SP resource_, SP device_) : m_device(device_), m_resource(resource_) { - if UNLIKELY (!good()) - return; - - m_resource->setData(this); - - m_resource->setDestroy([this](CExtDataControlSourceV1* r) { - m_events.destroy.emit(); - PROTO::extDataDevice->destroyResource(this); - }); - m_resource->setOnDestroy([this](CExtDataControlSourceV1* r) { - m_events.destroy.emit(); - PROTO::extDataDevice->destroyResource(this); - }); - - m_resource->setOffer([this](CExtDataControlSourceV1* r, const char* mime) { m_mimeTypes.emplace_back(mime); }); -} - -CExtDataSource::~CExtDataSource() { - m_events.destroy.emit(); -} - -SP CExtDataSource::fromResource(wl_resource* res) { - auto data = (CExtDataSource*)(((CExtDataControlSourceV1*)wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; -} - -bool CExtDataSource::good() { - return m_resource->resource(); -} - -std::vector CExtDataSource::mimes() { - return m_mimeTypes; -} - -void CExtDataSource::send(const std::string& mime, CFileDescriptor fd) { - if (std::ranges::find(m_mimeTypes, mime) == m_mimeTypes.end()) { - LOGM(Log::ERR, "Compositor/App bug: CExtDataSource::sendAskSend with non-existent mime"); - return; - } - - m_resource->sendSend(mime.c_str(), fd.get()); -} - -void CExtDataSource::accepted(const std::string& mime) { - if (std::ranges::find(m_mimeTypes, mime) == m_mimeTypes.end()) - LOGM(Log::ERR, "Compositor/App bug: CExtDataSource::sendAccepted with non-existent mime"); - - // ext has no accepted -} - -void CExtDataSource::cancelled() { - m_resource->sendCancelled(); -} - -void CExtDataSource::error(uint32_t code, const std::string& msg) { - m_resource->error(code, msg); -} - -CExtDataDevice::CExtDataDevice(SP resource_) : m_resource(resource_) { - if UNLIKELY (!good()) - return; - - m_client = m_resource->client(); - - m_resource->setDestroy([this](CExtDataControlDeviceV1* r) { PROTO::extDataDevice->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtDataControlDeviceV1* r) { PROTO::extDataDevice->destroyResource(this); }); - - m_resource->setSetSelection([](CExtDataControlDeviceV1* r, wl_resource* sourceR) { - auto source = sourceR ? CExtDataSource::fromResource(sourceR) : CSharedPointer{}; - if (!source) { - LOGM(Log::DEBUG, "ext reset selection received"); - g_pSeatManager->setCurrentSelection(nullptr); - return; - } - - if (source && source->used()) - LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); - - source->markUsed(); - - LOGM(Log::DEBUG, "ext manager requests selection to {:x}", (uintptr_t)source.get()); - g_pSeatManager->setCurrentSelection(source); - }); - - m_resource->setSetPrimarySelection([](CExtDataControlDeviceV1* r, wl_resource* sourceR) { - auto source = sourceR ? CExtDataSource::fromResource(sourceR) : CSharedPointer{}; - if (!source) { - LOGM(Log::DEBUG, "ext reset primary selection received"); - g_pSeatManager->setCurrentPrimarySelection(nullptr); - return; - } - - if (source && source->used()) - LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); - - source->markUsed(); - - LOGM(Log::DEBUG, "ext manager requests primary selection to {:x}", (uintptr_t)source.get()); - g_pSeatManager->setCurrentPrimarySelection(source); - }); -} - -bool CExtDataDevice::good() { - return m_resource->resource(); -} - -wl_client* CExtDataDevice::client() { - return m_client; -} - -void CExtDataDevice::sendInitialSelections() { - PROTO::extDataDevice->sendSelectionToDevice(self.lock(), g_pSeatManager->m_selection.currentSelection.lock(), false); - PROTO::extDataDevice->sendSelectionToDevice(self.lock(), g_pSeatManager->m_selection.currentPrimarySelection.lock(), true); -} - -void CExtDataDevice::sendDataOffer(SP offer) { - m_resource->sendDataOffer(offer->m_resource.get()); -} - -void CExtDataDevice::sendSelection(SP selection) { - m_resource->sendSelection(selection->m_resource.get()); -} - -void CExtDataDevice::sendPrimarySelection(SP selection) { - m_resource->sendPrimarySelection(selection->m_resource.get()); -} - -CExtDataControlManagerResource::CExtDataControlManagerResource(SP resource_) : m_resource(resource_) { - if UNLIKELY (!good()) - return; - - m_resource->setDestroy([this](CExtDataControlManagerV1* r) { PROTO::extDataDevice->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtDataControlManagerV1* r) { PROTO::extDataDevice->destroyResource(this); }); - - m_resource->setGetDataDevice([this](CExtDataControlManagerV1* r, uint32_t id, wl_resource* seat) { - const auto RESOURCE = PROTO::extDataDevice->m_devices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); - - if UNLIKELY (!RESOURCE->good()) { - r->noMemory(); - PROTO::extDataDevice->m_devices.pop_back(); - return; - } - - RESOURCE->self = RESOURCE; - m_device = RESOURCE; - - for (auto const& s : m_sources) { - if (!s) - continue; - s->m_device = RESOURCE; - } - - RESOURCE->sendInitialSelections(); - - LOGM(Log::DEBUG, "New ext data device bound at {:x}", (uintptr_t)RESOURCE.get()); - }); - - m_resource->setCreateDataSource([this](CExtDataControlManagerV1* r, uint32_t id) { - std::erase_if(m_sources, [](const auto& e) { return e.expired(); }); - - const auto RESOURCE = - PROTO::extDataDevice->m_sources.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_device.lock())); - - if UNLIKELY (!RESOURCE->good()) { - r->noMemory(); - PROTO::extDataDevice->m_sources.pop_back(); - return; - } - - if (!m_device) - LOGM(Log::WARN, "New data source before a device was created"); - - RESOURCE->m_self = RESOURCE; - - m_sources.emplace_back(RESOURCE); - - LOGM(Log::DEBUG, "New ext data source bound at {:x}", (uintptr_t)RESOURCE.get()); - }); -} - -bool CExtDataControlManagerResource::good() { - return m_resource->resource(); -} - -CExtDataDeviceProtocol::CExtDataDeviceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; -} - -void CExtDataDeviceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); - - if UNLIKELY (!RESOURCE->good()) { - wl_client_post_no_memory(client); - m_managers.pop_back(); - return; - } - - LOGM(Log::DEBUG, "New ext_data_control_manager at {:x}", (uintptr_t)RESOURCE.get()); -} - -void CExtDataDeviceProtocol::destroyResource(CExtDataControlManagerResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); -} - -void CExtDataDeviceProtocol::destroyResource(CExtDataSource* resource) { - std::erase_if(m_sources, [&](const auto& other) { return other.get() == resource; }); -} - -void CExtDataDeviceProtocol::destroyResource(CExtDataDevice* resource) { - std::erase_if(m_devices, [&](const auto& other) { return other.get() == resource; }); -} - -void CExtDataDeviceProtocol::destroyResource(CExtDataOffer* resource) { - std::erase_if(m_offers, [&](const auto& other) { return other.get() == resource; }); -} - -void CExtDataDeviceProtocol::sendSelectionToDevice(SP dev, SP sel, bool primary) { - if (!sel) { - if (primary) - dev->m_resource->sendPrimarySelectionRaw(nullptr); - else - dev->m_resource->sendSelectionRaw(nullptr); - return; - } - - const auto OFFER = m_offers.emplace_back(makeShared(makeShared(dev->m_resource->client(), dev->m_resource->version(), 0), sel)); - - if (!OFFER->good()) { - dev->m_resource->noMemory(); - m_offers.pop_back(); - return; - } - - OFFER->m_primary = primary; - - LOGM(Log::DEBUG, "New {}offer {:x} for data source {:x}", primary ? "primary " : " ", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); - - dev->sendDataOffer(OFFER); - OFFER->sendData(); - if (primary) - dev->sendPrimarySelection(OFFER); - else - dev->sendSelection(OFFER); -} - -void CExtDataDeviceProtocol::setSelection(SP source, bool primary) { - for (auto const& o : m_offers) { - if (o->m_source && o->m_source->hasDnd()) - continue; - if (o->m_primary != primary) - continue; - o->m_dead = true; - } - - if (!source) { - LOGM(Log::DEBUG, "resetting {}selection", primary ? "primary " : " "); - - for (auto const& d : m_devices) { - sendSelectionToDevice(d, nullptr, primary); - } - - return; - } - - LOGM(Log::DEBUG, "New {}selection for data source {:x}", primary ? "primary" : "", (uintptr_t)source.get()); - - for (auto const& d : m_devices) { - sendSelectionToDevice(d, source, primary); - } -} - -SP CExtDataDeviceProtocol::dataDeviceForClient(wl_client* c) { - auto it = std::ranges::find_if(m_devices, [c](const auto& e) { return e->client() == c; }); - if (it == m_devices.end()) - return nullptr; - return *it; -} diff --git a/src/protocols/ExtDataDevice.hpp b/src/protocols/ExtDataDevice.hpp deleted file mode 100644 index 462090f3..00000000 --- a/src/protocols/ExtDataDevice.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include -#include -#include "WaylandProtocol.hpp" -#include "ext-data-control-v1.hpp" -#include "types/DataDevice.hpp" -#include - -class CExtDataControlManagerResource; -class CExtDataSource; -class CExtDataDevice; -class CExtDataOffer; - -class CExtDataOffer { - public: - CExtDataOffer(SP resource_, SP source); - - bool good(); - void sendData(); - - bool m_dead = false; - bool m_primary = false; - - WP m_source; - - private: - SP m_resource; - - friend class CExtDataDevice; -}; - -class CExtDataSource : public IDataSource { - public: - CExtDataSource(SP resource_, SP device_); - ~CExtDataSource(); - static SP fromResource(wl_resource*); - - bool good(); - - virtual std::vector mimes(); - virtual void send(const std::string& mime, Hyprutils::OS::CFileDescriptor fd); - virtual void accepted(const std::string& mime); - virtual void cancelled(); - virtual void error(uint32_t code, const std::string& msg); - - std::vector m_mimeTypes; - WP m_self; - WP m_device; - - private: - SP m_resource; -}; - -class CExtDataDevice { - public: - CExtDataDevice(SP resource_); - - bool good(); - wl_client* client(); - void sendInitialSelections(); - - void sendDataOffer(SP offer); - void sendSelection(SP selection); - void sendPrimarySelection(SP selection); - - WP self; - - private: - SP m_resource; - wl_client* m_client = nullptr; - - friend class CExtDataDeviceProtocol; -}; - -class CExtDataControlManagerResource { - public: - CExtDataControlManagerResource(SP resource_); - - bool good(); - - WP m_device; - std::vector> m_sources; - - private: - SP m_resource; -}; - -class CExtDataDeviceProtocol : public IWaylandProtocol { - public: - CExtDataDeviceProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - private: - void destroyResource(CExtDataControlManagerResource* resource); - void destroyResource(CExtDataSource* resource); - void destroyResource(CExtDataDevice* resource); - void destroyResource(CExtDataOffer* resource); - - // - std::vector> m_managers; - std::vector> m_sources; - std::vector> m_devices; - std::vector> m_offers; - - // - void setSelection(SP source, bool primary); - void sendSelectionToDevice(SP dev, SP sel, bool primary); - - // - SP dataDeviceForClient(wl_client*); - - friend class CSeatManager; - friend class CExtDataControlManagerResource; - friend class CExtDataSource; - friend class CExtDataDevice; - friend class CExtDataOffer; -}; - -namespace PROTO { - inline UP extDataDevice; -}; diff --git a/src/protocols/ExtWorkspace.cpp b/src/protocols/ExtWorkspace.cpp deleted file mode 100644 index 4fa3152d..00000000 --- a/src/protocols/ExtWorkspace.cpp +++ /dev/null @@ -1,333 +0,0 @@ -#include "ExtWorkspace.hpp" -#include "../Compositor.hpp" -#include "../managers/eventLoop/EventLoopManager.hpp" -#include "../event/EventBus.hpp" -#include -#include -#include "core/Output.hpp" - -CExtWorkspaceGroupResource::CExtWorkspaceGroupResource(WP manager, UP resource, PHLMONITORREF monitor) : - m_monitor(std::move(monitor)), m_manager(std::move(manager)), m_resource(std::move(resource)) { - if (!good()) - return; - - m_resource->setData(this); - m_manager->m_resource->sendWorkspaceGroup(m_resource.get()); - - m_listeners.destroyed = m_monitor->m_events.destroy.listen([this] { m_resource->sendRemoved(); }); - - m_resource->setOnDestroy([this](auto) { PROTO::extWorkspace->destroyGroup(m_self); }); - m_resource->setDestroy([this](auto) { PROTO::extWorkspace->destroyGroup(m_self); }); - - m_resource->sendCapabilities(sc(0)); - - if (!PROTO::outputs.contains(m_monitor->m_name)) - return; - - const auto& output = PROTO::outputs.at(m_monitor->m_name); - - if (auto resources = output->outputResourcesFrom(m_resource->client()); !resources.empty()) { - for (const auto& r : resources) { - m_resource->sendOutputEnter(r->getResource()->resource()); - } - } - - m_listeners.outputBound = output->m_events.outputBound.listen([this](const SP& output) { - if (output->client() == m_resource->client()) - m_resource->sendOutputEnter(output->getResource()->resource()); - }); -} - -bool CExtWorkspaceGroupResource::good() const { - return m_resource; -} - -WP CExtWorkspaceGroupResource::fromResource(wl_resource* resource) { - auto handle = sc(wl_resource_get_user_data(resource))->data(); - auto data = sc(handle); - return data ? data->m_self : WP(); -} - -void CExtWorkspaceGroupResource::sendToWorkspaces() { - m_manager->sendGroupToWorkspaces(m_self); - m_manager->scheduleDone(); -} - -void CExtWorkspaceGroupResource::workspaceEnter(const WP& handle) { - m_resource->sendWorkspaceEnter(handle.get()); -} -void CExtWorkspaceGroupResource::workspaceLeave(const WP& handle) { - m_resource->sendWorkspaceLeave(handle.get()); -} - -CExtWorkspaceResource::CExtWorkspaceResource(WP manager, UP resource, PHLWORKSPACEREF workspace) : - m_manager(std::move(manager)), m_resource(std::move(resource)), m_workspace(std::move(workspace)) { - if (!good()) - return; - - m_resource->setData(this); - m_manager->m_resource->sendWorkspace(m_resource.get()); - - m_listeners.destroyed = m_workspace->m_events.destroy.listen([this] { - m_resource->sendRemoved(); - - if (m_manager) - m_manager->scheduleDone(); - }); - - m_listeners.activeChanged = m_workspace->m_events.activeChanged.listen([this] { - sendState(); - sendCapabilities(); - }); - - m_listeners.monitorChanged = m_workspace->m_events.monitorChanged.listen([this] { this->sendGroup(); }); - - m_listeners.renamed = m_workspace->m_events.renamed.listen([this] { - m_resource->sendName(m_workspace->m_name.c_str()); - - if (m_manager) - m_manager->scheduleDone(); - }); - - m_resource->setOnDestroy([this](auto) { PROTO::extWorkspace->destroyWorkspace(m_self); }); - m_resource->setDestroy([this](auto) { PROTO::extWorkspace->destroyWorkspace(m_self); }); - - m_resource->setActivate([this](void*) { m_pendingState.activate = true; }); - m_resource->setDeactivate([this](void*) { m_pendingState.deactivate = true; }); - - m_resource->setAssign([this](void*, wl_resource* groupResource) { - auto group = CExtWorkspaceGroupResource::fromResource(groupResource); - - if (group) - m_pendingState.targetMonitor = group->m_monitor; - }); - - m_resource->sendName(m_workspace->m_name.c_str()); - - wl_array coordinates; - wl_array_init(&coordinates); - - auto id = m_workspace->m_id; - if (id < 0 && !m_workspace->m_name.empty()) - id += UINT32_MAX - 1337; - - if (id > 0) - *sc(wl_array_add(&coordinates, sizeof(uint32_t))) = id; - - m_resource->sendCoordinates(&coordinates); - wl_array_release(&coordinates); - - sendState(); - sendCapabilities(); - sendGroup(); - - m_manager->scheduleDone(); -} - -bool CExtWorkspaceResource::good() const { - return m_resource; -} - -bool CExtWorkspaceResource::isActive() const { - if (!m_workspace) - return false; - - auto const& monitor = m_workspace->m_monitor; - auto const& cmpWorkspace = m_workspace->m_isSpecialWorkspace ? monitor->m_activeSpecialWorkspace : monitor->m_activeWorkspace; - return m_workspace == cmpWorkspace; -} - -void CExtWorkspaceResource::sendState() { - uint32_t state = 0; - - if (isActive()) - state |= EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE; - - if (m_workspace->hasUrgentWindow()) - state |= EXT_WORKSPACE_HANDLE_V1_STATE_URGENT; - - if (m_workspace->m_isSpecialWorkspace) - state |= EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN; - - m_resource->sendState(sc(state)); - - if (m_manager) - m_manager->scheduleDone(); -} - -void CExtWorkspaceResource::sendCapabilities() { - uint32_t capabilities = EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN; - auto active = isActive(); - - if (!active) - capabilities |= EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE; - - if (active && m_workspace->m_isSpecialWorkspace) - capabilities |= EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE; - - m_resource->sendCapabilities(sc(capabilities)); - - if (m_manager) - m_manager->scheduleDone(); -} - -void CExtWorkspaceResource::sendGroup() { - if (m_group) - m_group->workspaceLeave(m_resource); - - if (m_manager) { - m_group = m_manager->findGroup(m_workspace->m_monitor); - - if (m_group) - m_group->workspaceEnter(m_resource); - - m_manager->scheduleDone(); - } -} - -void CExtWorkspaceResource::commit() { - // order is important - - if (m_pendingState.deactivate && isActive() && m_workspace->m_isSpecialWorkspace) - m_workspace->m_monitor->setSpecialWorkspace(nullptr); - - if (m_pendingState.targetMonitor && m_workspace && m_workspace->m_monitor != m_pendingState.targetMonitor) - g_pCompositor->moveWorkspaceToMonitor(m_workspace.lock(), m_pendingState.targetMonitor.lock(), true); - - if (m_pendingState.activate && !isActive() && m_workspace) - m_workspace->m_monitor->changeWorkspace(m_workspace.lock()); - - m_pendingState.activate = false; - m_pendingState.deactivate = false; - m_pendingState.targetMonitor.reset(); -} - -CExtWorkspaceManagerResource::CExtWorkspaceManagerResource(UP resource) : m_resource(std::move(resource)) { - if (!good()) - return; - - m_resource->setOnDestroy([this](auto) { PROTO::extWorkspace->destroyManager(m_self); }); - - m_resource->setStop([this](auto) { - m_resource->sendFinished(); - PROTO::extWorkspace->destroyManager(m_self); - }); - - m_resource->setCommit([this](auto) { - for (auto& workspace : PROTO::extWorkspace->m_workspaces) { - if (workspace->m_manager == m_self) - workspace->commit(); - } - }); -} - -void CExtWorkspaceManagerResource::init(WP self) { - if (!good()) - return; - - m_self = self; - - for (auto const& m : g_pCompositor->m_monitors) { - onMonitorCreated(m); - } - - for (auto const& w : g_pCompositor->getWorkspaces()) { - onWorkspaceCreated(w.lock()); - } -} - -bool CExtWorkspaceManagerResource::good() const { - return m_resource; -} - -void CExtWorkspaceManagerResource::scheduleDone() { - if (m_doneScheduled) - return; - - m_doneScheduled = true; - g_pEventLoopManager->doLater([self = m_self] { - if (!self || !self->m_resource) - return; - - self->m_doneScheduled = false; - self->m_resource->sendDone(); - }); -} - -WP CExtWorkspaceManagerResource::findGroup(const PHLMONITORREF& monitor) const { - auto iter = std::ranges::find_if(PROTO::extWorkspace->m_groups, - [&](const UP& resource) { return resource->m_manager.get() == this && resource->m_monitor == monitor; }); - - return iter != PROTO::extWorkspace->m_groups.end() ? *iter : WP(); -} - -void CExtWorkspaceManagerResource::sendGroupToWorkspaces(const WP& group) { - for (auto& workspace : PROTO::extWorkspace->m_workspaces) { - if (workspace->m_manager == m_self && workspace->m_workspace && workspace->m_workspace->m_monitor == group->m_monitor) - workspace->sendGroup(); - } -} - -void CExtWorkspaceManagerResource::onMonitorCreated(const PHLMONITOR& monitor) { - auto& group = PROTO::extWorkspace->m_groups.emplace_back( - makeUnique(m_self, makeUnique(m_resource->client(), m_resource->version(), 0), monitor)); - group->m_self = group; - group->sendToWorkspaces(); - - if UNLIKELY (!group->good()) { - LOGM(Log::ERR, "Couldn't create a workspace group object"); - wl_client_post_no_memory(m_resource->client()); - return; - } - - scheduleDone(); -} - -void CExtWorkspaceManagerResource::onWorkspaceCreated(const PHLWORKSPACE& workspace) { - auto& ws = PROTO::extWorkspace->m_workspaces.emplace_back( - makeUnique(m_self, makeUnique(m_resource->client(), m_resource->version(), 0), workspace)); - ws->m_self = ws; - - if UNLIKELY (!ws->good()) { - LOGM(Log::ERR, "Couldn't create a workspace object"); - wl_client_post_no_memory(m_resource->client()); - return; - } -} - -CExtWorkspaceProtocol::CExtWorkspaceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P1 = Event::bus()->m_events.workspace.created.listen([this](PHLWORKSPACEREF workspace) { - for (auto const& m : m_managers) { - m->onWorkspaceCreated(workspace.lock()); - } - }); - - static auto P2 = Event::bus()->m_events.monitor.added.listen([this](PHLMONITOR monitor) { - for (auto const& m : m_managers) { - m->onMonitorCreated(monitor); - } - }); -} - -void CExtWorkspaceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - auto& manager = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))); - manager->init(manager); - - if UNLIKELY (!manager->good()) { - LOGM(Log::ERR, "Couldn't create a workspace manager"); - wl_client_post_no_memory(client); - return; - } -} - -void CExtWorkspaceProtocol::destroyGroup(const WP& group) { - std::erase_if(m_groups, [&](const UP& resource) { return resource == group; }); -} - -void CExtWorkspaceProtocol::destroyWorkspace(const WP& workspace) { - std::erase_if(m_workspaces, [&](const UP& resource) { return resource == workspace; }); -} - -void CExtWorkspaceProtocol::destroyManager(const WP& manager) { - std::erase_if(PROTO::extWorkspace->m_managers, [&](const UP& resource) { return resource == manager; }); -} diff --git a/src/protocols/ExtWorkspace.hpp b/src/protocols/ExtWorkspace.hpp deleted file mode 100644 index ba4aeeae..00000000 --- a/src/protocols/ExtWorkspace.hpp +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -#include "WaylandProtocol.hpp" -#include "../desktop/DesktopTypes.hpp" -#include "ext-workspace-v1.hpp" -#include -#include -#include "../helpers/signal/Signal.hpp" -#include "../helpers/Monitor.hpp" - -class CExtWorkspaceManagerResource; - -class CExtWorkspaceGroupResource { - public: - CExtWorkspaceGroupResource(WP manager, UP resource, PHLMONITORREF monitor); - - static WP fromResource(wl_resource*); - - [[nodiscard]] bool good() const; - - void workspaceEnter(const WP&); - void workspaceLeave(const WP&); - - void sendToWorkspaces(); - - PHLMONITORREF m_monitor; - - private: - WP m_self; - WP m_manager; - UP m_resource; - - struct { - CHyprSignalListener destroyed; - CHyprSignalListener outputBound; - } m_listeners; - - friend class CExtWorkspaceManagerResource; -}; - -class CExtWorkspaceResource { - public: - CExtWorkspaceResource(WP manager, UP resource, PHLWORKSPACEREF workspace); - - [[nodiscard]] bool good() const; - - void commit(); - - private: - WP m_self; - WP m_manager; - UP m_resource; - WP m_group; - PHLWORKSPACEREF m_workspace; - - [[nodiscard]] bool isActive() const; - - void sendState(); - void sendCapabilities(); - void sendGroup(); - - struct { - bool activate = false; - bool deactivate = false; - PHLMONITORREF targetMonitor; - } m_pendingState; - - struct { - CHyprSignalListener destroyed; - CHyprSignalListener activeChanged; - CHyprSignalListener monitorChanged; - CHyprSignalListener renamed; - } m_listeners; - - friend class CExtWorkspaceManagerResource; -}; - -class CExtWorkspaceManagerResource { - public: - CExtWorkspaceManagerResource(UP resource); - WP m_self; - - void init(WP self); - [[nodiscard]] bool good() const; - - void onMonitorCreated(const PHLMONITOR& monitor); - void onWorkspaceCreated(const PHLWORKSPACE& workspace); - - void scheduleDone(); - [[nodiscard]] WP findGroup(const PHLMONITORREF& monitor) const; - void sendGroupToWorkspaces(const WP& group); - - UP m_resource; - - private: - bool m_doneScheduled = false; -}; - -class CExtWorkspaceProtocol : public IWaylandProtocol { - public: - CExtWorkspaceProtocol(const wl_interface* iface, const int& var, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - void destroyManager(const WP& manager); - void destroyGroup(const WP& group); - void destroyWorkspace(const WP& workspace); - - private: - std::vector> m_managers; - std::vector> m_groups; - std::vector> m_workspaces; - - friend class CExtWorkspaceManagerResource; -}; - -namespace PROTO { - inline UP extWorkspace; -} diff --git a/src/protocols/Fifo.cpp b/src/protocols/Fifo.cpp deleted file mode 100644 index 355644d9..00000000 --- a/src/protocols/Fifo.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include "Fifo.hpp" -#include "Compositor.hpp" -#include "core/Compositor.hpp" -#include "../helpers/Monitor.hpp" -#include "../event/EventBus.hpp" - -CFifoResource::CFifoResource(UP&& resource_, SP surface) : m_resource(std::move(resource_)), m_surface(surface) { - if UNLIKELY (!m_resource->resource()) - return; - - m_resource->setData(this); - m_resource->setDestroy([this](CWpFifoV1* r) { PROTO::fifo->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpFifoV1* r) { PROTO::fifo->destroyResource(this); }); - - m_resource->setSetBarrier([this](CWpFifoV1* r) { - if (!m_surface) { - r->error(WP_FIFO_V1_ERROR_SURFACE_DESTROYED, "Surface was gone"); - return; - } - - m_surface->m_pending.barrierSet = true; - m_surface->m_pending.updated.bits.fifo = true; - }); - - m_resource->setWaitBarrier([this](CWpFifoV1* r) { - if (!m_surface) { - r->error(WP_FIFO_V1_ERROR_SURFACE_DESTROYED, "Surface was gone"); - return; - } - - if (!m_surface->m_current.barrierSet) { - // that might mean an empty commit with a barrier_set alone - static const auto PPEND = CConfigValue("debug:fifo_pending_workaround"); - if (!m_surface->m_pending.fifoScheduled) - m_surface->m_pending.fifoScheduled = checkMonitors(*PPEND); - - return; - } - - m_surface->m_pending.surfaceLocked = true; - }); - - m_listeners.surfaceStateCommit = m_surface->m_events.stateCommit.listen([this](auto state) { - if (!state || !state->surfaceLocked) - return; - - static const auto PPEND = CConfigValue("debug:fifo_pending_workaround"); - - //#TODO: - // this feels wrong, but if we have no pending frames, presented might never come because - // we are waiting on the barrier to unlock and no damage is around. - // unlock on timeout instead? - if (!state->fifoScheduled) - state->fifoScheduled = checkMonitors(*PPEND); - - if (!state->fifoScheduled) - return; - - // only lock once its mapped. - if (m_surface->m_mapped) - m_surface->m_stateQueue.lock(state, LOCK_REASON_FIFO); - }); -} - -CFifoResource::~CFifoResource() { - ; -} - -bool CFifoResource::good() { - return m_resource->resource(); -} - -void CFifoResource::presented() { - m_surface->m_current.barrierSet = false; - m_surface->m_stateQueue.unlockFirst(LOCK_REASON_FIFO); -} - -bool CFifoResource::checkMonitors(bool needsSchedule) { - if (m_surface->m_enteredOutputs.empty() && m_surface->m_hlSurface) { - for (auto& m : g_pCompositor->m_monitors) { - if (!m || !m->m_enabled) - continue; - - auto box = m_surface->m_hlSurface->getSurfaceBoxGlobal(); - if (box && !box->intersection({m->m_position, m->m_size}).empty()) { - if (m->m_tearingState.activelyTearing) - return false; // dont fifo lock on tearing. - - if (needsSchedule) - g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); - } - } - } else { - for (auto& m : m_surface->m_enteredOutputs) { - if (!m) - continue; - - if (m->m_tearingState.activelyTearing) - return false; // dont fifo lock on tearing. - - if (needsSchedule) - g_pCompositor->scheduleFrameForMonitor(m.lock(), Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); - } - } - - return true; -} - -CFifoManagerResource::CFifoManagerResource(UP&& resource_) : m_resource(std::move(resource_)) { - if UNLIKELY (!m_resource->resource()) - return; - - m_resource->setDestroy([this](CWpFifoManagerV1* r) { PROTO::fifo->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpFifoManagerV1* r) { PROTO::fifo->destroyResource(this); }); - - m_resource->setGetFifo([](CWpFifoManagerV1* r, uint32_t id, wl_resource* surfResource) { - if (!surfResource) { - r->error(-1, "No resource for fifo"); - return; - } - - auto surf = CWLSurfaceResource::fromResource(surfResource); - - if (!surf) { - r->error(-1, "No surface for fifo"); - return; - } - - if (surf->m_fifo) { - r->error(WP_FIFO_MANAGER_V1_ERROR_ALREADY_EXISTS, "Surface already has a fifo"); - return; - } - - const auto& RESOURCE = PROTO::fifo->m_fifos.emplace_back(makeUnique(makeUnique(r->client(), r->version(), id), surf)); - - if (!RESOURCE->good()) { - r->noMemory(); - PROTO::fifo->m_fifos.pop_back(); - return; - } - - surf->m_fifo = RESOURCE; - LOGM(Log::DEBUG, "New fifo at {:x} for surface {:x}", (uintptr_t)RESOURCE, (uintptr_t)surf.get()); - }); -} - -CFifoManagerResource::~CFifoManagerResource() { - ; -} - -bool CFifoManagerResource::good() { - return m_resource->resource(); -} - -CFifoProtocol::CFifoProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P = Event::bus()->m_events.monitor.added.listen([this](PHLMONITOR M) { - M->m_events.presented.listenStatic([this, m = PHLMONITORREF{M}]() { - if (!m || !PROTO::fifo) - return; - - onMonitorPresent(m.lock()); - }); - }); -} - -void CFifoProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))).get(); - - if (!RESOURCE->good()) { - wl_client_post_no_memory(client); - m_managers.pop_back(); - return; - } -} - -void CFifoProtocol::destroyResource(CFifoManagerResource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); -} - -void CFifoProtocol::destroyResource(CFifoResource* res) { - std::erase_if(m_fifos, [&](const auto& other) { return other.get() == res; }); -} - -void CFifoProtocol::onMonitorPresent(PHLMONITOR m) { - if (m->m_tearingState.activelyTearing) - return; // fifo isnt locked on tearing. - - for (const auto& fifo : m_fifos) { - if (!fifo->m_surface) - continue; - - if (!fifo->m_surface->m_mapped) { - fifo->presented(); - continue; - } - - auto it = std::ranges::find_if(fifo->m_surface->m_enteredOutputs, [m](auto& mon) { return mon == m; }); - if (it != fifo->m_surface->m_enteredOutputs.end()) { - fifo->presented(); - continue; - } - - if (fifo->m_surface->m_hlSurface) { - auto box = fifo->m_surface->m_hlSurface->getSurfaceBoxGlobal(); - if (box && !box->intersection({m->m_position, m->m_size}).empty()) { - fifo->presented(); - continue; - } - } - } -} diff --git a/src/protocols/Fifo.hpp b/src/protocols/Fifo.hpp deleted file mode 100644 index 5b143f79..00000000 --- a/src/protocols/Fifo.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include -#include "WaylandProtocol.hpp" -#include "fifo-v1.hpp" - -#include "../helpers/signal/Signal.hpp" - -class CWLSurfaceResource; - -class CFifoResource { - public: - CFifoResource(UP&& resource_, SP surface); - ~CFifoResource(); - - bool good(); - - private: - UP m_resource; - - WP m_surface; - - struct { - CHyprSignalListener surfaceStateCommit; - } m_listeners; - - void presented(); - bool checkMonitors(bool needsSchedule = false); - - friend class CFifoProtocol; - friend class CFifoManagerResource; -}; - -class CFifoManagerResource { - public: - CFifoManagerResource(UP&& resource_); - ~CFifoManagerResource(); - - bool good(); - - private: - UP m_resource; -}; - -class CFifoProtocol : public IWaylandProtocol { - public: - CFifoProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - private: - void destroyResource(CFifoManagerResource* resource); - void destroyResource(CFifoResource* resource); - - void onMonitorPresent(PHLMONITOR m); - - // - std::vector> m_managers; - std::vector> m_fifos; - - friend class CFifoManagerResource; - friend class CFifoResource; -}; - -namespace PROTO { - inline UP fifo; -}; diff --git a/src/protocols/FocusGrab.cpp b/src/protocols/FocusGrab.cpp index 62b65655..bef69f62 100644 --- a/src/protocols/FocusGrab.cpp +++ b/src/protocols/FocusGrab.cpp @@ -3,29 +3,28 @@ #include #include "../managers/input/InputManager.hpp" #include "../managers/SeatManager.hpp" -#include "../desktop/state/FocusState.hpp" #include "core/Compositor.hpp" #include #include CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, SP surface) { - m_listeners.destroy = surface->m_events.destroy.listen([grab, surface] { grab->eraseSurface(surface); }); + listeners.destroy = surface->events.destroy.registerListener([=](std::any d) { grab->eraseSurface(surface); }); } -CFocusGrab::CFocusGrab(SP resource_) : m_resource(resource_) { - if UNLIKELY (!m_resource->resource()) +CFocusGrab::CFocusGrab(SP resource_) : resource(resource_) { + if UNLIKELY (!resource->resource()) return; - m_grab = makeShared(); - m_grab->m_keyboard = true; - m_grab->m_pointer = true; - m_grab->setCallback([this]() { finish(true); }); + grab = makeShared(); + grab->keyboard = true; + grab->pointer = true; + grab->setCallback([this]() { finish(true); }); - m_resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); - m_resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); - m_resource->setAddSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { addSurface(CWLSurfaceResource::fromResource(surface)); }); - m_resource->setRemoveSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { removeSurface(CWLSurfaceResource::fromResource(surface)); }); - m_resource->setCommit([this](CHyprlandFocusGrabV1* pMgr) { commit(); }); + resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); + resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); + resource->setAddSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { addSurface(CWLSurfaceResource::fromResource(surface)); }); + resource->setRemoveSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { removeSurface(CWLSurfaceResource::fromResource(surface)); }); + resource->setCommit([this](CHyprlandFocusGrabV1* pMgr) { commit(); }); } CFocusGrab::~CFocusGrab() { @@ -33,56 +32,56 @@ CFocusGrab::~CFocusGrab() { } bool CFocusGrab::good() { - return m_resource->resource(); + return resource->resource(); } -bool CFocusGrab::isSurfaceCommitted(SP surface) { - auto iter = std::ranges::find_if(m_surfaces, [surface](const auto& o) { return o.first == surface; }); - if (iter == m_surfaces.end()) +bool CFocusGrab::isSurfaceComitted(SP surface) { + auto iter = std::find_if(m_mSurfaces.begin(), m_mSurfaces.end(), [surface](const auto& o) { return o.first == surface; }); + if (iter == m_mSurfaces.end()) return false; - return iter->second->m_state == CFocusGrabSurfaceState::Committed; + return iter->second->state == CFocusGrabSurfaceState::Comitted; } void CFocusGrab::start() { - if (!m_grabActive) { - m_grabActive = true; - g_pSeatManager->setGrab(m_grab); + if (!m_bGrabActive) { + m_bGrabActive = true; + g_pSeatManager->setGrab(grab); } - // Ensure new surfaces are focused if under the mouse when committed. + // Ensure new surfaces are focused if under the mouse when comitted. g_pInputManager->simulateMouseMovement(); refocusKeyboard(); } void CFocusGrab::finish(bool sendCleared) { - if (m_grabActive) { - m_grabActive = false; + if (m_bGrabActive) { + m_bGrabActive = false; - if (g_pSeatManager->m_seatGrab == m_grab) + if (g_pSeatManager->seatGrab == grab) g_pSeatManager->setGrab(nullptr); - m_grab->clear(); - m_surfaces.clear(); + grab->clear(); + m_mSurfaces.clear(); if (sendCleared) - m_resource->sendCleared(); + resource->sendCleared(); } } void CFocusGrab::addSurface(SP surface) { - auto iter = std::ranges::find_if(m_surfaces, [surface](const auto& e) { return e.first == surface; }); - if (iter == m_surfaces.end()) - m_surfaces.emplace(surface, makeUnique(this, surface)); + auto iter = std::find_if(m_mSurfaces.begin(), m_mSurfaces.end(), [surface](const auto& e) { return e.first == surface; }); + if (iter == m_mSurfaces.end()) + m_mSurfaces.emplace(surface, makeUnique(this, surface)); } void CFocusGrab::removeSurface(SP surface) { - auto iter = m_surfaces.find(surface); - if (iter != m_surfaces.end()) { - if (iter->second->m_state == CFocusGrabSurfaceState::PendingAddition) - m_surfaces.erase(iter); + auto iter = m_mSurfaces.find(surface); + if (iter != m_mSurfaces.end()) { + if (iter->second->state == CFocusGrabSurfaceState::PendingAddition) + m_mSurfaces.erase(iter); else - iter->second->m_state = CFocusGrabSurfaceState::PendingRemoval; + iter->second->state = CFocusGrabSurfaceState::PendingRemoval; } } @@ -92,50 +91,50 @@ void CFocusGrab::eraseSurface(SP surface) { } void CFocusGrab::refocusKeyboard() { - auto keyboardSurface = g_pSeatManager->m_state.keyboardFocus; - if (keyboardSurface && isSurfaceCommitted(keyboardSurface.lock())) + auto keyboardSurface = g_pSeatManager->state.keyboardFocus; + if (keyboardSurface && isSurfaceComitted(keyboardSurface.lock())) return; SP surface = nullptr; - for (auto const& [surf, state] : m_surfaces) { - if (state->m_state == CFocusGrabSurfaceState::Committed) { + for (auto const& [surf, state] : m_mSurfaces) { + if (state->state == CFocusGrabSurfaceState::Comitted) { surface = surf.lock(); break; } } if (surface) - Desktop::focusState()->rawSurfaceFocus(surface); + g_pCompositor->focusSurface(surface); else - LOGM(Log::ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen."); + LOGM(ERR, "CFocusGrab::refocusKeyboard called with no committed surfaces. This should never happen."); } void CFocusGrab::commit(bool removeOnly) { auto surfacesChanged = false; - auto anyCommitted = false; - for (auto iter = m_surfaces.begin(); iter != m_surfaces.end();) { - switch (iter->second->m_state) { + auto anyComitted = false; + for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) { + switch (iter->second->state) { case CFocusGrabSurfaceState::PendingRemoval: - m_grab->remove(iter->first.lock()); - iter = m_surfaces.erase(iter); + grab->remove(iter->first.lock()); + iter = m_mSurfaces.erase(iter); surfacesChanged = true; continue; case CFocusGrabSurfaceState::PendingAddition: if (!removeOnly) { - iter->second->m_state = CFocusGrabSurfaceState::Committed; - m_grab->add(iter->first.lock()); + iter->second->state = CFocusGrabSurfaceState::Comitted; + grab->add(iter->first.lock()); surfacesChanged = true; - anyCommitted = true; + anyComitted = true; } break; - case CFocusGrabSurfaceState::Committed: anyCommitted = true; break; + case CFocusGrabSurfaceState::Comitted: anyComitted = true; break; } iter++; } if (surfacesChanged) { - if (anyCommitted) + if (anyComitted) start(); else finish(true); @@ -147,7 +146,7 @@ CFocusGrabProtocol::CFocusGrabProtocol(const wl_interface* iface, const int& ver } void CFocusGrabProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CHyprlandFocusGrabManagerV1* p) { onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CHyprlandFocusGrabManagerV1* p) { onManagerResourceDestroy(p->resource()); }); @@ -155,19 +154,19 @@ void CFocusGrabProtocol::bindManager(wl_client* client, void* data, uint32_t ver } void CFocusGrabProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CFocusGrabProtocol::destroyGrab(CFocusGrab* grab) { - std::erase_if(m_grabs, [&](const auto& other) { return other.get() == grab; }); + std::erase_if(m_vGrabs, [&](const auto& other) { return other.get() == grab; }); } void CFocusGrabProtocol::onCreateGrab(CHyprlandFocusGrabManagerV1* pMgr, uint32_t id) { - m_grabs.push_back(makeUnique(makeShared(pMgr->client(), pMgr->version(), id))); - const auto RESOURCE = m_grabs.back().get(); + m_vGrabs.push_back(makeUnique(makeShared(pMgr->client(), pMgr->version(), id))); + const auto RESOURCE = m_vGrabs.back().get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_grabs.pop_back(); + m_vGrabs.pop_back(); } } diff --git a/src/protocols/FocusGrab.hpp b/src/protocols/FocusGrab.hpp index f02f97fc..1445a24c 100644 --- a/src/protocols/FocusGrab.hpp +++ b/src/protocols/FocusGrab.hpp @@ -20,13 +20,13 @@ class CFocusGrabSurfaceState { enum State { PendingAddition, PendingRemoval, - Committed, - } m_state = PendingAddition; + Comitted, + } state = PendingAddition; private: struct { CHyprSignalListener destroy; - } m_listeners; + } listeners; }; class CFocusGrab { @@ -35,7 +35,7 @@ class CFocusGrab { ~CFocusGrab(); bool good(); - bool isSurfaceCommitted(SP surface); + bool isSurfaceComitted(SP surface); void start(); void finish(bool sendCleared); @@ -47,11 +47,11 @@ class CFocusGrab { void refocusKeyboard(); void commit(bool removeOnly = false); - SP m_resource; - std::unordered_map, UP> m_surfaces; - SP m_grab; + SP resource; + std::unordered_map, UP> m_mSurfaces; + SP grab; - bool m_grabActive = false; + bool m_bGrabActive = false; friend class CFocusGrabSurfaceState; }; @@ -67,8 +67,8 @@ class CFocusGrabProtocol : public IWaylandProtocol { void destroyGrab(CFocusGrab* grab); void onCreateGrab(CHyprlandFocusGrabManagerV1* pMgr, uint32_t id); - std::vector> m_managers; - std::vector> m_grabs; + std::vector> m_vManagers; + std::vector> m_vGrabs; friend class CFocusGrab; }; diff --git a/src/protocols/ForeignToplevel.cpp b/src/protocols/ForeignToplevel.cpp index baabda7c..4e5fda48 100644 --- a/src/protocols/ForeignToplevel.cpp +++ b/src/protocols/ForeignToplevel.cpp @@ -1,180 +1,170 @@ #include "ForeignToplevel.hpp" #include "../Compositor.hpp" -#include "../event/EventBus.hpp" +#include "../managers/HookSystemManager.hpp" -CForeignToplevelHandle::CForeignToplevelHandle(SP resource_, PHLWINDOW pWindow_) : m_resource(resource_), m_window(pWindow_) { +CForeignToplevelHandle::CForeignToplevelHandle(SP resource_, PHLWINDOW pWindow_) : resource(resource_), pWindow(pWindow_) { if UNLIKELY (!resource_->resource()) return; - m_resource->setData(this); - - m_resource->setOnDestroy([this](CExtForeignToplevelHandleV1* h) { PROTO::foreignToplevel->destroyHandle(this); }); - m_resource->setDestroy([this](CExtForeignToplevelHandleV1* h) { PROTO::foreignToplevel->destroyHandle(this); }); + resource->setOnDestroy([this](CExtForeignToplevelHandleV1* h) { PROTO::foreignToplevel->destroyHandle(this); }); + resource->setDestroy([this](CExtForeignToplevelHandleV1* h) { PROTO::foreignToplevel->destroyHandle(this); }); } bool CForeignToplevelHandle::good() { - return m_resource->resource(); + return resource->resource(); } PHLWINDOW CForeignToplevelHandle::window() { - return m_window.lock(); + return pWindow.lock(); } -CForeignToplevelList::CForeignToplevelList(SP resource_) : m_resource(resource_) { +CForeignToplevelList::CForeignToplevelList(SP resource_) : resource(resource_) { if UNLIKELY (!resource_->resource()) return; - m_resource->setOnDestroy([this](CExtForeignToplevelListV1* h) { PROTO::foreignToplevel->onManagerResourceDestroy(this); }); - m_resource->setDestroy([this](CExtForeignToplevelListV1* h) { PROTO::foreignToplevel->onManagerResourceDestroy(this); }); + resource->setOnDestroy([this](CExtForeignToplevelListV1* h) { PROTO::foreignToplevel->onManagerResourceDestroy(this); }); + resource->setDestroy([this](CExtForeignToplevelListV1* h) { PROTO::foreignToplevel->onManagerResourceDestroy(this); }); - m_resource->setStop([this](CExtForeignToplevelListV1* h) { - m_resource->sendFinished(); - m_finished = true; - LOGM(Log::DEBUG, "CForeignToplevelList: finished"); + resource->setStop([this](CExtForeignToplevelListV1* h) { + resource->sendFinished(); + finished = true; + LOGM(LOG, "CForeignToplevelList: finished"); }); - for (auto const& w : g_pCompositor->m_windows) { + for (auto const& w : g_pCompositor->m_vWindows) { if (!PROTO::foreignToplevel->windowValidForForeign(w)) - continue; + return; onMap(w); } } void CForeignToplevelList::onMap(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; - // check if the window already had a handle in the past - const auto OLDHANDLE = handleForWindow(pWindow); - if (OLDHANDLE) { - if (!OLDHANDLE->m_closed) - OLDHANDLE->m_resource->sendClosed(); + const auto NEWHANDLE = PROTO::foreignToplevel->m_vHandles.emplace_back( + makeShared(makeShared(resource->client(), resource->version(), 0), pWindow)); - std::erase_if(m_handles, [&](const auto& other) { return other.get() == OLDHANDLE.get(); }); - } - - auto newHandle = PROTO::foreignToplevel->m_handles.emplace_back( - makeShared(makeShared(m_resource->client(), m_resource->version(), 0), pWindow)); - - if (!newHandle->good()) { - LOGM(Log::ERR, "Couldn't create a foreign handle"); - m_resource->noMemory(); - PROTO::foreignToplevel->m_handles.pop_back(); + if (!NEWHANDLE->good()) { + LOGM(ERR, "Couldn't create a foreign handle"); + resource->noMemory(); + PROTO::foreignToplevel->m_vHandles.pop_back(); return; } - const auto IDENTIFIER = std::format("{:x}", pWindow->m_stableID); + const auto IDENTIFIER = std::format("{:08x}->{:016x}", static_cast((uintptr_t)this & 0xFFFFFFFF), (uintptr_t)pWindow.get()); - LOGM(Log::DEBUG, "Newly mapped window gets an identifier of {}", IDENTIFIER); - m_resource->sendToplevel(newHandle->m_resource.get()); - newHandle->m_resource->sendIdentifier(IDENTIFIER.c_str()); - newHandle->m_resource->sendAppId(pWindow->m_initialClass.c_str()); - newHandle->m_resource->sendTitle(pWindow->m_initialTitle.c_str()); - newHandle->m_resource->sendDone(); + LOGM(LOG, "Newly mapped window gets an identifier of {}", IDENTIFIER); + resource->sendToplevel(NEWHANDLE->resource.get()); + NEWHANDLE->resource->sendIdentifier(IDENTIFIER.c_str()); + NEWHANDLE->resource->sendAppId(pWindow->m_szInitialClass.c_str()); + NEWHANDLE->resource->sendTitle(pWindow->m_szInitialTitle.c_str()); + NEWHANDLE->resource->sendDone(); - m_handles.emplace_back(std::move(newHandle)); + handles.push_back(NEWHANDLE); } SP CForeignToplevelList::handleForWindow(PHLWINDOW pWindow) { - std::erase_if(m_handles, [](const auto& wp) { return wp.expired(); }); - const auto IT = std::ranges::find_if(m_handles, [pWindow](const auto& h) { return h->window() == pWindow; }); - return IT == m_handles.end() ? SP{} : IT->lock(); + std::erase_if(handles, [](const auto& wp) { return wp.expired(); }); + const auto IT = std::find_if(handles.begin(), handles.end(), [pWindow](const auto& h) { return h->window() == pWindow; }); + return IT == handles.end() ? SP{} : IT->lock(); } void CForeignToplevelList::onTitle(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; const auto H = handleForWindow(pWindow); - if UNLIKELY (!H || H->m_closed) + if UNLIKELY (!H || H->closed) return; - H->m_resource->sendTitle(pWindow->m_title.c_str()); - H->m_resource->sendDone(); + H->resource->sendTitle(pWindow->m_szTitle.c_str()); + H->resource->sendDone(); } void CForeignToplevelList::onClass(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; const auto H = handleForWindow(pWindow); - if UNLIKELY (!H || H->m_closed) + if UNLIKELY (!H || H->closed) return; - H->m_resource->sendAppId(pWindow->m_class.c_str()); - H->m_resource->sendDone(); + H->resource->sendAppId(pWindow->m_szClass.c_str()); + H->resource->sendDone(); } void CForeignToplevelList::onUnmap(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; const auto H = handleForWindow(pWindow); if UNLIKELY (!H) return; - H->m_resource->sendClosed(); - H->m_closed = true; + H->resource->sendClosed(); + H->closed = true; } bool CForeignToplevelList::good() { - return m_resource->resource(); + return resource->resource(); } CForeignToplevelProtocol::CForeignToplevelProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P = Event::bus()->m_events.window.open.listen([this](PHLWINDOW window) { + static auto P = g_pHookSystem->hookDynamic("openWindow", [this](void* self, SCallbackInfo& info, std::any data) { + auto window = std::any_cast(data); + if (!windowValidForForeign(window)) return; - for (auto const& m : m_managers) { + for (auto const& m : m_vManagers) { m->onMap(window); } }); - static auto P1 = Event::bus()->m_events.window.close.listen([this](PHLWINDOW window) { + static auto P1 = g_pHookSystem->hookDynamic("closeWindow", [this](void* self, SCallbackInfo& info, std::any data) { + auto window = std::any_cast(data); + if (!windowValidForForeign(window)) return; - for (auto const& m : m_managers) { + for (auto const& m : m_vManagers) { m->onUnmap(window); } }); - static auto P2 = Event::bus()->m_events.window.title.listen([this](PHLWINDOW window) { + static auto P2 = g_pHookSystem->hookDynamic("windowTitle", [this](void* self, SCallbackInfo& info, std::any data) { + auto window = std::any_cast(data); + if (!windowValidForForeign(window)) return; - for (auto const& m : m_managers) { + for (auto const& m : m_vManagers) { m->onTitle(window); } }); } void CForeignToplevelProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(makeShared(client, ver, id))).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(makeShared(client, ver, id))).get(); if UNLIKELY (!RESOURCE->good()) { - LOGM(Log::ERR, "Couldn't create a foreign list"); + LOGM(ERR, "Couldn't create a foreign list"); wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CForeignToplevelProtocol::onManagerResourceDestroy(CForeignToplevelList* mgr) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == mgr; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == mgr; }); } void CForeignToplevelProtocol::destroyHandle(CForeignToplevelHandle* handle) { - std::erase_if(m_handles, [&](const auto& other) { return other.get() == handle; }); + std::erase_if(m_vHandles, [&](const auto& other) { return other.get() == handle; }); } bool CForeignToplevelProtocol::windowValidForForeign(PHLWINDOW pWindow) { return validMapped(pWindow) && !pWindow->isX11OverrideRedirect(); } - -PHLWINDOW CForeignToplevelProtocol::windowFromHandleResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->window() : nullptr; -} diff --git a/src/protocols/ForeignToplevel.hpp b/src/protocols/ForeignToplevel.hpp index 0ff74e75..712b32e0 100644 --- a/src/protocols/ForeignToplevel.hpp +++ b/src/protocols/ForeignToplevel.hpp @@ -3,7 +3,6 @@ #include #include #include "WaylandProtocol.hpp" -#include "../desktop/DesktopTypes.hpp" #include "ext-foreign-toplevel-list-v1.hpp" class CForeignToplevelHandle { @@ -14,12 +13,11 @@ class CForeignToplevelHandle { PHLWINDOW window(); private: - SP m_resource; - PHLWINDOWREF m_window; - bool m_closed = false; + SP resource; + PHLWINDOWREF pWindow; + bool closed = false; friend class CForeignToplevelList; - friend class CForeignToplevelProtocol; }; class CForeignToplevelList { @@ -34,12 +32,12 @@ class CForeignToplevelList { bool good(); private: - SP m_resource; - bool m_finished = false; + SP resource; + bool finished = false; SP handleForWindow(PHLWINDOW pWindow); - std::vector> m_handles; + std::vector> handles; }; class CForeignToplevelProtocol : public IWaylandProtocol { @@ -47,7 +45,6 @@ class CForeignToplevelProtocol : public IWaylandProtocol { CForeignToplevelProtocol(const wl_interface* iface, const int& ver, const std::string& name); virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - PHLWINDOW windowFromHandleResource(wl_resource* res); private: void onManagerResourceDestroy(CForeignToplevelList* mgr); @@ -55,8 +52,8 @@ class CForeignToplevelProtocol : public IWaylandProtocol { bool windowValidForForeign(PHLWINDOW pWindow); // - std::vector> m_managers; - std::vector> m_handles; + std::vector> m_vManagers; + std::vector> m_vHandles; friend class CForeignToplevelList; friend class CForeignToplevelHandle; diff --git a/src/protocols/ForeignToplevelWlr.cpp b/src/protocols/ForeignToplevelWlr.cpp index 56591261..e50c85d9 100644 --- a/src/protocols/ForeignToplevelWlr.cpp +++ b/src/protocols/ForeignToplevelWlr.cpp @@ -1,24 +1,19 @@ #include "ForeignToplevelWlr.hpp" -#include "core/Output.hpp" #include #include "../Compositor.hpp" -#include "../managers/input/InputManager.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../render/Renderer.hpp" -#include "../managers/EventManager.hpp" -#include "../event/EventBus.hpp" +#include "protocols/core/Output.hpp" +#include "render/Renderer.hpp" +#include "../managers/HookSystemManager.hpp" -CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP resource_, PHLWINDOW pWindow_) : m_resource(resource_), m_window(pWindow_) { +CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SP resource_, PHLWINDOW pWindow_) : resource(resource_), pWindow(pWindow_) { if UNLIKELY (!resource_->resource()) return; - m_resource->setData(this); + resource->setOnDestroy([this](CZwlrForeignToplevelHandleV1* h) { PROTO::foreignToplevelWlr->destroyHandle(this); }); + resource->setDestroy([this](CZwlrForeignToplevelHandleV1* h) { PROTO::foreignToplevelWlr->destroyHandle(this); }); - m_resource->setOnDestroy([this](CZwlrForeignToplevelHandleV1* h) { PROTO::foreignToplevelWlr->destroyHandle(this); }); - m_resource->setDestroy([this](CZwlrForeignToplevelHandleV1* h) { PROTO::foreignToplevelWlr->destroyHandle(this); }); - - m_resource->setActivate([this](CZwlrForeignToplevelHandleV1* p, wl_resource* seat) { - const auto PWINDOW = m_window.lock(); + resource->setActivate([this](CZwlrForeignToplevelHandleV1* p, wl_resource* seat) { + const auto PWINDOW = pWindow.lock(); if UNLIKELY (!PWINDOW) return; @@ -26,32 +21,31 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SPactivate(true); - g_pInputManager->simulateMouseMovement(); }); - m_resource->setSetFullscreen([this](CZwlrForeignToplevelHandleV1* p, wl_resource* output) { - const auto PWINDOW = m_window.lock(); + resource->setSetFullscreen([this](CZwlrForeignToplevelHandleV1* p, wl_resource* output) { + const auto PWINDOW = pWindow.lock(); if UNLIKELY (!PWINDOW) return; - if UNLIKELY (PWINDOW->m_suppressedEvents & Desktop::View::SUPPRESS_FULLSCREEN) + if UNLIKELY (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN) return; - if UNLIKELY (!PWINDOW->m_isMapped) { - PWINDOW->m_wantsInitialFullscreen = true; + if UNLIKELY (!PWINDOW->m_bIsMapped) { + PWINDOW->m_bWantsInitialFullscreen = true; return; } if (output) { - const auto wpMonitor = CWLOutputResource::fromResource(output)->m_monitor; + const auto wpMonitor = CWLOutputResource::fromResource(output)->monitor; if (!wpMonitor.expired()) { const auto monitor = wpMonitor.lock(); - if (PWINDOW->m_workspace != monitor->m_activeWorkspace) { - g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, monitor->m_activeWorkspace); - Desktop::focusState()->rawMonitorFocus(monitor); + if (PWINDOW->m_pWorkspace != monitor->activeWorkspace) { + g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, monitor->activeWorkspace); + g_pCompositor->setActiveMonitor(monitor); } } } @@ -60,73 +54,49 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SPdamageWindow(PWINDOW); }); - m_resource->setUnsetFullscreen([this](CZwlrForeignToplevelHandleV1* p) { - const auto PWINDOW = m_window.lock(); + resource->setUnsetFullscreen([this](CZwlrForeignToplevelHandleV1* p) { + const auto PWINDOW = pWindow.lock(); if UNLIKELY (!PWINDOW) return; - if UNLIKELY (PWINDOW->m_suppressedEvents & Desktop::View::SUPPRESS_FULLSCREEN) + if UNLIKELY (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN) return; g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_FULLSCREEN, false); }); - m_resource->setSetMaximized([this](CZwlrForeignToplevelHandleV1* p) { - const auto PWINDOW = m_window.lock(); + resource->setSetMaximized([this](CZwlrForeignToplevelHandleV1* p) { + const auto PWINDOW = pWindow.lock(); if UNLIKELY (!PWINDOW) return; - if UNLIKELY (PWINDOW->m_suppressedEvents & Desktop::View::SUPPRESS_MAXIMIZE) + if UNLIKELY (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE) return; - if UNLIKELY (!PWINDOW->m_isMapped) { - PWINDOW->m_wantsInitialFullscreen = true; + if UNLIKELY (!PWINDOW->m_bIsMapped) { + PWINDOW->m_bWantsInitialFullscreen = true; return; } g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_MAXIMIZED, true); }); - m_resource->setUnsetMaximized([this](CZwlrForeignToplevelHandleV1* p) { - const auto PWINDOW = m_window.lock(); + resource->setUnsetMaximized([this](CZwlrForeignToplevelHandleV1* p) { + const auto PWINDOW = pWindow.lock(); if UNLIKELY (!PWINDOW) return; - if UNLIKELY (PWINDOW->m_suppressedEvents & Desktop::View::SUPPRESS_MAXIMIZE) + if UNLIKELY (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE) return; g_pCompositor->changeWindowFullscreenModeClient(PWINDOW, FSMODE_MAXIMIZED, false); }); - m_resource->setSetMinimized([this](CZwlrForeignToplevelHandleV1* p) { - const auto PWINDOW = m_window.lock(); - - if UNLIKELY (!PWINDOW) - return; - - if UNLIKELY (!PWINDOW->m_isMapped) - return; - - g_pEventManager->postEvent(SHyprIPCEvent{.event = "minimized", .data = std::format("{:x},1", rc(PWINDOW.get()))}); - }); - - m_resource->setUnsetMinimized([this](CZwlrForeignToplevelHandleV1* p) { - const auto PWINDOW = m_window.lock(); - - if UNLIKELY (!PWINDOW) - return; - - if UNLIKELY (!PWINDOW->m_isMapped) - return; - - g_pEventManager->postEvent(SHyprIPCEvent{.event = "minimized", .data = std::format("{:x},0", rc(PWINDOW.get()))}); - }); - - m_resource->setClose([this](CZwlrForeignToplevelHandleV1* p) { - const auto PWINDOW = m_window.lock(); + resource->setClose([this](CZwlrForeignToplevelHandleV1* p) { + const auto PWINDOW = pWindow.lock(); if UNLIKELY (!PWINDOW) return; @@ -136,290 +106,303 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SPresource(); + return resource->resource(); } PHLWINDOW CForeignToplevelHandleWlr::window() { - return m_window.lock(); + return pWindow.lock(); } wl_resource* CForeignToplevelHandleWlr::res() { - return m_resource->resource(); + return resource->resource(); } void CForeignToplevelHandleWlr::sendMonitor(PHLMONITOR pMonitor) { - if (m_lastMonitorID == pMonitor->m_id) + if (lastMonitorID == pMonitor->ID) return; - const auto CLIENT = m_resource->client(); + const auto CLIENT = resource->client(); - if (const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(m_lastMonitorID); PLASTMONITOR && PROTO::outputs.contains(PLASTMONITOR->m_name)) { - const auto OLDRESOURCES = PROTO::outputs.at(PLASTMONITOR->m_name)->outputResourcesFrom(CLIENT); + if (const auto PLASTMONITOR = g_pCompositor->getMonitorFromID(lastMonitorID); PLASTMONITOR && PROTO::outputs.contains(PLASTMONITOR->szName)) { + const auto OLDRESOURCE = PROTO::outputs.at(PLASTMONITOR->szName)->outputResourceFrom(CLIENT); - if LIKELY (!OLDRESOURCES.empty()) { - for (const auto& r : OLDRESOURCES) { - m_resource->sendOutputLeave(r->getResource()->resource()); - } - } + if LIKELY (OLDRESOURCE) + resource->sendOutputLeave(OLDRESOURCE->getResource()->resource()); } - if (PROTO::outputs.contains(pMonitor->m_name)) { - const auto NEWRESOURCES = PROTO::outputs.at(pMonitor->m_name)->outputResourcesFrom(CLIENT); + if (PROTO::outputs.contains(pMonitor->szName)) { + const auto NEWRESOURCE = PROTO::outputs.at(pMonitor->szName)->outputResourceFrom(CLIENT); - if LIKELY (!NEWRESOURCES.empty()) { - for (const auto& r : NEWRESOURCES) { - m_resource->sendOutputEnter(r->getResource()->resource()); - } - } + if LIKELY (NEWRESOURCE) + resource->sendOutputEnter(NEWRESOURCE->getResource()->resource()); } - m_lastMonitorID = pMonitor->m_id; + lastMonitorID = pMonitor->ID; } void CForeignToplevelHandleWlr::sendState() { - const auto PWINDOW = m_window.lock(); + const auto PWINDOW = pWindow.lock(); - if UNLIKELY (!PWINDOW || !PWINDOW->m_workspace || !PWINDOW->m_isMapped) + if UNLIKELY (!PWINDOW || !PWINDOW->m_pWorkspace || !PWINDOW->m_bIsMapped) return; wl_array state; wl_array_init(&state); - if (PWINDOW == Desktop::focusState()->window()) { - auto p = sc(wl_array_add(&state, sizeof(uint32_t))); + if (PWINDOW == g_pCompositor->m_pLastWindow) { + auto p = (uint32_t*)wl_array_add(&state, sizeof(uint32_t)); *p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; } if (PWINDOW->isFullscreen()) { - auto p = sc(wl_array_add(&state, sizeof(uint32_t))); + auto p = (uint32_t*)wl_array_add(&state, sizeof(uint32_t)); if (PWINDOW->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) *p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; else *p = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; } - m_resource->sendState(&state); + resource->sendState(&state); wl_array_release(&state); } -CForeignToplevelWlrManager::CForeignToplevelWlrManager(SP resource_) : m_resource(resource_) { +CForeignToplevelWlrManager::CForeignToplevelWlrManager(SP resource_) : resource(resource_) { if UNLIKELY (!resource_->resource()) return; - m_resource->setOnDestroy([this](CZwlrForeignToplevelManagerV1* h) { PROTO::foreignToplevelWlr->onManagerResourceDestroy(this); }); + resource->setOnDestroy([this](CZwlrForeignToplevelManagerV1* h) { PROTO::foreignToplevelWlr->onManagerResourceDestroy(this); }); - m_resource->setStop([this](CZwlrForeignToplevelManagerV1* h) { - m_resource->sendFinished(); - m_finished = true; - LOGM(Log::DEBUG, "CForeignToplevelWlrManager: finished"); + resource->setStop([this](CZwlrForeignToplevelManagerV1* h) { + resource->sendFinished(); + finished = true; + LOGM(LOG, "CForeignToplevelWlrManager: finished"); PROTO::foreignToplevelWlr->onManagerResourceDestroy(this); }); - for (auto const& w : g_pCompositor->m_windows) { + for (auto const& w : g_pCompositor->m_vWindows) { if (!PROTO::foreignToplevelWlr->windowValidForForeign(w)) continue; onMap(w); } - m_lastFocus = Desktop::focusState()->window(); + lastFocus = g_pCompositor->m_pLastWindow; } void CForeignToplevelWlrManager::onMap(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; - const auto NEWHANDLE = PROTO::foreignToplevelWlr->m_handles.emplace_back( - makeShared(makeShared(m_resource->client(), m_resource->version(), 0), pWindow)); + const auto NEWHANDLE = PROTO::foreignToplevelWlr->m_vHandles.emplace_back( + makeShared(makeShared(resource->client(), resource->version(), 0), pWindow)); if UNLIKELY (!NEWHANDLE->good()) { - LOGM(Log::ERR, "Couldn't create a foreign handle"); - m_resource->noMemory(); - PROTO::foreignToplevelWlr->m_handles.pop_back(); + LOGM(ERR, "Couldn't create a foreign handle"); + resource->noMemory(); + PROTO::foreignToplevelWlr->m_vHandles.pop_back(); return; } - LOGM(Log::DEBUG, "Newly mapped window {:016x}", (uintptr_t)pWindow.get()); - m_resource->sendToplevel(NEWHANDLE->m_resource.get()); - NEWHANDLE->m_resource->sendAppId(pWindow->m_class.c_str()); - NEWHANDLE->m_resource->sendTitle(pWindow->m_title.c_str()); - if LIKELY (const auto PMONITOR = pWindow->m_monitor.lock(); PMONITOR) + LOGM(LOG, "Newly mapped window {:016x}", (uintptr_t)pWindow.get()); + resource->sendToplevel(NEWHANDLE->resource.get()); + NEWHANDLE->resource->sendAppId(pWindow->m_szClass.c_str()); + NEWHANDLE->resource->sendTitle(pWindow->m_szTitle.c_str()); + if LIKELY (const auto PMONITOR = pWindow->m_pMonitor.lock(); PMONITOR) NEWHANDLE->sendMonitor(PMONITOR); NEWHANDLE->sendState(); - NEWHANDLE->m_resource->sendDone(); + NEWHANDLE->resource->sendDone(); - m_handles.push_back(NEWHANDLE); + handles.push_back(NEWHANDLE); } SP CForeignToplevelWlrManager::handleForWindow(PHLWINDOW pWindow) { - std::erase_if(m_handles, [](const auto& wp) { return wp.expired(); }); - const auto IT = std::ranges::find_if(m_handles, [pWindow](const auto& h) { return h->window() == pWindow; }); - return IT == m_handles.end() ? SP{} : IT->lock(); + std::erase_if(handles, [](const auto& wp) { return wp.expired(); }); + const auto IT = std::find_if(handles.begin(), handles.end(), [pWindow](const auto& h) { return h->window() == pWindow; }); + return IT == handles.end() ? SP{} : IT->lock(); } void CForeignToplevelWlrManager::onTitle(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; const auto H = handleForWindow(pWindow); - if UNLIKELY (!H || H->m_closed) + if UNLIKELY (!H || H->closed) return; - H->m_resource->sendTitle(pWindow->m_title.c_str()); - H->m_resource->sendDone(); + H->resource->sendTitle(pWindow->m_szTitle.c_str()); + H->resource->sendDone(); } void CForeignToplevelWlrManager::onClass(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; const auto H = handleForWindow(pWindow); - if UNLIKELY (!H || H->m_closed) + if UNLIKELY (!H || H->closed) return; - H->m_resource->sendAppId(pWindow->m_class.c_str()); - H->m_resource->sendDone(); + H->resource->sendAppId(pWindow->m_szClass.c_str()); + H->resource->sendDone(); } void CForeignToplevelWlrManager::onUnmap(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; const auto H = handleForWindow(pWindow); if UNLIKELY (!H) return; - H->m_resource->sendClosed(); - H->m_resource->sendDone(); - H->m_closed = true; + H->resource->sendClosed(); + H->resource->sendDone(); + H->closed = true; } -void CForeignToplevelWlrManager::onMoveMonitor(PHLWINDOW pWindow, PHLMONITOR pMonitor) { - if UNLIKELY (m_finished) +void CForeignToplevelWlrManager::onMoveMonitor(PHLWINDOW pWindow) { + if UNLIKELY (finished) return; const auto H = handleForWindow(pWindow); - if UNLIKELY (!H || H->m_closed || !pMonitor) + if UNLIKELY (!H || H->closed) return; - H->sendMonitor(pMonitor); - H->m_resource->sendDone(); + const auto PMONITOR = pWindow->m_pMonitor.lock(); + + if UNLIKELY (!PMONITOR) + return; + + H->sendMonitor(PMONITOR); + H->resource->sendDone(); } void CForeignToplevelWlrManager::onFullscreen(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; const auto H = handleForWindow(pWindow); - if UNLIKELY (!H || H->m_closed) + if UNLIKELY (!H || H->closed) return; H->sendState(); - H->m_resource->sendDone(); + H->resource->sendDone(); } void CForeignToplevelWlrManager::onNewFocus(PHLWINDOW pWindow) { - if UNLIKELY (m_finished) + if UNLIKELY (finished) return; - if LIKELY (const auto HOLD = handleForWindow(m_lastFocus.lock()); HOLD) { + if LIKELY (const auto HOLD = handleForWindow(lastFocus.lock()); HOLD) { HOLD->sendState(); - HOLD->m_resource->sendDone(); + HOLD->resource->sendDone(); } - m_lastFocus = pWindow; + lastFocus = pWindow; const auto H = handleForWindow(pWindow); - if UNLIKELY (!H || H->m_closed) + if UNLIKELY (!H || H->closed) return; H->sendState(); - H->m_resource->sendDone(); + H->resource->sendDone(); } bool CForeignToplevelWlrManager::good() { - return m_resource->resource(); + return resource->resource(); } CForeignToplevelWlrProtocol::CForeignToplevelWlrProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P = Event::bus()->m_events.window.open.listen([this](PHLWINDOW window) { - if (!windowValidForForeign(window)) + static auto P = g_pHookSystem->hookDynamic("openWindow", [this](void* self, SCallbackInfo& info, std::any data) { + const auto PWINDOW = std::any_cast(data); + + if (!windowValidForForeign(PWINDOW)) return; - for (auto const& m : m_managers) { - m->onMap(window); + for (auto const& m : m_vManagers) { + m->onMap(PWINDOW); } }); - static auto P1 = Event::bus()->m_events.window.close.listen([this](PHLWINDOW window) { - if (!windowValidForForeign(window)) + static auto P1 = g_pHookSystem->hookDynamic("closeWindow", [this](void* self, SCallbackInfo& info, std::any data) { + const auto PWINDOW = std::any_cast(data); + + if (!windowValidForForeign(PWINDOW)) return; - for (auto const& m : m_managers) { - m->onUnmap(window); + for (auto const& m : m_vManagers) { + m->onUnmap(PWINDOW); } }); - static auto P2 = Event::bus()->m_events.window.title.listen([this](PHLWINDOW window) { - if (!windowValidForForeign(window)) + static auto P2 = g_pHookSystem->hookDynamic("windowTitle", [this](void* self, SCallbackInfo& info, std::any data) { + const auto PWINDOW = std::any_cast(data); + + if (!windowValidForForeign(PWINDOW)) return; - for (auto const& m : m_managers) { - m->onTitle(window); + for (auto const& m : m_vManagers) { + m->onTitle(PWINDOW); } }); - static auto P3 = Event::bus()->m_events.window.active.listen([this](PHLWINDOW window, Desktop::eFocusReason reason) { - if (window && !windowValidForForeign(window)) + static auto P3 = g_pHookSystem->hookDynamic("activeWindow", [this](void* self, SCallbackInfo& info, std::any data) { + const auto PWINDOW = std::any_cast(data); + + if (PWINDOW && !windowValidForForeign(PWINDOW)) return; - for (auto const& m : m_managers) { - m->onNewFocus(window); + for (auto const& m : m_vManagers) { + m->onNewFocus(PWINDOW); } }); - static auto P4 = Event::bus()->m_events.window.moveToWorkspace.listen([this](PHLWINDOW window, PHLWORKSPACE ws) { - if (!ws) - return; - - for (auto const& m : m_managers) { - m->onMoveMonitor(window, ws->m_monitor.lock()); + static auto P4 = g_pHookSystem->hookDynamic("moveWindow", [this](void* self, SCallbackInfo& info, std::any data) { + const auto PWINDOW = std::any_cast(std::any_cast>(data).at(0)); + for (auto const& m : m_vManagers) { + m->onMoveMonitor(PWINDOW); } }); - static auto P5 = Event::bus()->m_events.window.fullscreen.listen([this](PHLWINDOW window) { - if (!windowValidForForeign(window)) + static auto P5 = g_pHookSystem->hookDynamic("fullscreen", [this](void* self, SCallbackInfo& info, std::any data) { + const auto PWINDOW = std::any_cast(data); + + if (!windowValidForForeign(PWINDOW)) return; - for (auto const& m : m_managers) { - m->onFullscreen(window); + for (auto const& m : m_vManagers) { + m->onFullscreen(PWINDOW); } }); } void CForeignToplevelWlrProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(makeShared(client, ver, id))).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(makeShared(client, ver, id))).get(); if UNLIKELY (!RESOURCE->good()) { - LOGM(Log::ERR, "Couldn't create a foreign list"); + LOGM(ERR, "Couldn't create a foreign list"); wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CForeignToplevelWlrProtocol::onManagerResourceDestroy(CForeignToplevelWlrManager* mgr) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == mgr; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == mgr; }); } void CForeignToplevelWlrProtocol::destroyHandle(CForeignToplevelHandleWlr* handle) { - std::erase_if(m_handles, [&](const auto& other) { return other.get() == handle; }); + std::erase_if(m_vHandles, [&](const auto& other) { return other.get() == handle; }); } PHLWINDOW CForeignToplevelWlrProtocol::windowFromHandleResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->window() : nullptr; + for (auto const& h : m_vHandles) { + if (h->res() != res) + continue; + + return h->window(); + } + + return nullptr; } bool CForeignToplevelWlrProtocol::windowValidForForeign(PHLWINDOW pWindow) { diff --git a/src/protocols/ForeignToplevelWlr.hpp b/src/protocols/ForeignToplevelWlr.hpp index 444cbe0d..d0479d98 100644 --- a/src/protocols/ForeignToplevelWlr.hpp +++ b/src/protocols/ForeignToplevelWlr.hpp @@ -4,6 +4,7 @@ #include "WaylandProtocol.hpp" #include "wlr-foreign-toplevel-management-unstable-v1.hpp" +class CWindow; class CMonitor; class CForeignToplevelHandleWlr { @@ -15,10 +16,10 @@ class CForeignToplevelHandleWlr { wl_resource* res(); private: - SP m_resource; - PHLWINDOWREF m_window; - bool m_closed = false; - MONITORID m_lastMonitorID = MONITOR_INVALID; + SP resource; + PHLWINDOWREF pWindow; + bool closed = false; + MONITORID lastMonitorID = MONITOR_INVALID; void sendMonitor(PHLMONITOR pMonitor); void sendState(); @@ -33,7 +34,7 @@ class CForeignToplevelWlrManager { void onMap(PHLWINDOW pWindow); void onTitle(PHLWINDOW pWindow); void onClass(PHLWINDOW pWindow); - void onMoveMonitor(PHLWINDOW pWindow, PHLMONITOR pMonitor); + void onMoveMonitor(PHLWINDOW pWindow); void onFullscreen(PHLWINDOW pWindow); void onNewFocus(PHLWINDOW pWindow); void onUnmap(PHLWINDOW pWindow); @@ -41,13 +42,13 @@ class CForeignToplevelWlrManager { bool good(); private: - SP m_resource; - bool m_finished = false; - PHLWINDOWREF m_lastFocus; // READ-ONLY + SP resource; + bool finished = false; + PHLWINDOWREF lastFocus; // READ-ONLY SP handleForWindow(PHLWINDOW pWindow); - std::vector> m_handles; + std::vector> handles; }; class CForeignToplevelWlrProtocol : public IWaylandProtocol { @@ -64,8 +65,8 @@ class CForeignToplevelWlrProtocol : public IWaylandProtocol { bool windowValidForForeign(PHLWINDOW pWindow); // - std::vector> m_managers; - std::vector> m_handles; + std::vector> m_vManagers; + std::vector> m_vHandles; friend class CForeignToplevelWlrManager; friend class CForeignToplevelHandleWlr; diff --git a/src/protocols/FractionalScale.cpp b/src/protocols/FractionalScale.cpp index 9bdf5910..494de9c5 100644 --- a/src/protocols/FractionalScale.cpp +++ b/src/protocols/FractionalScale.cpp @@ -7,7 +7,7 @@ CFractionalScaleProtocol::CFractionalScaleProtocol(const wl_interface* iface, co } void CFractionalScaleProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CWpFractionalScaleManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CWpFractionalScaleManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -16,27 +16,27 @@ void CFractionalScaleProtocol::bindManager(wl_client* client, void* data, uint32 } void CFractionalScaleProtocol::removeAddon(CFractionalScaleAddon* addon) { - m_addons.erase(addon->surf()); + m_mAddons.erase(addon->surf()); } void CFractionalScaleProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [res](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [res](const auto& other) { return other->resource() == res; }); } void CFractionalScaleProtocol::onGetFractionalScale(CWpFractionalScaleManagerV1* pMgr, uint32_t id, SP surface) { - for (auto const& [k, v] : m_addons) { + for (auto const& [k, v] : m_mAddons) { if (k == surface) { - LOGM(Log::ERR, "Surface {:x} already has a fractionalScale addon", (uintptr_t)surface.get()); + LOGM(ERR, "Surface {:x} already has a fractionalScale addon", (uintptr_t)surface.get()); pMgr->error(WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_FRACTIONAL_SCALE_EXISTS, "Fractional scale already exists"); return; } } const auto PADDON = - m_addons.emplace(surface, makeUnique(makeShared(pMgr->client(), pMgr->version(), id), surface)).first->second.get(); + m_mAddons.emplace(surface, makeUnique(makeShared(pMgr->client(), pMgr->version(), id), surface)).first->second.get(); if UNLIKELY (!PADDON->good()) { - m_addons.erase(surface); + m_mAddons.erase(surface); pMgr->noMemory(); return; } @@ -44,20 +44,20 @@ void CFractionalScaleProtocol::onGetFractionalScale(CWpFractionalScaleManagerV1* PADDON->m_resource->setOnDestroy([this, PADDON](CWpFractionalScaleV1* self) { this->removeAddon(PADDON); }); PADDON->m_resource->setDestroy([this, PADDON](CWpFractionalScaleV1* self) { this->removeAddon(PADDON); }); - if (std::ranges::find_if(m_surfaceScales, [surface](const auto& e) { return e.first == surface; }) == m_surfaceScales.end()) - m_surfaceScales.emplace(surface, 1.F); + if (std::ranges::find_if(m_mSurfaceScales, [surface](const auto& e) { return e.first == surface; }) == m_mSurfaceScales.end()) + m_mSurfaceScales.emplace(surface, 1.F); - if (surface->m_mapped) - PADDON->setScale(m_surfaceScales.at(surface)); + if (surface->mapped) + PADDON->setScale(m_mSurfaceScales.at(surface)); // clean old - std::erase_if(m_surfaceScales, [](const auto& e) { return e.first.expired(); }); + std::erase_if(m_mSurfaceScales, [](const auto& e) { return e.first.expired(); }); } void CFractionalScaleProtocol::sendScale(SP surf, const float& scale) { - m_surfaceScales[surf] = scale; - if (m_addons.contains(surf)) - m_addons[surf]->setScale(scale); + m_mSurfaceScales[surf] = scale; + if (m_mAddons.contains(surf)) + m_mAddons[surf]->setScale(scale); } CFractionalScaleAddon::CFractionalScaleAddon(SP resource_, SP surf_) : m_resource(resource_), m_surface(surf_) { diff --git a/src/protocols/FractionalScale.hpp b/src/protocols/FractionalScale.hpp index c4aff560..ba896c01 100644 --- a/src/protocols/FractionalScale.hpp +++ b/src/protocols/FractionalScale.hpp @@ -49,9 +49,9 @@ class CFractionalScaleProtocol : public IWaylandProtocol { // - std::unordered_map, float> m_surfaceScales; - std::unordered_map, UP> m_addons; - std::vector> m_managers; + std::unordered_map, float> m_mSurfaceScales; + std::unordered_map, UP> m_mAddons; + std::vector> m_vManagers; friend class CFractionalScaleAddon; }; diff --git a/src/protocols/FrogColorManagement.cpp b/src/protocols/FrogColorManagement.cpp new file mode 100644 index 00000000..99bdb2bb --- /dev/null +++ b/src/protocols/FrogColorManagement.cpp @@ -0,0 +1,180 @@ +#include "FrogColorManagement.hpp" +#include "color-management-v1.hpp" +#include "macros.hpp" +#include "protocols/ColorManagement.hpp" +#include "protocols/core/Subcompositor.hpp" +#include "protocols/types/ColorManagement.hpp" + +using namespace NColorManagement; + +static wpColorManagerV1TransferFunction getWPTransferFunction(frogColorManagedSurfaceTransferFunction tf) { + switch (tf) { + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; + default: UNREACHABLE(); + } +} + +static wpColorManagerV1Primaries getWPPrimaries(frogColorManagedSurfacePrimaries primaries) { + return (wpColorManagerV1Primaries)(primaries + 1); +} + +CFrogColorManager::CFrogColorManager(SP resource_) : resource(resource_) { + if UNLIKELY (!good()) + return; + + resource->setDestroy([](CFrogColorManagementFactoryV1* r) { LOGM(TRACE, "Destroy frog_color_management at {:x} (generated default)", (uintptr_t)r); }); + resource->setOnDestroy([this](CFrogColorManagementFactoryV1* r) { PROTO::frogColorManagement->destroyResource(this); }); + + resource->setGetColorManagedSurface([](CFrogColorManagementFactoryV1* r, wl_resource* surface, uint32_t id) { + LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); + auto SURF = CWLSurfaceResource::fromResource(surface); + + if (!SURF) { + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); + r->error(-1, "Invalid surface (2)"); + return; + } + + const auto RESOURCE = PROTO::frogColorManagement->m_vSurfaces.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), SURF)); + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::frogColorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); +} + +bool CFrogColorManager::good() { + return resource->resource(); +} + +CFrogColorManagementSurface::CFrogColorManagementSurface(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if UNLIKELY (!good()) + return; + + pClient = resource->client(); + + if (!surface->colorManagement.valid()) { + const auto RESOURCE = PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared(surface_)); + if UNLIKELY (!RESOURCE) { + resource->noMemory(); + PROTO::colorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + surface->colorManagement = RESOURCE; + + resource->setOnDestroy([this](CFrogColorManagedSurface* r) { + LOGM(TRACE, "Destroy frog cm and xx cm for surface {}", (uintptr_t)surface); + if (surface.valid()) + PROTO::colorManagement->destroyResource(surface->colorManagement.get()); + PROTO::frogColorManagement->destroyResource(this); + }); + } else + resource->setOnDestroy([this](CFrogColorManagedSurface* r) { + LOGM(TRACE, "Destroy frog cm surface {}", (uintptr_t)surface); + PROTO::frogColorManagement->destroyResource(this); + }); + + resource->setDestroy([this](CFrogColorManagedSurface* r) { + LOGM(TRACE, "Destroy frog cm surface {}", (uintptr_t)surface); + PROTO::frogColorManagement->destroyResource(this); + }); + + resource->setSetKnownTransferFunction([this](CFrogColorManagedSurface* r, frogColorManagedSurfaceTransferFunction tf) { + LOGM(TRACE, "Set frog cm transfer function {} for {}", (uint32_t)tf, surface->id()); + switch (tf) { + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ: + surface->colorManagement->m_imageDescription.transferFunction = + convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ)); + break; + ; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22: + if (pqIntentSent) { + LOGM(TRACE, + "FIXME: assuming broken enum value 2 (FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_GAMMA_22) referring to eotf value 2 (TRANSFER_FUNCTION_ST2084_PQ)"); + surface->colorManagement->m_imageDescription.transferFunction = + convertTransferFunction(getWPTransferFunction(FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_ST2084_PQ)); + break; + }; + [[fallthrough]]; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_UNDEFINED: + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SCRGB_LINEAR: LOGM(TRACE, "FIXME: add tf support for {}", (uint32_t)tf); [[fallthrough]]; + case FROG_COLOR_MANAGED_SURFACE_TRANSFER_FUNCTION_SRGB: + surface->colorManagement->m_imageDescription.transferFunction = convertTransferFunction(getWPTransferFunction(tf)); + + surface->colorManagement->setHasImageDescription(true); + } + }); + resource->setSetKnownContainerColorVolume([this](CFrogColorManagedSurface* r, frogColorManagedSurfacePrimaries primariesName) { + LOGM(TRACE, "Set frog cm primaries {}", (uint32_t)primariesName); + switch (primariesName) { + case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_UNDEFINED: + case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC709: surface->colorManagement->m_imageDescription.primaries = NColorPrimaries::BT709; break; + case FROG_COLOR_MANAGED_SURFACE_PRIMARIES_REC2020: surface->colorManagement->m_imageDescription.primaries = NColorPrimaries::BT2020; break; + } + surface->colorManagement->m_imageDescription.primariesNamed = convertPrimaries(getWPPrimaries(primariesName)); + + surface->colorManagement->setHasImageDescription(true); + }); + resource->setSetRenderIntent([this](CFrogColorManagedSurface* r, frogColorManagedSurfaceRenderIntent intent) { + LOGM(TRACE, "Set frog cm intent {}", (uint32_t)intent); + pqIntentSent = intent == FROG_COLOR_MANAGED_SURFACE_RENDER_INTENT_PERCEPTUAL; + surface->colorManagement->setHasImageDescription(true); + }); + resource->setSetHdrMetadata([this](CFrogColorManagedSurface* r, uint32_t r_x, uint32_t r_y, uint32_t g_x, uint32_t g_y, uint32_t b_x, uint32_t b_y, uint32_t w_x, uint32_t w_y, + uint32_t max_lum, uint32_t min_lum, uint32_t cll, uint32_t fall) { + LOGM(TRACE, "Set frog primaries r:{},{} g:{},{} b:{},{} w:{},{} luminances {} - {} cll {} fall {}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y, min_lum, max_lum, cll, fall); + surface->colorManagement->m_imageDescription.masteringPrimaries = SPCPRimaries{.red = {.x = r_x / 50000.0f, .y = r_y / 50000.0f}, + .green = {.x = g_x / 50000.0f, .y = g_y / 50000.0f}, + .blue = {.x = b_x / 50000.0f, .y = b_y / 50000.0f}, + .white = {.x = w_x / 50000.0f, .y = w_y / 50000.0f}}; + surface->colorManagement->m_imageDescription.masteringLuminances.min = min_lum / 10000.0f; + surface->colorManagement->m_imageDescription.masteringLuminances.max = max_lum; + surface->colorManagement->m_imageDescription.maxCLL = cll; + surface->colorManagement->m_imageDescription.maxFALL = fall; + + surface->colorManagement->setHasImageDescription(true); + }); +} + +bool CFrogColorManagementSurface::good() { + return resource->resource(); +} + +wl_client* CFrogColorManagementSurface::client() { + return pClient; +} + +CFrogColorManagementProtocol::CFrogColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CFrogColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if UNLIKELY (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + LOGM(TRACE, "New frog_color_management at {:x}", (uintptr_t)RESOURCE.get()); +} + +void CFrogColorManagementProtocol::destroyResource(CFrogColorManager* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CFrogColorManagementProtocol::destroyResource(CFrogColorManagementSurface* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/FrogColorManagement.hpp b/src/protocols/FrogColorManagement.hpp new file mode 100644 index 00000000..db467b1d --- /dev/null +++ b/src/protocols/FrogColorManagement.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include +#include "WaylandProtocol.hpp" +#include "protocols/core/Compositor.hpp" +#include "frog-color-management-v1.hpp" + +class CFrogColorManager { + public: + CFrogColorManager(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CFrogColorManagementSurface { + public: + CFrogColorManagementSurface(SP resource_, SP surface_); + + bool good(); + wl_client* client(); + + WP self; + WP surface; + + bool pqIntentSent = false; + + private: + SP resource; + wl_client* pClient = nullptr; +}; + +class CFrogColorManagementProtocol : public IWaylandProtocol { + public: + CFrogColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + private: + void destroyResource(CFrogColorManager* resource); + void destroyResource(CFrogColorManagementSurface* resource); + + std::vector> m_vManagers; + std::vector> m_vSurfaces; + + friend class CFrogColorManager; + friend class CFrogColorManagementSurface; +}; + +namespace PROTO { + inline UP frogColorManagement; +}; diff --git a/src/protocols/GammaControl.cpp b/src/protocols/GammaControl.cpp index c28b881f..0a383e2e 100644 --- a/src/protocols/GammaControl.cpp +++ b/src/protocols/GammaControl.cpp @@ -6,166 +6,152 @@ #include "../render/Renderer.hpp" using namespace Hyprutils::OS; -CGammaControl::CGammaControl(SP resource_, wl_resource* output) : m_resource(resource_) { +CGammaControl::CGammaControl(SP resource_, wl_resource* output) : resource(resource_) { if UNLIKELY (!resource_->resource()) return; auto OUTPUTRES = CWLOutputResource::fromResource(output); if UNLIKELY (!OUTPUTRES) { - LOGM(Log::ERR, "No output in CGammaControl"); - m_resource->sendFailed(); + LOGM(ERR, "No output in CGammaControl"); + resource->sendFailed(); return; } - m_monitor = OUTPUTRES->m_monitor; + pMonitor = OUTPUTRES->monitor; - if UNLIKELY (!m_monitor || !m_monitor->m_output) { - LOGM(Log::ERR, "No CMonitor"); - m_resource->sendFailed(); + if UNLIKELY (!pMonitor || !pMonitor->output) { + LOGM(ERR, "No CMonitor"); + resource->sendFailed(); return; } - for (auto const& g : PROTO::gamma->m_gammaControllers) { - if UNLIKELY (g->m_monitor == m_monitor) { - m_resource->sendFailed(); + for (auto const& g : PROTO::gamma->m_vGammaControllers) { + if UNLIKELY (g->pMonitor == pMonitor) { + resource->sendFailed(); return; } } - m_gammaSize = m_monitor->m_output->getGammaSize(); + gammaSize = pMonitor->output->getGammaSize(); - if UNLIKELY (m_gammaSize <= 0) { - LOGM(Log::ERR, "Output {} doesn't support gamma", m_monitor->m_name); - m_resource->sendFailed(); + if UNLIKELY (gammaSize <= 0) { + LOGM(ERR, "Output {} doesn't support gamma", pMonitor->szName); + resource->sendFailed(); return; } - m_gammaTable.resize(m_gammaSize * 3); + gammaTable.resize(gammaSize * 3); - m_resource->setDestroy([this](CZwlrGammaControlV1* gamma) { PROTO::gamma->destroyGammaControl(this); }); - m_resource->setOnDestroy([this](CZwlrGammaControlV1* gamma) { PROTO::gamma->destroyGammaControl(this); }); + resource->setDestroy([this](CZwlrGammaControlV1* gamma) { PROTO::gamma->destroyGammaControl(this); }); + resource->setOnDestroy([this](CZwlrGammaControlV1* gamma) { PROTO::gamma->destroyGammaControl(this); }); - m_resource->setSetGamma([this](CZwlrGammaControlV1* gamma, int32_t fd) { + resource->setSetGamma([this](CZwlrGammaControlV1* gamma, int32_t fd) { CFileDescriptor gammaFd{fd}; - if UNLIKELY (!m_monitor) { - LOGM(Log::ERR, "setGamma for a dead monitor"); - m_resource->sendFailed(); + if UNLIKELY (!pMonitor) { + LOGM(ERR, "setGamma for a dead monitor"); + resource->sendFailed(); return; } - LOGM(Log::DEBUG, "setGamma for {}", m_monitor->m_name); - - if UNLIKELY (m_monitor->gammaRampsInUse()) { - LOGM(Log::ERR, "Monitor has gamma ramps in use (ICC?)"); - m_resource->sendFailed(); - return; - } + LOGM(LOG, "setGamma for {}", pMonitor->szName); // TODO: make CFileDescriptor getflags use F_GETFL int fdFlags = fcntl(gammaFd.get(), F_GETFL, 0); if UNLIKELY (fdFlags < 0) { - LOGM(Log::ERR, "Failed to get fd flags"); - m_resource->sendFailed(); + LOGM(ERR, "Failed to get fd flags"); + resource->sendFailed(); return; } // TODO: make CFileDescriptor setflags use F_SETFL if UNLIKELY (fcntl(gammaFd.get(), F_SETFL, fdFlags | O_NONBLOCK) < 0) { - LOGM(Log::ERR, "Failed to set fd flags"); - m_resource->sendFailed(); + LOGM(ERR, "Failed to set fd flags"); + resource->sendFailed(); return; } - ssize_t readBytes = read(gammaFd.get(), m_gammaTable.data(), m_gammaTable.size() * sizeof(uint16_t)); + ssize_t readBytes = pread(gammaFd.get(), gammaTable.data(), gammaTable.size() * sizeof(uint16_t), 0); + if (readBytes < 0 || (size_t)readBytes != gammaTable.size() * sizeof(uint16_t)) { + LOGM(ERR, "Failed to read bytes"); - ssize_t moreBytes = 0; - { - const size_t BUF_SIZE = 1; - char buf[BUF_SIZE] = {}; - moreBytes = read(gammaFd.get(), buf, BUF_SIZE); - } - - if (readBytes < 0 || sc(readBytes) != m_gammaTable.size() * sizeof(uint16_t) || moreBytes != 0) { - LOGM(Log::ERR, "Failed to read bytes"); - - if (sc(readBytes) != m_gammaTable.size() * sizeof(uint16_t) || moreBytes > 0) { + if ((size_t)readBytes != gammaTable.size() * sizeof(uint16_t)) { gamma->error(ZWLR_GAMMA_CONTROL_V1_ERROR_INVALID_GAMMA, "Gamma ramps size mismatch"); return; } - m_resource->sendFailed(); + resource->sendFailed(); return; } - m_gammaTableSet = true; + gammaTableSet = true; // translate the table to AQ format std::vector red, green, blue; - red.resize(m_gammaTable.size() / 3); - green.resize(m_gammaTable.size() / 3); - blue.resize(m_gammaTable.size() / 3); - for (size_t i = 0; i < m_gammaTable.size() / 3; ++i) { - red.at(i) = m_gammaTable.at(i); - green.at(i) = m_gammaTable.at(m_gammaTable.size() / 3 + i); - blue.at(i) = m_gammaTable.at((m_gammaTable.size() / 3) * 2 + i); + red.resize(gammaTable.size() / 3); + green.resize(gammaTable.size() / 3); + blue.resize(gammaTable.size() / 3); + for (size_t i = 0; i < gammaTable.size() / 3; ++i) { + red.at(i) = gammaTable.at(i); + green.at(i) = gammaTable.at(gammaTable.size() / 3 + i); + blue.at(i) = gammaTable.at((gammaTable.size() / 3) * 2 + i); } - for (size_t i = 0; i < m_gammaTable.size() / 3; ++i) { - m_gammaTable.at(i * 3) = red.at(i); - m_gammaTable.at(i * 3 + 1) = green.at(i); - m_gammaTable.at(i * 3 + 2) = blue.at(i); + for (size_t i = 0; i < gammaTable.size() / 3; ++i) { + gammaTable.at(i * 3) = red.at(i); + gammaTable.at(i * 3 + 1) = green.at(i); + gammaTable.at(i * 3 + 2) = blue.at(i); } applyToMonitor(); }); - m_resource->sendGammaSize(m_gammaSize); + resource->sendGammaSize(gammaSize); - m_listeners.monitorDestroy = m_monitor->m_events.destroy.listen([this] { this->onMonitorDestroy(); }); - m_listeners.monitorDisconnect = m_monitor->m_events.disconnect.listen([this] { this->onMonitorDestroy(); }); + listeners.monitorDestroy = pMonitor->events.destroy.registerListener([this](std::any) { this->onMonitorDestroy(); }); + listeners.monitorDisconnect = pMonitor->events.disconnect.registerListener([this](std::any) { this->onMonitorDestroy(); }); } CGammaControl::~CGammaControl() { - if (!m_gammaTableSet || !m_monitor || !m_monitor->m_output) + if (!gammaTableSet || !pMonitor || !pMonitor->output) return; // reset the LUT if the client dies for whatever reason and doesn't unset the gamma - m_monitor->m_output->state->setGammaLut({}); + pMonitor->output->state->setGammaLut({}); } bool CGammaControl::good() { - return m_resource->resource(); + return resource->resource(); } void CGammaControl::applyToMonitor() { - if UNLIKELY (!m_monitor || !m_monitor->m_output) + if UNLIKELY (!pMonitor || !pMonitor->output) return; // ?? - LOGM(Log::DEBUG, "setting to monitor {}", m_monitor->m_name); + LOGM(LOG, "setting to monitor {}", pMonitor->szName); - if (!m_gammaTableSet) { - m_monitor->m_output->state->setGammaLut({}); + if (!gammaTableSet) { + pMonitor->output->state->setGammaLut({}); return; } - m_monitor->m_output->state->setGammaLut(m_gammaTable); + pMonitor->output->state->setGammaLut(gammaTable); - if (!m_monitor->m_state.test()) { - LOGM(Log::DEBUG, "setting to monitor {} failed", m_monitor->m_name); - m_monitor->m_output->state->setGammaLut({}); + if (!pMonitor->state.test()) { + LOGM(LOG, "setting to monitor {} failed", pMonitor->szName); + pMonitor->output->state->setGammaLut({}); } - g_pHyprRenderer->damageMonitor(m_monitor.lock()); + g_pHyprRenderer->damageMonitor(pMonitor.lock()); } PHLMONITOR CGammaControl::getMonitor() { - return m_monitor ? m_monitor.lock() : nullptr; + return pMonitor ? pMonitor.lock() : nullptr; } void CGammaControl::onMonitorDestroy() { - LOGM(Log::DEBUG, "Destroying gamma control for {}", m_monitor->m_name); - m_resource->sendFailed(); + LOGM(LOG, "Destroying gamma control for {}", pMonitor->szName); + resource->sendFailed(); } CGammaControlProtocol::CGammaControlProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -173,7 +159,7 @@ CGammaControlProtocol::CGammaControlProtocol(const wl_interface* iface, const in } void CGammaControlProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwlrGammaControlManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwlrGammaControlManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -181,26 +167,26 @@ void CGammaControlProtocol::bindManager(wl_client* client, void* data, uint32_t } void CGammaControlProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CGammaControlProtocol::destroyGammaControl(CGammaControl* gamma) { - std::erase_if(m_gammaControllers, [&](const auto& other) { return other.get() == gamma; }); + std::erase_if(m_vGammaControllers, [&](const auto& other) { return other.get() == gamma; }); } void CGammaControlProtocol::onGetGammaControl(CZwlrGammaControlManagerV1* pMgr, uint32_t id, wl_resource* output) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_gammaControllers.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id), output)).get(); + const auto RESOURCE = m_vGammaControllers.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id), output)).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_gammaControllers.pop_back(); + m_vGammaControllers.pop_back(); return; } } void CGammaControlProtocol::applyGammaToState(PHLMONITOR pMonitor) { - for (auto const& g : m_gammaControllers) { + for (auto const& g : m_vGammaControllers) { if (g->getMonitor() != pMonitor) continue; diff --git a/src/protocols/GammaControl.hpp b/src/protocols/GammaControl.hpp index 525f5b67..9e21ef08 100644 --- a/src/protocols/GammaControl.hpp +++ b/src/protocols/GammaControl.hpp @@ -18,18 +18,18 @@ class CGammaControl { PHLMONITOR getMonitor(); private: - SP m_resource; - PHLMONITORREF m_monitor; - size_t m_gammaSize = 0; - bool m_gammaTableSet = false; - std::vector m_gammaTable; // [r,g,b]+ + SP resource; + PHLMONITORREF pMonitor; + size_t gammaSize = 0; + bool gammaTableSet = false; + std::vector gammaTable; // [r,g,b]+ void onMonitorDestroy(); struct { CHyprSignalListener monitorDisconnect; CHyprSignalListener monitorDestroy; - } m_listeners; + } listeners; }; class CGammaControlProtocol : public IWaylandProtocol { @@ -46,8 +46,8 @@ class CGammaControlProtocol : public IWaylandProtocol { void onGetGammaControl(CZwlrGammaControlManagerV1* pMgr, uint32_t id, wl_resource* output); // - std::vector> m_managers; - std::vector> m_gammaControllers; + std::vector> m_vManagers; + std::vector> m_vGammaControllers; friend class CGammaControl; }; diff --git a/src/protocols/GlobalShortcuts.cpp b/src/protocols/GlobalShortcuts.cpp index ee961b74..9f8f422c 100644 --- a/src/protocols/GlobalShortcuts.cpp +++ b/src/protocols/GlobalShortcuts.cpp @@ -1,21 +1,20 @@ #include "GlobalShortcuts.hpp" -#include "../helpers/time/Time.hpp" -CShortcutClient::CShortcutClient(SP resource_) : m_resource(resource_) { +CShortcutClient::CShortcutClient(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CHyprlandGlobalShortcutsManagerV1* pMgr) { PROTO::globalShortcuts->destroyResource(this); }); - m_resource->setDestroy([this](CHyprlandGlobalShortcutsManagerV1* pMgr) { PROTO::globalShortcuts->destroyResource(this); }); + resource->setOnDestroy([this](CHyprlandGlobalShortcutsManagerV1* pMgr) { PROTO::globalShortcuts->destroyResource(this); }); + resource->setDestroy([this](CHyprlandGlobalShortcutsManagerV1* pMgr) { PROTO::globalShortcuts->destroyResource(this); }); - m_resource->setRegisterShortcut([this](CHyprlandGlobalShortcutsManagerV1* pMgr, uint32_t shortcut, const char* id, const char* app_id, const char* description, - const char* trigger_description) { + resource->setRegisterShortcut([this](CHyprlandGlobalShortcutsManagerV1* pMgr, uint32_t shortcut, const char* id, const char* app_id, const char* description, + const char* trigger_description) { if UNLIKELY (PROTO::globalShortcuts->isTaken(id, app_id)) { - m_resource->error(HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ALREADY_TAKEN, "Combination is taken"); + resource->error(HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ALREADY_TAKEN, "Combination is taken"); return; } - const auto PSHORTCUT = m_shortcuts.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), shortcut))); + const auto PSHORTCUT = shortcuts.emplace_back(makeShared(makeShared(resource->client(), resource->version(), shortcut))); PSHORTCUT->id = id; PSHORTCUT->description = description; PSHORTCUT->appid = app_id; @@ -23,16 +22,16 @@ CShortcutClient::CShortcutClient(SP resource_ if UNLIKELY (!PSHORTCUT->resource->resource()) { PSHORTCUT->resource->noMemory(); - m_shortcuts.pop_back(); + shortcuts.pop_back(); return; } - PSHORTCUT->resource->setDestroy([this](CHyprlandGlobalShortcutV1* pMgr) { std::erase_if(m_shortcuts, [&](const auto& other) { return other->resource.get() == pMgr; }); }); + PSHORTCUT->resource->setDestroy([this](CHyprlandGlobalShortcutV1* pMgr) { std::erase_if(shortcuts, [&](const auto& other) { return other->resource.get() == pMgr; }); }); }); } bool CShortcutClient::good() { - return m_resource->resource(); + return resource->resource(); } CGlobalShortcutsProtocol::CGlobalShortcutsProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -40,22 +39,22 @@ CGlobalShortcutsProtocol::CGlobalShortcutsProtocol(const wl_interface* iface, co } void CGlobalShortcutsProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_clients.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESROUCE = m_vClients.emplace_back(makeShared(makeShared(client, ver, id))); - if UNLIKELY (!RESOURCE->good()) { + if UNLIKELY (!RESROUCE->good()) { wl_client_post_no_memory(client); - m_clients.pop_back(); + m_vClients.pop_back(); return; } } void CGlobalShortcutsProtocol::destroyResource(CShortcutClient* client) { - std::erase_if(m_clients, [&](const auto& other) { return other.get() == client; }); + std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; }); } bool CGlobalShortcutsProtocol::isTaken(std::string appid, std::string trigger) { - for (auto const& c : m_clients) { - for (auto const& sh : c->m_shortcuts) { + for (auto const& c : m_vClients) { + for (auto const& sh : c->shortcuts) { if (sh->appid == appid && sh->id == trigger) { return true; } @@ -66,16 +65,17 @@ bool CGlobalShortcutsProtocol::isTaken(std::string appid, std::string trigger) { } void CGlobalShortcutsProtocol::sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed) { - for (auto const& c : m_clients) { - for (auto const& sh : c->m_shortcuts) { + for (auto const& c : m_vClients) { + for (auto const& sh : c->shortcuts) { if (sh->appid == appid && sh->id == trigger) { - const auto [sec, nsec] = Time::secNsec(Time::steadyNow()); - uint32_t tvSecHi = (sizeof(sec) > 4) ? sec >> 32 : 0; - uint32_t tvSecLo = sec & 0xFFFFFFFF; + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0; + uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF; if (pressed) - sh->resource->sendPressed(tvSecHi, tvSecLo, nsec); + sh->resource->sendPressed(tvSecHi, tvSecLo, now.tv_nsec); else - sh->resource->sendReleased(tvSecHi, tvSecLo, nsec); + sh->resource->sendReleased(tvSecHi, tvSecLo, now.tv_nsec); } } } @@ -87,15 +87,15 @@ std::vector CGlobalShortcutsProtocol::getAllShortcuts() { // and potential reallocation is more costly then the added precompute overhead of looping // and finding the total size. size_t totalShortcuts = 0; - for (const auto& c : m_clients) { - totalShortcuts += c->m_shortcuts.size(); + for (const auto& c : m_vClients) { + totalShortcuts += c->shortcuts.size(); } // reserve number of elements to avoid reallocations copy.reserve(totalShortcuts); - for (const auto& c : m_clients) { - for (const auto& sh : c->m_shortcuts) { + for (const auto& c : m_vClients) { + for (const auto& sh : c->shortcuts) { copy.push_back(*sh); } } diff --git a/src/protocols/GlobalShortcuts.hpp b/src/protocols/GlobalShortcuts.hpp index a4d5baf9..14f6ee0b 100644 --- a/src/protocols/GlobalShortcuts.hpp +++ b/src/protocols/GlobalShortcuts.hpp @@ -17,8 +17,8 @@ class CShortcutClient { bool good(); private: - SP m_resource; - std::vector> m_shortcuts; + SP resource; + std::vector> shortcuts; friend class CGlobalShortcutsProtocol; }; @@ -35,7 +35,7 @@ class CGlobalShortcutsProtocol : IWaylandProtocol { std::vector getAllShortcuts(); private: - std::vector> m_clients; + std::vector> m_vClients; }; namespace PROTO { diff --git a/src/protocols/HyprlandSurface.cpp b/src/protocols/HyprlandSurface.cpp index 5d882206..8b65f0fa 100644 --- a/src/protocols/HyprlandSurface.cpp +++ b/src/protocols/HyprlandSurface.cpp @@ -1,88 +1,88 @@ #include "HyprlandSurface.hpp" -#include "../desktop/view/WLSurface.hpp" +#include "../desktop/WLSurface.hpp" #include "../render/Renderer.hpp" #include "core/Compositor.hpp" #include "hyprland-surface-v1.hpp" #include #include -CHyprlandSurface::CHyprlandSurface(SP resource, SP surface) : m_surface(surface) { +CHyprlandSurface::CHyprlandSurface(SP resource, SP surface) : m_pSurface(surface) { setResource(std::move(resource)); } bool CHyprlandSurface::good() const { - return m_resource->resource(); + return m_pResource->resource(); } void CHyprlandSurface::setResource(SP resource) { - m_resource = std::move(resource); + m_pResource = std::move(resource); - if UNLIKELY (!m_resource->resource()) + if UNLIKELY (!m_pResource->resource()) return; - m_resource->setDestroy([this](CHyprlandSurfaceV1* resource) { destroy(); }); - m_resource->setOnDestroy([this](CHyprlandSurfaceV1* resource) { destroy(); }); + m_pResource->setDestroy([this](CHyprlandSurfaceV1* resource) { destroy(); }); + m_pResource->setOnDestroy([this](CHyprlandSurfaceV1* resource) { destroy(); }); - m_resource->setSetOpacity([this](CHyprlandSurfaceV1* resource, uint32_t opacity) { - if UNLIKELY (!m_surface) { - m_resource->error(HYPRLAND_SURFACE_V1_ERROR_NO_SURFACE, "set_opacity called for destroyed wl_surface"); + m_pResource->setSetOpacity([this](CHyprlandSurfaceV1* resource, uint32_t opacity) { + if UNLIKELY (!m_pSurface) { + m_pResource->error(HYPRLAND_SURFACE_V1_ERROR_NO_SURFACE, "set_opacity called for destroyed wl_surface"); return; } auto fOpacity = wl_fixed_to_double(opacity); if UNLIKELY (fOpacity < 0.0 || fOpacity > 1.0) { - m_resource->error(HYPRLAND_SURFACE_V1_ERROR_OUT_OF_RANGE, "set_opacity called with an opacity value larger than 1.0 or smaller than 0.0."); + m_pResource->error(HYPRLAND_SURFACE_V1_ERROR_OUT_OF_RANGE, "set_opacity called with an opacity value larger than 1.0 or smaller than 0.0."); return; } - m_opacity = fOpacity; + m_fOpacity = fOpacity; }); - m_resource->setSetVisibleRegion([this](CHyprlandSurfaceV1* resource, wl_resource* region) { + m_pResource->setSetVisibleRegion([this](CHyprlandSurfaceV1* resource, wl_resource* region) { if (!region) { if (!m_visibleRegion.empty()) - m_visibleRegionChanged = true; + m_bVisibleRegionChanged = true; m_visibleRegion.clear(); return; } - m_visibleRegionChanged = true; - m_visibleRegion = CWLRegionResource::fromResource(region)->m_region; + m_bVisibleRegionChanged = true; + m_visibleRegion = CWLRegionResource::fromResource(region)->region; }); - m_listeners.surfaceCommitted = m_surface->m_events.commit.listen([this] { - auto surface = Desktop::View::CWLSurface::fromResource(m_surface.lock()); + listeners.surfaceCommitted = m_pSurface->events.commit.registerListener([this](std::any data) { + auto surface = CWLSurface::fromResource(m_pSurface.lock()); - if (surface && (surface->m_overallOpacity != m_opacity || m_visibleRegionChanged)) { - surface->m_overallOpacity = m_opacity; - surface->m_visibleRegion = m_visibleRegion; - auto box = surface->getSurfaceBoxGlobal(); + if (surface && (surface->m_fOverallOpacity != m_fOpacity || m_bVisibleRegionChanged)) { + surface->m_fOverallOpacity = m_fOpacity; + surface->m_visibleRegion = m_visibleRegion; + auto box = surface->getSurfaceBoxGlobal(); if (box.has_value()) g_pHyprRenderer->damageBox(*box); - if (!m_resource) + if (!m_pResource) PROTO::hyprlandSurface->destroySurface(this); } }); - m_listeners.surfaceDestroyed = m_surface->m_events.destroy.listen([this] { - if (!m_resource) + listeners.surfaceDestroyed = m_pSurface->events.destroy.registerListener([this](std::any data) { + if (!m_pResource) PROTO::hyprlandSurface->destroySurface(this); }); } void CHyprlandSurface::destroy() { - m_resource.reset(); - m_opacity = 1.F; + m_pResource.reset(); + m_fOpacity = 1.F; if (!m_visibleRegion.empty()) - m_visibleRegionChanged = true; + m_bVisibleRegionChanged = true; m_visibleRegion.clear(); - if (!m_surface) + if (!m_pSurface) PROTO::hyprlandSurface->destroySurface(this); } @@ -91,7 +91,7 @@ CHyprlandSurfaceProtocol::CHyprlandSurfaceProtocol(const wl_interface* iface, co } void CHyprlandSurfaceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - auto manager = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + auto manager = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); manager->setOnDestroy([this](CHyprlandSurfaceManagerV1* manager) { destroyManager(manager); }); manager->setDestroy([this](CHyprlandSurfaceManagerV1* manager) { destroyManager(manager); }); @@ -100,20 +100,20 @@ void CHyprlandSurfaceProtocol::bindManager(wl_client* client, void* data, uint32 } void CHyprlandSurfaceProtocol::destroyManager(CHyprlandSurfaceManagerV1* manager) { - std::erase_if(m_managers, [&](const auto& p) { return p.get() == manager; }); + std::erase_if(m_vManagers, [&](const auto& p) { return p.get() == manager; }); } void CHyprlandSurfaceProtocol::destroySurface(CHyprlandSurface* surface) { - std::erase_if(m_surfaces, [&](const auto& entry) { return entry.second.get() == surface; }); + std::erase_if(m_mSurfaces, [&](const auto& entry) { return entry.second.get() == surface; }); } void CHyprlandSurfaceProtocol::getSurface(CHyprlandSurfaceManagerV1* manager, uint32_t id, SP surface) { CHyprlandSurface* hyprlandSurface = nullptr; - auto iter = std::ranges::find_if(m_surfaces, [&](const auto& entry) { return entry.second->m_surface == surface; }); + auto iter = std::find_if(m_mSurfaces.begin(), m_mSurfaces.end(), [&](const auto& entry) { return entry.second->m_pSurface == surface; }); - if (iter != m_surfaces.end()) { - if (iter->second->m_resource) { - LOGM(Log::ERR, "HyprlandSurface already present for surface {:x}", (uintptr_t)surface.get()); + if (iter != m_mSurfaces.end()) { + if (iter->second->m_pResource) { + LOGM(ERR, "HyprlandSurface already present for surface {:x}", (uintptr_t)surface.get()); manager->error(HYPRLAND_SURFACE_MANAGER_V1_ERROR_ALREADY_CONSTRUCTED, "HyprlandSurface already present"); return; } else { @@ -122,11 +122,11 @@ void CHyprlandSurfaceProtocol::getSurface(CHyprlandSurfaceManagerV1* manager, ui } } else { hyprlandSurface = - m_surfaces.emplace(surface, makeUnique(makeShared(manager->client(), manager->version(), id), surface)).first->second.get(); + m_mSurfaces.emplace(surface, makeUnique(makeShared(manager->client(), manager->version(), id), surface)).first->second.get(); } if UNLIKELY (!hyprlandSurface->good()) { manager->noMemory(); - m_surfaces.erase(surface); + m_mSurfaces.erase(surface); } } diff --git a/src/protocols/HyprlandSurface.hpp b/src/protocols/HyprlandSurface.hpp index 5402e916..5c1181c4 100644 --- a/src/protocols/HyprlandSurface.hpp +++ b/src/protocols/HyprlandSurface.hpp @@ -18,10 +18,10 @@ class CHyprlandSurface { void setResource(SP resource); private: - SP m_resource; - WP m_surface; - float m_opacity = 1.0; - bool m_visibleRegionChanged = false; + SP m_pResource; + WP m_pSurface; + float m_fOpacity = 1.0; + bool m_bVisibleRegionChanged = false; CRegion m_visibleRegion; void destroy(); @@ -29,7 +29,7 @@ class CHyprlandSurface { struct { CHyprSignalListener surfaceCommitted; CHyprSignalListener surfaceDestroyed; - } m_listeners; + } listeners; friend class CHyprlandSurfaceProtocol; }; @@ -45,8 +45,8 @@ class CHyprlandSurfaceProtocol : public IWaylandProtocol { void destroySurface(CHyprlandSurface* surface); void getSurface(CHyprlandSurfaceManagerV1* manager, uint32_t id, SP surface); - std::vector> m_managers; - std::unordered_map, UP> m_surfaces; + std::vector> m_vManagers; + std::unordered_map, UP> m_mSurfaces; friend class CHyprlandSurface; }; diff --git a/src/protocols/IdleInhibit.cpp b/src/protocols/IdleInhibit.cpp index e7e0e676..a015cdd0 100644 --- a/src/protocols/IdleInhibit.cpp +++ b/src/protocols/IdleInhibit.cpp @@ -1,25 +1,25 @@ #include "IdleInhibit.hpp" #include "core/Compositor.hpp" -CIdleInhibitor::CIdleInhibitor(SP resource_, SP surf_) : m_resource(resource_), m_surface(surf_) { +CIdleInhibitor::CIdleInhibitor(SP resource_, SP surf_) : resource(resource_), surface(surf_) { ; } -CIdleInhibitorResource::CIdleInhibitorResource(SP resource_, SP surface_) : m_resource(resource_), m_surface(surface_) { - m_listeners.destroySurface = m_surface->m_events.destroy.listen([this] { - m_surface.reset(); - m_listeners.destroySurface.reset(); - m_destroySent = true; - m_events.destroy.emit(); +CIdleInhibitorResource::CIdleInhibitorResource(SP resource_, SP surface_) : resource(resource_), surface(surface_) { + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { + surface.reset(); + listeners.destroySurface.reset(); + destroySent = true; + events.destroy.emit(); }); - m_resource->setOnDestroy([this](CZwpIdleInhibitorV1* p) { PROTO::idleInhibit->removeInhibitor(this); }); - m_resource->setDestroy([this](CZwpIdleInhibitorV1* p) { PROTO::idleInhibit->removeInhibitor(this); }); + resource->setOnDestroy([this](CZwpIdleInhibitorV1* p) { PROTO::idleInhibit->removeInhibitor(this); }); + resource->setDestroy([this](CZwpIdleInhibitorV1* p) { PROTO::idleInhibit->removeInhibitor(this); }); } CIdleInhibitorResource::~CIdleInhibitorResource() { - if (!m_destroySent) - m_events.destroy.emit(); + if (!destroySent) + events.destroy.emit(); } CIdleInhibitProtocol::CIdleInhibitProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -27,11 +27,11 @@ CIdleInhibitProtocol::CIdleInhibitProtocol(const wl_interface* iface, const int& } void CIdleInhibitProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [res](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [res](const auto& other) { return other->resource() == res; }); } void CIdleInhibitProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpIdleInhibitManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwpIdleInhibitManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -40,13 +40,13 @@ void CIdleInhibitProtocol::bindManager(wl_client* client, void* data, uint32_t v } void CIdleInhibitProtocol::removeInhibitor(CIdleInhibitorResource* resource) { - std::erase_if(m_inhibitors, [resource](const auto& el) { return el.get() == resource; }); + std::erase_if(m_vInhibitors, [resource](const auto& el) { return el.get() == resource; }); } void CIdleInhibitProtocol::onCreateInhibitor(CZwpIdleInhibitManagerV1* pMgr, uint32_t id, SP surface) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_inhibitors.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), surface)); + const auto RESOURCE = m_vInhibitors.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), surface)); - RESOURCE->m_inhibitor = makeShared(RESOURCE, surface); - m_events.newIdleInhibitor.emit(RESOURCE->m_inhibitor); -} + RESOURCE->inhibitor = makeShared(RESOURCE, surface); + events.newIdleInhibitor.emit(RESOURCE->inhibitor); +} \ No newline at end of file diff --git a/src/protocols/IdleInhibit.hpp b/src/protocols/IdleInhibit.hpp index e04b6725..2e024a7a 100644 --- a/src/protocols/IdleInhibit.hpp +++ b/src/protocols/IdleInhibit.hpp @@ -14,10 +14,10 @@ class CIdleInhibitor { struct { CHyprSignalListener destroy; - } m_listeners; + } listeners; - WP m_resource; - WP m_surface; + WP resource; + WP surface; }; class CIdleInhibitorResource { @@ -25,20 +25,20 @@ class CIdleInhibitorResource { CIdleInhibitorResource(SP resource_, SP surface_); ~CIdleInhibitorResource(); - SP m_inhibitor; + SP inhibitor; struct { - CSignalT<> destroy; - } m_events; + CSignal destroy; + } events; private: - SP m_resource; - WP m_surface; - bool m_destroySent = false; + SP resource; + WP surface; + bool destroySent = false; struct { CHyprSignalListener destroySurface; - } m_listeners; + } listeners; }; class CIdleInhibitProtocol : public IWaylandProtocol { @@ -48,8 +48,8 @@ class CIdleInhibitProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); struct { - CSignalT> newIdleInhibitor; - } m_events; + CSignal newIdleInhibitor; // data: SP + } events; private: void onManagerResourceDestroy(wl_resource* res); @@ -58,8 +58,8 @@ class CIdleInhibitProtocol : public IWaylandProtocol { void removeInhibitor(CIdleInhibitorResource*); // - std::vector> m_managers; - std::vector> m_inhibitors; + std::vector> m_vManagers; + std::vector> m_vInhibitors; friend class CIdleInhibitorResource; }; diff --git a/src/protocols/IdleNotify.cpp b/src/protocols/IdleNotify.cpp index b80bf5c6..14a5f3e1 100644 --- a/src/protocols/IdleNotify.cpp +++ b/src/protocols/IdleNotify.cpp @@ -3,7 +3,7 @@ static int onTimer(SP self, void* data) { - const auto NOTIF = sc(data); + const auto NOTIF = (CExtIdleNotification*)data; NOTIF->onTimerFired(); @@ -11,67 +11,52 @@ static int onTimer(SP self, void* data) { } CExtIdleNotification::CExtIdleNotification(SP resource_, uint32_t timeoutMs_, bool obeyInhibitors_) : - m_resource(resource_), m_timeoutMs(timeoutMs_), m_obeyInhibitors(obeyInhibitors_) { + resource(resource_), timeoutMs(timeoutMs_), obeyInhibitors(obeyInhibitors_) { if UNLIKELY (!resource_->resource()) return; - m_resource->setDestroy([this](CExtIdleNotificationV1* r) { PROTO::idle->destroyNotification(this); }); - m_resource->setOnDestroy([this](CExtIdleNotificationV1* r) { PROTO::idle->destroyNotification(this); }); + resource->setDestroy([this](CExtIdleNotificationV1* r) { PROTO::idle->destroyNotification(this); }); + resource->setOnDestroy([this](CExtIdleNotificationV1* r) { PROTO::idle->destroyNotification(this); }); - m_timer = makeShared(std::nullopt, onTimer, this); - g_pEventLoopManager->addTimer(m_timer); + timer = makeShared(std::nullopt, onTimer, this); + g_pEventLoopManager->addTimer(timer); - update(); + updateTimer(); - LOGM(Log::DEBUG, "Registered idle-notification for {}ms", timeoutMs_); + LOGM(LOG, "Registered idle-notification for {}ms", timeoutMs_); } CExtIdleNotification::~CExtIdleNotification() { - g_pEventLoopManager->removeTimer(m_timer); - m_timer.reset(); + g_pEventLoopManager->removeTimer(timer); + timer.reset(); } bool CExtIdleNotification::good() { - return m_resource->resource(); + return resource->resource(); } -void CExtIdleNotification::update(uint32_t elapsedMs) { - m_timer->updateTimeout(std::nullopt); - - if (elapsedMs == 0 && PROTO::idle->isInhibited && m_obeyInhibitors) { - reset(); - return; - } - - if (m_timeoutMs > elapsedMs) { - reset(); - m_timer->updateTimeout(std::chrono::milliseconds(m_timeoutMs - elapsedMs)); - } else - onTimerFired(); -} - -void CExtIdleNotification::update() { - update(0); +void CExtIdleNotification::updateTimer() { + if (PROTO::idle->isInhibited && obeyInhibitors) + timer->updateTimeout(std::nullopt); + else + timer->updateTimeout(std::chrono::milliseconds(timeoutMs)); } void CExtIdleNotification::onTimerFired() { - if (m_idled) - return; - - m_resource->sendIdled(); - m_idled = true; + resource->sendIdled(); + idled = true; } -void CExtIdleNotification::reset() { - if (!m_idled) - return; +void CExtIdleNotification::onActivity() { + if (idled) + resource->sendResumed(); - m_resource->sendResumed(); - m_idled = false; + idled = false; + updateTimer(); } bool CExtIdleNotification::inhibitorsAreObeyed() const { - return m_obeyInhibitors; + return obeyInhibitors; } CIdleNotifyProtocol::CIdleNotifyProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -79,7 +64,7 @@ CIdleNotifyProtocol::CIdleNotifyProtocol(const wl_interface* iface, const int& v } void CIdleNotifyProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CExtIdleNotifierV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CExtIdleNotifierV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -90,41 +75,35 @@ void CIdleNotifyProtocol::bindManager(wl_client* client, void* data, uint32_t ve } void CIdleNotifyProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CIdleNotifyProtocol::destroyNotification(CExtIdleNotification* notif) { - std::erase_if(m_notifications, [&](const auto& other) { return other.get() == notif; }); + std::erase_if(m_vNotifications, [&](const auto& other) { return other.get() == notif; }); } void CIdleNotifyProtocol::onGetNotification(CExtIdleNotifierV1* pMgr, uint32_t id, uint32_t timeout, wl_resource* seat, bool obeyInhibitors) { const auto CLIENT = pMgr->client(); const auto RESOURCE = - m_notifications.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), timeout, obeyInhibitors)).get(); + m_vNotifications.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), timeout, obeyInhibitors)).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_notifications.pop_back(); + m_vNotifications.pop_back(); return; } } void CIdleNotifyProtocol::onActivity() { - for (auto const& n : m_notifications) { - n->update(); + for (auto const& n : m_vNotifications) { + n->onActivity(); } } void CIdleNotifyProtocol::setInhibit(bool inhibited) { isInhibited = inhibited; - for (auto const& n : m_notifications) { + for (auto const& n : m_vNotifications) { if (n->inhibitorsAreObeyed()) - n->update(); - } -} - -void CIdleNotifyProtocol::setTimers(uint32_t elapsedMs) { - for (auto const& n : m_notifications) { - n->update(elapsedMs); + n->onActivity(); } } diff --git a/src/protocols/IdleNotify.hpp b/src/protocols/IdleNotify.hpp index 0e0433a4..efc3accc 100644 --- a/src/protocols/IdleNotify.hpp +++ b/src/protocols/IdleNotify.hpp @@ -14,22 +14,19 @@ class CExtIdleNotification { bool good(); void onTimerFired(); + void onActivity(); bool inhibitorsAreObeyed() const; private: - SP m_resource; - uint32_t m_timeoutMs = 0; - SP m_timer; + SP resource; + uint32_t timeoutMs = 0; + SP timer; - bool m_idled = false; - bool m_obeyInhibitors = false; + bool idled = false; + bool obeyInhibitors = false; - void reset(); - void update(); - void update(uint32_t elapsedMs); - - friend class CIdleNotifyProtocol; + void updateTimer(); }; class CIdleNotifyProtocol : public IWaylandProtocol { @@ -40,7 +37,6 @@ class CIdleNotifyProtocol : public IWaylandProtocol { void onActivity(); void setInhibit(bool inhibited); - void setTimers(uint32_t elapsedMs); private: void onManagerResourceDestroy(wl_resource* res); @@ -50,8 +46,8 @@ class CIdleNotifyProtocol : public IWaylandProtocol { bool isInhibited = false; // - std::vector> m_managers; - std::vector> m_notifications; + std::vector> m_vManagers; + std::vector> m_vNotifications; friend class CExtIdleNotification; }; diff --git a/src/protocols/ImageCaptureSource.cpp b/src/protocols/ImageCaptureSource.cpp deleted file mode 100644 index 9f54533e..00000000 --- a/src/protocols/ImageCaptureSource.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "ImageCaptureSource.hpp" -#include "core/Output.hpp" -#include "../helpers/Monitor.hpp" -#include "../desktop/view/Window.hpp" -#include "ForeignToplevel.hpp" - -CImageCaptureSource::CImageCaptureSource(SP resource, PHLMONITOR pMonitor) : m_resource(resource), m_monitor(pMonitor) { - if UNLIKELY (!good()) - return; - - m_resource->setData(this); - m_resource->setDestroy([this](CExtImageCaptureSourceV1* pMgr) { PROTO::imageCaptureSource->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtImageCaptureSourceV1* pMgr) { PROTO::imageCaptureSource->destroyResource(this); }); -} - -CImageCaptureSource::CImageCaptureSource(SP resource, PHLWINDOW pWindow) : m_resource(resource), m_window(pWindow) { - if UNLIKELY (!good()) - return; - - m_resource->setData(this); - m_resource->setDestroy([this](CExtImageCaptureSourceV1* pMgr) { PROTO::imageCaptureSource->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtImageCaptureSourceV1* pMgr) { PROTO::imageCaptureSource->destroyResource(this); }); -} - -bool CImageCaptureSource::good() { - return m_resource && m_resource->resource(); -} - -std::string CImageCaptureSource::getName() { - if (!m_monitor.expired()) - return m_monitor->m_name; - if (!m_window.expired()) - return m_window->m_title; - - return "error"; -} - -std::string CImageCaptureSource::getTypeName() { - if (!m_monitor.expired()) - return "monitor"; - if (!m_window.expired()) - return "window"; - - return "error"; -} - -CBox CImageCaptureSource::logicalBox() { - if (!m_monitor.expired()) - return m_monitor->logicalBox(); - if (!m_window.expired()) - return m_window->getFullWindowBoundingBox(); - return CBox(); -} - -COutputImageCaptureSourceProtocol::COutputImageCaptureSourceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; -} - -void COutputImageCaptureSourceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = PROTO::imageCaptureSource->m_outputManagers.emplace_back(makeShared(client, ver, id)); - - if UNLIKELY (!RESOURCE->resource()) { - wl_client_post_no_memory(client); - PROTO::imageCaptureSource->m_outputManagers.pop_back(); - return; - } - - RESOURCE->setDestroy([](CExtOutputImageCaptureSourceManagerV1* pMgr) { PROTO::imageCaptureSource->destroyResource(pMgr); }); - RESOURCE->setOnDestroy([](CExtOutputImageCaptureSourceManagerV1* pMgr) { PROTO::imageCaptureSource->destroyResource(pMgr); }); - RESOURCE->setCreateSource([](CExtOutputImageCaptureSourceManagerV1* pMgr, uint32_t id, wl_resource* output) { - PHLMONITOR pMonitor = CWLOutputResource::fromResource(output)->m_monitor.lock(); - if (!pMonitor) { - LOGM(Log::ERR, "Client tried to create source from invalid output resource"); - pMgr->error(-1, "invalid output resource"); - return; - } - - auto PSOURCE = - PROTO::imageCaptureSource->m_sources.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id), pMonitor)); - PSOURCE->m_self = PSOURCE; - - LOGM(Log::INFO, "New capture source for monitor: {}", pMonitor->m_name); - }); -} - -CToplevelImageCaptureSourceProtocol::CToplevelImageCaptureSourceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; -} - -void CToplevelImageCaptureSourceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = PROTO::imageCaptureSource->m_toplevelManagers.emplace_back(makeShared(client, ver, id)); - - if UNLIKELY (!RESOURCE->resource()) { - RESOURCE->noMemory(); - PROTO::imageCaptureSource->m_toplevelManagers.pop_back(); - return; - } - - RESOURCE->setDestroy([](CExtForeignToplevelImageCaptureSourceManagerV1* pMgr) { PROTO::imageCaptureSource->destroyResource(pMgr); }); - RESOURCE->setOnDestroy([](CExtForeignToplevelImageCaptureSourceManagerV1* pMgr) { PROTO::imageCaptureSource->destroyResource(pMgr); }); - RESOURCE->setCreateSource([](CExtForeignToplevelImageCaptureSourceManagerV1* pMgr, uint32_t id, wl_resource* handle) { - PHLWINDOW pWindow = PROTO::foreignToplevel->windowFromHandleResource(handle); - if (!pWindow) { - LOGM(Log::ERR, "Client tried to create source from invalid foreign toplevel handle resource"); - pMgr->error(-1, "invalid foreign toplevel resource"); - return; - } - - auto PSOURCE = - PROTO::imageCaptureSource->m_sources.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id), pWindow)); - PSOURCE->m_self = PSOURCE; - - LOGM(Log::INFO, "New capture source for foreign toplevel: {}", pWindow->m_title); - }); -} - -CImageCaptureSourceProtocol::CImageCaptureSourceProtocol() { - m_output = makeUnique(&ext_output_image_capture_source_manager_v1_interface, 1, "OutputImageCaptureSource"); - m_toplevel = makeUnique(&ext_foreign_toplevel_image_capture_source_manager_v1_interface, 1, "ForeignToplevelImageCaptureSource"); -} - -SP CImageCaptureSourceProtocol::sourceFromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data && data->m_self ? data->m_self.lock() : nullptr; -} - -void CImageCaptureSourceProtocol::destroyResource(CExtOutputImageCaptureSourceManagerV1* resource) { - std::erase_if(m_outputManagers, [&](const auto& other) { return other.get() == resource; }); -} -void CImageCaptureSourceProtocol::destroyResource(CExtForeignToplevelImageCaptureSourceManagerV1* resource) { - std::erase_if(m_toplevelManagers, [&](const auto& other) { return other.get() == resource; }); -} -void CImageCaptureSourceProtocol::destroyResource(CImageCaptureSource* resource) { - std::erase_if(m_sources, [&](const auto& other) { return other.get() == resource; }); -} diff --git a/src/protocols/ImageCaptureSource.hpp b/src/protocols/ImageCaptureSource.hpp deleted file mode 100644 index 47580dd2..00000000 --- a/src/protocols/ImageCaptureSource.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include - -#include "../defines.hpp" -#include "../helpers/signal/Signal.hpp" -#include "WaylandProtocol.hpp" -#include "ext-image-capture-source-v1.hpp" - -class CImageCopyCaptureSession; - -class CImageCaptureSource { - public: - CImageCaptureSource(SP resource, PHLMONITOR pMonitor); - CImageCaptureSource(SP resource, PHLWINDOW pWindow); - - bool good(); - std::string getName(); - std::string getTypeName(); - CBox logicalBox(); - - WP m_self; - - private: - SP m_resource; - - PHLMONITORREF m_monitor; - PHLWINDOWREF m_window; - - friend class CImageCopyCaptureSession; - friend class CImageCopyCaptureCursorSession; -}; - -class COutputImageCaptureSourceProtocol : public IWaylandProtocol { - public: - COutputImageCaptureSourceProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); -}; - -class CToplevelImageCaptureSourceProtocol : public IWaylandProtocol { - public: - CToplevelImageCaptureSourceProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); -}; - -class CImageCaptureSourceProtocol { - public: - CImageCaptureSourceProtocol(); - - SP sourceFromResource(wl_resource* resource); - - void destroyResource(CExtOutputImageCaptureSourceManagerV1* resource); - void destroyResource(CExtForeignToplevelImageCaptureSourceManagerV1* resource); - void destroyResource(CImageCaptureSource* resource); - - private: - UP m_output; - UP m_toplevel; - - std::vector> m_outputManagers; - std::vector> m_toplevelManagers; - - std::vector> m_sources; - - friend class COutputImageCaptureSourceProtocol; - friend class CToplevelImageCaptureSourceProtocol; -}; - -namespace PROTO { - inline UP imageCaptureSource; -}; diff --git a/src/protocols/ImageCopyCapture.cpp b/src/protocols/ImageCopyCapture.cpp deleted file mode 100644 index eca3c939..00000000 --- a/src/protocols/ImageCopyCapture.cpp +++ /dev/null @@ -1,516 +0,0 @@ -#include "ImageCopyCapture.hpp" -#include "../managers/screenshare/ScreenshareManager.hpp" -#include "../managers/permissions/DynamicPermissionManager.hpp" -#include "../managers/PointerManager.hpp" -#include "./core/Seat.hpp" -#include "LinuxDMABUF.hpp" -#include "../desktop/view/Window.hpp" -#include "../render/OpenGL.hpp" -#include "../desktop/state/FocusState.hpp" -#include - -using namespace Screenshare; - -CImageCopyCaptureSession::CImageCopyCaptureSession(SP resource, SP source, extImageCopyCaptureManagerV1Options options) : - m_resource(resource), m_source(source), m_paintCursor(options & EXT_IMAGE_COPY_CAPTURE_MANAGER_V1_OPTIONS_PAINT_CURSORS) { - if UNLIKELY (!good()) - return; - - m_resource->setDestroy([this](CExtImageCopyCaptureSessionV1* pMgr) { PROTO::imageCopyCapture->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtImageCopyCaptureSessionV1* pMgr) { PROTO::imageCopyCapture->destroyResource(this); }); - - m_resource->setCreateFrame([this](CExtImageCopyCaptureSessionV1* pMgr, uint32_t id) { - if (!m_frame.expired()) { - LOGM(Log::ERR, "Duplicate frame in session for source: \"{}\"", m_source->getName()); - m_resource->error(EXT_IMAGE_COPY_CAPTURE_SESSION_V1_ERROR_DUPLICATE_FRAME, "duplicate frame"); - return; - } - - auto PFRAME = PROTO::imageCopyCapture->m_frames.emplace_back( - makeShared(makeShared(pMgr->client(), pMgr->version(), id), m_self)); - - m_frame = PFRAME; - }); - - if (m_source->m_monitor) - m_session = Screenshare::mgr()->newSession(m_resource->client(), m_source->m_monitor.lock()); - else - m_session = Screenshare::mgr()->newSession(m_resource->client(), m_source->m_window.lock()); - - if UNLIKELY (!m_session) { - m_resource->sendStopped(); - m_resource->error(-1, "unable to share screen"); - return; - } - - sendConstraints(); - - m_listeners.constraintsChanged = m_session->m_events.constraintsChanged.listen([this]() { sendConstraints(); }); - m_listeners.stopped = m_session->m_events.stopped.listen([this]() { PROTO::imageCopyCapture->destroyResource(this); }); -} - -CImageCopyCaptureSession::~CImageCopyCaptureSession() { - if (m_session) - m_session->stop(); - if (m_resource->resource()) - m_resource->sendStopped(); -} - -bool CImageCopyCaptureSession::good() { - return m_resource && m_resource->resource(); -} - -void CImageCopyCaptureSession::sendConstraints() { - auto formats = m_session->allowedFormats(); - - if UNLIKELY (formats.empty()) { - m_session->stop(); - m_resource->error(-1, "no formats available"); - return; - } - - for (DRMFormat format : formats) { - m_resource->sendShmFormat(NFormatUtils::drmToShm(format)); - - auto modifiers = g_pHyprOpenGL->getDRMFormatModifiers(format); - - wl_array modsArr; - wl_array_init(&modsArr); - if (!modifiers.empty()) { - wl_array_add(&modsArr, modifiers.size() * sizeof(uint64_t)); - memcpy(modsArr.data, modifiers.data(), modifiers.size() * sizeof(uint64_t)); - } - m_resource->sendDmabufFormat(format, &modsArr); - wl_array_release(&modsArr); - } - - dev_t device = PROTO::linuxDma->getMainDevice(); - struct wl_array deviceArr = { - .size = sizeof(device), - .data = sc(&device), - }; - m_resource->sendDmabufDevice(&deviceArr); - - m_bufferSize = m_session->bufferSize(); - m_resource->sendBufferSize(m_bufferSize.x, m_bufferSize.y); - - m_resource->sendDone(); -} - -CImageCopyCaptureCursorSession::CImageCopyCaptureCursorSession(SP resource, SP source, SP pointer) : - m_resource(resource), m_source(source), m_pointer(pointer) { - if UNLIKELY (!good()) - return; - - if (!m_source || (!m_source->m_monitor && !m_source->m_window)) - return; - - const auto PMONITOR = m_source->m_monitor.expired() ? m_source->m_window->m_monitor.lock() : m_source->m_monitor.lock(); - - // TODO: add listeners for source being destroyed - - sendCursorEvents(); - m_listeners.commit = PMONITOR->m_events.commit.listen([this, PMONITOR]() { sendCursorEvents(); }); - - m_resource->setDestroy([this](CExtImageCopyCaptureCursorSessionV1* pMgr) { PROTO::imageCopyCapture->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtImageCopyCaptureCursorSessionV1* pMgr) { PROTO::imageCopyCapture->destroyResource(this); }); - - m_resource->setGetCaptureSession([this](CExtImageCopyCaptureCursorSessionV1* pMgr, uint32_t id) { - if (m_session || m_sessionResource) { - LOGM(Log::ERR, "Duplicate cursor copy capture session for source: \"{}\"", m_source->getName()); - m_resource->error(EXT_IMAGE_COPY_CAPTURE_CURSOR_SESSION_V1_ERROR_DUPLICATE_SESSION, "duplicate session"); - return; - } - - m_sessionResource = makeShared(pMgr->client(), pMgr->version(), id); - - m_sessionResource->setDestroy([this](CExtImageCopyCaptureSessionV1* pMgr) { destroyCaptureSession(); }); - m_sessionResource->setOnDestroy([this](CExtImageCopyCaptureSessionV1* pMgr) { destroyCaptureSession(); }); - - m_sessionResource->setCreateFrame([this](CExtImageCopyCaptureSessionV1* pMgr, uint32_t id) { - if UNLIKELY (!m_session || !m_sessionResource) - return; - - if (m_frameResource) { - LOGM(Log::ERR, "Duplicate frame in session for source: \"{}\"", m_source->getName()); - m_resource->error(EXT_IMAGE_COPY_CAPTURE_SESSION_V1_ERROR_DUPLICATE_FRAME, "duplicate frame"); - return; - } - - createFrame(makeShared(pMgr->client(), pMgr->version(), id)); - }); - - m_session = Screenshare::mgr()->newCursorSession(pMgr->client(), m_pointer); - if UNLIKELY (!m_session) { - m_sessionResource->sendStopped(); - m_sessionResource->error(-1, "unable to share cursor"); - return; - } - - sendConstraints(); - - m_listeners.constraintsChanged = m_session->m_events.constraintsChanged.listen([this]() { sendConstraints(); }); - m_listeners.stopped = m_session->m_events.stopped.listen([this]() { destroyCaptureSession(); }); - }); -} - -CImageCopyCaptureCursorSession::~CImageCopyCaptureCursorSession() { - destroyCaptureSession(); -} - -bool CImageCopyCaptureCursorSession::good() { - return m_resource && m_resource->resource(); -} - -void CImageCopyCaptureCursorSession::destroyCaptureSession() { - m_listeners.constraintsChanged.reset(); - m_listeners.stopped.reset(); - - if (m_frameResource && m_frameResource->resource()) - m_frameResource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); - m_frameResource.reset(); - - m_sessionResource.reset(); - m_session.reset(); -} - -void CImageCopyCaptureCursorSession::createFrame(SP resource) { - m_frameResource = resource; - m_captured = false; - m_buffer.reset(); - - m_frameResource->setDestroy([this](CExtImageCopyCaptureFrameV1* pMgr) { m_frameResource.reset(); }); - m_frameResource->setOnDestroy([this](CExtImageCopyCaptureFrameV1* pMgr) { m_frameResource.reset(); }); - - m_frameResource->setAttachBuffer([this](CExtImageCopyCaptureFrameV1* pMgr, wl_resource* buf) { - if UNLIKELY (!m_frameResource || !m_frameResource->resource()) - return; - - if (m_captured) { - LOGM(Log::ERR, "Frame already captured in attach_buffer, {:x}", (uintptr_t)this); - m_frameResource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, "already captured"); - m_frameResource.reset(); - return; - } - - auto PBUFFERRES = CWLBufferResource::fromResource(buf); - if (!PBUFFERRES || !PBUFFERRES->m_buffer) { - LOGM(Log::ERR, "Invalid buffer in attach_buffer {:x}", (uintptr_t)this); - m_frameResource->error(-1, "invalid buffer"); - m_frameResource.reset(); - return; - } - - m_buffer = PBUFFERRES->m_buffer.lock(); - }); - - m_frameResource->setDamageBuffer([this](CExtImageCopyCaptureFrameV1* pMgr, int32_t x, int32_t y, int32_t w, int32_t h) { - if UNLIKELY (!m_frameResource || !m_frameResource->resource()) - return; - - if (m_captured) { - LOGM(Log::ERR, "Frame already captured in damage_buffer, {:x}", (uintptr_t)this); - m_frameResource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, "already captured"); - m_frameResource.reset(); - return; - } - - if (x < 0 || y < 0 || w <= 0 || h <= 0) { - m_frameResource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_INVALID_BUFFER_DAMAGE, "invalid buffer damage"); - m_frameResource.reset(); - return; - } - - // we don't really need to keep track of damage for cursor frames because we will just copy the whole thing - }); - - m_frameResource->setCapture([this](CExtImageCopyCaptureFrameV1* pMgr) { - if UNLIKELY (!m_frameResource || !m_frameResource->resource()) - return; - - if (m_captured) { - LOGM(Log::ERR, "Frame already captured in capture, {:x}", (uintptr_t)this); - m_frameResource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, "already captured"); - m_frameResource.reset(); - return; - } - - const auto PMONITOR = m_source->m_monitor.expired() ? m_source->m_window->m_monitor.lock() : m_source->m_monitor.lock(); - - auto sourceBoxCallback = [this]() { return m_source ? m_source->logicalBox() : CBox(); }; - auto error = m_session->share(PMONITOR, m_buffer, sourceBoxCallback, [this](eScreenshareResult result) { - switch (result) { - case RESULT_COPIED: m_frameResource->sendReady(); break; - case RESULT_NOT_COPIED: m_frameResource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN); break; - case RESULT_TIMESTAMP: - auto [sec, nsec] = Time::secNsec(Time::steadyNow()); - uint32_t tvSecHi = (sizeof(sec) > 4) ? sec >> 32 : 0; - uint32_t tvSecLo = sec & 0xFFFFFFFF; - m_frameResource->sendPresentationTime(tvSecHi, tvSecLo, nsec); - break; - } - }); - - if (!m_frameResource) - return; - - switch (error) { - case ERROR_NONE: m_captured = true; break; - case ERROR_NO_BUFFER: - m_frameResource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_NO_BUFFER, "no buffer attached"); - m_frameResource.reset(); - break; - case ERROR_BUFFER_SIZE: - case ERROR_BUFFER_FORMAT: m_frameResource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS); break; - case ERROR_STOPPED: m_frameResource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); break; - case ERROR_UNKNOWN: m_frameResource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN); break; - } - }); - - // we should always copy over the entire cursor image, it doesn't cost much - m_frameResource->sendDamage(0, 0, m_bufferSize.x, m_bufferSize.y); - - // the cursor is never transformed... probably? - m_frameResource->sendTransform(WL_OUTPUT_TRANSFORM_NORMAL); -} - -void CImageCopyCaptureCursorSession::sendConstraints() { - if UNLIKELY (!m_session || !m_sessionResource) - return; - - auto format = m_session->format(); - if UNLIKELY (format == DRM_FORMAT_INVALID) { - m_session->stop(); - m_sessionResource->error(-1, "no formats available"); - return; - } - - m_sessionResource->sendShmFormat(NFormatUtils::drmToShm(format)); - - auto modifiers = g_pHyprOpenGL->getDRMFormatModifiers(format); - - wl_array modsArr; - wl_array_init(&modsArr); - if (!modifiers.empty()) { - wl_array_add(&modsArr, modifiers.size() * sizeof(uint64_t)); - memcpy(modsArr.data, modifiers.data(), modifiers.size() * sizeof(uint64_t)); - } - m_sessionResource->sendDmabufFormat(format, &modsArr); - wl_array_release(&modsArr); - - dev_t device = PROTO::linuxDma->getMainDevice(); - struct wl_array deviceArr = { - .size = sizeof(device), - .data = sc(&device), - }; - m_sessionResource->sendDmabufDevice(&deviceArr); - - m_bufferSize = m_session->bufferSize(); - m_sessionResource->sendBufferSize(m_bufferSize.x, m_bufferSize.y); - - m_sessionResource->sendDone(); -} - -void CImageCopyCaptureCursorSession::sendCursorEvents() { - const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(m_resource->client(), PERMISSION_TYPE_CURSOR_POS); - if (PERM != PERMISSION_RULE_ALLOW_MODE_ALLOW) { - if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) { - m_resource->error(-1, "client not allowed to capture cursor"); - PROTO::imageCopyCapture->destroyResource(this); - } - return; - } - - const auto PMONITOR = m_source->m_monitor.expired() ? m_source->m_window->m_monitor.lock() : m_source->m_monitor.lock(); - CBox sourceBox = m_source->logicalBox(); - bool overlaps = g_pPointerManager->getCursorBoxGlobal().overlaps(sourceBox); - - if (m_entered && !overlaps) { - m_entered = false; - m_resource->sendLeave(); - return; - } else if (!m_entered && overlaps) { - m_entered = true; - m_resource->sendEnter(); - } - - if (!overlaps) - return; - - Vector2D pos = g_pPointerManager->position() - sourceBox.pos(); - if (pos != m_pos) { - m_pos = pos; - m_resource->sendPosition(m_pos.x, m_pos.y); - } - - Vector2D hotspot = g_pPointerManager->hotspot(); - if (hotspot != m_hotspot) { - m_hotspot = hotspot; - m_resource->sendHotspot(m_hotspot.x, m_hotspot.y); - } -} - -CImageCopyCaptureFrame::CImageCopyCaptureFrame(SP resource, WP session) : m_resource(resource), m_session(session) { - if UNLIKELY (!good()) - return; - - if (m_session->m_bufferSize != m_session->m_session->bufferSize()) { - m_session->sendConstraints(); - m_resource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN); - return; - } - - m_frame = m_session->m_session->nextFrame(m_session->m_paintCursor); - - m_resource->setDestroy([this](CExtImageCopyCaptureFrameV1* pMgr) { PROTO::imageCopyCapture->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtImageCopyCaptureFrameV1* pMgr) { PROTO::imageCopyCapture->destroyResource(this); }); - - m_resource->setAttachBuffer([this](CExtImageCopyCaptureFrameV1* pMgr, wl_resource* buf) { - if (m_captured) { - LOGM(Log::ERR, "Frame already captured in attach_buffer, {:x}", (uintptr_t)this); - m_resource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, "already captured"); - return; - } - - auto PBUFFERRES = CWLBufferResource::fromResource(buf); - if (!PBUFFERRES || !PBUFFERRES->m_buffer) { - LOGM(Log::ERR, "Invalid buffer in attach_buffer {:x}", (uintptr_t)this); - m_resource->error(-1, "invalid buffer"); - return; - } - - m_buffer = PBUFFERRES->m_buffer.lock(); - }); - - m_resource->setDamageBuffer([this](CExtImageCopyCaptureFrameV1* pMgr, int32_t x, int32_t y, int32_t w, int32_t h) { - if (m_captured) { - LOGM(Log::ERR, "Frame already captured in damage_buffer, {:x}", (uintptr_t)this); - m_resource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, "already captured"); - return; - } - - if (x < 0 || y < 0 || w <= 0 || h <= 0) { - m_resource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_INVALID_BUFFER_DAMAGE, "invalid buffer damage"); - return; - } - - m_clientDamage.add(x, y, w, h); - }); - - m_resource->setCapture([this](CExtImageCopyCaptureFrameV1* pMgr) { - if (m_captured) { - LOGM(Log::ERR, "Frame already captured in capture, {:x}", (uintptr_t)this); - m_resource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_ALREADY_CAPTURED, "already captured"); - return; - } - - auto error = m_frame->share(m_buffer, m_clientDamage, [this](eScreenshareResult result) { - switch (result) { - case RESULT_COPIED: m_resource->sendReady(); break; - case RESULT_NOT_COPIED: m_resource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN); break; - case RESULT_TIMESTAMP: - auto [sec, nsec] = Time::secNsec(Time::steadyNow()); - uint32_t tvSecHi = (sizeof(sec) > 4) ? sec >> 32 : 0; - uint32_t tvSecLo = sec & 0xFFFFFFFF; - m_resource->sendPresentationTime(tvSecHi, tvSecLo, nsec); - break; - } - }); - - switch (error) { - case ERROR_NONE: m_captured = true; break; - case ERROR_NO_BUFFER: m_resource->error(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_ERROR_NO_BUFFER, "no buffer attached"); break; - case ERROR_BUFFER_SIZE: - case ERROR_BUFFER_FORMAT: m_resource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_BUFFER_CONSTRAINTS); break; - case ERROR_STOPPED: m_resource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_STOPPED); break; - case ERROR_UNKNOWN: m_resource->sendFailed(EXT_IMAGE_COPY_CAPTURE_FRAME_V1_FAILURE_REASON_UNKNOWN); break; - } - }); - - m_clientDamage.clear(); - - // TODO: see ScreenshareFrame::share() for "add a damage ring for output damage since last shared frame" - m_resource->sendDamage(0, 0, m_session->m_bufferSize.x, m_session->m_bufferSize.y); - - m_resource->sendTransform(m_frame->transform()); -} - -CImageCopyCaptureFrame::~CImageCopyCaptureFrame() { - if (m_session) - m_session->m_frame.reset(); -} - -bool CImageCopyCaptureFrame::good() { - return m_resource && m_resource->resource(); -} - -CImageCopyCaptureProtocol::CImageCopyCaptureProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; -} - -void CImageCopyCaptureProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(client, ver, id)); - - if UNLIKELY (!RESOURCE->resource()) { - wl_client_post_no_memory(client); - m_managers.pop_back(); - return; - } - - RESOURCE->setDestroy([this](CExtImageCopyCaptureManagerV1* pMgr) { destroyResource(pMgr); }); - RESOURCE->setOnDestroy([this](CExtImageCopyCaptureManagerV1* pMgr) { destroyResource(pMgr); }); - - RESOURCE->setCreateSession([this](CExtImageCopyCaptureManagerV1* pMgr, uint32_t id, wl_resource* source_, extImageCopyCaptureManagerV1Options options) { - auto source = PROTO::imageCaptureSource->sourceFromResource(source_); - if (!source) { - LOGM(Log::ERR, "Client tried to create image copy capture session from invalid source"); - pMgr->error(-1, "invalid image capture source"); - return; - } - - if (options > 1) { - LOGM(Log::ERR, "Client tried to create image copy capture session with invalid options"); - pMgr->error(EXT_IMAGE_COPY_CAPTURE_MANAGER_V1_ERROR_INVALID_OPTION, "Options can't be above 1"); - return; - } - - auto& PSESSION = - m_sessions.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id), source, options)); - PSESSION->m_self = PSESSION; - LOGM(Log::INFO, "New image copy capture session for source ({}): \"{}\"", source->getTypeName(), source->getName()); - }); - - RESOURCE->setCreatePointerCursorSession([this](CExtImageCopyCaptureManagerV1* pMgr, uint32_t id, wl_resource* source_, wl_resource* pointer_) { - SP source = PROTO::imageCaptureSource->sourceFromResource(source_); - if (!source) { - LOGM(Log::ERR, "Client tried to create image copy capture session from invalid source"); - destroyResource(pMgr); - return; - } - - const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(pMgr->client(), PERMISSION_TYPE_CURSOR_POS); - if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) - return; - - m_cursorSessions.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id), source, - CWLPointerResource::fromResource(pointer_))); - - LOGM(Log::INFO, "New image copy capture cursor session for source ({}): \"{}\"", source->getTypeName(), source->getName()); - }); -} - -void CImageCopyCaptureProtocol::destroyResource(CExtImageCopyCaptureManagerV1* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); -} - -void CImageCopyCaptureProtocol::destroyResource(CImageCopyCaptureSession* resource) { - std::erase_if(m_sessions, [&](const auto& other) { return other.get() == resource; }); -} - -void CImageCopyCaptureProtocol::destroyResource(CImageCopyCaptureCursorSession* resource) { - std::erase_if(m_cursorSessions, [&](const auto& other) { return other.get() == resource; }); -} - -void CImageCopyCaptureProtocol::destroyResource(CImageCopyCaptureFrame* resource) { - std::erase_if(m_frames, [&](const auto& other) { return other.get() == resource; }); -} diff --git a/src/protocols/ImageCopyCapture.hpp b/src/protocols/ImageCopyCapture.hpp deleted file mode 100644 index b8cfa1e8..00000000 --- a/src/protocols/ImageCopyCapture.hpp +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once - -#include - -#include "../defines.hpp" -#include "../helpers/signal/Signal.hpp" -#include "../helpers/Format.hpp" -#include "WaylandProtocol.hpp" -#include "ImageCaptureSource.hpp" -#include "ext-image-copy-capture-v1.hpp" - -class IHLBuffer; -class CWLPointerResource; -namespace Screenshare { - class CCursorshareSession; - class CScreenshareSession; - class CScreenshareFrame; -}; - -class CImageCopyCaptureFrame { - public: - CImageCopyCaptureFrame(SP resource, WP session); - ~CImageCopyCaptureFrame(); - - bool good(); - - private: - SP m_resource; - WP m_session; - UP m_frame; - - bool m_captured = false; - SP m_buffer; - CRegion m_clientDamage; - - friend class CImageCopyCaptureSession; -}; - -class CImageCopyCaptureSession { - public: - CImageCopyCaptureSession(SP resource, SP source, extImageCopyCaptureManagerV1Options options); - ~CImageCopyCaptureSession(); - - bool good(); - - private: - SP m_resource; - - SP m_source; - UP m_session; - WP m_frame; - - Vector2D m_bufferSize = Vector2D(0, 0); - bool m_paintCursor = true; - - struct { - CHyprSignalListener constraintsChanged; - CHyprSignalListener stopped; - } m_listeners; - - WP m_self; - - // - void sendConstraints(); - - friend class CImageCopyCaptureProtocol; - friend class CImageCopyCaptureFrame; -}; - -class CImageCopyCaptureCursorSession { - public: - CImageCopyCaptureCursorSession(SP resource, SP source, SP pointer); - ~CImageCopyCaptureCursorSession(); - - bool good(); - - private: - SP m_resource; - SP m_source; - SP m_pointer; - - // cursor session stuff - bool m_entered = false; - Vector2D m_pos = Vector2D(0, 0); - Vector2D m_hotspot = Vector2D(0, 0); - - // capture session stuff - SP m_sessionResource; - UP m_session; - Vector2D m_bufferSize = Vector2D(0, 0); - - // frame stuff - SP m_frameResource; - bool m_captured = false; - SP m_buffer; - - struct { - CHyprSignalListener constraintsChanged; - CHyprSignalListener stopped; - CHyprSignalListener commit; - } m_listeners; - - void sendCursorEvents(); - - void createFrame(SP resource); - void destroyCaptureSession(); - void sendConstraints(); -}; - -class CImageCopyCaptureProtocol : public IWaylandProtocol { - public: - CImageCopyCaptureProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - void destroyResource(CExtImageCopyCaptureManagerV1* resource); - void destroyResource(CImageCopyCaptureSession* resource); - void destroyResource(CImageCopyCaptureCursorSession* resource); - void destroyResource(CImageCopyCaptureFrame* resource); - - private: - std::vector> m_managers; - std::vector> m_sessions; - std::vector> m_cursorSessions; - - std::vector> m_frames; - - friend class CImageCopyCaptureSession; -}; - -namespace PROTO { - inline UP imageCopyCapture; -}; diff --git a/src/protocols/InputMethodV2.cpp b/src/protocols/InputMethodV2.cpp index 0917d100..33121ecd 100644 --- a/src/protocols/InputMethodV2.cpp +++ b/src/protocols/InputMethodV2.cpp @@ -1,144 +1,142 @@ #include "InputMethodV2.hpp" -#include "../desktop/state/FocusState.hpp" +#include "../Compositor.hpp" #include "../managers/SeatManager.hpp" #include "../devices/IKeyboard.hpp" -#include "../helpers/MiscFunctions.hpp" #include #include "core/Compositor.hpp" #include -CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP resource_, SP owner_) : m_resource(resource_), m_owner(owner_) { - if UNLIKELY (!m_resource->resource()) +CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP resource_, SP owner_) : resource(resource_), owner(owner_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setRelease([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); }); + resource->setRelease([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); }); + resource->setOnDestroy([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); }); - if (!g_pSeatManager->m_keyboard) { - LOGM(Log::ERR, "IME called but no active keyboard???"); + if (!g_pSeatManager->keyboard) { + LOGM(ERR, "IME called but no active keyboard???"); return; } - sendKeyboardData(g_pSeatManager->m_keyboard.lock()); + sendKeyboardData(g_pSeatManager->keyboard.lock()); } CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() { - if (!m_owner.expired()) - std::erase_if(m_owner->m_grabs, [](const auto& g) { return g.expired(); }); + if (!owner.expired()) + std::erase_if(owner->grabs, [](const auto& g) { return g.expired(); }); } void CInputMethodKeyboardGrabV2::sendKeyboardData(SP keyboard) { - if (keyboard == m_lastKeyboard) + if (keyboard == pLastKeyboard) return; - m_lastKeyboard = keyboard; + pLastKeyboard = keyboard; - auto keymapFD = allocateSHMFile(keyboard->m_xkbKeymapV1String.length() + 1); + auto keymapFD = allocateSHMFile(keyboard->xkbKeymapString.length() + 1); if UNLIKELY (!keymapFD.isValid()) { - LOGM(Log::ERR, "Failed to create a keymap file for keyboard grab"); + LOGM(ERR, "Failed to create a keymap file for keyboard grab"); return; } - void* data = mmap(nullptr, keyboard->m_xkbKeymapV1String.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD.get(), 0); + void* data = mmap(nullptr, keyboard->xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD.get(), 0); if UNLIKELY (data == MAP_FAILED) { - LOGM(Log::ERR, "Failed to mmap a keymap file for keyboard grab"); + LOGM(ERR, "Failed to mmap a keymap file for keyboard grab"); return; } - memcpy(data, keyboard->m_xkbKeymapV1String.c_str(), keyboard->m_xkbKeymapV1String.length()); - munmap(data, keyboard->m_xkbKeymapV1String.length() + 1); + memcpy(data, keyboard->xkbKeymapString.c_str(), keyboard->xkbKeymapString.length()); + munmap(data, keyboard->xkbKeymapString.length() + 1); - m_resource->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymapFD.get(), keyboard->m_xkbKeymapV1String.length() + 1); + resource->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymapFD.get(), keyboard->xkbKeymapString.length() + 1); - sendMods(keyboard->m_modifiersState.depressed, keyboard->m_modifiersState.latched, keyboard->m_modifiersState.locked, keyboard->m_modifiersState.group); + sendMods(keyboard->modifiersState.depressed, keyboard->modifiersState.latched, keyboard->modifiersState.locked, keyboard->modifiersState.group); - m_resource->sendRepeatInfo(keyboard->m_repeatRate, keyboard->m_repeatDelay); + resource->sendRepeatInfo(keyboard->repeatRate, keyboard->repeatDelay); } void CInputMethodKeyboardGrabV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) { - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(m_resource->client())); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client())); - m_resource->sendKey(SERIAL, time, key, sc(state)); + resource->sendKey(SERIAL, time, key, (uint32_t)state); } void CInputMethodKeyboardGrabV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(m_resource->client())); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client())); - m_resource->sendModifiers(SERIAL, depressed, latched, locked, group); + resource->sendModifiers(SERIAL, depressed, latched, locked, group); } bool CInputMethodKeyboardGrabV2::good() { - return m_resource->resource(); + return resource->resource(); } SP CInputMethodKeyboardGrabV2::getOwner() { - return m_owner.lock(); + return owner.lock(); } wl_client* CInputMethodKeyboardGrabV2::client() { - return m_resource->resource() ? m_resource->client() : nullptr; + return resource->resource() ? resource->client() : nullptr; } -CInputMethodPopupV2::CInputMethodPopupV2(SP resource_, SP owner_, SP surface) : - m_resource(resource_), m_owner(owner_) { - if UNLIKELY (!m_resource->resource()) +CInputMethodPopupV2::CInputMethodPopupV2(SP resource_, SP owner_, SP surface) : resource(resource_), owner(owner_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); }); + resource->setDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); }); + resource->setOnDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); }); - m_surface = surface; + pSurface = surface; - m_listeners.destroySurface = surface->m_events.destroy.listen([this] { - if (m_mapped) - m_events.unmap.emit(); + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { + if (mapped) + events.unmap.emit(); - m_listeners.destroySurface.reset(); - m_listeners.commitSurface.reset(); + listeners.destroySurface.reset(); + listeners.commitSurface.reset(); - if (Desktop::focusState()->surface() == m_surface) - Desktop::focusState()->surface().reset(); + if (g_pCompositor->m_pLastFocus == pSurface) + g_pCompositor->m_pLastFocus.reset(); - m_surface.reset(); + pSurface.reset(); }); - m_listeners.commitSurface = surface->m_events.commit.listen([this] { - if (m_surface->m_current.texture && !m_mapped) { - m_mapped = true; - m_surface->map(); - m_events.map.emit(); + listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { + if (pSurface->current.texture && !mapped) { + mapped = true; + pSurface->map(); + events.map.emit(); return; } - if (!m_surface->m_current.texture && m_mapped) { - m_mapped = false; - m_surface->unmap(); - m_events.unmap.emit(); + if (!pSurface->current.texture && mapped) { + mapped = false; + pSurface->unmap(); + events.unmap.emit(); return; } - m_events.commit.emit(); + events.commit.emit(); }); } CInputMethodPopupV2::~CInputMethodPopupV2() { - if (!m_owner.expired()) - std::erase_if(m_owner->m_popups, [](const auto& p) { return p.expired(); }); + if (!owner.expired()) + std::erase_if(owner->popups, [](const auto& p) { return p.expired(); }); - m_events.destroy.emit(); + events.destroy.emit(); } bool CInputMethodPopupV2::good() { - return m_resource->resource(); + return resource->resource(); } void CInputMethodPopupV2::sendInputRectangle(const CBox& box) { - m_resource->sendTextInputRectangle(box.x, box.y, box.w, box.h); + resource->sendTextInputRectangle(box.x, box.y, box.w, box.h); } SP CInputMethodPopupV2::surface() { - return m_surface.lock(); + return pSurface.lock(); } void CInputMethodV2::SState::reset() { @@ -147,127 +145,127 @@ void CInputMethodV2::SState::reset() { preeditString.committed = false; } -CInputMethodV2::CInputMethodV2(SP resource_) : m_resource(resource_) { - if UNLIKELY (!m_resource->resource()) +CInputMethodV2::CInputMethodV2(SP resource_) : resource(resource_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setDestroy([this](CZwpInputMethodV2* r) { - m_events.destroy.emit(); + resource->setDestroy([this](CZwpInputMethodV2* r) { + events.destroy.emit(); PROTO::ime->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpInputMethodV2* r) { - m_events.destroy.emit(); + resource->setOnDestroy([this](CZwpInputMethodV2* r) { + events.destroy.emit(); PROTO::ime->destroyResource(this); }); - m_resource->setCommitString([this](CZwpInputMethodV2* r, const char* str) { - m_pending.committedString.string = str; - m_pending.committedString.committed = true; + resource->setCommitString([this](CZwpInputMethodV2* r, const char* str) { + pending.committedString.string = str; + pending.committedString.committed = true; }); - m_resource->setDeleteSurroundingText([this](CZwpInputMethodV2* r, uint32_t before, uint32_t after) { - m_pending.deleteSurrounding.before = before; - m_pending.deleteSurrounding.after = after; - m_pending.deleteSurrounding.committed = true; + resource->setDeleteSurroundingText([this](CZwpInputMethodV2* r, uint32_t before, uint32_t after) { + pending.deleteSurrounding.before = before; + pending.deleteSurrounding.after = after; + pending.deleteSurrounding.committed = true; }); - m_resource->setSetPreeditString([this](CZwpInputMethodV2* r, const char* str, int32_t begin, int32_t end) { - m_pending.preeditString.string = str; - m_pending.preeditString.begin = begin; - m_pending.preeditString.end = end; - m_pending.preeditString.committed = true; + resource->setSetPreeditString([this](CZwpInputMethodV2* r, const char* str, int32_t begin, int32_t end) { + pending.preeditString.string = str; + pending.preeditString.begin = begin; + pending.preeditString.end = end; + pending.preeditString.committed = true; }); - m_resource->setCommit([this](CZwpInputMethodV2* r, uint32_t serial) { - m_current = m_pending; - m_pending.reset(); - m_events.onCommit.emit(); + resource->setCommit([this](CZwpInputMethodV2* r, uint32_t serial) { + current = pending; + pending.reset(); + events.onCommit.emit(); }); - m_resource->setGetInputPopupSurface([this](CZwpInputMethodV2* r, uint32_t id, wl_resource* surface) { - const auto RESOURCE = PROTO::ime->m_popups.emplace_back( - makeShared(makeShared(r->client(), r->version(), id), m_self.lock(), CWLSurfaceResource::fromResource(surface))); + resource->setGetInputPopupSurface([this](CZwpInputMethodV2* r, uint32_t id, wl_resource* surface) { + const auto RESOURCE = PROTO::ime->m_vPopups.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), self.lock(), CWLSurfaceResource::fromResource(surface))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::ime->m_popups.pop_back(); + PROTO::ime->m_vPopups.pop_back(); return; } - LOGM(Log::DEBUG, "New IME Popup with resource id {}", id); + LOGM(LOG, "New IME Popup with resource id {}", id); - m_popups.emplace_back(RESOURCE); + popups.emplace_back(RESOURCE); - m_events.newPopup.emit(RESOURCE); + events.newPopup.emit(RESOURCE); }); - m_resource->setGrabKeyboard([this](CZwpInputMethodV2* r, uint32_t id) { + resource->setGrabKeyboard([this](CZwpInputMethodV2* r, uint32_t id) { const auto RESOURCE = - PROTO::ime->m_grabs.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_self.lock())); + PROTO::ime->m_vGrabs.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::ime->m_grabs.pop_back(); + PROTO::ime->m_vGrabs.pop_back(); return; } - LOGM(Log::DEBUG, "New IME Grab with resource id {}", id); + LOGM(LOG, "New IME Grab with resource id {}", id); - m_grabs.emplace_back(RESOURCE); + grabs.emplace_back(RESOURCE); }); } CInputMethodV2::~CInputMethodV2() { - m_events.destroy.emit(); + events.destroy.emit(); } bool CInputMethodV2::good() { - return m_resource->resource(); + return resource->resource(); } void CInputMethodV2::activate() { - if (m_active) + if (active) return; - m_resource->sendActivate(); - m_active = true; + resource->sendActivate(); + active = true; } void CInputMethodV2::deactivate() { - if (!m_active) + if (!active) return; - m_resource->sendDeactivate(); - m_active = false; + resource->sendDeactivate(); + active = false; } void CInputMethodV2::surroundingText(const std::string& text, uint32_t cursor, uint32_t anchor) { - m_resource->sendSurroundingText(text.c_str(), cursor, anchor); + resource->sendSurroundingText(text.c_str(), cursor, anchor); } void CInputMethodV2::textChangeCause(zwpTextInputV3ChangeCause changeCause) { - m_resource->sendTextChangeCause(changeCause); + resource->sendTextChangeCause((uint32_t)changeCause); } void CInputMethodV2::textContentType(zwpTextInputV3ContentHint hint, zwpTextInputV3ContentPurpose purpose) { - m_resource->sendContentType(hint, purpose); + resource->sendContentType((uint32_t)hint, (uint32_t)purpose); } void CInputMethodV2::done() { - m_resource->sendDone(); + resource->sendDone(); } void CInputMethodV2::unavailable() { - m_resource->sendUnavailable(); + resource->sendUnavailable(); } bool CInputMethodV2::hasGrab() { - return !m_grabs.empty(); + return !grabs.empty(); } wl_client* CInputMethodV2::grabClient() { - if (m_grabs.empty()) + if (grabs.empty()) return nullptr; - for (auto const& gw : m_grabs) { + for (auto const& gw : grabs) { auto g = gw.lock(); if (!g) @@ -280,19 +278,19 @@ wl_client* CInputMethodV2::grabClient() { } void CInputMethodV2::sendInputRectangle(const CBox& box) { - m_inputRectangle = box; - for (auto const& wp : m_popups) { + inputRectangle = box; + for (auto const& wp : popups) { auto p = wp.lock(); if (!p) continue; - p->sendInputRectangle(m_inputRectangle); + p->sendInputRectangle(inputRectangle); } } void CInputMethodV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) { - for (auto const& gw : m_grabs) { + for (auto const& gw : grabs) { auto g = gw.lock(); if (!g) @@ -303,7 +301,7 @@ void CInputMethodV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state } void CInputMethodV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { - for (auto const& gw : m_grabs) { + for (auto const& gw : grabs) { auto g = gw.lock(); if (!g) @@ -314,7 +312,7 @@ void CInputMethodV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t loc } void CInputMethodV2::setKeyboard(SP keyboard) { - for (auto const& gw : m_grabs) { + for (auto const& gw : grabs) { auto g = gw.lock(); if (!g) @@ -325,7 +323,7 @@ void CInputMethodV2::setKeyboard(SP keyboard) { } wl_client* CInputMethodV2::client() { - return m_resource->client(); + return resource->client(); } CInputMethodV2Protocol::CInputMethodV2Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -333,7 +331,7 @@ CInputMethodV2Protocol::CInputMethodV2Protocol(const wl_interface* iface, const } void CInputMethodV2Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpInputMethodManagerV2* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwpInputMethodManagerV2* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -341,33 +339,33 @@ void CInputMethodV2Protocol::bindManager(wl_client* client, void* data, uint32_t } void CInputMethodV2Protocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CInputMethodV2Protocol::destroyResource(CInputMethodPopupV2* popup) { - std::erase_if(m_popups, [&](const auto& other) { return other.get() == popup; }); + std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == popup; }); } void CInputMethodV2Protocol::destroyResource(CInputMethodKeyboardGrabV2* grab) { - std::erase_if(m_grabs, [&](const auto& other) { return other.get() == grab; }); + std::erase_if(m_vGrabs, [&](const auto& other) { return other.get() == grab; }); } void CInputMethodV2Protocol::destroyResource(CInputMethodV2* ime) { - std::erase_if(m_imes, [&](const auto& other) { return other.get() == ime; }); + std::erase_if(m_vIMEs, [&](const auto& other) { return other.get() == ime; }); } void CInputMethodV2Protocol::onGetIME(CZwpInputMethodManagerV2* mgr, wl_resource* seat, uint32_t id) { - const auto RESOURCE = m_imes.emplace_back(makeShared(makeShared(mgr->client(), mgr->version(), id))); + const auto RESOURCE = m_vIMEs.emplace_back(makeShared(makeShared(mgr->client(), mgr->version(), id))); if UNLIKELY (!RESOURCE->good()) { mgr->noMemory(); - m_imes.pop_back(); + m_vIMEs.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - LOGM(Log::DEBUG, "New IME with resource id {}", id); + LOGM(LOG, "New IME with resource id {}", id); - m_events.newIME.emit(RESOURCE); + events.newIME.emit(RESOURCE); } diff --git a/src/protocols/InputMethodV2.hpp b/src/protocols/InputMethodV2.hpp index b948d609..0c249c6c 100644 --- a/src/protocols/InputMethodV2.hpp +++ b/src/protocols/InputMethodV2.hpp @@ -6,7 +6,7 @@ #include "input-method-unstable-v2.hpp" #include "text-input-unstable-v3.hpp" #include "../helpers/signal/Signal.hpp" -#include "../desktop/view/WLSurface.hpp" +#include "../desktop/WLSurface.hpp" class CInputMethodKeyboardGrabV2; class CInputMethodPopupV2; @@ -18,10 +18,10 @@ class CInputMethodV2 { ~CInputMethodV2(); struct { - CSignalT<> onCommit; - CSignalT<> destroy; - CSignalT> newPopup; - } m_events; + CSignal onCommit; + CSignal destroy; + CSignal newPopup; + } events; struct SState { void reset(); @@ -43,8 +43,7 @@ class CInputMethodV2 { } deleteSurrounding; }; - SState m_pending; - SState m_current; + SState pending, current; bool good(); void activate(); @@ -65,15 +64,15 @@ class CInputMethodV2 { wl_client* grabClient(); private: - SP m_resource; - std::vector> m_grabs; - std::vector> m_popups; + SP resource; + std::vector> grabs; + std::vector> popups; - WP m_self; + WP self; - bool m_active = false; + bool active = false; - CBox m_inputRectangle; + CBox inputRectangle; friend class CInputMethodPopupV2; friend class CInputMethodKeyboardGrabV2; @@ -94,10 +93,10 @@ class CInputMethodKeyboardGrabV2 { void sendKeyboardData(SP keyboard); private: - SP m_resource; - WP m_owner; + SP resource; + WP owner; - WP m_lastKeyboard; + WP pLastKeyboard; }; class CInputMethodPopupV2 { @@ -110,23 +109,23 @@ class CInputMethodPopupV2 { SP surface(); struct { - CSignalT<> map; - CSignalT<> unmap; - CSignalT<> commit; - CSignalT<> destroy; - } m_events; + CSignal map; + CSignal unmap; + CSignal commit; + CSignal destroy; + } events; - bool m_mapped = false; + bool mapped = false; private: - SP m_resource; - WP m_owner; - WP m_surface; + SP resource; + WP owner; + WP pSurface; struct { CHyprSignalListener destroySurface; CHyprSignalListener commitSurface; - } m_listeners; + } listeners; }; class CInputMethodV2Protocol : public IWaylandProtocol { @@ -136,8 +135,8 @@ class CInputMethodV2Protocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); struct { - CSignalT> newIME; - } m_events; + CSignal newIME; // SP + } events; private: void onManagerResourceDestroy(wl_resource* res); @@ -148,10 +147,10 @@ class CInputMethodV2Protocol : public IWaylandProtocol { void onGetIME(CZwpInputMethodManagerV2* mgr, wl_resource* seat, uint32_t id); // - std::vector> m_managers; - std::vector> m_imes; - std::vector> m_grabs; - std::vector> m_popups; + std::vector> m_vManagers; + std::vector> m_vIMEs; + std::vector> m_vGrabs; + std::vector> m_vPopups; friend class CInputMethodPopupV2; friend class CInputMethodKeyboardGrabV2; @@ -160,4 +159,4 @@ class CInputMethodV2Protocol : public IWaylandProtocol { namespace PROTO { inline UP ime; -}; +}; \ No newline at end of file diff --git a/src/protocols/LayerShell.cpp b/src/protocols/LayerShell.cpp index 80222627..c88dc925 100644 --- a/src/protocols/LayerShell.cpp +++ b/src/protocols/LayerShell.cpp @@ -1,6 +1,6 @@ #include "LayerShell.hpp" #include "../Compositor.hpp" -#include "../desktop/view/LayerSurface.hpp" +#include "../desktop/LayerSurface.hpp" #include "XDGShell.hpp" #include "core/Compositor.hpp" #include "core/Output.hpp" @@ -11,189 +11,189 @@ void CLayerShellResource::SState::reset() { exclusive = 0; interactivity = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - exclusiveEdge = sc(0); + exclusiveEdge = (zwlrLayerSurfaceV1Anchor)0; desiredSize = {}; margin = {0, 0, 0, 0}; } CLayerShellResource::CLayerShellResource(SP resource_, SP surf_, std::string namespace_, PHLMONITOR pMonitor, - zwlrLayerShellV1Layer layer) : m_layerNamespace(namespace_), m_surface(surf_), m_resource(resource_) { + zwlrLayerShellV1Layer layer) : layerNamespace(namespace_), surface(surf_), resource(resource_) { if UNLIKELY (!good()) return; - m_current.layer = layer; - m_monitor = pMonitor ? pMonitor->m_name : ""; + current.layer = layer; + monitor = pMonitor ? pMonitor->szName : ""; - m_resource->setDestroy([this](CZwlrLayerSurfaceV1* r) { - m_events.destroy.emit(); + resource->setDestroy([this](CZwlrLayerSurfaceV1* r) { + events.destroy.emit(); PROTO::layerShell->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrLayerSurfaceV1* r) { - m_events.destroy.emit(); + resource->setOnDestroy([this](CZwlrLayerSurfaceV1* r) { + events.destroy.emit(); PROTO::layerShell->destroyResource(this); }); - m_listeners.destroySurface = surf_->m_events.destroy.listen([this] { - m_events.destroy.emit(); + listeners.destroySurface = surf_->events.destroy.registerListener([this](std::any d) { + events.destroy.emit(); PROTO::layerShell->destroyResource(this); }); - m_listeners.unmapSurface = surf_->m_events.unmap.listen([this] { m_events.unmap.emit(); }); + listeners.unmapSurface = surf_->events.unmap.registerListener([this](std::any d) { events.unmap.emit(); }); - m_listeners.commitSurface = surf_->m_events.commit.listen([this] { - m_current = m_pending; - m_pending.committed = 0; + listeners.commitSurface = surf_->events.commit.registerListener([this](std::any d) { + current = pending; + pending.committed = 0; - bool attachedBuffer = m_surface->m_current.texture; + bool attachedBuffer = surface->current.texture; - if (attachedBuffer && !m_configured) { - m_surface->error(-1, "layerSurface was not configured, but a buffer was attached"); + if (attachedBuffer && !configured) { + surface->error(-1, "layerSurface was not configured, but a buffer was attached"); return; } constexpr uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; constexpr uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - if (m_current.desiredSize.x <= 0 && (m_current.anchor & horiz) != horiz) { - m_surface->error(-1, "x == 0 but anchor doesn't have left and right"); + if (current.desiredSize.x <= 0 && (current.anchor & horiz) != horiz) { + surface->error(-1, "x == 0 but anchor doesn't have left and right"); return; } - if (m_current.desiredSize.y <= 0 && (m_current.anchor & vert) != vert) { - m_surface->error(-1, "y == 0 but anchor doesn't have top and bottom"); + if (current.desiredSize.y <= 0 && (current.anchor & vert) != vert) { + surface->error(-1, "y == 0 but anchor doesn't have top and bottom"); return; } - if (attachedBuffer && !m_mapped) { - m_mapped = true; - m_surface->map(); - m_events.map.emit(); + if (attachedBuffer && !mapped) { + mapped = true; + surface->map(); + events.map.emit(); return; } - if (!attachedBuffer && m_mapped) { - m_mapped = false; - m_events.unmap.emit(); - m_surface->unmap(); - m_configured = false; + if (!attachedBuffer && mapped) { + mapped = false; + events.unmap.emit(); + surface->unmap(); + configured = false; return; } - m_events.commit.emit(); + events.commit.emit(); }); - m_resource->setSetSize([this](CZwlrLayerSurfaceV1* r, uint32_t x, uint32_t y) { - m_pending.committed |= STATE_SIZE; - m_pending.desiredSize = {sc(x), sc(y)}; + resource->setSetSize([this](CZwlrLayerSurfaceV1* r, uint32_t x, uint32_t y) { + pending.committed |= STATE_SIZE; + pending.desiredSize = {(int)x, (int)y}; }); - m_resource->setSetAnchor([this](CZwlrLayerSurfaceV1* r, zwlrLayerSurfaceV1Anchor anchor) { + resource->setSetAnchor([this](CZwlrLayerSurfaceV1* r, zwlrLayerSurfaceV1Anchor anchor) { if (anchor > (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR, "Invalid anchor"); return; } - m_pending.committed |= STATE_ANCHOR; - m_pending.anchor = anchor; + pending.committed |= STATE_ANCHOR; + pending.anchor = anchor; }); - m_resource->setSetExclusiveZone([this](CZwlrLayerSurfaceV1* r, int32_t zone) { - m_pending.committed |= STATE_EXCLUSIVE; - m_pending.exclusive = zone; + resource->setSetExclusiveZone([this](CZwlrLayerSurfaceV1* r, int32_t zone) { + pending.committed |= STATE_EXCLUSIVE; + pending.exclusive = zone; }); - m_resource->setSetMargin([this](CZwlrLayerSurfaceV1* r, int32_t top, int32_t right, int32_t bottom, int32_t left) { - m_pending.committed |= STATE_MARGIN; - m_pending.margin = {left, right, top, bottom}; + resource->setSetMargin([this](CZwlrLayerSurfaceV1* r, int32_t top, int32_t right, int32_t bottom, int32_t left) { + pending.committed |= STATE_MARGIN; + pending.margin = {left, right, top, bottom}; }); - m_resource->setSetKeyboardInteractivity([this](CZwlrLayerSurfaceV1* r, zwlrLayerSurfaceV1KeyboardInteractivity kbi) { + resource->setSetKeyboardInteractivity([this](CZwlrLayerSurfaceV1* r, zwlrLayerSurfaceV1KeyboardInteractivity kbi) { if (kbi > ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) { r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_KEYBOARD_INTERACTIVITY, "Invalid keyboard interactivity"); return; } - m_pending.committed |= STATE_INTERACTIVITY; - m_pending.interactivity = kbi; + pending.committed |= STATE_INTERACTIVITY; + pending.interactivity = kbi; }); - m_resource->setGetPopup([this](CZwlrLayerSurfaceV1* r, wl_resource* popup_) { + resource->setGetPopup([this](CZwlrLayerSurfaceV1* r, wl_resource* popup_) { auto popup = CXDGPopupResource::fromResource(popup_); - if (popup->m_taken) { + if (popup->taken) { r->error(-1, "Parent already exists!"); return; } - popup->m_taken = true; - m_events.newPopup.emit(popup); + popup->taken = true; + events.newPopup.emit(popup); }); - m_resource->setAckConfigure([this](CZwlrLayerSurfaceV1* r, uint32_t serial) { - auto serialFound = std::ranges::find_if(m_serials, [serial](const auto& e) { return e.first == serial; }); + resource->setAckConfigure([this](CZwlrLayerSurfaceV1* r, uint32_t serial) { + auto serialFound = std::find_if(serials.begin(), serials.end(), [serial](const auto& e) { return e.first == serial; }); - if (serialFound == m_serials.end()) { + if (serialFound == serials.end()) { r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE, "Serial invalid in ack_configure"); return; } - m_configured = true; - m_size = serialFound->second; + configured = true; + size = serialFound->second; - m_serials.erase(serialFound); + serials.erase(serialFound); }); - m_resource->setSetLayer([this](CZwlrLayerSurfaceV1* r, uint32_t layer) { + resource->setSetLayer([this](CZwlrLayerSurfaceV1* r, uint32_t layer) { if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { r->error(ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, "Invalid layer"); return; } - m_pending.committed |= STATE_LAYER; - m_pending.layer = sc(layer); + pending.committed |= STATE_LAYER; + pending.layer = (zwlrLayerShellV1Layer)layer; }); - m_resource->setSetExclusiveEdge([this](CZwlrLayerSurfaceV1* r, zwlrLayerSurfaceV1Anchor anchor) { + resource->setSetExclusiveEdge([this](CZwlrLayerSurfaceV1* r, zwlrLayerSurfaceV1Anchor anchor) { if (anchor > (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, "Invalid exclusive edge"); return; } - if (anchor && (!m_pending.anchor || !(m_pending.anchor & anchor))) { + if (anchor && (!pending.anchor || !(pending.anchor & anchor))) { r->error(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_EXCLUSIVE_EDGE, "Exclusive edge doesn't align with anchor"); return; } - m_pending.committed |= STATE_EDGE; - m_pending.exclusiveEdge = anchor; + pending.committed |= STATE_EDGE; + pending.exclusiveEdge = anchor; }); } CLayerShellResource::~CLayerShellResource() { - m_events.destroy.emit(); - if (m_surface) - m_surface->resetRole(); + events.destroy.emit(); + if (surface) + surface->resetRole(); } bool CLayerShellResource::good() { - return m_resource->resource(); + return resource->resource(); } void CLayerShellResource::sendClosed() { - if (m_closed) + if (closed) return; - m_closed = true; - m_resource->sendClosed(); + closed = true; + resource->sendClosed(); } void CLayerShellResource::configure(const Vector2D& size_) { - m_size = size_; + size = size_; - auto serial = wl_display_next_serial(g_pCompositor->m_wlDisplay); + auto serial = wl_display_next_serial(g_pCompositor->m_sWLDisplay); - m_serials.push_back({serial, size_}); + serials.push_back({serial, size_}); - m_resource->sendConfigure(serial, size_.x, size_.y); + resource->sendConfigure(serial, size_.x, size_.y); } CLayerShellProtocol::CLayerShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -201,7 +201,7 @@ CLayerShellProtocol::CLayerShellProtocol(const wl_interface* iface, const int& v } void CLayerShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwlrLayerShellV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwlrLayerShellV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -211,16 +211,16 @@ void CLayerShellProtocol::bindManager(wl_client* client, void* data, uint32_t ve } void CLayerShellProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CLayerShellProtocol::destroyResource(CLayerShellResource* surf) { - std::erase_if(m_layers, [&](const auto& other) { return other.get() == surf; }); + std::erase_if(m_vLayers, [&](const auto& other) { return other.get() == surf; }); } void CLayerShellProtocol::onGetLayerSurface(CZwlrLayerShellV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* output, zwlrLayerShellV1Layer layer, std::string namespace_) { const auto CLIENT = pMgr->client(); - const auto PMONITOR = output ? CWLOutputResource::fromResource(output)->m_monitor.lock() : nullptr; + const auto PMONITOR = output ? CWLOutputResource::fromResource(output)->monitor.lock() : nullptr; auto SURF = CWLSurfaceResource::fromResource(surface); if UNLIKELY (!SURF) { @@ -228,7 +228,7 @@ void CLayerShellProtocol::onGetLayerSurface(CZwlrLayerShellV1* pMgr, uint32_t id return; } - if UNLIKELY (SURF->m_role->role() != SURFACE_ROLE_UNASSIGNED) { + if UNLIKELY (SURF->role->role() != SURFACE_ROLE_UNASSIGNED) { pMgr->error(-1, "Surface already has a different role"); return; } @@ -238,20 +238,20 @@ void CLayerShellProtocol::onGetLayerSurface(CZwlrLayerShellV1* pMgr, uint32_t id return; } - const auto RESOURCE = m_layers.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), SURF, namespace_, PMONITOR, layer)); + const auto RESOURCE = m_vLayers.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), SURF, namespace_, PMONITOR, layer)); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_layers.pop_back(); + m_vLayers.pop_back(); return; } - SURF->m_role = makeShared(RESOURCE); - g_pCompositor->m_layers.emplace_back(Desktop::View::CLayerSurface::create(RESOURCE)); + SURF->role = makeShared(RESOURCE); + g_pCompositor->m_vLayers.emplace_back(CLayerSurface::create(RESOURCE)); - LOGM(Log::DEBUG, "New wlr_layer_surface {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New wlr_layer_surface {:x}", (uintptr_t)RESOURCE.get()); } -CLayerShellRole::CLayerShellRole(SP ls) : m_layerSurface(ls) { +CLayerShellRole::CLayerShellRole(SP ls) : layerSurface(ls) { ; } diff --git a/src/protocols/LayerShell.hpp b/src/protocols/LayerShell.hpp index 08edc700..b634c63c 100644 --- a/src/protocols/LayerShell.hpp +++ b/src/protocols/LayerShell.hpp @@ -10,7 +10,6 @@ #include "types/SurfaceRole.hpp" class CMonitor; -class CXDGPopupResource; class CWLSurfaceResource; class CLayerShellResource; @@ -22,9 +21,8 @@ class CLayerShellRole : public ISurfaceRole { return SURFACE_ROLE_LAYER_SHELL; } - WP m_layerSurface; + WP layerSurface; }; - class CLayerShellResource { public: CLayerShellResource(SP resource_, SP surf_, std::string namespace_, PHLMONITOR pMonitor, zwlrLayerShellV1Layer layer); @@ -45,12 +43,12 @@ class CLayerShellResource { }; struct { - CSignalT<> destroy; - CSignalT<> commit; - CSignalT<> map; - CSignalT<> unmap; - CSignalT> newPopup; - } m_events; + CSignal destroy; + CSignal commit; + CSignal map; + CSignal unmap; + CSignal newPopup; // wlr_xdg_popup* + } events; struct SState { uint32_t anchor = 0; @@ -58,7 +56,7 @@ class CLayerShellResource { Vector2D desiredSize; zwlrLayerSurfaceV1KeyboardInteractivity interactivity = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; zwlrLayerShellV1Layer layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; - zwlrLayerSurfaceV1Anchor exclusiveEdge = sc(0); + zwlrLayerSurfaceV1Anchor exclusiveEdge = (zwlrLayerSurfaceV1Anchor)0; uint32_t committed = 0; struct { @@ -66,27 +64,27 @@ class CLayerShellResource { } margin; void reset(); - } m_current, m_pending; + } current, pending; - Vector2D m_size; - std::string m_layerNamespace; - std::string m_monitor = ""; - WP m_surface; - bool m_mapped = false; - bool m_configured = false; + Vector2D size; + std::string layerNamespace; + std::string monitor = ""; + WP surface; + bool mapped = false; + bool configured = false; private: - SP m_resource; + SP resource; struct { CHyprSignalListener commitSurface; CHyprSignalListener destroySurface; CHyprSignalListener unmapSurface; - } m_listeners; + } listeners; - bool m_closed = false; + bool closed = false; - std::vector> m_serials; + std::vector> serials; }; class CLayerShellProtocol : public IWaylandProtocol { @@ -101,8 +99,8 @@ class CLayerShellProtocol : public IWaylandProtocol { void onGetLayerSurface(CZwlrLayerShellV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* output, zwlrLayerShellV1Layer layer, std::string namespace_); // - std::vector> m_managers; - std::vector> m_layers; + std::vector> m_vManagers; + std::vector> m_vLayers; friend class CLayerShellResource; }; diff --git a/src/protocols/LinuxDMABUF.cpp b/src/protocols/LinuxDMABUF.cpp index f16c8c56..78834368 100644 --- a/src/protocols/LinuxDMABUF.cpp +++ b/src/protocols/LinuxDMABUF.cpp @@ -10,9 +10,9 @@ #include "core/Compositor.hpp" #include "types/DMABuffer.hpp" #include "types/WLBuffer.hpp" +#include "../managers/HookSystemManager.hpp" #include "../render/OpenGL.hpp" #include "../Compositor.hpp" -#include "../event/EventBus.hpp" using namespace Hyprutils::OS; @@ -24,9 +24,7 @@ static std::optional devIDFromFD(int fd) { } CDMABUFFormatTable::CDMABUFFormatTable(SDMABUFTranche _rendererTranche, std::vector> tranches_) : - m_rendererTranche(_rendererTranche), m_monitorTranches(tranches_) { - - static const auto PSKIP_NON_KMS = CConfigValue("quirks:skip_non_kms_dmabuf_formats"); + rendererTranche(_rendererTranche), monitorTranches(tranches_) { std::vector formatsVec; std::set> formats; @@ -34,121 +32,109 @@ CDMABUFFormatTable::CDMABUFFormatTable(SDMABUFTranche _rendererTranche, std::vec // insert formats into vec if they got inserted into set, meaning they're unique size_t i = 0; - m_rendererTranche.indices.clear(); - for (auto const& fmt : m_rendererTranche.formats) { + rendererTranche.indicies.clear(); + for (auto const& fmt : rendererTranche.formats) { for (auto const& mod : fmt.modifiers) { - LOGM(Log::TRACE, "Render format 0x{:x} ({}) with mod 0x{:x} ({})", fmt.drmFormat, NFormatUtils::drmFormatName(fmt.drmFormat), mod, NFormatUtils::drmModifierName(mod)); - if (*PSKIP_NON_KMS && !m_monitorTranches.empty()) { - if (std::ranges::none_of(m_monitorTranches, [fmt, mod](const std::pair& pair) { - return std::ranges::any_of(pair.second.formats, [fmt, mod](const SDRMFormat& format) { - return format.drmFormat == fmt.drmFormat && std::ranges::any_of(format.modifiers, [mod](uint64_t modifier) { return mod == modifier; }); - }); - })) { - LOGM(Log::TRACE, " skipped"); - continue; - } - } auto format = std::make_pair<>(fmt.drmFormat, mod); auto [_, inserted] = formats.insert(format); if (inserted) { // if it was inserted into set, then its unique and will have a new index in vec - m_rendererTranche.indices.push_back(i++); + rendererTranche.indicies.push_back(i++); formatsVec.push_back(SDMABUFFormatTableEntry{ .fmt = fmt.drmFormat, .modifier = mod, }); } else { // if it wasn't inserted then find its index in vec - auto it = std::ranges::find_if(formatsVec, [fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; }); - m_rendererTranche.indices.push_back(it - formatsVec.begin()); + auto it = + std::find_if(formatsVec.begin(), formatsVec.end(), [fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; }); + rendererTranche.indicies.push_back(it - formatsVec.begin()); } } } - for (auto& [monitor, tranche] : m_monitorTranches) { - tranche.indices.clear(); + for (auto& [monitor, tranche] : monitorTranches) { + tranche.indicies.clear(); for (auto const& fmt : tranche.formats) { for (auto const& mod : fmt.modifiers) { - LOGM(Log::TRACE, "[DMA] Monitor format 0x{:x} ({}) with mod 0x{:x} ({})", fmt.drmFormat, NFormatUtils::drmFormatName(fmt.drmFormat), mod, - NFormatUtils::drmModifierName(mod)); - // FIXME: recheck this. DRM_FORMAT_MOD_INVALID is allowed by the proto "For legacy support". DRM_FORMAT_MOD_LINEAR should be the most compatible mod - // apparently these can implode on planes, so don't use them + // apparently these can implode on planes, so dont use them if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR) continue; auto format = std::make_pair<>(fmt.drmFormat, mod); auto [_, inserted] = formats.insert(format); if (inserted) { - tranche.indices.push_back(i++); + tranche.indicies.push_back(i++); formatsVec.push_back(SDMABUFFormatTableEntry{ .fmt = fmt.drmFormat, .modifier = mod, }); } else { - auto it = std::ranges::find_if(formatsVec, [fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; }); - tranche.indices.push_back(it - formatsVec.begin()); + auto it = std::find_if(formatsVec.begin(), formatsVec.end(), + [fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; }); + tranche.indicies.push_back(it - formatsVec.begin()); } } } } - m_tableSize = formatsVec.size() * sizeof(SDMABUFFormatTableEntry); + tableSize = formatsVec.size() * sizeof(SDMABUFFormatTableEntry); CFileDescriptor fds[2]; - allocateSHMFilePair(m_tableSize, fds[0], fds[1]); + allocateSHMFilePair(tableSize, fds[0], fds[1]); - auto arr = sc(mmap(nullptr, m_tableSize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0].get(), 0)); + auto arr = (SDMABUFFormatTableEntry*)mmap(nullptr, tableSize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0].get(), 0); if (arr == MAP_FAILED) { - LOGM(Log::ERR, "mmap failed"); + LOGM(ERR, "mmap failed"); return; } - std::ranges::copy(formatsVec, arr); + std::copy(formatsVec.begin(), formatsVec.end(), arr); - munmap(arr, m_tableSize); + munmap(arr, tableSize); - m_tableFD = std::move(fds[1]); + tableFD = std::move(fds[1]); } CLinuxDMABuffer::CLinuxDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs) { - m_buffer = makeShared(id, client, attrs); + buffer = makeShared(id, client, attrs); - m_buffer->m_resource->m_buffer = m_buffer; + buffer->resource->buffer = buffer; - m_listeners.bufferResourceDestroy = m_buffer->events.destroy.listen([this] { - m_listeners.bufferResourceDestroy.reset(); + listeners.bufferResourceDestroy = buffer->events.destroy.registerListener([this](std::any d) { + listeners.bufferResourceDestroy.reset(); PROTO::linuxDma->destroyResource(this); }); - if (!m_buffer->m_success) - LOGM(Log::ERR, "Possibly compositor bug: buffer failed to create"); + if (!buffer->success) + LOGM(ERR, "Possibly compositor bug: buffer failed to create"); } CLinuxDMABuffer::~CLinuxDMABuffer() { - if (m_buffer && m_buffer->m_resource) - m_buffer->m_resource->sendRelease(); + if (buffer && buffer->resource) + buffer->resource->sendRelease(); - m_buffer.reset(); - m_listeners.bufferResourceDestroy.reset(); + buffer.reset(); + listeners.bufferResourceDestroy.reset(); } bool CLinuxDMABuffer::good() { - return m_buffer && m_buffer->good(); + return buffer && buffer->good(); } -CLinuxDMABUFParamsResource::CLinuxDMABUFParamsResource(UP&& resource_) : m_resource(std::move(resource_)) { +CLinuxDMABUFParamsResource::CLinuxDMABUFParamsResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); }); - m_resource->setDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setOnDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); }); - m_attrs = makeShared(); + attrs = makeShared(); - m_attrs->success = true; + attrs->success = true; - m_resource->setAdd([this](CZwpLinuxBufferParamsV1* r, int32_t fd, uint32_t plane, uint32_t offset, uint32_t stride, uint32_t modHi, uint32_t modLo) { - if (m_used) { + resource->setAdd([this](CZwpLinuxBufferParamsV1* r, int32_t fd, uint32_t plane, uint32_t offset, uint32_t stride, uint32_t modHi, uint32_t modLo) { + if (used) { r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "Already used"); return; } @@ -158,121 +144,107 @@ CLinuxDMABUFParamsResource::CLinuxDMABUFParamsResource(UPfds.at(plane) != -1) { + if (attrs->fds.at(plane) != -1) { r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, "plane used"); return; } - const uint64_t modifier = (sc(modHi) << 32) | modLo; - - if (m_resource->version() >= 5 && m_attrs->modifier && m_attrs->modifier != modifier) { - r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, "planes have different modifiers"); - return; - } - - m_attrs->fds[plane] = fd; - m_attrs->strides[plane] = stride; - m_attrs->offsets[plane] = offset; - m_attrs->modifier = modifier; + attrs->fds[plane] = fd; + attrs->strides[plane] = stride; + attrs->offsets[plane] = offset; + attrs->modifier = ((uint64_t)modHi << 32) | modLo; }); - m_resource->setCreate([this](CZwpLinuxBufferParamsV1* r, int32_t w, int32_t h, uint32_t fmt, zwpLinuxBufferParamsV1Flags flags) { - if (m_used) { + resource->setCreate([this](CZwpLinuxBufferParamsV1* r, int32_t w, int32_t h, uint32_t fmt, zwpLinuxBufferParamsV1Flags flags) { + if (used) { r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "Already used"); return; } if (flags > 0) { r->sendFailed(); - LOGM(Log::ERR, "DMABUF flags are not supported"); + LOGM(ERR, "DMABUF flags are not supported"); return; } - if (m_resource->version() >= 4 && std::ranges::none_of(PROTO::linuxDma->m_formatTable->m_rendererTranche.formats, [this, fmt](const auto format) { - return format.drmFormat == fmt && std::ranges::any_of(format.modifiers, [this](const auto mod) { return !mod || mod == m_attrs->modifier; }); - })) { - r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, "format + modifier pair is not supported"); - return; - } - - m_attrs->size = {w, h}; - m_attrs->format = fmt; - m_attrs->planes = 4 - std::ranges::count(m_attrs->fds, -1); + attrs->size = {w, h}; + attrs->format = fmt; + attrs->planes = 4 - std::count(attrs->fds.begin(), attrs->fds.end(), -1); create(0); }); - m_resource->setCreateImmed([this](CZwpLinuxBufferParamsV1* r, uint32_t id, int32_t w, int32_t h, uint32_t fmt, zwpLinuxBufferParamsV1Flags flags) { - if (m_used) { + resource->setCreateImmed([this](CZwpLinuxBufferParamsV1* r, uint32_t id, int32_t w, int32_t h, uint32_t fmt, zwpLinuxBufferParamsV1Flags flags) { + if (used) { r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "Already used"); return; } if (flags > 0) { r->sendFailed(); - LOGM(Log::ERR, "DMABUF flags are not supported"); + LOGM(ERR, "DMABUF flags are not supported"); return; } - m_attrs->size = {w, h}; - m_attrs->format = fmt; - m_attrs->planes = 4 - std::ranges::count(m_attrs->fds, -1); + attrs->size = {w, h}; + attrs->format = fmt; + attrs->planes = 4 - std::count(attrs->fds.begin(), attrs->fds.end(), -1); create(id); }); } bool CLinuxDMABUFParamsResource::good() { - return m_resource->resource(); + return resource->resource(); } void CLinuxDMABUFParamsResource::create(uint32_t id) { - m_used = true; + used = true; if UNLIKELY (!verify()) { - LOGM(Log::ERR, "Failed creating a dmabuf: verify() said no"); + LOGM(ERR, "Failed creating a dmabuf: verify() said no"); return; // if verify failed, we errored the resource. } if UNLIKELY (!commence()) { - LOGM(Log::ERR, "Failed creating a dmabuf: commence() said no"); - m_resource->sendFailed(); + LOGM(ERR, "Failed creating a dmabuf: commence() said no"); + resource->sendFailed(); return; } - LOGM(Log::DEBUG, "Creating a dmabuf, with id {}: size {}, fmt {}, planes {}", id, m_attrs->size, NFormatUtils::drmFormatName(m_attrs->format), m_attrs->planes); - for (int i = 0; i < m_attrs->planes; ++i) { - LOGM(Log::DEBUG, " | plane {}: mod {} fd {} stride {} offset {}", i, m_attrs->modifier, m_attrs->fds[i], m_attrs->strides[i], m_attrs->offsets[i]); + LOGM(LOG, "Creating a dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs->size, NFormatUtils::drmFormatName(attrs->format), attrs->planes); + for (int i = 0; i < attrs->planes; ++i) { + LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs->modifier, attrs->fds[i], attrs->strides[i], attrs->offsets[i]); } - auto& buf = PROTO::linuxDma->m_buffers.emplace_back(makeUnique(id, m_resource->client(), *m_attrs)); + auto buf = PROTO::linuxDma->m_vBuffers.emplace_back(makeShared(id, resource->client(), *attrs)); - if UNLIKELY (!buf->good() || !buf->m_buffer->m_success) { - m_resource->sendFailed(); - PROTO::linuxDma->m_buffers.pop_back(); + if UNLIKELY (!buf->good() || !buf->buffer->success) { + resource->sendFailed(); + PROTO::linuxDma->m_vBuffers.pop_back(); return; } if (!id) - m_resource->sendCreated(buf->m_buffer->m_resource->getResource()); + resource->sendCreated(PROTO::linuxDma->m_vBuffers.back()->buffer->resource->getResource()); - m_createdBuffer = buf; + createdBuffer = buf; } bool CLinuxDMABUFParamsResource::commence() { - if (!PROTO::linuxDma->m_mainDeviceFD.isValid()) + if (!PROTO::linuxDma->mainDeviceFD.isValid()) return true; - for (int i = 0; i < m_attrs->planes; i++) { + for (int i = 0; i < attrs->planes; i++) { uint32_t handle = 0; - if (drmPrimeFDToHandle(PROTO::linuxDma->m_mainDeviceFD.get(), m_attrs->fds.at(i), &handle)) { - LOGM(Log::ERR, "Failed to import dmabuf fd"); + if (drmPrimeFDToHandle(PROTO::linuxDma->mainDeviceFD.get(), attrs->fds.at(i), &handle)) { + LOGM(ERR, "Failed to import dmabuf fd"); return false; } - if (drmCloseBufferHandle(PROTO::linuxDma->m_mainDeviceFD.get(), handle)) { - LOGM(Log::ERR, "Failed to close dmabuf handle"); + if (drmCloseBufferHandle(PROTO::linuxDma->mainDeviceFD.get(), handle)) { + LOGM(ERR, "Failed to close dmabuf handle"); return false; } } @@ -281,20 +253,20 @@ bool CLinuxDMABUFParamsResource::commence() { } bool CLinuxDMABUFParamsResource::verify() { - if UNLIKELY (m_attrs->planes <= 0) { - m_resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "No planes added"); + if UNLIKELY (attrs->planes <= 0) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "No planes added"); return false; } - if UNLIKELY (m_attrs->fds.at(0) < 0) { - m_resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "No plane 0"); + if UNLIKELY (attrs->fds.at(0) < 0) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "No plane 0"); return false; } bool empty = false; - for (auto const& plane : m_attrs->fds) { + for (auto const& plane : attrs->fds) { if (empty && plane != -1) { - m_resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, "Gap in planes"); + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, "Gap in planes"); return false; } @@ -304,16 +276,16 @@ bool CLinuxDMABUFParamsResource::verify() { } } - if UNLIKELY (m_attrs->size.x < 1 || m_attrs->size.y < 1) { - m_resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, "x/y < 1"); + if UNLIKELY (attrs->size.x < 1 || attrs->size.y < 1) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, "x/y < 1"); return false; } - for (size_t i = 0; i < sc(m_attrs->planes); ++i) { - if (sc(m_attrs->offsets.at(i)) + sc(m_attrs->strides.at(i)) * m_attrs->size.y > UINT32_MAX) { - m_resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, - std::format("size overflow on plane {}: offset {} + stride {} * height {} = {}, overflows UINT32_MAX", i, sc(m_attrs->offsets.at(i)), - sc(m_attrs->strides.at(i)), m_attrs->size.y, sc(m_attrs->offsets.at(i)) + sc(m_attrs->strides.at(i)))); + for (size_t i = 0; i < (size_t)attrs->planes; ++i) { + if ((uint64_t)attrs->offsets.at(i) + (uint64_t)attrs->strides.at(i) * attrs->size.y > UINT32_MAX) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + std::format("size overflow on plane {}: offset {} + stride {} * height {} = {}, overflows UINT32_MAX", i, (uint64_t)attrs->offsets.at(i), + (uint64_t)attrs->strides.at(i), attrs->size.y, (uint64_t)attrs->offsets.at(i) + (uint64_t)attrs->strides.at(i))); return false; } } @@ -321,282 +293,260 @@ bool CLinuxDMABUFParamsResource::verify() { return true; } -CLinuxDMABUFFeedbackResource::CLinuxDMABUFFeedbackResource(UP&& resource_, SP surface_) : - m_surface(surface_), m_resource(std::move(resource_)) { +CLinuxDMABUFFeedbackResource::CLinuxDMABUFFeedbackResource(SP resource_, SP surface_) : surface(surface_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); }); - m_resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); }); - auto& formatTable = PROTO::linuxDma->m_formatTable; - m_resource->sendFormatTable(formatTable->m_tableFD.get(), formatTable->m_tableSize); + auto& formatTable = PROTO::linuxDma->formatTable; + resource->sendFormatTable(formatTable->tableFD.get(), formatTable->tableSize); sendDefaultFeedback(); } bool CLinuxDMABUFFeedbackResource::good() { - return m_resource->resource(); + return resource->resource(); } void CLinuxDMABUFFeedbackResource::sendTranche(SDMABUFTranche& tranche) { struct wl_array deviceArr = { .size = sizeof(tranche.device), - .data = sc(&tranche.device), + .data = (void*)&tranche.device, }; - m_resource->sendTrancheTargetDevice(&deviceArr); + resource->sendTrancheTargetDevice(&deviceArr); - m_resource->sendTrancheFlags(sc(tranche.flags)); + resource->sendTrancheFlags((zwpLinuxDmabufFeedbackV1TrancheFlags)tranche.flags); wl_array indices = { - .size = tranche.indices.size() * sizeof(tranche.indices.at(0)), - .data = tranche.indices.data(), + .size = tranche.indicies.size() * sizeof(tranche.indicies.at(0)), + .data = tranche.indicies.data(), }; - m_resource->sendTrancheFormats(&indices); - m_resource->sendTrancheDone(); + resource->sendTrancheFormats(&indices); + resource->sendTrancheDone(); } // default tranche is based on renderer (egl) void CLinuxDMABUFFeedbackResource::sendDefaultFeedback() { - auto mainDevice = PROTO::linuxDma->m_mainDevice; - auto& formatTable = PROTO::linuxDma->m_formatTable; + auto mainDevice = PROTO::linuxDma->mainDevice; + auto& formatTable = PROTO::linuxDma->formatTable; struct wl_array deviceArr = { .size = sizeof(mainDevice), - .data = sc(&mainDevice), + .data = (void*)&mainDevice, }; - m_resource->sendMainDevice(&deviceArr); + resource->sendMainDevice(&deviceArr); - sendTranche(formatTable->m_rendererTranche); + sendTranche(formatTable->rendererTranche); - m_resource->sendDone(); + resource->sendDone(); - m_lastFeedbackWasScanout = false; + lastFeedbackWasScanout = false; } -CLinuxDMABUFResource::CLinuxDMABUFResource(UP&& resource_) : m_resource(std::move(resource_)) { +CLinuxDMABUFResource::CLinuxDMABUFResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CZwpLinuxDmabufV1* r) { PROTO::linuxDma->destroyResource(this); }); - m_resource->setDestroy([this](CZwpLinuxDmabufV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setOnDestroy([this](CZwpLinuxDmabufV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setDestroy([this](CZwpLinuxDmabufV1* r) { PROTO::linuxDma->destroyResource(this); }); - m_resource->setGetDefaultFeedback([](CZwpLinuxDmabufV1* r, uint32_t id) { - const auto& RESOURCE = - PROTO::linuxDma->m_feedbacks.emplace_back(makeUnique(makeUnique(r->client(), r->version(), id), nullptr)); + resource->setGetDefaultFeedback([](CZwpLinuxDmabufV1* r, uint32_t id) { + const auto RESOURCE = + PROTO::linuxDma->m_vFeedbacks.emplace_back(makeShared(makeShared(r->client(), r->version(), id), nullptr)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::linuxDma->m_feedbacks.pop_back(); + PROTO::linuxDma->m_vFeedbacks.pop_back(); return; } }); - m_resource->setGetSurfaceFeedback([](CZwpLinuxDmabufV1* r, uint32_t id, wl_resource* surf) { - const auto& RESOURCE = PROTO::linuxDma->m_feedbacks.emplace_back( - makeUnique(makeUnique(r->client(), r->version(), id), CWLSurfaceResource::fromResource(surf))); + resource->setGetSurfaceFeedback([](CZwpLinuxDmabufV1* r, uint32_t id, wl_resource* surf) { + const auto RESOURCE = PROTO::linuxDma->m_vFeedbacks.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), CWLSurfaceResource::fromResource(surf))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::linuxDma->m_feedbacks.pop_back(); + PROTO::linuxDma->m_vFeedbacks.pop_back(); return; } }); - m_resource->setCreateParams([](CZwpLinuxDmabufV1* r, uint32_t id) { - const auto& RESOURCE = PROTO::linuxDma->m_params.emplace_back(makeUnique(makeUnique(r->client(), r->version(), id))); + resource->setCreateParams([](CZwpLinuxDmabufV1* r, uint32_t id) { + const auto RESOURCE = PROTO::linuxDma->m_vParams.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::linuxDma->m_params.pop_back(); + PROTO::linuxDma->m_vParams.pop_back(); return; } }); - sendMods(); + if (resource->version() < 4) + sendMods(); } bool CLinuxDMABUFResource::good() { - return m_resource->resource(); + return resource->resource(); } void CLinuxDMABUFResource::sendMods() { - for (auto const& fmt : PROTO::linuxDma->m_formatTable->m_rendererTranche.formats) { - m_resource->sendFormat(fmt.drmFormat); - - if (m_resource->version() == 3) { - for (auto const& mod : fmt.modifiers) { - // TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 - - m_resource->sendModifier(fmt.drmFormat, mod >> 32, mod & 0xFFFFFFFF); + for (auto const& fmt : PROTO::linuxDma->formatTable->rendererTranche.formats) { + for (auto const& mod : fmt.modifiers) { + if (resource->version() < 3) { + if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR) + resource->sendFormat(fmt.drmFormat); + continue; } + + // TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 + + resource->sendModifier(fmt.drmFormat, mod >> 32, mod & 0xFFFFFFFF); } } } CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P = Event::bus()->m_events.ready.listen([this] { - int rendererFD = g_pCompositor->m_drmRenderNode.fd >= 0 ? g_pCompositor->m_drmRenderNode.fd : g_pCompositor->m_drm.fd; + static auto P = g_pHookSystem->hookDynamic("ready", [this](void* self, SCallbackInfo& info, std::any d) { + int rendererFD = g_pCompositor->m_iDRMFD; auto dev = devIDFromFD(rendererFD); if (!dev.has_value()) { - LOGM(Log::ERR, "failed to get drm dev, disabling linux dmabuf"); + LOGM(ERR, "failed to get drm dev, disabling linux dmabuf"); removeGlobal(); return; } - m_mainDevice = *dev; + mainDevice = *dev; SDMABUFTranche eglTranche = { - .device = m_mainDevice, - .flags = 0, // renderer isn't for ds so don't set flag. + .device = mainDevice, + .flags = 0, // renderer isnt for ds so dont set flag. .formats = g_pHyprOpenGL->getDRMFormats(), }; std::vector> tches; - if (g_pCompositor->m_aqBackend->hasSession()) { + if (g_pCompositor->m_pAqBackend->hasSession()) { // this assumes there's only 1 device used for both scanout and rendering // also that each monitor never changes its primary plane - for (auto const& mon : g_pCompositor->m_monitors) { + for (auto const& mon : g_pCompositor->m_vMonitors) { auto tranche = SDMABUFTranche{ - .device = m_mainDevice, + .device = mainDevice, .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, - .formats = mon->m_output->getRenderFormats(), + .formats = mon->output->getRenderFormats(), }; tches.emplace_back(std::make_pair<>(mon, tranche)); } - static auto monitorAdded = Event::bus()->m_events.monitor.added.listen([this](PHLMONITOR mon) { - auto tranche = SDMABUFTranche{ - .device = m_mainDevice, - .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, - .formats = mon->m_output->getRenderFormats(), + static auto monitorAdded = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) { + auto pMonitor = std::any_cast(param); + auto tranche = SDMABUFTranche{ + .device = mainDevice, + .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, + .formats = pMonitor->output->getRenderFormats(), }; - m_formatTable->m_monitorTranches.emplace_back(std::make_pair<>(mon, tranche)); + formatTable->monitorTranches.emplace_back(std::make_pair<>(pMonitor, tranche)); resetFormatTable(); }); - static auto monitorRemoved = Event::bus()->m_events.monitor.removed.listen([this](PHLMONITOR mon) { - std::erase_if(m_formatTable->m_monitorTranches, [mon](std::pair pair) { return pair.first == mon; }); + static auto monitorRemoved = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) { + auto pMonitor = std::any_cast(param); + std::erase_if(formatTable->monitorTranches, [pMonitor](std::pair pair) { return pair.first == pMonitor; }); resetFormatTable(); }); - - static auto configReloaded = Event::bus()->m_events.config.reloaded.listen([this] { - static const auto PSKIP_NON_KMS = CConfigValue("quirks:skip_non_kms_dmabuf_formats"); - static auto prev = *PSKIP_NON_KMS; - if (prev != *PSKIP_NON_KMS) { - prev = *PSKIP_NON_KMS; - resetFormatTable(); - } - }); } - m_formatTable = makeUnique(eglTranche, tches); + formatTable = makeUnique(eglTranche, tches); drmDevice* device = nullptr; - if (drmGetDeviceFromDevId(m_mainDevice, 0, &device) != 0) { - LOGM(Log::ERR, "failed to get drm dev, disabling linux dmabuf"); + if (drmGetDeviceFromDevId(mainDevice, 0, &device) != 0) { + LOGM(ERR, "failed to get drm dev, disabling linux dmabuf"); removeGlobal(); return; } - if (g_pCompositor->m_drmRenderNode.fd >= 0 && rendererFD == g_pCompositor->m_drmRenderNode.fd) { - // Already using the compositor's render node, reuse it. - m_mainDeviceFD = CFileDescriptor{fcntl(g_pCompositor->m_drmRenderNode.fd, F_DUPFD_CLOEXEC, 0)}; - drmFreeDevice(&device); - if (!m_mainDeviceFD.isValid()) { - LOGM(Log::ERR, "failed to open rendernode, disabling linux dmabuf"); - removeGlobal(); - return; - } - - return; // already using rendernode. - } - if (device->available_nodes & (1 << DRM_NODE_RENDER)) { const char* name = device->nodes[DRM_NODE_RENDER]; - m_mainDeviceFD = CFileDescriptor{open(name, O_RDWR | O_CLOEXEC)}; + mainDeviceFD = CFileDescriptor{open(name, O_RDWR | O_CLOEXEC)}; drmFreeDevice(&device); - if (!m_mainDeviceFD.isValid()) { - LOGM(Log::ERR, "failed to open drm dev, disabling linux dmabuf"); + if (!mainDeviceFD.isValid()) { + LOGM(ERR, "failed to open drm dev, disabling linux dmabuf"); removeGlobal(); return; } } else { - LOGM(Log::ERR, "DRM device {} has no render node, disabling linux dmabuf checks", device->nodes[DRM_NODE_PRIMARY] ? device->nodes[DRM_NODE_PRIMARY] : "null"); + LOGM(ERR, "DRM device {} has no render node, disabling linux dmabuf checks", device->nodes[DRM_NODE_PRIMARY] ? device->nodes[DRM_NODE_PRIMARY] : "null"); drmFreeDevice(&device); } }); } void CLinuxDMABufV1Protocol::resetFormatTable() { - if (!m_formatTable) + if (!formatTable) return; - LOGM(Log::DEBUG, "Resetting format table"); + LOGM(LOG, "Resetting format table"); // this might be a big copy - auto newFormatTable = makeUnique(m_formatTable->m_rendererTranche, m_formatTable->m_monitorTranches); + auto newFormatTable = makeUnique(formatTable->rendererTranche, formatTable->monitorTranches); - for (auto const& feedback : m_feedbacks) { - feedback->m_resource->sendFormatTable(newFormatTable->m_tableFD.get(), newFormatTable->m_tableSize); - if (feedback->m_lastFeedbackWasScanout) { + for (auto const& feedback : m_vFeedbacks) { + feedback->resource->sendFormatTable(newFormatTable->tableFD.get(), newFormatTable->tableSize); + if (feedback->lastFeedbackWasScanout) { PHLMONITOR mon; - auto HLSurface = Desktop::View::CWLSurface::fromResource(feedback->m_surface); - if (!HLSurface) { - feedback->sendDefaultFeedback(); - continue; - } - if (auto w = Desktop::View::CWindow::fromView(HLSurface->view()); w) - if (auto m = w->m_monitor.lock(); m) - mon = m->m_self.lock(); + auto HLSurface = CWLSurface::fromResource(feedback->surface); + if (auto w = HLSurface->getWindow(); w) + if (auto m = w->m_pMonitor.lock(); m) + mon = m->self.lock(); if (!mon) { feedback->sendDefaultFeedback(); - continue; + return; } - updateScanoutTranche(feedback->m_surface, mon); + updateScanoutTranche(feedback->surface, mon); } else { feedback->sendDefaultFeedback(); } } // delete old table after we sent new one - m_formatTable = std::move(newFormatTable); + formatTable = std::move(newFormatTable); } void CLinuxDMABufV1Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto& RESOURCE = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABUFResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABUFFeedbackResource* resource) { - std::erase_if(m_feedbacks, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vFeedbacks, [&](const auto& other) { return other.get() == resource; }); } void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABUFParamsResource* resource) { - std::erase_if(m_params, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vParams, [&](const auto& other) { return other.get() == resource; }); } void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABuffer* resource) { - std::erase_if(m_buffers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; }); } void CLinuxDMABufV1Protocol::updateScanoutTranche(SP surface, PHLMONITOR pMonitor) { - WP feedbackResource; - for (auto const& f : m_feedbacks) { - if (f->m_surface != surface) + SP feedbackResource; + for (auto const& f : m_vFeedbacks) { + if (f->surface != surface) continue; feedbackResource = f; @@ -604,44 +554,40 @@ void CLinuxDMABufV1Protocol::updateScanoutTranche(SP surface } if (!feedbackResource) { - LOGM(Log::DEBUG, "updateScanoutTranche: surface has no dmabuf_feedback"); + LOGM(LOG, "updateScanoutTranche: surface has no dmabuf_feedback"); return; } if (!pMonitor) { - LOGM(Log::DEBUG, "updateScanoutTranche: resetting feedback"); + LOGM(LOG, "updateScanoutTranche: resetting feedback"); feedbackResource->sendDefaultFeedback(); return; } - const auto& monitorTranchePair = - std::ranges::find_if(m_formatTable->m_monitorTranches, [pMonitor](std::pair pair) { return pair.first == pMonitor; }); + const auto& monitorTranchePair = std::find_if(formatTable->monitorTranches.begin(), formatTable->monitorTranches.end(), + [pMonitor](std::pair pair) { return pair.first == pMonitor; }); - if (monitorTranchePair == m_formatTable->m_monitorTranches.end()) { - LOGM(Log::DEBUG, "updateScanoutTranche: monitor has no tranche"); + if (monitorTranchePair == formatTable->monitorTranches.end()) { + LOGM(LOG, "updateScanoutTranche: monitor has no tranche"); return; } auto& monitorTranche = (*monitorTranchePair).second; - LOGM(Log::DEBUG, "updateScanoutTranche: sending a scanout tranche"); + LOGM(LOG, "updateScanoutTranche: sending a scanout tranche"); struct wl_array deviceArr = { - .size = sizeof(m_mainDevice), - .data = sc(&m_mainDevice), + .size = sizeof(mainDevice), + .data = (void*)&mainDevice, }; - feedbackResource->m_resource->sendMainDevice(&deviceArr); + feedbackResource->resource->sendMainDevice(&deviceArr); // prioritize scnaout tranche but have renderer fallback tranche // also yes formats can be duped here because different tranche flags (ds and no ds) feedbackResource->sendTranche(monitorTranche); - feedbackResource->sendTranche(m_formatTable->m_rendererTranche); + feedbackResource->sendTranche(formatTable->rendererTranche); - feedbackResource->m_resource->sendDone(); + feedbackResource->resource->sendDone(); - feedbackResource->m_lastFeedbackWasScanout = true; -} - -dev_t CLinuxDMABufV1Protocol::getMainDevice() { - return m_mainDevice; + feedbackResource->lastFeedbackWasScanout = true; } diff --git a/src/protocols/LinuxDMABUF.hpp b/src/protocols/LinuxDMABUF.hpp index b1d59155..d55ce8a7 100644 --- a/src/protocols/LinuxDMABUF.hpp +++ b/src/protocols/LinuxDMABUF.hpp @@ -22,11 +22,11 @@ class CLinuxDMABuffer { bool good(); private: - SP m_buffer; + SP buffer; struct { CHyprSignalListener bufferResourceDestroy; - } m_listeners; + } listeners; friend class CLinuxDMABUFParamsResource; }; @@ -43,7 +43,7 @@ struct SDMABUFTranche { dev_t device = 0; uint32_t flags = 0; std::vector formats; - std::vector indices; + std::vector indicies; }; class CDMABUFFormatTable { @@ -51,26 +51,26 @@ class CDMABUFFormatTable { CDMABUFFormatTable(SDMABUFTranche rendererTranche, std::vector> tranches); ~CDMABUFFormatTable() = default; - Hyprutils::OS::CFileDescriptor m_tableFD; - size_t m_tableSize = 0; - SDMABUFTranche m_rendererTranche; - std::vector> m_monitorTranches; + Hyprutils::OS::CFileDescriptor tableFD; + size_t tableSize = 0; + SDMABUFTranche rendererTranche; + std::vector> monitorTranches; }; class CLinuxDMABUFParamsResource { public: - CLinuxDMABUFParamsResource(UP&& resource_); + CLinuxDMABUFParamsResource(SP resource_); ~CLinuxDMABUFParamsResource() = default; bool good(); void create(uint32_t id); // 0 means not immed - SP m_attrs; - WP m_createdBuffer; - bool m_used = false; + SP attrs; + WP createdBuffer; + bool used = false; private: - UP m_resource; + SP resource; bool verify(); bool commence(); @@ -78,32 +78,32 @@ class CLinuxDMABUFParamsResource { class CLinuxDMABUFFeedbackResource { public: - CLinuxDMABUFFeedbackResource(UP&& resource_, SP surface_); + CLinuxDMABUFFeedbackResource(SP resource_, SP surface_); ~CLinuxDMABUFFeedbackResource() = default; bool good(); void sendDefaultFeedback(); void sendTranche(SDMABUFTranche& tranche); - SP m_surface; // optional, for surface feedbacks + SP surface; // optional, for surface feedbacks private: - UP m_resource; - bool m_lastFeedbackWasScanout = false; + SP resource; + bool lastFeedbackWasScanout = false; friend class CLinuxDMABufV1Protocol; }; class CLinuxDMABUFResource { public: - CLinuxDMABUFResource(UP&& resource_); + CLinuxDMABUFResource(SP resource_); ~CLinuxDMABUFResource() = default; bool good(); void sendMods(); private: - UP m_resource; + SP resource; }; class CLinuxDMABufV1Protocol : public IWaylandProtocol { @@ -113,7 +113,6 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); void updateScanoutTranche(SP surface, PHLMONITOR pMonitor); - dev_t getMainDevice(); private: void destroyResource(CLinuxDMABUFResource* resource); @@ -124,14 +123,14 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol { void resetFormatTable(); // - std::vector> m_managers; - std::vector> m_feedbacks; - std::vector> m_params; - std::vector> m_buffers; + std::vector> m_vManagers; + std::vector> m_vFeedbacks; + std::vector> m_vParams; + std::vector> m_vBuffers; - UP m_formatTable; - dev_t m_mainDevice; - Hyprutils::OS::CFileDescriptor m_mainDeviceFD; + UP formatTable; + dev_t mainDevice; + Hyprutils::OS::CFileDescriptor mainDeviceFD; friend class CLinuxDMABUFResource; friend class CLinuxDMABUFFeedbackResource; diff --git a/src/protocols/LockNotify.cpp b/src/protocols/LockNotify.cpp index 46736ead..1855f891 100644 --- a/src/protocols/LockNotify.cpp +++ b/src/protocols/LockNotify.cpp @@ -63,7 +63,7 @@ void CLockNotifyProtocol::onGetNotification(CHyprlandLockNotifierV1* pMgr, uint3 void CLockNotifyProtocol::onLocked() { if UNLIKELY (m_isLocked) { - LOGM(Log::ERR, "Not sending lock notification. Already locked!"); + LOGM(ERR, "Not sending lock notification. Already locked!"); return; } @@ -76,7 +76,7 @@ void CLockNotifyProtocol::onLocked() { void CLockNotifyProtocol::onUnlocked() { if UNLIKELY (!m_isLocked) { - LOGM(Log::ERR, "Not sending unlock notification. Not locked!"); + LOGM(ERR, "Not sending unlock notification. Not locked!"); return; } diff --git a/src/protocols/MesaDRM.cpp b/src/protocols/MesaDRM.cpp index 8a0b08b7..caa539a2 100644 --- a/src/protocols/MesaDRM.cpp +++ b/src/protocols/MesaDRM.cpp @@ -6,53 +6,52 @@ #include "../render/OpenGL.hpp" CMesaDRMBufferResource::CMesaDRMBufferResource(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs_) { - LOGM(Log::DEBUG, "Creating a Mesa dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs_.size, attrs_.format, attrs_.planes); + LOGM(LOG, "Creating a Mesa dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs_.size, attrs_.format, attrs_.planes); for (int i = 0; i < attrs_.planes; ++i) { - LOGM(Log::DEBUG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs_.modifier, attrs_.fds[i], attrs_.strides[i], attrs_.offsets[i]); + LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs_.modifier, attrs_.fds[i], attrs_.strides[i], attrs_.offsets[i]); } - m_buffer = makeShared(id, client, attrs_); - m_buffer->m_resource->m_buffer = m_buffer; + buffer = makeShared(id, client, attrs_); + buffer->resource->buffer = buffer; - m_listeners.bufferResourceDestroy = m_buffer->events.destroy.listen([this] { - m_listeners.bufferResourceDestroy.reset(); + listeners.bufferResourceDestroy = buffer->events.destroy.registerListener([this](std::any d) { + listeners.bufferResourceDestroy.reset(); PROTO::mesaDRM->destroyResource(this); }); - if (!m_buffer->m_success) - LOGM(Log::ERR, "Possibly compositor bug: buffer failed to create"); + if (!buffer->success) + LOGM(ERR, "Possibly compositor bug: buffer failed to create"); } CMesaDRMBufferResource::~CMesaDRMBufferResource() { - if (m_buffer && m_buffer->m_resource) - m_buffer->m_resource->sendRelease(); - m_buffer.reset(); - m_listeners.bufferResourceDestroy.reset(); + if (buffer && buffer->resource) + buffer->resource->sendRelease(); + buffer.reset(); + listeners.bufferResourceDestroy.reset(); } bool CMesaDRMBufferResource::good() { - return m_buffer && m_buffer->good(); + return buffer && buffer->good(); } -CMesaDRMResource::CMesaDRMResource(SP resource_) : m_resource(resource_) { +CMesaDRMResource::CMesaDRMResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWlDrm* r) { PROTO::mesaDRM->destroyResource(this); }); + resource->setOnDestroy([this](CWlDrm* r) { PROTO::mesaDRM->destroyResource(this); }); - m_resource->setAuthenticate([this](CWlDrm* r, uint32_t token) { + resource->setAuthenticate([this](CWlDrm* r, uint32_t token) { // we don't need this - m_resource->sendAuthenticated(); + resource->sendAuthenticated(); }); - m_resource->setCreateBuffer( - [](CWlDrm* r, uint32_t, uint32_t, int32_t, int32_t, uint32_t, uint32_t) { r->error(WL_DRM_ERROR_INVALID_NAME, "Not supported, use prime instead"); }); + resource->setCreateBuffer([](CWlDrm* r, uint32_t, uint32_t, int32_t, int32_t, uint32_t, uint32_t) { r->error(WL_DRM_ERROR_INVALID_NAME, "Not supported, use prime instead"); }); - m_resource->setCreatePlanarBuffer([](CWlDrm* r, uint32_t, uint32_t, int32_t, int32_t, uint32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) { + resource->setCreatePlanarBuffer([](CWlDrm* r, uint32_t, uint32_t, int32_t, int32_t, uint32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) { r->error(WL_DRM_ERROR_INVALID_NAME, "Not supported, use prime instead"); }); - m_resource->setCreatePrimeBuffer( + resource->setCreatePrimeBuffer( [this](CWlDrm* r, uint32_t id, int32_t nameFd, int32_t w, int32_t h, uint32_t fmt, int32_t off0, int32_t str0, int32_t off1, int32_t str1, int32_t off2, int32_t str2) { if (off0 < 0 || w <= 0 || h <= 0) { r->error(WL_DRM_ERROR_INVALID_FORMAT, "Invalid w, h, or offset"); @@ -86,70 +85,72 @@ CMesaDRMResource::CMesaDRMResource(SP resource_) : m_resource(resource_) attrs.fds[0] = nameFd; attrs.format = fmt; - const auto RESOURCE = PROTO::mesaDRM->m_buffers.emplace_back(makeShared(id, m_resource->client(), attrs)); + const auto RESOURCE = PROTO::mesaDRM->m_vBuffers.emplace_back(makeShared(id, resource->client(), attrs)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::mesaDRM->m_buffers.pop_back(); + PROTO::mesaDRM->m_vBuffers.pop_back(); return; } // append instance so that buffer knows its owner - RESOURCE->m_buffer->m_resource->m_buffer = RESOURCE->m_buffer; + RESOURCE->buffer->resource->buffer = RESOURCE->buffer; }); - m_resource->sendDevice(PROTO::mesaDRM->m_nodeName.c_str()); - m_resource->sendCapabilities(WL_DRM_CAPABILITY_PRIME); + resource->sendDevice(PROTO::mesaDRM->nodeName.c_str()); + resource->sendCapabilities(WL_DRM_CAPABILITY_PRIME); auto fmts = g_pHyprOpenGL->getDRMFormats(); for (auto const& fmt : fmts) { - m_resource->sendFormat(fmt.drmFormat); + resource->sendFormat(fmt.drmFormat); } } bool CMesaDRMResource::good() { - return m_resource->resource(); + return resource->resource(); } CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { drmDevice* dev = nullptr; - int drmFD = g_pCompositor->m_drmRenderNode.fd >= 0 ? g_pCompositor->m_drmRenderNode.fd : g_pCompositor->m_drm.fd; - + int drmFD = g_pCompositor->m_iDRMFD; if (drmGetDevice2(drmFD, 0, &dev) != 0) { - LOGM(Log::ERR, "Failed to get device from fd {}, disabling MesaDRM", drmFD); + LOGM(ERR, "Failed to get device, disabling MesaDRM"); removeGlobal(); return; } - if (dev->available_nodes & (1 << DRM_NODE_RENDER) && dev->nodes[DRM_NODE_RENDER]) { - m_nodeName = dev->nodes[DRM_NODE_RENDER]; - } else if (dev->available_nodes & (1 << DRM_NODE_PRIMARY) && dev->nodes[DRM_NODE_PRIMARY]) { - LOGM(Log::WARN, "No DRM render node, falling back to primary {}", dev->nodes[DRM_NODE_PRIMARY]); - m_nodeName = dev->nodes[DRM_NODE_PRIMARY]; + if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { + nodeName = dev->nodes[DRM_NODE_RENDER]; } else { - LOGM(Log::ERR, "No usable DRM node (render or primary) found, disabling MesaDRM"); - drmFreeDevice(&dev); - removeGlobal(); - return; - } + ASSERT(dev->available_nodes & (1 << DRM_NODE_PRIMARY)); + if (!dev->nodes[DRM_NODE_PRIMARY]) { + LOGM(ERR, "No DRM render node available, both render and primary are null, disabling MesaDRM"); + drmFreeDevice(&dev); + removeGlobal(); + return; + } + + LOGM(WARN, "No DRM render node, falling back to primary {}", dev->nodes[DRM_NODE_PRIMARY]); + nodeName = dev->nodes[DRM_NODE_PRIMARY]; + } drmFreeDevice(&dev); } void CMesaDRMProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CMesaDRMProtocol::destroyResource(CMesaDRMResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CMesaDRMProtocol::destroyResource(CMesaDRMBufferResource* resource) { - std::erase_if(m_buffers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; }); } diff --git a/src/protocols/MesaDRM.hpp b/src/protocols/MesaDRM.hpp index d3d3a50a..bfa604cf 100644 --- a/src/protocols/MesaDRM.hpp +++ b/src/protocols/MesaDRM.hpp @@ -15,11 +15,11 @@ class CMesaDRMBufferResource { bool good(); private: - SP m_buffer; + SP buffer; struct { CHyprSignalListener bufferResourceDestroy; - } m_listeners; + } listeners; friend class CMesaDRMResource; }; @@ -31,7 +31,7 @@ class CMesaDRMResource { bool good(); private: - SP m_resource; + SP resource; }; class CMesaDRMProtocol : public IWaylandProtocol { @@ -45,10 +45,10 @@ class CMesaDRMProtocol : public IWaylandProtocol { void destroyResource(CMesaDRMBufferResource* resource); // - std::vector> m_managers; - std::vector> m_buffers; + std::vector> m_vManagers; + std::vector> m_vBuffers; - std::string m_nodeName = ""; + std::string nodeName = ""; friend class CMesaDRMResource; friend class CMesaDRMBufferResource; diff --git a/src/protocols/OutputManagement.cpp b/src/protocols/OutputManagement.cpp index f85578e2..ecca0c48 100644 --- a/src/protocols/OutputManagement.cpp +++ b/src/protocols/OutputManagement.cpp @@ -2,42 +2,40 @@ #include #include "../Compositor.hpp" #include "../managers/input/InputManager.hpp" +#include "../managers/HookSystemManager.hpp" #include "../config/ConfigManager.hpp" -#include "../event/EventBus.hpp" using namespace Aquamarine; -COutputManager::COutputManager(SP resource_) : m_resource(resource_) { +COutputManager::COutputManager(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - LOGM(Log::DEBUG, "New OutputManager registered"); + LOGM(LOG, "New OutputManager registered"); - m_resource->setOnDestroy([this](CZwlrOutputManagerV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrOutputManagerV1* r) { PROTO::outputManagement->destroyResource(this); }); - m_resource->setStop([this](CZwlrOutputManagerV1* r) { m_stopped = true; }); + resource->setStop([this](CZwlrOutputManagerV1* r) { stopped = true; }); - m_resource->setCreateConfiguration([this](CZwlrOutputManagerV1* r, uint32_t id, uint32_t serial) { - LOGM(Log::DEBUG, "Creating new configuration"); + resource->setCreateConfiguration([this](CZwlrOutputManagerV1* r, uint32_t id, uint32_t serial) { + LOGM(LOG, "Creating new configuration"); - const auto RESOURCE = PROTO::outputManagement->m_configurations.emplace_back( - makeShared(makeShared(m_resource->client(), m_resource->version(), id), m_self.lock())); + const auto RESOURCE = PROTO::outputManagement->m_vConfigurations.emplace_back( + makeShared(makeShared(resource->client(), resource->version(), id), self.lock())); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::outputManagement->m_configurations.pop_back(); + resource->noMemory(); + PROTO::outputManagement->m_vConfigurations.pop_back(); return; } - - RESOURCE->m_self = RESOURCE; }); // send all heads at start - for (auto const& m : g_pCompositor->m_realMonitors) { - if (m == g_pCompositor->m_unsafeOutput) + for (auto const& m : g_pCompositor->m_vRealMonitors) { + if (m == g_pCompositor->m_pUnsafeOutput) continue; - LOGM(Log::DEBUG, " | sending output head for {}", m->m_name); + LOGM(LOG, " | sending output head for {}", m->szName); makeAndSendNewHead(m); } @@ -46,39 +44,39 @@ COutputManager::COutputManager(SP resource_) : m_resource( } bool COutputManager::good() { - return m_resource->resource(); + return resource->resource(); } void COutputManager::makeAndSendNewHead(PHLMONITOR pMonitor) { - if UNLIKELY (m_stopped) + if UNLIKELY (stopped) return; const auto RESOURCE = - PROTO::outputManagement->m_heads.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), 0), pMonitor)); + PROTO::outputManagement->m_vHeads.emplace_back(makeShared(makeShared(resource->client(), resource->version(), 0), pMonitor)); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::outputManagement->m_heads.pop_back(); + resource->noMemory(); + PROTO::outputManagement->m_vHeads.pop_back(); return; } - m_heads.emplace_back(RESOURCE); + heads.emplace_back(RESOURCE); - m_resource->sendHead(RESOURCE->m_resource.get()); + resource->sendHead(RESOURCE->resource.get()); RESOURCE->sendAllData(); } void COutputManager::ensureMonitorSent(PHLMONITOR pMonitor) { - if (pMonitor == g_pCompositor->m_unsafeOutput) + if (pMonitor == g_pCompositor->m_pUnsafeOutput) return; - for (auto const& hw : m_heads) { + for (auto const& hw : heads) { auto h = hw.lock(); if (!h) continue; - if (h->m_monitor == pMonitor) + if (h->pMonitor == pMonitor) return; } @@ -88,93 +86,93 @@ void COutputManager::ensureMonitorSent(PHLMONITOR pMonitor) { } void COutputManager::sendDone() { - m_resource->sendDone(wl_display_next_serial(g_pCompositor->m_wlDisplay)); + resource->sendDone(wl_display_next_serial(g_pCompositor->m_sWLDisplay)); } -COutputHead::COutputHead(SP resource_, PHLMONITOR pMonitor_) : m_resource(resource_), m_monitor(pMonitor_) { +COutputHead::COutputHead(SP resource_, PHLMONITOR pMonitor_) : resource(resource_), pMonitor(pMonitor_) { if UNLIKELY (!good()) return; - m_resource->setRelease([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setRelease([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrOutputHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); - m_listeners.monitorDestroy = m_monitor->m_events.destroy.listen([this] { - m_resource->sendFinished(); + listeners.monitorDestroy = pMonitor->events.destroy.registerListener([this](std::any d) { + resource->sendFinished(); - for (auto const& mw : m_modes) { + for (auto const& mw : modes) { auto m = mw.lock(); if (!m) continue; - m->m_resource->sendFinished(); + m->resource->sendFinished(); } - m_monitor.reset(); - for (auto const& m : PROTO::outputManagement->m_managers) { + pMonitor.reset(); + for (auto const& m : PROTO::outputManagement->m_vManagers) { m->sendDone(); } }); - m_listeners.monitorModeChange = m_monitor->m_events.modeChanged.listen([this] { updateMode(); }); + listeners.monitorModeChange = pMonitor->events.modeChanged.registerListener([this](std::any d) { updateMode(); }); } bool COutputHead::good() { - return m_resource->resource(); + return resource->resource(); } void COutputHead::sendAllData() { - const auto VERSION = m_resource->version(); + const auto VERSION = resource->version(); - m_resource->sendName(m_monitor->m_name.c_str()); - m_resource->sendDescription(m_monitor->m_description.c_str()); - if (m_monitor->m_output->physicalSize.x > 0 && m_monitor->m_output->physicalSize.y > 0) - m_resource->sendPhysicalSize(m_monitor->m_output->physicalSize.x, m_monitor->m_output->physicalSize.y); - m_resource->sendEnabled(m_monitor->m_enabled); + resource->sendName(pMonitor->szName.c_str()); + resource->sendDescription(pMonitor->szDescription.c_str()); + if (pMonitor->output->physicalSize.x > 0 && pMonitor->output->physicalSize.y > 0) + resource->sendPhysicalSize(pMonitor->output->physicalSize.x, pMonitor->output->physicalSize.y); + resource->sendEnabled(pMonitor->m_bEnabled); - if (m_monitor->m_enabled) { - m_resource->sendPosition(m_monitor->m_position.x, m_monitor->m_position.y); - m_resource->sendTransform(m_monitor->m_transform); - m_resource->sendScale(wl_fixed_from_double(m_monitor->m_scale)); + if (pMonitor->m_bEnabled) { + resource->sendPosition(pMonitor->vecPosition.x, pMonitor->vecPosition.y); + resource->sendTransform(pMonitor->transform); + resource->sendScale(wl_fixed_from_double(pMonitor->scale)); } - if (!m_monitor->m_output->make.empty() && VERSION >= 2) - m_resource->sendMake(m_monitor->m_output->make.c_str()); - if (!m_monitor->m_output->model.empty() && VERSION >= 2) - m_resource->sendModel(m_monitor->m_output->model.c_str()); - if (!m_monitor->m_output->serial.empty() && VERSION >= 2) - m_resource->sendSerialNumber(m_monitor->m_output->serial.c_str()); + if (!pMonitor->output->make.empty() && VERSION >= 2) + resource->sendMake(pMonitor->output->make.c_str()); + if (!pMonitor->output->model.empty() && VERSION >= 2) + resource->sendModel(pMonitor->output->model.c_str()); + if (!pMonitor->output->serial.empty() && VERSION >= 2) + resource->sendSerialNumber(pMonitor->output->serial.c_str()); if (VERSION >= 4) - m_resource->sendAdaptiveSync(m_monitor->m_vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); + resource->sendAdaptiveSync(pMonitor->vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); // send all available modes - if (m_modes.empty()) { - if (!m_monitor->m_output->modes.empty()) { - for (auto const& m : m_monitor->m_output->modes) { + if (modes.empty()) { + if (!pMonitor->output->modes.empty()) { + for (auto const& m : pMonitor->output->modes) { makeAndSendNewMode(m); } - } else if (m_monitor->m_output->state->state().customMode) { - makeAndSendNewMode(m_monitor->m_output->state->state().customMode); + } else if (pMonitor->output->state->state().customMode) { + makeAndSendNewMode(pMonitor->output->state->state().customMode); } else makeAndSendNewMode(nullptr); } // send current mode - if (m_monitor->m_enabled) { - for (auto const& mw : m_modes) { + if (pMonitor->m_bEnabled) { + for (auto const& mw : modes) { auto m = mw.lock(); if (!m) continue; - if (m->m_mode == m_monitor->m_output->state->state().mode) { - if (m->m_mode) - LOGM(Log::DEBUG, " | sending current mode for {}: {}x{}@{}", m_monitor->m_name, m->m_mode->pixelSize.x, m->m_mode->pixelSize.y, m->m_mode->refreshRate); + if (m->mode == pMonitor->output->state->state().mode) { + if (m->mode) + LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->pixelSize.x, m->mode->pixelSize.y, m->mode->refreshRate); else - LOGM(Log::DEBUG, " | sending current mode for {}: null (fake)", m_monitor->m_name); - m_resource->sendCurrentMode(m->m_resource.get()); + LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName); + resource->sendCurrentMode(m->resource.get()); break; } } @@ -182,30 +180,30 @@ void COutputHead::sendAllData() { } void COutputHead::updateMode() { - m_resource->sendEnabled(m_monitor->m_enabled); + resource->sendEnabled(pMonitor->m_bEnabled); - if (m_monitor->m_enabled) { - m_resource->sendPosition(m_monitor->m_position.x, m_monitor->m_position.y); - m_resource->sendTransform(m_monitor->m_transform); - m_resource->sendScale(wl_fixed_from_double(m_monitor->m_scale)); + if (pMonitor->m_bEnabled) { + resource->sendPosition(pMonitor->vecPosition.x, pMonitor->vecPosition.y); + resource->sendTransform(pMonitor->transform); + resource->sendScale(wl_fixed_from_double(pMonitor->scale)); } - if (m_resource->version() >= 4) - m_resource->sendAdaptiveSync(m_monitor->m_vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); + if (resource->version() >= 4) + resource->sendAdaptiveSync(pMonitor->vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); - if (m_monitor->m_enabled) { - for (auto const& mw : m_modes) { + if (pMonitor->m_bEnabled) { + for (auto const& mw : modes) { auto m = mw.lock(); if (!m) continue; - if (m->m_mode == m_monitor->m_currentMode) { - if (m->m_mode) - LOGM(Log::DEBUG, " | sending current mode for {}: {}x{}@{}", m_monitor->m_name, m->m_mode->pixelSize.x, m->m_mode->pixelSize.y, m->m_mode->refreshRate); + if (m->mode == pMonitor->currentMode) { + if (m->mode) + LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->pixelSize.x, m->mode->pixelSize.y, m->mode->refreshRate); else - LOGM(Log::DEBUG, " | sending current mode for {}: null (fake)", m_monitor->m_name); - m_resource->sendCurrentMode(m->m_resource.get()); + LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName); + resource->sendCurrentMode(m->resource.get()); break; } } @@ -213,420 +211,416 @@ void COutputHead::updateMode() { } void COutputHead::makeAndSendNewMode(SP mode) { - const auto RESOURCE = - PROTO::outputManagement->m_modes.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), 0), mode)); + const auto RESOURCE = PROTO::outputManagement->m_vModes.emplace_back(makeShared(makeShared(resource->client(), resource->version(), 0), mode)); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::outputManagement->m_modes.pop_back(); + resource->noMemory(); + PROTO::outputManagement->m_vModes.pop_back(); return; } - m_modes.emplace_back(RESOURCE); - m_resource->sendMode(RESOURCE->m_resource.get()); + modes.emplace_back(RESOURCE); + resource->sendMode(RESOURCE->resource.get()); RESOURCE->sendAllData(); } PHLMONITOR COutputHead::monitor() { - return m_monitor.lock(); + return pMonitor.lock(); } -COutputMode::COutputMode(SP resource_, SP mode_) : m_resource(resource_), m_mode(mode_) { +COutputMode::COutputMode(SP resource_, SP mode_) : resource(resource_), mode(mode_) { if UNLIKELY (!good()) return; - m_resource->setRelease([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setRelease([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrOutputModeV1* r) { PROTO::outputManagement->destroyResource(this); }); } void COutputMode::sendAllData() { - if (!m_mode) + if (!mode) return; - LOGM(Log::DEBUG, " | sending mode {}x{}@{}mHz, pref: {}", m_mode->pixelSize.x, m_mode->pixelSize.y, m_mode->refreshRate, m_mode->preferred); + LOGM(LOG, " | sending mode {}x{}@{}mHz, pref: {}", mode->pixelSize.x, mode->pixelSize.y, mode->refreshRate, mode->preferred); - m_resource->sendSize(m_mode->pixelSize.x, m_mode->pixelSize.y); - if (m_mode->refreshRate > 0) - m_resource->sendRefresh(m_mode->refreshRate); - if (m_mode->preferred) - m_resource->sendPreferred(); + resource->sendSize(mode->pixelSize.x, mode->pixelSize.y); + if (mode->refreshRate > 0) + resource->sendRefresh(mode->refreshRate); + if (mode->preferred) + resource->sendPreferred(); } bool COutputMode::good() { - return m_resource->resource(); + return resource->resource(); } SP COutputMode::getMode() { - return m_mode.lock(); + return mode.lock(); } -COutputConfiguration::COutputConfiguration(SP resource_, SP owner_) : m_resource(resource_), m_owner(owner_) { +COutputConfiguration::COutputConfiguration(SP resource_, SP owner_) : resource(resource_), owner(owner_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrOutputConfigurationV1* r) { PROTO::outputManagement->destroyResource(this); }); - m_resource->setEnableHead([this](CZwlrOutputConfigurationV1* r, uint32_t id, wl_resource* outputHead) { + resource->setEnableHead([this](CZwlrOutputConfigurationV1* r, uint32_t id, wl_resource* outputHead) { const auto HEAD = PROTO::outputManagement->headFromResource(outputHead); if (!HEAD) { - LOGM(Log::ERR, "No head in setEnableHead??"); + LOGM(ERR, "No head in setEnableHead??"); return; } const auto PMONITOR = HEAD->monitor(); if (!PMONITOR) { - LOGM(Log::ERR, "No monitor in setEnableHead??"); + LOGM(ERR, "No monitor in setEnableHead??"); return; } - const auto RESOURCE = PROTO::outputManagement->m_configurationHeads.emplace_back( - makeShared(makeShared(m_resource->client(), m_resource->version(), id), PMONITOR)); + const auto RESOURCE = PROTO::outputManagement->m_vConfigurationHeads.emplace_back( + makeShared(makeShared(resource->client(), resource->version(), id), PMONITOR)); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::outputManagement->m_configurationHeads.pop_back(); + resource->noMemory(); + PROTO::outputManagement->m_vConfigurationHeads.pop_back(); return; } - m_heads.emplace_back(RESOURCE); + heads.emplace_back(RESOURCE); - LOGM(Log::DEBUG, "enableHead on {}. For now, doing nothing. Waiting for apply().", PMONITOR->m_name); + LOGM(LOG, "enableHead on {}. For now, doing nothing. Waiting for apply().", PMONITOR->szName); }); - m_resource->setDisableHead([this](CZwlrOutputConfigurationV1* r, wl_resource* outputHead) { + resource->setDisableHead([this](CZwlrOutputConfigurationV1* r, wl_resource* outputHead) { const auto HEAD = PROTO::outputManagement->headFromResource(outputHead); if (!HEAD) { - LOGM(Log::ERR, "No head in setDisableHead??"); + LOGM(ERR, "No head in setDisableHead??"); return; } const auto PMONITOR = HEAD->monitor(); if (!PMONITOR) { - LOGM(Log::ERR, "No monitor in setDisableHead??"); + LOGM(ERR, "No monitor in setDisableHead??"); return; } - LOGM(Log::DEBUG, "disableHead on {}", PMONITOR->m_name); + LOGM(LOG, "disableHead on {}", PMONITOR->szName); SWlrManagerSavedOutputState newState; - if (m_owner->m_monitorStates.contains(PMONITOR->m_name)) - newState = m_owner->m_monitorStates.at(PMONITOR->m_name); + if (owner->monitorStates.contains(PMONITOR->szName)) + newState = owner->monitorStates.at(PMONITOR->szName); newState.enabled = false; - g_pConfigManager->m_wantsMonitorReload = true; + g_pConfigManager->m_bWantsMonitorReload = true; - m_owner->m_monitorStates[PMONITOR->m_name] = newState; + owner->monitorStates[PMONITOR->szName] = newState; }); - m_resource->setTest([this](CZwlrOutputConfigurationV1* r) { + resource->setTest([this](CZwlrOutputConfigurationV1* r) { const auto SUCCESS = applyTestConfiguration(true); if (SUCCESS) - m_resource->sendSucceeded(); + resource->sendSucceeded(); else - m_resource->sendFailed(); + resource->sendFailed(); }); - m_resource->setApply([this](CZwlrOutputConfigurationV1* r) { + resource->setApply([this](CZwlrOutputConfigurationV1* r) { const auto SUCCESS = applyTestConfiguration(false); if (SUCCESS) - PROTO::outputManagement->m_pendingConfigurationSuccessEvents.emplace_back(m_self); + resource->sendSucceeded(); else - m_resource->sendFailed(); + resource->sendFailed(); - m_owner->sendDone(); + owner->sendDone(); }); } bool COutputConfiguration::good() { - return m_resource->resource(); + return resource->resource(); } bool COutputConfiguration::applyTestConfiguration(bool test) { if (test) { - LOGM(Log::WARN, "TODO: STUB: applyTestConfiguration for test not implemented, returning true."); + LOGM(WARN, "TODO: STUB: applyTestConfiguration for test not implemented, returning true."); return true; } - LOGM(Log::DEBUG, "Applying configuration"); + LOGM(LOG, "Applying configuration"); - if (!m_owner) { - LOGM(Log::ERR, "applyTestConfiguration: no owner?!"); + if (!owner) { + LOGM(ERR, "applyTestConfiguration: no owner?!"); return false; } - for (auto const& headw : m_heads) { + for (auto const& headw : heads) { auto head = headw.lock(); if (!head) continue; - const auto PMONITOR = head->m_monitor; + const auto PMONITOR = head->pMonitor; if (!PMONITOR) continue; - LOGM(Log::DEBUG, "Saving config for monitor {}", PMONITOR->m_name); + LOGM(LOG, "Saving config for monitor {}", PMONITOR->szName); SWlrManagerSavedOutputState newState; - if (m_owner->m_monitorStates.contains(PMONITOR->m_name)) - newState = m_owner->m_monitorStates.at(PMONITOR->m_name); + if (owner->monitorStates.contains(PMONITOR->szName)) + newState = owner->monitorStates.at(PMONITOR->szName); newState.enabled = true; - if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) { - newState.resolution = head->m_state.mode->getMode()->pixelSize; - newState.refresh = head->m_state.mode->getMode()->refreshRate; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) { + newState.resolution = head->state.mode->getMode()->pixelSize; + newState.refresh = head->state.mode->getMode()->refreshRate; newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE; - LOGM(Log::DEBUG, " > Mode: {:.0f}x{:.0f}@{}mHz", newState.resolution.x, newState.resolution.y, newState.refresh); - } else if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) { - newState.resolution = head->m_state.customMode.size; - newState.refresh = head->m_state.customMode.refresh; + LOGM(LOG, " > Mode: {:.0f}x{:.0f}@{}mHz", newState.resolution.x, newState.resolution.y, newState.refresh); + } else if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) { + newState.resolution = head->state.customMode.size; + newState.refresh = head->state.customMode.refresh; newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE; - LOGM(Log::DEBUG, " > Custom mode: {:.0f}x{:.0f}@{}mHz", newState.resolution.x, newState.resolution.y, newState.refresh); + LOGM(LOG, " > Custom mode: {:.0f}x{:.0f}@{}mHz", newState.resolution.x, newState.resolution.y, newState.refresh); } - if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION) { - newState.position = head->m_state.position; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION) { + newState.position = head->state.position; newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_POSITION; - LOGM(Log::DEBUG, " > Position: {:.0f}, {:.0f}", head->m_state.position.x, head->m_state.position.y); + LOGM(LOG, " > Position: {:.0f}, {:.0f}", head->state.position.x, head->state.position.y); } - if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) { - newState.adaptiveSync = head->m_state.adaptiveSync; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) { + newState.adaptiveSync = head->state.adaptiveSync; newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC; - LOGM(Log::DEBUG, " > vrr: {}", newState.adaptiveSync); + LOGM(LOG, " > vrr: {}", newState.adaptiveSync); } - if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE) { - newState.scale = head->m_state.scale; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE) { + newState.scale = head->state.scale; newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_SCALE; - LOGM(Log::DEBUG, " > scale: {:.2f}", newState.scale); + LOGM(LOG, " > scale: {:.2f}", newState.scale); } - if (head->m_state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM) { - newState.transform = head->m_state.transform; + if (head->state.committedProperties & eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM) { + newState.transform = head->state.transform; newState.committedProperties |= eWlrOutputCommittedProperties::OUTPUT_HEAD_COMMITTED_TRANSFORM; - LOGM(Log::DEBUG, " > transform: {}", (uint8_t)newState.transform); + LOGM(LOG, " > transform: {}", (uint8_t)newState.transform); } // reset properties for next set. - head->m_state.committedProperties = 0; + head->state.committedProperties = 0; - g_pConfigManager->m_wantsMonitorReload = true; + g_pConfigManager->m_bWantsMonitorReload = true; - m_owner->m_monitorStates[PMONITOR->m_name] = newState; + owner->monitorStates[PMONITOR->szName] = newState; } - LOGM(Log::DEBUG, "Saved configuration"); + LOGM(LOG, "Saved configuration"); return true; } -COutputConfigurationHead::COutputConfigurationHead(SP resource_, PHLMONITOR pMonitor_) : m_resource(resource_), m_monitor(pMonitor_) { +COutputConfigurationHead::COutputConfigurationHead(SP resource_, PHLMONITOR pMonitor_) : resource(resource_), pMonitor(pMonitor_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CZwlrOutputConfigurationHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrOutputConfigurationHeadV1* r) { PROTO::outputManagement->destroyResource(this); }); - m_resource->setSetMode([this](CZwlrOutputConfigurationHeadV1* r, wl_resource* outputMode) { + resource->setSetMode([this](CZwlrOutputConfigurationHeadV1* r, wl_resource* outputMode) { const auto MODE = PROTO::outputManagement->modeFromResource(outputMode); if (!MODE || !MODE->getMode()) { - LOGM(Log::ERR, "No mode in setMode??"); + LOGM(ERR, "No mode in setMode??"); return; } - if (!m_monitor) { - LOGM(Log::ERR, "setMode on inert resource"); + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); return; } - if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_MODE) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + if (state.committedProperties & OUTPUT_HEAD_COMMITTED_MODE) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } - m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_MODE; - m_state.mode = MODE; + state.committedProperties |= OUTPUT_HEAD_COMMITTED_MODE; + state.mode = MODE; - LOGM(Log::DEBUG, " | configHead for {}: set mode to {}x{}@{}", m_monitor->m_name, MODE->getMode()->pixelSize.x, MODE->getMode()->pixelSize.y, MODE->getMode()->refreshRate); + LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", pMonitor->szName, MODE->getMode()->pixelSize.x, MODE->getMode()->pixelSize.y, MODE->getMode()->refreshRate); }); - m_resource->setSetCustomMode([this](CZwlrOutputConfigurationHeadV1* r, int32_t w, int32_t h, int32_t refresh) { - if (!m_monitor) { - LOGM(Log::ERR, "setCustomMode on inert resource"); + resource->setSetCustomMode([this](CZwlrOutputConfigurationHeadV1* r, int32_t w, int32_t h, int32_t refresh) { + if (!pMonitor) { + LOGM(ERR, "setCustomMode on inert resource"); return; } - if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + if (state.committedProperties & OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } if (w <= 0 || h <= 0 || refresh < 0) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_CUSTOM_MODE, "Invalid mode"); + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_CUSTOM_MODE, "Invalid mode"); return; } if (refresh == 0) { - LOGM(Log::DEBUG, " | configHead for {}: refreshRate 0, using old refresh rate of {:.2f}Hz", m_monitor->m_name, m_monitor->m_refreshRate); - refresh = std::round(m_monitor->m_refreshRate * 1000.F); + LOGM(LOG, " | configHead for {}: refreshRate 0, using old refresh rate of {:.2f}Hz", pMonitor->szName, pMonitor->refreshRate); + refresh = std::round(pMonitor->refreshRate * 1000.F); } - m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_CUSTOM_MODE; - m_state.customMode = {{w, h}, sc(refresh)}; + state.committedProperties |= OUTPUT_HEAD_COMMITTED_CUSTOM_MODE; + state.customMode = {{w, h}, (uint32_t)refresh}; - LOGM(Log::DEBUG, " | configHead for {}: set custom mode to {}x{}@{}", m_monitor->m_name, w, h, refresh); + LOGM(LOG, " | configHead for {}: set custom mode to {}x{}@{}", pMonitor->szName, w, h, refresh); }); - m_resource->setSetPosition([this](CZwlrOutputConfigurationHeadV1* r, int32_t x, int32_t y) { - if (!m_monitor) { - LOGM(Log::ERR, "setMode on inert resource"); + resource->setSetPosition([this](CZwlrOutputConfigurationHeadV1* r, int32_t x, int32_t y) { + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); return; } - if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_POSITION) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + if (state.committedProperties & OUTPUT_HEAD_COMMITTED_POSITION) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } - m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_POSITION; - m_state.position = {x, y}; + state.committedProperties |= OUTPUT_HEAD_COMMITTED_POSITION; + state.position = {x, y}; - LOGM(Log::DEBUG, " | configHead for {}: set pos to {}, {}", m_monitor->m_name, x, y); + LOGM(LOG, " | configHead for {}: set pos to {}, {}", pMonitor->szName, x, y); }); - m_resource->setSetTransform([this](CZwlrOutputConfigurationHeadV1* r, int32_t transform) { - if (!m_monitor) { - LOGM(Log::ERR, "setMode on inert resource"); + resource->setSetTransform([this](CZwlrOutputConfigurationHeadV1* r, int32_t transform) { + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); return; } - if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_TRANSFORM) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + if (state.committedProperties & OUTPUT_HEAD_COMMITTED_TRANSFORM) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } if (transform < 0 || transform > 7) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM, "Invalid transform"); + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM, "Invalid transform"); return; } - m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_TRANSFORM; - m_state.transform = sc(transform); + state.committedProperties |= OUTPUT_HEAD_COMMITTED_TRANSFORM; + state.transform = (wl_output_transform)transform; - LOGM(Log::DEBUG, " | configHead for {}: set transform to {}", m_monitor->m_name, transform); + LOGM(LOG, " | configHead for {}: set transform to {}", pMonitor->szName, transform); }); - m_resource->setSetScale([this](CZwlrOutputConfigurationHeadV1* r, wl_fixed_t scale_) { - if (!m_monitor) { - LOGM(Log::ERR, "setMode on inert resource"); + resource->setSetScale([this](CZwlrOutputConfigurationHeadV1* r, wl_fixed_t scale_) { + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); return; } - if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_SCALE) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + if (state.committedProperties & OUTPUT_HEAD_COMMITTED_SCALE) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } double scale = wl_fixed_to_double(scale_); if (scale < 0.1 || scale > 10.0) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_SCALE, "Invalid scale"); + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_SCALE, "Invalid scale"); return; } - m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_SCALE; - m_state.scale = scale; + state.committedProperties |= OUTPUT_HEAD_COMMITTED_SCALE; + state.scale = scale; - LOGM(Log::DEBUG, " | configHead for {}: set scale to {:.2f}", m_monitor->m_name, scale); + LOGM(LOG, " | configHead for {}: set scale to {:.2f}", pMonitor->szName, scale); }); - m_resource->setSetAdaptiveSync([this](CZwlrOutputConfigurationHeadV1* r, uint32_t as) { - if (!m_monitor) { - LOGM(Log::ERR, "setMode on inert resource"); + resource->setSetAdaptiveSync([this](CZwlrOutputConfigurationHeadV1* r, uint32_t as) { + if (!pMonitor) { + LOGM(ERR, "setMode on inert resource"); return; } - if (m_state.committedProperties & OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); + if (state.committedProperties & OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC) { + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_ALREADY_SET, "Property already set"); return; } if (as > 1) { - m_resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE, "Invalid adaptive sync state"); + resource->error(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE, "Invalid adaptive sync state"); return; } - m_state.committedProperties |= OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC; - m_state.adaptiveSync = as; + state.committedProperties |= OUTPUT_HEAD_COMMITTED_ADAPTIVE_SYNC; + state.adaptiveSync = as; - LOGM(Log::DEBUG, " | configHead for {}: set adaptiveSync to {}", m_monitor->m_name, as); + LOGM(LOG, " | configHead for {}: set adaptiveSync to {}", pMonitor->szName, as); }); } bool COutputConfigurationHead::good() { - return m_resource->resource(); + return resource->resource(); } COutputManagementProtocol::COutputManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P = Event::bus()->m_events.monitor.layoutChanged.listen([this] { - updateAllOutputs(); - sendPendingSuccessEvents(); - }); + static auto P = g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); }); } void COutputManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; } void COutputManagementProtocol::destroyResource(COutputManager* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void COutputManagementProtocol::destroyResource(COutputHead* resource) { - std::erase_if(m_heads, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vHeads, [&](const auto& other) { return other.get() == resource; }); } void COutputManagementProtocol::destroyResource(COutputMode* resource) { - std::erase_if(m_modes, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vModes, [&](const auto& other) { return other.get() == resource; }); } void COutputManagementProtocol::destroyResource(COutputConfiguration* resource) { - std::erase_if(m_configurations, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vConfigurations, [&](const auto& other) { return other.get() == resource; }); } void COutputManagementProtocol::destroyResource(COutputConfigurationHead* resource) { - std::erase_if(m_configurationHeads, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vConfigurationHeads, [&](const auto& other) { return other.get() == resource; }); } void COutputManagementProtocol::updateAllOutputs() { - for (auto const& m : g_pCompositor->m_realMonitors) { - for (auto const& mgr : m_managers) { + for (auto const& m : g_pCompositor->m_vRealMonitors) { + for (auto const& mgr : m_vManagers) { mgr->ensureMonitorSent(m); } } } SP COutputManagementProtocol::headFromResource(wl_resource* r) { - for (auto const& h : m_heads) { - if (h->m_resource->resource() == r) + for (auto const& h : m_vHeads) { + if (h->resource->resource() == r) return h; } @@ -634,8 +628,8 @@ SP COutputManagementProtocol::headFromResource(wl_resource* r) { } SP COutputManagementProtocol::modeFromResource(wl_resource* r) { - for (auto const& h : m_modes) { - if (h->m_resource->resource() == r) + for (auto const& h : m_vModes) { + if (h->resource->resource() == r) return h; } @@ -643,28 +637,12 @@ SP COutputManagementProtocol::modeFromResource(wl_resource* r) { } SP COutputManagementProtocol::getOutputStateFor(PHLMONITOR pMonitor) { - for (auto const& m : m_managers) { - if (!m->m_monitorStates.contains(pMonitor->m_name)) + for (auto const& m : m_vManagers) { + if (!m->monitorStates.contains(pMonitor->szName)) continue; - return makeShared(m->m_monitorStates.at(pMonitor->m_name)); + return makeShared(m->monitorStates.at(pMonitor->szName)); } return nullptr; } - -void COutputManagementProtocol::sendPendingSuccessEvents() { - if (m_pendingConfigurationSuccessEvents.empty()) - return; - - LOGM(Log::DEBUG, "Sending {} pending configuration success events", m_pendingConfigurationSuccessEvents.size()); - - for (auto const& config : m_pendingConfigurationSuccessEvents) { - if (!config) - continue; - - config->m_resource->sendSucceeded(); - } - - m_pendingConfigurationSuccessEvents.clear(); -} diff --git a/src/protocols/OutputManagement.hpp b/src/protocols/OutputManagement.hpp index f26e9c91..1594df5d 100644 --- a/src/protocols/OutputManagement.hpp +++ b/src/protocols/OutputManagement.hpp @@ -59,15 +59,15 @@ class COutputManager { void sendDone(); // holds the states for this manager. - std::unordered_map m_monitorStates; + std::unordered_map monitorStates; private: - SP m_resource; - bool m_stopped = false; + SP resource; + bool stopped = false; - WP m_self; + WP self; - std::vector> m_heads; + std::vector> heads; void makeAndSendNewHead(PHLMONITOR pMonitor); friend class COutputManagementProtocol; @@ -82,8 +82,8 @@ class COutputMode { void sendAllData(); private: - SP m_resource; - WP m_mode; + SP resource; + WP mode; friend class COutputHead; friend class COutputManagementProtocol; @@ -99,18 +99,18 @@ class COutputHead { PHLMONITOR monitor(); private: - SP m_resource; - PHLMONITORREF m_monitor; + SP resource; + PHLMONITORREF pMonitor; void makeAndSendNewMode(SP mode); void sendCurrentMode(); - std::vector> m_modes; + std::vector> modes; struct { CHyprSignalListener monitorDestroy; CHyprSignalListener monitorModeChange; - } m_listeners; + } listeners; friend class COutputManager; friend class COutputManagementProtocol; @@ -122,11 +122,11 @@ class COutputConfigurationHead { bool good(); - SWlrManagerOutputState m_state; + SWlrManagerOutputState state; private: - SP m_resource; - PHLMONITORREF m_monitor; + SP resource; + PHLMONITORREF pMonitor; friend class COutputConfiguration; }; @@ -138,15 +138,11 @@ class COutputConfiguration { bool good(); private: - SP m_resource; - std::vector> m_heads; - WP m_owner; - WP m_self; + SP resource; + std::vector> heads; + WP owner; bool applyTestConfiguration(bool test); - - friend class COutputManagementProtocol; - friend class COutputManager; }; class COutputManagementProtocol : public IWaylandProtocol { @@ -158,8 +154,6 @@ class COutputManagementProtocol : public IWaylandProtocol { // doesn't have to return one SP getOutputStateFor(PHLMONITOR pMonitor); - void sendPendingSuccessEvents(); - private: void destroyResource(COutputManager* resource); void destroyResource(COutputHead* resource); @@ -170,12 +164,11 @@ class COutputManagementProtocol : public IWaylandProtocol { void updateAllOutputs(); // - std::vector> m_managers; - std::vector> m_heads; - std::vector> m_modes; - std::vector> m_configurations; - std::vector> m_configurationHeads; - std::vector> m_pendingConfigurationSuccessEvents; + std::vector> m_vManagers; + std::vector> m_vHeads; + std::vector> m_vModes; + std::vector> m_vConfigurations; + std::vector> m_vConfigurationHeads; SP headFromResource(wl_resource* r); SP modeFromResource(wl_resource* r); diff --git a/src/protocols/OutputPower.cpp b/src/protocols/OutputPower.cpp index f97abf85..24106817 100644 --- a/src/protocols/OutputPower.cpp +++ b/src/protocols/OutputPower.cpp @@ -2,35 +2,40 @@ #include "core/Output.hpp" #include "../helpers/Monitor.hpp" -COutputPower::COutputPower(SP resource_, PHLMONITOR pMonitor_) : m_resource(resource_), m_monitor(pMonitor_) { - if UNLIKELY (!m_resource->resource()) +COutputPower::COutputPower(SP resource_, PHLMONITOR pMonitor_) : resource(resource_), pMonitor(pMonitor_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setDestroy([this](CZwlrOutputPowerV1* r) { PROTO::outputPower->destroyOutputPower(this); }); - m_resource->setOnDestroy([this](CZwlrOutputPowerV1* r) { PROTO::outputPower->destroyOutputPower(this); }); + resource->setDestroy([this](CZwlrOutputPowerV1* r) { PROTO::outputPower->destroyOutputPower(this); }); + resource->setOnDestroy([this](CZwlrOutputPowerV1* r) { PROTO::outputPower->destroyOutputPower(this); }); - m_resource->setSetMode([this](CZwlrOutputPowerV1* r, zwlrOutputPowerV1Mode mode) { - if (!m_monitor) + resource->setSetMode([this](CZwlrOutputPowerV1* r, zwlrOutputPowerV1Mode mode) { + if (!pMonitor) return; - m_monitor->setDPMS(mode == ZWLR_OUTPUT_POWER_V1_MODE_ON); + pMonitor->dpmsStatus = mode == ZWLR_OUTPUT_POWER_V1_MODE_ON; + + pMonitor->output->state->setEnabled(mode == ZWLR_OUTPUT_POWER_V1_MODE_ON); + + if (!pMonitor->state.commit()) + LOGM(ERR, "Couldn't set dpms to {} for {}", pMonitor->dpmsStatus, pMonitor->szName); }); - m_resource->sendMode(m_monitor->m_dpmsStatus ? ZWLR_OUTPUT_POWER_V1_MODE_ON : ZWLR_OUTPUT_POWER_V1_MODE_OFF); + resource->sendMode(pMonitor->dpmsStatus ? ZWLR_OUTPUT_POWER_V1_MODE_ON : ZWLR_OUTPUT_POWER_V1_MODE_OFF); - m_listeners.monitorDestroy = m_monitor->m_events.destroy.listen([this] { - m_monitor.reset(); - m_resource->sendFailed(); + listeners.monitorDestroy = pMonitor->events.destroy.registerListener([this](std::any v) { + pMonitor.reset(); + resource->sendFailed(); }); - m_listeners.monitorDpms = - m_monitor->m_events.dpmsChanged.listen([this] { m_resource->sendMode(m_monitor->m_dpmsStatus ? ZWLR_OUTPUT_POWER_V1_MODE_ON : ZWLR_OUTPUT_POWER_V1_MODE_OFF); }); - m_listeners.monitorState = - m_monitor->m_events.modeChanged.listen([this] { m_resource->sendMode(m_monitor->m_dpmsStatus ? ZWLR_OUTPUT_POWER_V1_MODE_ON : ZWLR_OUTPUT_POWER_V1_MODE_OFF); }); + listeners.monitorDpms = pMonitor->events.dpmsChanged.registerListener( + [this](std::any v) { resource->sendMode(pMonitor->dpmsStatus ? ZWLR_OUTPUT_POWER_V1_MODE_ON : ZWLR_OUTPUT_POWER_V1_MODE_OFF); }); + listeners.monitorState = pMonitor->events.modeChanged.registerListener( + [this](std::any v) { resource->sendMode(pMonitor->dpmsStatus ? ZWLR_OUTPUT_POWER_V1_MODE_ON : ZWLR_OUTPUT_POWER_V1_MODE_OFF); }); } bool COutputPower::good() { - return m_resource->resource(); + return resource->resource(); } COutputPowerProtocol::COutputPowerProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -38,7 +43,7 @@ COutputPowerProtocol::COutputPowerProtocol(const wl_interface* iface, const int& } void COutputPowerProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwlrOutputPowerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwlrOutputPowerManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -46,11 +51,11 @@ void COutputPowerProtocol::bindManager(wl_client* client, void* data, uint32_t v } void COutputPowerProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void COutputPowerProtocol::destroyOutputPower(COutputPower* power) { - std::erase_if(m_outputPowers, [&](const auto& other) { return other.get() == power; }); + std::erase_if(m_vOutputPowers, [&](const auto& other) { return other.get() == power; }); } void COutputPowerProtocol::onGetOutputPower(CZwlrOutputPowerManagerV1* pMgr, uint32_t id, wl_resource* output) { @@ -63,11 +68,11 @@ void COutputPowerProtocol::onGetOutputPower(CZwlrOutputPowerManagerV1* pMgr, uin } const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_outputPowers.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id), OUTPUT->m_monitor.lock())).get(); + const auto RESOURCE = m_vOutputPowers.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id), OUTPUT->monitor.lock())).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_outputPowers.pop_back(); + m_vOutputPowers.pop_back(); return; } } diff --git a/src/protocols/OutputPower.hpp b/src/protocols/OutputPower.hpp index 1c3ecc77..c61eaa3d 100644 --- a/src/protocols/OutputPower.hpp +++ b/src/protocols/OutputPower.hpp @@ -15,15 +15,15 @@ class COutputPower { bool good(); private: - SP m_resource; + SP resource; - PHLMONITORREF m_monitor; + PHLMONITORREF pMonitor; struct { CHyprSignalListener monitorDestroy; CHyprSignalListener monitorState; CHyprSignalListener monitorDpms; - } m_listeners; + } listeners; }; class COutputPowerProtocol : public IWaylandProtocol { @@ -38,8 +38,8 @@ class COutputPowerProtocol : public IWaylandProtocol { void onGetOutputPower(CZwlrOutputPowerManagerV1* pMgr, uint32_t id, wl_resource* output); // - std::vector> m_managers; - std::vector> m_outputPowers; + std::vector> m_vManagers; + std::vector> m_vOutputPowers; friend class COutputPower; }; diff --git a/src/protocols/PointerConstraints.cpp b/src/protocols/PointerConstraints.cpp index a78f3548..30ab0a55 100644 --- a/src/protocols/PointerConstraints.cpp +++ b/src/protocols/PointerConstraints.cpp @@ -1,7 +1,6 @@ #include "PointerConstraints.hpp" -#include "../desktop/view/WLSurface.hpp" -#include "../desktop/state/FocusState.hpp" -#include "../desktop/view/Window.hpp" +#include "../desktop/WLSurface.hpp" +#include "../Compositor.hpp" #include "../config/ConfigValue.hpp" #include "../managers/SeatManager.hpp" #include "core/Compositor.hpp" @@ -10,38 +9,38 @@ #include "../helpers/Monitor.hpp" CPointerConstraint::CPointerConstraint(SP resource_, SP surf, wl_resource* region_, zwpPointerConstraintsV1Lifetime lifetime_) : - m_resourceLocked(resource_), m_locked(true), m_lifetime(lifetime_) { + resourceL(resource_), locked(true), lifetime(lifetime_) { if UNLIKELY (!resource_->resource()) return; resource_->setOnDestroy([this](CZwpLockedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); }); resource_->setDestroy([this](CZwpLockedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); }); - m_hlSurface = Desktop::View::CWLSurface::fromResource(surf); + pHLSurface = CWLSurface::fromResource(surf); - if (!m_hlSurface) + if (!pHLSurface) return; if (region_) - m_region.set(CWLRegionResource::fromResource(region_)->m_region); + region.set(CWLRegionResource::fromResource(region_)->region); resource_->setSetRegion([this](CZwpLockedPointerV1* p, wl_resource* region) { onSetRegion(region); }); resource_->setSetCursorPositionHint([this](CZwpLockedPointerV1* p, wl_fixed_t x, wl_fixed_t y) { static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - if (!m_hlSurface) + if (!pHLSurface) return; - m_hintSet = true; + hintSet = true; float scale = 1.f; - const auto PWINDOW = Desktop::View::CWindow::fromView(m_hlSurface->view()); + const auto PWINDOW = pHLSurface->getWindow(); if (PWINDOW) { - const auto ISXWL = PWINDOW->m_isX11; - scale = ISXWL && *PXWLFORCESCALEZERO ? PWINDOW->m_X11SurfaceScaledBy : 1.f; + const auto ISXWL = PWINDOW->m_bIsX11; + scale = ISXWL && *PXWLFORCESCALEZERO ? PWINDOW->m_fX11SurfaceScaledBy : 1.f; } - m_positionHint = {wl_fixed_to_double(x) / scale, wl_fixed_to_double(y) / scale}; + positionHint = {wl_fixed_to_double(x) / scale, wl_fixed_to_double(y) / scale}; g_pInputManager->simulateMouseMovement(); }); @@ -49,20 +48,20 @@ CPointerConstraint::CPointerConstraint(SP resource_, SP resource_, SP surf, wl_resource* region_, zwpPointerConstraintsV1Lifetime lifetime_) : - m_resourceConfined(resource_), m_lifetime(lifetime_) { + resourceC(resource_), lifetime(lifetime_) { if UNLIKELY (!resource_->resource()) return; resource_->setOnDestroy([this](CZwpConfinedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); }); resource_->setDestroy([this](CZwpConfinedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); }); - m_hlSurface = Desktop::View::CWLSurface::fromResource(surf); + pHLSurface = CWLSurface::fromResource(surf); - if (!m_hlSurface) + if (!pHLSurface) return; if (region_) - m_region.set(CWLRegionResource::fromResource(region_)->m_region); + region.set(CWLRegionResource::fromResource(region_)->region); resource_->setSetRegion([this](CZwpConfinedPointerV1* p, wl_resource* region) { onSetRegion(region); }); @@ -70,51 +69,54 @@ CPointerConstraint::CPointerConstraint(SP resource_, SPm_constraints, [this](const auto& c) { + std::erase_if(g_pInputManager->m_vConstraints, [this](const auto& c) { const auto SHP = c.lock(); return !SHP || SHP.get() == this; }); - if (m_hlSurface) - m_hlSurface->m_constraint.reset(); + if (pHLSurface) + pHLSurface->m_pConstraint.reset(); } void CPointerConstraint::sharedConstructions() { - if (m_hlSurface) { - m_listeners.destroySurface = m_hlSurface->m_events.destroy.listen([this] { - m_hlSurface.reset(); - if (m_active) + if (pHLSurface) { + listeners.destroySurface = pHLSurface->events.destroy.registerListener([this](std::any d) { + pHLSurface.reset(); + if (active) deactivate(); - std::erase_if(g_pInputManager->m_constraints, [this](const auto& c) { + std::erase_if(g_pInputManager->m_vConstraints, [this](const auto& c) { const auto SHP = c.lock(); return !SHP || SHP.get() == this; }); }); } - m_cursorPosOnActivate = g_pInputManager->getMouseCoordsInternal(); + cursorPosOnActivate = g_pInputManager->getMouseCoordsInternal(); + + if (g_pCompositor->m_pLastFocus == pHLSurface->resource()) + activate(); } bool CPointerConstraint::good() { - return m_locked ? m_resourceLocked->resource() : m_resourceConfined->resource(); + return locked ? resourceL->resource() : resourceC->resource(); } void CPointerConstraint::deactivate() { - if (!m_active) + if (!active) return; - if (m_locked) - m_resourceLocked->sendUnlocked(); + if (locked) + resourceL->sendUnlocked(); else - m_resourceConfined->sendUnconfined(); + resourceC->sendUnconfined(); - m_active = false; + active = false; - if (m_lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) { - m_dead = true; + if (lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) { + dead = true; // remove from inputmgr - std::erase_if(g_pInputManager->m_constraints, [this](const auto& c) { + std::erase_if(g_pInputManager->m_vConstraints, [this](const auto& c) { const auto SHP = c.lock(); return !SHP || SHP.get() == this; }); @@ -122,50 +124,50 @@ void CPointerConstraint::deactivate() { } void CPointerConstraint::activate() { - if (m_dead || m_active) + if (dead || active) return; // TODO: hack, probably not a super duper great idea - if (g_pSeatManager->m_state.pointerFocus != m_hlSurface->resource()) { - const auto SURFBOX = m_hlSurface->getSurfaceBoxGlobal(); + if (g_pSeatManager->state.pointerFocus != pHLSurface->resource()) { + const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal(); const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{}; - g_pSeatManager->setPointerFocus(m_hlSurface->resource(), LOCAL); + g_pSeatManager->setPointerFocus(pHLSurface->resource(), LOCAL); } - if (m_locked) - m_resourceLocked->sendLocked(); + if (locked) + resourceL->sendLocked(); else - m_resourceConfined->sendConfined(); + resourceC->sendConfined(); - m_active = true; + active = true; g_pInputManager->simulateMouseMovement(); } bool CPointerConstraint::isActive() { - return m_active; + return active; } void CPointerConstraint::onSetRegion(wl_resource* wlRegion) { if (!wlRegion) { - m_region.clear(); + region.clear(); return; } - const auto REGION = m_region.set(CWLRegionResource::fromResource(wlRegion)->m_region); + const auto REGION = region.set(CWLRegionResource::fromResource(wlRegion)->region); - m_region.set(REGION); - m_positionHint = m_region.closestPoint(m_positionHint); + region.set(REGION); + positionHint = region.closestPoint(positionHint); g_pInputManager->simulateMouseMovement(); // to warp the cursor if anything's amiss } -SP CPointerConstraint::owner() { - return m_hlSurface.lock(); +SP CPointerConstraint::owner() { + return pHLSurface.lock(); } CRegion CPointerConstraint::logicConstraintRegion() { - CRegion rg = m_region; - const auto SURFBOX = m_hlSurface->getSurfaceBoxGlobal(); + CRegion rg = region; + const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal(); // if region wasn't set in pointer-constraints request take surface region if (rg.empty() && SURFBOX.has_value()) { @@ -179,17 +181,17 @@ CRegion CPointerConstraint::logicConstraintRegion() { } bool CPointerConstraint::isLocked() { - return m_locked; + return locked; } Vector2D CPointerConstraint::logicPositionHint() { - if UNLIKELY (!m_hlSurface) + if UNLIKELY (!pHLSurface) return {}; - const auto SURFBOX = m_hlSurface->getSurfaceBoxGlobal(); + const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal(); const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{}; - return m_hintSet ? CONSTRAINTPOS + m_positionHint : m_cursorPosOnActivate; + return hintSet ? CONSTRAINTPOS + positionHint : cursorPosOnActivate; } CPointerConstraintsProtocol::CPointerConstraintsProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -197,7 +199,7 @@ CPointerConstraintsProtocol::CPointerConstraintsProtocol(const wl_interface* ifa } void CPointerConstraintsProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpPointerConstraintsV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwpPointerConstraintsV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -208,49 +210,46 @@ void CPointerConstraintsProtocol::bindManager(wl_client* client, void* data, uin } void CPointerConstraintsProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CPointerConstraintsProtocol::destroyPointerConstraint(CPointerConstraint* hyprlandEgg) { - std::erase_if(m_constraints, [&](const auto& other) { return other.get() == hyprlandEgg; }); + std::erase_if(m_vConstraints, [&](const auto& other) { return other.get() == hyprlandEgg; }); } void CPointerConstraintsProtocol::onNewConstraint(SP constraint, CZwpPointerConstraintsV1* pMgr) { if UNLIKELY (!constraint->good()) { - LOGM(Log::ERR, "Couldn't create constraint??"); + LOGM(ERR, "Couldn't create constraint??"); pMgr->noMemory(); - m_constraints.pop_back(); + m_vConstraints.pop_back(); return; } if UNLIKELY (!constraint->owner()) { - LOGM(Log::ERR, "New constraint has no CWLSurface owner??"); + LOGM(ERR, "New constraint has no CWLSurface owner??"); return; } const auto OWNER = constraint->owner(); - const auto DUPES = std::ranges::count_if(m_constraints, [OWNER](const auto& c) { return c->owner() == OWNER; }); + const auto DUPES = std::count_if(m_vConstraints.begin(), m_vConstraints.end(), [OWNER](const auto& c) { return c->owner() == OWNER; }); if UNLIKELY (DUPES > 1) { - LOGM(Log::ERR, "Constraint for surface duped"); + LOGM(ERR, "Constraint for surface duped"); pMgr->error(ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED, "Surface already confined"); - m_constraints.pop_back(); + m_vConstraints.pop_back(); return; } OWNER->appendConstraint(constraint); - g_pInputManager->m_constraints.emplace_back(constraint); - - if (Desktop::focusState()->surface() == OWNER->resource()) - constraint->activate(); + g_pInputManager->m_vConstraints.emplace_back(constraint); } void CPointerConstraintsProtocol::onLockPointer(CZwpPointerConstraintsV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* pointer, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_constraints.emplace_back( + const auto RESOURCE = m_vConstraints.emplace_back( makeShared(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surface), region, lifetime)); onNewConstraint(RESOURCE, pMgr); @@ -259,7 +258,7 @@ void CPointerConstraintsProtocol::onLockPointer(CZwpPointerConstraintsV1* pMgr, void CPointerConstraintsProtocol::onConfinePointer(CZwpPointerConstraintsV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* pointer, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_constraints.emplace_back( + const auto RESOURCE = m_vConstraints.emplace_back( makeShared(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surface), region, lifetime)); onNewConstraint(RESOURCE, pMgr); diff --git a/src/protocols/PointerConstraints.hpp b/src/protocols/PointerConstraints.hpp index b190c041..4d2b4b38 100644 --- a/src/protocols/PointerConstraints.hpp +++ b/src/protocols/PointerConstraints.hpp @@ -6,10 +6,10 @@ #include #include "WaylandProtocol.hpp" #include "pointer-constraints-unstable-v1.hpp" -#include "../desktop/view/WLSurface.hpp" #include "../helpers/math/Math.hpp" #include "../helpers/signal/Signal.hpp" +class CWLSurface; class CWLSurfaceResource; class CPointerConstraint { @@ -18,39 +18,39 @@ class CPointerConstraint { CPointerConstraint(SP resource_, SP surf, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime_); ~CPointerConstraint(); - bool good(); + bool good(); - void deactivate(); - void activate(); - bool isActive(); + void deactivate(); + void activate(); + bool isActive(); - SP owner(); + SP owner(); - CRegion logicConstraintRegion(); - bool isLocked(); - Vector2D logicPositionHint(); + CRegion logicConstraintRegion(); + bool isLocked(); + Vector2D logicPositionHint(); private: - SP m_resourceLocked; - SP m_resourceConfined; + SP resourceL; + SP resourceC; - WP m_hlSurface; + WP pHLSurface; - CRegion m_region; - bool m_hintSet = false; - Vector2D m_positionHint = {-1, -1}; - Vector2D m_cursorPosOnActivate = {-1, -1}; - bool m_active = false; - bool m_locked = false; - bool m_dead = false; - zwpPointerConstraintsV1Lifetime m_lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT; + CRegion region; + bool hintSet = false; + Vector2D positionHint = {-1, -1}; + Vector2D cursorPosOnActivate = {-1, -1}; + bool active = false; + bool locked = false; + bool dead = false; + zwpPointerConstraintsV1Lifetime lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT; void sharedConstructions(); void onSetRegion(wl_resource* region); struct { CHyprSignalListener destroySurface; - } m_listeners; + } listeners; }; class CPointerConstraintsProtocol : public IWaylandProtocol { @@ -67,8 +67,8 @@ class CPointerConstraintsProtocol : public IWaylandProtocol { void onNewConstraint(SP constraint, CZwpPointerConstraintsV1* pMgr); // - std::vector> m_managers; - std::vector> m_constraints; + std::vector> m_vManagers; + std::vector> m_vConstraints; friend class CPointerConstraint; }; diff --git a/src/protocols/PointerGestures.cpp b/src/protocols/PointerGestures.cpp index eb14bbf8..8de73ffa 100644 --- a/src/protocols/PointerGestures.cpp +++ b/src/protocols/PointerGestures.cpp @@ -3,40 +3,40 @@ #include "core/Seat.hpp" #include "core/Compositor.hpp" -CPointerGestureSwipe::CPointerGestureSwipe(SP resource_) : m_resource(resource_) { - if UNLIKELY (!m_resource->resource()) +CPointerGestureSwipe::CPointerGestureSwipe(SP resource_) : resource(resource_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setOnDestroy([this](CZwpPointerGestureSwipeV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); - m_resource->setDestroy([this](CZwpPointerGestureSwipeV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); + resource->setOnDestroy([this](CZwpPointerGestureSwipeV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); + resource->setDestroy([this](CZwpPointerGestureSwipeV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); } bool CPointerGestureSwipe::good() { - return m_resource->resource(); + return resource->resource(); } -CPointerGestureHold::CPointerGestureHold(SP resource_) : m_resource(resource_) { - if UNLIKELY (!m_resource->resource()) +CPointerGestureHold::CPointerGestureHold(SP resource_) : resource(resource_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setOnDestroy([this](CZwpPointerGestureHoldV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); - m_resource->setDestroy([this](CZwpPointerGestureHoldV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); + resource->setOnDestroy([this](CZwpPointerGestureHoldV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); + resource->setDestroy([this](CZwpPointerGestureHoldV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); } bool CPointerGestureHold::good() { - return m_resource->resource(); + return resource->resource(); } -CPointerGesturePinch::CPointerGesturePinch(SP resource_) : m_resource(resource_) { - if UNLIKELY (!m_resource->resource()) +CPointerGesturePinch::CPointerGesturePinch(SP resource_) : resource(resource_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setOnDestroy([this](CZwpPointerGesturePinchV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); - m_resource->setDestroy([this](CZwpPointerGesturePinchV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); + resource->setOnDestroy([this](CZwpPointerGesturePinchV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); + resource->setDestroy([this](CZwpPointerGesturePinchV1* p) { PROTO::pointerGestures->onGestureDestroy(this); }); } bool CPointerGesturePinch::good() { - return m_resource->resource(); + return resource->resource(); } CPointerGesturesProtocol::CPointerGesturesProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -44,7 +44,7 @@ CPointerGesturesProtocol::CPointerGesturesProtocol(const wl_interface* iface, co } void CPointerGesturesProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpPointerGesturesV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setRelease([this](CZwpPointerGesturesV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -54,174 +54,174 @@ void CPointerGesturesProtocol::bindManager(wl_client* client, void* data, uint32 } void CPointerGesturesProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CPointerGesturesProtocol::onGestureDestroy(CPointerGestureSwipe* gesture) { - std::erase_if(m_swipes, [&](const auto& other) { return other.get() == gesture; }); + std::erase_if(m_vSwipes, [&](const auto& other) { return other.get() == gesture; }); } void CPointerGesturesProtocol::onGestureDestroy(CPointerGesturePinch* gesture) { - std::erase_if(m_pinches, [&](const auto& other) { return other.get() == gesture; }); + std::erase_if(m_vPinches, [&](const auto& other) { return other.get() == gesture; }); } void CPointerGesturesProtocol::onGestureDestroy(CPointerGestureHold* gesture) { - std::erase_if(m_holds, [&](const auto& other) { return other.get() == gesture; }); + std::erase_if(m_vHolds, [&](const auto& other) { return other.get() == gesture; }); } void CPointerGesturesProtocol::onGetPinchGesture(CZwpPointerGesturesV1* pMgr, uint32_t id, wl_resource* pointer) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_pinches.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); + const auto RESOURCE = m_vPinches.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - LOGM(Log::ERR, "Couldn't create gesture"); + LOGM(ERR, "Couldn't create gesture"); return; } } void CPointerGesturesProtocol::onGetSwipeGesture(CZwpPointerGesturesV1* pMgr, uint32_t id, wl_resource* pointer) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_swipes.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); + const auto RESOURCE = m_vSwipes.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - LOGM(Log::ERR, "Couldn't create gesture"); + LOGM(ERR, "Couldn't create gesture"); return; } } void CPointerGesturesProtocol::onGetHoldGesture(CZwpPointerGesturesV1* pMgr, uint32_t id, wl_resource* pointer) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_holds.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); + const auto RESOURCE = m_vHolds.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - LOGM(Log::ERR, "Couldn't create gesture"); + LOGM(ERR, "Couldn't create gesture"); return; } } void CPointerGesturesProtocol::swipeBegin(uint32_t timeMs, uint32_t fingers) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->m_state.pointerFocusResource.lock()); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); - for (auto const& sw : m_swipes) { - if (sw->m_resource->client() != FOCUSEDCLIENT) + for (auto const& sw : m_vSwipes) { + if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->m_resource->sendBegin(SERIAL, timeMs, g_pSeatManager->m_state.pointerFocus->getResource()->resource(), fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->getResource()->resource(), fingers); } } void CPointerGesturesProtocol::swipeUpdate(uint32_t timeMs, const Vector2D& delta) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - for (auto const& sw : m_swipes) { - if (sw->m_resource->client() != FOCUSEDCLIENT) + for (auto const& sw : m_vSwipes) { + if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->m_resource->sendUpdate(timeMs, wl_fixed_from_double(delta.x), wl_fixed_from_double(delta.y)); + sw->resource->sendUpdate(timeMs, wl_fixed_from_double(delta.x), wl_fixed_from_double(delta.y)); } } void CPointerGesturesProtocol::swipeEnd(uint32_t timeMs, bool cancelled) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->m_state.pointerFocusResource.lock()); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); - for (auto const& sw : m_swipes) { - if (sw->m_resource->client() != FOCUSEDCLIENT) + for (auto const& sw : m_vSwipes) { + if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->m_resource->sendEnd(SERIAL, timeMs, cancelled); + sw->resource->sendEnd(SERIAL, timeMs, cancelled); } } void CPointerGesturesProtocol::pinchBegin(uint32_t timeMs, uint32_t fingers) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->m_state.pointerFocusResource.lock()); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); - for (auto const& sw : m_pinches) { - if (sw->m_resource->client() != FOCUSEDCLIENT) + for (auto const& sw : m_vPinches) { + if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->m_resource->sendBegin(SERIAL, timeMs, g_pSeatManager->m_state.pointerFocus->getResource()->resource(), fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->getResource()->resource(), fingers); } } void CPointerGesturesProtocol::pinchUpdate(uint32_t timeMs, const Vector2D& delta, double scale, double rotation) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - for (auto const& sw : m_pinches) { - if (sw->m_resource->client() != FOCUSEDCLIENT) + for (auto const& sw : m_vPinches) { + if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->m_resource->sendUpdate(timeMs, wl_fixed_from_double(delta.x), wl_fixed_from_double(delta.y), wl_fixed_from_double(scale), wl_fixed_from_double(rotation)); + sw->resource->sendUpdate(timeMs, wl_fixed_from_double(delta.x), wl_fixed_from_double(delta.y), wl_fixed_from_double(scale), wl_fixed_from_double(rotation)); } } void CPointerGesturesProtocol::pinchEnd(uint32_t timeMs, bool cancelled) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->m_state.pointerFocusResource.lock()); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); - for (auto const& sw : m_pinches) { - if (sw->m_resource->client() != FOCUSEDCLIENT) + for (auto const& sw : m_vPinches) { + if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->m_resource->sendEnd(SERIAL, timeMs, cancelled); + sw->resource->sendEnd(SERIAL, timeMs, cancelled); } } void CPointerGesturesProtocol::holdBegin(uint32_t timeMs, uint32_t fingers) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->m_state.pointerFocusResource.lock()); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); - for (auto const& sw : m_holds) { - if (sw->m_resource->client() != FOCUSEDCLIENT) + for (auto const& sw : m_vHolds) { + if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->m_resource->sendBegin(SERIAL, timeMs, g_pSeatManager->m_state.pointerFocus->getResource()->resource(), fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->getResource()->resource(), fingers); } } void CPointerGesturesProtocol::holdEnd(uint32_t timeMs, bool cancelled) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSEDCLIENT = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSEDCLIENT = g_pSeatManager->state.pointerFocusResource->client(); - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->m_state.pointerFocusResource.lock()); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->state.pointerFocusResource.lock()); - for (auto const& sw : m_holds) { - if (sw->m_resource->client() != FOCUSEDCLIENT) + for (auto const& sw : m_vHolds) { + if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->m_resource->sendEnd(SERIAL, timeMs, cancelled); + sw->resource->sendEnd(SERIAL, timeMs, cancelled); } } diff --git a/src/protocols/PointerGestures.hpp b/src/protocols/PointerGestures.hpp index 7e169b24..5211ecf3 100644 --- a/src/protocols/PointerGestures.hpp +++ b/src/protocols/PointerGestures.hpp @@ -12,7 +12,7 @@ class CPointerGestureSwipe { bool good(); private: - SP m_resource; + SP resource; friend class CPointerGesturesProtocol; }; @@ -24,7 +24,7 @@ class CPointerGesturePinch { bool good(); private: - SP m_resource; + SP resource; friend class CPointerGesturesProtocol; }; @@ -36,7 +36,7 @@ class CPointerGestureHold { bool good(); private: - SP m_resource; + SP resource; friend class CPointerGesturesProtocol; }; @@ -68,10 +68,10 @@ class CPointerGesturesProtocol : public IWaylandProtocol { void onGetHoldGesture(CZwpPointerGesturesV1* pMgr, uint32_t id, wl_resource* pointer); // - std::vector> m_managers; - std::vector> m_swipes; - std::vector> m_pinches; - std::vector> m_holds; + std::vector> m_vManagers; + std::vector> m_vSwipes; + std::vector> m_vPinches; + std::vector> m_vHolds; friend class CPointerGestureHold; friend class CPointerGesturePinch; diff --git a/src/protocols/PointerWarp.cpp b/src/protocols/PointerWarp.cpp deleted file mode 100644 index a297a04d..00000000 --- a/src/protocols/PointerWarp.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "PointerWarp.hpp" -#include "core/Compositor.hpp" -#include "core/Seat.hpp" -#include "../desktop/view/WLSurface.hpp" -#include "../managers/SeatManager.hpp" -#include "../managers/PointerManager.hpp" -#include "../desktop/view/Window.hpp" - -CPointerWarpProtocol::CPointerWarpProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; -} - -void CPointerWarpProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto& RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)); - - if UNLIKELY (!RESOURCE->resource()) { - wl_client_post_no_memory(client); - m_managers.pop_back(); - return; - } - - RESOURCE->setOnDestroy([this](CWpPointerWarpV1* pMgr) { destroyManager(pMgr); }); - RESOURCE->setDestroy([this](CWpPointerWarpV1* pMgr) { destroyManager(pMgr); }); - - RESOURCE->setWarpPointer([](CWpPointerWarpV1* pMgr, wl_resource* surface, wl_resource* pointer, wl_fixed_t x, wl_fixed_t y, uint32_t serial) { - const auto PSURFACE = CWLSurfaceResource::fromResource(surface); - if (g_pSeatManager->m_state.pointerFocus != PSURFACE) - return; - - auto WINDOW = Desktop::View::CWindow::fromView(Desktop::View::CWLSurface::fromResource(PSURFACE)->view()); - if (!WINDOW) - return; - - const auto SURFBOX = WINDOW->getWindowMainSurfaceBox().expand(1); - const auto LOCALPOS = Vector2D{wl_fixed_to_double(x), wl_fixed_to_double(y)}; - const auto GLOBALPOS = LOCALPOS + SURFBOX.pos(); - if (!SURFBOX.containsPoint(GLOBALPOS)) - return; - - const auto PSEAT = CWLPointerResource::fromResource(pointer)->m_owner.lock(); - if (!g_pSeatManager->serialValid(PSEAT, serial, false)) - return; - - LOGM(Log::DEBUG, "warped pointer to {}", GLOBALPOS); - - g_pPointerManager->warpTo(GLOBALPOS); - g_pSeatManager->sendPointerMotion(Time::millis(Time::steadyNow()), LOCALPOS); - }); -} - -void CPointerWarpProtocol::destroyManager(CWpPointerWarpV1* manager) { - std::erase_if(m_managers, [&](const UP& resource) { return resource.get() == manager; }); -} diff --git a/src/protocols/PointerWarp.hpp b/src/protocols/PointerWarp.hpp deleted file mode 100644 index d1ce0062..00000000 --- a/src/protocols/PointerWarp.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "WaylandProtocol.hpp" -#include "pointer-warp-v1.hpp" - -class CPointerWarpProtocol : public IWaylandProtocol { - public: - CPointerWarpProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - private: - void destroyManager(CWpPointerWarpV1* manager); - - // - std::vector> m_managers; -}; - -namespace PROTO { - inline UP pointerWarp; -}; diff --git a/src/protocols/PresentationTime.cpp b/src/protocols/PresentationTime.cpp index 456ad724..05fda8ea 100644 --- a/src/protocols/PresentationTime.cpp +++ b/src/protocols/PresentationTime.cpp @@ -1,148 +1,144 @@ #include "PresentationTime.hpp" #include #include "../helpers/Monitor.hpp" -#include "../event/EventBus.hpp" +#include "../managers/HookSystemManager.hpp" #include "core/Compositor.hpp" #include "core/Output.hpp" #include -CQueuedPresentationData::CQueuedPresentationData(SP surf) : m_surface(surf) { +CQueuedPresentationData::CQueuedPresentationData(SP surf) : surface(surf) { ; } void CQueuedPresentationData::setPresentationType(bool zeroCopy_) { - m_zeroCopy = zeroCopy_; + zeroCopy = zeroCopy_; } void CQueuedPresentationData::attachMonitor(PHLMONITOR pMonitor_) { - m_monitor = pMonitor_; + pMonitor = pMonitor_; } void CQueuedPresentationData::presented() { - m_wasPresented = true; + wasPresented = true; } void CQueuedPresentationData::discarded() { - m_wasPresented = false; + wasPresented = false; } -CPresentationFeedback::CPresentationFeedback(UP&& resource_, SP surf) : m_resource(std::move(resource_)), m_surface(surf) { +CPresentationFeedback::CPresentationFeedback(SP resource_, SP surf) : resource(resource_), surface(surf) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWpPresentationFeedback* pMgr) { - if (!m_done) // if it's done, it's probably already destroyed. If not, it will be in a sec. + resource->setOnDestroy([this](CWpPresentationFeedback* pMgr) { + if (!done) // if it's done, it's probably already destroyed. If not, it will be in a sec. PROTO::presentation->destroyResource(this); }); } bool CPresentationFeedback::good() { - return m_resource->resource(); + return resource->resource(); } -void CPresentationFeedback::sendQueued(WP data, const timespec& when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) { - auto client = m_resource->client(); +void CPresentationFeedback::sendQueued(SP data, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) { + auto client = resource->client(); - if LIKELY (PROTO::outputs.contains(data->m_monitor->m_name) && data->m_wasPresented) { - if LIKELY (auto outputResources = PROTO::outputs.at(data->m_monitor->m_name)->outputResourcesFrom(client); !outputResources.empty()) { - for (const auto& r : outputResources) { - m_resource->sendSyncOutput(r->getResource()->resource()); - } - } + if LIKELY (PROTO::outputs.contains(data->pMonitor->szName)) { + if LIKELY (auto outputResource = PROTO::outputs.at(data->pMonitor->szName)->outputResourceFrom(client); outputResource) + resource->sendSyncOutput(outputResource->getResource()->resource()); } - if (data->m_wasPresented) { - uint32_t flags = 0; - if (!data->m_monitor->m_tearingState.activelyTearing) - flags |= WP_PRESENTATION_FEEDBACK_KIND_VSYNC; - if (data->m_zeroCopy) - flags |= WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY; - if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK) - flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; - if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION) - flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION; + uint32_t flags = 0; + if (!data->pMonitor->tearingState.activelyTearing) + flags |= WP_PRESENTATION_FEEDBACK_KIND_VSYNC; + if (data->zeroCopy) + flags |= WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY; + if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK) + flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; + if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION) + flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION; - time_t tv_sec = 0; - if (sizeof(time_t) > 4) - tv_sec = when.tv_sec >> 32; + time_t tv_sec = 0; + if (sizeof(time_t) > 4) + tv_sec = when->tv_sec >> 32; - uint32_t refreshNs = m_resource->version() == 1 && data->m_monitor->m_vrrActive && data->m_monitor->m_output->vrrCapable ? 0 : untilRefreshNs; + if (data->wasPresented) + resource->sendPresented((uint32_t)tv_sec, (uint32_t)(when->tv_sec & 0xFFFFFFFF), (uint32_t)(when->tv_nsec), untilRefreshNs, (uint32_t)(seq >> 32), + (uint32_t)(seq & 0xFFFFFFFF), (wpPresentationFeedbackKind)flags); + else + resource->sendDiscarded(); - m_resource->sendPresented(sc(tv_sec), sc(when.tv_sec & 0xFFFFFFFF), sc(when.tv_nsec), refreshNs, sc(seq >> 32), - sc(seq & 0xFFFFFFFF), sc(flags)); - } else - m_resource->sendDiscarded(); - - m_done = true; + done = true; } CPresentationProtocol::CPresentationProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P = Event::bus()->m_events.monitor.removed.listen( - [this](PHLMONITOR mon) { std::erase_if(m_queue, [mon](const auto& other) { return !other->m_surface || other->m_monitor == mon; }); }); + static auto P = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) { + const auto PMONITOR = PHLMONITORREF{std::any_cast(param)}; + std::erase_if(m_vQueue, [PMONITOR](const auto& other) { return !other->surface || other->pMonitor == PMONITOR; }); + }); } void CPresentationProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CWpPresentation* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CWpPresentation* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); RESOURCE->setFeedback([this](CWpPresentation* pMgr, wl_resource* surf, uint32_t id) { this->onGetFeedback(pMgr, surf, id); }); - RESOURCE->sendClockId(CLOCK_MONOTONIC); } void CPresentationProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CPresentationProtocol::destroyResource(CPresentationFeedback* feedback) { - std::erase_if(m_feedbacks, [&](const auto& other) { return other.get() == feedback; }); + std::erase_if(m_vFeedbacks, [&](const auto& other) { return other.get() == feedback; }); } void CPresentationProtocol::onGetFeedback(CWpPresentation* pMgr, wl_resource* surf, uint32_t id) { - const auto CLIENT = pMgr->client(); - const auto& RESOURCE = - m_feedbacks.emplace_back(makeUnique(makeUnique(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surf))).get(); + const auto CLIENT = pMgr->client(); + const auto RESOURCE = + m_vFeedbacks.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surf))) + .get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_feedbacks.pop_back(); + m_vFeedbacks.pop_back(); return; } } -void CPresentationProtocol::onPresented(PHLMONITOR pMonitor, const timespec& when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) { - for (auto const& feedback : m_feedbacks) { - if (!feedback->m_surface) +void CPresentationProtocol::onPresented(PHLMONITOR pMonitor, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) { + timespec now; + timespec* presentedAt = when; + if (!presentedAt) { + // just put the current time, we don't have anything better + clock_gettime(CLOCK_MONOTONIC, &now); + when = &now; + } + + for (auto const& feedback : m_vFeedbacks) { + if (!feedback->surface) continue; - for (auto const& data : m_queue) { - if (!data->m_surface || data->m_surface != feedback->m_surface || (data->m_monitor && data->m_monitor != pMonitor)) + for (auto const& data : m_vQueue) { + if (!data->surface || data->surface != feedback->surface || (data->pMonitor && data->pMonitor != pMonitor)) continue; feedback->sendQueued(data, when, untilRefreshNs, seq, reportedFlags); - feedback->m_done = true; + feedback->done = true; break; } } - if (m_feedbacks.size() > 10000) { - LOGM(Log::ERR, "FIXME: presentation has a feedback leak, and has grown to {} pending entries!!! Dropping!!!!!", m_feedbacks.size()); - - // Move the elements from the 9000th position to the end of the vector. - std::vector> newFeedbacks; - newFeedbacks.reserve(m_feedbacks.size() - 9000); - - for (auto it = m_feedbacks.begin() + 9000; it != m_feedbacks.end(); ++it) { - newFeedbacks.push_back(std::move(*it)); - } - - m_feedbacks = std::move(newFeedbacks); + if (m_vFeedbacks.size() > 10000 /* arbitrary number I chose as fitting */) { + LOGM(ERR, "FIXME: presentation has a feedback leak, and has grown to {} pending entries!!! Dropping!!!!!", m_vFeedbacks.size()); + m_vFeedbacks = {m_vFeedbacks.begin() + 9000, m_vFeedbacks.end()}; } - std::erase_if(m_feedbacks, [](const auto& other) { return !other->m_surface || other->m_done; }); - std::erase_if(m_queue, [pMonitor](const auto& other) { return !other->m_surface || other->m_monitor == pMonitor || !other->m_monitor || other->m_done; }); + std::erase_if(m_vFeedbacks, [](const auto& other) { return !other->surface || other->done; }); + std::erase_if(m_vQueue, [pMonitor](const auto& other) { return !other->surface || other->pMonitor == pMonitor || !other->pMonitor || other->done; }); } -void CPresentationProtocol::queueData(UP&& data) { - m_queue.emplace_back(std::move(data)); +void CPresentationProtocol::queueData(SP data) { + m_vQueue.emplace_back(data); } diff --git a/src/protocols/PresentationTime.hpp b/src/protocols/PresentationTime.hpp index caf63ace..03b59b89 100644 --- a/src/protocols/PresentationTime.hpp +++ b/src/protocols/PresentationTime.hpp @@ -1,11 +1,9 @@ #pragma once -#include #include #include #include "WaylandProtocol.hpp" #include "presentation-time.hpp" -#include "../helpers/time/Time.hpp" class CMonitor; class CWLSurfaceResource; @@ -20,13 +18,13 @@ class CQueuedPresentationData { void presented(); void discarded(); - bool m_done = false; + bool done = false; private: - bool m_wasPresented = false; - bool m_zeroCopy = false; - PHLMONITORREF m_monitor; - WP m_surface; + bool wasPresented = false; + bool zeroCopy = false; + PHLMONITORREF pMonitor; + WP surface; friend class CPresentationFeedback; friend class CPresentationProtocol; @@ -34,16 +32,16 @@ class CQueuedPresentationData { class CPresentationFeedback { public: - CPresentationFeedback(UP&& resource_, SP surf); + CPresentationFeedback(SP resource_, SP surf); bool good(); - void sendQueued(WP data, const timespec& when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags); + void sendQueued(SP data, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags); private: - UP m_resource; - WP m_surface; - bool m_done = false; + SP resource; + WP surface; + bool done = false; friend class CPresentationProtocol; }; @@ -54,8 +52,8 @@ class CPresentationProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - void onPresented(PHLMONITOR pMonitor, const timespec& when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags); - void queueData(UP&& data); + void onPresented(PHLMONITOR pMonitor, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags); + void queueData(SP data); private: void onManagerResourceDestroy(wl_resource* res); @@ -63,9 +61,9 @@ class CPresentationProtocol : public IWaylandProtocol { void onGetFeedback(CWpPresentation* pMgr, wl_resource* surf, uint32_t id); // - std::vector> m_managers; - std::vector> m_feedbacks; - std::vector> m_queue; + std::vector> m_vManagers; + std::vector> m_vFeedbacks; + std::vector> m_vQueue; friend class CPresentationFeedback; }; diff --git a/src/protocols/PrimarySelection.cpp b/src/protocols/PrimarySelection.cpp index 7da1fa0a..620f262e 100644 --- a/src/protocols/PrimarySelection.cpp +++ b/src/protocols/PrimarySelection.cpp @@ -5,210 +5,210 @@ #include "../config/ConfigValue.hpp" using namespace Hyprutils::OS; -CPrimarySelectionOffer::CPrimarySelectionOffer(SP resource_, SP source_) : m_source(source_), m_resource(resource_) { +CPrimarySelectionOffer::CPrimarySelectionOffer(SP resource_, SP source_) : source(source_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpPrimarySelectionOfferV1* r) { PROTO::primarySelection->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpPrimarySelectionOfferV1* r) { PROTO::primarySelection->destroyResource(this); }); + resource->setDestroy([this](CZwpPrimarySelectionOfferV1* r) { PROTO::primarySelection->destroyResource(this); }); + resource->setOnDestroy([this](CZwpPrimarySelectionOfferV1* r) { PROTO::primarySelection->destroyResource(this); }); - m_resource->setReceive([this](CZwpPrimarySelectionOfferV1* r, const char* mime, int32_t fd) { + resource->setReceive([this](CZwpPrimarySelectionOfferV1* r, const char* mime, int32_t fd) { CFileDescriptor sendFd{fd}; - if (!m_source) { - LOGM(Log::WARN, "Possible bug: Receive on an offer w/o a source"); + if (!source) { + LOGM(WARN, "Possible bug: Receive on an offer w/o a source"); return; } - if (m_dead) { - LOGM(Log::WARN, "Possible bug: Receive on an offer that's dead"); + if (dead) { + LOGM(WARN, "Possible bug: Receive on an offer that's dead"); return; } - LOGM(Log::DEBUG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get()); + LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get()); - m_source->send(mime, std::move(sendFd)); + source->send(mime, std::move(sendFd)); }); } bool CPrimarySelectionOffer::good() { - return m_resource->resource(); + return resource->resource(); } void CPrimarySelectionOffer::sendData() { - if UNLIKELY (!m_source) + if UNLIKELY (!source) return; - for (auto const& m : m_source->mimes()) { - m_resource->sendOffer(m.c_str()); + for (auto const& m : source->mimes()) { + resource->sendOffer(m.c_str()); } } -CPrimarySelectionSource::CPrimarySelectionSource(SP resource_, SP device_) : m_device(device_), m_resource(resource_) { +CPrimarySelectionSource::CPrimarySelectionSource(SP resource_, SP device_) : device(device_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setDestroy([this](CZwpPrimarySelectionSourceV1* r) { - m_events.destroy.emit(); + resource->setDestroy([this](CZwpPrimarySelectionSourceV1* r) { + events.destroy.emit(); PROTO::primarySelection->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpPrimarySelectionSourceV1* r) { - m_events.destroy.emit(); + resource->setOnDestroy([this](CZwpPrimarySelectionSourceV1* r) { + events.destroy.emit(); PROTO::primarySelection->destroyResource(this); }); - m_resource->setOffer([this](CZwpPrimarySelectionSourceV1* r, const char* mime) { m_mimeTypes.emplace_back(mime); }); + resource->setOffer([this](CZwpPrimarySelectionSourceV1* r, const char* mime) { mimeTypes.emplace_back(mime); }); } CPrimarySelectionSource::~CPrimarySelectionSource() { - m_events.destroy.emit(); + events.destroy.emit(); } SP CPrimarySelectionSource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CPrimarySelectionSource*)(((CZwpPrimarySelectionSourceV1*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } bool CPrimarySelectionSource::good() { - return m_resource->resource(); + return resource->resource(); } std::vector CPrimarySelectionSource::mimes() { - return m_mimeTypes; + return mimeTypes; } void CPrimarySelectionSource::send(const std::string& mime, CFileDescriptor fd) { - if (std::ranges::find(m_mimeTypes, mime) == m_mimeTypes.end()) { - LOGM(Log::ERR, "Compositor/App bug: CPrimarySelectionSource::sendAskSend with non-existent mime"); + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CPrimarySelectionSource::sendAskSend with non-existent mime"); return; } - m_resource->sendSend(mime.c_str(), fd.get()); + resource->sendSend(mime.c_str(), fd.get()); } void CPrimarySelectionSource::accepted(const std::string& mime) { - if (std::ranges::find(m_mimeTypes, mime) == m_mimeTypes.end()) - LOGM(Log::ERR, "Compositor/App bug: CPrimarySelectionSource::sendAccepted with non-existent mime"); + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) + LOGM(ERR, "Compositor/App bug: CPrimarySelectionSource::sendAccepted with non-existent mime"); // primary sel has no accepted } void CPrimarySelectionSource::cancelled() { - m_resource->sendCancelled(); + resource->sendCancelled(); } void CPrimarySelectionSource::error(uint32_t code, const std::string& msg) { - m_resource->error(code, msg); + resource->error(code, msg); } -CPrimarySelectionDevice::CPrimarySelectionDevice(SP resource_) : m_resource(resource_) { +CPrimarySelectionDevice::CPrimarySelectionDevice(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_client = m_resource->client(); + pClient = resource->client(); - m_resource->setDestroy([this](CZwpPrimarySelectionDeviceV1* r) { PROTO::primarySelection->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpPrimarySelectionDeviceV1* r) { PROTO::primarySelection->destroyResource(this); }); + resource->setDestroy([this](CZwpPrimarySelectionDeviceV1* r) { PROTO::primarySelection->destroyResource(this); }); + resource->setOnDestroy([this](CZwpPrimarySelectionDeviceV1* r) { PROTO::primarySelection->destroyResource(this); }); - m_resource->setSetSelection([](CZwpPrimarySelectionDeviceV1* r, wl_resource* sourceR, uint32_t serial) { + resource->setSetSelection([](CZwpPrimarySelectionDeviceV1* r, wl_resource* sourceR, uint32_t serial) { static auto PPRIMARYSEL = CConfigValue("misc:middle_click_paste"); if (!*PPRIMARYSEL) { - LOGM(Log::DEBUG, "Ignoring primary selection: disabled in config"); + LOGM(LOG, "Ignoring primary selection: disabled in config"); g_pSeatManager->setCurrentPrimarySelection(nullptr); return; } auto source = sourceR ? CPrimarySelectionSource::fromResource(sourceR) : CSharedPointer{}; if (!source) { - LOGM(Log::DEBUG, "wlr reset selection received"); + LOGM(LOG, "wlr reset selection received"); g_pSeatManager->setCurrentPrimarySelection(nullptr); return; } if (source && source->used()) - LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); source->markUsed(); - LOGM(Log::DEBUG, "wlr manager requests selection to {:x}", (uintptr_t)source.get()); + LOGM(LOG, "wlr manager requests selection to {:x}", (uintptr_t)source.get()); g_pSeatManager->setCurrentPrimarySelection(source); }); } bool CPrimarySelectionDevice::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CPrimarySelectionDevice::client() { - return m_client; + return pClient; } void CPrimarySelectionDevice::sendDataOffer(SP offer) { - m_resource->sendDataOffer(offer->m_resource.get()); + resource->sendDataOffer(offer->resource.get()); } void CPrimarySelectionDevice::sendSelection(SP selection) { if (!selection) - m_resource->sendSelectionRaw(nullptr); + resource->sendSelectionRaw(nullptr); else - m_resource->sendSelection(selection->m_resource.get()); + resource->sendSelection(selection->resource.get()); } -CPrimarySelectionManager::CPrimarySelectionManager(SP resource_) : m_resource(resource_) { +CPrimarySelectionManager::CPrimarySelectionManager(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CZwpPrimarySelectionDeviceManagerV1* r) { PROTO::primarySelection->destroyResource(this); }); + resource->setOnDestroy([this](CZwpPrimarySelectionDeviceManagerV1* r) { PROTO::primarySelection->destroyResource(this); }); - m_resource->setGetDevice([this](CZwpPrimarySelectionDeviceManagerV1* r, uint32_t id, wl_resource* seat) { + resource->setGetDevice([this](CZwpPrimarySelectionDeviceManagerV1* r, uint32_t id, wl_resource* seat) { const auto RESOURCE = - PROTO::primarySelection->m_devices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + PROTO::primarySelection->m_vDevices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::primarySelection->m_devices.pop_back(); + PROTO::primarySelection->m_vDevices.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - m_device = RESOURCE; + RESOURCE->self = RESOURCE; + device = RESOURCE; - for (auto const& s : m_sources) { + for (auto const& s : sources) { if (!s) continue; - s->m_device = RESOURCE; + s->device = RESOURCE; } - LOGM(Log::DEBUG, "New primary selection data device bound at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New primary selection data device bound at {:x}", (uintptr_t)RESOURCE.get()); }); - m_resource->setCreateSource([this](CZwpPrimarySelectionDeviceManagerV1* r, uint32_t id) { - std::erase_if(m_sources, [](const auto& e) { return e.expired(); }); + resource->setCreateSource([this](CZwpPrimarySelectionDeviceManagerV1* r, uint32_t id) { + std::erase_if(sources, [](const auto& e) { return e.expired(); }); - const auto RESOURCE = PROTO::primarySelection->m_sources.emplace_back( - makeShared(makeShared(r->client(), r->version(), id), m_device.lock())); + const auto RESOURCE = PROTO::primarySelection->m_vSources.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), device.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::primarySelection->m_sources.pop_back(); + PROTO::primarySelection->m_vSources.pop_back(); return; } - if (!m_device) - LOGM(Log::WARN, "New data source before a device was created"); + if (!device) + LOGM(WARN, "New data source before a device was created"); - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - m_sources.emplace_back(RESOURCE); + sources.emplace_back(RESOURCE); - LOGM(Log::DEBUG, "New primary selection data source bound at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New primary selection data source bound at {:x}", (uintptr_t)RESOURCE.get()); }); } bool CPrimarySelectionManager::good() { - return m_resource->resource(); + return resource->resource(); } CPrimarySelectionProtocol::CPrimarySelectionProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -216,35 +216,35 @@ CPrimarySelectionProtocol::CPrimarySelectionProtocol(const wl_interface* iface, } void CPrimarySelectionProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } - LOGM(Log::DEBUG, "New primary_seletion_manager at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New primary_seletion_manager at {:x}", (uintptr_t)RESOURCE.get()); // we need to do it here because protocols come before seatMgr - if (!m_listeners.onPointerFocusChange) - m_listeners.onPointerFocusChange = g_pSeatManager->m_events.pointerFocusChange.listen([this] { this->onPointerFocus(); }); + if (!listeners.onPointerFocusChange) + listeners.onPointerFocusChange = g_pSeatManager->events.pointerFocusChange.registerListener([this](std::any d) { this->onPointerFocus(); }); } void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionManager* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionSource* resource) { - std::erase_if(m_sources, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; }); } void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionDevice* resource) { - std::erase_if(m_devices, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; }); } void CPrimarySelectionProtocol::destroyResource(CPrimarySelectionOffer* resource) { - std::erase_if(m_offers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; }); } void CPrimarySelectionProtocol::sendSelectionToDevice(SP dev, SP sel) { @@ -254,15 +254,15 @@ void CPrimarySelectionProtocol::sendSelectionToDevice(SP(makeShared(dev->m_resource->client(), dev->m_resource->version(), 0), sel)); + m_vOffers.emplace_back(makeShared(makeShared(dev->resource->client(), dev->resource->version(), 0), sel)); if (!OFFER->good()) { - dev->m_resource->noMemory(); - m_offers.pop_back(); + dev->resource->noMemory(); + m_vOffers.pop_back(); return; } - LOGM(Log::DEBUG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); + LOGM(LOG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); dev->sendDataOffer(OFFER); OFFER->sendData(); @@ -270,35 +270,34 @@ void CPrimarySelectionProtocol::sendSelectionToDevice(SP source) { - for (auto const& o : m_offers) { - if (o->m_source && o->m_source->hasDnd()) + for (auto const& o : m_vOffers) { + if (o->source && o->source->hasDnd()) continue; - o->m_dead = true; + o->dead = true; } if (!source) { - LOGM(Log::DEBUG, "resetting selection"); + LOGM(LOG, "resetting selection"); - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->m_state.pointerFocusResource->client()); + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client()); if (DESTDEVICE) sendSelectionToDevice(DESTDEVICE, nullptr); return; } - LOGM(Log::DEBUG, "New selection for data source {:x}", (uintptr_t)source.get()); + LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get()); - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->m_state.pointerFocusResource->client()); + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client()); if (!DESTDEVICE) { - LOGM(Log::DEBUG, "CWLDataDeviceProtocol::setSelection: cannot send selection to a client without a data_device"); - g_pSeatManager->m_selection.currentPrimarySelection.reset(); + LOGM(LOG, "CWLDataDeviceProtocol::setSelection: cannot send selection to a client without a data_device"); return; } @@ -306,31 +305,30 @@ void CPrimarySelectionProtocol::setSelection(SP source) { } void CPrimarySelectionProtocol::updateSelection() { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - auto selection = g_pSeatManager->m_selection.currentPrimarySelection.lock(); - auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->m_state.pointerFocusResource->client()); + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.pointerFocusResource->client()); - if (!selection || !DESTDEVICE) { - LOGM(Log::DEBUG, "CPrimarySelectionProtocol::updateSelection: cannot send selection to a client without a data_device"); + if (!DESTDEVICE) { + LOGM(LOG, "CPrimarySelectionProtocol::updateSelection: cannot send selection to a client without a data_device"); return; } - sendSelectionToDevice(DESTDEVICE, g_pSeatManager->m_selection.currentPrimarySelection.lock()); + sendSelectionToDevice(DESTDEVICE, g_pSeatManager->selection.currentPrimarySelection.lock()); } void CPrimarySelectionProtocol::onPointerFocus() { - for (auto const& o : m_offers) { - o->m_dead = true; + for (auto const& o : m_vOffers) { + o->dead = true; } updateSelection(); } SP CPrimarySelectionProtocol::dataDeviceForClient(wl_client* c) { - auto it = std::ranges::find_if(m_devices, [c](const auto& e) { return e->client() == c; }); - if (it == m_devices.end()) + auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; }); + if (it == m_vDevices.end()) return nullptr; return *it; } diff --git a/src/protocols/PrimarySelection.hpp b/src/protocols/PrimarySelection.hpp index f5a8823e..0ecc962b 100644 --- a/src/protocols/PrimarySelection.hpp +++ b/src/protocols/PrimarySelection.hpp @@ -19,12 +19,12 @@ class CPrimarySelectionOffer { bool good(); void sendData(); - bool m_dead = false; + bool dead = false; - WP m_source; + WP source; private: - SP m_resource; + SP resource; friend class CPrimarySelectionDevice; }; @@ -44,12 +44,12 @@ class CPrimarySelectionSource : public IDataSource { virtual void cancelled(); virtual void error(uint32_t code, const std::string& msg); - std::vector m_mimeTypes; - WP m_self; - WP m_device; + std::vector mimeTypes; + WP self; + WP device; private: - SP m_resource; + SP resource; }; class CPrimarySelectionDevice { @@ -62,11 +62,11 @@ class CPrimarySelectionDevice { void sendDataOffer(SP offer); void sendSelection(SP selection); - WP m_self; + WP self; private: - SP m_resource; - wl_client* m_client = nullptr; + SP resource; + wl_client* pClient = nullptr; friend class CPrimarySelectionProtocol; }; @@ -77,11 +77,11 @@ class CPrimarySelectionManager { bool good(); - WP m_device; - std::vector> m_sources; + WP device; + std::vector> sources; private: - SP m_resource; + SP resource; }; class CPrimarySelectionProtocol : public IWaylandProtocol { @@ -97,10 +97,10 @@ class CPrimarySelectionProtocol : public IWaylandProtocol { void destroyResource(CPrimarySelectionOffer* resource); // - std::vector> m_managers; - std::vector> m_devices; - std::vector> m_sources; - std::vector> m_offers; + std::vector> m_vManagers; + std::vector> m_vDevices; + std::vector> m_vSources; + std::vector> m_vOffers; // void setSelection(SP source); @@ -119,7 +119,7 @@ class CPrimarySelectionProtocol : public IWaylandProtocol { struct { CHyprSignalListener onPointerFocusChange; - } m_listeners; + } listeners; }; namespace PROTO { diff --git a/src/protocols/RelativePointer.cpp b/src/protocols/RelativePointer.cpp index 67bff46e..f002fac1 100644 --- a/src/protocols/RelativePointer.cpp +++ b/src/protocols/RelativePointer.cpp @@ -3,27 +3,27 @@ #include "core/Seat.hpp" #include -CRelativePointer::CRelativePointer(SP resource_) : m_resource(resource_) { +CRelativePointer::CRelativePointer(SP resource_) : resource(resource_) { if UNLIKELY (!resource_->resource()) return; - m_client = m_resource->client(); + pClient = resource->client(); - m_resource->setDestroy([this](CZwpRelativePointerV1* pMgr) { PROTO::relativePointer->destroyRelativePointer(this); }); - m_resource->setOnDestroy([this](CZwpRelativePointerV1* pMgr) { PROTO::relativePointer->destroyRelativePointer(this); }); + resource->setDestroy([this](CZwpRelativePointerV1* pMgr) { PROTO::relativePointer->destroyRelativePointer(this); }); + resource->setOnDestroy([this](CZwpRelativePointerV1* pMgr) { PROTO::relativePointer->destroyRelativePointer(this); }); } bool CRelativePointer::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CRelativePointer::client() { - return m_client; + return pClient; } void CRelativePointer::sendRelativeMotion(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { - m_resource->sendRelativeMotion(time >> 32, time & 0xFFFFFFFF, wl_fixed_from_double(delta.x), wl_fixed_from_double(delta.y), wl_fixed_from_double(deltaUnaccel.x), - wl_fixed_from_double(deltaUnaccel.y)); + resource->sendRelativeMotion(time >> 32, time & 0xFFFFFFFF, wl_fixed_from_double(delta.x), wl_fixed_from_double(delta.y), wl_fixed_from_double(deltaUnaccel.x), + wl_fixed_from_double(deltaUnaccel.y)); } CRelativePointerProtocol::CRelativePointerProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -31,7 +31,7 @@ CRelativePointerProtocol::CRelativePointerProtocol(const wl_interface* iface, co } void CRelativePointerProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpRelativePointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwpRelativePointerManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -39,32 +39,32 @@ void CRelativePointerProtocol::bindManager(wl_client* client, void* data, uint32 } void CRelativePointerProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CRelativePointerProtocol::destroyRelativePointer(CRelativePointer* pointer) { - std::erase_if(m_relativePointers, [&](const auto& other) { return other.get() == pointer; }); + std::erase_if(m_vRelativePointers, [&](const auto& other) { return other.get() == pointer; }); } void CRelativePointerProtocol::onGetRelativePointer(CZwpRelativePointerManagerV1* pMgr, uint32_t id, wl_resource* pointer) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_relativePointers.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); + const auto RESOURCE = m_vRelativePointers.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_relativePointers.pop_back(); + m_vRelativePointers.pop_back(); return; } } void CRelativePointerProtocol::sendRelativeMotion(uint64_t time, const Vector2D& delta, const Vector2D& deltaUnaccel) { - if (!g_pSeatManager->m_state.pointerFocusResource) + if (!g_pSeatManager->state.pointerFocusResource) return; - const auto FOCUSED = g_pSeatManager->m_state.pointerFocusResource->client(); + const auto FOCUSED = g_pSeatManager->state.pointerFocusResource->client(); - for (auto const& rp : m_relativePointers) { + for (auto const& rp : m_vRelativePointers) { if (FOCUSED != rp->client()) continue; diff --git a/src/protocols/RelativePointer.hpp b/src/protocols/RelativePointer.hpp index 3d6a3e30..ce060ed3 100644 --- a/src/protocols/RelativePointer.hpp +++ b/src/protocols/RelativePointer.hpp @@ -16,8 +16,8 @@ class CRelativePointer { wl_client* client(); private: - SP m_resource; - wl_client* m_client = nullptr; + SP resource; + wl_client* pClient = nullptr; }; class CRelativePointerProtocol : public IWaylandProtocol { @@ -34,8 +34,8 @@ class CRelativePointerProtocol : public IWaylandProtocol { void onGetRelativePointer(CZwpRelativePointerManagerV1* pMgr, uint32_t id, wl_resource* pointer); // - std::vector> m_managers; - std::vector> m_relativePointers; + std::vector> m_vManagers; + std::vector> m_vRelativePointers; friend class CRelativePointer; }; diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp index 825939ef..3fd9b9f2 100644 --- a/src/protocols/Screencopy.cpp +++ b/src/protocols/Screencopy.cpp @@ -1,191 +1,459 @@ #include "Screencopy.hpp" -#include "../managers/screenshare/ScreenshareManager.hpp" -#include "core/Output.hpp" +#include "../Compositor.hpp" +#include "../managers/eventLoop/EventLoopManager.hpp" +#include "../managers/PointerManager.hpp" +#include "../managers/EventManager.hpp" #include "../render/Renderer.hpp" +#include "../render/OpenGL.hpp" +#include "../helpers/Monitor.hpp" +#include "core/Output.hpp" +#include "types/WLBuffer.hpp" #include "types/Buffer.hpp" #include "../helpers/Format.hpp" -#include "../helpers/time/Time.hpp" -using namespace Screenshare; +#include +#include -CScreencopyClient::CScreencopyClient(SP resource_) : m_resource(resource_) { +CScreencopyFrame::~CScreencopyFrame() { + if (buffer && buffer->locked()) + buffer->unlock(); +} + +CScreencopyFrame::CScreencopyFrame(SP resource_, int32_t overlay_cursor, wl_resource* output, CBox box_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); }); - m_resource->setCaptureOutput( - [this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output) { captureOutput(frame, overlayCursor, output, {}); }); - m_resource->setCaptureOutputRegion([this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output, int32_t x, int32_t y, int32_t w, - int32_t h) { captureOutput(frame, overlayCursor, output, {x, y, w, h}); }); + overlayCursor = !!overlay_cursor; + pMonitor = CWLOutputResource::fromResource(output)->monitor; - m_savedClient = m_resource->client(); -} - -void CScreencopyClient::captureOutput(uint32_t frame, int32_t overlayCursor_, wl_resource* output, CBox box) { - const auto PMONITORRES = CWLOutputResource::fromResource(output); - if (!PMONITORRES || !PMONITORRES->m_monitor) { - LOGM(Log::ERR, "Tried to capture invalid output/monitor in {:x}", (uintptr_t)this); - m_resource->error(-1, "invalid output"); + if (!pMonitor) { + LOGM(ERR, "Client requested sharing of a monitor that doesnt exist"); + resource->sendFailed(); return; } - const auto PMONITOR = PMONITORRES->m_monitor.lock(); - auto session = box.w == 0 && box.h == 0 ? Screenshare::mgr()->getManagedSession(m_resource->client(), PMONITOR) : - Screenshare::mgr()->getManagedSession(m_resource->client(), PMONITOR, box); + resource->setOnDestroy([this](CZwlrScreencopyFrameV1* pMgr) { PROTO::screencopy->destroyResource(this); }); + resource->setDestroy([this](CZwlrScreencopyFrameV1* pFrame) { PROTO::screencopy->destroyResource(this); }); + resource->setCopy([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { this->copy(pFrame, res); }); + resource->setCopyWithDamage([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { + withDamage = true; + this->copy(pFrame, res); + }); - const auto FRAME = PROTO::screencopy->m_frames.emplace_back( - makeShared(makeShared(m_resource->client(), m_resource->version(), frame), session, !!overlayCursor_)); + g_pHyprRenderer->makeEGLCurrent(); + + shmFormat = g_pHyprOpenGL->getPreferredReadFormat(pMonitor.lock()); + if (shmFormat == DRM_FORMAT_INVALID) { + LOGM(ERR, "No format supported by renderer in capture output"); + resource->sendFailed(); + return; + } + + // TODO: hack, we can't bit flip so we'll format flip heh, GL_BGRA_EXT wont work here + if (shmFormat == DRM_FORMAT_XRGB2101010 || shmFormat == DRM_FORMAT_ARGB2101010) + shmFormat = DRM_FORMAT_XBGR2101010; + + const auto PSHMINFO = NFormatUtils::getPixelFormatFromDRM(shmFormat); + if (!PSHMINFO) { + LOGM(ERR, "No pixel format supported by renderer in capture output"); + resource->sendFailed(); + return; + } + + dmabufFormat = pMonitor->output->state->state().drmFormat; + + if (box_.width == 0 && box_.height == 0) + box = {0, 0, (int)(pMonitor->vecSize.x), (int)(pMonitor->vecSize.y)}; + else { + box = box_; + } + + box.transform(wlTransformToHyprutils(pMonitor->transform), pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y).scale(pMonitor->scale).round(); + + shmStride = NFormatUtils::minStride(PSHMINFO, box.w); + + resource->sendBuffer(NFormatUtils::drmToShm(shmFormat), box.width, box.height, shmStride); + + if (resource->version() >= 3) { + if LIKELY (dmabufFormat != DRM_FORMAT_INVALID) + resource->sendLinuxDmabuf(dmabufFormat, box.width, box.height); + + resource->sendBufferDone(); + } +} + +void CScreencopyFrame::copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer_) { + if UNLIKELY (!good()) { + LOGM(ERR, "No frame in copyFrame??"); + return; + } + + if UNLIKELY (!g_pCompositor->monitorExists(pMonitor.lock())) { + LOGM(ERR, "Client requested sharing of a monitor that is gone"); + resource->sendFailed(); + return; + } + + const auto PBUFFER = CWLBufferResource::fromResource(buffer_); + if UNLIKELY (!PBUFFER) { + LOGM(ERR, "Invalid buffer in {:x}", (uintptr_t)this); + resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); + PROTO::screencopy->destroyResource(this); + return; + } + + PBUFFER->buffer->lock(); + + if UNLIKELY (PBUFFER->buffer->size != box.size()) { + LOGM(ERR, "Invalid dimensions in {:x}", (uintptr_t)this); + resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions"); + PROTO::screencopy->destroyResource(this); + return; + } + + if UNLIKELY (buffer) { + LOGM(ERR, "Buffer used in {:x}", (uintptr_t)this); + resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used"); + PROTO::screencopy->destroyResource(this); + return; + } + + if (auto attrs = PBUFFER->buffer->dmabuf(); attrs.success) { + bufferDMA = true; + + if (attrs.format != dmabufFormat) { + LOGM(ERR, "Invalid buffer dma format in {:x}", (uintptr_t)pFrame); + resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); + PROTO::screencopy->destroyResource(this); + return; + } + } else if (auto attrs = PBUFFER->buffer->shm(); attrs.success) { + if (attrs.format != shmFormat) { + LOGM(ERR, "Invalid buffer shm format in {:x}", (uintptr_t)pFrame); + resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); + PROTO::screencopy->destroyResource(this); + return; + } else if ((int)attrs.stride != shmStride) { + LOGM(ERR, "Invalid buffer shm stride in {:x}", (uintptr_t)pFrame); + resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride"); + PROTO::screencopy->destroyResource(this); + return; + } + } else { + LOGM(ERR, "Invalid buffer type in {:x}", (uintptr_t)pFrame); + resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type"); + PROTO::screencopy->destroyResource(this); + return; + } + + buffer = PBUFFER->buffer; + + PROTO::screencopy->m_vFramesAwaitingWrite.emplace_back(self); + + g_pHyprRenderer->m_bDirectScanoutBlocked = true; + if (overlayCursor && !lockedSWCursors) { + lockedSWCursors = true; + // TODO: make it per-monitor + if (!PROTO::screencopy->m_bTimerArmed) { + for (auto const& m : g_pCompositor->m_vMonitors) { + g_pPointerManager->lockSoftwareForMonitor(m); + } + PROTO::screencopy->m_bTimerArmed = true; + LOGM(LOG, "Locking sw cursors due to screensharing"); + } + PROTO::screencopy->m_pSoftwareCursorTimer->updateTimeout(std::chrono::seconds(1)); + } + + if (!withDamage) + g_pHyprRenderer->damageMonitor(pMonitor.lock()); +} + +void CScreencopyFrame::share() { + if (!buffer || !pMonitor) + return; + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + auto callback = [this, now, weak = self](bool success) { + if (weak.expired()) + return; + + if (!success) { + LOGM(ERR, "{} copy failed in {:x}", bufferDMA ? "Dmabuf" : "Shm", (uintptr_t)this); + resource->sendFailed(); + return; + } + + resource->sendFlags((zwlrScreencopyFrameV1Flags)0); + if (withDamage) { + // TODO: add a damage ring for this. + resource->sendDamage(0, 0, buffer->size.x, buffer->size.y); + } + + uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0; + uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF; + resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec); + }; + + if (bufferDMA) + copyDmabuf(callback); + else + callback(copyShm()); +} + +void CScreencopyFrame::copyDmabuf(std::function callback) { + auto TEXTURE = makeShared(pMonitor->output->state->state().buffer); + + CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; + + if (!g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock(), nullptr, true)) { + LOGM(ERR, "Can't copy: failed to begin rendering to dma frame"); + callback(false); + return; + } + + CBox monbox = CBox{0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y} + .translate({-box.x, -box.y}) // vvvv kinda ass-backwards but that's how I designed the renderer... sigh. + .transform(wlTransformToHyprutils(invertTransform(pMonitor->transform)), pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); + g_pHyprOpenGL->setMonitorTransformEnabled(true); + g_pHyprOpenGL->setRenderModifEnabled(false); + g_pHyprOpenGL->renderTexture(TEXTURE, monbox, 1); + g_pHyprOpenGL->setRenderModifEnabled(true); + g_pHyprOpenGL->setMonitorTransformEnabled(false); + + g_pHyprOpenGL->m_RenderData.blockScreenShader = true; + g_pHyprRenderer->endRender(); + + auto explicitOptions = g_pHyprRenderer->getExplicitSyncSettings(pMonitor->output); + if (pMonitor->inTimeline && explicitOptions.explicitEnabled) { + pMonitor->inTimeline->addWaiter( + [callback]() { + LOGM(TRACE, "Copied frame via dma with explicit sync"); + callback(true); + }, + pMonitor->inTimelinePoint, 0); + } else { + LOGM(TRACE, "Copied frame via dma"); + callback(true); + } +} + +bool CScreencopyFrame::copyShm() { + auto TEXTURE = makeShared(pMonitor->output->state->state().buffer); + + auto shm = buffer->shm(); + auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm + + CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; + + g_pHyprRenderer->makeEGLCurrent(); + + CFramebuffer fb; + fb.alloc(box.w, box.h, pMonitor->output->state->state().drmFormat); + + if (!g_pHyprRenderer->beginRender(pMonitor.lock(), fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &fb, true)) { + LOGM(ERR, "Can't copy: failed to begin rendering"); + return false; + } + + CBox monbox = CBox{0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}.translate({-box.x, -box.y}); + g_pHyprOpenGL->setMonitorTransformEnabled(true); + g_pHyprOpenGL->setRenderModifEnabled(false); + g_pHyprOpenGL->renderTexture(TEXTURE, monbox, 1); + g_pHyprOpenGL->setRenderModifEnabled(true); + g_pHyprOpenGL->setMonitorTransformEnabled(false); + +#ifndef GLES2 + glBindFramebuffer(GL_READ_FRAMEBUFFER, fb.getFBID()); +#else + glBindFramebuffer(GL_FRAMEBUFFER, fb.getFBID()); +#endif + + const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(shm.format); + if (!PFORMAT) { + LOGM(ERR, "Can't copy: failed to find a pixel format"); + g_pHyprRenderer->endRender(); + return false; + } + + auto glFormat = PFORMAT->flipRB ? GL_BGRA_EXT : GL_RGBA; + + g_pHyprOpenGL->m_RenderData.blockScreenShader = true; + g_pHyprRenderer->endRender(); + + g_pHyprRenderer->makeEGLCurrent(); + g_pHyprOpenGL->m_RenderData.pMonitor = pMonitor; + fb.bind(); + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + const auto drmFmt = NFormatUtils::getPixelFormatFromDRM(shm.format); + uint32_t packStride = NFormatUtils::minStride(drmFmt, box.w); + + // This could be optimized by using a pixel buffer object to make this async, + // but really clients should just use a dma buffer anyways. + if (packStride == (uint32_t)shm.stride) { + glReadPixels(0, 0, box.w, box.h, glFormat, PFORMAT->glType, pixelData); + } else { + for (size_t i = 0; i < box.h; ++i) { + uint32_t y = i; + glReadPixels(0, y, box.w, 1, glFormat, PFORMAT->glType, ((unsigned char*)pixelData) + i * shm.stride); + } + } + + g_pHyprOpenGL->m_RenderData.pMonitor.reset(); + +#ifndef GLES2 + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +#else + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + LOGM(TRACE, "Copied frame via shm"); + + return true; +} + +bool CScreencopyFrame::good() { + return resource->resource(); +} + +CScreencopyClient::~CScreencopyClient() { + g_pHookSystem->unhook(tickCallback); +} + +CScreencopyClient::CScreencopyClient(SP resource_) : resource(resource_) { + if UNLIKELY (!good()) + return; + + resource->setDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrScreencopyManagerV1* pMgr) { PROTO::screencopy->destroyResource(this); }); + resource->setCaptureOutput( + [this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output) { this->captureOutput(frame, overlayCursor, output, {}); }); + resource->setCaptureOutputRegion([this](CZwlrScreencopyManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* output, int32_t x, int32_t y, int32_t w, + int32_t h) { this->captureOutput(frame, overlayCursor, output, {x, y, w, h}); }); + + lastMeasure.reset(); + lastFrame.reset(); + tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, SCallbackInfo& info, std::any data) { onTick(); }); +} + +void CScreencopyClient::captureOutput(uint32_t frame, int32_t overlayCursor_, wl_resource* output, CBox box) { + const auto FRAME = PROTO::screencopy->m_vFrames.emplace_back( + makeShared(makeShared(resource->client(), resource->version(), frame), overlayCursor_, output, box)); if (!FRAME->good()) { - LOGM(Log::ERR, "Couldn't alloc frame for sharing! (no memory)"); - m_resource->noMemory(); + LOGM(ERR, "Couldn't alloc frame for sharing! (no memory)"); + resource->noMemory(); PROTO::screencopy->destroyResource(FRAME.get()); return; } - FRAME->m_client = m_self; - FRAME->m_self = FRAME; + FRAME->self = FRAME; + FRAME->client = self; +} + +void CScreencopyClient::onTick() { + if (lastMeasure.getMillis() < 500) + return; + + framesInLastHalfSecond = frameCounter; + frameCounter = 0; + lastMeasure.reset(); + + const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0; + const bool FRAMEAWAITING = std::ranges::any_of(PROTO::screencopy->m_vFrames, [&](const auto& frame) { return frame->client.get() == this; }); + + if (framesInLastHalfSecond > 3 && !sentScreencast) { + EMIT_HOOK_EVENT("screencast", (std::vector{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner})); + g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)}); + sentScreencast = true; + } else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) { + EMIT_HOOK_EVENT("screencast", (std::vector{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner})); + g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)}); + sentScreencast = false; + } } bool CScreencopyClient::good() { - return m_resource && m_resource->resource(); -} - -CScreencopyFrame::CScreencopyFrame(SP resource_, WP session, bool overlayCursor) : - m_resource(resource_), m_session(session), m_overlayCursor(overlayCursor) { - if UNLIKELY (!good()) - return; - - m_resource->setOnDestroy([this](CZwlrScreencopyFrameV1* pMgr) { PROTO::screencopy->destroyResource(this); }); - m_resource->setDestroy([this](CZwlrScreencopyFrameV1* pFrame) { PROTO::screencopy->destroyResource(this); }); - m_resource->setCopy([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { shareFrame(pFrame, res, false); }); - m_resource->setCopyWithDamage([this](CZwlrScreencopyFrameV1* pFrame, wl_resource* res) { shareFrame(pFrame, res, true); }); - - m_frame = m_session->nextFrame(overlayCursor); - - auto formats = m_session->allowedFormats(); - if (formats.empty()) { - LOGM(Log::ERR, "No format supported by renderer in screencopy protocol"); - m_resource->sendFailed(); - return; - } - - DRMFormat format = formats.at(0); - auto bufSize = m_frame->bufferSize(); - - const auto PSHMINFO = NFormatUtils::getPixelFormatFromDRM(format); - - if (!PSHMINFO) { - LOGM(Log::ERR, "No pixel format for drm format"); - m_resource->sendFailed(); - return; - } - - const auto stride = NFormatUtils::minStride(PSHMINFO, bufSize.x); - m_resource->sendBuffer(NFormatUtils::drmToShm(format), bufSize.x, bufSize.y, stride); - - if (m_resource->version() >= 3) { - if LIKELY (format != DRM_FORMAT_INVALID) - m_resource->sendLinuxDmabuf(format, bufSize.x, bufSize.y); - - m_resource->sendBufferDone(); - } -} - -void CScreencopyFrame::shareFrame(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer, bool withDamage) { - if UNLIKELY (!good()) { - LOGM(Log::ERR, "No frame in shareFrame??"); - return; - } - - if UNLIKELY (m_session.expired() || !m_session->monitor()) { - LOGM(Log::ERR, "Session stopped for frame {:x}", (uintptr_t)this); - m_resource->sendFailed(); - return; - } - - if UNLIKELY (m_buffer) { - LOGM(Log::ERR, "Buffer used in {:x}", (uintptr_t)this); - m_resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used"); - m_resource->sendFailed(); - return; - } - - const auto PBUFFERRES = CWLBufferResource::fromResource(buffer); - if UNLIKELY (!PBUFFERRES || !PBUFFERRES->m_buffer) { - LOGM(Log::ERR, "Invalid buffer in {:x}", (uintptr_t)this); - m_resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); - m_resource->sendFailed(); - return; - } - - const auto& PBUFFER = PBUFFERRES->m_buffer.lock(); - - if (!withDamage) - g_pHyprRenderer->damageMonitor(m_session->monitor()); - - auto error = m_frame->share(PBUFFER, {}, [this, withDamage, self = m_self](eScreenshareResult result) { - if (self.expired() || !good()) - return; - switch (result) { - case RESULT_COPIED: { - m_resource->sendFlags(sc(0)); - if (withDamage) - m_frame->damage().forEachRect([&](const auto& rect) { m_resource->sendDamage(rect.x1, rect.y1, rect.x2 - rect.x1, rect.y2 - rect.y1); }); - - const auto [sec, nsec] = Time::secNsec(m_timestamp); - uint32_t tvSecHi = (sizeof(sec) > 4) ? sec >> 32 : 0; - uint32_t tvSecLo = sec & 0xFFFFFFFF; - m_resource->sendReady(tvSecHi, tvSecLo, nsec); - break; - } - case RESULT_NOT_COPIED: - LOGM(Log::ERR, "Frame share failed in {:x}", (uintptr_t)this); - m_resource->sendFailed(); - break; - case RESULT_TIMESTAMP: m_timestamp = Time::steadyNow(); break; - } - }); - - switch (error) { - case ERROR_NONE: m_buffer = CHLBufferReference(PBUFFER); break; - case ERROR_NO_BUFFER: m_resource->error(ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); break; - case ERROR_BUFFER_SIZE: - case ERROR_BUFFER_FORMAT: m_resource->sendFailed(); break; - case ERROR_UNKNOWN: - case ERROR_STOPPED: m_resource->sendFailed(); break; - } -} - -bool CScreencopyFrame::good() { - return m_resource && m_resource->resource(); + return resource->resource(); } CScreencopyProtocol::CScreencopyProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; + m_pSoftwareCursorTimer = makeShared( + std::nullopt, + [this](SP self, void* data) { + // TODO: make it per-monitor + for (auto const& m : g_pCompositor->m_vMonitors) { + g_pPointerManager->unlockSoftwareForMonitor(m); + } + m_bTimerArmed = false; + + LOGM(LOG, "Releasing software cursor lock"); + }, + nullptr); + g_pEventLoopManager->addTimer(m_pSoftwareCursorTimer); } void CScreencopyProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto CLIENT = m_clients.emplace_back(makeShared(makeShared(client, ver, id))); + const auto CLIENT = m_vClients.emplace_back(makeShared(makeShared(client, ver, id))); if (!CLIENT->good()) { - LOGM(Log::DEBUG, "Failed to bind client! (out of memory)"); - CLIENT->m_resource->noMemory(); - m_clients.pop_back(); + LOGM(LOG, "Failed to bind client! (out of memory)"); + CLIENT->resource->noMemory(); + m_vClients.pop_back(); return; } - CLIENT->m_self = CLIENT; + CLIENT->self = CLIENT; - LOGM(Log::DEBUG, "Bound client successfully!"); + LOGM(LOG, "Bound client successfully!"); } void CScreencopyProtocol::destroyResource(CScreencopyClient* client) { - std::erase_if(m_frames, [&](const auto& other) { return other->m_client.get() == client; }); - std::erase_if(m_clients, [&](const auto& other) { return other.get() == client; }); + std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; }); + std::erase_if(m_vFrames, [&](const auto& other) { return other->client.get() == client; }); + std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return !other || other->client.get() == client; }); } void CScreencopyProtocol::destroyResource(CScreencopyFrame* frame) { - std::erase_if(m_frames, [&](const auto& other) { return other.get() == frame; }); + std::erase_if(m_vFrames, [&](const auto& other) { return other.get() == frame; }); + std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return !other || other.get() == frame; }); +} + +void CScreencopyProtocol::onOutputCommit(PHLMONITOR pMonitor) { + if (m_vFramesAwaitingWrite.empty()) { + g_pHyprRenderer->m_bDirectScanoutBlocked = false; + return; // nothing to share + } + + std::vector> framesToRemove; + // reserve number of elements to avoid reallocations + framesToRemove.reserve(m_vFramesAwaitingWrite.size()); + + // share frame if correct output + for (auto const& f : m_vFramesAwaitingWrite) { + if (!f) + continue; + + if (!f->pMonitor || !f->buffer) { + framesToRemove.emplace_back(f); + continue; + } + + if (f->pMonitor != pMonitor) + continue; + + f->share(); + + f->client->lastFrame.reset(); + ++f->client->frameCounter; + + framesToRemove.emplace_back(f); + } + + for (auto const& f : framesToRemove) { + std::erase(m_vFramesAwaitingWrite, f); + } } diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp index b73c090d..3b46b0b8 100644 --- a/src/protocols/Screencopy.hpp +++ b/src/protocols/Screencopy.hpp @@ -1,32 +1,46 @@ #pragma once -#include "WaylandProtocol.hpp" +#include "../defines.hpp" #include "wlr-screencopy-unstable-v1.hpp" +#include "WaylandProtocol.hpp" -#include "../helpers/time/Time.hpp" -#include "./types/Buffer.hpp" -#include - +#include #include +#include "../managers/HookSystemManager.hpp" +#include "../helpers/Timer.hpp" +#include "../managers/eventLoop/EventLoopTimer.hpp" +#include class CMonitor; class IHLBuffer; -namespace Screenshare { - class CScreenshareSession; - class CScreenshareFrame; + +enum eClientOwners { + CLIENT_SCREENCOPY = 0, + CLIENT_TOPLEVEL_EXPORT }; class CScreencopyClient { public: CScreencopyClient(SP resource_); + ~CScreencopyClient(); - bool good(); + bool good(); + + WP self; + eClientOwners clientOwner = CLIENT_SCREENCOPY; + + CTimer lastFrame; + int frameCounter = 0; private: - SP m_resource; - WP m_self; + SP resource; - wl_client* m_savedClient = nullptr; + int framesInLastHalfSecond = 0; + CTimer lastMeasure; + bool sentScreencast = false; + + SP tickCallback; + void onTick(); void captureOutput(uint32_t frame, int32_t overlayCursor, wl_resource* output, CBox box); @@ -35,27 +49,35 @@ class CScreencopyClient { class CScreencopyFrame { public: - CScreencopyFrame(SP resource, WP session, bool overlayCursor); + CScreencopyFrame(SP resource, int32_t overlay_cursor, wl_resource* output, CBox box); + ~CScreencopyFrame(); - bool good(); + bool good(); + + WP self; + WP client; private: - SP m_resource; - WP m_self; - WP m_client; + SP resource; - WP m_session; - UP m_frame; + PHLMONITORREF pMonitor; + bool overlayCursor = false; + bool withDamage = false; + bool lockedSWCursors = false; - CHLBufferReference m_buffer; - Time::steady_tp m_timestamp; - bool m_overlayCursor = true; + WP buffer; + bool bufferDMA = false; + uint32_t shmFormat = 0; + uint32_t dmabufFormat = 0; + int shmStride = 0; + CBox box = {}; - // - void shareFrame(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer, bool withDamage); + void copy(CZwlrScreencopyFrameV1* pFrame, wl_resource* buffer); + void copyDmabuf(std::function callback); + bool copyShm(); + void share(); friend class CScreencopyProtocol; - friend class CScreencopyClient; }; class CScreencopyProtocol : public IWaylandProtocol { @@ -66,9 +88,21 @@ class CScreencopyProtocol : public IWaylandProtocol { void destroyResource(CScreencopyClient* resource); void destroyResource(CScreencopyFrame* resource); + void onOutputCommit(PHLMONITOR pMonitor); + private: - std::vector> m_frames; - std::vector> m_clients; + std::vector> m_vFrames; + std::vector> m_vFramesAwaitingWrite; + std::vector> m_vClients; + + SP m_pSoftwareCursorTimer; + bool m_bTimerArmed = false; + + void shareAllFrames(PHLMONITOR pMonitor); + void shareFrame(CScreencopyFrame* frame); + void sendFrameDamage(CScreencopyFrame* frame); + bool copyFrameDmabuf(CScreencopyFrame* frame); + bool copyFrameShm(CScreencopyFrame* frame, timespec* now); friend class CScreencopyFrame; friend class CScreencopyClient; diff --git a/src/protocols/SecurityContext.cpp b/src/protocols/SecurityContext.cpp index 6c7f8226..30fca260 100644 --- a/src/protocols/SecurityContext.cpp +++ b/src/protocols/SecurityContext.cpp @@ -4,20 +4,20 @@ using namespace Hyprutils::OS; static int onListenFdEvent(int fd, uint32_t mask, void* data) { - auto secCtx = sc(data); - secCtx->onListen(mask); + auto sc = (CSecurityContext*)data; + sc->onListen(mask); return 0; } static int onCloseFdEvent(int fd, uint32_t mask, void* data) { - auto secCtx = sc(data); - secCtx->onClose(mask); + auto sc = (CSecurityContext*)data; + sc->onClose(mask); return 0; } SP CSecurityContextSandboxedClient::create(CFileDescriptor clientFD_) { auto p = SP(new CSecurityContextSandboxedClient(std::move(clientFD_))); - if (!p->m_client) + if (!p->client) return nullptr; return p; } @@ -28,95 +28,95 @@ static void onSecurityContextClientDestroy(wl_listener* l, void* d) { client->onDestroy(); } -CSecurityContextSandboxedClient::CSecurityContextSandboxedClient(CFileDescriptor clientFD_) : m_clientFD(std::move(clientFD_)) { - m_client = wl_client_create(g_pCompositor->m_wlDisplay, m_clientFD.get()); - if (!m_client) +CSecurityContextSandboxedClient::CSecurityContextSandboxedClient(CFileDescriptor clientFD_) : clientFD(std::move(clientFD_)) { + client = wl_client_create(g_pCompositor->m_sWLDisplay, clientFD.get()); + if (!client) return; - wl_list_init(&m_destroyListener.listener.link); - m_destroyListener.listener.notify = ::onSecurityContextClientDestroy; - m_destroyListener.parent = this; - wl_client_add_destroy_late_listener(m_client, &m_destroyListener.listener); + wl_list_init(&destroyListener.listener.link); + destroyListener.listener.notify = ::onSecurityContextClientDestroy; + destroyListener.parent = this; + wl_client_add_destroy_late_listener(client, &destroyListener.listener); } CSecurityContextSandboxedClient::~CSecurityContextSandboxedClient() { - wl_list_remove(&m_destroyListener.listener.link); - wl_list_init(&m_destroyListener.listener.link); + wl_list_remove(&destroyListener.listener.link); + wl_list_init(&destroyListener.listener.link); } void CSecurityContextSandboxedClient::onDestroy() { - std::erase_if(PROTO::securityContext->m_sandboxedClients, [this](const auto& e) { return e.get() == this; }); + std::erase_if(PROTO::securityContext->m_vSandboxedClients, [this](const auto& e) { return e.get() == this; }); } -CSecurityContext::CSecurityContext(SP resource_, int listenFD_, int closeFD_) : m_listenFD(listenFD_), m_closeFD(closeFD_), m_resource(resource_) { +CSecurityContext::CSecurityContext(SP resource_, int listenFD_, int closeFD_) : listenFD(listenFD_), closeFD(closeFD_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CWpSecurityContextV1* r) { - LOGM(Log::DEBUG, "security_context at 0x{:x}: resource destroyed, keeping context until fd hangup", (uintptr_t)this); - m_resource = nullptr; + resource->setDestroy([this](CWpSecurityContextV1* r) { + LOGM(LOG, "security_context at 0x{:x}: resource destroyed, keeping context until fd hangup", (uintptr_t)this); + resource = nullptr; }); - m_resource->setOnDestroy([this](CWpSecurityContextV1* r) { - LOGM(Log::DEBUG, "security_context at 0x{:x}: resource destroyed, keeping context until fd hangup", (uintptr_t)this); - m_resource = nullptr; + resource->setOnDestroy([this](CWpSecurityContextV1* r) { + LOGM(LOG, "security_context at 0x{:x}: resource destroyed, keeping context until fd hangup", (uintptr_t)this); + resource = nullptr; }); - LOGM(Log::DEBUG, "New security_context at 0x{:x}", (uintptr_t)this); + LOGM(LOG, "New security_context at 0x{:x}", (uintptr_t)this); - m_resource->setSetSandboxEngine([this](CWpSecurityContextV1* r, const char* engine) { - if UNLIKELY (!m_sandboxEngine.empty()) { + resource->setSetSandboxEngine([this](CWpSecurityContextV1* r, const char* engine) { + if UNLIKELY (!sandboxEngine.empty()) { r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "Sandbox engine already set"); return; } - if UNLIKELY (m_committed) { + if UNLIKELY (committed) { r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Context already committed"); return; } - m_sandboxEngine = engine ? engine : "(null)"; - LOGM(Log::DEBUG, "security_context at 0x{:x} sets engine to {}", (uintptr_t)this, m_sandboxEngine); + sandboxEngine = engine ? engine : "(null)"; + LOGM(LOG, "security_context at 0x{:x} sets engine to {}", (uintptr_t)this, sandboxEngine); }); - m_resource->setSetAppId([this](CWpSecurityContextV1* r, const char* appid) { - if UNLIKELY (!m_appID.empty()) { + resource->setSetAppId([this](CWpSecurityContextV1* r, const char* appid) { + if UNLIKELY (!appID.empty()) { r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "Sandbox appid already set"); return; } - if UNLIKELY (m_committed) { + if UNLIKELY (committed) { r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Context already committed"); return; } - m_appID = appid ? appid : "(null)"; - LOGM(Log::DEBUG, "security_context at 0x{:x} sets appid to {}", (uintptr_t)this, m_appID); + appID = appid ? appid : "(null)"; + LOGM(LOG, "security_context at 0x{:x} sets appid to {}", (uintptr_t)this, appID); }); - m_resource->setSetInstanceId([this](CWpSecurityContextV1* r, const char* instance) { - if UNLIKELY (!m_instanceID.empty()) { + resource->setSetInstanceId([this](CWpSecurityContextV1* r, const char* instance) { + if UNLIKELY (!instanceID.empty()) { r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "Sandbox instance already set"); return; } - if UNLIKELY (m_committed) { + if UNLIKELY (committed) { r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Context already committed"); return; } - m_instanceID = instance ? instance : "(null)"; - LOGM(Log::DEBUG, "security_context at 0x{:x} sets instance to {}", (uintptr_t)this, m_instanceID); + instanceID = instance ? instance : "(null)"; + LOGM(LOG, "security_context at 0x{:x} sets instance to {}", (uintptr_t)this, instanceID); }); - m_resource->setCommit([this](CWpSecurityContextV1* r) { - m_committed = true; + resource->setCommit([this](CWpSecurityContextV1* r) { + committed = true; - LOGM(Log::DEBUG, "security_context at 0x{:x} commits", (uintptr_t)this); + LOGM(LOG, "security_context at 0x{:x} commits", (uintptr_t)this); - m_listenSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_listenFD.get(), WL_EVENT_READABLE, ::onListenFdEvent, this); - m_closeSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_closeFD.get(), 0, ::onCloseFdEvent, this); + listenSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, listenFD.get(), WL_EVENT_READABLE, ::onListenFdEvent, this); + closeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, closeFD.get(), 0, ::onCloseFdEvent, this); - if (!m_listenSource || !m_closeSource) { + if (!listenSource || !closeSource) { r->noMemory(); return; } @@ -124,19 +124,19 @@ CSecurityContext::CSecurityContext(SP resource_, int liste } CSecurityContext::~CSecurityContext() { - if (m_listenSource) - wl_event_source_remove(m_listenSource); - if (m_closeSource) - wl_event_source_remove(m_closeSource); + if (listenSource) + wl_event_source_remove(listenSource); + if (closeSource) + wl_event_source_remove(closeSource); } bool CSecurityContext::good() { - return m_resource->resource(); + return resource->resource(); } void CSecurityContext::onListen(uint32_t mask) { if UNLIKELY (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { - LOGM(Log::ERR, "security_context at 0x{:x} got an error in listen", (uintptr_t)this); + LOGM(ERR, "security_context at 0x{:x} got an error in listen", (uintptr_t)this); PROTO::securityContext->destroyContext(this); return; } @@ -144,21 +144,21 @@ void CSecurityContext::onListen(uint32_t mask) { if (!(mask & WL_EVENT_READABLE)) return; - CFileDescriptor clientFD{accept(m_listenFD.get(), nullptr, nullptr)}; + CFileDescriptor clientFD{accept(listenFD.get(), nullptr, nullptr)}; if UNLIKELY (!clientFD.isValid()) { - LOGM(Log::ERR, "security_context at 0x{:x} couldn't accept", (uintptr_t)this); + LOGM(ERR, "security_context at 0x{:x} couldn't accept", (uintptr_t)this); return; } auto newClient = CSecurityContextSandboxedClient::create(std::move(clientFD)); if UNLIKELY (!newClient) { - LOGM(Log::ERR, "security_context at 0x{:x} couldn't create a client", (uintptr_t)this); + LOGM(ERR, "security_context at 0x{:x} couldn't create a client", (uintptr_t)this); return; } - PROTO::securityContext->m_sandboxedClients.emplace_back(newClient); + PROTO::securityContext->m_vSandboxedClients.emplace_back(newClient); - LOGM(Log::DEBUG, "security_context at 0x{:x} got a new wl_client 0x{:x}", (uintptr_t)this, (uintptr_t)newClient->m_client); + LOGM(LOG, "security_context at 0x{:x} got a new wl_client 0x{:x}", (uintptr_t)this, (uintptr_t)newClient->client); } void CSecurityContext::onClose(uint32_t mask) { @@ -168,27 +168,27 @@ void CSecurityContext::onClose(uint32_t mask) { PROTO::securityContext->destroyContext(this); } -CSecurityContextManagerResource::CSecurityContextManagerResource(SP resource_) : m_resource(resource_) { +CSecurityContextManagerResource::CSecurityContextManagerResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CWpSecurityContextManagerV1* r) { PROTO::securityContext->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpSecurityContextManagerV1* r) { PROTO::securityContext->destroyResource(this); }); + resource->setDestroy([this](CWpSecurityContextManagerV1* r) { PROTO::securityContext->destroyResource(this); }); + resource->setOnDestroy([this](CWpSecurityContextManagerV1* r) { PROTO::securityContext->destroyResource(this); }); - m_resource->setCreateListener([](CWpSecurityContextManagerV1* r, uint32_t id, int32_t lfd, int32_t cfd) { + resource->setCreateListener([](CWpSecurityContextManagerV1* r, uint32_t id, int32_t lfd, int32_t cfd) { const auto RESOURCE = - PROTO::securityContext->m_contexts.emplace_back(makeShared(makeShared(r->client(), r->version(), id), lfd, cfd)); + PROTO::securityContext->m_vContexts.emplace_back(makeShared(makeShared(r->client(), r->version(), id), lfd, cfd)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::securityContext->m_contexts.pop_back(); + PROTO::securityContext->m_vContexts.pop_back(); return; } }); } bool CSecurityContextManagerResource::good() { - return m_resource->resource(); + return resource->resource(); } CSecurityContextProtocol::CSecurityContextProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -196,23 +196,23 @@ CSecurityContextProtocol::CSecurityContextProtocol(const wl_interface* iface, co } void CSecurityContextProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CSecurityContextProtocol::destroyResource(CSecurityContextManagerResource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == res; }); } void CSecurityContextProtocol::destroyContext(CSecurityContext* context) { - std::erase_if(m_contexts, [&](const auto& other) { return other.get() == context; }); + std::erase_if(m_vContexts, [&](const auto& other) { return other.get() == context; }); } bool CSecurityContextProtocol::isClientSandboxed(const wl_client* client) { - return std::ranges::find_if(m_sandboxedClients, [client](const auto& e) { return e->m_client == client; }) != m_sandboxedClients.end(); + return std::find_if(m_vSandboxedClients.begin(), m_vSandboxedClients.end(), [client](const auto& e) { return e->client == client; }) != m_vSandboxedClients.end(); } diff --git a/src/protocols/SecurityContext.hpp b/src/protocols/SecurityContext.hpp index c2acb66a..56d4f7b4 100644 --- a/src/protocols/SecurityContext.hpp +++ b/src/protocols/SecurityContext.hpp @@ -13,23 +13,18 @@ class CSecurityContext { bool good(); - std::string m_sandboxEngine; - std::string m_appID; - std::string m_instanceID; - - Hyprutils::OS::CFileDescriptor m_listenFD; - Hyprutils::OS::CFileDescriptor m_closeFD; + std::string sandboxEngine, appID, instanceID; + Hyprutils::OS::CFileDescriptor listenFD, closeFD; void onListen(uint32_t mask); void onClose(uint32_t mask); private: - SP m_resource; + SP resource; - wl_event_source* m_listenSource = nullptr; - wl_event_source* m_closeSource = nullptr; + wl_event_source * listenSource = nullptr, *closeSource = nullptr; - bool m_committed = false; + bool committed = false; }; class CSecurityContextManagerResource { @@ -39,7 +34,7 @@ class CSecurityContextManagerResource { bool good(); private: - SP m_resource; + SP resource; }; class CSecurityContextSandboxedClient; @@ -55,13 +50,13 @@ class CSecurityContextSandboxedClient { void onDestroy(); - SCSecurityContextSandboxedClientDestroyWrapper m_destroyListener; + SCSecurityContextSandboxedClientDestroyWrapper destroyListener; private: CSecurityContextSandboxedClient(Hyprutils::OS::CFileDescriptor clientFD_); - wl_client* m_client = nullptr; - Hyprutils::OS::CFileDescriptor m_clientFD; + wl_client* client = nullptr; + Hyprutils::OS::CFileDescriptor clientFD; friend class CSecurityContextProtocol; friend class CSecurityContext; @@ -81,9 +76,9 @@ class CSecurityContextProtocol : public IWaylandProtocol { void destroyContext(CSecurityContext* context); // - std::vector> m_managers; - std::vector> m_contexts; - std::vector> m_sandboxedClients; + std::vector> m_vManagers; + std::vector> m_vContexts; + std::vector> m_vSandboxedClients; friend class CSecurityContextManagerResource; friend class CSecurityContext; diff --git a/src/protocols/ServerDecorationKDE.cpp b/src/protocols/ServerDecorationKDE.cpp index 1db5a0c6..0726c8a5 100644 --- a/src/protocols/ServerDecorationKDE.cpp +++ b/src/protocols/ServerDecorationKDE.cpp @@ -1,43 +1,19 @@ #include "ServerDecorationKDE.hpp" #include "core/Compositor.hpp" -CServerDecorationKDE::CServerDecorationKDE(SP resource_, SP surf_) : m_surf(surf_), m_resource(resource_) { +CServerDecorationKDE::CServerDecorationKDE(SP resource_, SP surf) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setRelease([this](COrgKdeKwinServerDecoration* pMgr) { PROTO::serverDecorationKDE->destroyResource(this); }); - m_resource->setOnDestroy([this](COrgKdeKwinServerDecoration* pMgr) { PROTO::serverDecorationKDE->destroyResource(this); }); - m_resource->setRequestMode([this](COrgKdeKwinServerDecoration*, uint32_t mode) { - if (m_requestsSent > 3) - return; // don't start a tug of war - - auto sendMode = kdeModeOnRequestCSD(mode); - m_resource->sendMode(sendMode); - m_mostRecentlySent = sendMode; - m_mostRecentlyRequested = mode; - m_requestsSent++; - }); + resource->setRelease([this](COrgKdeKwinServerDecoration* pMgr) { PROTO::serverDecorationKDE->destroyResource(this); }); + resource->setOnDestroy([this](COrgKdeKwinServerDecoration* pMgr) { PROTO::serverDecorationKDE->destroyResource(this); }); // we send this and ignore request_mode. - auto sendMode = kdeDefaultModeCSD(); - m_resource->sendMode(sendMode); - m_mostRecentlySent = sendMode; + resource->sendMode(ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER); } bool CServerDecorationKDE::good() { - return m_resource->resource(); -} - -uint32_t CServerDecorationKDE::kdeDefaultModeCSD() { - return ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER; -} - -uint32_t CServerDecorationKDE::kdeModeOnRequestCSD(uint32_t modeRequestedByClient) { - return kdeDefaultModeCSD(); -} - -uint32_t CServerDecorationKDE::kdeModeOnReleaseCSD() { - return kdeDefaultModeCSD(); + return resource->resource(); } CServerDecorationKDEProtocol::CServerDecorationKDEProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -45,35 +21,31 @@ CServerDecorationKDEProtocol::CServerDecorationKDEProtocol(const wl_interface* i } void CServerDecorationKDEProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](COrgKdeKwinServerDecorationManager* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setCreate([this](COrgKdeKwinServerDecorationManager* pMgr, uint32_t id, wl_resource* pointer) { this->createDecoration(pMgr, id, pointer); }); // send default mode of SSD, as Hyprland will never ask for CSD. Screw Gnome and GTK. - RESOURCE->sendDefaultMode(kdeDefaultManagerModeCSD()); -} - -uint32_t CServerDecorationKDEProtocol::kdeDefaultManagerModeCSD() { - return ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER; + RESOURCE->sendDefaultMode(ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER); } void CServerDecorationKDEProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CServerDecorationKDEProtocol::destroyResource(CServerDecorationKDE* hayperlaaaand) { - std::erase_if(m_decos, [&](const auto& other) { return other.get() == hayperlaaaand; }); + std::erase_if(m_vDecos, [&](const auto& other) { return other.get() == hayperlaaaand; }); } void CServerDecorationKDEProtocol::createDecoration(COrgKdeKwinServerDecorationManager* pMgr, uint32_t id, wl_resource* surf) { const auto CLIENT = pMgr->client(); const auto RESOURCE = - m_decos.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surf))).get(); + m_vDecos.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surf))).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_decos.pop_back(); + m_vDecos.pop_back(); return; } } diff --git a/src/protocols/ServerDecorationKDE.hpp b/src/protocols/ServerDecorationKDE.hpp index 7eb2395a..0d0fa898 100644 --- a/src/protocols/ServerDecorationKDE.hpp +++ b/src/protocols/ServerDecorationKDE.hpp @@ -11,21 +11,10 @@ class CServerDecorationKDE { public: CServerDecorationKDE(SP resource_, SP surf); - SP m_surf; - - uint32_t m_mostRecentlySent = 0; - uint32_t m_mostRecentlyRequested = 0; - - bool good(); + bool good(); private: - uint32_t kdeDefaultModeCSD(); - uint32_t kdeModeOnRequestCSD(uint32_t modeRequestedByClient); - uint32_t kdeModeOnReleaseCSD(); - - SP m_resource; - - uint32_t m_requestsSent = 0; + SP resource; }; class CServerDecorationKDEProtocol : public IWaylandProtocol { @@ -35,16 +24,14 @@ class CServerDecorationKDEProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); private: - uint32_t kdeDefaultManagerModeCSD(); + void onManagerResourceDestroy(wl_resource* res); + void destroyResource(CServerDecorationKDE* deco); - void onManagerResourceDestroy(wl_resource* res); - void destroyResource(CServerDecorationKDE* deco); - - void createDecoration(COrgKdeKwinServerDecorationManager* pMgr, uint32_t id, wl_resource* surf); + void createDecoration(COrgKdeKwinServerDecorationManager* pMgr, uint32_t id, wl_resource* surf); // - std::vector> m_managers; - std::vector> m_decos; + std::vector> m_vManagers; + std::vector> m_vDecos; friend class CServerDecorationKDE; }; diff --git a/src/protocols/SessionLock.cpp b/src/protocols/SessionLock.cpp index 88231f38..d87775e9 100644 --- a/src/protocols/SessionLock.cpp +++ b/src/protocols/SessionLock.cpp @@ -1,161 +1,146 @@ #include "SessionLock.hpp" +#include "../Compositor.hpp" #include "../managers/SeatManager.hpp" #include "FractionalScale.hpp" #include "LockNotify.hpp" #include "core/Compositor.hpp" #include "core/Output.hpp" #include "../helpers/Monitor.hpp" -#include "../render/Renderer.hpp" -#include "../desktop/state/FocusState.hpp" CSessionLockSurface::CSessionLockSurface(SP resource_, SP surface_, PHLMONITOR pMonitor_, WP owner_) : - m_resource(resource_), m_sessionLock(owner_), m_surface(surface_), m_monitor(pMonitor_) { - if UNLIKELY (!m_resource->resource()) + resource(resource_), sessionLock(owner_), pSurface(surface_), pMonitor(pMonitor_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setDestroy([this](CExtSessionLockSurfaceV1* r) { - m_events.destroy.emit(); + resource->setDestroy([this](CExtSessionLockSurfaceV1* r) { + events.destroy.emit(); PROTO::sessionLock->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtSessionLockSurfaceV1* r) { - m_events.destroy.emit(); + resource->setOnDestroy([this](CExtSessionLockSurfaceV1* r) { + events.destroy.emit(); PROTO::sessionLock->destroyResource(this); }); - m_resource->setAckConfigure([this](CExtSessionLockSurfaceV1* r, uint32_t serial) { m_ackdConfigure = true; }); + resource->setAckConfigure([this](CExtSessionLockSurfaceV1* r, uint32_t serial) { ackdConfigure = true; }); - m_listeners.surfaceCommit = m_surface->m_events.commit.listen([this] { - if (!m_surface->m_current.texture) { - LOGM(Log::ERR, "SessionLock attached a null buffer"); - m_resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "Null buffer attached"); + listeners.surfaceCommit = pSurface->events.commit.registerListener([this](std::any d) { + if (!pSurface->current.texture) { + LOGM(ERR, "SessionLock attached a null buffer"); + resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "Null buffer attached"); return; } - if (!m_ackdConfigure) { - LOGM(Log::ERR, "SessionLock committed without an ack"); - m_resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK, "Committed surface before first ack"); + if (!ackdConfigure) { + LOGM(ERR, "SessionLock committed without an ack"); + resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK, "Committed surface before first ack"); return; } - if (m_committed) - m_events.commit.emit(); + if (committed) + events.commit.emit(); else { - m_surface->map(); - m_events.map.emit(); + pSurface->map(); + events.map.emit(); } - m_committed = true; + committed = true; }); - m_listeners.surfaceDestroy = m_surface->m_events.destroy.listen([this] { - LOGM(Log::WARN, "SessionLockSurface object remains but surface is being destroyed???"); - m_surface->unmap(); - m_listeners.surfaceCommit.reset(); - m_listeners.surfaceDestroy.reset(); - if (Desktop::focusState()->surface() == m_surface) - Desktop::focusState()->surface().reset(); + listeners.surfaceDestroy = pSurface->events.destroy.registerListener([this](std::any d) { + LOGM(WARN, "SessionLockSurface object remains but surface is being destroyed???"); + pSurface->unmap(); + listeners.surfaceCommit.reset(); + listeners.surfaceDestroy.reset(); + if (g_pCompositor->m_pLastFocus == pSurface) + g_pCompositor->m_pLastFocus.reset(); - m_surface.reset(); + pSurface.reset(); }); - if (m_monitor) { - PROTO::fractional->sendScale(surface_, m_monitor->m_scale); - - if (m_surface) - m_surface->enter(m_monitor.lock()); - } + PROTO::fractional->sendScale(surface_, pMonitor_->scale); sendConfigure(); - m_listeners.monitorMode = m_monitor->m_events.modeChanged.listen([this] { sendConfigure(); }); + listeners.monitorMode = pMonitor->events.modeChanged.registerListener([this](std::any data) { sendConfigure(); }); } CSessionLockSurface::~CSessionLockSurface() { - if (m_surface && m_surface->m_mapped) - m_surface->unmap(); - m_listeners.surfaceCommit.reset(); - m_listeners.surfaceDestroy.reset(); - m_events.destroy.emit(); // just in case. + if (pSurface && pSurface->mapped) + pSurface->unmap(); + listeners.surfaceCommit.reset(); + listeners.surfaceDestroy.reset(); + events.destroy.emit(); // just in case. } void CSessionLockSurface::sendConfigure() { - if (!m_monitor) { - LOGM(Log::ERR, "sendConfigure: monitor is gone"); - return; - } - - const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(m_resource->client())); - m_resource->sendConfigure(SERIAL, m_monitor->m_size.x, m_monitor->m_size.y); + const auto SERIAL = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(resource->client())); + resource->sendConfigure(SERIAL, pMonitor->vecSize.x, pMonitor->vecSize.y); } bool CSessionLockSurface::good() { - return m_resource->resource(); + return resource->resource(); } bool CSessionLockSurface::inert() { - return m_sessionLock.expired(); + return sessionLock.expired(); } PHLMONITOR CSessionLockSurface::monitor() { - return m_monitor.lock(); + return pMonitor.lock(); } SP CSessionLockSurface::surface() { - return m_surface.lock(); + return pSurface.lock(); } -CSessionLock::CSessionLock(SP resource_) : m_resource(resource_) { - if UNLIKELY (!m_resource->resource()) +CSessionLock::CSessionLock(SP resource_) : resource(resource_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setDestroy([this](CExtSessionLockV1* r) { PROTO::sessionLock->destroyResource(this); }); - m_resource->setOnDestroy([this](CExtSessionLockV1* r) { PROTO::sessionLock->destroyResource(this); }); + resource->setDestroy([this](CExtSessionLockV1* r) { PROTO::sessionLock->destroyResource(this); }); + resource->setOnDestroy([this](CExtSessionLockV1* r) { PROTO::sessionLock->destroyResource(this); }); - m_resource->setGetLockSurface([this](CExtSessionLockV1* r, uint32_t id, wl_resource* surf, wl_resource* output) { - if (m_inert) { - LOGM(Log::ERR, "Lock is trying to send getLockSurface after it's inert"); + resource->setGetLockSurface([this](CExtSessionLockV1* r, uint32_t id, wl_resource* surf, wl_resource* output) { + if (inert) { + LOGM(ERR, "Lock is trying to send getLockSurface after it's inert"); return; } PROTO::sessionLock->onGetLockSurface(r, id, surf, output); }); - m_resource->setUnlockAndDestroy([this](CExtSessionLockV1* r) { - if (m_inert) { + resource->setUnlockAndDestroy([this](CExtSessionLockV1* r) { + if (inert) { PROTO::sessionLock->destroyResource(this); return; } - PROTO::sessionLock->m_locked = false; + PROTO::sessionLock->locked = false; PROTO::lockNotify->onUnlocked(); - m_events.unlockAndDestroy.emit(); + events.unlockAndDestroy.emit(); - // if lock tools have hidden it and doesn't restore it, we won't receive a new cursor until the cursorshape protocol gives us one. - // so set it to left_ptr so the "desktop/wallpaper" doesn't end up missing a cursor until hover over a window sending us a shape. - g_pHyprRenderer->setCursorFromName("left_ptr"); - - m_inert = true; + inert = true; PROTO::sessionLock->destroyResource(this); }); } CSessionLock::~CSessionLock() { - m_events.destroyed.emit(); + events.destroyed.emit(); } void CSessionLock::sendLocked() { - m_resource->sendLocked(); + resource->sendLocked(); PROTO::lockNotify->onLocked(); } bool CSessionLock::good() { - return m_resource->resource(); + return resource->resource(); } void CSessionLock::sendDenied() { - m_inert = true; - m_resource->sendFinished(); + inert = true; + resource->sendFinished(); } CSessionLockProtocol::CSessionLockProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -163,7 +148,7 @@ CSessionLockProtocol::CSessionLockProtocol(const wl_interface* iface, const int& } void CSessionLockProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CExtSessionLockManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CExtSessionLockManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -171,61 +156,61 @@ void CSessionLockProtocol::bindManager(wl_client* client, void* data, uint32_t v } void CSessionLockProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CSessionLockProtocol::destroyResource(CSessionLock* lock) { - std::erase_if(m_locks, [&](const auto& other) { return other.get() == lock; }); + std::erase_if(m_vLocks, [&](const auto& other) { return other.get() == lock; }); } void CSessionLockProtocol::destroyResource(CSessionLockSurface* surf) { - std::erase_if(m_lockSurfaces, [&](const auto& other) { return other.get() == surf; }); + std::erase_if(m_vLockSurfaces, [&](const auto& other) { return other.get() == surf; }); } void CSessionLockProtocol::onLock(CExtSessionLockManagerV1* pMgr, uint32_t id) { - LOGM(Log::DEBUG, "New sessionLock with id {}", id); + LOGM(LOG, "New sessionLock with id {}", id); const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_locks.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id))); + const auto RESOURCE = m_vLocks.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id))); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_locks.pop_back(); + m_vLocks.pop_back(); return; } - m_events.newLock.emit(RESOURCE); + events.newLock.emit(RESOURCE); - m_locked = true; + locked = true; } void CSessionLockProtocol::onGetLockSurface(CExtSessionLockV1* lock, uint32_t id, wl_resource* surface, wl_resource* output) { - LOGM(Log::DEBUG, "New sessionLockSurface with id {}", id); + LOGM(LOG, "New sessionLockSurface with id {}", id); auto PSURFACE = CWLSurfaceResource::fromResource(surface); - auto PMONITOR = CWLOutputResource::fromResource(output)->m_monitor.lock(); + auto PMONITOR = CWLOutputResource::fromResource(output)->monitor.lock(); SP sessionLock; - for (auto const& l : m_locks) { - if (l->m_resource.get() == lock) { + for (auto const& l : m_vLocks) { + if (l->resource.get() == lock) { sessionLock = l; break; } } const auto RESOURCE = - m_lockSurfaces.emplace_back(makeShared(makeShared(lock->client(), lock->version(), id), PSURFACE, PMONITOR, sessionLock)); + m_vLockSurfaces.emplace_back(makeShared(makeShared(lock->client(), lock->version(), id), PSURFACE, PMONITOR, sessionLock)); if UNLIKELY (!RESOURCE->good()) { lock->noMemory(); - m_lockSurfaces.pop_back(); + m_vLockSurfaces.pop_back(); return; } - sessionLock->m_events.newLockSurface.emit(RESOURCE); + sessionLock->events.newLockSurface.emit(RESOURCE); } bool CSessionLockProtocol::isLocked() { - return m_locked; + return locked; } diff --git a/src/protocols/SessionLock.hpp b/src/protocols/SessionLock.hpp index 09b48b32..670e5e1d 100644 --- a/src/protocols/SessionLock.hpp +++ b/src/protocols/SessionLock.hpp @@ -21,19 +21,19 @@ class CSessionLockSurface { SP surface(); struct { - CSignalT<> map; - CSignalT<> destroy; - CSignalT<> commit; - } m_events; + CSignal map; + CSignal destroy; + CSignal commit; + } events; private: - SP m_resource; - WP m_sessionLock; - WP m_surface; - PHLMONITORREF m_monitor; + SP resource; + WP sessionLock; + WP pSurface; + PHLMONITORREF pMonitor; - bool m_ackdConfigure = false; - bool m_committed = false; + bool ackdConfigure = false; + bool committed = false; void sendConfigure(); @@ -41,7 +41,7 @@ class CSessionLockSurface { CHyprSignalListener monitorMode; CHyprSignalListener surfaceCommit; CHyprSignalListener surfaceDestroy; - } m_listeners; + } listeners; }; class CSessionLock { @@ -54,15 +54,15 @@ class CSessionLock { void sendDenied(); struct { - CSignalT> newLockSurface; - CSignalT<> unlockAndDestroy; - CSignalT<> destroyed; // fires regardless of whether there was a unlockAndDestroy or not. - } m_events; + CSignal newLockSurface; // SP + CSignal unlockAndDestroy; + CSignal destroyed; // fires regardless of whether there was a unlockAndDestroy or not. + } events; private: - SP m_resource; + SP resource; - bool m_inert = false; + bool inert = false; friend class CSessionLockProtocol; }; @@ -76,8 +76,8 @@ class CSessionLockProtocol : public IWaylandProtocol { bool isLocked(); struct { - CSignalT> newLock; - } m_events; + CSignal newLock; // SP + } events; private: void onManagerResourceDestroy(wl_resource* res); @@ -86,12 +86,12 @@ class CSessionLockProtocol : public IWaylandProtocol { void onLock(CExtSessionLockManagerV1* pMgr, uint32_t id); void onGetLockSurface(CExtSessionLockV1* lock, uint32_t id, wl_resource* surface, wl_resource* output); - bool m_locked = false; + bool locked = false; // - std::vector> m_managers; - std::vector> m_locks; - std::vector> m_lockSurfaces; + std::vector> m_vManagers; + std::vector> m_vLocks; + std::vector> m_vLockSurfaces; friend class CSessionLock; friend class CSessionLockSurface; diff --git a/src/protocols/ShortcutsInhibit.cpp b/src/protocols/ShortcutsInhibit.cpp index 6e6bf002..5786de26 100644 --- a/src/protocols/ShortcutsInhibit.cpp +++ b/src/protocols/ShortcutsInhibit.cpp @@ -1,27 +1,26 @@ #include "ShortcutsInhibit.hpp" #include #include "../Compositor.hpp" -#include "../desktop/state/FocusState.hpp" #include "core/Compositor.hpp" -CKeyboardShortcutsInhibitor::CKeyboardShortcutsInhibitor(SP resource_, SP surf) : m_resource(resource_), m_surface(surf) { - if UNLIKELY (!m_resource->resource()) +CKeyboardShortcutsInhibitor::CKeyboardShortcutsInhibitor(SP resource_, SP surf) : resource(resource_), pSurface(surf) { + if UNLIKELY (!resource->resource()) return; - m_resource->setDestroy([this](CZwpKeyboardShortcutsInhibitorV1* pMgr) { PROTO::shortcutsInhibit->destroyInhibitor(this); }); - m_resource->setOnDestroy([this](CZwpKeyboardShortcutsInhibitorV1* pMgr) { PROTO::shortcutsInhibit->destroyInhibitor(this); }); + resource->setDestroy([this](CZwpKeyboardShortcutsInhibitorV1* pMgr) { PROTO::shortcutsInhibit->destroyInhibitor(this); }); + resource->setOnDestroy([this](CZwpKeyboardShortcutsInhibitorV1* pMgr) { PROTO::shortcutsInhibit->destroyInhibitor(this); }); // I don't really care about following the spec here that much, // let's make the app believe it's always active - m_resource->sendActive(); + resource->sendActive(); } SP CKeyboardShortcutsInhibitor::surface() { - return m_surface.lock(); + return pSurface.lock(); } bool CKeyboardShortcutsInhibitor::good() { - return m_resource->resource(); + return resource->resource(); } CKeyboardShortcutsInhibitProtocol::CKeyboardShortcutsInhibitProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -29,7 +28,7 @@ CKeyboardShortcutsInhibitProtocol::CKeyboardShortcutsInhibitProtocol(const wl_in } void CKeyboardShortcutsInhibitProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpKeyboardShortcutsInhibitManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwpKeyboardShortcutsInhibitManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -38,18 +37,18 @@ void CKeyboardShortcutsInhibitProtocol::bindManager(wl_client* client, void* dat } void CKeyboardShortcutsInhibitProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CKeyboardShortcutsInhibitProtocol::destroyInhibitor(CKeyboardShortcutsInhibitor* inhibitor) { - std::erase_if(m_inhibitors, [&](const auto& other) { return other.get() == inhibitor; }); + std::erase_if(m_vInhibitors, [&](const auto& other) { return other.get() == inhibitor; }); } void CKeyboardShortcutsInhibitProtocol::onInhibit(CZwpKeyboardShortcutsInhibitManagerV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* seat) { SP surf = CWLSurfaceResource::fromResource(surface); const auto CLIENT = pMgr->client(); - for (auto const& in : m_inhibitors) { + for (auto const& in : m_vInhibitors) { if LIKELY (in->surface() != surf) continue; @@ -57,25 +56,26 @@ void CKeyboardShortcutsInhibitProtocol::onInhibit(CZwpKeyboardShortcutsInhibitMa return; } - const auto RESOURCE = m_inhibitors.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id), surf)).get(); + const auto RESOURCE = + m_vInhibitors.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id), surf)).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_inhibitors.pop_back(); - LOGM(Log::ERR, "Failed to create an inhibitor resource"); + m_vInhibitors.pop_back(); + LOGM(ERR, "Failed to create an inhibitor resource"); return; } } bool CKeyboardShortcutsInhibitProtocol::isInhibited() { - if (!Desktop::focusState()->surface()) + if (!g_pCompositor->m_pLastFocus) return false; - if (const auto PWINDOW = g_pCompositor->getWindowFromSurface(Desktop::focusState()->surface()); PWINDOW && PWINDOW->m_ruleApplicator->noShortcutsInhibit().valueOrDefault()) + if (const auto PWINDOW = g_pCompositor->getWindowFromSurface(g_pCompositor->m_pLastFocus.lock()); PWINDOW && PWINDOW->m_sWindowData.noShortcutsInhibit.valueOrDefault()) return false; - for (auto const& in : m_inhibitors) { - if (in->surface() != Desktop::focusState()->surface()) + for (auto const& in : m_vInhibitors) { + if (in->surface() != g_pCompositor->m_pLastFocus) continue; return true; diff --git a/src/protocols/ShortcutsInhibit.hpp b/src/protocols/ShortcutsInhibit.hpp index 33b40f5b..c093ac6e 100644 --- a/src/protocols/ShortcutsInhibit.hpp +++ b/src/protocols/ShortcutsInhibit.hpp @@ -16,8 +16,8 @@ class CKeyboardShortcutsInhibitor { bool good(); private: - SP m_resource; - WP m_surface; + SP resource; + WP pSurface; }; class CKeyboardShortcutsInhibitProtocol : public IWaylandProtocol { @@ -34,8 +34,8 @@ class CKeyboardShortcutsInhibitProtocol : public IWaylandProtocol { void onInhibit(CZwpKeyboardShortcutsInhibitManagerV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* seat); // - std::vector> m_managers; - std::vector> m_inhibitors; + std::vector> m_vManagers; + std::vector> m_vInhibitors; friend class CKeyboardShortcutsInhibitor; }; diff --git a/src/protocols/SinglePixel.cpp b/src/protocols/SinglePixel.cpp index c32379a3..c383dbb3 100644 --- a/src/protocols/SinglePixel.cpp +++ b/src/protocols/SinglePixel.cpp @@ -1,32 +1,32 @@ #include "SinglePixel.hpp" -#include "../desktop/view/LayerSurface.hpp" +#include "../desktop/LayerSurface.hpp" #include #include "render/Renderer.hpp" CSinglePixelBuffer::CSinglePixelBuffer(uint32_t id, wl_client* client, CHyprColor col_) { - LOGM(Log::DEBUG, "New single-pixel buffer with color 0x{:x}", col_.getAsHex()); + LOGM(LOG, "New single-pixel buffer with color 0x{:x}", col_.getAsHex()); - m_color = col_.getAsHex(); + color = col_.getAsHex(); g_pHyprRenderer->makeEGLCurrent(); - m_opaque = col_.a >= 1.F; + opaque = col_.a >= 1.F; - m_texture = g_pHyprRenderer->createTexture(DRM_FORMAT_ARGB8888, rc(&m_color), 4, Vector2D{1, 1}); + texture = makeShared(DRM_FORMAT_ARGB8888, (uint8_t*)&color, 4, Vector2D{1, 1}); - m_resource = CWLBufferResource::create(makeShared(client, 1, id)); + resource = CWLBufferResource::create(makeShared(client, 1, id)); - m_success = m_texture->ok(); + success = texture->m_iTexID; size = {1, 1}; - if (!m_success) - Log::logger->log(Log::ERR, "Failed creating a single pixel texture: null texture id"); + if (!success) + Debug::log(ERR, "Failed creating a single pixel texture: null texture id"); } CSinglePixelBuffer::~CSinglePixelBuffer() { - if (m_resource) - m_resource->sendRelease(); + if (resource) + resource->sendRelease(); } Aquamarine::eBufferCapability CSinglePixelBuffer::caps() { @@ -50,7 +50,7 @@ Aquamarine::SDMABUFAttrs CSinglePixelBuffer::dmabuf() { } std::tuple CSinglePixelBuffer::beginDataPtr(uint32_t flags) { - return {rc(&m_color), DRM_FORMAT_ARGB8888, 4}; + return {(uint8_t*)&color, DRM_FORMAT_ARGB8888, 4}; } void CSinglePixelBuffer::endDataPtr() { @@ -58,49 +58,49 @@ void CSinglePixelBuffer::endDataPtr() { } bool CSinglePixelBuffer::good() { - return m_resource->good(); + return resource->good(); } CSinglePixelBufferResource::CSinglePixelBufferResource(uint32_t id, wl_client* client, CHyprColor color) { - m_buffer = makeShared(id, client, color); + buffer = makeShared(id, client, color); - if UNLIKELY (!m_buffer->good()) + if UNLIKELY (!buffer->good()) return; - m_buffer->m_resource->m_buffer = m_buffer; + buffer->resource->buffer = buffer; - m_listeners.bufferResourceDestroy = m_buffer->events.destroy.listen([this] { - m_listeners.bufferResourceDestroy.reset(); + listeners.bufferResourceDestroy = buffer->events.destroy.registerListener([this](std::any d) { + listeners.bufferResourceDestroy.reset(); PROTO::singlePixel->destroyResource(this); }); } bool CSinglePixelBufferResource::good() { - return m_buffer->good(); + return buffer->good(); } -CSinglePixelBufferManagerResource::CSinglePixelBufferManagerResource(UP&& resource_) : m_resource(std::move(resource_)) { +CSinglePixelBufferManagerResource::CSinglePixelBufferManagerResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CWpSinglePixelBufferManagerV1* r) { PROTO::singlePixel->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpSinglePixelBufferManagerV1* r) { PROTO::singlePixel->destroyResource(this); }); + resource->setDestroy([this](CWpSinglePixelBufferManagerV1* r) { PROTO::singlePixel->destroyResource(this); }); + resource->setOnDestroy([this](CWpSinglePixelBufferManagerV1* r) { PROTO::singlePixel->destroyResource(this); }); - m_resource->setCreateU32RgbaBuffer([this](CWpSinglePixelBufferManagerV1* res, uint32_t id, uint32_t r, uint32_t g, uint32_t b, uint32_t a) { - CHyprColor color{r / sc(std::numeric_limits::max()), g / sc(std::numeric_limits::max()), - b / sc(std::numeric_limits::max()), a / sc(std::numeric_limits::max())}; - const auto& RESOURCE = PROTO::singlePixel->m_buffers.emplace_back(makeUnique(id, m_resource->client(), color)); + resource->setCreateU32RgbaBuffer([this](CWpSinglePixelBufferManagerV1* res, uint32_t id, uint32_t r, uint32_t g, uint32_t b, uint32_t a) { + CHyprColor color{r / (float)std::numeric_limits::max(), g / (float)std::numeric_limits::max(), b / (float)std::numeric_limits::max(), + a / (float)std::numeric_limits::max()}; + const auto RESOURCE = PROTO::singlePixel->m_vBuffers.emplace_back(makeShared(id, resource->client(), color)); if UNLIKELY (!RESOURCE->good()) { res->noMemory(); - PROTO::singlePixel->m_buffers.pop_back(); + PROTO::singlePixel->m_vBuffers.pop_back(); return; } }); } bool CSinglePixelBufferManagerResource::good() { - return m_resource->resource(); + return resource->resource(); } CSinglePixelProtocol::CSinglePixelProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -108,19 +108,19 @@ CSinglePixelProtocol::CSinglePixelProtocol(const wl_interface* iface, const int& } void CSinglePixelProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto& RESOURCE = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CSinglePixelProtocol::destroyResource(CSinglePixelBufferManagerResource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == res; }); } void CSinglePixelProtocol::destroyResource(CSinglePixelBufferResource* surf) { - std::erase_if(m_buffers, [&](const auto& other) { return other.get() == surf; }); + std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == surf; }); } diff --git a/src/protocols/SinglePixel.hpp b/src/protocols/SinglePixel.hpp index f8bbfba9..478f3e35 100644 --- a/src/protocols/SinglePixel.hpp +++ b/src/protocols/SinglePixel.hpp @@ -20,10 +20,14 @@ class CSinglePixelBuffer : public IHLBuffer { virtual void endDataPtr(); // bool good(); - bool m_success = false; + bool success = false; private: - uint32_t m_color = 0x00000000; + uint32_t color = 0x00000000; + + struct { + CHyprSignalListener resourceDestroy; + } listeners; }; class CSinglePixelBufferResource { @@ -34,21 +38,21 @@ class CSinglePixelBufferResource { bool good(); private: - SP m_buffer; + SP buffer; struct { CHyprSignalListener bufferResourceDestroy; - } m_listeners; + } listeners; }; class CSinglePixelBufferManagerResource { public: - CSinglePixelBufferManagerResource(UP&& resource_); + CSinglePixelBufferManagerResource(SP resource_); bool good(); private: - UP m_resource; + SP resource; }; class CSinglePixelProtocol : public IWaylandProtocol { @@ -62,8 +66,8 @@ class CSinglePixelProtocol : public IWaylandProtocol { void destroyResource(CSinglePixelBufferResource* resource); // - std::vector> m_managers; - std::vector> m_buffers; + std::vector> m_vManagers; + std::vector> m_vBuffers; friend class CSinglePixelBufferManagerResource; friend class CSinglePixelBufferResource; diff --git a/src/protocols/Tablet.cpp b/src/protocols/Tablet.cpp index 00f811a4..de2ed71b 100644 --- a/src/protocols/Tablet.cpp +++ b/src/protocols/Tablet.cpp @@ -3,160 +3,160 @@ #include "../Compositor.hpp" #include "../managers/SeatManager.hpp" #include "../managers/input/InputManager.hpp" -#include "../helpers/time/Time.hpp" #include "core/Seat.hpp" #include "core/Compositor.hpp" #include #include -CTabletPadStripV2Resource::CTabletPadStripV2Resource(SP resource_, uint32_t id_) : m_id(id_), m_resource(resource_) { +CTabletPadStripV2Resource::CTabletPadStripV2Resource(SP resource_, uint32_t id_) : id(id_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpTabletPadStripV2* r) { PROTO::tablet->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpTabletPadStripV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setDestroy([this](CZwpTabletPadStripV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setOnDestroy([this](CZwpTabletPadStripV2* r) { PROTO::tablet->destroyResource(this); }); } bool CTabletPadStripV2Resource::good() { - return m_resource->resource(); + return resource->resource(); } -CTabletPadRingV2Resource::CTabletPadRingV2Resource(SP resource_, uint32_t id_) : m_id(id_), m_resource(resource_) { +CTabletPadRingV2Resource::CTabletPadRingV2Resource(SP resource_, uint32_t id_) : id(id_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpTabletPadRingV2* r) { PROTO::tablet->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpTabletPadRingV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setDestroy([this](CZwpTabletPadRingV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setOnDestroy([this](CZwpTabletPadRingV2* r) { PROTO::tablet->destroyResource(this); }); } bool CTabletPadRingV2Resource::good() { - return m_resource->resource(); + return resource->resource(); } -CTabletPadGroupV2Resource::CTabletPadGroupV2Resource(SP resource_, size_t idx_) : m_idx(idx_), m_resource(resource_) { +CTabletPadGroupV2Resource::CTabletPadGroupV2Resource(SP resource_, size_t idx_) : idx(idx_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpTabletPadGroupV2* r) { PROTO::tablet->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpTabletPadGroupV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setDestroy([this](CZwpTabletPadGroupV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setOnDestroy([this](CZwpTabletPadGroupV2* r) { PROTO::tablet->destroyResource(this); }); } bool CTabletPadGroupV2Resource::good() { - return m_resource->resource(); + return resource->resource(); } void CTabletPadGroupV2Resource::sendData(SP pad, SP group) { - m_resource->sendModes(group->modes); + resource->sendModes(group->modes); wl_array buttonArr; wl_array_init(&buttonArr); wl_array_add(&buttonArr, group->buttons.size() * sizeof(int)); memcpy(buttonArr.data, group->buttons.data(), group->buttons.size() * sizeof(int)); - m_resource->sendButtons(&buttonArr); + resource->sendButtons(&buttonArr); wl_array_release(&buttonArr); for (size_t i = 0; i < group->strips.size(); ++i) { const auto RESOURCE = - PROTO::tablet->m_strips.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), 0), i)); + PROTO::tablet->m_vStrips.emplace_back(makeShared(makeShared(resource->client(), resource->version(), 0), i)); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::tablet->m_strips.pop_back(); + resource->noMemory(); + PROTO::tablet->m_vStrips.pop_back(); return; } - m_resource->sendStrip(RESOURCE->m_resource.get()); + resource->sendStrip(RESOURCE->resource.get()); } for (size_t i = 0; i < group->rings.size(); ++i) { const auto RESOURCE = - PROTO::tablet->m_rings.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), 0), i)); + PROTO::tablet->m_vRings.emplace_back(makeShared(makeShared(resource->client(), resource->version(), 0), i)); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::tablet->m_rings.pop_back(); + resource->noMemory(); + PROTO::tablet->m_vRings.pop_back(); return; } - m_resource->sendRing(RESOURCE->m_resource.get()); + resource->sendRing(RESOURCE->resource.get()); } - m_resource->sendDone(); + resource->sendDone(); } -CTabletPadV2Resource::CTabletPadV2Resource(SP resource_, SP pad_, SP seat_) : m_pad(pad_), m_seat(seat_), m_resource(resource_) { +CTabletPadV2Resource::CTabletPadV2Resource(SP resource_, SP pad_, SP seat_) : pad(pad_), seat(seat_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpTabletPadV2* r) { PROTO::tablet->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpTabletPadV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setDestroy([this](CZwpTabletPadV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setOnDestroy([this](CZwpTabletPadV2* r) { PROTO::tablet->destroyResource(this); }); } bool CTabletPadV2Resource::good() { - return m_resource->resource(); + return resource->resource(); } void CTabletPadV2Resource::sendData() { - for (auto const& p : m_pad->aq()->paths) { - m_resource->sendPath(p.c_str()); + // this is dodgy as fuck. I hate wl_array. it's expanded wl_array_for_each because C++ would complain about the implicit casts + for (auto const& p : pad->aq()->paths) { + resource->sendPath(p.c_str()); } - m_resource->sendButtons(m_pad->aq()->buttons); + resource->sendButtons(pad->aq()->buttons); - for (size_t i = 0; i < m_pad->aq()->groups.size(); ++i) { - createGroup(m_pad->aq()->groups.at(i), i); + for (size_t i = 0; i < pad->aq()->groups.size(); ++i) { + createGroup(pad->aq()->groups.at(i), i); } - m_resource->sendDone(); + resource->sendDone(); } void CTabletPadV2Resource::createGroup(SP group, size_t idx) { const auto RESOURCE = - PROTO::tablet->m_groups.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), 0), idx)); + PROTO::tablet->m_vGroups.emplace_back(makeShared(makeShared(resource->client(), resource->version(), 0), idx)); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::tablet->m_groups.pop_back(); + resource->noMemory(); + PROTO::tablet->m_vGroups.pop_back(); return; } - m_resource->sendGroup(RESOURCE->m_resource.get()); + resource->sendGroup(RESOURCE->resource.get()); - RESOURCE->sendData(m_pad.lock(), group); + RESOURCE->sendData(pad.lock(), group); } -CTabletV2Resource::CTabletV2Resource(SP resource_, SP tablet_, SP seat_) : m_tablet(tablet_), m_seat(seat_), m_resource(resource_) { +CTabletV2Resource::CTabletV2Resource(SP resource_, SP tablet_, SP seat_) : tablet(tablet_), seat(seat_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpTabletV2* r) { PROTO::tablet->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpTabletV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setDestroy([this](CZwpTabletV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setOnDestroy([this](CZwpTabletV2* r) { PROTO::tablet->destroyResource(this); }); } bool CTabletV2Resource::good() { - return m_resource->resource(); + return resource->resource(); } void CTabletV2Resource::sendData() { - m_resource->sendName(m_tablet->m_deviceName.c_str()); - m_resource->sendId(m_tablet->aq()->usbVendorID, m_tablet->aq()->usbProductID); + resource->sendName(tablet->deviceName.c_str()); + resource->sendId(tablet->aq()->usbVendorID, tablet->aq()->usbProductID); - for (auto const& p : m_tablet->aq()->paths) { - m_resource->sendPath(p.c_str()); + for (auto const& p : tablet->aq()->paths) { + resource->sendPath(p.c_str()); } - m_resource->sendDone(); + resource->sendDone(); } -CTabletToolV2Resource::CTabletToolV2Resource(SP resource_, SP tool_, SP seat_) : m_tool(tool_), m_seat(seat_), m_resource(resource_) { +CTabletToolV2Resource::CTabletToolV2Resource(SP resource_, SP tool_, SP seat_) : tool(tool_), seat(seat_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setOnDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); }); - m_resource->setSetCursor([](CZwpTabletToolV2* r, uint32_t serial, wl_resource* surf, int32_t hot_x, int32_t hot_y) { - if (!g_pSeatManager->m_state.pointerFocusResource || g_pSeatManager->m_state.pointerFocusResource->client() != r->client()) + resource->setSetCursor([](CZwpTabletToolV2* r, uint32_t serial, wl_resource* surf, int32_t hot_x, int32_t hot_y) { + if (!g_pSeatManager->state.pointerFocusResource || g_pSeatManager->state.pointerFocusResource->client() != r->client()) return; g_pInputManager->processMouseRequest(CSeatManager::SSetCursorEvent{surf ? CWLSurfaceResource::fromResource(surf) : nullptr, {hot_x, hot_y}}); @@ -164,12 +164,12 @@ CTabletToolV2Resource::CTabletToolV2Resource(SP resource_, SP< } CTabletToolV2Resource::~CTabletToolV2Resource() { - if (m_frameSource) - wl_event_source_remove(m_frameSource); + if (frameSource) + wl_event_source_remove(frameSource); } bool CTabletToolV2Resource::good() { - return m_resource->resource(); + return resource->resource(); } void CTabletToolV2Resource::sendData() { @@ -187,120 +187,122 @@ void CTabletToolV2Resource::sendData() { UNREACHABLE(); }; - m_resource->sendType(AQ_TYPE_TO_PROTO(m_tool->aq()->type)); - m_resource->sendHardwareSerial(m_tool->aq()->serial >> 32, m_tool->aq()->serial & 0xFFFFFFFF); - m_resource->sendHardwareIdWacom(m_tool->aq()->id >> 32, m_tool->aq()->id & 0xFFFFFFFF); - if (m_tool->m_toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_DISTANCE) - m_resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE); - if (m_tool->m_toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_PRESSURE) - m_resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE); - if (m_tool->m_toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_ROTATION) - m_resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION); - if (m_tool->m_toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_SLIDER) - m_resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER); - if (m_tool->m_toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_TILT) - m_resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_TILT); - if (m_tool->m_toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_WHEEL) - m_resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL); - m_resource->sendDone(); + resource->sendType(AQ_TYPE_TO_PROTO(tool->aq()->type)); + resource->sendHardwareSerial(tool->aq()->serial >> 32, tool->aq()->serial & 0xFFFFFFFF); + resource->sendHardwareIdWacom(tool->aq()->id >> 32, tool->aq()->id & 0xFFFFFFFF); + if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_DISTANCE) + resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE); + if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_PRESSURE) + resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE); + if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_ROTATION) + resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION); + if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_SLIDER) + resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER); + if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_TILT) + resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_TILT); + if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_WHEEL) + resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL); + resource->sendDone(); } void CTabletToolV2Resource::queueFrame() { - if (m_frameSource) + if (frameSource) return; - m_frameSource = wl_event_loop_add_idle(g_pCompositor->m_wlEventLoop, [](void* data) { sc(data)->sendFrame(false); }, this); + frameSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, [](void* data) { ((CTabletToolV2Resource*)data)->sendFrame(false); }, this); } void CTabletToolV2Resource::sendFrame(bool removeSource) { - if (m_frameSource) { + if (frameSource) { if (removeSource) - wl_event_source_remove(m_frameSource); - m_frameSource = nullptr; + wl_event_source_remove(frameSource); + frameSource = nullptr; } - if (!m_current) + if (!current) return; - m_resource->sendFrame(Time::millis(Time::steadyNow())); + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + resource->sendFrame(now.tv_sec * 1000 + now.tv_nsec / 1000000); } -CTabletSeat::CTabletSeat(SP resource_) : m_resource(resource_) { +CTabletSeat::CTabletSeat(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpTabletSeatV2* r) { PROTO::tablet->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwpTabletSeatV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setDestroy([this](CZwpTabletSeatV2* r) { PROTO::tablet->destroyResource(this); }); + resource->setOnDestroy([this](CZwpTabletSeatV2* r) { PROTO::tablet->destroyResource(this); }); } bool CTabletSeat::good() { - return m_resource->resource(); + return resource->resource(); } void CTabletSeat::sendTool(SP tool) { const auto RESOURCE = - PROTO::tablet->m_tools.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), 0), tool, m_self.lock())); + PROTO::tablet->m_vTools.emplace_back(makeShared(makeShared(resource->client(), resource->version(), 0), tool, self.lock())); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::tablet->m_tools.pop_back(); + resource->noMemory(); + PROTO::tablet->m_vTools.pop_back(); return; } - m_resource->sendToolAdded(RESOURCE->m_resource.get()); + resource->sendToolAdded(RESOURCE->resource.get()); RESOURCE->sendData(); - m_tools.emplace_back(RESOURCE); + tools.emplace_back(RESOURCE); } void CTabletSeat::sendPad(SP pad) { const auto RESOURCE = - PROTO::tablet->m_pads.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), 0), pad, m_self.lock())); + PROTO::tablet->m_vPads.emplace_back(makeShared(makeShared(resource->client(), resource->version(), 0), pad, self.lock())); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::tablet->m_pads.pop_back(); + resource->noMemory(); + PROTO::tablet->m_vPads.pop_back(); return; } - m_resource->sendPadAdded(RESOURCE->m_resource.get()); + resource->sendPadAdded(RESOURCE->resource.get()); RESOURCE->sendData(); - m_pads.emplace_back(RESOURCE); + pads.emplace_back(RESOURCE); } void CTabletSeat::sendTablet(SP tablet) { const auto RESOURCE = - PROTO::tablet->m_tablets.emplace_back(makeShared(makeShared(m_resource->client(), m_resource->version(), 0), tablet, m_self.lock())); + PROTO::tablet->m_vTablets.emplace_back(makeShared(makeShared(resource->client(), resource->version(), 0), tablet, self.lock())); if UNLIKELY (!RESOURCE->good()) { - m_resource->noMemory(); - PROTO::tablet->m_tablets.pop_back(); + resource->noMemory(); + PROTO::tablet->m_vTablets.pop_back(); return; } - m_resource->sendTabletAdded(RESOURCE->m_resource.get()); + resource->sendTabletAdded(RESOURCE->resource.get()); RESOURCE->sendData(); - m_tablets.emplace_back(RESOURCE); + tablets.emplace_back(RESOURCE); } void CTabletSeat::sendData() { - for (auto const& tw : PROTO::tablet->m_tabletDevices) { + for (auto const& tw : PROTO::tablet->tablets) { if (tw.expired()) continue; sendTablet(tw.lock()); } - for (auto const& tw : PROTO::tablet->m_toolDevices) { + for (auto const& tw : PROTO::tablet->tools) { if (tw.expired()) continue; sendTool(tw.lock()); } - for (auto const& tw : PROTO::tablet->m_padDevices) { + for (auto const& tw : PROTO::tablet->pads) { if (tw.expired()) continue; @@ -313,7 +315,7 @@ CTabletV2Protocol::CTabletV2Protocol(const wl_interface* iface, const int& ver, } void CTabletV2Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpTabletManagerV2* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwpTabletManagerV2* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -321,218 +323,218 @@ void CTabletV2Protocol::bindManager(wl_client* client, void* data, uint32_t ver, } void CTabletV2Protocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CTabletV2Protocol::destroyResource(CTabletSeat* resource) { - std::erase_if(m_seats, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSeats, [&](const auto& other) { return other.get() == resource; }); } void CTabletV2Protocol::destroyResource(CTabletToolV2Resource* resource) { - std::erase_if(m_tools, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vTools, [&](const auto& other) { return other.get() == resource; }); } void CTabletV2Protocol::destroyResource(CTabletV2Resource* resource) { - std::erase_if(m_tablets, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vTablets, [&](const auto& other) { return other.get() == resource; }); } void CTabletV2Protocol::destroyResource(CTabletPadV2Resource* resource) { - std::erase_if(m_pads, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vPads, [&](const auto& other) { return other.get() == resource; }); } void CTabletV2Protocol::destroyResource(CTabletPadGroupV2Resource* resource) { - std::erase_if(m_groups, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vGroups, [&](const auto& other) { return other.get() == resource; }); } void CTabletV2Protocol::destroyResource(CTabletPadRingV2Resource* resource) { - std::erase_if(m_rings, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vRings, [&](const auto& other) { return other.get() == resource; }); } void CTabletV2Protocol::destroyResource(CTabletPadStripV2Resource* resource) { - std::erase_if(m_strips, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vStrips, [&](const auto& other) { return other.get() == resource; }); } void CTabletV2Protocol::onGetSeat(CZwpTabletManagerV2* pMgr, uint32_t id, wl_resource* seat) { - const auto RESOURCE = m_seats.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id))); + const auto RESOURCE = m_vSeats.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id))); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_seats.pop_back(); + m_vSeats.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; RESOURCE->sendData(); } void CTabletV2Protocol::registerDevice(SP tablet) { - for (auto const& s : m_seats) { + for (auto const& s : m_vSeats) { s->sendTablet(tablet); } - m_tabletDevices.emplace_back(tablet); + tablets.emplace_back(tablet); } void CTabletV2Protocol::registerDevice(SP tool) { - for (auto const& s : m_seats) { + for (auto const& s : m_vSeats) { s->sendTool(tool); } - m_toolDevices.emplace_back(tool); + tools.emplace_back(tool); } void CTabletV2Protocol::registerDevice(SP pad) { - for (auto const& s : m_seats) { + for (auto const& s : m_vSeats) { s->sendPad(pad); } - m_padDevices.emplace_back(pad); + pads.emplace_back(pad); } void CTabletV2Protocol::unregisterDevice(SP tablet) { - for (auto const& t : m_tablets) { - if (t->m_tablet == tablet) { - t->m_resource->sendRemoved(); - t->m_inert = true; + for (auto const& t : m_vTablets) { + if (t->tablet == tablet) { + t->resource->sendRemoved(); + t->inert = true; } } - std::erase_if(m_tabletDevices, [tablet](const auto& e) { return e.expired() || e == tablet; }); + std::erase_if(tablets, [tablet](const auto& e) { return e.expired() || e == tablet; }); } void CTabletV2Protocol::unregisterDevice(SP tool) { - for (auto const& t : m_tools) { - if (t->m_tool == tool) { - t->m_resource->sendRemoved(); - t->m_inert = true; + for (auto const& t : m_vTools) { + if (t->tool == tool) { + t->resource->sendRemoved(); + t->inert = true; } } - std::erase_if(m_toolDevices, [tool](const auto& e) { return e.expired() || e == tool; }); + std::erase_if(tools, [tool](const auto& e) { return e.expired() || e == tool; }); } void CTabletV2Protocol::unregisterDevice(SP pad) { - for (auto const& t : m_pads) { - if (t->m_pad == pad) { - t->m_resource->sendRemoved(); - t->m_inert = true; + for (auto const& t : m_vPads) { + if (t->pad == pad) { + t->resource->sendRemoved(); + t->inert = true; } } - std::erase_if(m_padDevices, [pad](const auto& e) { return e.expired() || e == pad; }); + std::erase_if(pads, [pad](const auto& e) { return e.expired() || e == pad; }); } void CTabletV2Protocol::recheckRegisteredDevices() { - std::erase_if(m_tabletDevices, [](const auto& e) { return e.expired(); }); - std::erase_if(m_toolDevices, [](const auto& e) { return e.expired(); }); - std::erase_if(m_padDevices, [](const auto& e) { return e.expired(); }); + std::erase_if(tablets, [](const auto& e) { return e.expired(); }); + std::erase_if(tools, [](const auto& e) { return e.expired(); }); + std::erase_if(pads, [](const auto& e) { return e.expired(); }); // now we need to send removed events - for (auto const& t : m_tablets) { - if (!t->m_tablet.expired() || t->m_inert) + for (auto const& t : m_vTablets) { + if (!t->tablet.expired() || t->inert) continue; - t->m_resource->sendRemoved(); - t->m_inert = true; + t->resource->sendRemoved(); + t->inert = true; } - for (auto const& t : m_tools) { - if (!t->m_tool.expired() || t->m_inert) + for (auto const& t : m_vTools) { + if (!t->tool.expired() || t->inert) continue; - if (t->m_current) { - t->m_resource->sendProximityOut(); + if (t->current) { + t->resource->sendProximityOut(); t->sendFrame(); - t->m_lastSurf.reset(); + t->lastSurf.reset(); } - t->m_resource->sendRemoved(); - t->m_inert = true; + t->resource->sendRemoved(); + t->inert = true; } - for (auto const& t : m_pads) { - if (!t->m_pad.expired() || t->m_inert) + for (auto const& t : m_vPads) { + if (!t->pad.expired() || t->inert) continue; - t->m_resource->sendRemoved(); - t->m_inert = true; + t->resource->sendRemoved(); + t->inert = true; } } void CTabletV2Protocol::pressure(SP tool, double value) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_resource->sendPressure(std::clamp(value * 65535, 0.0, 65535.0)); + t->resource->sendPressure(std::clamp(value * 65535, 0.0, 65535.0)); t->queueFrame(); } } void CTabletV2Protocol::distance(SP tool, double value) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_resource->sendDistance(std::clamp(value * 65535, 0.0, 65535.0)); + t->resource->sendDistance(std::clamp(value * 65535, 0.0, 65535.0)); t->queueFrame(); } } void CTabletV2Protocol::rotation(SP tool, double value) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_resource->sendRotation(wl_fixed_from_double(value)); + t->resource->sendRotation(wl_fixed_from_double(value)); t->queueFrame(); } } void CTabletV2Protocol::slider(SP tool, double value) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_resource->sendSlider(std::clamp(value * 65535, -65535.0, 65535.0)); + t->resource->sendSlider(std::clamp(value * 65535, -65535.0, 65535.0)); t->queueFrame(); } } void CTabletV2Protocol::wheel(SP tool, double value) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_resource->sendWheel(wl_fixed_from_double(value), 0); + t->resource->sendWheel(wl_fixed_from_double(value), 0); t->queueFrame(); } } void CTabletV2Protocol::tilt(SP tool, const Vector2D& value) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_resource->sendTilt(wl_fixed_from_double(value.x), wl_fixed_from_double(value.y)); + t->resource->sendTilt(wl_fixed_from_double(value.x), wl_fixed_from_double(value.y)); t->queueFrame(); } } void CTabletV2Protocol::up(SP tool) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_resource->sendUp(); + t->resource->sendUp(); t->queueFrame(); } } void CTabletV2Protocol::down(SP tool) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->m_resource->client())); - t->m_resource->sendDown(serial); + auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client())); + t->resource->sendDown(serial); t->queueFrame(); } } @@ -544,25 +546,25 @@ void CTabletV2Protocol::proximityIn(SP tool, SP tablet, SP SP toolResource; SP tabletResource; - for (auto const& t : m_tools) { - if (t->m_tool != tool || t->m_resource->client() != CLIENT) + for (auto const& t : m_vTools) { + if (t->tool != tool || t->resource->client() != CLIENT) continue; - if (t->m_seat.expired()) { - LOGM(Log::ERR, "proximityIn on a tool without a seat parent"); + if (t->seat.expired()) { + LOGM(ERR, "proximityIn on a tool without a seat parent"); return; } - if (t->m_lastSurf == surf) + if (t->lastSurf == surf) return; toolResource = t; - for (auto const& tab : m_tablets) { - if (tab->m_tablet != tablet) + for (auto const& tab : m_vTablets) { + if (tab->tablet != tablet) continue; - if (tab->m_seat != t->m_seat || !tab->m_seat) + if (tab->seat != t->seat || !tab->seat) continue; tabletResource = tab; @@ -571,78 +573,78 @@ void CTabletV2Protocol::proximityIn(SP tool, SP tablet, SP } if (!tabletResource || !toolResource) { - LOGM(Log::ERR, "proximityIn on a tool and tablet without valid resource(s)??"); + LOGM(ERR, "proximityIn on a tool and tablet without valid resource(s)??"); return; } - toolResource->m_current = true; - toolResource->m_lastSurf = surf; + toolResource->current = true; + toolResource->lastSurf = surf; - auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(toolResource->m_resource->client())); - toolResource->m_resource->sendProximityIn(serial, tabletResource->m_resource.get(), surf->getResource()->resource()); + auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(toolResource->resource->client())); + toolResource->resource->sendProximityIn(serial, tabletResource->resource.get(), surf->getResource()->resource()); toolResource->queueFrame(); - LOGM(Log::ERR, "proximityIn: found no resource to send enter"); + LOGM(ERR, "proximityIn: found no resource to send enter"); } void CTabletV2Protocol::proximityOut(SP tool) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_lastSurf.reset(); - t->m_resource->sendProximityOut(); + t->lastSurf.reset(); + t->resource->sendProximityOut(); t->sendFrame(); - t->m_current = false; + t->current = false; } } void CTabletV2Protocol::buttonTool(SP tool, uint32_t button, uint32_t state) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->m_resource->client())); - t->m_resource->sendButton(serial, button, sc(state)); + auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client())); + t->resource->sendButton(serial, button, (zwpTabletToolV2ButtonState)state); t->queueFrame(); } } void CTabletV2Protocol::motion(SP tool, const Vector2D& value) { - for (auto const& t : m_tools) { - if (t->m_tool != tool || !t->m_current) + for (auto const& t : m_vTools) { + if (t->tool != tool || !t->current) continue; - t->m_resource->sendMotion(wl_fixed_from_double(value.x), wl_fixed_from_double(value.y)); + t->resource->sendMotion(wl_fixed_from_double(value.x), wl_fixed_from_double(value.y)); t->queueFrame(); } } void CTabletV2Protocol::mode(SP pad, uint32_t group, uint32_t mode, uint32_t timeMs) { - for (auto const& t : m_pads) { - if (t->m_pad != pad) + for (auto const& t : m_vPads) { + if (t->pad != pad) continue; - if (t->m_groups.size() <= group) { - LOGM(Log::ERR, "BUG THIS: group >= t->groups.size()"); + if (t->groups.size() <= group) { + LOGM(ERR, "BUG THIS: group >= t->groups.size()"); return; } - auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->m_resource->client())); - t->m_groups.at(group)->m_resource->sendModeSwitch(timeMs, serial, mode); + auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(t->resource->client())); + t->groups.at(group)->resource->sendModeSwitch(timeMs, serial, mode); } } void CTabletV2Protocol::buttonPad(SP pad, uint32_t button, uint32_t timeMs, uint32_t state) { - for (auto const& t : m_pads) { - if (t->m_pad != pad) + for (auto const& t : m_vPads) { + if (t->pad != pad) continue; - t->m_resource->sendButton(timeMs, button, zwpTabletToolV2ButtonState{state}); + t->resource->sendButton(timeMs, button, zwpTabletToolV2ButtonState{state}); } } void CTabletV2Protocol::strip(SP pad, uint32_t strip, double position, bool finger, uint32_t timeMs) { - LOGM(Log::ERR, "FIXME: STUB: CTabletV2Protocol::strip not implemented"); + LOGM(ERR, "FIXME: STUB: CTabletV2Protocol::strip not implemented"); } void CTabletV2Protocol::ring(SP pad, uint32_t ring, double position, bool finger, uint32_t timeMs) { - LOGM(Log::ERR, "FIXME: STUB: CTabletV2Protocol::ring not implemented"); + LOGM(ERR, "FIXME: STUB: CTabletV2Protocol::ring not implemented"); } diff --git a/src/protocols/Tablet.hpp b/src/protocols/Tablet.hpp index 25e85e18..264c7633 100644 --- a/src/protocols/Tablet.hpp +++ b/src/protocols/Tablet.hpp @@ -20,10 +20,10 @@ class CTabletPadStripV2Resource { bool good(); - uint32_t m_id = 0; + uint32_t id = 0; private: - SP m_resource; + SP resource; friend class CTabletSeat; friend class CTabletPadGroupV2Resource; @@ -36,10 +36,10 @@ class CTabletPadRingV2Resource { bool good(); - uint32_t m_id = 0; + uint32_t id = 0; private: - SP m_resource; + SP resource; friend class CTabletSeat; friend class CTabletPadGroupV2Resource; @@ -50,13 +50,16 @@ class CTabletPadGroupV2Resource { public: CTabletPadGroupV2Resource(SP resource_, size_t idx); - bool good(); - void sendData(SP pad, SP group); + bool good(); + void sendData(SP pad, SP group); - size_t m_idx = 0; + std::vector> rings; + std::vector> strips; + + size_t idx = 0; private: - SP m_resource; + SP resource; friend class CTabletSeat; friend class CTabletPadV2Resource; @@ -70,15 +73,15 @@ class CTabletPadV2Resource { bool good(); void sendData(); - std::vector> m_groups; + std::vector> groups; - WP m_pad; - WP m_seat; + WP pad; + WP seat; - bool m_inert = false; // removed was sent + bool inert = false; // removed was sent private: - SP m_resource; + SP resource; void createGroup(SP group, size_t idx); @@ -93,13 +96,13 @@ class CTabletV2Resource { bool good(); void sendData(); - WP m_tablet; - WP m_seat; + WP tablet; + WP seat; - bool m_inert = false; // removed was sent + bool inert = false; // removed was sent private: - SP m_resource; + SP resource; friend class CTabletSeat; friend class CTabletV2Protocol; @@ -115,17 +118,17 @@ class CTabletToolV2Resource { void queueFrame(); void sendFrame(bool removeSource = true); - bool m_current = false; - WP m_lastSurf; + bool current = false; + WP lastSurf; - WP m_tool; - WP m_seat; - wl_event_source* m_frameSource = nullptr; + WP tool; + WP seat; + wl_event_source* frameSource = nullptr; - bool m_inert = false; // removed was sent + bool inert = false; // removed was sent private: - SP m_resource; + SP resource; friend class CTabletSeat; friend class CTabletV2Protocol; @@ -138,17 +141,17 @@ class CTabletSeat { bool good(); void sendData(); - std::vector> m_tools; - std::vector> m_pads; - std::vector> m_tablets; + std::vector> tools; + std::vector> pads; + std::vector> tablets; void sendTool(SP tool); void sendPad(SP pad); void sendTablet(SP tablet); private: - SP m_resource; - WP m_self; + SP resource; + WP self; friend class CTabletV2Protocol; }; @@ -201,19 +204,19 @@ class CTabletV2Protocol : public IWaylandProtocol { void onGetSeat(CZwpTabletManagerV2* pMgr, uint32_t id, wl_resource* seat); // - std::vector> m_managers; - std::vector> m_seats; - std::vector> m_tools; - std::vector> m_tablets; - std::vector> m_pads; - std::vector> m_groups; - std::vector> m_rings; - std::vector> m_strips; + std::vector> m_vManagers; + std::vector> m_vSeats; + std::vector> m_vTools; + std::vector> m_vTablets; + std::vector> m_vPads; + std::vector> m_vGroups; + std::vector> m_vRings; + std::vector> m_vStrips; // registered - std::vector> m_tabletDevices; - std::vector> m_toolDevices; - std::vector> m_padDevices; + std::vector> tablets; + std::vector> tools; + std::vector> pads; // FIXME: rings and strips are broken, I don't understand how this shit works. // It's 2am. diff --git a/src/protocols/TearingControl.cpp b/src/protocols/TearingControl.cpp index 3fd346a7..16e85352 100644 --- a/src/protocols/TearingControl.cpp +++ b/src/protocols/TearingControl.cpp @@ -1,16 +1,17 @@ #include "TearingControl.hpp" #include "../managers/ProtocolManager.hpp" -#include "../desktop/view/Window.hpp" -#include "../event/EventBus.hpp" +#include "../desktop/Window.hpp" #include "../Compositor.hpp" #include "core/Compositor.hpp" +#include "../managers/HookSystemManager.hpp" CTearingControlProtocol::CTearingControlProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P = Event::bus()->m_events.window.destroy.listen([this](PHLWINDOW window) { onWindowDestroy(window); }); + static auto P = + g_pHookSystem->hookDynamic("destroyWindow", [this](void* self, SCallbackInfo& info, std::any param) { this->onWindowDestroy(std::any_cast(param)); }); } void CTearingControlProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CWpTearingControlManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CWpTearingControlManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -20,58 +21,58 @@ void CTearingControlProtocol::bindManager(wl_client* client, void* data, uint32_ } void CTearingControlProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CTearingControlProtocol::onGetController(wl_client* client, CWpTearingControlManagerV1* pMgr, uint32_t id, SP surf) { - const auto CONTROLLER = m_tearingControllers.emplace_back(makeUnique(makeShared(client, pMgr->version(), id), surf)).get(); + const auto CONTROLLER = m_vTearingControllers.emplace_back(makeUnique(makeShared(client, pMgr->version(), id), surf)).get(); if UNLIKELY (!CONTROLLER->good()) { pMgr->noMemory(); - m_tearingControllers.pop_back(); + m_vTearingControllers.pop_back(); return; } } void CTearingControlProtocol::onControllerDestroy(CTearingControl* control) { - std::erase_if(m_tearingControllers, [control](const auto& other) { return other.get() == control; }); + std::erase_if(m_vTearingControllers, [control](const auto& other) { return other.get() == control; }); } void CTearingControlProtocol::onWindowDestroy(PHLWINDOW pWindow) { - for (auto const& c : m_tearingControllers) { - if (c->m_window.lock() == pWindow) - c->m_window.reset(); + for (auto const& c : m_vTearingControllers) { + if (c->pWindow.lock() == pWindow) + c->pWindow.reset(); } } // -CTearingControl::CTearingControl(SP resource_, SP surf_) : m_resource(resource_) { - m_resource->setData(this); - m_resource->setOnDestroy([this](CWpTearingControlV1* res) { PROTO::tearing->onControllerDestroy(this); }); - m_resource->setDestroy([this](CWpTearingControlV1* res) { PROTO::tearing->onControllerDestroy(this); }); - m_resource->setSetPresentationHint([this](CWpTearingControlV1* res, wpTearingControlV1PresentationHint hint) { this->onHint(hint); }); +CTearingControl::CTearingControl(SP resource_, SP surf_) : resource(resource_) { + resource->setData(this); + resource->setOnDestroy([this](CWpTearingControlV1* res) { PROTO::tearing->onControllerDestroy(this); }); + resource->setDestroy([this](CWpTearingControlV1* res) { PROTO::tearing->onControllerDestroy(this); }); + resource->setSetPresentationHint([this](CWpTearingControlV1* res, wpTearingControlV1PresentationHint hint) { this->onHint(hint); }); - for (auto const& w : g_pCompositor->m_windows) { - if (w->wlSurface()->resource() == surf_) { - m_window = w; + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWLSurface->resource() == surf_) { + pWindow = w; break; } } } void CTearingControl::onHint(wpTearingControlV1PresentationHint hint_) { - m_hint = hint_; + hint = hint_; updateWindow(); } void CTearingControl::updateWindow() { - if UNLIKELY (m_window.expired()) + if UNLIKELY (pWindow.expired()) return; - m_window->m_tearingHint = m_hint == WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; + pWindow->m_bTearingHint = hint == WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; } bool CTearingControl::good() { - return m_resource->resource(); + return resource->resource(); } diff --git a/src/protocols/TearingControl.hpp b/src/protocols/TearingControl.hpp index ec31b6f6..7763214e 100644 --- a/src/protocols/TearingControl.hpp +++ b/src/protocols/TearingControl.hpp @@ -3,6 +3,7 @@ #include "WaylandProtocol.hpp" #include "tearing-control-v1.hpp" +class CWindow; class CTearingControlProtocol; class CWLSurfaceResource; @@ -15,19 +16,19 @@ class CTearingControl { bool good(); bool operator==(const wl_resource* other) const { - return other == m_resource->resource(); + return other == resource->resource(); } bool operator==(const CTearingControl* other) const { - return other->m_resource == m_resource; + return other->resource == resource; } private: void updateWindow(); - SP m_resource; - PHLWINDOWREF m_window; - wpTearingControlV1PresentationHint m_hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC; + SP resource; + PHLWINDOWREF pWindow; + wpTearingControlV1PresentationHint hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC; friend class CTearingControlProtocol; }; @@ -45,8 +46,8 @@ class CTearingControlProtocol : public IWaylandProtocol { void onWindowDestroy(PHLWINDOW pWindow); // - std::vector> m_managers; - std::vector> m_tearingControllers; + std::vector> m_vManagers; + std::vector> m_vTearingControllers; friend class CTearingControl; }; diff --git a/src/protocols/TextInputV1.cpp b/src/protocols/TextInputV1.cpp index d77bb736..4bf5e9e1 100644 --- a/src/protocols/TextInputV1.cpp +++ b/src/protocols/TextInputV1.cpp @@ -3,94 +3,94 @@ #include "core/Compositor.hpp" CTextInputV1::~CTextInputV1() { - m_events.destroy.emit(); + events.destroy.emit(); } -CTextInputV1::CTextInputV1(SP resource_) : m_resource(resource_) { +CTextInputV1::CTextInputV1(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CZwpTextInputV1* pMgr) { PROTO::textInputV1->destroyResource(this); }); + resource->setOnDestroy([this](CZwpTextInputV1* pMgr) { PROTO::textInputV1->destroyResource(this); }); - m_resource->setActivate([this](CZwpTextInputV1* pMgr, wl_resource* seat, wl_resource* surface) { + resource->setActivate([this](CZwpTextInputV1* pMgr, wl_resource* seat, wl_resource* surface) { if UNLIKELY (!surface) { - LOGM(Log::WARN, "Text-input-v1 PTI{:x}: No surface to activate text input on!", (uintptr_t)this); + LOGM(WARN, "Text-input-v1 PTI{:x}: No surface to activate text input on!", (uintptr_t)this); return; } - m_active = true; - m_events.enable.emit(CWLSurfaceResource::fromResource(surface)); + active = true; + events.enable.emit(CWLSurfaceResource::fromResource(surface)); }); - m_resource->setDeactivate([this](CZwpTextInputV1* pMgr, wl_resource* seat) { - m_active = false; - m_events.disable.emit(); + resource->setDeactivate([this](CZwpTextInputV1* pMgr, wl_resource* seat) { + active = false; + events.disable.emit(); }); - m_resource->setReset([this](CZwpTextInputV1* pMgr) { - m_pendingSurrounding.isPending = false; - m_pendingContentType.isPending = false; - m_events.reset.emit(); + resource->setReset([this](CZwpTextInputV1* pMgr) { + pendingSurrounding.isPending = false; + pendingContentType.isPending = false; + events.reset.emit(); }); - m_resource->setSetSurroundingText( - [this](CZwpTextInputV1* pMgr, const char* text, uint32_t cursor, uint32_t anchor) { m_pendingSurrounding = {true, std::string(text), cursor, anchor}; }); + resource->setSetSurroundingText( + [this](CZwpTextInputV1* pMgr, const char* text, uint32_t cursor, uint32_t anchor) { pendingSurrounding = {true, std::string(text), cursor, anchor}; }); - m_resource->setSetContentType([this](CZwpTextInputV1* pMgr, uint32_t hint, uint32_t purpose) { - m_pendingContentType = {true, hint == sc(ZWP_TEXT_INPUT_V1_CONTENT_HINT_DEFAULT) ? sc(ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE) : hint, - purpose > sc(ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD) ? hint + 1 : hint}; + resource->setSetContentType([this](CZwpTextInputV1* pMgr, uint32_t hint, uint32_t purpose) { + pendingContentType = {true, hint == (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_HINT_DEFAULT ? (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE : hint, + purpose > (uint32_t)ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD ? hint + 1 : hint}; }); - m_resource->setSetCursorRectangle([this](CZwpTextInputV1* pMgr, int32_t x, int32_t y, int32_t width, int32_t height) { m_cursorRectangle = CBox{x, y, width, height}; }); + resource->setSetCursorRectangle([this](CZwpTextInputV1* pMgr, int32_t x, int32_t y, int32_t width, int32_t height) { cursorRectangle = CBox{x, y, width, height}; }); - m_resource->setCommitState([this](CZwpTextInputV1* pMgr, uint32_t serial_) { - m_serial = serial_; - m_events.onCommit.emit(); + resource->setCommitState([this](CZwpTextInputV1* pMgr, uint32_t serial_) { + serial = serial_; + events.onCommit.emit(); }); // nothing - m_resource->setShowInputPanel([](CZwpTextInputV1* pMgr) {}); - m_resource->setHideInputPanel([](CZwpTextInputV1* pMgr) {}); - m_resource->setSetPreferredLanguage([](CZwpTextInputV1* pMgr, const char* language) {}); - m_resource->setInvokeAction([](CZwpTextInputV1* pMgr, uint32_t button, uint32_t index) {}); + resource->setShowInputPanel([](CZwpTextInputV1* pMgr) {}); + resource->setHideInputPanel([](CZwpTextInputV1* pMgr) {}); + resource->setSetPreferredLanguage([](CZwpTextInputV1* pMgr, const char* language) {}); + resource->setInvokeAction([](CZwpTextInputV1* pMgr, uint32_t button, uint32_t index) {}); } bool CTextInputV1::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CTextInputV1::client() { - return m_resource->client(); + return resource->client(); } void CTextInputV1::enter(SP surface) { - m_resource->sendEnter(surface->getResource()->resource()); - m_active = true; + resource->sendEnter(surface->getResource()->resource()); + active = true; } void CTextInputV1::leave() { - m_resource->sendLeave(); - m_active = false; + resource->sendLeave(); + active = false; } void CTextInputV1::preeditCursor(int32_t index) { - m_resource->sendPreeditCursor(index); + resource->sendPreeditCursor(index); } void CTextInputV1::preeditStyling(uint32_t index, uint32_t length, zwpTextInputV1PreeditStyle style) { - m_resource->sendPreeditStyling(index, length, style); + resource->sendPreeditStyling(index, length, style); } void CTextInputV1::preeditString(uint32_t serial, const char* text, const char* commit) { - m_resource->sendPreeditString(serial, text, commit); + resource->sendPreeditString(serial, text, commit); } void CTextInputV1::commitString(uint32_t serial, const char* text) { - m_resource->sendCommitString(serial, text); + resource->sendCommitString(serial, text); } void CTextInputV1::deleteSurroundingText(int32_t index, uint32_t length) { - m_resource->sendDeleteSurroundingText(index, length); + resource->sendDeleteSurroundingText(index, length); } CTextInputV1Protocol::CTextInputV1Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -98,28 +98,28 @@ CTextInputV1Protocol::CTextInputV1Protocol(const wl_interface* iface, const int& } void CTextInputV1Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(client, ver, id)); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(client, ver, id)); RESOURCE->setOnDestroy([](CZwpTextInputManagerV1* pMgr) { PROTO::textInputV1->destroyResource(pMgr); }); RESOURCE->setCreateTextInput([this](CZwpTextInputManagerV1* pMgr, uint32_t id) { - const auto PTI = m_clients.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id))); - LOGM(Log::DEBUG, "New TI V1 at {:x}", (uintptr_t)PTI.get()); + const auto PTI = m_vClients.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id))); + LOGM(LOG, "New TI V1 at {:x}", (uintptr_t)PTI.get()); if UNLIKELY (!PTI->good()) { - LOGM(Log::ERR, "Could not alloc wl_resource for TIV1"); + LOGM(ERR, "Could not alloc wl_resource for TIV1"); pMgr->noMemory(); PROTO::textInputV1->destroyResource(PTI.get()); return; } - m_events.newTextInput.emit(WP(PTI)); + events.newTextInput.emit(WP(PTI)); }); } void CTextInputV1Protocol::destroyResource(CTextInputV1* client) { - std::erase_if(m_clients, [&](const auto& other) { return other.get() == client; }); + std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; }); } void CTextInputV1Protocol::destroyResource(CZwpTextInputManagerV1* client) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == client; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == client; }); } diff --git a/src/protocols/TextInputV1.hpp b/src/protocols/TextInputV1.hpp index f22dfdfe..d3b0d71b 100644 --- a/src/protocols/TextInputV1.hpp +++ b/src/protocols/TextInputV1.hpp @@ -27,33 +27,34 @@ class CTextInputV1 { wl_client* client(); private: - SP m_resource; + SP resource; + WP self; - uint32_t m_serial = 0; - bool m_active = false; + uint32_t serial = 0; + bool active = false; struct { - CSignalT<> onCommit; - CSignalT> enable; - CSignalT<> disable; - CSignalT<> reset; - CSignalT<> destroy; - } m_events; + CSignal onCommit; + CSignal enable; + CSignal disable; + CSignal reset; + CSignal destroy; + } events; struct SPendingSurr { bool isPending = false; std::string text = ""; uint32_t cursor = 0; uint32_t anchor = 0; - } m_pendingSurrounding; + } pendingSurrounding; struct SPendingCT { bool isPending = false; uint32_t hint = 0; uint32_t purpose = 0; - } m_pendingContentType; + } pendingContentType; - CBox m_cursorRectangle = {0, 0, 0, 0}; + CBox cursorRectangle = {0, 0, 0, 0}; friend class CTextInput; friend class CTextInputV1Protocol; @@ -68,12 +69,12 @@ class CTextInputV1Protocol : public IWaylandProtocol { void destroyResource(CZwpTextInputManagerV1* client); struct { - CSignalT> newTextInput; - } m_events; + CSignal newTextInput; // WP + } events; private: - std::vector> m_managers; - std::vector> m_clients; + std::vector> m_vManagers; + std::vector> m_vClients; friend class CTextInputV1; }; diff --git a/src/protocols/TextInputV3.cpp b/src/protocols/TextInputV3.cpp index 595467c4..06aea5ae 100644 --- a/src/protocols/TextInputV3.cpp +++ b/src/protocols/TextInputV3.cpp @@ -9,100 +9,100 @@ void CTextInputV3::SState::reset() { box.updated = false; } -CTextInputV3::CTextInputV3(SP resource_) : m_resource(resource_) { - if UNLIKELY (!m_resource->resource()) +CTextInputV3::CTextInputV3(SP resource_) : resource(resource_) { + if UNLIKELY (!resource->resource()) return; - LOGM(Log::DEBUG, "New tiv3 at {:016x}", (uintptr_t)this); + LOGM(LOG, "New tiv3 at {:016x}", (uintptr_t)this); - m_resource->setDestroy([this](CZwpTextInputV3* r) { PROTO::textInputV3->destroyTextInput(this); }); - m_resource->setOnDestroy([this](CZwpTextInputV3* r) { PROTO::textInputV3->destroyTextInput(this); }); + resource->setDestroy([this](CZwpTextInputV3* r) { PROTO::textInputV3->destroyTextInput(this); }); + resource->setOnDestroy([this](CZwpTextInputV3* r) { PROTO::textInputV3->destroyTextInput(this); }); - m_resource->setCommit([this](CZwpTextInputV3* r) { - bool wasEnabled = m_current.enabled.value; + resource->setCommit([this](CZwpTextInputV3* r) { + bool wasEnabled = current.enabled.value; - m_current = m_pending; - m_serial++; + current = pending; + serial++; - if (wasEnabled && !m_current.enabled.value) - m_events.disable.emit(); - else if (!wasEnabled && m_current.enabled.value) - m_events.enable.emit(); - else if (m_current.enabled.value && m_current.enabled.isEnablePending && m_current.enabled.isDisablePending) - m_events.reset.emit(); + if (wasEnabled && !current.enabled.value) + events.disable.emit(); + else if (!wasEnabled && current.enabled.value) + events.enable.emit(); + else if (current.enabled.value && current.enabled.isEnablePending && current.enabled.isDisablePending) + events.reset.emit(); else - m_events.onCommit.emit(); + events.onCommit.emit(); - m_pending.enabled.isEnablePending = false; - m_pending.enabled.isDisablePending = false; + pending.enabled.isEnablePending = false; + pending.enabled.isDisablePending = false; }); - m_resource->setSetSurroundingText([this](CZwpTextInputV3* r, const char* text, int32_t cursor, int32_t anchor) { - m_pending.surrounding.updated = true; - m_pending.surrounding.anchor = anchor; - m_pending.surrounding.cursor = cursor; - m_pending.surrounding.text = text; + resource->setSetSurroundingText([this](CZwpTextInputV3* r, const char* text, int32_t cursor, int32_t anchor) { + pending.surrounding.updated = true; + pending.surrounding.anchor = anchor; + pending.surrounding.cursor = cursor; + pending.surrounding.text = text; }); - m_resource->setSetTextChangeCause([this](CZwpTextInputV3* r, zwpTextInputV3ChangeCause cause) { m_pending.cause = cause; }); + resource->setSetTextChangeCause([this](CZwpTextInputV3* r, zwpTextInputV3ChangeCause cause) { pending.cause = cause; }); - m_resource->setSetContentType([this](CZwpTextInputV3* r, zwpTextInputV3ContentHint hint, zwpTextInputV3ContentPurpose purpose) { - m_pending.contentType.updated = true; - m_pending.contentType.hint = hint; - m_pending.contentType.purpose = purpose; + resource->setSetContentType([this](CZwpTextInputV3* r, zwpTextInputV3ContentHint hint, zwpTextInputV3ContentPurpose purpose) { + pending.contentType.updated = true; + pending.contentType.hint = hint; + pending.contentType.purpose = purpose; }); - m_resource->setSetCursorRectangle([this](CZwpTextInputV3* r, int32_t x, int32_t y, int32_t w, int32_t h) { - m_pending.box.updated = true; - m_pending.box.cursorBox = {x, y, w, h}; + resource->setSetCursorRectangle([this](CZwpTextInputV3* r, int32_t x, int32_t y, int32_t w, int32_t h) { + pending.box.updated = true; + pending.box.cursorBox = {x, y, w, h}; }); - m_resource->setEnable([this](CZwpTextInputV3* r) { - m_pending.reset(); - m_pending.enabled.value = true; - m_pending.enabled.isEnablePending = true; + resource->setEnable([this](CZwpTextInputV3* r) { + pending.reset(); + pending.enabled.value = true; + pending.enabled.isEnablePending = true; }); - m_resource->setDisable([this](CZwpTextInputV3* r) { - m_pending.enabled.value = false; - m_pending.enabled.isDisablePending = true; + resource->setDisable([this](CZwpTextInputV3* r) { + pending.enabled.value = false; + pending.enabled.isDisablePending = true; }); } CTextInputV3::~CTextInputV3() { - m_events.destroy.emit(); + events.destroy.emit(); } void CTextInputV3::enter(SP surf) { - m_resource->sendEnter(surf->getResource()->resource()); + resource->sendEnter(surf->getResource()->resource()); } void CTextInputV3::leave(SP surf) { - m_resource->sendLeave(surf->getResource()->resource()); + resource->sendLeave(surf->getResource()->resource()); } void CTextInputV3::preeditString(const std::string& text, int32_t cursorBegin, int32_t cursorEnd) { - m_resource->sendPreeditString(text.c_str(), cursorBegin, cursorEnd); + resource->sendPreeditString(text.c_str(), cursorBegin, cursorEnd); } void CTextInputV3::commitString(const std::string& text) { - m_resource->sendCommitString(text.c_str()); + resource->sendCommitString(text.c_str()); } void CTextInputV3::deleteSurroundingText(uint32_t beforeLength, uint32_t afterLength) { - m_resource->sendDeleteSurroundingText(beforeLength, afterLength); + resource->sendDeleteSurroundingText(beforeLength, afterLength); } void CTextInputV3::sendDone() { - m_resource->sendDone(m_serial); + resource->sendDone(serial); } bool CTextInputV3::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CTextInputV3::client() { - return wl_resource_get_client(m_resource->resource()); + return wl_resource_get_client(resource->resource()); } CTextInputV3Protocol::CTextInputV3Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -110,7 +110,7 @@ CTextInputV3Protocol::CTextInputV3Protocol(const wl_interface* iface, const int& } void CTextInputV3Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpTextInputManagerV3* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwpTextInputManagerV3* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -118,23 +118,23 @@ void CTextInputV3Protocol::bindManager(wl_client* client, void* data, uint32_t v } void CTextInputV3Protocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CTextInputV3Protocol::destroyTextInput(CTextInputV3* input) { - std::erase_if(m_textInputs, [&](const auto& other) { return other.get() == input; }); + std::erase_if(m_vTextInputs, [&](const auto& other) { return other.get() == input; }); } void CTextInputV3Protocol::onGetTextInput(CZwpTextInputManagerV3* pMgr, uint32_t id, wl_resource* seat) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_textInputs.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id))); + const auto RESOURCE = m_vTextInputs.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id))); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_textInputs.pop_back(); - LOGM(Log::ERR, "Failed to create a tiv3 resource"); + m_vTextInputs.pop_back(); + LOGM(ERR, "Failed to create a tiv3 resource"); return; } - m_events.newTextInput.emit(WP(RESOURCE)); + events.newTextInput.emit(WP(RESOURCE)); } \ No newline at end of file diff --git a/src/protocols/TextInputV3.hpp b/src/protocols/TextInputV3.hpp index 420adf53..6cece521 100644 --- a/src/protocols/TextInputV3.hpp +++ b/src/protocols/TextInputV3.hpp @@ -27,12 +27,12 @@ class CTextInputV3 { wl_client* client(); struct { - CSignalT<> onCommit; - CSignalT<> enable; - CSignalT<> disable; - CSignalT<> reset; - CSignalT<> destroy; - } m_events; + CSignal onCommit; + CSignal enable; + CSignal disable; + CSignal reset; + CSignal destroy; + } events; struct SState { struct { @@ -63,14 +63,12 @@ class CTextInputV3 { void reset(); }; - - SState m_pending; - SState m_current; + SState pending, current; private: - SP m_resource; + SP resource; - int m_serial = 0; + int serial = 0; }; class CTextInputV3Protocol : public IWaylandProtocol { @@ -80,8 +78,8 @@ class CTextInputV3Protocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); struct { - CSignalT> newTextInput; - } m_events; + CSignal newTextInput; // WP + } events; private: void onManagerResourceDestroy(wl_resource* res); @@ -89,12 +87,12 @@ class CTextInputV3Protocol : public IWaylandProtocol { void onGetTextInput(CZwpTextInputManagerV3* pMgr, uint32_t id, wl_resource* seat); // - std::vector> m_managers; - std::vector> m_textInputs; + std::vector> m_vManagers; + std::vector> m_vTextInputs; friend class CTextInputV3; }; namespace PROTO { inline UP textInputV3; -}; +}; \ No newline at end of file diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp index d7ba7519..fb0bd9c7 100644 --- a/src/protocols/ToplevelExport.cpp +++ b/src/protocols/ToplevelExport.cpp @@ -1,150 +1,384 @@ #include "ToplevelExport.hpp" #include "../Compositor.hpp" #include "ForeignToplevelWlr.hpp" -#include "../managers/screenshare/ScreenshareManager.hpp" +#include "../managers/PointerManager.hpp" +#include "../managers/SeatManager.hpp" +#include "types/WLBuffer.hpp" +#include "types/Buffer.hpp" #include "../helpers/Format.hpp" +#include "../managers/EventManager.hpp" +#include "../managers/input/InputManager.hpp" #include "../render/Renderer.hpp" +#include #include -using namespace Screenshare; - -CToplevelExportClient::CToplevelExportClient(SP resource_) : m_resource(resource_) { +CToplevelExportClient::CToplevelExportClient(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); }); - m_resource->setDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); }); - m_resource->setCaptureToplevel([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, uint32_t handle) { - captureToplevel(frame, overlayCursor, g_pCompositor->getWindowFromHandle(handle)); + resource->setOnDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); }); + resource->setDestroy([this](CHyprlandToplevelExportManagerV1* pMgr) { PROTO::toplevelExport->destroyResource(this); }); + resource->setCaptureToplevel([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, uint32_t handle) { + this->captureToplevel(pMgr, frame, overlayCursor, g_pCompositor->getWindowFromHandle(handle)); }); - m_resource->setCaptureToplevelWithWlrToplevelHandle([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* handle) { - captureToplevel(frame, overlayCursor, PROTO::foreignToplevelWlr->windowFromHandleResource(handle)); + resource->setCaptureToplevelWithWlrToplevelHandle([this](CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, wl_resource* handle) { + this->captureToplevel(pMgr, frame, overlayCursor, PROTO::foreignToplevelWlr->windowFromHandleResource(handle)); }); - m_savedClient = m_resource->client(); + lastMeasure.reset(); + lastFrame.reset(); + tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, SCallbackInfo& info, std::any data) { onTick(); }); } -void CToplevelExportClient::captureToplevel(uint32_t frame, int32_t overlayCursor_, PHLWINDOW handle) { - auto session = Screenshare::mgr()->getManagedSession(m_resource->client(), handle); - +void CToplevelExportClient::captureToplevel(CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor_, PHLWINDOW handle) { // create a frame - const auto FRAME = PROTO::toplevelExport->m_frames.emplace_back( - makeShared(makeShared(m_resource->client(), m_resource->version(), frame), session, !!overlayCursor_)); + const auto FRAME = PROTO::toplevelExport->m_vFrames.emplace_back( + makeShared(makeShared(resource->client(), resource->version(), frame), overlayCursor_, handle)); if UNLIKELY (!FRAME->good()) { - LOGM(Log::ERR, "Couldn't alloc frame for sharing! (no memory)"); - m_resource->noMemory(); + LOGM(ERR, "Couldn't alloc frame for sharing! (no memory)"); + resource->noMemory(); PROTO::toplevelExport->destroyResource(FRAME.get()); return; } - FRAME->m_client = m_self; - FRAME->m_self = FRAME; + FRAME->self = FRAME; + FRAME->client = self; +} + +void CToplevelExportClient::onTick() { + if (lastMeasure.getMillis() < 500) + return; + + framesInLastHalfSecond = frameCounter; + frameCounter = 0; + lastMeasure.reset(); + + const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0; + const bool FRAMEAWAITING = std::ranges::any_of(PROTO::toplevelExport->m_vFrames, [&](const auto& frame) { return frame->client.get() == this; }); + + if (framesInLastHalfSecond > 3 && !sentScreencast) { + EMIT_HOOK_EVENT("screencast", (std::vector{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner})); + g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)}); + sentScreencast = true; + } else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) { + EMIT_HOOK_EVENT("screencast", (std::vector{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner})); + g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)}); + sentScreencast = false; + } } bool CToplevelExportClient::good() { - return m_resource && m_resource->resource(); + return resource->resource(); } -CToplevelExportFrame::CToplevelExportFrame(SP resource_, WP session, bool overlayCursor) : - m_resource(resource_), m_session(session) { +CToplevelExportFrame::~CToplevelExportFrame() { + if (buffer && buffer->locked()) + buffer->unlock(); +} + +CToplevelExportFrame::CToplevelExportFrame(SP resource_, int32_t overlayCursor_, PHLWINDOW pWindow_) : resource(resource_), pWindow(pWindow_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); }); - m_resource->setDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); }); - m_resource->setCopy([this](CHyprlandToplevelExportFrameV1* pFrame, wl_resource* res, int32_t ignoreDamage) { shareFrame(res, !!ignoreDamage); }); + cursorOverlayRequested = !!overlayCursor_; - m_frame = m_session->nextFrame(overlayCursor); - - auto formats = m_session->allowedFormats(); - if (formats.empty()) { - LOGM(Log::ERR, "No format supported by renderer in toplevel export protocol"); - m_resource->sendFailed(); + if UNLIKELY (!pWindow) { + LOGM(ERR, "Client requested sharing of window handle {:x} which does not exist!", pWindow); + resource->sendFailed(); return; } - DRMFormat format = formats.at(0); - auto bufSize = m_frame->bufferSize(); + if UNLIKELY (!pWindow->m_bIsMapped) { + LOGM(ERR, "Client requested sharing of window handle {:x} which is not shareable!", pWindow); + resource->sendFailed(); + return; + } - const auto PSHMINFO = NFormatUtils::getPixelFormatFromDRM(format); - const auto stride = NFormatUtils::minStride(PSHMINFO, bufSize.x); - m_resource->sendBuffer(NFormatUtils::drmToShm(format), bufSize.x, bufSize.y, stride); + resource->setOnDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); }); + resource->setDestroy([this](CHyprlandToplevelExportFrameV1* pFrame) { PROTO::toplevelExport->destroyResource(this); }); + resource->setCopy([this](CHyprlandToplevelExportFrameV1* pFrame, wl_resource* res, int32_t ignoreDamage) { this->copy(pFrame, res, ignoreDamage); }); - if LIKELY (format != DRM_FORMAT_INVALID) - m_resource->sendLinuxDmabuf(format, bufSize.x, bufSize.y); + const auto PMONITOR = pWindow->m_pMonitor.lock(); - m_resource->sendBufferDone(); + g_pHyprRenderer->makeEGLCurrent(); + + shmFormat = g_pHyprOpenGL->getPreferredReadFormat(PMONITOR); + if UNLIKELY (shmFormat == DRM_FORMAT_INVALID) { + LOGM(ERR, "No format supported by renderer in capture toplevel"); + resource->sendFailed(); + return; + } + + const auto PSHMINFO = NFormatUtils::getPixelFormatFromDRM(shmFormat); + if UNLIKELY (!PSHMINFO) { + LOGM(ERR, "No pixel format supported by renderer in capture toplevel"); + resource->sendFailed(); + return; + } + + dmabufFormat = PMONITOR->output->state->state().drmFormat; + + box = {0, 0, (int)(pWindow->m_vRealSize->value().x * PMONITOR->scale), (int)(pWindow->m_vRealSize->value().y * PMONITOR->scale)}; + + box.transform(wlTransformToHyprutils(PMONITOR->transform), PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y).round(); + + shmStride = NFormatUtils::minStride(PSHMINFO, box.w); + + resource->sendBuffer(NFormatUtils::drmToShm(shmFormat), box.width, box.height, shmStride); + + if LIKELY (dmabufFormat != DRM_FORMAT_INVALID) + resource->sendLinuxDmabuf(dmabufFormat, box.width, box.height); + + resource->sendBufferDone(); +} + +void CToplevelExportFrame::copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resource* buffer_, int32_t ignoreDamage) { + if UNLIKELY (!good()) { + LOGM(ERR, "No frame in copyFrame??"); + return; + } + + if UNLIKELY (!validMapped(pWindow)) { + LOGM(ERR, "Client requested sharing of window handle {:x} which is gone!", pWindow); + resource->sendFailed(); + return; + } + + if UNLIKELY (!pWindow->m_bIsMapped) { + LOGM(ERR, "Client requested sharing of window handle {:x} which is not shareable (2)!", pWindow); + resource->sendFailed(); + return; + } + + const auto PBUFFER = CWLBufferResource::fromResource(buffer_); + if UNLIKELY (!PBUFFER) { + resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); + PROTO::toplevelExport->destroyResource(this); + return; + } + + PBUFFER->buffer->lock(); + + if UNLIKELY (PBUFFER->buffer->size != box.size()) { + resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions"); + PROTO::toplevelExport->destroyResource(this); + return; + } + + if UNLIKELY (buffer) { + resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used"); + PROTO::toplevelExport->destroyResource(this); + return; + } + + if (auto attrs = PBUFFER->buffer->dmabuf(); attrs.success) { + bufferDMA = true; + + if (attrs.format != dmabufFormat) { + resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); + PROTO::toplevelExport->destroyResource(this); + return; + } + } else if (auto attrs = PBUFFER->buffer->shm(); attrs.success) { + if (attrs.format != shmFormat) { + resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); + PROTO::toplevelExport->destroyResource(this); + return; + } else if ((int)attrs.stride != shmStride) { + resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride"); + PROTO::toplevelExport->destroyResource(this); + return; + } + } else { + resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type"); + PROTO::toplevelExport->destroyResource(this); + return; + } + + buffer = PBUFFER->buffer; + + m_ignoreDamage = ignoreDamage; + + if (ignoreDamage && validMapped(pWindow)) + share(); + else + PROTO::toplevelExport->m_vFramesAwaitingWrite.emplace_back(self); +} + +void CToplevelExportFrame::share() { + if (!buffer || !validMapped(pWindow)) + return; + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + if (bufferDMA) { + if (!copyDmabuf(&now)) { + resource->sendFailed(); + return; + } + } else { + if (!copyShm(&now)) { + resource->sendFailed(); + return; + } + } + + resource->sendFlags((hyprlandToplevelExportFrameV1Flags)0); + + if (!m_ignoreDamage) { + resource->sendDamage(0, 0, box.width, box.height); + } + + uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0; + uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF; + resource->sendReady(tvSecHi, tvSecLo, now.tv_nsec); +} + +bool CToplevelExportFrame::copyShm(timespec* now) { + auto shm = buffer->shm(); + auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm + + // render the client + const auto PMONITOR = pWindow->m_pMonitor.lock(); + CRegion fakeDamage{0, 0, PMONITOR->vecPixelSize.x * 10, PMONITOR->vecPixelSize.y * 10}; + + g_pHyprRenderer->makeEGLCurrent(); + + CFramebuffer outFB; + outFB.alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat); + + auto overlayCursor = shouldOverlayCursor(); + + if (overlayCursor) { + g_pPointerManager->lockSoftwareForMonitor(PMONITOR->self.lock()); + g_pPointerManager->damageCursor(PMONITOR->self.lock()); + } + + if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &outFB)) + return false; + + g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 1.0)); + + // render client at 0,0 + g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible + g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true); + g_pHyprRenderer->m_bBlockSurfaceFeedback = false; + + if (overlayCursor) + g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition->value()); + + const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(shm.format); + if (!PFORMAT) { + g_pHyprRenderer->endRender(); + return false; + } + + g_pHyprOpenGL->m_RenderData.blockScreenShader = true; + g_pHyprRenderer->endRender(); + + g_pHyprRenderer->makeEGLCurrent(); + g_pHyprOpenGL->m_RenderData.pMonitor = PMONITOR; + outFB.bind(); + +#ifndef GLES2 + glBindFramebuffer(GL_READ_FRAMEBUFFER, outFB.getFBID()); +#endif + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + auto glFormat = PFORMAT->flipRB ? GL_BGRA_EXT : GL_RGBA; + + auto origin = Vector2D(0, 0); + switch (PMONITOR->transform) { + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_90: { + origin.y = PMONITOR->vecPixelSize.y - box.height; + break; + } + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + case WL_OUTPUT_TRANSFORM_180: { + origin.x = PMONITOR->vecPixelSize.x - box.width; + origin.y = PMONITOR->vecPixelSize.y - box.height; + break; + } + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_270: { + origin.x = PMONITOR->vecPixelSize.x - box.width; + break; + } + default: break; + } + + glReadPixels(origin.x, origin.y, box.width, box.height, glFormat, PFORMAT->glType, pixelData); + + if (overlayCursor) { + g_pPointerManager->unlockSoftwareForMonitor(PMONITOR->self.lock()); + g_pPointerManager->damageCursor(PMONITOR->self.lock()); + } + + outFB.unbind(); + +#ifndef GLES2 + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +#endif + + return true; +} + +bool CToplevelExportFrame::copyDmabuf(timespec* now) { + const auto PMONITOR = pWindow->m_pMonitor.lock(); + + CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; + + auto overlayCursor = shouldOverlayCursor(); + + if (overlayCursor) { + g_pPointerManager->lockSoftwareForMonitor(PMONITOR->self.lock()); + g_pPointerManager->damageCursor(PMONITOR->self.lock()); + } + + if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, buffer.lock())) + return false; + + g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 1.0)); + + g_pHyprRenderer->m_bBlockSurfaceFeedback = g_pHyprRenderer->shouldRenderWindow(pWindow); // block the feedback to avoid spamming the surface if it's visible + g_pHyprRenderer->renderWindow(pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true); + g_pHyprRenderer->m_bBlockSurfaceFeedback = false; + + if (overlayCursor) + g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - pWindow->m_vRealPosition->value()); + + g_pHyprOpenGL->m_RenderData.blockScreenShader = true; + g_pHyprRenderer->endRender(); + + if (overlayCursor) { + g_pPointerManager->unlockSoftwareForMonitor(PMONITOR->self.lock()); + g_pPointerManager->damageCursor(PMONITOR->self.lock()); + } + + return true; +} + +bool CToplevelExportFrame::shouldOverlayCursor() const { + if (!cursorOverlayRequested) + return false; + + auto pointerSurfaceResource = g_pSeatManager->state.pointerFocus.lock(); + + if (!pointerSurfaceResource) + return false; + + auto pointerSurface = CWLSurface::fromResource(pointerSurfaceResource); + + return pointerSurface && pointerSurface->getWindow() == pWindow; } bool CToplevelExportFrame::good() { - return m_resource && m_resource->resource(); -} - -void CToplevelExportFrame::shareFrame(wl_resource* buffer, bool ignoreDamage) { - if UNLIKELY (!good()) { - LOGM(Log::ERR, "No frame in shareFrame??"); - return; - } - - if UNLIKELY (m_session.expired() || !m_session->monitor()) { - LOGM(Log::ERR, "Session stopped for frame {:x}", (uintptr_t)this); - m_resource->sendFailed(); - return; - } - - if UNLIKELY (m_buffer) { - LOGM(Log::ERR, "Buffer used in {:x}", (uintptr_t)this); - m_resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED, "frame already used"); - m_resource->sendFailed(); - return; - } - - const auto PBUFFERRES = CWLBufferResource::fromResource(buffer); - if UNLIKELY (!PBUFFERRES || !PBUFFERRES->m_buffer) { - LOGM(Log::ERR, "Invalid buffer in {:x}", (uintptr_t)this); - m_resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); - m_resource->sendFailed(); - return; - } - - const auto& PBUFFER = PBUFFERRES->m_buffer.lock(); - - if (ignoreDamage) - g_pHyprRenderer->damageMonitor(m_session->monitor()); - - auto error = m_frame->share(PBUFFER, {}, [this, ignoreDamage, self = m_self](eScreenshareResult result) { - if (self.expired() || !good()) - return; - switch (result) { - case RESULT_COPIED: { - m_resource->sendFlags(sc(0)); - if (!ignoreDamage) - m_frame->damage().forEachRect([&](const auto& rect) { m_resource->sendDamage(rect.x1, rect.y1, rect.x2 - rect.x1, rect.y2 - rect.y1); }); - - const auto [sec, nsec] = Time::secNsec(m_timestamp); - uint32_t tvSecHi = (sizeof(sec) > 4) ? sec >> 32 : 0; - uint32_t tvSecLo = sec & 0xFFFFFFFF; - m_resource->sendReady(tvSecHi, tvSecLo, nsec); - break; - } - case RESULT_NOT_COPIED: - LOGM(Log::ERR, "Frame share failed in {:x}", (uintptr_t)this); - m_resource->sendFailed(); - break; - case RESULT_TIMESTAMP: m_timestamp = Time::steadyNow(); break; - } - }); - - switch (error) { - case ERROR_NONE: m_buffer = CHLBufferReference(PBUFFER); break; - case ERROR_NO_BUFFER: m_resource->error(HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); break; - case ERROR_BUFFER_SIZE: - case ERROR_BUFFER_FORMAT: m_resource->sendFailed(); break; - case ERROR_UNKNOWN: - case ERROR_STOPPED: m_resource->sendFailed(); break; - } + return resource->resource(); } CToplevelExportProtocol::CToplevelExportProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -152,25 +386,78 @@ CToplevelExportProtocol::CToplevelExportProtocol(const wl_interface* iface, cons } void CToplevelExportProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto CLIENT = m_clients.emplace_back(makeShared(makeShared(client, ver, id))); + const auto CLIENT = m_vClients.emplace_back(makeShared(makeShared(client, ver, id))); if (!CLIENT->good()) { - LOGM(Log::DEBUG, "Failed to bind client! (out of memory)"); + LOGM(LOG, "Failed to bind client! (out of memory)"); wl_client_post_no_memory(client); - m_clients.pop_back(); + m_vClients.pop_back(); return; } - CLIENT->m_self = CLIENT; + CLIENT->self = CLIENT; - LOGM(Log::DEBUG, "Bound client successfully!"); + LOGM(LOG, "Bound client successfully!"); } void CToplevelExportProtocol::destroyResource(CToplevelExportClient* client) { - std::erase_if(m_frames, [&](const auto& other) { return other->m_client.get() == client; }); - std::erase_if(m_clients, [&](const auto& other) { return other.get() == client; }); + std::erase_if(m_vClients, [&](const auto& other) { return other.get() == client; }); + std::erase_if(m_vFrames, [&](const auto& other) { return other->client.get() == client; }); + std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return !other || other->client.get() == client; }); } void CToplevelExportProtocol::destroyResource(CToplevelExportFrame* frame) { - std::erase_if(m_frames, [&](const auto& other) { return other.get() == frame; }); + std::erase_if(m_vFrames, [&](const auto& other) { return other.get() == frame; }); + std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return !other || other.get() == frame; }); +} + +void CToplevelExportProtocol::onOutputCommit(PHLMONITOR pMonitor) { + if (m_vFramesAwaitingWrite.empty()) + return; // nothing to share + + std::vector> framesToRemove; + // reserve number of elements to avoid reallocations + framesToRemove.reserve(m_vFramesAwaitingWrite.size()); + + // share frame if correct output + for (auto const& f : m_vFramesAwaitingWrite) { + if (!f) + continue; + + if (!validMapped(f->pWindow)) { + framesToRemove.emplace_back(f); + continue; + } + + if (!f->pWindow) + continue; + + const auto PWINDOW = f->pWindow; + + if (pMonitor != PWINDOW->m_pMonitor.lock()) + continue; + + CBox geometry = {PWINDOW->m_vRealPosition->value().x, PWINDOW->m_vRealPosition->value().y, PWINDOW->m_vRealSize->value().x, PWINDOW->m_vRealSize->value().y}; + + if (geometry.intersection({pMonitor->vecPosition, pMonitor->vecSize}).empty()) + continue; + + f->share(); + + f->client->lastFrame.reset(); + ++f->client->frameCounter; + + framesToRemove.push_back(f); + } + + for (auto const& f : framesToRemove) { + std::erase(m_vFramesAwaitingWrite, f); + } +} + +void CToplevelExportProtocol::onWindowUnmap(PHLWINDOW pWindow) { + for (auto const& f : m_vFrames) { + if (f->pWindow == pWindow) + f->pWindow.reset(); + } } diff --git a/src/protocols/ToplevelExport.hpp b/src/protocols/ToplevelExport.hpp index 5d30f09b..8d734dbd 100644 --- a/src/protocols/ToplevelExport.hpp +++ b/src/protocols/ToplevelExport.hpp @@ -1,59 +1,74 @@ #pragma once -#include "WaylandProtocol.hpp" +#include "../defines.hpp" #include "hyprland-toplevel-export-v1.hpp" - -#include "../helpers/time/Time.hpp" -#include "./types/Buffer.hpp" -#include +#include "WaylandProtocol.hpp" +#include "Screencopy.hpp" #include class CMonitor; -namespace Screenshare { - class CScreenshareSession; - class CScreenshareFrame; -}; +class CWindow; class CToplevelExportClient { public: CToplevelExportClient(SP resource_); - bool good(); + bool good(); + + WP self; + eClientOwners clientOwner = CLIENT_TOPLEVEL_EXPORT; + + CTimer lastFrame; + int frameCounter = 0; private: - SP m_resource; - WP m_self; + SP resource; - wl_client* m_savedClient = nullptr; + int framesInLastHalfSecond = 0; + CTimer lastMeasure; + bool sentScreencast = false; - void captureToplevel(uint32_t frame, int32_t overlayCursor, PHLWINDOW handle); + SP tickCallback; + void onTick(); + + void captureToplevel(CHyprlandToplevelExportManagerV1* pMgr, uint32_t frame, int32_t overlayCursor, PHLWINDOW handle); friend class CToplevelExportProtocol; }; class CToplevelExportFrame { public: - CToplevelExportFrame(SP resource, WP session, bool overlayCursor); + CToplevelExportFrame(SP resource_, int32_t overlayCursor, PHLWINDOW pWindow); + ~CToplevelExportFrame(); - bool good(); + bool good(); + + WP self; + WP client; private: - SP m_resource; - WP m_self; - WP m_client; + SP resource; - WP m_session; - UP m_frame; + PHLWINDOW pWindow; + bool cursorOverlayRequested = false; + bool m_ignoreDamage = false; + bool lockedSWCursors = false; - CHLBufferReference m_buffer; - Time::steady_tp m_timestamp; + WP buffer; + bool bufferDMA = false; + uint32_t shmFormat = 0; + uint32_t dmabufFormat = 0; + int shmStride = 0; + CBox box = {}; - // - void shareFrame(wl_resource* buffer, bool ignoreDamage); + void copy(CHyprlandToplevelExportFrameV1* pFrame, wl_resource* buffer, int32_t ignoreDamage); + bool copyDmabuf(timespec* now); + bool copyShm(timespec* now); + void share(); + bool shouldOverlayCursor() const; friend class CToplevelExportProtocol; - friend class CToplevelExportClient; }; class CToplevelExportProtocol : IWaylandProtocol { @@ -64,13 +79,18 @@ class CToplevelExportProtocol : IWaylandProtocol { void destroyResource(CToplevelExportClient* client); void destroyResource(CToplevelExportFrame* frame); + void onWindowUnmap(PHLWINDOW pWindow); void onOutputCommit(PHLMONITOR pMonitor); private: - std::vector> m_clients; - std::vector> m_frames; + std::vector> m_vClients; + std::vector> m_vFrames; + std::vector> m_vFramesAwaitingWrite; - void onWindowUnmap(PHLWINDOW pWindow); + void shareFrame(CToplevelExportFrame* frame); + bool copyFrameDmabuf(CToplevelExportFrame* frame, timespec* now); + bool copyFrameShm(CToplevelExportFrame* frame, timespec* now); + void sendDamage(CToplevelExportFrame* frame); friend class CToplevelExportClient; friend class CToplevelExportFrame; diff --git a/src/protocols/ToplevelMapping.cpp b/src/protocols/ToplevelMapping.cpp deleted file mode 100644 index 823956df..00000000 --- a/src/protocols/ToplevelMapping.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "ToplevelMapping.hpp" -#include "hyprland-toplevel-mapping-v1.hpp" -#include "ForeignToplevelWlr.hpp" -#include "ForeignToplevel.hpp" - -CToplevelWindowMappingHandle::CToplevelWindowMappingHandle(SP resource_) : m_resource(resource_) {} - -CToplevelMappingManager::CToplevelMappingManager(SP resource_) : m_resource(resource_) { - if UNLIKELY (!resource_->resource()) - return; - - m_resource->setOnDestroy([this](CHyprlandToplevelMappingManagerV1* h) { PROTO::toplevelMapping->onManagerResourceDestroy(this); }); - m_resource->setDestroy([this](CHyprlandToplevelMappingManagerV1* h) { PROTO::toplevelMapping->onManagerResourceDestroy(this); }); - - m_resource->setGetWindowForToplevel([this](CHyprlandToplevelMappingManagerV1* mgr, uint32_t handle, wl_resource* toplevel) { - const auto NEWHANDLE = PROTO::toplevelMapping->m_handles.emplace_back( - makeShared(makeShared(m_resource->client(), m_resource->version(), handle))); - - if UNLIKELY (!NEWHANDLE->m_resource->resource()) { - LOGM(Log::ERR, "Couldn't alloc mapping handle! (no memory)"); - m_resource->noMemory(); - return; - } - - NEWHANDLE->m_resource->setOnDestroy([](CHyprlandToplevelWindowMappingHandleV1* h) { PROTO::toplevelMapping->destroyHandle(h); }); - NEWHANDLE->m_resource->setDestroy([](CHyprlandToplevelWindowMappingHandleV1* h) { PROTO::toplevelMapping->destroyHandle(h); }); - - const auto WINDOW = PROTO::foreignToplevel->windowFromHandleResource(toplevel); - if (!WINDOW) - NEWHANDLE->m_resource->sendFailed(); - else - NEWHANDLE->m_resource->sendWindowAddress(rc(WINDOW.get()) >> 32 & 0xFFFFFFFF, rc(WINDOW.get()) & 0xFFFFFFFF); - }); - m_resource->setGetWindowForToplevelWlr([this](CHyprlandToplevelMappingManagerV1* mgr, uint32_t handle, wl_resource* toplevel) { - const auto NEWHANDLE = PROTO::toplevelMapping->m_handles.emplace_back( - makeShared(makeShared(m_resource->client(), m_resource->version(), handle))); - - if UNLIKELY (!NEWHANDLE->m_resource->resource()) { - LOGM(Log::ERR, "Couldn't alloc mapping handle! (no memory)"); - m_resource->noMemory(); - return; - } - - NEWHANDLE->m_resource->setOnDestroy([](CHyprlandToplevelWindowMappingHandleV1* h) { PROTO::toplevelMapping->destroyHandle(h); }); - NEWHANDLE->m_resource->setDestroy([](CHyprlandToplevelWindowMappingHandleV1* h) { PROTO::toplevelMapping->destroyHandle(h); }); - - const auto WINDOW = PROTO::foreignToplevelWlr->windowFromHandleResource(toplevel); - if (!WINDOW) - NEWHANDLE->m_resource->sendFailed(); - else - NEWHANDLE->m_resource->sendWindowAddress(rc(WINDOW.get()) >> 32 & 0xFFFFFFFF, rc(WINDOW.get()) & 0xFFFFFFFF); - }); -} - -bool CToplevelMappingManager::good() const { - return m_resource->resource(); -} - -CToplevelMappingProtocol::CToplevelMappingProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {} - -void CToplevelMappingProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(makeShared(client, ver, id))).get(); - - if UNLIKELY (!RESOURCE->good()) { - LOGM(Log::ERR, "Couldn't create a toplevel mapping manager"); - wl_client_post_no_memory(client); - m_managers.pop_back(); - return; - } -} - -void CToplevelMappingProtocol::onManagerResourceDestroy(CToplevelMappingManager* mgr) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == mgr; }); -} - -void CToplevelMappingProtocol::destroyHandle(CHyprlandToplevelWindowMappingHandleV1* handle) { - std::erase_if(m_handles, [&](const auto& other) { return other->m_resource.get() == handle; }); -} \ No newline at end of file diff --git a/src/protocols/ToplevelMapping.hpp b/src/protocols/ToplevelMapping.hpp deleted file mode 100644 index 65824a05..00000000 --- a/src/protocols/ToplevelMapping.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include "WaylandProtocol.hpp" -#include "hyprland-toplevel-mapping-v1.hpp" - -class CToplevelWindowMappingHandle { - public: - CToplevelWindowMappingHandle(SP resource_); - - private: - SP m_resource; - - friend class CToplevelMappingManager; - friend class CToplevelMappingProtocol; -}; - -class CToplevelMappingManager { - public: - CToplevelMappingManager(SP resource_); - - bool good() const; - - private: - SP m_resource; -}; - -class CToplevelMappingProtocol : IWaylandProtocol { - public: - CToplevelMappingProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - private: - void onManagerResourceDestroy(CToplevelMappingManager* mgr); - void destroyHandle(CHyprlandToplevelWindowMappingHandleV1* handle); - - std::vector> m_managers; - std::vector> m_handles; - - friend class CToplevelMappingManager; -}; - -namespace PROTO { - inline UP toplevelMapping; -}; \ No newline at end of file diff --git a/src/protocols/Viewporter.cpp b/src/protocols/Viewporter.cpp index 9612d3f8..d14e7ee5 100644 --- a/src/protocols/Viewporter.cpp +++ b/src/protocols/Viewporter.cpp @@ -1,25 +1,22 @@ #include "Viewporter.hpp" #include "core/Compositor.hpp" #include -#include -CViewportResource::CViewportResource(SP resource_, SP surface_) : m_surface(surface_), m_resource(resource_) { +CViewportResource::CViewportResource(SP resource_, SP surface_) : surface(surface_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CWpViewport* r) { PROTO::viewport->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpViewport* r) { PROTO::viewport->destroyResource(this); }); + resource->setDestroy([this](CWpViewport* r) { PROTO::viewport->destroyResource(this); }); + resource->setOnDestroy([this](CWpViewport* r) { PROTO::viewport->destroyResource(this); }); - m_resource->setSetDestination([this](CWpViewport* r, int32_t x, int32_t y) { - if UNLIKELY (!m_surface) { + resource->setSetDestination([this](CWpViewport* r, int32_t x, int32_t y) { + if UNLIKELY (!surface) { r->error(WP_VIEWPORT_ERROR_NO_SURFACE, "Surface is gone"); return; } - m_surface->m_pending.updated.bits.viewport = true; - if (x == -1 && y == -1) { - m_surface->m_pending.viewport.hasDestination = false; + surface->pending.viewport.hasDestination = false; return; } @@ -28,22 +25,20 @@ CViewportResource::CViewportResource(SP resource_, SPm_pending.viewport.hasDestination = true; - m_surface->m_pending.viewport.destination = {x, y}; + surface->pending.viewport.hasDestination = true; + surface->pending.viewport.destination = {x, y}; }); - m_resource->setSetSource([this](CWpViewport* r, wl_fixed_t fx, wl_fixed_t fy, wl_fixed_t fw, wl_fixed_t fh) { - if UNLIKELY (!m_surface) { + resource->setSetSource([this](CWpViewport* r, wl_fixed_t fx, wl_fixed_t fy, wl_fixed_t fw, wl_fixed_t fh) { + if UNLIKELY (!surface) { r->error(WP_VIEWPORT_ERROR_NO_SURFACE, "Surface is gone"); return; } - m_surface->m_pending.updated.bits.viewport = true; - double x = wl_fixed_to_double(fx), y = wl_fixed_to_double(fy), w = wl_fixed_to_double(fw), h = wl_fixed_to_double(fh); if (x == -1 && y == -1 && w == -1 && h == -1) { - m_surface->m_pending.viewport.hasSource = false; + surface->pending.viewport.hasSource = false; return; } @@ -52,42 +47,20 @@ CViewportResource::CViewportResource(SP resource_, SPm_pending.viewport.hasSource = true; - m_surface->m_pending.viewport.source = {x, y, w, h}; + surface->pending.viewport.hasSource = true; + surface->pending.viewport.source = {x, y, w, h}; }); - m_listeners.surfacePrecommit = m_surface->m_events.precommit.listen([this] { - if (!m_surface || !m_surface->m_pending.buffer) + listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) { + if (!surface || !surface->pending.buffer) return; - if (m_surface->m_pending.viewport.hasSource) { - auto& src = m_surface->m_pending.viewport.source; - const auto& size = m_surface->m_pending.bufferSize; + if (surface->pending.viewport.hasSource) { + auto& src = surface->pending.viewport.source; - if (size.x <= 0.0 || size.y <= 0.0) - return; - - constexpr wl_fixed_t MAX_TOLERANCE = 1 << 8; // wl_fixed 1.0 = 256 - - auto clampAxis = [&](double& start, double& length, double limit) -> bool { - const double originalStart = start; - const double originalEnd = start + length; - const double clampedStart = std::clamp(start, 0.0, std::max(0.0, limit)); - const double clampedEnd = std::clamp(originalEnd, 0.0, std::max(0.0, limit)); - const wl_fixed_t startDelta = std::abs(wl_fixed_from_double(clampedStart) - wl_fixed_from_double(originalStart)); - const wl_fixed_t endDelta = std::abs(wl_fixed_from_double(clampedEnd) - wl_fixed_from_double(originalEnd)); - - if (startDelta > MAX_TOLERANCE || endDelta > MAX_TOLERANCE) - return false; - - start = clampedStart; - length = clampedEnd - clampedStart; - return length > 0.0; - }; - - if (!clampAxis(src.x, src.w, size.x) || !clampAxis(src.y, src.h, size.y)) { - m_resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit"); - m_surface->m_pending.rejected = true; + if (src.w + src.x > surface->pending.bufferSize.x || src.h + src.y > surface->pending.bufferSize.y) { + resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit"); + surface->pending.rejected = true; return; } } @@ -95,38 +68,38 @@ CViewportResource::CViewportResource(SP resource_, SPm_pending.viewport.hasDestination = false; - m_surface->m_pending.viewport.hasSource = false; + surface->pending.viewport.hasDestination = false; + surface->pending.viewport.hasSource = false; } bool CViewportResource::good() { - return m_resource->resource(); + return resource->resource(); } -CViewporterResource::CViewporterResource(SP resource_) : m_resource(resource_) { +CViewporterResource::CViewporterResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CWpViewporter* r) { PROTO::viewport->destroyResource(this); }); - m_resource->setOnDestroy([this](CWpViewporter* r) { PROTO::viewport->destroyResource(this); }); + resource->setDestroy([this](CWpViewporter* r) { PROTO::viewport->destroyResource(this); }); + resource->setOnDestroy([this](CWpViewporter* r) { PROTO::viewport->destroyResource(this); }); - m_resource->setGetViewport([](CWpViewporter* r, uint32_t id, wl_resource* surf) { - const auto RESOURCE = PROTO::viewport->m_viewports.emplace_back( + resource->setGetViewport([](CWpViewporter* r, uint32_t id, wl_resource* surf) { + const auto RESOURCE = PROTO::viewport->m_vViewports.emplace_back( makeShared(makeShared(r->client(), r->version(), id), CWLSurfaceResource::fromResource(surf))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::viewport->m_viewports.pop_back(); + PROTO::viewport->m_vViewports.pop_back(); return; } }); } bool CViewporterResource::good() { - return m_resource->resource(); + return resource->resource(); } CViewporterProtocol::CViewporterProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -134,19 +107,19 @@ CViewporterProtocol::CViewporterProtocol(const wl_interface* iface, const int& v } void CViewporterProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CViewporterProtocol::destroyResource(CViewporterResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CViewporterProtocol::destroyResource(CViewportResource* resource) { - std::erase_if(m_viewports, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vViewports, [&](const auto& other) { return other.get() == resource; }); } diff --git a/src/protocols/Viewporter.hpp b/src/protocols/Viewporter.hpp index d967da74..6824f2ae 100644 --- a/src/protocols/Viewporter.hpp +++ b/src/protocols/Viewporter.hpp @@ -14,14 +14,14 @@ class CViewportResource { ~CViewportResource(); bool good(); - WP m_surface; + WP surface; private: - SP m_resource; + SP resource; struct { CHyprSignalListener surfacePrecommit; - } m_listeners; + } listeners; }; class CViewporterResource { @@ -31,7 +31,7 @@ class CViewporterResource { bool good(); private: - SP m_resource; + SP resource; }; class CViewporterProtocol : public IWaylandProtocol { @@ -45,8 +45,8 @@ class CViewporterProtocol : public IWaylandProtocol { void destroyResource(CViewportResource* resource); // - std::vector> m_managers; - std::vector> m_viewports; + std::vector> m_vManagers; + std::vector> m_vViewports; friend class CViewporterResource; friend class CViewportResource; diff --git a/src/protocols/VirtualKeyboard.cpp b/src/protocols/VirtualKeyboard.cpp index 2f7e0bd1..00aca041 100644 --- a/src/protocols/VirtualKeyboard.cpp +++ b/src/protocols/VirtualKeyboard.cpp @@ -1,69 +1,49 @@ #include "VirtualKeyboard.hpp" -#include #include -#include "../config/ConfigValue.hpp" -#include "../config/ConfigManager.hpp" #include "../devices/IKeyboard.hpp" -#include "../helpers/time/Time.hpp" -#include "../helpers/MiscFunctions.hpp" using namespace Hyprutils::OS; -static std::string virtualKeyboardNameForWlClient(wl_client* client) { - std::string name = "hl-virtual-keyboard"; - - static auto PVKNAMEPROC = CConfigValue("misc:name_vk_after_proc"); - if (!*PVKNAMEPROC) - return name; - - name += "-"; - const auto CLIENTNAME = binaryNameForWlClient(client); - if (CLIENTNAME.has_value()) { - const auto PATH = std::filesystem::path(CLIENTNAME.value()); - if (PATH.has_filename()) { - const auto FILENAME = PATH.filename(); - const auto NAME = deviceNameToInternalString(FILENAME); - name += NAME; - return name; - } - } - - name += "unknown"; - return name; -} - -CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP resource_) : m_resource(resource_) { +CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpVirtualKeyboardV1* r) { destroy(); }); - m_resource->setOnDestroy([this](CZwpVirtualKeyboardV1* r) { destroy(); }); + resource->setDestroy([this](CZwpVirtualKeyboardV1* r) { + releasePressed(); + events.destroy.emit(); + PROTO::virtualKeyboard->destroyResource(this); + }); + resource->setOnDestroy([this](CZwpVirtualKeyboardV1* r) { + releasePressed(); + events.destroy.emit(); + PROTO::virtualKeyboard->destroyResource(this); + }); - m_resource->setKey([this](CZwpVirtualKeyboardV1* r, uint32_t timeMs, uint32_t key, uint32_t state) { - if UNLIKELY (!m_hasKeymap) { + resource->setKey([this](CZwpVirtualKeyboardV1* r, uint32_t timeMs, uint32_t key, uint32_t state) { + if UNLIKELY (!hasKeymap) { r->error(ZWP_VIRTUAL_KEYBOARD_V1_ERROR_NO_KEYMAP, "Key event received before a keymap was set"); return; } - m_events.key.emit(IKeyboard::SKeyEvent{ + events.key.emit(IKeyboard::SKeyEvent{ .timeMs = timeMs, .keycode = key, - .state = sc(state), + .state = (wl_keyboard_key_state)state, }); - const bool CONTAINS = std::ranges::contains(m_pressed, key); + const bool CONTAINS = std::find(pressed.begin(), pressed.end(), key) != pressed.end(); if (state && !CONTAINS) - m_pressed.emplace_back(key); + pressed.emplace_back(key); else if (!state && CONTAINS) - std::erase(m_pressed, key); + std::erase(pressed, key); }); - m_resource->setModifiers([this](CZwpVirtualKeyboardV1* r, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { - if UNLIKELY (!m_hasKeymap) { + resource->setModifiers([this](CZwpVirtualKeyboardV1* r, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { + if UNLIKELY (!hasKeymap) { r->error(ZWP_VIRTUAL_KEYBOARD_V1_ERROR_NO_KEYMAP, "Mods event received before a keymap was set"); return; } - m_events.modifiers.emit(IKeyboard::SModifiersEvent{ + events.modifiers.emit(IKeyboard::SModifiersEvent{ .depressed = depressed, .latched = latched, .locked = locked, @@ -71,75 +51,70 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP }); }); - m_resource->setKeymap([this](CZwpVirtualKeyboardV1* r, uint32_t fmt, int32_t fd, uint32_t len) { + resource->setKeymap([this](CZwpVirtualKeyboardV1* r, uint32_t fmt, int32_t fd, uint32_t len) { auto xkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS); CFileDescriptor keymapFd{fd}; if UNLIKELY (!xkbContext) { - LOGM(Log::ERR, "xkbContext creation failed"); + LOGM(ERR, "xkbContext creation failed"); r->noMemory(); return; } auto keymapData = mmap(nullptr, len, PROT_READ, MAP_PRIVATE, keymapFd.get(), 0); if UNLIKELY (keymapData == MAP_FAILED) { - LOGM(Log::ERR, "keymapData alloc failed"); + LOGM(ERR, "keymapData alloc failed"); xkb_context_unref(xkbContext); r->noMemory(); return; } - auto xkbKeymap = xkb_keymap_new_from_string(xkbContext, sc(keymapData), XKB_KEYMAP_FORMAT_TEXT_V2, XKB_KEYMAP_COMPILE_NO_FLAGS); + auto xkbKeymap = xkb_keymap_new_from_string(xkbContext, (const char*)keymapData, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); munmap(keymapData, len); if UNLIKELY (!xkbKeymap) { - LOGM(Log::ERR, "xkbKeymap creation failed"); + LOGM(ERR, "xkbKeymap creation failed"); xkb_context_unref(xkbContext); r->noMemory(); return; } - m_events.keymap.emit(IKeyboard::SKeymapEvent{ + events.keymap.emit(IKeyboard::SKeymapEvent{ .keymap = xkbKeymap, }); - m_hasKeymap = true; + hasKeymap = true; xkb_keymap_unref(xkbKeymap); xkb_context_unref(xkbContext); }); - m_name = virtualKeyboardNameForWlClient(resource_->client()); + name = "hl-virtual-keyboard"; } CVirtualKeyboardV1Resource::~CVirtualKeyboardV1Resource() { - m_events.destroy.emit(); + events.destroy.emit(); } bool CVirtualKeyboardV1Resource::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CVirtualKeyboardV1Resource::client() { - return m_resource->resource() ? m_resource->client() : nullptr; + return resource->resource() ? resource->client() : nullptr; } void CVirtualKeyboardV1Resource::releasePressed() { - for (auto const& p : m_pressed) { - m_events.key.emit(IKeyboard::SKeyEvent{ - .timeMs = Time::millis(Time::steadyNow()), + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + for (auto const& p : pressed) { + events.key.emit(IKeyboard::SKeyEvent{ + .timeMs = now.tv_sec * 1000 + now.tv_nsec / 1000000, .keycode = p, .state = WL_KEYBOARD_KEY_STATE_RELEASED, }); } - m_pressed.clear(); -} - -void CVirtualKeyboardV1Resource::destroy() { - const auto RELEASEPRESSED = g_pConfigManager->getDeviceInt(m_name, "release_pressed_on_close", "input:virtualkeyboard:release_pressed_on_close"); - if (RELEASEPRESSED) - releasePressed(); - m_events.destroy.emit(); - PROTO::virtualKeyboard->destroyResource(this); + pressed.clear(); } CVirtualKeyboardProtocol::CVirtualKeyboardProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -147,31 +122,31 @@ CVirtualKeyboardProtocol::CVirtualKeyboardProtocol(const wl_interface* iface, co } void CVirtualKeyboardProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwpVirtualKeyboardManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setCreateVirtualKeyboard([this](CZwpVirtualKeyboardManagerV1* pMgr, wl_resource* seat, uint32_t id) { this->onCreateKeeb(pMgr, seat, id); }); } void CVirtualKeyboardProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CVirtualKeyboardProtocol::destroyResource(CVirtualKeyboardV1Resource* keeb) { - std::erase_if(m_keyboards, [&](const auto& other) { return other.get() == keeb; }); + std::erase_if(m_vKeyboards, [&](const auto& other) { return other.get() == keeb; }); } void CVirtualKeyboardProtocol::onCreateKeeb(CZwpVirtualKeyboardManagerV1* pMgr, wl_resource* seat, uint32_t id) { - const auto RESOURCE = m_keyboards.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id))); + const auto RESOURCE = m_vKeyboards.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id))); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_keyboards.pop_back(); + m_vKeyboards.pop_back(); return; } - LOGM(Log::DEBUG, "New VKeyboard at id {}", id); + LOGM(LOG, "New VKeyboard at id {}", id); - m_events.newKeyboard.emit(RESOURCE); + events.newKeyboard.emit(RESOURCE); } diff --git a/src/protocols/VirtualKeyboard.hpp b/src/protocols/VirtualKeyboard.hpp index 1ff9f161..0a13003b 100644 --- a/src/protocols/VirtualKeyboard.hpp +++ b/src/protocols/VirtualKeyboard.hpp @@ -3,8 +3,6 @@ #include #include #include "WaylandProtocol.hpp" -#include "../devices/IKeyboard.hpp" -#include "../devices/VirtualKeyboard.hpp" #include "virtual-keyboard-unstable-v1.hpp" #include "../helpers/signal/Signal.hpp" #include @@ -15,26 +13,25 @@ class CVirtualKeyboardV1Resource { ~CVirtualKeyboardV1Resource(); struct { - CSignalT<> destroy; - CSignalT key; - CSignalT modifiers; - CSignalT keymap; - } m_events; + CSignal destroy; + CSignal key; + CSignal modifiers; + CSignal keymap; + } events; bool good(); wl_client* client(); - std::string m_name = ""; + std::string name = ""; private: - SP m_resource; + SP resource; void releasePressed(); - void destroy(); - bool m_hasKeymap = false; + bool hasKeymap = false; - std::vector m_pressed; + std::vector pressed; }; class CVirtualKeyboardProtocol : public IWaylandProtocol { @@ -44,8 +41,8 @@ class CVirtualKeyboardProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); struct { - CSignalT> newKeyboard; - } m_events; + CSignal newKeyboard; // SP + } events; private: void onManagerResourceDestroy(wl_resource* res); @@ -53,8 +50,8 @@ class CVirtualKeyboardProtocol : public IWaylandProtocol { void onCreateKeeb(CZwpVirtualKeyboardManagerV1* pMgr, wl_resource* seat, uint32_t id); // - std::vector> m_managers; - std::vector> m_keyboards; + std::vector> m_vManagers; + std::vector> m_vKeyboards; friend class CVirtualKeyboardV1Resource; }; diff --git a/src/protocols/VirtualPointer.cpp b/src/protocols/VirtualPointer.cpp index 075f7cb9..8dc4bab6 100644 --- a/src/protocols/VirtualPointer.cpp +++ b/src/protocols/VirtualPointer.cpp @@ -1,21 +1,21 @@ #include "VirtualPointer.hpp" #include "core/Output.hpp" -CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP resource_, PHLMONITORREF boundOutput_) : m_boundOutput(boundOutput_), m_resource(resource_) { +CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP resource_, PHLMONITORREF boundOutput_) : boundOutput(boundOutput_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwlrVirtualPointerV1* r) { - m_events.destroy.emit(); + resource->setDestroy([this](CZwlrVirtualPointerV1* r) { + events.destroy.emit(); PROTO::virtualPointer->destroyResource(this); }); - m_resource->setOnDestroy([this](CZwlrVirtualPointerV1* r) { - m_events.destroy.emit(); + resource->setOnDestroy([this](CZwlrVirtualPointerV1* r) { + events.destroy.emit(); PROTO::virtualPointer->destroyResource(this); }); - m_resource->setMotion([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, wl_fixed_t dx, wl_fixed_t dy) { - m_events.move.emit(IPointer::SMotionEvent{ + resource->setMotion([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, wl_fixed_t dx, wl_fixed_t dy) { + events.move.emit(IPointer::SMotionEvent{ .timeMs = timeMs, .delta = {wl_fixed_to_double(dx), wl_fixed_to_double(dy)}, .unaccel = {wl_fixed_to_double(dx), wl_fixed_to_double(dy)}, @@ -23,84 +23,84 @@ CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP r }); }); - m_resource->setMotionAbsolute([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t x, uint32_t y, uint32_t xExtent, uint32_t yExtent) { + resource->setMotionAbsolute([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t x, uint32_t y, uint32_t xExtent, uint32_t yExtent) { if (!xExtent || !yExtent) return; - m_events.warp.emit(IPointer::SMotionAbsoluteEvent{ + events.warp.emit(IPointer::SMotionAbsoluteEvent{ .timeMs = timeMs, - .absolute = {sc(x) / xExtent, sc(y) / yExtent}, + .absolute = {(double)x / xExtent, (double)y / yExtent}, }); }); - m_resource->setButton([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t button, uint32_t state) { - m_events.button.emit(IPointer::SButtonEvent{ + resource->setButton([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t button, uint32_t state) { + events.button.emit(IPointer::SButtonEvent{ .timeMs = timeMs, .button = button, - .state = sc(state), + .state = (wl_pointer_button_state)state, }); }); - m_resource->setAxis([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_, wl_fixed_t value) { - if UNLIKELY (m_axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + resource->setAxis([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_, wl_fixed_t value) { + if UNLIKELY (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { r->error(ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid axis"); return; } - m_axis = axis_; - m_axisEvents[m_axis] = IPointer::SAxisEvent{.timeMs = timeMs, .axis = sc(m_axis), .delta = wl_fixed_to_double(value)}; + axis = axis_; + axisEvents[axis] = IPointer::SAxisEvent{.timeMs = timeMs, .axis = (wl_pointer_axis)axis, .delta = wl_fixed_to_double(value)}; }); - m_resource->setFrame([this](CZwlrVirtualPointerV1* r) { - for (auto& e : m_axisEvents) { + resource->setFrame([this](CZwlrVirtualPointerV1* r) { + for (auto& e : axisEvents) { if (!e.timeMs) continue; - m_events.axis.emit(e); + events.axis.emit(e); e.timeMs = 0; } - m_events.frame.emit(); + events.frame.emit(); }); - m_resource->setAxisSource([this](CZwlrVirtualPointerV1* r, uint32_t source) { m_axisEvents[m_axis].source = sc(source); }); + resource->setAxisSource([this](CZwlrVirtualPointerV1* r, uint32_t source) { axisEvents[axis].source = (wl_pointer_axis_source)source; }); - m_resource->setAxisStop([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_) { - if UNLIKELY (m_axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + resource->setAxisStop([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_) { + if UNLIKELY (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { r->error(ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid axis"); return; } - m_axis = axis_; - m_axisEvents[m_axis].timeMs = timeMs; - m_axisEvents[m_axis].axis = sc(m_axis); - m_axisEvents[m_axis].delta = 0; - m_axisEvents[m_axis].deltaDiscrete = 0; + axis = axis_; + axisEvents[axis].timeMs = timeMs; + axisEvents[axis].axis = (wl_pointer_axis)axis; + axisEvents[axis].delta = 0; + axisEvents[axis].deltaDiscrete = 0; }); - m_resource->setAxisDiscrete([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_, wl_fixed_t value, int32_t discrete) { - if UNLIKELY (m_axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + resource->setAxisDiscrete([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_, wl_fixed_t value, int32_t discrete) { + if UNLIKELY (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { r->error(ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid axis"); return; } - m_axis = axis_; - m_axisEvents[m_axis].timeMs = timeMs; - m_axisEvents[m_axis].axis = sc(m_axis); - m_axisEvents[m_axis].delta = wl_fixed_to_double(value); - m_axisEvents[m_axis].deltaDiscrete = discrete * 120; + axis = axis_; + axisEvents[axis].timeMs = timeMs; + axisEvents[axis].axis = (wl_pointer_axis)axis; + axisEvents[axis].delta = wl_fixed_to_double(value); + axisEvents[axis].deltaDiscrete = discrete * 120; }); } CVirtualPointerV1Resource::~CVirtualPointerV1Resource() { - m_events.destroy.emit(); + events.destroy.emit(); } bool CVirtualPointerV1Resource::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CVirtualPointerV1Resource::client() { - return m_resource->client(); + return resource->client(); } CVirtualPointerProtocol::CVirtualPointerProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -108,7 +108,7 @@ CVirtualPointerProtocol::CVirtualPointerProtocol(const wl_interface* iface, cons } void CVirtualPointerProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZwlrVirtualPointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZwlrVirtualPointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); @@ -121,31 +121,31 @@ void CVirtualPointerProtocol::bindManager(wl_client* client, void* data, uint32_ return; } - this->onCreatePointer(pMgr, seat, id, RES->m_monitor); + this->onCreatePointer(pMgr, seat, id, RES->monitor); } else this->onCreatePointer(pMgr, seat, id, {}); }); } void CVirtualPointerProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CVirtualPointerProtocol::destroyResource(CVirtualPointerV1Resource* pointer) { - std::erase_if(m_pointers, [&](const auto& other) { return other.get() == pointer; }); + std::erase_if(m_vPointers, [&](const auto& other) { return other.get() == pointer; }); } void CVirtualPointerProtocol::onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id, PHLMONITORREF output) { - const auto RESOURCE = m_pointers.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id), output)); + const auto RESOURCE = m_vPointers.emplace_back(makeShared(makeShared(pMgr->client(), pMgr->version(), id), output)); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_pointers.pop_back(); + m_vPointers.pop_back(); return; } - LOGM(Log::DEBUG, "New VPointer at id {}", id); + LOGM(LOG, "New VPointer at id {}", id); - m_events.newPointer.emit(RESOURCE); -} + events.newPointer.emit(RESOURCE); +} \ No newline at end of file diff --git a/src/protocols/VirtualPointer.hpp b/src/protocols/VirtualPointer.hpp index 96f31d81..ac8bcae2 100644 --- a/src/protocols/VirtualPointer.hpp +++ b/src/protocols/VirtualPointer.hpp @@ -15,38 +15,38 @@ class CVirtualPointerV1Resource { ~CVirtualPointerV1Resource(); struct { - CSignalT<> destroy; - CSignalT move; - CSignalT warp; - CSignalT button; - CSignalT axis; - CSignalT<> frame; + CSignal destroy; + CSignal move; + CSignal warp; + CSignal button; + CSignal axis; + CSignal frame; - CSignalT swipeBegin; - CSignalT swipeUpdate; - CSignalT swipeEnd; + CSignal swipeBegin; + CSignal swipeUpdate; + CSignal swipeEnd; - CSignalT pinchBegin; - CSignalT pinchUpdate; - CSignalT pinchEnd; + CSignal pinchBegin; + CSignal pinchUpdate; + CSignal pinchEnd; - CSignalT holdBegin; - CSignalT holdEnd; - } m_events; + CSignal holdBegin; + CSignal holdEnd; + } events; bool good(); wl_client* client(); - std::string m_name; + std::string name; - PHLMONITORREF m_boundOutput; + PHLMONITORREF boundOutput; private: - SP m_resource; + SP resource; - uint32_t m_axis = 0; + uint32_t axis = 0; - std::array m_axisEvents; + std::array axisEvents; }; class CVirtualPointerProtocol : public IWaylandProtocol { @@ -56,8 +56,8 @@ class CVirtualPointerProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); struct { - CSignalT> newPointer; - } m_events; + CSignal newPointer; // SP + } events; private: void onManagerResourceDestroy(wl_resource* res); @@ -65,8 +65,8 @@ class CVirtualPointerProtocol : public IWaylandProtocol { void onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id, PHLMONITORREF output); // - std::vector> m_managers; - std::vector> m_pointers; + std::vector> m_vManagers; + std::vector> m_vPointers; friend class CVirtualPointerV1Resource; }; diff --git a/src/protocols/WaylandProtocol.cpp b/src/protocols/WaylandProtocol.cpp index 4cb4f991..48bd6d2a 100644 --- a/src/protocols/WaylandProtocol.cpp +++ b/src/protocols/WaylandProtocol.cpp @@ -2,7 +2,7 @@ #include "../Compositor.hpp" static void bindManagerInternal(wl_client* client, void* data, uint32_t ver, uint32_t id) { - sc(data)->bindManager(client, data, ver, id); + ((IWaylandProtocol*)data)->bindManager(client, data, ver, id); } static void displayDestroyInternal(struct wl_listener* listener, void* data) { @@ -14,26 +14,26 @@ static void displayDestroyInternal(struct wl_listener* listener, void* data) { void IWaylandProtocol::onDisplayDestroy() { wl_list_remove(&m_liDisplayDestroy.listener.link); wl_list_init(&m_liDisplayDestroy.listener.link); - if (m_global) { - wl_global_destroy(m_global); - m_global = nullptr; + if (m_pGlobal) { + wl_global_destroy(m_pGlobal); + m_pGlobal = nullptr; } } IWaylandProtocol::IWaylandProtocol(const wl_interface* iface, const int& ver, const std::string& name) : - m_name(name), m_global(wl_global_create(g_pCompositor->m_wlDisplay, iface, ver, this, &bindManagerInternal)) { + m_szName(name), m_pGlobal(wl_global_create(g_pCompositor->m_sWLDisplay, iface, ver, this, &bindManagerInternal)) { - if UNLIKELY (!m_global) { - LOGM(Log::ERR, "could not create a global [{}]", m_name); + if UNLIKELY (!m_pGlobal) { + LOGM(ERR, "could not create a global [{}]", m_szName); return; } wl_list_init(&m_liDisplayDestroy.listener.link); m_liDisplayDestroy.listener.notify = displayDestroyInternal; m_liDisplayDestroy.parent = this; - wl_display_add_destroy_listener(g_pCompositor->m_wlDisplay, &m_liDisplayDestroy.listener); + wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy.listener); - LOGM(Log::DEBUG, "Registered global [{}]", m_name); + LOGM(LOG, "Registered global [{}]", m_szName); } IWaylandProtocol::~IWaylandProtocol() { @@ -41,10 +41,10 @@ IWaylandProtocol::~IWaylandProtocol() { } void IWaylandProtocol::removeGlobal() { - if (m_global) - wl_global_remove(m_global); + if (m_pGlobal) + wl_global_remove(m_pGlobal); } wl_global* IWaylandProtocol::getGlobal() { - return m_global; + return m_pGlobal; } diff --git a/src/protocols/WaylandProtocol.hpp b/src/protocols/WaylandProtocol.hpp index 5c187c7d..918ac70f 100644 --- a/src/protocols/WaylandProtocol.hpp +++ b/src/protocols/WaylandProtocol.hpp @@ -4,7 +4,6 @@ #include "../helpers/memory/Memory.hpp" #include -#include #define RESOURCE_OR_BAIL(resname) \ const auto resname = (CWaylandResource*)wl_resource_get_user_data(resource); \ @@ -29,16 +28,16 @@ #define LOGM(level, ...) \ do { \ std::ostringstream oss; \ - if (level == Log::WARN || level == Log::ERR || level == Log::CRIT) { \ + if (level == WARN || level == ERR || level == CRIT) { \ oss << "[" << __FILE__ << ":" << __LINE__ << "] "; \ - } else if (level == Log::DEBUG || level == Log::INFO || level == Log::TRACE) { \ + } else if (level == LOG || level == INFO || level == TRACE) { \ oss << "[" << EXTRACT_CLASS_NAME() << "] "; \ } \ - if constexpr (std::tuple_size_v == 1 && std::is_same_v) { \ + if constexpr (std::tuple_size::value == 1 && std::is_same_v) { \ oss << __VA_ARGS__; \ - Log::logger->log(level, oss.str()); \ + Debug::log(level, oss.str()); \ } else { \ - Log::logger->log(level, std::format("{}{}", oss.str(), std::format(__VA_ARGS__))); \ + Debug::log(level, std::format("{}{}", oss.str(), std::format(__VA_ARGS__))); \ } \ } while (0) @@ -62,6 +61,6 @@ class IWaylandProtocol { SIWaylandProtocolDestroyWrapper m_liDisplayDestroy; private: - std::string m_name; - wl_global* m_global = nullptr; + std::string m_szName; + wl_global* m_pGlobal = nullptr; }; diff --git a/src/protocols/XDGActivation.cpp b/src/protocols/XDGActivation.cpp index 3d094fca..4062847b 100644 --- a/src/protocols/XDGActivation.cpp +++ b/src/protocols/XDGActivation.cpp @@ -4,42 +4,43 @@ #include "core/Compositor.hpp" #include -CXDGActivationToken::CXDGActivationToken(SP resource_) : m_resource(resource_) { +CXDGActivationToken::CXDGActivationToken(SP resource_) : resource(resource_) { if UNLIKELY (!resource_->resource()) return; - m_resource->setDestroy([this](CXdgActivationTokenV1* r) { PROTO::activation->destroyToken(this); }); - m_resource->setOnDestroy([this](CXdgActivationTokenV1* r) { PROTO::activation->destroyToken(this); }); + resource->setDestroy([this](CXdgActivationTokenV1* r) { PROTO::activation->destroyToken(this); }); + resource->setOnDestroy([this](CXdgActivationTokenV1* r) { PROTO::activation->destroyToken(this); }); - m_resource->setSetSerial([this](CXdgActivationTokenV1* r, uint32_t serial_, wl_resource* seat) { m_serial = serial_; }); + resource->setSetSerial([this](CXdgActivationTokenV1* r, uint32_t serial_, wl_resource* seat) { serial = serial_; }); - m_resource->setSetAppId([this](CXdgActivationTokenV1* r, const char* appid) { m_appID = appid; }); + resource->setSetAppId([this](CXdgActivationTokenV1* r, const char* appid) { appID = appid; }); - m_resource->setCommit([this](CXdgActivationTokenV1* r) { + resource->setCommit([this](CXdgActivationTokenV1* r) { // TODO: should we send a protocol error of already_used here // if it was used? the protocol spec doesn't say _when_ it should be sent... - if UNLIKELY (m_committed) { - LOGM(Log::WARN, "possible protocol error, two commits from one token. Ignoring."); + if UNLIKELY (committed) { + LOGM(WARN, "possible protocol error, two commits from one token. Ignoring."); return; } - m_committed = true; + committed = true; // send done with a new token - m_token = g_pTokenManager->registerNewToken({}, std::chrono::months{12}); + token = g_pTokenManager->registerNewToken({}, std::chrono::months{12}); - LOGM(Log::DEBUG, "assigned new xdg-activation token {}", m_token); + LOGM(LOG, "assigned new xdg-activation token {}", token); - m_resource->sendDone(m_token.c_str()); + resource->sendDone(token.c_str()); - PROTO::activation->m_sentTokens.push_back({m_token, m_resource->client()}); + PROTO::activation->m_vSentTokens.push_back({token, resource->client()}); - auto count = std::ranges::count_if(PROTO::activation->m_sentTokens, [this](const auto& other) { return other.client == m_resource->client(); }); + auto count = std::count_if(PROTO::activation->m_vSentTokens.begin(), PROTO::activation->m_vSentTokens.end(), + [this](const auto& other) { return other.client == resource->client(); }); if UNLIKELY (count > 10) { // remove first token. Too many, dear app. - for (auto i = PROTO::activation->m_sentTokens.begin(); i != PROTO::activation->m_sentTokens.end(); ++i) { - if (i->client == m_resource->client()) { - PROTO::activation->m_sentTokens.erase(i); + for (auto i = PROTO::activation->m_vSentTokens.begin(); i != PROTO::activation->m_vSentTokens.end(); ++i) { + if (i->client == resource->client()) { + PROTO::activation->m_vSentTokens.erase(i); break; } } @@ -48,12 +49,12 @@ CXDGActivationToken::CXDGActivationToken(SP resource_) : } CXDGActivationToken::~CXDGActivationToken() { - if (m_committed) - g_pTokenManager->removeToken(g_pTokenManager->getToken(m_token)); + if (committed) + g_pTokenManager->removeToken(g_pTokenManager->getToken(token)); } bool CXDGActivationToken::good() { - return m_resource->resource(); + return resource->resource(); } CXDGActivationProtocol::CXDGActivationProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -61,27 +62,27 @@ CXDGActivationProtocol::CXDGActivationProtocol(const wl_interface* iface, const } void CXDGActivationProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CXdgActivationV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CXdgActivationV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); RESOURCE->setGetActivationToken([this](CXdgActivationV1* pMgr, uint32_t id) { this->onGetToken(pMgr, id); }); RESOURCE->setActivate([this](CXdgActivationV1* pMgr, const char* token, wl_resource* surface) { - auto TOKEN = std::ranges::find_if(m_sentTokens, [token](const auto& t) { return t.token == token; }); + auto TOKEN = std::find_if(m_vSentTokens.begin(), m_vSentTokens.end(), [token](const auto& t) { return t.token == token; }); - if UNLIKELY (TOKEN == m_sentTokens.end()) { - LOGM(Log::WARN, "activate event for non-existent token {}??", token); + if UNLIKELY (TOKEN == m_vSentTokens.end()) { + LOGM(WARN, "activate event for non-existent token {}??", token); return; } // remove token. It's been now spent. - m_sentTokens.erase(TOKEN); + m_vSentTokens.erase(TOKEN); SP surf = CWLSurfaceResource::fromResource(surface); const auto PWINDOW = g_pCompositor->getWindowFromSurface(surf); if UNLIKELY (!PWINDOW) { - LOGM(Log::WARN, "activate event for non-window or gone surface with token {}, ignoring", token); + LOGM(WARN, "activate event for non-window or gone surface with token {}, ignoring", token); return; } @@ -90,20 +91,20 @@ void CXDGActivationProtocol::bindManager(wl_client* client, void* data, uint32_t } void CXDGActivationProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CXDGActivationProtocol::destroyToken(CXDGActivationToken* token) { - std::erase_if(m_tokens, [&](const auto& other) { return other.get() == token; }); + std::erase_if(m_vTokens, [&](const auto& other) { return other.get() == token; }); } void CXDGActivationProtocol::onGetToken(CXdgActivationV1* pMgr, uint32_t id) { const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_tokens.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); + const auto RESOURCE = m_vTokens.emplace_back(makeUnique(makeShared(CLIENT, pMgr->version(), id))).get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_tokens.pop_back(); + m_vTokens.pop_back(); return; } } \ No newline at end of file diff --git a/src/protocols/XDGActivation.hpp b/src/protocols/XDGActivation.hpp index ae1bc07a..acc6fdb3 100644 --- a/src/protocols/XDGActivation.hpp +++ b/src/protocols/XDGActivation.hpp @@ -13,13 +13,13 @@ class CXDGActivationToken { bool good(); private: - SP m_resource; + SP resource; - uint32_t m_serial = 0; - std::string m_appID = ""; - bool m_committed = false; + uint32_t serial = 0; + std::string appID = ""; + bool committed = false; - std::string m_token = ""; + std::string token = ""; friend class CXDGActivationProtocol; }; @@ -39,11 +39,11 @@ class CXDGActivationProtocol : public IWaylandProtocol { std::string token; wl_client* client = nullptr; // READ-ONLY: can be dead }; - std::vector m_sentTokens; + std::vector m_vSentTokens; // - std::vector> m_managers; - std::vector> m_tokens; + std::vector> m_vManagers; + std::vector> m_vTokens; friend class CXDGActivationToken; }; diff --git a/src/protocols/XDGBell.cpp b/src/protocols/XDGBell.cpp deleted file mode 100644 index b53621d5..00000000 --- a/src/protocols/XDGBell.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "XDGBell.hpp" -#include "core/Compositor.hpp" -#include "../desktop/view/Window.hpp" -#include "../managers/EventManager.hpp" -#include "../Compositor.hpp" - -CXDGSystemBellManagerResource::CXDGSystemBellManagerResource(UP&& resource) : m_resource(std::move(resource)) { - if UNLIKELY (!good()) - return; - - m_resource->setDestroy([this](CXdgSystemBellV1* r) { PROTO::xdgBell->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgSystemBellV1* r) { PROTO::xdgBell->destroyResource(this); }); - - m_resource->setRing([](CXdgSystemBellV1* r, wl_resource* surface) { - if (!surface) { - g_pEventManager->postEvent(SHyprIPCEvent{ - .event = "bell", - .data = "", - }); - return; - } - - const auto SURFACE = CWLSurfaceResource::fromResource(surface); - - if (!SURFACE) { - g_pEventManager->postEvent(SHyprIPCEvent{ - .event = "bell", - .data = "", - }); - return; - } - - for (const auto& w : g_pCompositor->m_windows) { - if (!w->m_isMapped || w->m_isX11 || !w->m_xdgSurface || !w->wlSurface()) - continue; - - if (w->wlSurface()->resource() == SURFACE) { - g_pEventManager->postEvent(SHyprIPCEvent{ - .event = "bell", - .data = std::format("{:x}", rc(w.get())), - }); - return; - } - } - - g_pEventManager->postEvent(SHyprIPCEvent{ - .event = "bell", - .data = "", - }); - }); -} - -bool CXDGSystemBellManagerResource::good() { - return m_resource->resource(); -} - -CXDGSystemBellProtocol::CXDGSystemBellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; -} - -void CXDGSystemBellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = WP{m_managers.emplace_back(makeUnique(makeUnique(client, ver, id)))}; - - if UNLIKELY (!RESOURCE->good()) { - wl_client_post_no_memory(client); - return; - } -} - -void CXDGSystemBellProtocol::destroyResource(CXDGSystemBellManagerResource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); -} diff --git a/src/protocols/XDGBell.hpp b/src/protocols/XDGBell.hpp deleted file mode 100644 index f36315bd..00000000 --- a/src/protocols/XDGBell.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include "WaylandProtocol.hpp" -#include "xdg-system-bell-v1.hpp" - -class CXDGSystemBellManagerResource { - public: - CXDGSystemBellManagerResource(UP&& resource); - - bool good(); - - private: - UP m_resource; -}; - -class CXDGSystemBellProtocol : public IWaylandProtocol { - public: - CXDGSystemBellProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - private: - void destroyResource(CXDGSystemBellManagerResource* res); - - // - std::vector> m_managers; - - friend class CXDGSystemBellManagerResource; -}; - -namespace PROTO { - inline UP xdgBell; -}; diff --git a/src/protocols/XDGDecoration.cpp b/src/protocols/XDGDecoration.cpp index e711ab3b..f9dbf411 100644 --- a/src/protocols/XDGDecoration.cpp +++ b/src/protocols/XDGDecoration.cpp @@ -1,14 +1,14 @@ #include "XDGDecoration.hpp" #include -CXDGDecoration::CXDGDecoration(SP resource_, wl_resource* toplevel) : m_resource(resource_), m_toplevelResource(toplevel) { - if UNLIKELY (!m_resource->resource()) +CXDGDecoration::CXDGDecoration(SP resource_, wl_resource* toplevel) : resource(resource_), pToplevelResource(toplevel) { + if UNLIKELY (!resource->resource()) return; - m_resource->setDestroy([this](CZxdgToplevelDecorationV1* pMgr) { PROTO::xdgDecoration->destroyDecoration(this); }); - m_resource->setOnDestroy([this](CZxdgToplevelDecorationV1* pMgr) { PROTO::xdgDecoration->destroyDecoration(this); }); + resource->setDestroy([this](CZxdgToplevelDecorationV1* pMgr) { PROTO::xdgDecoration->destroyDecoration(this); }); + resource->setOnDestroy([this](CZxdgToplevelDecorationV1* pMgr) { PROTO::xdgDecoration->destroyDecoration(this); }); - m_resource->setSetMode([this](CZxdgToplevelDecorationV1*, zxdgToplevelDecorationV1Mode mode) { + resource->setSetMode([this](CZxdgToplevelDecorationV1*, zxdgToplevelDecorationV1Mode mode) { std::string modeString; switch (mode) { case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: modeString = "MODE_CLIENT_SIDE"; break; @@ -16,44 +16,24 @@ CXDGDecoration::CXDGDecoration(SP resource_, wl_resou default: modeString = "INVALID"; break; } - LOGM(Log::DEBUG, "setMode: {}. {} MODE_SERVER_SIDE as reply.", modeString, (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE ? "Sending" : "Ignoring and sending")); - auto sendMode = xdgModeOnRequestCSD(mode); - m_resource->sendConfigure(sendMode); - mostRecentlySent = sendMode; - mostRecentlyRequested = mode; + LOGM(LOG, "setMode: {}. {} MODE_SERVER_SIDE as reply.", modeString, (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE ? "Sending" : "Ignoring and sending")); + resource->sendConfigure(ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); }); - m_resource->setUnsetMode([this](CZxdgToplevelDecorationV1*) { - LOGM(Log::DEBUG, "unsetMode. Sending MODE_SERVER_SIDE."); - auto sendMode = xdgModeOnReleaseCSD(); - m_resource->sendConfigure(sendMode); - mostRecentlySent = sendMode; - mostRecentlyRequested = 0; + resource->setUnsetMode([this](CZxdgToplevelDecorationV1*) { + LOGM(LOG, "unsetMode. Sending MODE_SERVER_SIDE."); + resource->sendConfigure(ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); }); - auto sendMode = xdgDefaultModeCSD(); - m_resource->sendConfigure(sendMode); - mostRecentlySent = sendMode; -} - -zxdgToplevelDecorationV1Mode CXDGDecoration::xdgDefaultModeCSD() { - return ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; -} - -zxdgToplevelDecorationV1Mode CXDGDecoration::xdgModeOnRequestCSD(uint32_t modeRequestedByClient) { - return xdgDefaultModeCSD(); -} - -zxdgToplevelDecorationV1Mode CXDGDecoration::xdgModeOnReleaseCSD() { - return xdgDefaultModeCSD(); + resource->sendConfigure(ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } bool CXDGDecoration::good() { - return m_resource->resource(); + return resource->resource(); } wl_resource* CXDGDecoration::toplevelResource() { - return m_toplevelResource; + return pToplevelResource; } CXDGDecorationProtocol::CXDGDecorationProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -61,7 +41,7 @@ CXDGDecorationProtocol::CXDGDecorationProtocol(const wl_interface* iface, const } void CXDGDecorationProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagers.emplace_back(makeUnique(client, ver, id)).get(); RESOURCE->setOnDestroy([this](CZxdgDecorationManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CZxdgDecorationManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); @@ -69,26 +49,26 @@ void CXDGDecorationProtocol::bindManager(wl_client* client, void* data, uint32_t } void CXDGDecorationProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } void CXDGDecorationProtocol::destroyDecoration(CXDGDecoration* decoration) { - m_decorations.erase(decoration->toplevelResource()); + m_mDecorations.erase(decoration->toplevelResource()); } void CXDGDecorationProtocol::onGetDecoration(CZxdgDecorationManagerV1* pMgr, uint32_t id, wl_resource* xdgToplevel) { - if UNLIKELY (m_decorations.contains(xdgToplevel)) { + if UNLIKELY (m_mDecorations.contains(xdgToplevel)) { pMgr->error(ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED, "Decoration object already exists"); return; } const auto CLIENT = pMgr->client(); const auto RESOURCE = - m_decorations.emplace(xdgToplevel, makeUnique(makeShared(CLIENT, pMgr->version(), id), xdgToplevel)).first->second.get(); + m_mDecorations.emplace(xdgToplevel, makeUnique(makeShared(CLIENT, pMgr->version(), id), xdgToplevel)).first->second.get(); if UNLIKELY (!RESOURCE->good()) { pMgr->noMemory(); - m_decorations.erase(xdgToplevel); + m_mDecorations.erase(xdgToplevel); return; } -} +} \ No newline at end of file diff --git a/src/protocols/XDGDecoration.hpp b/src/protocols/XDGDecoration.hpp index 7e8b6ab9..33a9b663 100644 --- a/src/protocols/XDGDecoration.hpp +++ b/src/protocols/XDGDecoration.hpp @@ -9,19 +9,12 @@ class CXDGDecoration { public: CXDGDecoration(SP resource_, wl_resource* toplevel); - uint32_t mostRecentlySent = 0; - uint32_t mostRecentlyRequested = 0; - bool good(); wl_resource* toplevelResource(); private: - zxdgToplevelDecorationV1Mode xdgDefaultModeCSD(); - zxdgToplevelDecorationV1Mode xdgModeOnRequestCSD(uint32_t modeRequestedByClient); - zxdgToplevelDecorationV1Mode xdgModeOnReleaseCSD(); - - SP m_resource; - wl_resource* m_toplevelResource = nullptr; // READ-ONLY. + SP resource; + wl_resource* pToplevelResource = nullptr; // READ-ONLY. }; class CXDGDecorationProtocol : public IWaylandProtocol { @@ -36,8 +29,8 @@ class CXDGDecorationProtocol : public IWaylandProtocol { void onGetDecoration(CZxdgDecorationManagerV1* pMgr, uint32_t id, wl_resource* xdgToplevel); // - std::vector> m_managers; - std::unordered_map> m_decorations; // xdg_toplevel -> deco + std::vector> m_vManagers; + std::unordered_map> m_mDecorations; // xdg_toplevel -> deco friend class CXDGDecoration; }; diff --git a/src/protocols/XDGDialog.cpp b/src/protocols/XDGDialog.cpp index ad6b3eeb..675d4e78 100644 --- a/src/protocols/XDGDialog.cpp +++ b/src/protocols/XDGDialog.cpp @@ -1,74 +1,69 @@ #include "XDGDialog.hpp" #include "XDGShell.hpp" -#include "../desktop/view/WLSurface.hpp" +#include "../desktop/WLSurface.hpp" #include "../Compositor.hpp" #include -CXDGDialogV1Resource::CXDGDialogV1Resource(SP resource_, SP toplevel_) : m_resource(resource_), m_toplevel(toplevel_) { +CXDGDialogV1Resource::CXDGDialogV1Resource(SP resource_, SP toplevel_) : resource(resource_), toplevel(toplevel_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CXdgDialogV1* r) { PROTO::xdgDialog->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgDialogV1* r) { PROTO::xdgDialog->destroyResource(this); }); + resource->setDestroy([this](CXdgDialogV1* r) { PROTO::xdgDialog->destroyResource(this); }); + resource->setOnDestroy([this](CXdgDialogV1* r) { PROTO::xdgDialog->destroyResource(this); }); - m_resource->setSetModal([this](CXdgDialogV1* r) { + resource->setSetModal([this](CXdgDialogV1* r) { modal = true; updateWindow(); }); - m_resource->setUnsetModal([this](CXdgDialogV1* r) { + resource->setUnsetModal([this](CXdgDialogV1* r) { modal = false; updateWindow(); }); } void CXDGDialogV1Resource::updateWindow() { - if UNLIKELY (!m_toplevel || !m_toplevel->m_parent || !m_toplevel->m_parent->m_owner) + if UNLIKELY (!toplevel || !toplevel->parent || !toplevel->parent->owner) return; - const auto HLSURFACE = Desktop::View::CWLSurface::fromResource(m_toplevel->m_parent->m_owner->m_surface.lock()); - if UNLIKELY (!HLSURFACE) + auto HLSurface = CWLSurface::fromResource(toplevel->parent->owner->surface.lock()); + if UNLIKELY (!HLSurface || !HLSurface->getWindow()) return; - const auto WINDOW = Desktop::View::CWindow::fromView(HLSURFACE->view()); - - if UNLIKELY (!WINDOW) - return; - - WINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_MODAL); + g_pCompositor->updateWindowAnimatedDecorationValues(HLSurface->getWindow()); } bool CXDGDialogV1Resource::good() { - return m_resource->resource(); + return resource->resource(); } -CXDGWmDialogManagerResource::CXDGWmDialogManagerResource(SP resource_) : m_resource(resource_) { +CXDGWmDialogManagerResource::CXDGWmDialogManagerResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CXdgWmDialogV1* r) { PROTO::xdgDialog->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgWmDialogV1* r) { PROTO::xdgDialog->destroyResource(this); }); + resource->setDestroy([this](CXdgWmDialogV1* r) { PROTO::xdgDialog->destroyResource(this); }); + resource->setOnDestroy([this](CXdgWmDialogV1* r) { PROTO::xdgDialog->destroyResource(this); }); - m_resource->setGetXdgDialog([](CXdgWmDialogV1* r, uint32_t id, wl_resource* toplevel) { + resource->setGetXdgDialog([](CXdgWmDialogV1* r, uint32_t id, wl_resource* toplevel) { auto tl = CXDGToplevelResource::fromResource(toplevel); if UNLIKELY (!tl) { r->error(-1, "Toplevel inert"); return; } - const auto RESOURCE = PROTO::xdgDialog->m_dialogs.emplace_back(makeShared(makeShared(r->client(), r->version(), id), tl)); + const auto RESOURCE = PROTO::xdgDialog->m_vDialogs.emplace_back(makeShared(makeShared(r->client(), r->version(), id), tl)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); return; } - tl->m_dialog = RESOURCE; + tl->dialog = RESOURCE; }); } bool CXDGWmDialogManagerResource::good() { - return m_resource->resource(); + return resource->resource(); } CXDGDialogProtocol::CXDGDialogProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -76,7 +71,7 @@ CXDGDialogProtocol::CXDGDialogProtocol(const wl_interface* iface, const int& ver } void CXDGDialogProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); @@ -85,9 +80,9 @@ void CXDGDialogProtocol::bindManager(wl_client* client, void* data, uint32_t ver } void CXDGDialogProtocol::destroyResource(CXDGWmDialogManagerResource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == res; }); } void CXDGDialogProtocol::destroyResource(CXDGDialogV1Resource* res) { - std::erase_if(m_dialogs, [&](const auto& other) { return other.get() == res; }); + std::erase_if(m_vDialogs, [&](const auto& other) { return other.get() == res; }); } diff --git a/src/protocols/XDGDialog.hpp b/src/protocols/XDGDialog.hpp index 316f1687..1b29e692 100644 --- a/src/protocols/XDGDialog.hpp +++ b/src/protocols/XDGDialog.hpp @@ -16,8 +16,8 @@ class CXDGDialogV1Resource { bool modal = false; private: - SP m_resource; - WP m_toplevel; + SP resource; + WP toplevel; void updateWindow(); }; @@ -29,7 +29,7 @@ class CXDGWmDialogManagerResource { bool good(); private: - SP m_resource; + SP resource; }; class CXDGDialogProtocol : public IWaylandProtocol { @@ -44,8 +44,8 @@ class CXDGDialogProtocol : public IWaylandProtocol { void destroyResource(CXDGDialogV1Resource* res); // - std::vector> m_managers; - std::vector> m_dialogs; + std::vector> m_vManagers; + std::vector> m_vDialogs; friend class CXDGWmDialogManagerResource; friend class CXDGDialogV1Resource; diff --git a/src/protocols/XDGOutput.cpp b/src/protocols/XDGOutput.cpp index 3553a74b..544deed0 100644 --- a/src/protocols/XDGOutput.cpp +++ b/src/protocols/XDGOutput.cpp @@ -1,8 +1,7 @@ #include "XDGOutput.hpp" #include "../config/ConfigValue.hpp" -#include "../helpers/Monitor.hpp" #include "../xwayland/XWayland.hpp" -#include "../event/EventBus.hpp" +#include "../managers/HookSystemManager.hpp" #include "core/Output.hpp" #define OUTPUT_MANAGER_VERSION 3 @@ -14,18 +13,18 @@ // void CXDGOutputProtocol::onManagerResourceDestroy(wl_resource* res) { - std::erase_if(m_managerResources, [&](const auto& other) { return other->resource() == res; }); + std::erase_if(m_vManagerResources, [&](const auto& other) { return other->resource() == res; }); } void CXDGOutputProtocol::onOutputResourceDestroy(wl_resource* res) { - std::erase_if(m_xdgOutputs, [&](const auto& other) { return other->m_resource->resource() == res; }); + std::erase_if(m_vXDGOutputs, [&](const auto& other) { return other->resource->resource() == res; }); } void CXDGOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managerResources.emplace_back(makeUnique(client, ver, id)).get(); + const auto RESOURCE = m_vManagerResources.emplace_back(makeUnique(client, ver, id)).get(); if UNLIKELY (!RESOURCE->resource()) { - LOGM(Log::DEBUG, "Couldn't bind XDGOutputMgr"); + LOGM(LOG, "Couldn't bind XDGOutputMgr"); wl_client_post_no_memory(client); return; } @@ -36,43 +35,43 @@ void CXDGOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver } CXDGOutputProtocol::CXDGOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - static auto P = Event::bus()->m_events.monitor.layoutChanged.listen([this] { updateAllOutputs(); }); - static auto P2 = Event::bus()->m_events.config.reloaded.listen([this] { updateAllOutputs(); }); + static auto P = g_pHookSystem->hookDynamic("monitorLayoutChanged", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); }); + static auto P2 = g_pHookSystem->hookDynamic("configReloaded", [this](void* self, SCallbackInfo& info, std::any param) { this->updateAllOutputs(); }); } void CXDGOutputProtocol::onManagerGetXDGOutput(CZxdgOutputManagerV1* mgr, uint32_t id, wl_resource* outputResource) { const auto OUTPUT = CWLOutputResource::fromResource(outputResource); - const auto PMONITOR = OUTPUT->m_monitor.lock(); + const auto PMONITOR = OUTPUT->monitor.lock(); const auto CLIENT = mgr->client(); - CXDGOutput* pXDGOutput = m_xdgOutputs.emplace_back(makeUnique(makeShared(CLIENT, mgr->version(), id), PMONITOR)).get(); + CXDGOutput* pXDGOutput = m_vXDGOutputs.emplace_back(makeUnique(makeShared(CLIENT, mgr->version(), id), PMONITOR)).get(); #ifndef NO_XWAYLAND - if (g_pXWayland && g_pXWayland->m_server && g_pXWayland->m_server->m_xwaylandClient == CLIENT) - pXDGOutput->m_isXWayland = true; + if (g_pXWayland && g_pXWayland->pServer && g_pXWayland->pServer->xwaylandClient == CLIENT) + pXDGOutput->isXWayland = true; #endif - pXDGOutput->m_client = CLIENT; + pXDGOutput->client = CLIENT; - pXDGOutput->m_outputProto = OUTPUT->m_owner; + pXDGOutput->outputProto = OUTPUT->owner; - if UNLIKELY (!pXDGOutput->m_resource->resource()) { - m_xdgOutputs.pop_back(); + if UNLIKELY (!pXDGOutput->resource->resource()) { + m_vXDGOutputs.pop_back(); mgr->noMemory(); return; } if UNLIKELY (!PMONITOR) { - LOGM(Log::ERR, "New xdg_output from client {:x} ({}) has no CMonitor?!", (uintptr_t)CLIENT, pXDGOutput->m_isXWayland ? "xwayland" : "not xwayland"); + LOGM(ERR, "New xdg_output from client {:x} ({}) has no CMonitor?!", (uintptr_t)CLIENT, pXDGOutput->isXWayland ? "xwayland" : "not xwayland"); return; } - LOGM(Log::DEBUG, "New xdg_output for {}: client {:x} ({})", PMONITOR->m_name, (uintptr_t)CLIENT, pXDGOutput->m_isXWayland ? "xwayland" : "not xwayland"); + LOGM(LOG, "New xdg_output for {}: client {:x} ({})", PMONITOR->szName, (uintptr_t)CLIENT, pXDGOutput->isXWayland ? "xwayland" : "not xwayland"); - const auto XDGVER = pXDGOutput->m_resource->version(); + const auto XDGVER = pXDGOutput->resource->version(); if (XDGVER >= OUTPUT_NAME_SINCE_VERSION) - pXDGOutput->m_resource->sendName(PMONITOR->m_name.c_str()); - if (XDGVER >= OUTPUT_DESCRIPTION_SINCE_VERSION && !PMONITOR->m_output->description.empty()) - pXDGOutput->m_resource->sendDescription(PMONITOR->m_output->description.c_str()); + pXDGOutput->resource->sendName(PMONITOR->szName.c_str()); + if (XDGVER >= OUTPUT_DESCRIPTION_SINCE_VERSION && !PMONITOR->output->description.empty()) + pXDGOutput->resource->sendDescription(PMONITOR->output->description.c_str()); pXDGOutput->sendDetails(); @@ -82,42 +81,42 @@ void CXDGOutputProtocol::onManagerGetXDGOutput(CZxdgOutputManagerV1* mgr, uint32 } void CXDGOutputProtocol::updateAllOutputs() { - LOGM(Log::DEBUG, "updating all xdg_output heads"); + LOGM(LOG, "updating all xdg_output heads"); - for (auto const& o : m_xdgOutputs) { - if (!o->m_monitor) + for (auto const& o : m_vXDGOutputs) { + if (!o->monitor) continue; o->sendDetails(); - o->m_monitor->scheduleDone(); + o->monitor->scheduleDone(); } } // -CXDGOutput::CXDGOutput(SP resource_, PHLMONITOR monitor_) : m_monitor(monitor_), m_resource(resource_) { - if UNLIKELY (!m_resource->resource()) +CXDGOutput::CXDGOutput(SP resource_, PHLMONITOR monitor_) : monitor(monitor_), resource(resource_) { + if UNLIKELY (!resource->resource()) return; - m_resource->setDestroy([](CZxdgOutputV1* pMgr) { PROTO::xdgOutput->onOutputResourceDestroy(pMgr->resource()); }); - m_resource->setOnDestroy([](CZxdgOutputV1* pMgr) { PROTO::xdgOutput->onOutputResourceDestroy(pMgr->resource()); }); + resource->setDestroy([](CZxdgOutputV1* pMgr) { PROTO::xdgOutput->onOutputResourceDestroy(pMgr->resource()); }); + resource->setOnDestroy([](CZxdgOutputV1* pMgr) { PROTO::xdgOutput->onOutputResourceDestroy(pMgr->resource()); }); } void CXDGOutput::sendDetails() { static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - if UNLIKELY (!m_monitor || !m_outputProto || m_outputProto->isDefunct()) + if UNLIKELY (!monitor || !outputProto || outputProto->isDefunct()) return; - const auto POS = m_isXWayland ? m_monitor->m_xwaylandPosition : m_monitor->m_position; - m_resource->sendLogicalPosition(POS.x, POS.y); + const auto POS = isXWayland ? monitor->vecXWaylandPosition : monitor->vecPosition; + resource->sendLogicalPosition(POS.x, POS.y); - if (*PXWLFORCESCALEZERO && m_isXWayland) - m_resource->sendLogicalSize(m_monitor->m_transformedSize.x, m_monitor->m_transformedSize.y); + if (*PXWLFORCESCALEZERO && isXWayland) + resource->sendLogicalSize(monitor->vecTransformedSize.x, monitor->vecTransformedSize.y); else - m_resource->sendLogicalSize(m_monitor->m_size.x, m_monitor->m_size.y); + resource->sendLogicalSize(monitor->vecSize.x, monitor->vecSize.y); - if (m_resource->version() < OUTPUT_DONE_DEPRECATED_SINCE_VERSION) - m_resource->sendDone(); + if (resource->version() < OUTPUT_DONE_DEPRECATED_SINCE_VERSION) + resource->sendDone(); } diff --git a/src/protocols/XDGOutput.hpp b/src/protocols/XDGOutput.hpp index eccce837..6a5be284 100644 --- a/src/protocols/XDGOutput.hpp +++ b/src/protocols/XDGOutput.hpp @@ -15,12 +15,14 @@ class CXDGOutput { void sendDetails(); private: - PHLMONITORREF m_monitor; - SP m_resource; - WP m_outputProto; + PHLMONITORREF monitor; + SP resource; + WP outputProto; - wl_client* m_client = nullptr; - bool m_isXWayland = false; + std::optional overridePosition; + + wl_client* client = nullptr; + bool isXWayland = false; friend class CXDGOutputProtocol; }; @@ -38,8 +40,8 @@ class CXDGOutputProtocol : public IWaylandProtocol { void onManagerGetXDGOutput(CZxdgOutputManagerV1* mgr, uint32_t id, wl_resource* outputResource); // - std::vector> m_managerResources; - std::vector> m_xdgOutputs; + std::vector> m_vManagerResources; + std::vector> m_vXDGOutputs; friend class CXDGOutput; }; diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index 969556a3..db274e3c 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -7,10 +7,7 @@ #include "../helpers/Monitor.hpp" #include "core/Seat.hpp" #include "core/Compositor.hpp" -#include "../desktop/DesktopTypes.hpp" -#include "../desktop/view/Window.hpp" #include "protocols/core/Output.hpp" -#include #include #include @@ -29,72 +26,72 @@ void SXDGPositionerState::setGravity(xdgPositionerGravity edges) { } CXDGPopupResource::CXDGPopupResource(SP resource_, SP owner_, SP surface_, SP positioner) : - m_surface(surface_), m_parent(owner_), m_resource(resource_), m_positionerRules(positioner) { + surface(surface_), parent(owner_), resource(resource_), positionerRules(positioner) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setDestroy([this](CXdgPopup* r) { - if (m_surface && m_surface->m_mapped) - m_surface->m_events.unmap.emit(); - PROTO::xdgShell->onPopupDestroy(m_self); - m_events.destroy.emit(); + resource->setDestroy([this](CXdgPopup* r) { + if (surface && surface->mapped) + surface->events.unmap.emit(); + PROTO::xdgShell->onPopupDestroy(self); + events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgPopup* r) { - if (m_surface && m_surface->m_mapped) - m_surface->m_events.unmap.emit(); - PROTO::xdgShell->onPopupDestroy(m_self); - m_events.destroy.emit(); + resource->setOnDestroy([this](CXdgPopup* r) { + if (surface && surface->mapped) + surface->events.unmap.emit(); + PROTO::xdgShell->onPopupDestroy(self); + events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); - m_resource->setReposition([this](CXdgPopup* r, wl_resource* positionerRes, uint32_t token) { - LOGM(Log::DEBUG, "Popup {:x} asks for reposition", (uintptr_t)this); - m_lastRepositionToken = token; - auto pos = CXDGPositionerResource::fromResource(positionerRes); + resource->setReposition([this](CXdgPopup* r, wl_resource* positionerRes, uint32_t token) { + LOGM(LOG, "Popup {:x} asks for reposition", (uintptr_t)this); + lastRepositionToken = token; + auto pos = CXDGPositionerResource::fromResource(positionerRes); if (!pos) return; - m_positionerRules = CXDGPositionerRules{pos}; - m_events.reposition.emit(); + positionerRules = CXDGPositionerRules{pos}; + events.reposition.emit(); }); - m_resource->setGrab([this](CXdgPopup* r, wl_resource* seat, uint32_t serial) { - LOGM(Log::DEBUG, "xdg_popup {:x} requests grab", (uintptr_t)this); - PROTO::xdgShell->addOrStartGrab(m_self.lock()); + resource->setGrab([this](CXdgPopup* r, wl_resource* seat, uint32_t serial) { + LOGM(LOG, "xdg_popup {:x} requests grab", (uintptr_t)this); + PROTO::xdgShell->addOrStartGrab(self.lock()); }); - if (m_parent) - m_taken = true; + if (parent) + taken = true; } CXDGPopupResource::~CXDGPopupResource() { - PROTO::xdgShell->onPopupDestroy(m_self); - m_events.destroy.emit(); + PROTO::xdgShell->onPopupDestroy(self); + events.destroy.emit(); } void CXDGPopupResource::applyPositioning(const CBox& box, const Vector2D& t1coord) { - CBox constraint = box.copy().translate(m_surface->m_pending.geometry.pos()); + CBox constraint = box.copy().translate(surface->pending.geometry.pos()); - m_geometry = m_positionerRules.getPosition(constraint, accumulateParentOffset() + t1coord); + geometry = positionerRules.getPosition(constraint, accumulateParentOffset() + t1coord); - LOGM(Log::DEBUG, "Popup {:x} gets unconstrained to {} {}", (uintptr_t)this, m_geometry.pos(), m_geometry.size()); + LOGM(LOG, "Popup {:x} gets unconstrained to {} {}", (uintptr_t)this, geometry.pos(), geometry.size()); - configure(m_geometry); + configure(geometry); - if UNLIKELY (m_lastRepositionToken) + if UNLIKELY (lastRepositionToken) repositioned(); } Vector2D CXDGPopupResource::accumulateParentOffset() { - SP current = m_parent.lock(); + SP current = parent.lock(); Vector2D off; while (current) { - off += current->m_current.geometry.pos(); - if (current->m_popup) { - off += current->m_popup->m_geometry.pos(); - current = current->m_popup->m_parent.lock(); + off += current->current.geometry.pos(); + if (current->popup) { + off += current->popup->geometry.pos(); + current = current->popup->parent.lock(); } else break; } @@ -102,529 +99,490 @@ Vector2D CXDGPopupResource::accumulateParentOffset() { } SP CXDGPopupResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CXDGPopupResource*)(((CXdgPopup*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } bool CXDGPopupResource::good() { - return m_resource->resource(); + return resource->resource(); } void CXDGPopupResource::configure(const CBox& box) { - m_resource->sendConfigure(box.x, box.y, box.w, box.h); - if (m_surface) - m_surface->scheduleConfigure(); + resource->sendConfigure(box.x, box.y, box.w, box.h); + if (surface) + surface->scheduleConfigure(); } void CXDGPopupResource::done() { - m_events.dismissed.emit(); - m_resource->sendPopupDone(); + events.dismissed.emit(); + resource->sendPopupDone(); } void CXDGPopupResource::repositioned() { - if LIKELY (!m_lastRepositionToken) + if LIKELY (!lastRepositionToken) return; - LOGM(Log::DEBUG, "repositioned: sending reposition token {}", m_lastRepositionToken); + LOGM(LOG, "repositioned: sending reposition token {}", lastRepositionToken); - m_resource->sendRepositioned(m_lastRepositionToken); - m_lastRepositionToken = 0; + resource->sendRepositioned(lastRepositionToken); + lastRepositionToken = 0; } -CXDGToplevelResource::CXDGToplevelResource(SP resource_, SP owner_) : m_owner(owner_), m_resource(resource_) { +CXDGToplevelResource::CXDGToplevelResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setDestroy([this](CXdgToplevel* r) { - m_events.destroy.emit(); + resource->setDestroy([this](CXdgToplevel* r) { + events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgToplevel* r) { - m_events.destroy.emit(); + resource->setOnDestroy([this](CXdgToplevel* r) { + events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); - if (m_resource->version() >= 5) { + if (resource->version() >= 5) { wl_array arr; wl_array_init(&arr); - auto p = sc(wl_array_add(&arr, sizeof(uint32_t))); + auto p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t)); *p = XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN; - p = sc(wl_array_add(&arr, sizeof(uint32_t))); + p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t)); *p = XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE; - m_resource->sendWmCapabilities(&arr); + resource->sendWmCapabilities(&arr); wl_array_release(&arr); } - if (m_resource->version() >= 2) { - m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT); - m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT); - m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP); - m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM); + if (resource->version() >= 2) { + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM); } - m_resource->setSetTitle([this](CXdgToplevel* r, const char* t) { - m_state.title = t; - m_events.metadataChanged.emit(); + resource->setSetTitle([this](CXdgToplevel* r, const char* t) { + state.title = t; + events.metadataChanged.emit(); }); - m_resource->setSetAppId([this](CXdgToplevel* r, const char* id) { - m_state.appid = id; - m_events.metadataChanged.emit(); + resource->setSetAppId([this](CXdgToplevel* r, const char* id) { + state.appid = id; + events.metadataChanged.emit(); }); - m_resource->setSetMaxSize([this](CXdgToplevel* r, int32_t x, int32_t y) { - m_pending.maxSize = {x, y}; - m_events.sizeLimitsChanged.emit(); + resource->setSetMaxSize([this](CXdgToplevel* r, int32_t x, int32_t y) { + pending.maxSize = {x, y}; + events.sizeLimitsChanged.emit(); }); - m_resource->setSetMinSize([this](CXdgToplevel* r, int32_t x, int32_t y) { - m_pending.minSize = {x, y}; - m_events.sizeLimitsChanged.emit(); + resource->setSetMinSize([this](CXdgToplevel* r, int32_t x, int32_t y) { + pending.minSize = {x, y}; + events.sizeLimitsChanged.emit(); }); - m_resource->setSetMaximized([this](CXdgToplevel* r) { - m_state.requestsMaximize = true; - m_events.stateChanged.emit(); - m_state.requestsMaximize.reset(); + resource->setSetMaximized([this](CXdgToplevel* r) { + state.requestsMaximize = true; + events.stateChanged.emit(); + state.requestsMaximize.reset(); }); - m_resource->setUnsetMaximized([this](CXdgToplevel* r) { - m_state.requestsMaximize = false; - m_events.stateChanged.emit(); - m_state.requestsMaximize.reset(); + resource->setUnsetMaximized([this](CXdgToplevel* r) { + state.requestsMaximize = false; + events.stateChanged.emit(); + state.requestsMaximize.reset(); }); - m_resource->setSetFullscreen([this](CXdgToplevel* r, wl_resource* output) { + resource->setSetFullscreen([this](CXdgToplevel* r, wl_resource* output) { if (output) - if (const auto PM = CWLOutputResource::fromResource(output)->m_monitor; PM) - m_state.requestsFullscreenMonitor = PM->m_id; + if (const auto PM = CWLOutputResource::fromResource(output)->monitor; PM) + state.requestsFullscreenMonitor = PM->ID; - m_state.requestsFullscreen = true; - m_events.stateChanged.emit(); - m_state.requestsFullscreen.reset(); - m_state.requestsFullscreenMonitor.reset(); + state.requestsFullscreen = true; + events.stateChanged.emit(); + state.requestsFullscreen.reset(); + state.requestsFullscreenMonitor.reset(); }); - m_resource->setUnsetFullscreen([this](CXdgToplevel* r) { - m_state.requestsFullscreen = false; - m_events.stateChanged.emit(); - m_state.requestsFullscreen.reset(); + resource->setUnsetFullscreen([this](CXdgToplevel* r) { + state.requestsFullscreen = false; + events.stateChanged.emit(); + state.requestsFullscreen.reset(); }); - m_resource->setSetMinimized([this](CXdgToplevel* r) { - m_state.requestsMinimize = true; - m_events.stateChanged.emit(); - m_state.requestsMinimize.reset(); + resource->setSetMinimized([this](CXdgToplevel* r) { + state.requestsMinimize = true; + events.stateChanged.emit(); + state.requestsMinimize.reset(); }); - m_resource->setSetParent([this](CXdgToplevel* r, wl_resource* parentR) { - auto oldParent = m_parent; + resource->setSetParent([this](CXdgToplevel* r, wl_resource* parentR) { + auto oldParent = parent; - if (m_parent) - std::erase(m_parent->m_children, m_self); + if (parent) + std::erase(parent->children, self); auto newp = parentR ? CXDGToplevelResource::fromResource(parentR) : nullptr; + parent = newp; - if (newp) { - // check for protocol constraints - if (newp == m_self) { - r->error(XDG_TOPLEVEL_ERROR_INVALID_PARENT, "Parent cannot be self"); - return; - } + if (parent) + parent->children.emplace_back(self); - static std::function, WP)> exploreChildren = [](WP tl, - WP target) -> bool { - bool any = false; - for (const auto& c : tl->m_children) { - if (c == target) - return true; - - any = any || exploreChildren(c, target); - - if (any) - break; - } - return any; - }; - - if (exploreChildren(m_self, newp)) { - r->error(XDG_TOPLEVEL_ERROR_INVALID_PARENT, "Parent cannot be a descendant"); - return; - } - } - - m_parent = newp; - - if (m_parent) { - m_parent->m_children.emplace_back(m_self); - - if (m_parent->m_window && m_parent->m_window->m_pinned) - m_self->m_window->m_pinned = true; - } - - LOGM(Log::DEBUG, "Toplevel {:x} sets parent to {:x}{}", (uintptr_t)this, (uintptr_t)newp.get(), (oldParent ? std::format(" (was {:x})", (uintptr_t)oldParent.get()) : "")); + LOGM(LOG, "Toplevel {:x} sets parent to {:x}{}", (uintptr_t)this, (uintptr_t)newp.get(), (oldParent ? std::format(" (was {:x})", (uintptr_t)oldParent.get()) : "")); }); } CXDGToplevelResource::~CXDGToplevelResource() { - m_events.destroy.emit(); - if (m_parent) - std::erase_if(m_parent->m_children, [this](const auto& other) { return !other || other.get() == this; }); + events.destroy.emit(); + if (parent) + std::erase_if(parent->children, [this](const auto& other) { return !other || other.get() == this; }); } SP CXDGToplevelResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CXDGToplevelResource*)(((CXdgToplevel*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } bool CXDGToplevelResource::good() { - return m_resource->resource(); + return resource->resource(); } bool CXDGToplevelResource::anyChildModal() { - return std::ranges::any_of(m_children, [](const auto& child) { return child && child->m_dialog && child->m_dialog->modal; }); + return std::ranges::any_of(children, [](const auto& child) { return child && child->dialog && child->dialog->modal; }); } uint32_t CXDGToplevelResource::setSize(const Vector2D& size) { - m_pendingApply.size = size; + pendingApply.size = size; applyState(); - return m_owner->scheduleConfigure(); + return owner->scheduleConfigure(); } uint32_t CXDGToplevelResource::setMaximized(bool maximized) { - bool set = std::ranges::find(m_pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED) != m_pendingApply.states.end(); + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_MAXIMIZED) != pendingApply.states.end(); if (maximized == set) - return m_owner->m_scheduledSerial; + return owner->scheduledSerial; if (maximized && !set) - m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_MAXIMIZED); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_MAXIMIZED); else if (!maximized && set) - std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED); + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED); applyState(); - return m_owner->scheduleConfigure(); + return owner->scheduleConfigure(); } uint32_t CXDGToplevelResource::setFullscreen(bool fullscreen) { - bool set = std::ranges::find(m_pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN) != m_pendingApply.states.end(); + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_FULLSCREEN) != pendingApply.states.end(); if (fullscreen == set) - return m_owner->m_scheduledSerial; + return owner->scheduledSerial; if (fullscreen && !set) - m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_FULLSCREEN); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_FULLSCREEN); else if (!fullscreen && set) - std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN); + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN); applyState(); - return m_owner->scheduleConfigure(); + return owner->scheduleConfigure(); } uint32_t CXDGToplevelResource::setActive(bool active) { - bool set = std::ranges::find(m_pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED) != m_pendingApply.states.end(); + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_ACTIVATED) != pendingApply.states.end(); if (active == set) - return m_owner->m_scheduledSerial; + return owner->scheduledSerial; if (active && !set) - m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_ACTIVATED); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_ACTIVATED); else if (!active && set) - std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED); + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED); applyState(); - return m_owner->scheduleConfigure(); + return owner->scheduleConfigure(); } uint32_t CXDGToplevelResource::setSuspeneded(bool sus) { - if (m_resource->version() < 6) - return m_owner->scheduleConfigure(); // SUSPENDED is since 6 + if (resource->version() < 6) + return owner->scheduleConfigure(); // SUSPENDED is since 6 - bool set = std::ranges::find(m_pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED) != m_pendingApply.states.end(); + bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_SUSPENDED) != pendingApply.states.end(); if (sus == set) - return m_owner->m_scheduledSerial; + return owner->scheduledSerial; if (sus && !set) - m_pendingApply.states.push_back(XDG_TOPLEVEL_STATE_SUSPENDED); + pendingApply.states.push_back(XDG_TOPLEVEL_STATE_SUSPENDED); else if (!sus && set) - std::erase(m_pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED); + std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED); applyState(); - return m_owner->scheduleConfigure(); + return owner->scheduleConfigure(); } void CXDGToplevelResource::applyState() { wl_array arr; wl_array_init(&arr); + wl_array_add(&arr, pendingApply.states.size() * sizeof(int)); + memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int)); - if (!m_pendingApply.states.empty()) { - wl_array_add(&arr, m_pendingApply.states.size() * sizeof(int)); - memcpy(arr.data, m_pendingApply.states.data(), m_pendingApply.states.size() * sizeof(int)); - } - - m_resource->sendConfigure(m_pendingApply.size.x, m_pendingApply.size.y, &arr); + resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr); wl_array_release(&arr); } void CXDGToplevelResource::close() { - m_resource->sendClose(); + resource->sendClose(); } Vector2D CXDGToplevelResource::layoutMinSize() { Vector2D minSize; - if (m_current.minSize.x > 1) - minSize.x = m_owner ? m_current.minSize.x + m_owner->m_current.geometry.pos().x : m_current.minSize.x; - if (m_current.minSize.y > 1) - minSize.y = m_owner ? m_current.minSize.y + m_owner->m_current.geometry.pos().y : m_current.minSize.y; + if (current.minSize.x > 1) + minSize.x = owner ? current.minSize.x + owner->current.geometry.pos().x : current.minSize.x; + if (current.minSize.y > 1) + minSize.y = owner ? current.minSize.y + owner->current.geometry.pos().y : current.minSize.y; return minSize; } Vector2D CXDGToplevelResource::layoutMaxSize() { Vector2D maxSize; - if (m_current.maxSize.x > 1) - maxSize.x = m_owner ? m_current.maxSize.x + m_owner->m_current.geometry.pos().x : m_current.maxSize.x; - if (m_current.maxSize.y > 1) - maxSize.y = m_owner ? m_current.maxSize.y + m_owner->m_current.geometry.pos().y : m_current.maxSize.y; + if (current.maxSize.x > 1) + maxSize.x = owner ? current.maxSize.x + owner->current.geometry.pos().x : current.maxSize.x; + if (current.maxSize.y > 1) + maxSize.y = owner ? current.maxSize.y + owner->current.geometry.pos().y : current.maxSize.y; return maxSize; } CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SP owner_, SP surface_) : - m_owner(owner_), m_surface(surface_), m_resource(resource_) { + owner(owner_), surface(surface_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setDestroy([this](CXdgSurface* r) { - if (m_mapped) - m_events.unmap.emit(); - m_events.destroy.emit(); + resource->setDestroy([this](CXdgSurface* r) { + if (mapped) + events.unmap.emit(); + events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgSurface* r) { - if (m_mapped) - m_events.unmap.emit(); - m_events.destroy.emit(); + resource->setOnDestroy([this](CXdgSurface* r) { + if (mapped) + events.unmap.emit(); + events.destroy.emit(); PROTO::xdgShell->destroyResource(this); }); - m_listeners.surfaceDestroy = m_surface->m_events.destroy.listen([this] { - LOGM(Log::WARN, "wl_surface destroyed before its xdg_surface role object"); - m_listeners.surfaceDestroy.reset(); - m_listeners.surfaceCommit.reset(); + listeners.surfaceDestroy = surface->events.destroy.registerListener([this](std::any d) { + LOGM(WARN, "wl_surface destroyed before its xdg_surface role object"); + listeners.surfaceDestroy.reset(); + listeners.surfaceCommit.reset(); - if (m_mapped) - m_events.unmap.emit(); + if (mapped) + events.unmap.emit(); - m_mapped = false; - m_surface.reset(); - m_events.destroy.emit(); + mapped = false; + surface.reset(); + events.destroy.emit(); }); - m_listeners.surfaceCommit = m_surface->m_events.commit.listen([this] { - m_current = m_pending; - if (m_toplevel) - m_toplevel->m_current = m_toplevel->m_pending; + listeners.surfaceCommit = surface->events.commit.registerListener([this](std::any d) { + current = pending; + if (toplevel) + toplevel->current = toplevel->pending; - if UNLIKELY (m_initialCommit && m_surface->m_pending.buffer) { - m_resource->error(-1, "Buffer attached before initial commit"); + if UNLIKELY (initialCommit && surface->pending.buffer) { + resource->error(-1, "Buffer attached before initial commit"); return; } - if (m_surface->m_current.texture && !m_mapped) { + if (surface->current.texture && !mapped) { // this forces apps to not draw CSD. - if (m_toplevel) - m_toplevel->setMaximized(true); + if (toplevel) + toplevel->setMaximized(true); - m_mapped = true; - m_surface->map(); - m_events.map.emit(); + mapped = true; + surface->map(); + events.map.emit(); return; } - if (!m_surface->m_current.texture && m_mapped) { - m_mapped = false; - m_events.unmap.emit(); - m_surface->unmap(); + if (!surface->current.texture && mapped) { + mapped = false; + events.unmap.emit(); + surface->unmap(); return; } - m_events.commit.emit(); - m_initialCommit = false; + events.commit.emit(); + initialCommit = false; }); - m_resource->setGetToplevel([this](CXdgSurface* r, uint32_t id) { - const auto RESOURCE = PROTO::xdgShell->m_toplevels.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_self.lock())); + resource->setGetToplevel([this](CXdgSurface* r, uint32_t id) { + const auto RESOURCE = PROTO::xdgShell->m_vToplevels.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::xdgShell->m_toplevels.pop_back(); + PROTO::xdgShell->m_vToplevels.pop_back(); return; } - m_toplevel = RESOURCE; - m_toplevel->m_self = RESOURCE; + toplevel = RESOURCE; + toplevel->self = RESOURCE; - LOGM(Log::DEBUG, "xdg_surface {:x} gets a toplevel {:x}", (uintptr_t)m_owner.get(), (uintptr_t)RESOURCE.get()); + LOGM(LOG, "xdg_surface {:x} gets a toplevel {:x}", (uintptr_t)owner.get(), (uintptr_t)RESOURCE.get()); - PHLWINDOW createdWindow = g_pCompositor->m_windows.emplace_back(Desktop::View::CWindow::create(m_self.lock())); + g_pCompositor->m_vWindows.emplace_back(CWindow::create(self.lock())); - if (RESOURCE->m_parent && RESOURCE->m_parent->m_window->m_pinned) - createdWindow->m_pinned = true; - - for (auto const& p : m_popups) { + for (auto const& p : popups) { if (!p) continue; - m_events.newPopup.emit(p.lock()); + events.newPopup.emit(p); } }); - m_resource->setGetPopup([this](CXdgSurface* r, uint32_t id, wl_resource* parentXDG, wl_resource* positionerRes) { + resource->setGetPopup([this](CXdgSurface* r, uint32_t id, wl_resource* parentXDG, wl_resource* positionerRes) { auto parent = parentXDG ? CXDGSurfaceResource::fromResource(parentXDG) : nullptr; auto positioner = CXDGPositionerResource::fromResource(positionerRes); const auto RESOURCE = - PROTO::xdgShell->m_popups.emplace_back(makeShared(makeShared(r->client(), r->version(), id), parent, m_self.lock(), positioner)); + PROTO::xdgShell->m_vPopups.emplace_back(makeShared(makeShared(r->client(), r->version(), id), parent, self.lock(), positioner)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::xdgShell->m_popups.pop_back(); + PROTO::xdgShell->m_vPopups.pop_back(); return; } - m_popup = RESOURCE; - RESOURCE->m_self = RESOURCE; + popup = RESOURCE; + RESOURCE->self = RESOURCE; - LOGM(Log::DEBUG, "xdg_surface {:x} gets a popup {:x} owner {:x}", (uintptr_t)m_self.get(), (uintptr_t)RESOURCE.get(), (uintptr_t)parent.get()); + LOGM(LOG, "xdg_surface {:x} gets a popup {:x} owner {:x}", (uintptr_t)self.get(), (uintptr_t)RESOURCE.get(), (uintptr_t)parent.get()); if (!parent) return; - parent->m_popups.emplace_back(RESOURCE); - if (parent->m_mapped) - parent->m_events.newPopup.emit(RESOURCE); + parent->popups.emplace_back(RESOURCE); + if (parent->mapped) + parent->events.newPopup.emit(RESOURCE); }); - m_resource->setAckConfigure([this](CXdgSurface* r, uint32_t serial) { - if (serial < m_lastConfigureSerial) + resource->setAckConfigure([this](CXdgSurface* r, uint32_t serial) { + if (serial < lastConfigureSerial) return; - m_lastConfigureSerial = serial; - m_events.ack.emit(serial); + lastConfigureSerial = serial; + events.ack.emit(serial); }); - m_resource->setSetWindowGeometry([this](CXdgSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { - LOGM(Log::DEBUG, "xdg_surface {:x} requests geometry {}x{} {}x{}", (uintptr_t)this, x, y, w, h); - m_pending.geometry = {x, y, w, h}; + resource->setSetWindowGeometry([this](CXdgSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { + LOGM(LOG, "xdg_surface {:x} requests geometry {}x{} {}x{}", (uintptr_t)this, x, y, w, h); + pending.geometry = {x, y, w, h}; }); } CXDGSurfaceResource::~CXDGSurfaceResource() { - m_events.destroy.emit(); - if (m_configureSource) - wl_event_source_remove(m_configureSource); - if (m_surface) - m_surface->resetRole(); + events.destroy.emit(); + if (configureSource) + wl_event_source_remove(configureSource); + if (surface) + surface->resetRole(); } bool CXDGSurfaceResource::good() { - return m_resource->resource(); + return resource->resource(); } SP CXDGSurfaceResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CXDGSurfaceResource*)(((CXdgSurface*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } static void onConfigure(void* data) { - sc(data)->configure(); + ((CXDGSurfaceResource*)data)->configure(); } uint32_t CXDGSurfaceResource::scheduleConfigure() { - if (m_configureSource) - return m_scheduledSerial; + if (configureSource) + return scheduledSerial; - m_configureSource = wl_event_loop_add_idle(g_pCompositor->m_wlEventLoop, onConfigure, this); - m_scheduledSerial = wl_display_next_serial(g_pCompositor->m_wlDisplay); + configureSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, onConfigure, this); + scheduledSerial = wl_display_next_serial(g_pCompositor->m_sWLDisplay); - return m_scheduledSerial; + return scheduledSerial; } void CXDGSurfaceResource::configure() { - m_configureSource = nullptr; - m_resource->sendConfigure(m_scheduledSerial); + configureSource = nullptr; + resource->sendConfigure(scheduledSerial); } -CXDGPositionerResource::CXDGPositionerResource(SP resource_, SP owner_) : m_owner(owner_), m_resource(resource_) { +CXDGPositionerResource::CXDGPositionerResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); }); + resource->setDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); }); + resource->setOnDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); }); - m_resource->setSetSize([this](CXdgPositioner* r, int32_t x, int32_t y) { + resource->setSetSize([this](CXdgPositioner* r, int32_t x, int32_t y) { if UNLIKELY (x <= 0 || y <= 0) { r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid size"); return; } - m_state.requestedSize = {x, y}; + state.requestedSize = {x, y}; }); - m_resource->setSetAnchorRect([this](CXdgPositioner* r, int32_t x, int32_t y, int32_t w, int32_t h) { + resource->setSetAnchorRect([this](CXdgPositioner* r, int32_t x, int32_t y, int32_t w, int32_t h) { if UNLIKELY (w <= 0 || h <= 0) { r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid box"); return; } - m_state.anchorRect = {x, y, w, h}; + state.anchorRect = {x, y, w, h}; }); - m_resource->setSetOffset([this](CXdgPositioner* r, int32_t x, int32_t y) { m_state.offset = {x, y}; }); + resource->setSetOffset([this](CXdgPositioner* r, int32_t x, int32_t y) { state.offset = {x, y}; }); - m_resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { m_state.setAnchor(a); }); + resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { state.setAnchor(a); }); - m_resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { m_state.setGravity(g); }); + resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.setGravity(g); }); - m_resource->setSetConstraintAdjustment([this](CXdgPositioner* r, xdgPositionerConstraintAdjustment a) { m_state.constraintAdjustment = sc(a); }); + resource->setSetConstraintAdjustment([this](CXdgPositioner* r, xdgPositionerConstraintAdjustment a) { state.constraintAdjustment = (uint32_t)a; }); // TODO: support this shit better. The current impl _works_, but is lacking and could be wrong in a few cases. // doesn't matter _that_ much for now, though. } SP CXDGPositionerResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CXDGPositionerResource*)(((CXdgPositioner*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } bool CXDGPositionerResource::good() { - return m_resource->resource(); + return resource->resource(); } -CXDGPositionerRules::CXDGPositionerRules(SP positioner) : m_state(positioner->m_state) { +CXDGPositionerRules::CXDGPositionerRules(SP positioner) : state(positioner->state) { ; } CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoord) { - Log::logger->log(Log::DEBUG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord); + Debug::log(LOG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord); // padding constraint.expand(-4); - auto anchorRect = m_state.anchorRect.copy().translate(parentCoord); + auto anchorRect = state.anchorRect.copy().translate(parentCoord); - auto width = m_state.requestedSize.x; - auto height = m_state.requestedSize.y; - auto gravity = m_state.gravity; + auto width = state.requestedSize.x; + auto height = state.requestedSize.y; + auto gravity = state.gravity; - auto anchorX = m_state.anchor.left() ? anchorRect.x : m_state.anchor.right() ? anchorRect.extent().x : anchorRect.middle().x; - auto anchorY = m_state.anchor.top() ? anchorRect.y : m_state.anchor.bottom() ? anchorRect.extent().y : anchorRect.middle().y; + auto anchorX = state.anchor.left() ? anchorRect.x : state.anchor.right() ? anchorRect.extent().x : anchorRect.middle().x; + auto anchorY = state.anchor.top() ? anchorRect.y : state.anchor.bottom() ? anchorRect.extent().y : anchorRect.middle().y; auto calcEffectiveX = [&](CEdges anchorGravity, double anchorX) { return anchorGravity.left() ? anchorX - width : anchorGravity.right() ? anchorX : anchorX - width / 2; }; auto calcEffectiveY = [&](CEdges anchorGravity, double anchorY) { return anchorGravity.top() ? anchorY - height : anchorGravity.bottom() ? anchorY : anchorY - height / 2; }; auto calcRemainingWidth = [&](double effectiveX) { - auto width = m_state.requestedSize.x; + auto width = state.requestedSize.x; if (effectiveX < constraint.x) { auto diff = constraint.x - effectiveX; effectiveX = constraint.x; @@ -639,7 +597,7 @@ CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoo }; auto calcRemainingHeight = [&](double effectiveY) { - auto height = m_state.requestedSize.y; + auto height = state.requestedSize.y; if (effectiveY < constraint.y) { auto diff = constraint.y - effectiveY; effectiveY = constraint.y; @@ -660,12 +618,12 @@ CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoo // It considers the offset when deciding whether or not to flip but does not actually flip the offset, instead // applying it after the flip step. - if (m_state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) { - auto flip = (gravity.left() && effectiveX + m_state.offset.x < constraint.x) || (gravity.right() && effectiveX + m_state.offset.x + width > constraint.extent().x); + if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) { + auto flip = (gravity.left() && effectiveX + state.offset.x < constraint.x) || (gravity.right() && effectiveX + state.offset.x + width > constraint.extent().x); if (flip) { auto newGravity = gravity ^ (CEdges::LEFT | CEdges::RIGHT); - auto newAnchorX = m_state.anchor.left() ? anchorRect.extent().x : m_state.anchor.right() ? anchorRect.x : anchorX; + auto newAnchorX = state.anchor.left() ? anchorRect.extent().x : state.anchor.right() ? anchorRect.x : anchorX; auto newEffectiveX = calcEffectiveX(newGravity, newAnchorX); if (calcRemainingWidth(newEffectiveX).second > calcRemainingWidth(effectiveX).second) { @@ -676,13 +634,12 @@ CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoo } } - if (m_state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) { - auto flip = - (m_state.gravity.top() && effectiveY + m_state.offset.y < constraint.y) || (m_state.gravity.bottom() && effectiveY + m_state.offset.y + height > constraint.extent().y); + if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) { + auto flip = (state.gravity.top() && effectiveY + state.offset.y < constraint.y) || (state.gravity.bottom() && effectiveY + state.offset.y + height > constraint.extent().y); if (flip) { auto newGravity = gravity ^ (CEdges::TOP | CEdges::BOTTOM); - auto newAnchorY = m_state.anchor.top() ? anchorRect.extent().y : m_state.anchor.bottom() ? anchorRect.y : anchorY; + auto newAnchorY = state.anchor.top() ? anchorRect.extent().y : state.anchor.bottom() ? anchorRect.y : anchorY; auto newEffectiveY = calcEffectiveY(newGravity, newAnchorY); if (calcRemainingHeight(newEffectiveY).second > calcRemainingHeight(effectiveY).second) { @@ -693,12 +650,12 @@ CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoo } } - effectiveX += m_state.offset.x; - effectiveY += m_state.offset.y; + effectiveX += state.offset.x; + effectiveY += state.offset.y; // Slide order is important for the case where the window is too large to fit on screen. - if (m_state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) { + if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) { if (effectiveX + width > constraint.extent().x) effectiveX = constraint.extent().x - width; @@ -706,7 +663,7 @@ CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoo effectiveX = constraint.x; } - if (m_state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) { + if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) { if (effectiveY + height > constraint.extent().y) effectiveY = constraint.extent().y - height; @@ -714,13 +671,13 @@ CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoo effectiveY = constraint.y; } - if (m_state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) { + if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) { auto [newX, newWidth] = calcRemainingWidth(effectiveX); effectiveX = newX; width = newWidth; } - if (m_state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) { + if (state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) { auto [newY, newHeight] = calcRemainingHeight(effectiveY); effectiveY = newY; height = newHeight; @@ -729,33 +686,33 @@ CBox CXDGPositionerRules::getPosition(CBox constraint, const Vector2D& parentCoo return {effectiveX - parentCoord.x, effectiveY - parentCoord.y, width, height}; } -CXDGWMBase::CXDGWMBase(SP resource_) : m_resource(resource_) { +CXDGWMBase::CXDGWMBase(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); }); + resource->setDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); }); + resource->setOnDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); }); - m_client = m_resource->client(); + pClient = resource->client(); - m_resource->setCreatePositioner([this](CXdgWmBase* r, uint32_t id) { + resource->setCreatePositioner([this](CXdgWmBase* r, uint32_t id) { const auto RESOURCE = - PROTO::xdgShell->m_positioners.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_self.lock())); + PROTO::xdgShell->m_vPositioners.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::xdgShell->m_positioners.pop_back(); + PROTO::xdgShell->m_vPositioners.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - m_positioners.emplace_back(RESOURCE); + positioners.emplace_back(RESOURCE); - LOGM(Log::DEBUG, "New xdg_positioner at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New xdg_positioner at {:x}", (uintptr_t)RESOURCE.get()); }); - m_resource->setGetXdgSurface([this](CXdgWmBase* r, uint32_t id, wl_resource* surf) { + resource->setGetXdgSurface([this](CXdgWmBase* r, uint32_t id, wl_resource* surf) { auto SURF = CWLSurfaceResource::fromResource(surf); if UNLIKELY (!SURF) { @@ -763,129 +720,128 @@ CXDGWMBase::CXDGWMBase(SP resource_) : m_resource(resource_) { return; } - if UNLIKELY (SURF->m_role->role() != SURFACE_ROLE_UNASSIGNED) { + if UNLIKELY (SURF->role->role() != SURFACE_ROLE_UNASSIGNED) { r->error(-1, "Surface already has a different role"); return; } - const auto RESOURCE = - PROTO::xdgShell->m_surfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_self.lock(), SURF)); + const auto RESOURCE = PROTO::xdgShell->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock(), SURF)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::xdgShell->m_surfaces.pop_back(); + PROTO::xdgShell->m_vSurfaces.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - RESOURCE->m_surface = SURF; - SURF->m_role = makeShared(RESOURCE); + RESOURCE->self = RESOURCE; + RESOURCE->surface = SURF; + SURF->role = makeShared(RESOURCE); - m_surfaces.emplace_back(RESOURCE); + surfaces.emplace_back(RESOURCE); - LOGM(Log::DEBUG, "New xdg_surface at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New xdg_surface at {:x}", (uintptr_t)RESOURCE.get()); }); - m_resource->setPong([this](CXdgWmBase* r, uint32_t serial) { - g_pANRManager->onResponse(m_self.lock()); - m_events.pong.emit(); + resource->setPong([this](CXdgWmBase* r, uint32_t serial) { + g_pANRManager->onResponse(self.lock()); + events.pong.emit(); }); } bool CXDGWMBase::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CXDGWMBase::client() { - return m_client; + return pClient; } void CXDGWMBase::ping() { - m_resource->sendPing(1337); + resource->sendPing(1337); } CXDGShellProtocol::CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - m_grab = makeShared(); - m_grab->m_keyboard = true; - m_grab->m_pointer = true; - m_grab->setCallback([this]() { - for (auto const& g : m_grabbed) { + grab = makeShared(); + grab->keyboard = true; + grab->pointer = true; + grab->setCallback([this]() { + for (auto const& g : grabbed) { g->done(); } - m_grabbed.clear(); + grabbed.clear(); }); } void CXDGShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_wmBases.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vWMBases.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_wmBases.pop_back(); + m_vWMBases.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - LOGM(Log::DEBUG, "New xdg_wm_base at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New xdg_wm_base at {:x}", (uintptr_t)RESOURCE.get()); } void CXDGShellProtocol::destroyResource(CXDGWMBase* resource) { - std::erase_if(m_wmBases, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vWMBases, [&](const auto& other) { return other.get() == resource; }); } void CXDGShellProtocol::destroyResource(CXDGPositionerResource* resource) { - std::erase_if(m_positioners, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vPositioners, [&](const auto& other) { return other.get() == resource; }); } void CXDGShellProtocol::destroyResource(CXDGSurfaceResource* resource) { - std::erase_if(m_surfaces, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); } void CXDGShellProtocol::destroyResource(CXDGToplevelResource* resource) { - std::erase_if(m_toplevels, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vToplevels, [&](const auto& other) { return other.get() == resource; }); } void CXDGShellProtocol::destroyResource(CXDGPopupResource* resource) { - std::erase_if(m_popups, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == resource; }); } void CXDGShellProtocol::addOrStartGrab(SP popup) { - if (!m_grabOwner) { - m_grabOwner = popup; - m_grabbed.clear(); - m_grab->clear(); - m_grab->add(popup->m_surface->m_surface.lock()); - if (popup->m_parent) - m_grab->add(popup->m_parent->m_surface.lock()); - g_pSeatManager->setGrab(m_grab); - m_grabbed.emplace_back(popup); + if (!grabOwner) { + grabOwner = popup; + grabbed.clear(); + grab->clear(); + grab->add(popup->surface->surface.lock()); + if (popup->parent) + grab->add(popup->parent->surface.lock()); + g_pSeatManager->setGrab(grab); + grabbed.emplace_back(popup); return; } - m_grabbed.emplace_back(popup); + grabbed.emplace_back(popup); - m_grab->add(popup->m_surface->m_surface.lock()); + grab->add(popup->surface->surface.lock()); - if (popup->m_parent) - m_grab->add(popup->m_parent->m_surface.lock()); + if (popup->parent) + grab->add(popup->parent->surface.lock()); } void CXDGShellProtocol::onPopupDestroy(WP popup) { - if (popup == m_grabOwner) { + if (popup == grabOwner) { g_pSeatManager->setGrab(nullptr); - for (auto const& g : m_grabbed) { + for (auto const& g : grabbed) { g->done(); } - m_grabbed.clear(); + grabbed.clear(); return; } - std::erase(m_grabbed, popup); - if (popup->m_surface) - m_grab->remove(popup->m_surface->m_surface.lock()); + std::erase(grabbed, popup); + if (popup->surface) + grab->remove(popup->surface->surface.lock()); } -CXDGSurfaceRole::CXDGSurfaceRole(SP xdg) : m_xdgSurface(xdg) { +CXDGSurfaceRole::CXDGSurfaceRole(SP xdg) : xdgSurface(xdg) { ; } diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp index fbb6ba94..b46e0236 100644 --- a/src/protocols/XDGShell.hpp +++ b/src/protocols/XDGShell.hpp @@ -40,7 +40,7 @@ class CXDGPositionerRules { CBox getPosition(CBox constraint, const Vector2D& parentPos); private: - SXDGPositionerState m_state; + SXDGPositionerState state; }; class CXDGPopupResource { @@ -54,19 +54,19 @@ class CXDGPopupResource { void applyPositioning(const CBox& availableBox, const Vector2D& t1coord /* relative to box */); - WP m_surface; - WP m_parent; - WP m_self; + WP surface; + WP parent; + WP self; - bool m_taken = false; + bool taken = false; - CBox m_geometry; + CBox geometry; struct { - CSignalT<> reposition; - CSignalT<> dismissed; - CSignalT<> destroy; // only the role - } m_events; + CSignal reposition; + CSignal dismissed; + CSignal destroy; // only the role + } events; // schedules a configure event void configure(const CBox& box); @@ -75,13 +75,13 @@ class CXDGPopupResource { void repositioned(); private: - SP m_resource; + SP resource; - uint32_t m_lastRepositionToken = 0; + uint32_t lastRepositionToken = 0; Vector2D accumulateParentOffset(); - CXDGPositionerRules m_positionerRules; + CXDGPositionerRules positionerRules; }; class CXDGToplevelResource { @@ -91,10 +91,10 @@ class CXDGToplevelResource { static SP fromResource(wl_resource*); - WP m_owner; - WP m_self; + WP owner; + WP self; - PHLWINDOWREF m_window; + PHLWINDOWREF window; bool good(); @@ -111,11 +111,11 @@ class CXDGToplevelResource { void close(); struct { - CSignalT<> sizeLimitsChanged; - CSignalT<> stateChanged; // maximized, fs, minimized, etc. - CSignalT<> metadataChanged; // title, appid - CSignalT<> destroy; // only the role - } m_events; + CSignal sizeLimitsChanged; + CSignal stateChanged; // maximized, fs, minimized, etc. + CSignal metadataChanged; // title, appid + CSignal destroy; // only the role + } events; struct { std::string title; @@ -126,30 +126,27 @@ class CXDGToplevelResource { std::optional requestsFullscreen; std::optional requestsFullscreenMonitor; std::optional requestsMinimize; - } m_state; + } state; struct { Vector2D size; std::vector states; - } m_pendingApply; + } pendingApply; struct { Vector2D minSize = {1, 1}; Vector2D maxSize = {1337420, 694200}; - } m_pending, m_current; + } pending, current; - WP m_parent; - WP m_dialog; - - std::optional m_toplevelTag; - std::optional m_toplevelDescription; + WP parent; + WP dialog; bool anyChildModal(); - std::vector> m_children; + std::vector> children; private: - SP m_resource; + SP resource; void applyState(); }; @@ -161,7 +158,7 @@ class CXDGSurfaceRole : public ISurfaceRole { return SURFACE_ROLE_XDG_SHELL; } - WP m_xdgSurface; + WP xdgSurface; }; class CXDGSurfaceResource { @@ -173,49 +170,49 @@ class CXDGSurfaceResource { bool good(); - WP m_owner; - WP m_surface; + WP owner; + WP surface; - WP m_toplevel; - WP m_popup; + WP toplevel; + WP popup; - WP m_self; + WP self; struct { CBox geometry; - } m_pending, m_current; + } pending, current; struct { - CSignalT ack; - CSignalT<> commit; - CSignalT<> map; - CSignalT<> unmap; - CSignalT<> destroy; - CSignalT> newPopup; - } m_events; + CSignal ack; + CSignal commit; + CSignal map; + CSignal unmap; + CSignal destroy; + CSignal newPopup; // SP + } events; - bool m_initialCommit = true; - bool m_mapped = false; + bool initialCommit = true; + bool mapped = false; uint32_t scheduleConfigure(); // do not call directly void configure(); private: - SP m_resource; + SP resource; - uint32_t m_lastConfigureSerial = 0; - uint32_t m_scheduledSerial = 0; + uint32_t lastConfigureSerial = 0; + uint32_t scheduledSerial = 0; - wl_event_source* m_configureSource = nullptr; + wl_event_source* configureSource = nullptr; // - std::vector> m_popups; + std::vector> popups; struct { CHyprSignalListener surfaceDestroy; CHyprSignalListener surfaceCommit; - } m_listeners; + } listeners; friend class CXDGPopupResource; friend class CXDGToplevelResource; @@ -229,13 +226,13 @@ class CXDGPositionerResource { bool good(); - SXDGPositionerState m_state; + SXDGPositionerState state; - WP m_owner; - WP m_self; + WP owner; + WP self; private: - SP m_resource; + SP resource; }; class CXDGWMBase { @@ -246,18 +243,18 @@ class CXDGWMBase { wl_client* client(); void ping(); - std::vector> m_positioners; - std::vector> m_surfaces; + std::vector> positioners; + std::vector> surfaces; - WP m_self; + WP self; struct { - CSignalT<> pong; - } m_events; + CSignal pong; + } events; private: - SP m_resource; - wl_client* m_client = nullptr; + SP resource; + wl_client* pClient = nullptr; }; class CXDGShellProtocol : public IWaylandProtocol { @@ -274,16 +271,16 @@ class CXDGShellProtocol : public IWaylandProtocol { void destroyResource(CXDGPopupResource* resource); // - std::vector> m_wmBases; - std::vector> m_positioners; - std::vector> m_surfaces; - std::vector> m_toplevels; - std::vector> m_popups; + std::vector> m_vWMBases; + std::vector> m_vPositioners; + std::vector> m_vSurfaces; + std::vector> m_vToplevels; + std::vector> m_vPopups; // current popup grab - WP m_grabOwner; - SP m_grab; - std::vector> m_grabbed; + WP grabOwner; + SP grab; + std::vector> grabbed; void addOrStartGrab(SP popup); void onPopupDestroy(WP popup); diff --git a/src/protocols/XDGTag.cpp b/src/protocols/XDGTag.cpp deleted file mode 100644 index 84415d71..00000000 --- a/src/protocols/XDGTag.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "XDGTag.hpp" -#include "XDGShell.hpp" -#include "../desktop/view/Window.hpp" - -CXDGToplevelTagManagerResource::CXDGToplevelTagManagerResource(UP&& resource) : m_resource(std::move(resource)) { - if UNLIKELY (!good()) - return; - - m_resource->setDestroy([this](CXdgToplevelTagManagerV1* r) { PROTO::xdgTag->destroyResource(this); }); - m_resource->setOnDestroy([this](CXdgToplevelTagManagerV1* r) { PROTO::xdgTag->destroyResource(this); }); - - m_resource->setSetToplevelTag([](CXdgToplevelTagManagerV1* r, wl_resource* toplevel, const char* tag) { - auto TOPLEVEL = CXDGToplevelResource::fromResource(toplevel); - - if (!TOPLEVEL) { - r->error(-1, "Invalid toplevel handle"); - return; - } - - TOPLEVEL->m_toplevelTag = tag; - if (TOPLEVEL->m_window) - TOPLEVEL->m_window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_XDG_TAG); - }); - - m_resource->setSetToplevelDescription([](CXdgToplevelTagManagerV1* r, wl_resource* toplevel, const char* description) { - auto TOPLEVEL = CXDGToplevelResource::fromResource(toplevel); - - if (!TOPLEVEL) { - r->error(-1, "Invalid toplevel handle"); - return; - } - - TOPLEVEL->m_toplevelDescription = description; - }); -} - -bool CXDGToplevelTagManagerResource::good() { - return m_resource->resource(); -} - -CXDGToplevelTagProtocol::CXDGToplevelTagProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { - ; -} - -void CXDGToplevelTagProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = - WP{m_managers.emplace_back(makeUnique(makeUnique(client, ver, id)))}; - - if UNLIKELY (!RESOURCE->good()) { - wl_client_post_no_memory(client); - return; - } -} - -void CXDGToplevelTagProtocol::destroyResource(CXDGToplevelTagManagerResource* res) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == res; }); -} diff --git a/src/protocols/XDGTag.hpp b/src/protocols/XDGTag.hpp deleted file mode 100644 index 920d5030..00000000 --- a/src/protocols/XDGTag.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include "WaylandProtocol.hpp" -#include "xdg-toplevel-tag-v1.hpp" - -class CXDGToplevelResource; - -class CXDGToplevelTagManagerResource { - public: - CXDGToplevelTagManagerResource(UP&& resource); - - bool good(); - - private: - UP m_resource; -}; - -class CXDGToplevelTagProtocol : public IWaylandProtocol { - public: - CXDGToplevelTagProtocol(const wl_interface* iface, const int& ver, const std::string& name); - - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - - private: - void destroyResource(CXDGToplevelTagManagerResource* res); - - // - std::vector> m_managers; - - friend class CXDGToplevelTagManagerResource; -}; - -namespace PROTO { - inline UP xdgTag; -}; diff --git a/src/protocols/XWaylandShell.cpp b/src/protocols/XWaylandShell.cpp index 62c1e208..b4615e19 100644 --- a/src/protocols/XWaylandShell.cpp +++ b/src/protocols/XWaylandShell.cpp @@ -2,24 +2,24 @@ #include "core/Compositor.hpp" #include -CXWaylandSurfaceResource::CXWaylandSurfaceResource(SP resource_, SP surface_) : m_surface(surface_), m_resource(resource_) { +CXWaylandSurfaceResource::CXWaylandSurfaceResource(SP resource_, SP surface_) : surface(surface_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CXwaylandSurfaceV1* r) { + resource->setDestroy([this](CXwaylandSurfaceV1* r) { events.destroy.emit(); PROTO::xwaylandShell->destroyResource(this); }); - m_resource->setOnDestroy([this](CXwaylandSurfaceV1* r) { + resource->setOnDestroy([this](CXwaylandSurfaceV1* r) { events.destroy.emit(); PROTO::xwaylandShell->destroyResource(this); }); - m_client = m_resource->client(); + pClient = resource->client(); - m_resource->setSetSerial([this](CXwaylandSurfaceV1* r, uint32_t lo, uint32_t hi) { - m_serial = (sc(hi) << 32) + lo; - PROTO::xwaylandShell->m_events.newSurface.emit(m_self.lock()); + resource->setSetSerial([this](CXwaylandSurfaceV1* r, uint32_t lo, uint32_t hi) { + serial = (((uint64_t)hi) << 32) + lo; + PROTO::xwaylandShell->events.newSurface.emit(self.lock()); }); } @@ -28,36 +28,36 @@ CXWaylandSurfaceResource::~CXWaylandSurfaceResource() { } bool CXWaylandSurfaceResource::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CXWaylandSurfaceResource::client() { - return m_client; + return pClient; } -CXWaylandShellResource::CXWaylandShellResource(SP resource_) : m_resource(resource_) { +CXWaylandShellResource::CXWaylandShellResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CXwaylandShellV1* r) { PROTO::xwaylandShell->destroyResource(this); }); - m_resource->setOnDestroy([this](CXwaylandShellV1* r) { PROTO::xwaylandShell->destroyResource(this); }); + resource->setDestroy([this](CXwaylandShellV1* r) { PROTO::xwaylandShell->destroyResource(this); }); + resource->setOnDestroy([this](CXwaylandShellV1* r) { PROTO::xwaylandShell->destroyResource(this); }); - m_resource->setGetXwaylandSurface([](CXwaylandShellV1* r, uint32_t id, wl_resource* surface) { - const auto RESOURCE = PROTO::xwaylandShell->m_surfaces.emplace_back( + resource->setGetXwaylandSurface([](CXwaylandShellV1* r, uint32_t id, wl_resource* surface) { + const auto RESOURCE = PROTO::xwaylandShell->m_vSurfaces.emplace_back( makeShared(makeShared(r->client(), r->version(), id), CWLSurfaceResource::fromResource(surface))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::xwaylandShell->m_surfaces.pop_back(); + PROTO::xwaylandShell->m_vSurfaces.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; }); } bool CXWaylandShellResource::good() { - return m_resource->resource(); + return resource->resource(); } CXWaylandShellProtocol::CXWaylandShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -65,19 +65,19 @@ CXWaylandShellProtocol::CXWaylandShellProtocol(const wl_interface* iface, const } void CXWaylandShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CXWaylandShellProtocol::destroyResource(CXWaylandShellResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CXWaylandShellProtocol::destroyResource(CXWaylandSurfaceResource* resource) { - std::erase_if(m_surfaces, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); } diff --git a/src/protocols/XWaylandShell.hpp b/src/protocols/XWaylandShell.hpp index 346bfe88..f6f91c49 100644 --- a/src/protocols/XWaylandShell.hpp +++ b/src/protocols/XWaylandShell.hpp @@ -17,17 +17,17 @@ class CXWaylandSurfaceResource { wl_client* client(); struct { - CSignalT<> destroy; + CSignal destroy; } events; - uint64_t m_serial = 0; - WP m_surface; + uint64_t serial = 0; + WP surface; - WP m_self; + WP self; private: - SP m_resource; - wl_client* m_client = nullptr; + SP resource; + wl_client* pClient = nullptr; }; class CXWaylandShellResource { @@ -37,7 +37,7 @@ class CXWaylandShellResource { bool good(); private: - SP m_resource; + SP resource; }; class CXWaylandShellProtocol : public IWaylandProtocol { @@ -47,16 +47,16 @@ class CXWaylandShellProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); struct { - CSignalT> newSurface; // Fired when it sets a serial, otherwise it's useless - } m_events; + CSignal newSurface; // SP. Fired when it sets a serial, otherwise it's useless + } events; private: void destroyResource(CXWaylandSurfaceResource* resource); void destroyResource(CXWaylandShellResource* resource); // - std::vector> m_managers; - std::vector> m_surfaces; + std::vector> m_vManagers; + std::vector> m_vSurfaces; friend class CXWaylandSurfaceResource; friend class CXWaylandShellResource; diff --git a/src/protocols/XXColorManagement.cpp b/src/protocols/XXColorManagement.cpp new file mode 100644 index 00000000..056a2cef --- /dev/null +++ b/src/protocols/XXColorManagement.cpp @@ -0,0 +1,649 @@ +#include "XXColorManagement.hpp" +#include "../Compositor.hpp" +#include "ColorManagement.hpp" +#include "color-management-v1.hpp" +#include "types/ColorManagement.hpp" +#include "xx-color-management-v4.hpp" + +using namespace NColorManagement; + +static wpColorManagerV1TransferFunction getWPTransferFunction(xxColorManagerV4TransferFunction tf) { + switch (tf) { + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT709: + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_BT1361: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA28; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST240: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST240; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_100: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_100; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LOG_316: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_LOG_316; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_XVYCC: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_XVYCC; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_EXT_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_SRGB; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST428: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST428; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_HLG; + default: UNREACHABLE(); + } +} + +static wpColorManagerV1Primaries getWPPrimaries(xxColorManagerV4Primaries primaries) { + return (wpColorManagerV1Primaries)(primaries + 1); +} + +CXXColorManager::CXXColorManager(SP resource_) : resource(resource_) { + if UNLIKELY (!good()) + return; + + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_PARAMETRIC); + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_EXTENDED_TARGET_VOLUME); + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_MASTERING_DISPLAY_PRIMARIES); + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_PRIMARIES); + resource->sendSupportedFeature(XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES); + + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_SRGB); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_PAL); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_NTSC); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_BT2020); + // resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_CIE1931_XYZ); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3); + resource->sendSupportedPrimariesNamed(XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB); + + resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22); + resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28); + resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG); + resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB); + resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ); + resource->sendSupportedTfNamed(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR); + + resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL); + // resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE); + // resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_ABSOLUTE); + // resource->sendSupportedIntent(XX_COLOR_MANAGER_V4_RENDER_INTENT_RELATIVE_BPC); + + resource->setDestroy([](CXxColorManagerV4* r) { LOGM(TRACE, "Destroy xx_color_manager at {:x} (generated default)", (uintptr_t)r); }); + resource->setGetOutput([](CXxColorManagerV4* r, uint32_t id, wl_resource* output) { + LOGM(TRACE, "Get output for id={}, output={}", id, (uintptr_t)output); + const auto RESOURCE = + PROTO::xxColorManagement->m_vOutputs.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vOutputs.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + resource->setGetSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) { + LOGM(TRACE, "Get surface for id={}, surface={}", id, (uintptr_t)surface); + auto SURF = CWLSurfaceResource::fromResource(surface); + + if (!SURF) { + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); + r->error(-1, "Invalid surface (2)"); + return; + } + + if (SURF->colorManagement) { + r->error(XX_COLOR_MANAGER_V4_ERROR_SURFACE_EXISTS, "CM Surface already exists"); + return; + } + + const auto RESOURCE = + PROTO::xxColorManagement->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF)); + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + resource->setGetFeedbackSurface([](CXxColorManagerV4* r, uint32_t id, wl_resource* surface) { + LOGM(TRACE, "Get feedback surface for id={}, surface={}", id, (uintptr_t)surface); + auto SURF = CWLSurfaceResource::fromResource(surface); + + if (!SURF) { + LOGM(ERR, "No surface for resource {}", (uintptr_t)surface); + r->error(-1, "Invalid surface (2)"); + return; + } + + const auto RESOURCE = PROTO::xxColorManagement->m_vFeedbackSurfaces.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), SURF)); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vFeedbackSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + resource->setNewIccCreator([](CXxColorManagerV4* r, uint32_t id) { + LOGM(WARN, "New ICC creator for id={} (unsupported)", id); + r->error(XX_COLOR_MANAGER_V4_ERROR_UNSUPPORTED_FEATURE, "ICC profiles are not supported"); + }); + resource->setNewParametricCreator([](CXxColorManagerV4* r, uint32_t id) { + LOGM(TRACE, "New parametric creator for id={}", id); + + const auto RESOURCE = PROTO::xxColorManagement->m_vParametricCreators.emplace_back( + makeShared(makeShared(r->client(), r->version(), id))); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vParametricCreators.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + + resource->setOnDestroy([this](CXxColorManagerV4* r) { PROTO::xxColorManagement->destroyResource(this); }); +} + +bool CXXColorManager::good() { + return resource->resource(); +} + +CXXColorManagementOutput::CXXColorManagementOutput(SP resource_) : resource(resource_) { + if UNLIKELY (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CXxColorManagementOutputV4* r) { PROTO::xxColorManagement->destroyResource(this); }); + resource->setOnDestroy([this](CXxColorManagementOutputV4* r) { PROTO::xxColorManagement->destroyResource(this); }); + + resource->setGetImageDescription([this](CXxColorManagementOutputV4* r, uint32_t id) { + LOGM(TRACE, "Get image description for output={}, id={}", (uintptr_t)r, id); + if (imageDescription.valid()) + PROTO::xxColorManagement->destroyResource(imageDescription.get()); + + const auto RESOURCE = PROTO::xxColorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), true)); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vImageDescriptions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); +} + +bool CXXColorManagementOutput::good() { + return resource->resource(); +} + +wl_client* CXXColorManagementOutput::client() { + return pClient; +} + +CXXColorManagementSurface::CXXColorManagementSurface(SP surface_) : surface(surface_) { + // only for frog cm untill wayland cm is adopted +} + +CXXColorManagementSurface::CXXColorManagementSurface(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if UNLIKELY (!good()) + return; + + pClient = resource->client(); + + if (!surface->colorManagement.valid()) { + const auto RESOURCE = PROTO::colorManagement->m_vSurfaces.emplace_back(makeShared(surface_)); + if UNLIKELY (!RESOURCE) { + resource->noMemory(); + PROTO::colorManagement->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + surface->colorManagement = RESOURCE; + + resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Destroy wp cm and xx cm for surface {}", (uintptr_t)surface); + if (surface.valid()) + PROTO::colorManagement->destroyResource(surface->colorManagement.get()); + PROTO::xxColorManagement->destroyResource(this); + }); + } else + resource->setOnDestroy([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); + PROTO::xxColorManagement->destroyResource(this); + }); + + resource->setDestroy([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm surface {}", (uintptr_t)surface); + PROTO::xxColorManagement->destroyResource(this); + }); + + resource->setSetImageDescription([this](CXxColorManagementSurfaceV4* r, wl_resource* image_description, uint32_t render_intent) { + LOGM(TRACE, "Set image description for surface={}, desc={}, intent={}", (uintptr_t)r, (uintptr_t)image_description, render_intent); + + const auto PO = (CXxImageDescriptionV4*)wl_resource_get_user_data(image_description); + if (!PO) { // FIXME check validity + r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description creation failed"); + return; + } + if (render_intent != XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL) { + r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_RENDER_INTENT, "Unsupported render intent"); + return; + } + + const auto imageDescription = std::find_if(PROTO::xxColorManagement->m_vImageDescriptions.begin(), PROTO::xxColorManagement->m_vImageDescriptions.end(), + [&](const auto& other) { return other->resource()->resource() == image_description; }); + if (imageDescription == PROTO::xxColorManagement->m_vImageDescriptions.end()) { + r->error(XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Image description not found"); + return; + } + + if (surface.valid()) { + surface->colorManagement->setHasImageDescription(true); + surface->colorManagement->m_imageDescription = imageDescription->get()->settings; + } else + LOGM(ERR, "Set image description for invalid surface"); + }); + resource->setUnsetImageDescription([this](CXxColorManagementSurfaceV4* r) { + LOGM(TRACE, "Unset image description for surface={}", (uintptr_t)r); + if (surface.valid()) { + surface->colorManagement->m_imageDescription = SImageDescription{}; + surface->colorManagement->setHasImageDescription(false); + } else + LOGM(ERR, "Unset image description for invalid surface"); + }); +} + +bool CXXColorManagementSurface::good() { + return resource && resource->resource(); +} + +wl_client* CXXColorManagementSurface::client() { + return pClient; +} + +const SImageDescription& CXXColorManagementSurface::imageDescription() { + if (!hasImageDescription()) + LOGM(WARN, "Reading imageDescription while none set. Returns default or empty values"); + return m_imageDescription; +} + +bool CXXColorManagementSurface::hasImageDescription() { + return m_hasImageDescription; +} + +void CXXColorManagementSurface::setHasImageDescription(bool has) { + m_hasImageDescription = has; + m_needsNewMetadata = true; +} + +const hdr_output_metadata& CXXColorManagementSurface::hdrMetadata() { + return m_hdrMetadata; +} + +void CXXColorManagementSurface::setHDRMetadata(const hdr_output_metadata& metadata) { + m_hdrMetadata = metadata; + m_needsNewMetadata = false; +} + +bool CXXColorManagementSurface::needsHdrMetadataUpdate() { + return m_needsNewMetadata; +} + +CXXColorManagementFeedbackSurface::CXXColorManagementFeedbackSurface(SP resource_, SP surface_) : + surface(surface_), resource(resource_) { + if UNLIKELY (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface); + if (m_currentPreferred.valid()) + PROTO::xxColorManagement->destroyResource(m_currentPreferred.get()); + PROTO::xxColorManagement->destroyResource(this); + }); + resource->setOnDestroy([this](CXxColorManagementFeedbackSurfaceV4* r) { + LOGM(TRACE, "Destroy xx cm feedback surface {}", (uintptr_t)surface); + if (m_currentPreferred.valid()) + PROTO::xxColorManagement->destroyResource(m_currentPreferred.get()); + PROTO::xxColorManagement->destroyResource(this); + }); + + resource->setGetPreferred([this](CXxColorManagementFeedbackSurfaceV4* r, uint32_t id) { + LOGM(TRACE, "Get preferred for id {}", id); + + if (m_currentPreferred.valid()) + PROTO::xxColorManagement->destroyResource(m_currentPreferred.get()); + + const auto RESOURCE = PROTO::xxColorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), true)); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vImageDescriptions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + m_currentPreferred = RESOURCE; + + m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription(); + + RESOURCE->resource()->sendReady(id); + }); +} + +bool CXXColorManagementFeedbackSurface::good() { + return resource->resource(); +} + +wl_client* CXXColorManagementFeedbackSurface::client() { + return pClient; +} + +CXXColorManagementParametricCreator::CXXColorManagementParametricCreator(SP resource_) : resource(resource_) { + if UNLIKELY (!good()) + return; + // + pClient = resource->client(); + + resource->setOnDestroy([this](CXxImageDescriptionCreatorParamsV4* r) { PROTO::xxColorManagement->destroyResource(this); }); + + resource->setCreate([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t id) { + LOGM(TRACE, "Create image description from params for id {}", id); + + // FIXME actually check completeness + if (!valuesSet) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCOMPLETE_SET, "Missing required settings"); + return; + } + + // FIXME actually check consistency + if (!valuesSet) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCONSISTENT_SET, "Set is not consistent"); + return; + } + + const auto RESOURCE = PROTO::xxColorManagement->m_vImageDescriptions.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), false)); + + if UNLIKELY (!RESOURCE->good()) { + r->noMemory(); + PROTO::xxColorManagement->m_vImageDescriptions.pop_back(); + return; + } + + // FIXME actually check support + if (!valuesSet) { + RESOURCE->resource()->sendFailed(XX_IMAGE_DESCRIPTION_V4_CAUSE_UNSUPPORTED, "unsupported"); + return; + } + + RESOURCE->self = RESOURCE; + RESOURCE->settings = settings; + RESOURCE->resource()->sendReady(id); + + PROTO::xxColorManagement->destroyResource(this); + }); + resource->setSetTfNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t tf) { + LOGM(TRACE, "Set image description transfer function to {}", tf); + if (valuesSet & PC_TF) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function already set"); + return; + } + + switch (tf) { + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22: + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28: + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_HLG: + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR: break; + default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, "Unsupported transfer function"); return; + } + + settings.transferFunction = convertTransferFunction(getWPTransferFunction((xxColorManagerV4TransferFunction)tf)); + valuesSet |= PC_TF; + }); + resource->setSetTfPower([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t eexp) { + LOGM(TRACE, "Set image description tf power to {}", eexp); + if (valuesSet & PC_TF_POWER) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Transfer function power already set"); + return; + } + settings.transferFunctionPower = eexp / 10000.0f; + valuesSet |= PC_TF_POWER; + }); + resource->setSetPrimariesNamed([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t primaries) { + LOGM(TRACE, "Set image description primaries by name {}", primaries); + if (valuesSet & PC_PRIMARIES) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set"); + return; + } + + switch (primaries) { + case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB: + case XX_COLOR_MANAGER_V4_PRIMARIES_PAL_M: + case XX_COLOR_MANAGER_V4_PRIMARIES_PAL: + case XX_COLOR_MANAGER_V4_PRIMARIES_NTSC: + case XX_COLOR_MANAGER_V4_PRIMARIES_GENERIC_FILM: + case XX_COLOR_MANAGER_V4_PRIMARIES_BT2020: + case XX_COLOR_MANAGER_V4_PRIMARIES_DCI_P3: + case XX_COLOR_MANAGER_V4_PRIMARIES_DISPLAY_P3: + case XX_COLOR_MANAGER_V4_PRIMARIES_ADOBE_RGB: + settings.primariesNameSet = true; + settings.primariesNamed = convertPrimaries(getWPPrimaries((xxColorManagerV4Primaries)primaries)); + settings.primaries = getPrimaries(settings.primariesNamed); + valuesSet |= PC_PRIMARIES; + break; + default: r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES, "Unsupported primaries"); + } + }); + resource->setSetPrimaries( + [this](CXxImageDescriptionCreatorParamsV4* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { + LOGM(TRACE, "Set image description primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y); + if (valuesSet & PC_PRIMARIES) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Primaries already set"); + return; + } + settings.primariesNameSet = false; + settings.primaries = SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}}; + valuesSet |= PC_PRIMARIES; + }); + resource->setSetLuminances([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) { + auto min = min_lum / 10000.0f; + LOGM(TRACE, "Set image description luminances to {} - {} ({})", min, max_lum, reference_lum); + if (valuesSet & PC_LUMINANCES) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Luminances already set"); + return; + } + if (max_lum < reference_lum || reference_lum <= min) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances"); + return; + } + settings.luminances = SImageDescription::SPCLuminances{.min = min, .max = max_lum, .reference = reference_lum}; + valuesSet |= PC_LUMINANCES; + }); + resource->setSetMasteringDisplayPrimaries( + [this](CXxImageDescriptionCreatorParamsV4* r, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { + LOGM(TRACE, "Set image description mastering primaries by values r:{},{} g:{},{} b:{},{} w:{},{}", r_x, r_y, g_x, g_y, b_x, b_y, w_x, w_y); + // if (valuesSet & PC_MASTERING_PRIMARIES) { + // r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering primaries already set"); + // return; + // } + settings.masteringPrimaries = SPCPRimaries{.red = {.x = r_x, .y = r_y}, .green = {.x = g_x, .y = g_y}, .blue = {.x = b_x, .y = b_y}, .white = {.x = w_x, .y = w_y}}; + valuesSet |= PC_MASTERING_PRIMARIES; + }); + resource->setSetMasteringLuminance([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t min_lum, uint32_t max_lum) { + auto min = min_lum / 10000.0f; + LOGM(TRACE, "Set image description mastering luminances to {} - {}", min, max_lum); + // if (valuesSet & PC_MASTERING_LUMINANCES) { + // r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Mastering luminances already set"); + // return; + // } + if (min > 0 && max_lum > 0 && max_lum <= min) { + r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "Invalid luminances"); + return; + } + settings.masteringLuminances = SImageDescription::SPCMasteringLuminances{.min = min, .max = max_lum}; + valuesSet |= PC_MASTERING_LUMINANCES; + }); + resource->setSetMaxCll([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_cll) { + LOGM(TRACE, "Set image description max content light level to {}", max_cll); + // if (valuesSet & PC_CLL) { + // r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max CLL already set"); + // return; + // } + settings.maxCLL = max_cll; + valuesSet |= PC_CLL; + }); + resource->setSetMaxFall([this](CXxImageDescriptionCreatorParamsV4* r, uint32_t max_fall) { + LOGM(TRACE, "Set image description max frame-average light level to {}", max_fall); + // if (valuesSet & PC_FALL) { + // r->error(XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "Max FALL already set"); + // return; + // } + settings.maxFALL = max_fall; + valuesSet |= PC_FALL; + }); +} + +bool CXXColorManagementParametricCreator::good() { + return resource->resource(); +} + +wl_client* CXXColorManagementParametricCreator::client() { + return pClient; +} + +CXXColorManagementImageDescription::CXXColorManagementImageDescription(SP resource_, bool allowGetInformation) : + m_resource(resource_), m_allowGetInformation(allowGetInformation) { + if UNLIKELY (!good()) + return; + + pClient = m_resource->client(); + + m_resource->setDestroy([this](CXxImageDescriptionV4* r) { PROTO::xxColorManagement->destroyResource(this); }); + m_resource->setOnDestroy([this](CXxImageDescriptionV4* r) { PROTO::xxColorManagement->destroyResource(this); }); + + m_resource->setGetInformation([this](CXxImageDescriptionV4* r, uint32_t id) { + LOGM(TRACE, "Get image information for image={}, id={}", (uintptr_t)r, id); + if (!m_allowGetInformation) { + r->error(XX_IMAGE_DESCRIPTION_V4_ERROR_NO_INFORMATION, "Image descriptions doesn't allow get_information request"); + return; + } + + auto RESOURCE = makeShared(makeShared(r->client(), r->version(), id), settings); + + if UNLIKELY (!RESOURCE->good()) + r->noMemory(); + + // CXXColorManagementImageDescriptionInfo should send everything in the constructor and be ready for destroying at this point + RESOURCE.reset(); + }); +} + +bool CXXColorManagementImageDescription::good() { + return m_resource->resource(); +} + +wl_client* CXXColorManagementImageDescription::client() { + return pClient; +} + +SP CXXColorManagementImageDescription::resource() { + return m_resource; +} + +CXXColorManagementImageDescriptionInfo::CXXColorManagementImageDescriptionInfo(SP resource_, const SImageDescription& settings_) : + m_resource(resource_), settings(settings_) { + if UNLIKELY (!good()) + return; + + pClient = m_resource->client(); + + const auto toProto = [](float value) { return int32_t(std::round(value * 10000)); }; + + if (settings.icc.fd >= 0) + m_resource->sendIccFile(settings.icc.fd, settings.icc.length); + + // send preferred client paramateres + m_resource->sendPrimaries(toProto(settings.primaries.red.x), toProto(settings.primaries.red.y), toProto(settings.primaries.green.x), toProto(settings.primaries.green.y), + toProto(settings.primaries.blue.x), toProto(settings.primaries.blue.y), toProto(settings.primaries.white.x), toProto(settings.primaries.white.y)); + if (settings.primariesNameSet) + m_resource->sendPrimariesNamed(settings.primariesNamed); + m_resource->sendTfPower(std::round(settings.transferFunctionPower * 10000)); + m_resource->sendTfNamed(settings.transferFunction); + m_resource->sendLuminances(std::round(settings.luminances.min * 10000), settings.luminances.max, settings.luminances.reference); + + // send expexted display paramateres + m_resource->sendTargetPrimaries(toProto(settings.masteringPrimaries.red.x), toProto(settings.masteringPrimaries.red.y), toProto(settings.masteringPrimaries.green.x), + toProto(settings.masteringPrimaries.green.y), toProto(settings.masteringPrimaries.blue.x), toProto(settings.masteringPrimaries.blue.y), + toProto(settings.masteringPrimaries.white.x), toProto(settings.masteringPrimaries.white.y)); + m_resource->sendTargetLuminance(std::round(settings.masteringLuminances.min * 10000), settings.masteringLuminances.max); + m_resource->sendTargetMaxCll(settings.maxCLL); + m_resource->sendTargetMaxFall(settings.maxFALL); + + m_resource->sendDone(); +} + +bool CXXColorManagementImageDescriptionInfo::good() { + return m_resource->resource(); +} + +wl_client* CXXColorManagementImageDescriptionInfo::client() { + return pClient; +} + +CXXColorManagementProtocol::CXXColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CXXColorManagementProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if UNLIKELY (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + LOGM(TRACE, "New xx_color_manager at {:x}", (uintptr_t)RESOURCE.get()); +} + +void CXXColorManagementProtocol::onImagePreferredChanged() { + for (auto const& feedback : m_vFeedbackSurfaces) { + feedback->resource->sendPreferredChanged(); + } +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManager* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementOutput* resource) { + std::erase_if(m_vOutputs, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementSurface* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementFeedbackSurface* resource) { + std::erase_if(m_vFeedbackSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementParametricCreator* resource) { + std::erase_if(m_vParametricCreators, [&](const auto& other) { return other.get() == resource; }); +} + +void CXXColorManagementProtocol::destroyResource(CXXColorManagementImageDescription* resource) { + std::erase_if(m_vImageDescriptions, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/XXColorManagement.hpp b/src/protocols/XXColorManagement.hpp new file mode 100644 index 00000000..96e99752 --- /dev/null +++ b/src/protocols/XXColorManagement.hpp @@ -0,0 +1,188 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "core/Compositor.hpp" +#include "xx-color-management-v4.hpp" +#include "types/ColorManagement.hpp" + +class CXXColorManager; +class CXXColorManagementOutput; +class CXXColorManagementImageDescription; +class CXXColorManagementProtocol; + +class CXXColorManager { + public: + CXXColorManager(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CXXColorManagementOutput { + public: + CXXColorManagementOutput(SP resource_); + + bool good(); + wl_client* client(); + + WP self; + WP imageDescription; + + private: + SP resource; + wl_client* pClient = nullptr; + + friend class CXXColorManagementProtocol; + friend class CXXColorManagementImageDescription; +}; + +class CXXColorManagementSurface { + public: + CXXColorManagementSurface(SP surface_); // temporary interface for frog CM + CXXColorManagementSurface(SP resource_, SP surface_); + + bool good(); + wl_client* client(); + + WP self; + WP surface; + + const NColorManagement::SImageDescription& imageDescription(); + bool hasImageDescription(); + void setHasImageDescription(bool has); + const hdr_output_metadata& hdrMetadata(); + void setHDRMetadata(const hdr_output_metadata& metadata); + bool needsHdrMetadataUpdate(); + + private: + SP resource; + wl_client* pClient = nullptr; + NColorManagement::SImageDescription m_imageDescription; + bool m_hasImageDescription = false; + bool m_needsNewMetadata = false; + hdr_output_metadata m_hdrMetadata; + + friend class CFrogColorManagementSurface; +}; + +class CXXColorManagementFeedbackSurface { + public: + CXXColorManagementFeedbackSurface(SP resource_, SP surface_); + + bool good(); + wl_client* client(); + + WP self; + WP surface; + + private: + SP resource; + wl_client* pClient = nullptr; + + WP m_currentPreferred; + + friend class CXXColorManagementProtocol; +}; + +class CXXColorManagementParametricCreator { + public: + CXXColorManagementParametricCreator(SP resource_); + + bool good(); + wl_client* client(); + + WP self; + + NColorManagement::SImageDescription settings; + + private: + enum eValuesSet : uint32_t { // NOLINT + PC_TF = (1 << 0), + PC_TF_POWER = (1 << 1), + PC_PRIMARIES = (1 << 2), + PC_LUMINANCES = (1 << 3), + PC_MASTERING_PRIMARIES = (1 << 4), + PC_MASTERING_LUMINANCES = (1 << 5), + PC_CLL = (1 << 6), + PC_FALL = (1 << 7), + }; + + SP resource; + wl_client* pClient = nullptr; + uint32_t valuesSet = 0; // enum eValuesSet +}; + +class CXXColorManagementImageDescription { + public: + CXXColorManagementImageDescription(SP resource_, bool allowGetInformation); + + bool good(); + wl_client* client(); + SP resource(); + + WP self; + + NColorManagement::SImageDescription settings; + + private: + SP m_resource; + wl_client* pClient = nullptr; + bool m_allowGetInformation = false; + + friend class CXXColorManagementOutput; +}; + +class CXXColorManagementImageDescriptionInfo { + public: + CXXColorManagementImageDescriptionInfo(SP resource_, const NColorManagement::SImageDescription& settings_); + + bool good(); + wl_client* client(); + + private: + SP m_resource; + wl_client* pClient = nullptr; + NColorManagement::SImageDescription settings; +}; + +class CXXColorManagementProtocol : public IWaylandProtocol { + public: + CXXColorManagementProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + void onImagePreferredChanged(); + + private: + void destroyResource(CXXColorManager* resource); + void destroyResource(CXXColorManagementOutput* resource); + void destroyResource(CXXColorManagementSurface* resource); + void destroyResource(CXXColorManagementFeedbackSurface* resource); + void destroyResource(CXXColorManagementParametricCreator* resource); + void destroyResource(CXXColorManagementImageDescription* resource); + + std::vector> m_vManagers; + std::vector> m_vOutputs; + std::vector> m_vSurfaces; + std::vector> m_vFeedbackSurfaces; + std::vector> m_vParametricCreators; + std::vector> m_vImageDescriptions; + + friend class CXXColorManager; + friend class CXXColorManagementOutput; + friend class CXXColorManagementSurface; + friend class CXXColorManagementFeedbackSurface; + friend class CXXColorManagementParametricCreator; + friend class CXXColorManagementImageDescription; + + friend class CFrogColorManagementSurface; +}; + +namespace PROTO { + inline UP xxColorManagement; +}; diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp index 06f430b9..2dd374a8 100644 --- a/src/protocols/core/Compositor.cpp +++ b/src/protocols/core/Compositor.cpp @@ -1,5 +1,4 @@ #include "Compositor.hpp" -#include "../../Compositor.hpp" #include "Output.hpp" #include "Seat.hpp" #include "../types/WLBuffer.hpp" @@ -8,18 +7,15 @@ #include "Subcompositor.hpp" #include "../Viewporter.hpp" #include "../../helpers/Monitor.hpp" +#include "../../helpers/sync/SyncReleaser.hpp" #include "../PresentationTime.hpp" #include "../DRMSyncobj.hpp" -#include "../types/DMABuffer.hpp" #include "../../render/Renderer.hpp" #include "config/ConfigValue.hpp" -#include "../../managers/eventLoop/EventLoopManager.hpp" #include "protocols/types/SurfaceRole.hpp" #include "render/Texture.hpp" #include -using namespace NColorManagement; - class CDefaultSurfaceRole : public ISurfaceRole { public: virtual eSurfaceRole role() { @@ -27,344 +23,253 @@ class CDefaultSurfaceRole : public ISurfaceRole { } }; -CWLCallbackResource::CWLCallbackResource(SP&& resource_) : m_resource(std::move(resource_)) { +CWLCallbackResource::CWLCallbackResource(SP resource_) : resource(resource_) { ; } bool CWLCallbackResource::good() { - return m_resource && m_resource->resource(); + return resource->resource(); } -void CWLCallbackResource::send(const Time::steady_tp& now) { - if (!good()) - return; - - m_resource->sendDone(Time::millis(now)); - m_resource.reset(); +void CWLCallbackResource::send(timespec* now) { + resource->sendDone(now->tv_sec * 1000 + now->tv_nsec / 1000000); } -CWLRegionResource::CWLRegionResource(SP resource_) : m_resource(resource_) { +CWLRegionResource::CWLRegionResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setDestroy([this](CWlRegion* r) { PROTO::compositor->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlRegion* r) { PROTO::compositor->destroyResource(this); }); + resource->setDestroy([this](CWlRegion* r) { PROTO::compositor->destroyResource(this); }); + resource->setOnDestroy([this](CWlRegion* r) { PROTO::compositor->destroyResource(this); }); - m_resource->setAdd([this](CWlRegion* r, int32_t x, int32_t y, int32_t w, int32_t h) { m_region.add(CBox{x, y, w, h}); }); - m_resource->setSubtract([this](CWlRegion* r, int32_t x, int32_t y, int32_t w, int32_t h) { m_region.subtract(CBox{x, y, w, h}); }); + resource->setAdd([this](CWlRegion* r, int32_t x, int32_t y, int32_t w, int32_t h) { region.add(CBox{x, y, w, h}); }); + resource->setSubtract([this](CWlRegion* r, int32_t x, int32_t y, int32_t w, int32_t h) { region.subtract(CBox{x, y, w, h}); }); } bool CWLRegionResource::good() { - return m_resource->resource(); + return resource->resource(); } SP CWLRegionResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CWLRegionResource*)(((CWlRegion*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } -CWLSurfaceResource::CWLSurfaceResource(SP resource_) : m_resource(resource_) { +CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_client = m_resource->client(); + pClient = resource->client(); - m_resource->setData(this); + resource->setData(this); - m_role = makeShared(); + role = makeShared(); - m_resource->setDestroy([this](CWlSurface* r) { destroy(); }); - m_resource->setOnDestroy([this](CWlSurface* r) { destroy(); }); + resource->setDestroy([this](CWlSurface* r) { destroy(); }); + resource->setOnDestroy([this](CWlSurface* r) { destroy(); }); - m_resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) { - m_pending.updated.bits.buffer = true; - m_pending.updated.bits.offset = true; + resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) { + pending.offset = {x, y}; + pending.newBuffer = true; - m_pending.offset = {x, y}; - - if (m_pending.buffer) - m_pending.buffer.drop(); - - auto buf = buffer ? CWLBufferResource::fromResource(buffer) : nullptr; - - if (buf && buf->m_buffer) { - m_pending.buffer = CHLBufferReference(buf->m_buffer.lock()); - m_pending.texture = buf->m_buffer->m_texture; - m_pending.size = buf->m_buffer->size; - m_pending.bufferSize = buf->m_buffer->size; + if (!buffer) { + pending.buffer.reset(); + pending.texture.reset(); } else { - m_pending.buffer = {}; - m_pending.texture.reset(); - m_pending.size = Vector2D{}; - m_pending.bufferSize = Vector2D{}; + auto res = CWLBufferResource::fromResource(buffer); + pending.buffer = res && res->buffer ? makeShared(res->buffer.lock(), self.lock()) : nullptr; + pending.size = res && res->buffer ? res->buffer->size : Vector2D{}; + pending.texture = res && res->buffer ? res->buffer->texture : nullptr; + pending.bufferSize = res && res->buffer ? res->buffer->size : Vector2D{}; } - if (m_pending.bufferSize != m_current.bufferSize) { - m_pending.updated.bits.damage = true; - m_pending.bufferDamage = CBox{{}, m_pending.bufferSize}; - } + Vector2D oldBufSize = current.buffer ? current.bufferSize : Vector2D{}; + Vector2D newBufSize = pending.buffer ? pending.bufferSize : Vector2D{}; + + if (oldBufSize != newBufSize || current.buffer != pending.buffer) + pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; }); - m_resource->setCommit([this](CWlSurface* r) { - if (m_pending.buffer) - m_pending.bufferDamage.intersect(CBox{{}, m_pending.bufferSize}); + resource->setCommit([this](CWlSurface* r) { + if (pending.buffer) + pending.bufferDamage.intersect(CBox{{}, pending.bufferSize}); - if (!m_pending.buffer) - m_pending.size = {}; - else if (m_pending.viewport.hasDestination) - m_pending.size = m_pending.viewport.destination; - else if (m_pending.viewport.hasSource) - m_pending.size = m_pending.viewport.source.size(); + if (!pending.buffer) + pending.size = {}; + else if (pending.viewport.hasDestination) + pending.size = pending.viewport.destination; + else if (pending.viewport.hasSource) + pending.size = pending.viewport.source.size(); else { - Vector2D tfs = m_pending.transform % 2 == 1 ? Vector2D{m_pending.bufferSize.y, m_pending.bufferSize.x} : m_pending.bufferSize; - m_pending.size = tfs / m_pending.scale; + Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.bufferSize.y, pending.bufferSize.x} : pending.bufferSize; + pending.size = tfs / pending.scale; } - m_pending.damage.intersect(CBox{{}, m_pending.size}); + pending.damage.intersect(CBox{{}, pending.size}); - m_events.precommit.emit(); - if (m_pending.rejected) { - m_pending.rejected = false; + events.precommit.emit(); + if (pending.rejected) { dropPendingBuffer(); return; } - // null buffer attached - if (!m_pending.buffer && m_pending.updated.bits.buffer) { - commitState(m_pending); - - // remove any pending states. - m_stateQueue.clear(); - m_pending.reset(); - return; - } - - // save state while we wait for buffer to become ready - auto state = m_stateQueue.enqueue(makeUnique(m_pending)); - m_pending.reset(); - - // fifo and fences first - m_events.stateCommit.emit(state); - - if (state->buffer && state->buffer->type() == Aquamarine::BUFFER_TYPE_DMABUF && state->buffer->dmabuf().success && !state->updated.bits.acquire) { - state->buffer->m_syncFd = dc(state->buffer.m_buffer.get())->exportSyncFile(); - if (state->buffer->m_syncFd.isValid()) - m_stateQueue.lock(state, LOCK_REASON_FENCE); - } - - // now for timer. - m_events.stateCommit2.emit(state); - - if (state->rejected) { - m_stateQueue.dropState(state); - return; - } - - scheduleState(state); + if (!syncobj) + commitPendingState(pending); }); - m_resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { - m_pending.updated.bits.damage = true; - m_pending.damage.add(CBox{x, y, w, h}); - }); - m_resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { - m_pending.updated.bits.damage = true; - const auto damageSize = Vector2D(w, h); + resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.damage.add(CBox{x, y, w, h}); }); + resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.bufferDamage.add(CBox{x, y, w, h}); }); - if (damageSize > m_pending.bufferSize) - m_pending.bufferDamage.add(CBox{{x, y}, m_pending.bufferSize}); - else - m_pending.bufferDamage.add(CBox{{x, y}, damageSize}); - }); - - m_resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) { - if (scale == m_pending.scale) - return; - - m_pending.updated.bits.scale = true; - m_pending.updated.bits.damage = true; - - m_pending.scale = scale; - m_pending.bufferDamage = CBox{{}, m_pending.bufferSize}; - }); - - m_resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { - if (tr == m_pending.transform) - return; - - m_pending.updated.bits.transform = true; - m_pending.updated.bits.damage = true; - - m_pending.transform = sc(tr); - m_pending.bufferDamage = CBox{{}, m_pending.bufferSize}; - }); - - m_resource->setSetInputRegion([this](CWlSurface* r, wl_resource* region) { - m_pending.updated.bits.input = true; + resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) { pending.scale = scale; }); + resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { pending.transform = (wl_output_transform)tr; }); + resource->setSetInputRegion([this](CWlSurface* r, wl_resource* region) { if (!region) { - m_pending.input = CBox{{}, Vector2D{INT32_MAX - 1, INT32_MAX - 1}}; + pending.input = CBox{{}, {INT32_MAX, INT32_MAX}}; return; } - auto RG = CWLRegionResource::fromResource(region); - m_pending.input = RG->m_region; + auto RG = CWLRegionResource::fromResource(region); + pending.input = RG->region; }); - m_resource->setSetOpaqueRegion([this](CWlSurface* r, wl_resource* region) { - m_pending.updated.bits.opaque = true; - + resource->setSetOpaqueRegion([this](CWlSurface* r, wl_resource* region) { if (!region) { - m_pending.opaque = CBox{{}, {}}; + pending.opaque = CBox{{}, {}}; return; } - auto RG = CWLRegionResource::fromResource(region); - m_pending.opaque = RG->m_region; + auto RG = CWLRegionResource::fromResource(region); + pending.opaque = RG->region; }); - m_resource->setFrame([this](CWlSurface* r, uint32_t id) { - m_pending.updated.bits.frame = true; - m_pending.callbacks.emplace_back(makeShared(makeShared(m_client, 1, id))); - }); + resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared(makeShared(pClient, 1, id))); }); - m_resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { - m_pending.updated.bits.offset = true; - m_pending.offset = {x, y}; - }); + resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { pending.offset = {x, y}; }); } CWLSurfaceResource::~CWLSurfaceResource() { - m_events.destroy.emit(); + events.destroy.emit(); } void CWLSurfaceResource::destroy() { - if (m_mapped) { - m_events.unmap.emit(); + if (mapped) { + events.unmap.emit(); unmap(); } - m_events.destroy.emit(); + events.destroy.emit(); releaseBuffers(false); PROTO::compositor->destroyResource(this); } void CWLSurfaceResource::dropPendingBuffer() { - m_pending.buffer = {}; + pending.buffer.reset(); } void CWLSurfaceResource::dropCurrentBuffer() { - m_current.buffer = {}; + current.buffer.reset(); } SP CWLSurfaceResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CWLSurfaceResource*)(((CWlSurface*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } bool CWLSurfaceResource::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CWLSurfaceResource::client() { - return m_client; + return pClient; } void CWLSurfaceResource::enter(PHLMONITOR monitor) { - if (std::ranges::find(m_enteredOutputs, monitor) != m_enteredOutputs.end()) + if (std::find(enteredOutputs.begin(), enteredOutputs.end(), monitor) != enteredOutputs.end()) return; - if UNLIKELY (!PROTO::outputs.contains(monitor->m_name)) { + if UNLIKELY (!PROTO::outputs.contains(monitor->szName)) { // can happen on unplug/replug - LOGM(Log::ERR, "enter() called on a non-existent output global"); + LOGM(ERR, "enter() called on a non-existent output global"); return; } - if UNLIKELY (PROTO::outputs.at(monitor->m_name)->isDefunct()) { - LOGM(Log::ERR, "enter() called on a defunct output global"); + if UNLIKELY (PROTO::outputs.at(monitor->szName)->isDefunct()) { + LOGM(ERR, "enter() called on a defunct output global"); return; } - auto outputs = PROTO::outputs.at(monitor->m_name)->outputResourcesFrom(m_client); + auto output = PROTO::outputs.at(monitor->szName)->outputResourceFrom(pClient); - if UNLIKELY (outputs.empty() || std::ranges::all_of(outputs, [](const auto& o) { return !o->getResource() || !o->getResource()->resource(); })) { - LOGM(Log::ERR, "Cannot enter surface {:x} to {}, client hasn't bound the output", (uintptr_t)this, monitor->m_name); + if UNLIKELY (!output || !output->getResource() || !output->getResource()->resource()) { + LOGM(ERR, "Cannot enter surface {:x} to {}, client hasn't bound the output", (uintptr_t)this, monitor->szName); return; } - m_enteredOutputs.emplace_back(monitor); + enteredOutputs.emplace_back(monitor); - for (const auto& o : outputs) { - if (!o->getResource() || !o->getResource()->resource()) - continue; - m_resource->sendEnter(o->getResource().get()); - } - m_events.enter.emit(monitor); + resource->sendEnter(output->getResource().get()); } void CWLSurfaceResource::leave(PHLMONITOR monitor) { - if UNLIKELY (std::ranges::find(m_enteredOutputs, monitor) == m_enteredOutputs.end()) + if UNLIKELY (std::find(enteredOutputs.begin(), enteredOutputs.end(), monitor) == enteredOutputs.end()) return; - auto outputs = PROTO::outputs.at(monitor->m_name)->outputResourcesFrom(m_client); + auto output = PROTO::outputs.at(monitor->szName)->outputResourceFrom(pClient); - if UNLIKELY (outputs.empty() || std::ranges::all_of(outputs, [](const auto& o) { return !o->getResource() || !o->getResource()->resource(); })) { - LOGM(Log::ERR, "Cannot leave surface {:x} from {}, client hasn't bound the output", (uintptr_t)this, monitor->m_name); + if UNLIKELY (!output) { + LOGM(ERR, "Cannot leave surface {:x} from {}, client hasn't bound the output", (uintptr_t)this, monitor->szName); return; } - std::erase(m_enteredOutputs, monitor); + std::erase(enteredOutputs, monitor); - for (const auto& o : outputs) { - if (!o->getResource() || !o->getResource()->resource()) - continue; - m_resource->sendLeave(o->getResource().get()); - } - m_events.leave.emit(monitor); + resource->sendLeave(output->getResource().get()); } void CWLSurfaceResource::sendPreferredTransform(wl_output_transform t) { - if (m_resource->version() < 6) + if (resource->version() < 6) return; - m_resource->sendPreferredBufferTransform(t); + resource->sendPreferredBufferTransform(t); } void CWLSurfaceResource::sendPreferredScale(int32_t scale) { - if (m_resource->version() < 6) + if (resource->version() < 6) return; - m_resource->sendPreferredBufferScale(scale); + resource->sendPreferredBufferScale(scale); } -void CWLSurfaceResource::frame(const Time::steady_tp& now) { - if (m_current.callbacks.empty()) +void CWLSurfaceResource::frame(timespec* now) { + if (callbacks.empty()) return; - for (auto const& c : m_current.callbacks) { + for (auto const& c : callbacks) { c->send(now); } - m_current.callbacks.clear(); + callbacks.clear(); } void CWLSurfaceResource::resetRole() { - m_role = makeShared(); + role = makeShared(); } void CWLSurfaceResource::bfHelper(std::vector> const& nodes, std::function, const Vector2D&, void*)> fn, void* data) { + std::vector> nodes2; nodes2.reserve(nodes.size() * 2); // first, gather all nodes below for (auto const& n : nodes) { - std::erase_if(n->m_subsurfaces, [](const auto& e) { return e.expired(); }); + std::erase_if(n->subsurfaces, [](const auto& e) { return e.expired(); }); // subsurfaces is sorted lowest -> highest - for (auto const& c : n->m_subsurfaces) { - if (c->m_zIndex >= 0) + for (auto const& c : n->subsurfaces) { + if (c->zIndex >= 0) break; - if (c->m_surface.expired()) + if (c->surface.expired()) continue; - nodes2.emplace_back(c->m_surface.lock()); + nodes2.push_back(c->surface.lock()); } } @@ -375,8 +280,8 @@ void CWLSurfaceResource::bfHelper(std::vector> const& nod for (auto const& n : nodes) { Vector2D offset = {}; - if (n->m_role->role() == SURFACE_ROLE_SUBSURFACE) { - auto subsurface = sc(n->m_role.get())->m_subsurface.lock(); + if (n->role->role() == SURFACE_ROLE_SUBSURFACE) { + auto subsurface = ((CSubsurfaceRole*)n->role.get())->subsurface.lock(); offset = subsurface->posRelativeToParent(); } @@ -384,12 +289,12 @@ void CWLSurfaceResource::bfHelper(std::vector> const& nod } for (auto const& n : nodes) { - for (auto const& c : n->m_subsurfaces) { - if (c->m_zIndex < 0) + for (auto const& c : n->subsurfaces) { + if (c->zIndex < 0) continue; - if (c->m_surface.expired()) + if (c->surface.expired()) continue; - nodes2.emplace_back(c->m_surface.lock()); + nodes2.push_back(c->surface.lock()); } } @@ -399,17 +304,17 @@ void CWLSurfaceResource::bfHelper(std::vector> const& nod void CWLSurfaceResource::breadthfirst(std::function, const Vector2D&, void*)> fn, void* data) { std::vector> surfs; - surfs.emplace_back(m_self.lock()); + surfs.push_back(self.lock()); bfHelper(surfs, fn, data); } SP CWLSurfaceResource::findFirstPreorderHelper(SP root, std::function)> fn) { if (fn(root)) return root; - for (auto const& sub : root->m_subsurfaces) { - if (sub.expired() || sub->m_surface.expired()) + for (auto const& sub : root->subsurfaces) { + if (sub.expired() || sub->surface.expired()) continue; - const auto found = findFirstPreorderHelper(sub->m_surface.lock(), fn); + const auto found = findFirstPreorderHelper(sub->surface.lock(), fn); if (found) return found; } @@ -417,11 +322,7 @@ SP CWLSurfaceResource::findFirstPreorderHelper(SP CWLSurfaceResource::findFirstPreorder(std::function)> fn) { - return findFirstPreorderHelper(m_self.lock(), fn); -} - -SP CWLSurfaceResource::findWithCM() { - return findFirstPreorder([this](SP surf) { return surf->m_colorManagement.valid() && surf->extends() == extends(); }); + return findFirstPreorderHelper(self.lock(), fn); } std::pair, Vector2D> CWLSurfaceResource::at(const Vector2D& localCoords, bool allowsInput) { @@ -430,11 +331,11 @@ std::pair, Vector2D> CWLSurfaceResource::at(const Vector2 for (auto const& [surf, pos] : surfs | std::views::reverse) { if (!allowsInput) { - const auto BOX = CBox{pos, surf->m_current.size}; + const auto BOX = CBox{pos, surf->current.size}; if (BOX.containsPoint(localCoords)) return {surf, localCoords - pos}; } else { - const auto REGION = surf->m_current.input.copy().intersect(CBox{{}, surf->m_current.size}).translate(pos); + const auto REGION = surf->current.input.copy().intersect(CBox{{}, surf->current.size}).translate(pos); if (REGION.containsPoint(localCoords)) return {surf, localCoords - pos}; } @@ -444,26 +345,28 @@ std::pair, Vector2D> CWLSurfaceResource::at(const Vector2 } uint32_t CWLSurfaceResource::id() { - return wl_resource_get_id(m_resource->resource()); + return wl_resource_get_id(resource->resource()); } void CWLSurfaceResource::map() { - if UNLIKELY (m_mapped) + if UNLIKELY (mapped) return; - m_mapped = true; + mapped = true; - frame(Time::steadyNow()); + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + frame(&now); - m_current.bufferDamage = CBox{{}, m_current.bufferSize}; - m_pending.bufferDamage = CBox{{}, m_pending.bufferSize}; + current.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; + pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; } void CWLSurfaceResource::unmap() { - if UNLIKELY (!m_mapped) + if UNLIKELY (!mapped) return; - m_mapped = false; + mapped = false; // release the buffers. // this is necessary for XWayland to function correctly, @@ -478,200 +381,90 @@ void CWLSurfaceResource::releaseBuffers(bool onlyCurrent) { } void CWLSurfaceResource::error(int code, const std::string& str) { - m_resource->error(code, str); + resource->error(code, str); } SP CWLSurfaceResource::getResource() { - return m_resource; + return resource; } CBox CWLSurfaceResource::extends() { - CRegion full = CBox{{}, m_current.size}; + CRegion full = CBox{{}, current.size}; breadthfirst( [](SP surf, const Vector2D& offset, void* d) { - if (surf->m_role->role() != SURFACE_ROLE_SUBSURFACE) + if (surf->role->role() != SURFACE_ROLE_SUBSURFACE) return; - sc(d)->add(CBox{offset, surf->m_current.size}); + ((CRegion*)d)->add(CBox{offset, surf->current.size}); }, &full); return full.getExtents(); } -void CWLSurfaceResource::scheduleState(WP state) { - auto whenReadable = [this, surf = m_self](auto state, auto reason) { - if (!surf || !state) - return; - - m_stateQueue.unlock(state, reason); - }; - - if (state->updated.bits.acquire) { - // wait on acquire point for this surface, from explicit sync protocol - if (!state->acquire.addWaiter([state, whenReadable]() { whenReadable(state, LOCK_REASON_FENCE); })) { - Log::logger->log(Log::ERR, "Failed to addWaiter in CWLSurfaceResource::scheduleState"); - whenReadable(state, LOCK_REASON_FENCE); - } - } else if (state->buffer && state->buffer->isSynchronous()) { - // synchronous (shm) buffers can be read immediately - m_stateQueue.unlock(state, LOCK_REASON_FENCE); - } else if (state->buffer && state->buffer->m_syncFd.isValid()) { - // async buffer and is dmabuf, then we can wait on implicit fences - g_pEventLoopManager->doOnReadable(std::move(state->buffer->m_syncFd), [state, whenReadable]() { whenReadable(state, LOCK_REASON_FENCE); }); - } else { - // state commit without a buffer. - m_stateQueue.tryProcess(); +void CWLSurfaceResource::commitPendingState(SSurfaceState& state) { + auto lastTexture = current.texture; + if (state.newBuffer) { + state.newBuffer = false; + current = state; + state.damage.clear(); + state.bufferDamage.clear(); + state.buffer.reset(); } -} -void CWLSurfaceResource::commitState(SSurfaceState& state) { - // TODO might be incorrect. needed for VRR with FIFO to avoid same buffer extra frames for second commit when it's used in this way: - // wp_fifo_v1#43.set_barrier() - // wp_fifo_v1#43.wait_barrier() - // wl_surface#3.commit() - // wp_fifo_v1#43.wait_barrier() - // wl_surface#3.commit() - if (!state.updated.all && m_mapped && state.fifoScheduled) - return; - - auto lastTexture = m_current.texture; - m_current.updateFrom(state); - - if (m_current.buffer) { - if (m_current.buffer->isSynchronous()) - m_current.updateSynchronousTexture(lastTexture); + if (current.buffer) { + if (current.buffer->buffer->isSynchronous()) + current.updateSynchronousTexture(lastTexture); // if the surface is a cursor, update the shm buffer // TODO: don't update the entire texture - if (m_role->role() == SURFACE_ROLE_CURSOR) - updateCursorShm(m_current.accumulateBufferDamage()); + if (role->role() == SURFACE_ROLE_CURSOR) + updateCursorShm(current.accumulateBufferDamage()); } - if (m_current.texture) - m_current.texture->m_transform = Math::wlTransformToHyprutils(m_current.transform); + if (current.texture) + current.texture->m_eTransform = wlTransformToHyprutils(current.transform); - if (m_role->role() == SURFACE_ROLE_SUBSURFACE) { - auto subsurface = sc(m_role.get())->m_subsurface.lock(); - if (subsurface->m_sync) + if (role->role() == SURFACE_ROLE_SUBSURFACE) { + auto subsurface = ((CSubsurfaceRole*)role.get())->subsurface.lock(); + if (subsurface->sync) return; - m_events.commit.emit(); + events.commit.emit(); } else { // send commit to all synced surfaces in this tree. breadthfirst( [](SP surf, const Vector2D& offset, void* data) { - if (surf->m_role->role() == SURFACE_ROLE_SUBSURFACE) { - auto subsurface = sc(surf->m_role.get())->m_subsurface.lock(); - if (!subsurface->m_sync) + if (surf->role->role() == SURFACE_ROLE_SUBSURFACE) { + auto subsurface = ((CSubsurfaceRole*)surf->role.get())->subsurface.lock(); + if (!subsurface->sync) return; } - surf->m_events.commit.emit(); + surf->events.commit.emit(); }, nullptr); } - // release the buffer if it's synchronous (SHM) as updateSynchronousTexture() has copied the buffer data to a GPU tex + // release the buffer if it's synchronous (SHM) as update() has done everything thats needed + // so we can let the app know we're done. // if it doesn't have a role, we can't release it yet, in case it gets turned into a cursor. - if (m_current.buffer && m_current.buffer->isSynchronous() && m_role->role() != SURFACE_ROLE_UNASSIGNED) + if (current.buffer && current.buffer->buffer && current.buffer->buffer->isSynchronous() && role->role() != SURFACE_ROLE_UNASSIGNED) dropCurrentBuffer(); } -PImageDescription CWLSurfaceResource::getPreferredImageDescription() { - static const auto PFORCE_HDR = CConfigValue("quirks:prefer_hdr"); - const auto WINDOW = m_hlSurface ? Desktop::View::CWindow::fromView(m_hlSurface->view()) : nullptr; - - if (*PFORCE_HDR == 1 || (*PFORCE_HDR == 2 && m_hlSurface && WINDOW && WINDOW->m_class == "gamescope")) - return g_pCompositor->getHDRImageDescription(); - - auto parent = m_self; - if (parent->m_role->role() == SURFACE_ROLE_SUBSURFACE) { - auto subsurface = sc(parent->m_role.get())->m_subsurface.lock(); - parent = subsurface->t1Parent(); - } - WP monitor; - if (parent->m_enteredOutputs.size() == 1) - monitor = parent->m_enteredOutputs[0]; - else if (m_hlSurface.valid() && WINDOW) - monitor = WINDOW->m_monitor; - - return monitor ? monitor->m_imageDescription : g_pCompositor->getPreferredImageDescription(); -} - -void CWLSurfaceResource::sortSubsurfaces() { - std::ranges::sort(m_subsurfaces, [](const auto& a, const auto& b) { return a->m_zIndex < b->m_zIndex; }); - - // find the first non-negative index. We will preserve negativity: e.g. -2, -1, 1, 2 - int firstNonNegative = -1; - for (size_t i = 0; i < m_subsurfaces.size(); ++i) { - if (m_subsurfaces.at(i)->m_zIndex >= 0) { - firstNonNegative = i; - break; - } - } - - if (firstNonNegative == -1) - firstNonNegative = m_subsurfaces.size(); - - for (size_t i = firstNonNegative; i < m_subsurfaces.size(); ++i) { - m_subsurfaces.at(i)->m_zIndex = i - firstNonNegative; - } - - for (int i = 0; i < firstNonNegative; ++i) { - m_subsurfaces.at(i)->m_zIndex = -firstNonNegative + i; - } -} - -bool CWLSurfaceResource::hasVisibleSubsurface() { - for (auto const& subsurface : m_subsurfaces) { - if (!subsurface || !subsurface->m_surface) - continue; - - const auto& surf = subsurface->m_surface; - if (surf->m_current.size.x > 0 && surf->m_current.size.y > 0) - return true; - } - - return false; -} - -bool CWLSurfaceResource::isTearing() { - if (m_enteredOutputs.empty() && m_hlSurface) { - for (auto& m : g_pCompositor->m_monitors) { - if (!m || !m->m_enabled) - continue; - - auto box = m_hlSurface->getSurfaceBoxGlobal(); - if (box && !box->intersection({m->m_position, m->m_size}).empty()) { - if (m->m_tearingState.activelyTearing) - return true; - } - } - } else { - for (auto& m : m_enteredOutputs) { - if (!m) - continue; - - if (m->m_tearingState.activelyTearing) - return true; - } - } - return false; -} - void CWLSurfaceResource::updateCursorShm(CRegion damage) { if (damage.empty()) return; - auto buf = m_current.buffer ? m_current.buffer : SP{}; + auto buf = current.buffer ? current.buffer->buffer : SP{}; if UNLIKELY (!buf) return; - auto& shmData = CCursorSurfaceRole::cursorPixelData(m_self.lock()); + auto& shmData = CCursorSurfaceRole::cursorPixelData(self.lock()); auto shmAttrs = buf->shm(); if (!shmAttrs.success) { - LOGM(Log::TRACE, "updateCursorShm: ignoring, not a shm buffer"); + LOGM(TRACE, "updateCursorShm: ignoring, not a shm buffer"); return; } @@ -685,74 +478,73 @@ void CWLSurfaceResource::updateCursorShm(CRegion damage) { if (const auto RECTS = damage.getRects(); RECTS.size() == 1 && RECTS.at(0).x2 == buf->size.x && RECTS.at(0).y2 == buf->size.y) memcpy(shmData.data(), pixelData, bufLen); else { - damage.forEachRect([&pixelData, &shmData](const auto& box) { + for (auto& box : damage.getRects()) { for (auto y = box.y1; y < box.y2; ++y) { // bpp is 32 INSALLAH auto begin = 4 * box.y1 * (box.x2 - box.x1) + box.x1; auto len = 4 * (box.x2 - box.x1); - memcpy(shmData.data() + begin, pixelData + begin, len); + memcpy((uint8_t*)shmData.data() + begin, (uint8_t*)pixelData + begin, len); } - }); + } } } -void CWLSurfaceResource::presentFeedback(const Time::steady_tp& when, PHLMONITOR pMonitor, bool discarded) { +void CWLSurfaceResource::presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded) { frame(when); - auto FEEDBACK = makeUnique(m_self.lock()); + auto FEEDBACK = makeShared(self.lock()); FEEDBACK->attachMonitor(pMonitor); if (discarded) FEEDBACK->discarded(); - else { + else FEEDBACK->presented(); - if (!pMonitor->m_lastScanout.expired()) { - const auto WINDOW = m_hlSurface ? Desktop::View::CWindow::fromView(m_hlSurface->view()) : nullptr; - if (WINDOW == pMonitor->m_lastScanout) - FEEDBACK->setPresentationType(true); - } - } - PROTO::presentation->queueData(std::move(FEEDBACK)); + PROTO::presentation->queueData(FEEDBACK); + + if (!pMonitor || !pMonitor->inTimeline || !syncobj) + return; + + // attach explicit sync + g_pHyprRenderer->explicitPresented.emplace_back(self.lock()); } -CWLCompositorResource::CWLCompositorResource(SP resource_) : m_resource(resource_) { +CWLCompositorResource::CWLCompositorResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWlCompositor* r) { PROTO::compositor->destroyResource(this); }); + resource->setOnDestroy([this](CWlCompositor* r) { PROTO::compositor->destroyResource(this); }); - m_resource->setCreateSurface([](CWlCompositor* r, uint32_t id) { - const auto RESOURCE = PROTO::compositor->m_surfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + resource->setCreateSurface([](CWlCompositor* r, uint32_t id) { + const auto RESOURCE = PROTO::compositor->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::compositor->m_surfaces.pop_back(); + PROTO::compositor->m_vSurfaces.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - RESOURCE->m_stateQueue = CSurfaceStateQueue(RESOURCE); + RESOURCE->self = RESOURCE; - LOGM(Log::DEBUG, "New wl_surface with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New wl_surface with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); - PROTO::compositor->m_events.newSurface.emit(RESOURCE); + PROTO::compositor->events.newSurface.emit(RESOURCE); }); - m_resource->setCreateRegion([](CWlCompositor* r, uint32_t id) { - const auto RESOURCE = PROTO::compositor->m_regions.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + resource->setCreateRegion([](CWlCompositor* r, uint32_t id) { + const auto RESOURCE = PROTO::compositor->m_vRegions.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::compositor->m_regions.pop_back(); + PROTO::compositor->m_vRegions.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - LOGM(Log::DEBUG, "New wl_region with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New wl_region with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); }); } bool CWLCompositorResource::good() { - return m_resource->resource(); + return resource->resource(); } CWLCompositorProtocol::CWLCompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -760,29 +552,29 @@ CWLCompositorProtocol::CWLCompositorProtocol(const wl_interface* iface, const in } void CWLCompositorProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CWLCompositorProtocol::destroyResource(CWLCompositorResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CWLCompositorProtocol::destroyResource(CWLSurfaceResource* resource) { - std::erase_if(m_surfaces, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); } void CWLCompositorProtocol::destroyResource(CWLRegionResource* resource) { - std::erase_if(m_regions, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vRegions, [&](const auto& other) { return other.get() == resource; }); } void CWLCompositorProtocol::forEachSurface(std::function)> fn) { - for (auto& surf : m_surfaces) { + for (auto& surf : m_vSurfaces) { fn(surf); } } diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp index 37ca51b7..c732f797 100644 --- a/src/protocols/core/Compositor.hpp +++ b/src/protocols/core/Compositor.hpp @@ -9,49 +9,36 @@ */ #include -#include #include #include "../WaylandProtocol.hpp" #include "../../render/Texture.hpp" -#include "../types/SurfaceStateQueue.hpp" #include "wayland.hpp" -#include "../../desktop/view/WLSurface.hpp" #include "../../helpers/signal/Signal.hpp" #include "../../helpers/math/Math.hpp" -#include "../../helpers/time/Time.hpp" #include "../types/Buffer.hpp" -#include "../../helpers/cm/ColorManagement.hpp" #include "../types/SurfaceRole.hpp" #include "../types/SurfaceState.hpp" class CWLOutputResource; class CMonitor; +class CWLSurface; class CWLSurfaceResource; class CWLSubsurfaceResource; class CViewportResource; class CDRMSyncobjSurfaceResource; -class CFifoResource; -class CCommitTimerResource; class CColorManagementSurface; +class CFrogColorManagementSurface; class CContentType; class CWLCallbackResource { public: - CWLCallbackResource(SP&& resource_); - ~CWLCallbackResource() noexcept = default; - // disable copy - CWLCallbackResource(const CWLCallbackResource&) = delete; - CWLCallbackResource& operator=(const CWLCallbackResource&) = delete; + CWLCallbackResource(SP resource_); - // allow move - CWLCallbackResource(CWLCallbackResource&&) noexcept = default; - CWLCallbackResource& operator=(CWLCallbackResource&&) noexcept = default; - - bool good(); - void send(const Time::steady_tp& now); + bool good(); + void send(timespec* now); private: - SP m_resource; + SP resource; }; class CWLRegionResource { @@ -61,11 +48,11 @@ class CWLRegionResource { bool good(); - CRegion m_region; - WP m_self; + CRegion region; + WP self; private: - SP m_resource; + SP resource; }; class CWLSurfaceResource { @@ -81,7 +68,7 @@ class CWLSurfaceResource { void leave(PHLMONITOR monitor); void sendPreferredTransform(wl_output_transform t); void sendPreferredScale(int32_t scale); - void frame(const Time::steady_tp& now); + void frame(timespec* now); uint32_t id(); void map(); void unmap(); @@ -91,52 +78,40 @@ class CWLSurfaceResource { void resetRole(); struct { - CSignalT<> precommit; // before commit - CSignalT> stateCommit; // when placing state in queue - CSignalT> stateCommit2; // when placing state in queue used for commit timing so we apply fifo/fences first. - CSignalT<> commit; // after commit - CSignalT<> map; - CSignalT<> unmap; - CSignalT> newSubsurface; - CSignalT<> destroy; - CSignalT> enter; - CSignalT> leave; - } m_events; + CSignal precommit; // before commit + CSignal commit; // after commit + CSignal map; + CSignal unmap; + CSignal newSubsurface; + CSignal destroy; + } events; - SSurfaceState m_current; - SSurfaceState m_pending; - CSurfaceStateQueue m_stateQueue; + SSurfaceState current, pending; - WP m_self; - WP m_hlSurface; - std::vector m_enteredOutputs; - bool m_mapped = false; - std::vector> m_subsurfaces; - SP m_role; - WP m_syncobj; // may not be present - WP m_fifo; // may not be present - WP m_commitTimer; // may not be present - WP m_colorManagement; - WP m_contentType; + std::vector> callbacks; + WP self; + WP hlSurface; + std::vector enteredOutputs; + bool mapped = false; + std::vector> subsurfaces; + SP role; + WP viewportResource; + WP syncobj; // may not be present + WP colorManagement; + WP contentType; void breadthfirst(std::function, const Vector2D&, void*)> fn, void* data); SP findFirstPreorder(std::function)> fn); - SP findWithCM(); - void presentFeedback(const Time::steady_tp& when, PHLMONITOR pMonitor, bool discarded = false); - void scheduleState(WP state); - void commitState(SSurfaceState& state); - NColorManagement::PImageDescription getPreferredImageDescription(); - void sortSubsurfaces(); - bool hasVisibleSubsurface(); - bool isTearing(); + void presentFeedback(timespec* when, PHLMONITOR pMonitor, bool discarded = false); + void commitPendingState(SSurfaceState& state); // returns a pair: found surface (null if not found) and surface local coords. // localCoords param is relative to 0,0 of this surface std::pair, Vector2D> at(const Vector2D& localCoords, bool allowsInput = false); private: - SP m_resource; - wl_client* m_client = nullptr; + SP resource; + wl_client* pClient = nullptr; void destroy(); void releaseBuffers(bool onlyCurrent = true); @@ -156,7 +131,7 @@ class CWLCompositorResource { bool good(); private: - SP m_resource; + SP resource; }; class CWLCompositorProtocol : public IWaylandProtocol { @@ -168,8 +143,8 @@ class CWLCompositorProtocol : public IWaylandProtocol { void forEachSurface(std::function)> fn); struct { - CSignalT> newSurface; - } m_events; + CSignal newSurface; // SP + } events; private: void destroyResource(CWLCompositorResource* resource); @@ -177,9 +152,9 @@ class CWLCompositorProtocol : public IWaylandProtocol { void destroyResource(CWLRegionResource* resource); // - std::vector> m_managers; - std::vector> m_surfaces; - std::vector> m_regions; + std::vector> m_vManagers; + std::vector> m_vSurfaces; + std::vector> m_vRegions; friend class CWLSurfaceResource; friend class CWLCompositorResource; diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index 22ccae6c..f4044ec5 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -10,67 +10,66 @@ #include "../../xwayland/XWayland.hpp" #include "../../xwayland/Server.hpp" #include "../../managers/input/InputManager.hpp" -#include "../../managers/cursor/CursorShapeOverrideController.hpp" +#include "../../managers/HookSystemManager.hpp" #include "../../helpers/Monitor.hpp" #include "../../render/Renderer.hpp" #include "../../xwayland/Dnd.hpp" -#include "../../event/EventBus.hpp" using namespace Hyprutils::OS; -CWLDataOfferResource::CWLDataOfferResource(SP resource_, SP source_) : m_source(source_), m_resource(resource_) { +CWLDataOfferResource::CWLDataOfferResource(SP resource_, SP source_) : source(source_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CWlDataOffer* r) { PROTO::data->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlDataOffer* r) { PROTO::data->destroyResource(this); }); + resource->setDestroy([this](CWlDataOffer* r) { PROTO::data->destroyResource(this); }); + resource->setOnDestroy([this](CWlDataOffer* r) { PROTO::data->destroyResource(this); }); - m_resource->setAccept([this](CWlDataOffer* r, uint32_t serial, const char* mime) { - if (!m_source) { - LOGM(Log::WARN, "Possible bug: Accept on an offer w/o a source"); + resource->setAccept([this](CWlDataOffer* r, uint32_t serial, const char* mime) { + if (!source) { + LOGM(WARN, "Possible bug: Accept on an offer w/o a source"); return; } - if (m_dead) { - LOGM(Log::WARN, "Possible bug: Accept on an offer that's dead"); + if (dead) { + LOGM(WARN, "Possible bug: Accept on an offer that's dead"); return; } - LOGM(Log::DEBUG, "Offer {:x} accepts data from source {:x} with mime {}", (uintptr_t)this, (uintptr_t)m_source.get(), mime ? mime : "null"); + LOGM(LOG, "Offer {:x} accepts data from source {:x} with mime {}", (uintptr_t)this, (uintptr_t)source.get(), mime ? mime : "null"); - m_source->accepted(mime ? mime : ""); - m_accepted = mime; + source->accepted(mime ? mime : ""); + accepted = mime; }); - m_resource->setReceive([this](CWlDataOffer* r, const char* mime, int fd) { + resource->setReceive([this](CWlDataOffer* r, const char* mime, int fd) { CFileDescriptor sendFd{fd}; - if (!m_source) { - LOGM(Log::WARN, "Possible bug: Receive on an offer w/o a source"); + if (!source) { + LOGM(WARN, "Possible bug: Receive on an offer w/o a source"); return; } - if (m_dead) { - LOGM(Log::WARN, "Possible bug: Receive on an offer that's dead"); + if (dead) { + LOGM(WARN, "Possible bug: Receive on an offer that's dead"); return; } - LOGM(Log::DEBUG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get()); + LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get()); - if (!m_accepted) { - LOGM(Log::WARN, "Offer was never accepted, sending accept first"); - m_source->accepted(mime ? mime : ""); + if (!accepted) { + LOGM(WARN, "Offer was never accepted, sending accept first"); + source->accepted(mime ? mime : ""); } - m_source->send(mime ? mime : "", std::move(sendFd)); + source->send(mime ? mime : "", std::move(sendFd)); - m_recvd = true; + recvd = true; // if (source->hasDnd()) // PROTO::data->completeDrag(); }); - m_resource->setFinish([this](CWlDataOffer* r) { - m_dead = true; - if (!m_source || !m_recvd || !m_accepted) + resource->setFinish([this](CWlDataOffer* r) { + dead = true; + if (!source || !recvd || !accepted) PROTO::data->abortDrag(); else PROTO::data->completeDrag(); @@ -78,37 +77,37 @@ CWLDataOfferResource::CWLDataOfferResource(SP resource_, SPhasDnd() || m_dead) + if (!source || !source->hasDnd() || dead) return; - m_source->sendDndFinished(); + source->sendDndFinished(); } bool CWLDataOfferResource::good() { - return m_resource->resource(); + return resource->resource(); } void CWLDataOfferResource::sendData() { - if (!m_source) + if (!source) return; - const auto SOURCEACTIONS = m_source->actions(); + const auto SOURCEACTIONS = source->actions(); - if (m_resource->version() >= 3 && SOURCEACTIONS > 0) { - m_resource->sendSourceActions(SOURCEACTIONS); + if (resource->version() >= 3 && SOURCEACTIONS > 0) { + resource->sendSourceActions(SOURCEACTIONS); if (SOURCEACTIONS & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) - m_resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); + resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); else if (SOURCEACTIONS & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) - m_resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); else { - LOGM(Log::ERR, "Client bug? dnd source has no action move or copy. Sending move, f this."); - m_resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); + LOGM(ERR, "Client bug? dnd source has no action move or copy. Sending move, f this."); + resource->sendAction(WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); } } - for (auto const& m : m_source->mimes()) { - LOGM(Log::DEBUG, " | offer {:x} supports mime {}", (uintptr_t)this, m); - m_resource->sendOffer(m.c_str()); + for (auto const& m : source->mimes()) { + LOGM(LOG, " | offer {:x} supports mime {}", (uintptr_t)this, m); + resource->sendOffer(m.c_str()); } } @@ -117,7 +116,7 @@ eDataSourceType CWLDataOfferResource::type() { } SP CWLDataOfferResource::getWayland() { - return m_self.lock(); + return self.lock(); } SP CWLDataOfferResource::getX11() { @@ -125,206 +124,199 @@ SP CWLDataOfferResource::getX11() { } SP CWLDataOfferResource::getSource() { - return m_source.lock(); + return source.lock(); } -CWLDataSourceResource::CWLDataSourceResource(SP resource_, SP device_) : m_device(device_), m_resource(resource_) { +CWLDataSourceResource::CWLDataSourceResource(SP resource_, SP device_) : device(device_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_resource->setDestroy([this](CWlDataSource* r) { - m_events.destroy.emit(); - PROTO::data->onDestroyDataSource(m_self); + resource->setDestroy([this](CWlDataSource* r) { + events.destroy.emit(); + PROTO::data->onDestroyDataSource(self); PROTO::data->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlDataSource* r) { - m_events.destroy.emit(); - PROTO::data->onDestroyDataSource(m_self); + resource->setOnDestroy([this](CWlDataSource* r) { + events.destroy.emit(); + PROTO::data->onDestroyDataSource(self); PROTO::data->destroyResource(this); }); - m_resource->setOffer([this](CWlDataSource* r, const char* mime) { m_mimeTypes.emplace_back(mime); }); - m_resource->setSetActions([this](CWlDataSource* r, uint32_t a) { - LOGM(Log::DEBUG, "DataSource {:x} actions {}", (uintptr_t)this, a); - m_supportedActions = a; + resource->setOffer([this](CWlDataSource* r, const char* mime) { mimeTypes.emplace_back(mime); }); + resource->setSetActions([this](CWlDataSource* r, uint32_t a) { + LOGM(LOG, "DataSource {:x} actions {}", (uintptr_t)this, a); + supportedActions = a; }); } CWLDataSourceResource::~CWLDataSourceResource() { - m_events.destroy.emit(); - PROTO::data->onDestroyDataSource(m_self); + events.destroy.emit(); + PROTO::data->onDestroyDataSource(self); } SP CWLDataSourceResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CWLDataSourceResource*)(((CWlDataSource*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } bool CWLDataSourceResource::good() { - return m_resource->resource(); + return resource->resource(); } void CWLDataSourceResource::accepted(const std::string& mime) { if (mime.empty()) { - m_resource->sendTarget(nullptr); + resource->sendTarget(nullptr); return; } - if (std::ranges::find(m_mimeTypes, mime) == m_mimeTypes.end()) { - LOGM(Log::ERR, "Compositor/App bug: CWLDataSourceResource::sendAccepted with non-existent mime"); + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CWLDataSourceResource::sendAccepted with non-existent mime"); return; } - m_resource->sendTarget(mime.c_str()); + resource->sendTarget(mime.c_str()); } std::vector CWLDataSourceResource::mimes() { - return m_mimeTypes; + return mimeTypes; } void CWLDataSourceResource::send(const std::string& mime, CFileDescriptor fd) { - if (std::ranges::find(m_mimeTypes, mime) == m_mimeTypes.end()) { - LOGM(Log::ERR, "Compositor/App bug: CWLDataSourceResource::sendAskSend with non-existent mime"); + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CWLDataSourceResource::sendAskSend with non-existent mime"); return; } - m_resource->sendSend(mime.c_str(), fd.get()); + resource->sendSend(mime.c_str(), fd.get()); } void CWLDataSourceResource::cancelled() { - m_resource->sendCancelled(); + resource->sendCancelled(); } bool CWLDataSourceResource::hasDnd() { - return m_dnd; + return dnd; } bool CWLDataSourceResource::dndDone() { - return m_dndSuccess; + return dndSuccess; } void CWLDataSourceResource::error(uint32_t code, const std::string& msg) { - m_resource->error(code, msg); + resource->error(code, msg); } void CWLDataSourceResource::sendDndDropPerformed() { - if (m_resource->version() < 3) + if (resource->version() < 3) return; - m_resource->sendDndDropPerformed(); - m_dropped = true; + resource->sendDndDropPerformed(); + dropped = true; } void CWLDataSourceResource::sendDndFinished() { - if (m_resource->version() < 3) + if (resource->version() < 3) return; - m_resource->sendDndFinished(); + resource->sendDndFinished(); } void CWLDataSourceResource::sendDndAction(wl_data_device_manager_dnd_action a) { - if (m_resource->version() < 3) + if (resource->version() < 3) return; - m_resource->sendAction(a); + resource->sendAction(a); } uint32_t CWLDataSourceResource::actions() { - return m_supportedActions; + return supportedActions; } eDataSourceType CWLDataSourceResource::type() { return DATA_SOURCE_TYPE_WAYLAND; } -CWLDataDeviceResource::CWLDataDeviceResource(SP resource_) : m_resource(resource_) { +CWLDataDeviceResource::CWLDataDeviceResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setRelease([this](CWlDataDevice* r) { PROTO::data->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlDataDevice* r) { PROTO::data->destroyResource(this); }); + resource->setRelease([this](CWlDataDevice* r) { PROTO::data->destroyResource(this); }); + resource->setOnDestroy([this](CWlDataDevice* r) { PROTO::data->destroyResource(this); }); - m_client = m_resource->client(); + pClient = resource->client(); - m_resource->setSetSelection([](CWlDataDevice* r, wl_resource* sourceR, uint32_t serial) { + resource->setSetSelection([](CWlDataDevice* r, wl_resource* sourceR, uint32_t serial) { auto source = sourceR ? CWLDataSourceResource::fromResource(sourceR) : CSharedPointer{}; if (!source) { - LOGM(Log::DEBUG, "Reset selection received"); + LOGM(LOG, "Reset selection received"); g_pSeatManager->setCurrentSelection(nullptr); return; } - if (source && source->m_used) - LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + if (source && source->used) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); source->markUsed(); g_pSeatManager->setCurrentSelection(source); }); - m_resource->setStartDrag([](CWlDataDevice* r, wl_resource* sourceR, wl_resource* origin, wl_resource* icon, uint32_t serial) { + resource->setStartDrag([](CWlDataDevice* r, wl_resource* sourceR, wl_resource* origin, wl_resource* icon, uint32_t serial) { auto source = CWLDataSourceResource::fromResource(sourceR); if (!source) { - LOGM(Log::ERR, "No source in drag"); + LOGM(ERR, "No source in drag"); return; } - if (source && source->m_used) - LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + if (source && source->used) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); source->markUsed(); - source->m_dnd = true; + source->dnd = true; PROTO::data->initiateDrag(source, icon ? CWLSurfaceResource::fromResource(icon) : nullptr, CWLSurfaceResource::fromResource(origin)); }); } bool CWLDataDeviceResource::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CWLDataDeviceResource::client() { - return m_client; + return pClient; } void CWLDataDeviceResource::sendDataOffer(SP offer) { if (!offer) - m_resource->sendDataOfferRaw(nullptr); + resource->sendDataOfferRaw(nullptr); else if (const auto WL = offer->getWayland(); WL) - m_resource->sendDataOffer(WL->m_resource.get()); + resource->sendDataOffer(WL->resource.get()); //FIXME: X11 } void CWLDataDeviceResource::sendEnter(uint32_t serial, SP surf, const Vector2D& local, SP offer) { if (const auto WL = offer->getWayland(); WL) - m_resource->sendEnterRaw(serial, surf->getResource()->resource(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), WL->m_resource->resource()); - - m_entered = surf; - + resource->sendEnterRaw(serial, surf->getResource()->resource(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), WL->resource->resource()); // FIXME: X11 } void CWLDataDeviceResource::sendLeave() { - if (!m_entered) - return; - - m_entered.reset(); - m_resource->sendLeave(); + resource->sendLeave(); } void CWLDataDeviceResource::sendMotion(uint32_t timeMs, const Vector2D& local) { - m_resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); + resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); } void CWLDataDeviceResource::sendDrop() { - m_resource->sendDrop(); + resource->sendDrop(); } void CWLDataDeviceResource::sendSelection(SP offer) { if (!offer) - m_resource->sendSelectionRaw(nullptr); + resource->sendSelectionRaw(nullptr); else if (const auto WL = offer->getWayland(); WL) - m_resource->sendSelection(WL->m_resource.get()); + resource->sendSelection(WL->resource.get()); } eDataSourceType CWLDataDeviceResource::type() { @@ -332,108 +324,108 @@ eDataSourceType CWLDataDeviceResource::type() { } SP CWLDataDeviceResource::getWayland() { - return m_self.lock(); + return self.lock(); } SP CWLDataDeviceResource::getX11() { return nullptr; } -CWLDataDeviceManagerResource::CWLDataDeviceManagerResource(SP resource_) : m_resource(resource_) { +CWLDataDeviceManagerResource::CWLDataDeviceManagerResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWlDataDeviceManager* r) { PROTO::data->destroyResource(this); }); + resource->setOnDestroy([this](CWlDataDeviceManager* r) { PROTO::data->destroyResource(this); }); - m_resource->setCreateDataSource([this](CWlDataDeviceManager* r, uint32_t id) { - std::erase_if(m_sources, [](const auto& e) { return e.expired(); }); + resource->setCreateDataSource([this](CWlDataDeviceManager* r, uint32_t id) { + std::erase_if(sources, [](const auto& e) { return e.expired(); }); - const auto RESOURCE = PROTO::data->m_sources.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_device.lock())); + const auto RESOURCE = PROTO::data->m_vSources.emplace_back(makeShared(makeShared(r->client(), r->version(), id), device.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::data->m_sources.pop_back(); + PROTO::data->m_vSources.pop_back(); return; } - if (!m_device) - LOGM(Log::WARN, "New data source before a device was created"); + if (!device) + LOGM(WARN, "New data source before a device was created"); - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - m_sources.emplace_back(RESOURCE); + sources.emplace_back(RESOURCE); - LOGM(Log::DEBUG, "New data source bound at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New data source bound at {:x}", (uintptr_t)RESOURCE.get()); }); - m_resource->setGetDataDevice([this](CWlDataDeviceManager* r, uint32_t id, wl_resource* seat) { - const auto RESOURCE = PROTO::data->m_devices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + resource->setGetDataDevice([this](CWlDataDeviceManager* r, uint32_t id, wl_resource* seat) { + const auto RESOURCE = PROTO::data->m_vDevices.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::data->m_devices.pop_back(); + PROTO::data->m_vDevices.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - for (auto const& s : m_sources) { + for (auto const& s : sources) { if (!s) continue; - s->m_device = RESOURCE; + s->device = RESOURCE; } - LOGM(Log::DEBUG, "New data device bound at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New data device bound at {:x}", (uintptr_t)RESOURCE.get()); }); } bool CWLDataDeviceManagerResource::good() { - return m_resource->resource(); + return resource->resource(); } CWLDataDeviceProtocol::CWLDataDeviceProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { g_pEventLoopManager->doLater([this]() { - m_listeners.onKeyboardFocusChange = g_pSeatManager->m_events.keyboardFocusChange.listen([this] { onKeyboardFocus(); }); - m_listeners.onDndPointerFocusChange = g_pSeatManager->m_events.dndPointerFocusChange.listen([this] { onDndPointerFocus(); }); + listeners.onKeyboardFocusChange = g_pSeatManager->events.keyboardFocusChange.registerListener([this](std::any d) { onKeyboardFocus(); }); + listeners.onDndPointerFocusChange = g_pSeatManager->events.dndPointerFocusChange.registerListener([this](std::any d) { onDndPointerFocus(); }); }); } void CWLDataDeviceProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } - LOGM(Log::DEBUG, "New datamgr resource bound at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New datamgr resource bound at {:x}", (uintptr_t)RESOURCE.get()); } void CWLDataDeviceProtocol::destroyResource(CWLDataDeviceManagerResource* seat) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == seat; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == seat; }); } void CWLDataDeviceProtocol::destroyResource(CWLDataDeviceResource* resource) { - std::erase_if(m_devices, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; }); } void CWLDataDeviceProtocol::destroyResource(CWLDataSourceResource* resource) { - std::erase_if(m_sources, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; }); } void CWLDataDeviceProtocol::destroyResource(CWLDataOfferResource* resource) { - std::erase_if(m_offers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; }); } SP CWLDataDeviceProtocol::dataDeviceForClient(wl_client* c) { #ifndef NO_XWAYLAND - if (g_pXWayland && g_pXWayland->m_server && c == g_pXWayland->m_server->m_xwaylandClient) - return g_pXWayland->m_wm->getDataDevice(); + if (g_pXWayland->pServer && c == g_pXWayland->pServer->xwaylandClient) + return g_pXWayland->pWM->getDataDevice(); #endif - auto it = std::ranges::find_if(m_devices, [c](const auto& e) { return e->client() == c; }); - if (it == m_devices.end()) + auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; }); + if (it == m_vDevices.end()) return nullptr; return *it; } @@ -447,27 +439,27 @@ void CWLDataDeviceProtocol::sendSelectionToDevice(SP dev, SP offer; if (const auto WL = dev->getWayland(); WL) { - const auto OFFER = m_offers.emplace_back(makeShared(makeShared(WL->m_resource->client(), WL->m_resource->version(), 0), sel)); + const auto OFFER = m_vOffers.emplace_back(makeShared(makeShared(WL->resource->client(), WL->resource->version(), 0), sel)); if UNLIKELY (!OFFER->good()) { - WL->m_resource->noMemory(); - m_offers.pop_back(); + WL->resource->noMemory(); + m_vOffers.pop_back(); return; } - OFFER->m_source = sel; - OFFER->m_self = OFFER; - offer = OFFER; + OFFER->source = sel; + OFFER->self = OFFER; + offer = OFFER; } #ifndef NO_XWAYLAND else if (const auto X11 = dev->getX11(); X11) - offer = g_pXWayland->m_wm->createX11DataOffer(g_pSeatManager->m_state.keyboardFocus.lock(), sel); + offer = g_pXWayland->pWM->createX11DataOffer(g_pSeatManager->state.keyboardFocus.lock(), sel); #endif if UNLIKELY (!offer) { - LOGM(Log::ERR, "No offer could be created in sendSelectionToDevice"); + LOGM(ERR, "No offer could be created in sendSelectionToDevice"); return; } - LOGM(Log::DEBUG, "New {} offer {:x} for data source {:x}", offer->type() == DATA_SOURCE_TYPE_WAYLAND ? "wayland" : "X11", (uintptr_t)offer.get(), (uintptr_t)sel.get()); + LOGM(LOG, "New {} offer {:x} for data source {:x}", offer->type() == DATA_SOURCE_TYPE_WAYLAND ? "wayland" : "X11", (uintptr_t)offer.get(), (uintptr_t)sel.get()); dev->sendDataOffer(offer); if (const auto WL = offer->getWayland(); WL) @@ -476,44 +468,44 @@ void CWLDataDeviceProtocol::sendSelectionToDevice(SP dev, SP source) { - if (m_dnd.currentSource == source) + if (dnd.currentSource == source) abortDrag(); } void CWLDataDeviceProtocol::setSelection(SP source) { - for (auto const& o : m_offers) { - if (o->m_source && o->m_source->hasDnd()) + for (auto const& o : m_vOffers) { + if (o->source && o->source->hasDnd()) continue; - o->m_dead = true; + o->dead = true; } if (!source) { - LOGM(Log::DEBUG, "resetting selection"); + LOGM(LOG, "resetting selection"); - if (!g_pSeatManager->m_state.keyboardFocusResource) + if (!g_pSeatManager->state.keyboardFocusResource) return; - auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->m_state.keyboardFocusResource->client()); + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client()); if (DESTDEVICE && DESTDEVICE->type() == DATA_SOURCE_TYPE_WAYLAND) sendSelectionToDevice(DESTDEVICE, nullptr); return; } - LOGM(Log::DEBUG, "New selection for data source {:x}", (uintptr_t)source.get()); + LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get()); - if (!g_pSeatManager->m_state.keyboardFocusResource) + if (!g_pSeatManager->state.keyboardFocusResource) return; - auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->m_state.keyboardFocusResource->client()); + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client()); if (!DESTDEVICE) { - LOGM(Log::DEBUG, "CWLDataDeviceProtocol::setSelection: cannot send selection to a client without a data_device"); + LOGM(LOG, "CWLDataDeviceProtocol::setSelection: cannot send selection to a client without a data_device"); return; } if (DESTDEVICE->type() != DATA_SOURCE_TYPE_WAYLAND) { - LOGM(Log::DEBUG, "CWLDataDeviceProtocol::setSelection: ignoring X11 data device"); + LOGM(LOG, "CWLDataDeviceProtocol::setSelection: ignoring X11 data device"); return; } @@ -521,34 +513,34 @@ void CWLDataDeviceProtocol::setSelection(SP source) { } void CWLDataDeviceProtocol::updateSelection() { - if (!g_pSeatManager->m_state.keyboardFocusResource) + if (!g_pSeatManager->state.keyboardFocusResource) return; - auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->m_state.keyboardFocusResource->client()); + auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client()); if (!DESTDEVICE) { - LOGM(Log::DEBUG, "CWLDataDeviceProtocol::onKeyboardFocus: cannot send selection to a client without a data_device"); + LOGM(LOG, "CWLDataDeviceProtocol::onKeyboardFocus: cannot send selection to a client without a data_device"); return; } - sendSelectionToDevice(DESTDEVICE, g_pSeatManager->m_selection.currentSelection.lock()); + sendSelectionToDevice(DESTDEVICE, g_pSeatManager->selection.currentSelection.lock()); } void CWLDataDeviceProtocol::onKeyboardFocus() { - for (auto const& o : m_offers) { - if (o->m_source && o->m_source->hasDnd()) + for (auto const& o : m_vOffers) { + if (o->source && o->source->hasDnd()) continue; - o->m_dead = true; + o->dead = true; } updateSelection(); } void CWLDataDeviceProtocol::onDndPointerFocus() { - for (auto const& o : m_offers) { - if (o->m_source && !o->m_source->hasDnd()) + for (auto const& o : m_vOffers) { + if (o->source && !o->source->hasDnd()) continue; - o->m_dead = true; + o->dead = true; } updateDrag(); @@ -556,58 +548,53 @@ void CWLDataDeviceProtocol::onDndPointerFocus() { void CWLDataDeviceProtocol::initiateDrag(WP currentSource, SP dragSurface, SP origin) { - if (m_dnd.currentSource) { - LOGM(Log::WARN, "New drag started while old drag still active??"); + if (dnd.currentSource) { + LOGM(WARN, "New drag started while old drag still active??"); abortDrag(); } - Cursor::overrideController->setOverride("grabbing", Cursor::CURSOR_OVERRIDE_DND); - m_dnd.overriddenCursor = true; + g_pInputManager->setCursorImageUntilUnset("grabbing"); + dnd.overriddenCursor = true; - LOGM(Log::DEBUG, "initiateDrag: source {:x}, surface: {:x}, origin: {:x}", (uintptr_t)currentSource.get(), (uintptr_t)dragSurface, (uintptr_t)origin); + LOGM(LOG, "initiateDrag: source {:x}, surface: {:x}, origin: {:x}", (uintptr_t)currentSource.get(), (uintptr_t)dragSurface, (uintptr_t)origin); - currentSource->m_used = true; + currentSource->used = true; - m_dnd.currentSource = currentSource; - m_dnd.originSurface = origin; - m_dnd.dndSurface = dragSurface; + dnd.currentSource = currentSource; + dnd.originSurface = origin; + dnd.dndSurface = dragSurface; if (dragSurface) { - m_dnd.dndSurfaceDestroy = dragSurface->m_events.destroy.listen([this] { abortDrag(); }); - m_dnd.dndSurfaceCommit = dragSurface->m_events.commit.listen([this] { - if (m_dnd.dndSurface->m_current.texture && !m_dnd.dndSurface->m_mapped) { - m_dnd.dndSurface->map(); + dnd.dndSurfaceDestroy = dragSurface->events.destroy.registerListener([this](std::any d) { abortDrag(); }); + dnd.dndSurfaceCommit = dragSurface->events.commit.registerListener([this](std::any d) { + if (dnd.dndSurface->current.texture && !dnd.dndSurface->mapped) { + dnd.dndSurface->map(); return; } - if (m_dnd.dndSurface->m_current.texture <= 0 && m_dnd.dndSurface->m_mapped) { - m_dnd.dndSurface->unmap(); + if (dnd.dndSurface->current.texture <= 0 && dnd.dndSurface->mapped) { + dnd.dndSurface->unmap(); return; } }); } - m_dnd.mouseButton = Event::bus()->m_events.input.mouse.button.listen([this](IPointer::SButtonEvent e, Event::SCallbackInfo&) { - if (e.state == WL_POINTER_BUTTON_STATE_RELEASED) { - LOGM(Log::DEBUG, "Dropping drag on mouseUp"); + dnd.mouseButton = g_pHookSystem->hookDynamic("mouseButton", [this](void* self, SCallbackInfo& info, std::any e) { + auto E = std::any_cast(e); + if (E.state == WL_POINTER_BUTTON_STATE_RELEASED) { + LOGM(LOG, "Dropping drag on mouseUp"); dropDrag(); } }); - m_dnd.touchUp = Event::bus()->m_events.input.touch.up.listen([this](ITouch::SUpEvent e, Event::SCallbackInfo&) { - LOGM(Log::DEBUG, "Dropping drag on touchUp"); + dnd.touchUp = g_pHookSystem->hookDynamic("touchUp", [this](void* self, SCallbackInfo& info, std::any e) { + LOGM(LOG, "Dropping drag on touchUp"); dropDrag(); }); - m_dnd.tabletTip = Event::bus()->m_events.input.tablet.tip.listen([this](CTablet::STipEvent e, Event::SCallbackInfo&) { - if (!e.in) { - LOGM(Log::DEBUG, "Dropping drag on tablet tipUp"); - dropDrag(); - } - }); - - m_dnd.mouseMove = Event::bus()->m_events.input.mouse.move.listen([this](Vector2D pos, Event::SCallbackInfo&) { - if (m_dnd.focusedDevice && g_pSeatManager->m_state.dndPointerFocus) { - auto surf = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.dndPointerFocus.lock()); + dnd.mouseMove = g_pHookSystem->hookDynamic("mouseMove", [this](void* self, SCallbackInfo& info, std::any e) { + auto V = std::any_cast(e); + if (dnd.focusedDevice && g_pSeatManager->state.dndPointerFocus) { + auto surf = CWLSurface::fromResource(g_pSeatManager->state.dndPointerFocus.lock()); if (!surf) return; @@ -617,14 +604,18 @@ void CWLDataDeviceProtocol::initiateDrag(WP currentSource if (!box.has_value()) return; - m_dnd.focusedDevice->sendMotion(Time::millis(Time::steadyNow()), pos - box->pos()); - LOGM(Log::DEBUG, "Drag motion {}", pos - box->pos()); + timespec timeNow; + clock_gettime(CLOCK_MONOTONIC, &timeNow); + + dnd.focusedDevice->sendMotion(timeNow.tv_sec * 1000 + timeNow.tv_nsec / 1000000, V - box->pos()); + LOGM(LOG, "Drag motion {}", V - box->pos()); } }); - m_dnd.touchMove = Event::bus()->m_events.input.touch.motion.listen([this](ITouch::SMotionEvent e, Event::SCallbackInfo&) { - if (m_dnd.focusedDevice && g_pSeatManager->m_state.dndPointerFocus) { - auto surf = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.dndPointerFocus.lock()); + dnd.touchMove = g_pHookSystem->hookDynamic("touchMove", [this](void* self, SCallbackInfo& info, std::any e) { + auto E = std::any_cast(e); + if (dnd.focusedDevice && g_pSeatManager->state.dndPointerFocus) { + auto surf = CWLSurface::fromResource(g_pSeatManager->state.dndPointerFocus.lock()); if (!surf) return; @@ -634,17 +625,17 @@ void CWLDataDeviceProtocol::initiateDrag(WP currentSource if (!box.has_value()) return; - m_dnd.focusedDevice->sendMotion(e.timeMs, e.pos); - LOGM(Log::DEBUG, "Drag motion {}", e.pos); + dnd.focusedDevice->sendMotion(E.timeMs, E.pos); + LOGM(LOG, "Drag motion {}", E.pos); } }); // unfocus the pointer from the surface, this is part of """standard""" wayland procedure and gtk will freak out if this isn't happening. // BTW, the spec does NOT require this explicitly... // Fuck you gtk. - const auto LASTDNDFOCUS = g_pSeatManager->m_state.dndPointerFocus; + const auto LASTDNDFOCUS = g_pSeatManager->state.dndPointerFocus; g_pSeatManager->setPointerFocus(nullptr, {}); - g_pSeatManager->m_state.dndPointerFocus = LASTDNDFOCUS; + g_pSeatManager->state.dndPointerFocus = LASTDNDFOCUS; // make a new offer, etc updateDrag(); @@ -654,65 +645,64 @@ void CWLDataDeviceProtocol::updateDrag() { if (!dndActive()) return; - if (m_dnd.focusedDevice) - m_dnd.focusedDevice->sendLeave(); + if (dnd.focusedDevice) + dnd.focusedDevice->sendLeave(); - if (!g_pSeatManager->m_state.dndPointerFocus) + if (!g_pSeatManager->state.dndPointerFocus) return; - m_dnd.focusedDevice = dataDeviceForClient(g_pSeatManager->m_state.dndPointerFocus->client()); + dnd.focusedDevice = dataDeviceForClient(g_pSeatManager->state.dndPointerFocus->client()); - if (!m_dnd.focusedDevice) + if (!dnd.focusedDevice) return; SP offer; - if (const auto WL = m_dnd.focusedDevice->getWayland(); WL) { + if (const auto WL = dnd.focusedDevice->getWayland(); WL) { const auto OFFER = - m_offers.emplace_back(makeShared(makeShared(WL->m_resource->client(), WL->m_resource->version(), 0), m_dnd.currentSource.lock())); + m_vOffers.emplace_back(makeShared(makeShared(WL->resource->client(), WL->resource->version(), 0), dnd.currentSource.lock())); if (!OFFER->good()) { - WL->m_resource->noMemory(); - m_offers.pop_back(); + WL->resource->noMemory(); + m_vOffers.pop_back(); return; } - OFFER->m_source = m_dnd.currentSource; - OFFER->m_self = OFFER; - offer = OFFER; + OFFER->source = dnd.currentSource; + OFFER->self = OFFER; + offer = OFFER; } #ifndef NO_XWAYLAND - else if (const auto X11 = m_dnd.focusedDevice->getX11(); X11) - offer = g_pXWayland->m_wm->createX11DataOffer(g_pSeatManager->m_state.keyboardFocus.lock(), m_dnd.currentSource.lock()); + else if (const auto X11 = dnd.focusedDevice->getX11(); X11) + offer = g_pXWayland->pWM->createX11DataOffer(g_pSeatManager->state.keyboardFocus.lock(), dnd.currentSource.lock()); #endif if (!offer) { - LOGM(Log::ERR, "No offer could be created in updateDrag"); + LOGM(ERR, "No offer could be created in updateDrag"); return; } - LOGM(Log::DEBUG, "New {} dnd offer {:x} for data source {:x}", offer->type() == DATA_SOURCE_TYPE_WAYLAND ? "wayland" : "X11", (uintptr_t)offer.get(), - (uintptr_t)m_dnd.currentSource.get()); + LOGM(LOG, "New {} dnd offer {:x} for data source {:x}", offer->type() == DATA_SOURCE_TYPE_WAYLAND ? "wayland" : "X11", (uintptr_t)offer.get(), + (uintptr_t)dnd.currentSource.get()); - m_dnd.focusedDevice->sendDataOffer(offer); + dnd.focusedDevice->sendDataOffer(offer); if (const auto WL = offer->getWayland(); WL) WL->sendData(); - m_dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_wlDisplay), g_pSeatManager->m_state.dndPointerFocus.lock(), - g_pSeatManager->m_state.dndPointerFocus->m_current.size / 2.F, offer); + dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_sWLDisplay), g_pSeatManager->state.dndPointerFocus.lock(), + g_pSeatManager->state.dndPointerFocus->current.size / 2.F, offer); } void CWLDataDeviceProtocol::cleanupDndState(bool resetDevice, bool resetSource, bool simulateInput) { - m_dnd.dndSurface.reset(); - m_dnd.dndSurfaceCommit.reset(); - m_dnd.dndSurfaceDestroy.reset(); - m_dnd.mouseButton.reset(); - m_dnd.mouseMove.reset(); - m_dnd.touchUp.reset(); - m_dnd.touchMove.reset(); - m_dnd.tabletTip.reset(); + dnd.dndSurface.reset(); + dnd.dndSurfaceCommit.reset(); + dnd.dndSurfaceDestroy.reset(); + dnd.mouseButton.reset(); + dnd.mouseMove.reset(); + dnd.touchUp.reset(); + dnd.touchMove.reset(); if (resetDevice) - m_dnd.focusedDevice.reset(); + dnd.focusedDevice.reset(); if (resetSource) - m_dnd.currentSource.reset(); + dnd.currentSource.reset(); if (simulateInput) { g_pInputManager->simulateMouseMovement(); @@ -721,8 +711,8 @@ void CWLDataDeviceProtocol::cleanupDndState(bool resetDevice, bool resetSource, } void CWLDataDeviceProtocol::dropDrag() { - if (!m_dnd.focusedDevice || !m_dnd.currentSource) { - if (m_dnd.currentSource) + if (!dnd.focusedDevice || !dnd.currentSource) { + if (dnd.currentSource) abortDrag(); return; } @@ -732,40 +722,40 @@ void CWLDataDeviceProtocol::dropDrag() { return; } - m_dnd.focusedDevice->sendDrop(); + dnd.focusedDevice->sendDrop(); #ifndef NO_XWAYLAND - if (m_dnd.focusedDevice->getX11()) { - m_dnd.focusedDevice->sendLeave(); - if (m_dnd.overriddenCursor) - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_DND); - m_dnd.overriddenCursor = false; + if (dnd.focusedDevice->getX11()) { + dnd.focusedDevice->sendLeave(); + if (dnd.overriddenCursor) + g_pInputManager->unsetCursorImage(); + dnd.overriddenCursor = false; cleanupDndState(true, true, true); return; } #endif - m_dnd.focusedDevice->sendLeave(); - if (m_dnd.overriddenCursor) - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_DND); - m_dnd.overriddenCursor = false; + dnd.focusedDevice->sendLeave(); + if (dnd.overriddenCursor) + g_pInputManager->unsetCursorImage(); + dnd.overriddenCursor = false; cleanupDndState(false, false, false); } bool CWLDataDeviceProtocol::wasDragSuccessful() { - if (!m_dnd.currentSource) + if (!dnd.currentSource) return false; - for (auto const& o : m_offers) { - if (o->m_dead || o->m_source != m_dnd.currentSource) + for (auto const& o : m_vOffers) { + if (o->dead || o->source != dnd.currentSource) continue; - if (o->m_recvd || o->m_accepted) + if (o->recvd || o->accepted) return true; } #ifndef NO_XWAYLAND - if (m_dnd.focusedDevice->getX11()) + if (dnd.focusedDevice->getX11()) return true; #endif @@ -773,12 +763,12 @@ bool CWLDataDeviceProtocol::wasDragSuccessful() { } void CWLDataDeviceProtocol::completeDrag() { - if (!m_dnd.focusedDevice && !m_dnd.currentSource) + if (!dnd.focusedDevice && !dnd.currentSource) return; - if (m_dnd.currentSource) { - m_dnd.currentSource->sendDndDropPerformed(); - m_dnd.currentSource->sendDndFinished(); + if (dnd.currentSource) { + dnd.currentSource->sendDndDropPerformed(); + dnd.currentSource->sendDndFinished(); } cleanupDndState(true, true, true); @@ -787,56 +777,52 @@ void CWLDataDeviceProtocol::completeDrag() { void CWLDataDeviceProtocol::abortDrag() { cleanupDndState(false, false, false); - if (m_dnd.overriddenCursor) - Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_DND); - m_dnd.overriddenCursor = false; + if (dnd.overriddenCursor) + g_pInputManager->unsetCursorImage(); + dnd.overriddenCursor = false; - if (!m_dnd.focusedDevice && !m_dnd.currentSource) + if (!dnd.focusedDevice && !dnd.currentSource) return; - if (m_dnd.focusedDevice) { + if (dnd.focusedDevice) { #ifndef NO_XWAYLAND - if (auto x11Device = m_dnd.focusedDevice->getX11(); x11Device) + if (auto x11Device = dnd.focusedDevice->getX11(); x11Device) x11Device->forceCleanupDnd(); #endif - m_dnd.focusedDevice->sendLeave(); + dnd.focusedDevice->sendLeave(); } - if (m_dnd.currentSource) - m_dnd.currentSource->cancelled(); + if (dnd.currentSource) + dnd.currentSource->cancelled(); - m_dnd.focusedDevice.reset(); - m_dnd.currentSource.reset(); + dnd.focusedDevice.reset(); + dnd.currentSource.reset(); g_pInputManager->simulateMouseMovement(); g_pSeatManager->resendEnterEvents(); } -void CWLDataDeviceProtocol::renderDND(PHLMONITOR pMonitor, const Time::steady_tp& when) { - if (!m_dnd.dndSurface || !m_dnd.dndSurface->m_current.texture) +void CWLDataDeviceProtocol::renderDND(PHLMONITOR pMonitor, timespec* when) { + if (!dnd.dndSurface || !dnd.dndSurface->current.texture) return; const auto POS = g_pInputManager->getMouseCoordsInternal(); - Vector2D surfacePos = POS; - - surfacePos += m_dnd.dndSurface->m_current.offset; - - CBox box = CBox{surfacePos, m_dnd.dndSurface->m_current.size}.translate(-pMonitor->m_position).scale(pMonitor->m_scale).round(); + CBox box = CBox{POS, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F).scale(pMonitor->scale); CTexPassElement::SRenderData data; - data.tex = m_dnd.dndSurface->m_current.texture; + data.tex = dnd.dndSurface->current.texture; data.box = box; - g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); - CBox damageBox = CBox{surfacePos, m_dnd.dndSurface->m_current.size}.expand(5); - g_pHyprRenderer->damageBox(damageBox); + box = CBox{POS, dnd.dndSurface->current.size}.translate(g_pPointerManager->cursorSizeLogical() / 2.F).expand(5); + g_pHyprRenderer->damageBox(box); - m_dnd.dndSurface->frame(when); + dnd.dndSurface->frame(when); } bool CWLDataDeviceProtocol::dndActive() { - return m_dnd.currentSource; + return dnd.currentSource; } void CWLDataDeviceProtocol::abortDndIfPresent() { diff --git a/src/protocols/core/DataDevice.hpp b/src/protocols/core/DataDevice.hpp index f3717f78..fa5de091 100644 --- a/src/protocols/core/DataDevice.hpp +++ b/src/protocols/core/DataDevice.hpp @@ -15,7 +15,6 @@ #include "wayland.hpp" #include "../../helpers/signal/Signal.hpp" #include "../../helpers/math/Math.hpp" -#include "../../helpers/time/Time.hpp" #include "../types/DataDevice.hpp" #include @@ -40,15 +39,17 @@ class CWLDataOfferResource : public IDataOffer { virtual SP getX11(); virtual SP getSource(); - WP m_source; - WP m_self; + WP source; + WP self; - bool m_dead = false; - bool m_accepted = false; - bool m_recvd = false; + bool dead = false; + bool accepted = false; + bool recvd = false; + + uint32_t actions = 0; private: - SP m_resource; + SP resource; friend class CWLDataDeviceResource; }; @@ -74,19 +75,19 @@ class CWLDataSourceResource : public IDataSource { virtual void sendDndDropPerformed(); virtual void sendDndAction(wl_data_device_manager_dnd_action a); - bool m_used = false; - bool m_dnd = false; - bool m_dndSuccess = false; - bool m_dropped = false; + bool used = false; + bool dnd = false; + bool dndSuccess = false; + bool dropped = false; - WP m_device; - WP m_self; + WP device; + WP self; - std::vector m_mimeTypes; - uint32_t m_supportedActions = 0; + std::vector mimeTypes; + uint32_t supportedActions = 0; private: - SP m_resource; + SP resource; friend class CWLDataDeviceProtocol; }; @@ -108,13 +109,11 @@ class CWLDataDeviceResource : public IDataDevice { virtual void sendSelection(SP offer); virtual eDataSourceType type(); - WP m_self; + WP self; private: - SP m_resource; - wl_client* m_client = nullptr; - - WP m_entered; + SP resource; + wl_client* pClient = nullptr; friend class CWLDataDeviceProtocol; }; @@ -125,11 +124,11 @@ class CWLDataDeviceManagerResource { bool good(); - WP m_device; - std::vector> m_sources; + WP device; + std::vector> sources; private: - SP m_resource; + SP resource; }; class CWLDataDeviceProtocol : public IWaylandProtocol { @@ -139,7 +138,7 @@ class CWLDataDeviceProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); // renders and damages the dnd icon, if present - void renderDND(PHLMONITOR pMonitor, const Time::steady_tp& when); + void renderDND(PHLMONITOR pMonitor, timespec* when); // for inputmgr to force refocus // TODO: move handling to seatmgr bool dndActive(); @@ -154,10 +153,10 @@ class CWLDataDeviceProtocol : public IWaylandProtocol { void destroyResource(CWLDataOfferResource* resource); // - std::vector> m_managers; - std::vector> m_devices; - std::vector> m_sources; - std::vector> m_offers; + std::vector> m_vManagers; + std::vector> m_vDevices; + std::vector> m_vSources; + std::vector> m_vOffers; // @@ -178,12 +177,11 @@ class CWLDataDeviceProtocol : public IWaylandProtocol { CHyprSignalListener dndSurfaceCommit; // for ending a dnd - CHyprSignalListener mouseMove; - CHyprSignalListener mouseButton; - CHyprSignalListener touchUp; - CHyprSignalListener touchMove; - CHyprSignalListener tabletTip; - } m_dnd; + SP mouseMove; + SP mouseButton; + SP touchUp; + SP touchMove; + } dnd; void abortDrag(); void initiateDrag(WP currentSource, SP dragSurface, SP origin); @@ -205,7 +203,7 @@ class CWLDataDeviceProtocol : public IWaylandProtocol { struct { CHyprSignalListener onKeyboardFocusChange; CHyprSignalListener onDndPointerFocusChange; - } m_listeners; + } listeners; }; namespace PROTO { diff --git a/src/protocols/core/Output.cpp b/src/protocols/core/Output.cpp index dd9c3166..77f0661e 100644 --- a/src/protocols/core/Output.cpp +++ b/src/protocols/core/Output.cpp @@ -3,35 +3,35 @@ #include "../../Compositor.hpp" #include "../../helpers/Monitor.hpp" -CWLOutputResource::CWLOutputResource(SP resource_, PHLMONITOR pMonitor) : m_monitor(pMonitor), m_resource(resource_) { +CWLOutputResource::CWLOutputResource(SP resource_, PHLMONITOR pMonitor) : monitor(pMonitor), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setData(this); - m_client = m_resource->client(); + pClient = resource->client(); - if (!m_monitor) + if (!monitor) return; - m_resource->setOnDestroy([this](CWlOutput* r) { - if (m_monitor && PROTO::outputs.contains(m_monitor->m_name)) - PROTO::outputs.at(m_monitor->m_name)->destroyResource(this); + resource->setOnDestroy([this](CWlOutput* r) { + if (monitor && PROTO::outputs.contains(monitor->szName)) + PROTO::outputs.at(monitor->szName)->destroyResource(this); }); - m_resource->setRelease([this](CWlOutput* r) { - if (m_monitor && PROTO::outputs.contains(m_monitor->m_name)) - PROTO::outputs.at(m_monitor->m_name)->destroyResource(this); + resource->setRelease([this](CWlOutput* r) { + if (monitor && PROTO::outputs.contains(monitor->szName)) + PROTO::outputs.at(monitor->szName)->destroyResource(this); }); - if (m_resource->version() >= 4) { - m_resource->sendName(m_monitor->m_name.c_str()); - m_resource->sendDescription(m_monitor->m_description.c_str()); + if (resource->version() >= 4) { + resource->sendName(monitor->szName.c_str()); + resource->sendDescription(monitor->szDescription.c_str()); } updateState(); PROTO::compositor->forEachSurface([](SP surf) { - auto HLSurf = Desktop::View::CWLSurface::fromResource(surf); + auto HLSurf = CWLSurface::fromResource(surf); if (!HLSurf) return; @@ -41,7 +41,7 @@ CWLOutputResource::CWLOutputResource(SP resource_, PHLMONITOR pMonito if (!GEOMETRY.has_value()) return; - for (auto& m : g_pCompositor->m_monitors) { + for (auto& m : g_pCompositor->m_vMonitors) { if (!m->logicalBox().expand(-4).overlaps(*GEOMETRY)) continue; @@ -51,102 +51,99 @@ CWLOutputResource::CWLOutputResource(SP resource_, PHLMONITOR pMonito } SP CWLOutputResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CWLOutputResource*)(((CWlOutput*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } bool CWLOutputResource::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CWLOutputResource::client() { - return m_client; + return pClient; } SP CWLOutputResource::getResource() { - return m_resource; + return resource; } void CWLOutputResource::updateState() { - if (!m_monitor || (m_owner && m_owner->m_defunct)) + if (!monitor || (owner && owner->defunct)) return; - if (m_resource->version() >= 2) - m_resource->sendScale(std::ceil(m_monitor->m_scale)); + if (resource->version() >= 2) + resource->sendScale(std::ceil(monitor->scale)); - m_resource->sendMode(WL_OUTPUT_MODE_CURRENT, m_monitor->m_pixelSize.x, m_monitor->m_pixelSize.y, m_monitor->m_refreshRate * 1000.0); + resource->sendMode((wl_output_mode)(WL_OUTPUT_MODE_CURRENT), monitor->vecPixelSize.x, monitor->vecPixelSize.y, monitor->refreshRate * 1000.0); - m_resource->sendGeometry(0, 0, m_monitor->m_output->physicalSize.x, m_monitor->m_output->physicalSize.y, m_monitor->m_output->subpixel, m_monitor->m_output->make.c_str(), - m_monitor->m_output->model.c_str(), m_monitor->m_transform); + resource->sendGeometry(0, 0, monitor->output->physicalSize.x, monitor->output->physicalSize.y, (wl_output_subpixel)monitor->output->subpixel, monitor->output->make.c_str(), + monitor->output->model.c_str(), monitor->transform); - if (m_resource->version() >= 2) - m_resource->sendDone(); + if (resource->version() >= 2) + resource->sendDone(); } CWLOutputProtocol::CWLOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name, PHLMONITOR pMonitor) : - IWaylandProtocol(iface, ver, name), m_monitor(pMonitor), m_name(pMonitor->m_name) { + IWaylandProtocol(iface, ver, name), monitor(pMonitor), szName(pMonitor->szName) { - m_listeners.modeChanged = m_monitor->m_events.modeChanged.listen([this] { - for (auto const& o : m_outputs) { + listeners.modeChanged = monitor->events.modeChanged.registerListener([this](std::any d) { + for (auto const& o : m_vOutputs) { o->updateState(); } }); } void CWLOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - if UNLIKELY (m_defunct) - Log::logger->log(Log::WARN, "[wl_output] Binding a wl_output that's inert?? Possible client bug."); + if UNLIKELY (defunct) + Debug::log(WARN, "[wl_output] Binding a wl_output that's inert?? Possible client bug."); - const auto RESOURCE = m_outputs.emplace_back(makeShared(makeShared(client, ver, id), m_monitor.lock())); + const auto RESOURCE = m_vOutputs.emplace_back(makeShared(makeShared(client, ver, id), monitor.lock())); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_outputs.pop_back(); + m_vOutputs.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - RESOURCE->m_owner = m_self; - m_events.outputBound.emit(RESOURCE); + RESOURCE->self = RESOURCE; + RESOURCE->owner = self; } void CWLOutputProtocol::destroyResource(CWLOutputResource* resource) { - std::erase_if(m_outputs, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vOutputs, [&](const auto& other) { return other.get() == resource; }); - if (m_outputs.empty() && m_defunct) - PROTO::outputs.erase(m_name); + if (m_vOutputs.empty() && defunct) + PROTO::outputs.erase(szName); } -std::vector> CWLOutputProtocol::outputResourcesFrom(wl_client* client) { - std::vector> ret; - - for (auto const& r : m_outputs) { +SP CWLOutputProtocol::outputResourceFrom(wl_client* client) { + for (auto const& r : m_vOutputs) { if (r->client() != client) continue; - ret.emplace_back(r); + return r; } - return ret; + return nullptr; } void CWLOutputProtocol::remove() { - if UNLIKELY (m_defunct) + if UNLIKELY (defunct) return; - m_defunct = true; + defunct = true; removeGlobal(); } bool CWLOutputProtocol::isDefunct() { - return m_defunct; + return defunct; } void CWLOutputProtocol::sendDone() { - if UNLIKELY (m_defunct) + if UNLIKELY (defunct) return; - for (auto const& r : m_outputs) { - r->m_resource->sendDone(); + for (auto const& r : m_vOutputs) { + r->resource->sendDone(); } } diff --git a/src/protocols/core/Output.hpp b/src/protocols/core/Output.hpp index cf266685..8ade5178 100644 --- a/src/protocols/core/Output.hpp +++ b/src/protocols/core/Output.hpp @@ -19,13 +19,13 @@ class CWLOutputResource { SP getResource(); void updateState(); - PHLMONITORREF m_monitor; - WP m_owner; - WP m_self; + PHLMONITORREF monitor; + WP owner; + WP self; private: - SP m_resource; - wl_client* m_client = nullptr; + SP resource; + wl_client* pClient = nullptr; friend class CWLOutputProtocol; }; @@ -34,33 +34,29 @@ class CWLOutputProtocol : public IWaylandProtocol { public: CWLOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name, PHLMONITOR pMonitor); - virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); - std::vector> outputResourcesFrom(wl_client* client); - void sendDone(); + SP outputResourceFrom(wl_client* client); + void sendDone(); - PHLMONITORREF m_monitor; - WP m_self; + PHLMONITORREF monitor; + WP self; // will mark the protocol for removal, will be removed when no. of bound outputs is 0 (or when overwritten by a new global) void remove(); bool isDefunct(); // true if above was called - struct { - CSignalT> outputBound; - } m_events; - private: void destroyResource(CWLOutputResource* resource); // - std::vector> m_outputs; - bool m_defunct = false; - std::string m_name = ""; + std::vector> m_vOutputs; + bool defunct = false; + std::string szName = ""; struct { CHyprSignalListener modeChanged; - } m_listeners; + } listeners; friend class CWLOutputResource; }; diff --git a/src/protocols/core/Seat.cpp b/src/protocols/core/Seat.cpp index 52015fd8..5bdb95f0 100644 --- a/src/protocols/core/Seat.cpp +++ b/src/protocols/core/Seat.cpp @@ -4,500 +4,462 @@ #include "../../devices/IKeyboard.hpp" #include "../../devices/IHID.hpp" #include "../../managers/SeatManager.hpp" -#include "../../helpers/time/Time.hpp" #include "../../config/ConfigValue.hpp" #include #include -constexpr const float WL_FIXED_EPSILON = 1.F / 256.F; - -CWLTouchResource::CWLTouchResource(SP resource_, SP owner_) : m_owner(owner_), m_resource(resource_) { +CWLTouchResource::CWLTouchResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setRelease([this](CWlTouch* r) { PROTO::seat->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlTouch* r) { PROTO::seat->destroyResource(this); }); + resource->setRelease([this](CWlTouch* r) { PROTO::seat->destroyResource(this); }); + resource->setOnDestroy([this](CWlTouch* r) { PROTO::seat->destroyResource(this); }); } bool CWLTouchResource::good() { - return m_resource->resource(); + return resource->resource(); } void CWLTouchResource::sendDown(SP surface, uint32_t timeMs, int32_t id, const Vector2D& local) { - if (!m_owner || !surface || !surface->getResource()->resource()) + if (!owner) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) return; - ASSERT(surface->client() == m_owner->client()); + ASSERT(surface->client() == owner->client()); - m_currentSurface = surface; - m_listeners.destroySurface = surface->m_events.destroy.listen([this, timeMs, id] { sendUp(timeMs + 10 /* hack */, id); }); + currentSurface = surface; + listeners.destroySurface = surface->events.destroy.registerListener([this, timeMs, id](std::any d) { sendUp(timeMs + 10 /* hack */, id); }); - m_resource->sendDown(g_pSeatManager->nextSerial(m_owner.lock()), timeMs, surface->getResource().get(), id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); + resource->sendDown(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->getResource().get(), id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); - m_fingers++; + fingers++; } void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) { - if (!m_owner) + if (!owner) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) return; - m_resource->sendUp(g_pSeatManager->nextSerial(m_owner.lock()), timeMs, id); - m_fingers--; - if (m_fingers <= 0) { - m_currentSurface.reset(); - m_listeners.destroySurface.reset(); - m_fingers = 0; + resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id); + fingers--; + if (fingers <= 0) { + currentSurface.reset(); + listeners.destroySurface.reset(); + fingers = 0; } } void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) { - if (!m_owner) + if (!owner) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) return; - m_resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); + resource->sendMotion(timeMs, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); } void CWLTouchResource::sendFrame() { - if (!m_owner) + if (!owner) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) return; - m_resource->sendFrame(); + resource->sendFrame(); } void CWLTouchResource::sendCancel() { - if (!m_owner || !m_currentSurface) + if (!owner || !currentSurface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) return; - m_resource->sendCancel(); + resource->sendCancel(); } void CWLTouchResource::sendShape(int32_t id, const Vector2D& shape) { - if (!m_owner || !m_currentSurface || m_resource->version() < 6) + if (!owner || !currentSurface || resource->version() < 6) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) return; - m_resource->sendShape(id, wl_fixed_from_double(shape.x), wl_fixed_from_double(shape.y)); + resource->sendShape(id, wl_fixed_from_double(shape.x), wl_fixed_from_double(shape.y)); } void CWLTouchResource::sendOrientation(int32_t id, double angle) { - if (!m_owner || !m_currentSurface || m_resource->version() < 6) + if (!owner || !currentSurface || resource->version() < 6) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH)) return; - m_resource->sendOrientation(id, wl_fixed_from_double(angle)); + resource->sendOrientation(id, wl_fixed_from_double(angle)); } -CWLPointerResource::CWLPointerResource(SP resource_, SP owner_) : m_owner(owner_), m_resource(resource_) { +CWLPointerResource::CWLPointerResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setData(this); + resource->setRelease([this](CWlPointer* r) { PROTO::seat->destroyResource(this); }); + resource->setOnDestroy([this](CWlPointer* r) { PROTO::seat->destroyResource(this); }); - m_resource->setRelease([this](CWlPointer* r) { PROTO::seat->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlPointer* r) { PROTO::seat->destroyResource(this); }); - - m_resource->setSetCursor([this](CWlPointer* r, uint32_t serial, wl_resource* surf, int32_t hotX, int32_t hotY) { - if (!m_owner) { - LOGM(Log::ERR, "Client bug: setCursor when seatClient is already dead"); + resource->setSetCursor([this](CWlPointer* r, uint32_t serial, wl_resource* surf, int32_t hotX, int32_t hotY) { + if (!owner) { + LOGM(ERR, "Client bug: setCursor when seatClient is already dead"); return; } auto surfResource = surf ? CWLSurfaceResource::fromResource(surf) : nullptr; - if (surfResource && surfResource->m_role->role() != SURFACE_ROLE_CURSOR && surfResource->m_role->role() != SURFACE_ROLE_UNASSIGNED) { + if (surfResource && surfResource->role->role() != SURFACE_ROLE_CURSOR && surfResource->role->role() != SURFACE_ROLE_UNASSIGNED) { r->error(-1, "Cursor surface already has a different role"); return; } - if (surfResource && surfResource->m_role->role() != SURFACE_ROLE_CURSOR) { - surfResource->m_role = makeShared(); + if (surfResource && surfResource->role->role() != SURFACE_ROLE_CURSOR) { + surfResource->role = makeShared(); surfResource->updateCursorShm(); } - g_pSeatManager->onSetCursor(m_owner.lock(), serial, surfResource, {hotX, hotY}); + g_pSeatManager->onSetCursor(owner.lock(), serial, surfResource, {hotX, hotY}); }); - if (g_pSeatManager->m_state.pointerFocus && g_pSeatManager->m_state.pointerFocus->client() == m_resource->client()) - sendEnter(g_pSeatManager->m_state.pointerFocus.lock(), {-1, -1} /* Coords don't really matter that much, they will be updated next move */); -} - -CWLPointerResource::~CWLPointerResource() { - m_events.destroyed.emit(); + if (g_pSeatManager->state.pointerFocus && g_pSeatManager->state.pointerFocus->client() == resource->client()) + sendEnter(g_pSeatManager->state.pointerFocus.lock(), {-1, -1} /* Coords don't really matter that much, they will be updated next move */); } int CWLPointerResource::version() { - return m_resource->version(); + return resource->version(); } bool CWLPointerResource::good() { - return m_resource->resource(); -} - -SP CWLPointerResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + return resource->resource(); } void CWLPointerResource::sendEnter(SP surface, const Vector2D& local) { - if (!m_owner || m_currentSurface == surface || !surface->getResource()->resource()) + if (!owner || currentSurface == surface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - if (m_currentSurface) { - LOGM(Log::WARN, "requested CWLPointerResource::sendEnter without sendLeave first."); + if (currentSurface) { + LOGM(WARN, "requested CWLPointerResource::sendEnter without sendLeave first."); sendLeave(); } - ASSERT(surface->client() == m_owner->client()); + ASSERT(surface->client() == owner->client()); - m_currentSurface = surface; - m_listeners.destroySurface = surface->m_events.destroy.listen([this] { sendLeave(); }); + currentSurface = surface; + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { sendLeave(); }); - const auto fixedLocal = fixPosWithWlFixed(local); - - m_resource->sendEnter(g_pSeatManager->nextSerial(m_owner.lock()), surface->getResource().get(), wl_fixed_from_double(fixedLocal.x), wl_fixed_from_double(fixedLocal.y)); + resource->sendEnter(g_pSeatManager->nextSerial(owner.lock()), surface->getResource().get(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); } void CWLPointerResource::sendLeave() { - if (!m_owner || !m_currentSurface || !m_currentSurface->getResource()->resource()) + if (!owner || !currentSurface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; // release all buttons unless we have a dnd going on in which case // the events shall be lost. if (!PROTO::data->dndActive()) { - for (auto const& b : m_pressedButtons) { - sendButton(Time::millis(Time::steadyNow()), b, WL_POINTER_BUTTON_STATE_RELEASED); + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + for (auto const& b : pressedButtons) { + sendButton(now.tv_sec * 1000 + now.tv_nsec / 1000000, b, WL_POINTER_BUTTON_STATE_RELEASED); } } - m_pressedButtons.clear(); + pressedButtons.clear(); - m_resource->sendLeave(g_pSeatManager->nextSerial(m_owner.lock()), m_currentSurface->getResource().get()); - m_currentSurface.reset(); - m_listeners.destroySurface.reset(); + resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get()); + currentSurface.reset(); + listeners.destroySurface.reset(); } void CWLPointerResource::sendMotion(uint32_t timeMs, const Vector2D& local) { - if (!m_owner || !m_currentSurface) + if (!owner || !currentSurface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - const auto fixedLocal = fixPosWithWlFixed(local); - - m_resource->sendMotion(timeMs, wl_fixed_from_double(fixedLocal.x), wl_fixed_from_double(fixedLocal.y)); + resource->sendMotion(timeMs, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); } void CWLPointerResource::sendButton(uint32_t timeMs, uint32_t button, wl_pointer_button_state state) { - if (!m_owner || !m_currentSurface) + if (!owner || !currentSurface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - if (state == WL_POINTER_BUTTON_STATE_RELEASED && std::ranges::find(m_pressedButtons, button) == m_pressedButtons.end()) { - LOGM(Log::ERR, "sendButton release on a non-pressed button"); + if (state == WL_POINTER_BUTTON_STATE_RELEASED && std::find(pressedButtons.begin(), pressedButtons.end(), button) == pressedButtons.end()) { + LOGM(ERR, "sendButton release on a non-pressed button"); return; - } else if (state == WL_POINTER_BUTTON_STATE_PRESSED && std::ranges::find(m_pressedButtons, button) != m_pressedButtons.end()) { - LOGM(Log::ERR, "sendButton press on a non-pressed button"); + } else if (state == WL_POINTER_BUTTON_STATE_PRESSED && std::find(pressedButtons.begin(), pressedButtons.end(), button) != pressedButtons.end()) { + LOGM(ERR, "sendButton press on a non-pressed button"); return; } if (state == WL_POINTER_BUTTON_STATE_RELEASED) - std::erase(m_pressedButtons, button); + std::erase(pressedButtons, button); else if (state == WL_POINTER_BUTTON_STATE_PRESSED) - m_pressedButtons.emplace_back(button); + pressedButtons.emplace_back(button); - m_resource->sendButton(g_pSeatManager->nextSerial(m_owner.lock()), timeMs, button, state); + resource->sendButton(g_pSeatManager->nextSerial(owner.lock()), timeMs, button, state); } void CWLPointerResource::sendAxis(uint32_t timeMs, wl_pointer_axis axis, double value) { - if (!m_owner || !m_currentSurface) + if (!owner || !currentSurface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - m_resource->sendAxis(timeMs, axis, wl_fixed_from_double(value)); + resource->sendAxis(timeMs, axis, wl_fixed_from_double(value)); } void CWLPointerResource::sendFrame() { - if (!m_owner || m_resource->version() < 5) + if (!owner || resource->version() < 5) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - m_resource->sendFrame(); + resource->sendFrame(); } void CWLPointerResource::sendAxisSource(wl_pointer_axis_source source) { - if (!m_owner || !m_currentSurface || m_resource->version() < 5) + if (!owner || !currentSurface || resource->version() < 5) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - m_resource->sendAxisSource(source); + resource->sendAxisSource(source); } void CWLPointerResource::sendAxisStop(uint32_t timeMs, wl_pointer_axis axis) { - if (!m_owner || !m_currentSurface || m_resource->version() < 5) + if (!owner || !currentSurface || resource->version() < 5) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - m_resource->sendAxisStop(timeMs, axis); + resource->sendAxisStop(timeMs, axis); } void CWLPointerResource::sendAxisDiscrete(wl_pointer_axis axis, int32_t discrete) { - if (!m_owner || !m_currentSurface || m_resource->version() < 5) + if (!owner || !currentSurface || resource->version() < 5) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - m_resource->sendAxisDiscrete(axis, discrete); + resource->sendAxisDiscrete(axis, discrete); } void CWLPointerResource::sendAxisValue120(wl_pointer_axis axis, int32_t value120) { - if (!m_owner || !m_currentSurface || m_resource->version() < 8) + if (!owner || !currentSurface || resource->version() < 8) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - m_resource->sendAxisValue120(axis, value120); + resource->sendAxisValue120(axis, value120); } void CWLPointerResource::sendAxisRelativeDirection(wl_pointer_axis axis, wl_pointer_axis_relative_direction direction) { - if (!m_owner || !m_currentSurface || m_resource->version() < 9) + if (!owner || !currentSurface || resource->version() < 9) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_POINTER)) return; - m_resource->sendAxisRelativeDirection(axis, direction); + resource->sendAxisRelativeDirection(axis, direction); } -Vector2D CWLPointerResource::fixPosWithWlFixed(const Vector2D& pos) { - if (!m_currentSurface) - return pos; - - Vector2D newPos = pos; - - // When our cursor pos is right at the edge, wl_fixed will round it up, - // instead of down, meaning 10.999999 -> 11 instead of 10 + 255/256 - // if we are within that epsilon, move the coord a bit down to account for that. - if (std::abs(newPos.x - m_currentSurface->m_current.size.x) < WL_FIXED_EPSILON) - newPos.x = m_currentSurface->m_current.size.x - WL_FIXED_EPSILON * 2; - if (std::abs(newPos.y - m_currentSurface->m_current.size.y) < WL_FIXED_EPSILON) - newPos.y = m_currentSurface->m_current.size.y - WL_FIXED_EPSILON * 2; - - return newPos; -} - -CWLKeyboardResource::CWLKeyboardResource(SP resource_, SP owner_) : m_owner(owner_), m_resource(resource_) { +CWLKeyboardResource::CWLKeyboardResource(SP resource_, SP owner_) : owner(owner_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setRelease([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); }); + resource->setRelease([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); }); + resource->setOnDestroy([this](CWlKeyboard* r) { PROTO::seat->destroyResource(this); }); - if (!g_pSeatManager->m_keyboard) { - LOGM(Log::ERR, "No keyboard on bound wl_keyboard??"); + if (!g_pSeatManager->keyboard) { + LOGM(ERR, "No keyboard on bound wl_keyboard??"); return; } - sendKeymap(g_pSeatManager->m_keyboard.lock()); - repeatInfo(g_pSeatManager->m_keyboard->m_repeatRate, g_pSeatManager->m_keyboard->m_repeatDelay); + sendKeymap(g_pSeatManager->keyboard.lock()); + repeatInfo(g_pSeatManager->keyboard->repeatRate, g_pSeatManager->keyboard->repeatDelay); - if (g_pSeatManager->m_state.keyboardFocus && g_pSeatManager->m_state.keyboardFocus->client() == m_resource->client()) { - wl_array keys; - wl_array_init(&keys); - - sendEnter(g_pSeatManager->m_state.keyboardFocus.lock(), &keys); - - wl_array_release(&keys); - } + if (g_pSeatManager->state.keyboardFocus && g_pSeatManager->state.keyboardFocus->client() == resource->client()) + sendEnter(g_pSeatManager->state.keyboardFocus.lock()); } bool CWLKeyboardResource::good() { - return m_resource->resource(); + return resource->resource(); } void CWLKeyboardResource::sendKeymap(SP keyboard) { if (!keyboard) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) return; - std::string_view keymap = keyboard->m_xkbKeymapV1String; - Hyprutils::OS::CFileDescriptor& fd = keyboard->m_xkbKeymapV1FD; - uint32_t size = keyboard->m_xkbKeymapV1String.length() + 1; + std::string_view keymap = keyboard->xkbKeymapString; + Hyprutils::OS::CFileDescriptor& fd = keyboard->xkbKeymapFD; + uint32_t size = keyboard->xkbKeymapString.length() + 1; - if (keymap == m_lastKeymap) + if (keymap == lastKeymap) return; - m_lastKeymap = keymap; + lastKeymap = keymap; const wl_keyboard_keymap_format format = keyboard ? WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 : WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP; - m_resource->sendKeymap(format, fd.get(), size); + resource->sendKeymap(format, fd.get(), size); } -void CWLKeyboardResource::sendEnter(SP surface, wl_array* keys) { - ASSERT(keys); - - if (!m_owner || m_currentSurface == surface || !surface->getResource()->resource()) +void CWLKeyboardResource::sendEnter(SP surface) { + if (!owner || currentSurface == surface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) return; - if (m_currentSurface) { - LOGM(Log::WARN, "requested CWLKeyboardResource::sendEnter without sendLeave first."); + if (currentSurface) { + LOGM(WARN, "requested CWLKeyboardResource::sendEnter without sendLeave first."); sendLeave(); } - ASSERT(surface->client() == m_owner->client()); + ASSERT(surface->client() == owner->client()); - m_currentSurface = surface; - m_listeners.destroySurface = surface->m_events.destroy.listen([this] { sendLeave(); }); + currentSurface = surface; + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { sendLeave(); }); - m_resource->sendEnter(g_pSeatManager->nextSerial(m_owner.lock()), surface->getResource().get(), keys); + wl_array arr; + wl_array_init(&arr); + + resource->sendEnter(g_pSeatManager->nextSerial(owner.lock()), surface->getResource().get(), &arr); + + wl_array_release(&arr); } void CWLKeyboardResource::sendLeave() { - if (!m_owner || !m_currentSurface || !m_currentSurface->getResource()->resource()) + if (!owner || !currentSurface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) return; - m_resource->sendLeave(g_pSeatManager->nextSerial(m_owner.lock()), m_currentSurface->getResource().get()); - m_currentSurface.reset(); - m_listeners.destroySurface.reset(); + resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get()); + currentSurface.reset(); + listeners.destroySurface.reset(); } void CWLKeyboardResource::sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state) { - if (!m_owner || !m_currentSurface) + if (!owner || !currentSurface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) return; - m_resource->sendKey(g_pSeatManager->nextSerial(m_owner.lock()), timeMs, key, state); + resource->sendKey(g_pSeatManager->nextSerial(owner.lock()), timeMs, key, state); } void CWLKeyboardResource::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { - if (!m_owner || !m_currentSurface) + if (!owner || !currentSurface) return; - if (!(PROTO::seat->m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + if (!(PROTO::seat->currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) return; - m_resource->sendModifiers(g_pSeatManager->nextSerial(m_owner.lock()), depressed, latched, locked, group); + resource->sendModifiers(g_pSeatManager->nextSerial(owner.lock()), depressed, latched, locked, group); } void CWLKeyboardResource::repeatInfo(uint32_t rate, uint32_t delayMs) { - if (!m_owner || m_resource->version() < 4 || (rate == m_lastRate && delayMs == m_lastDelayMs)) + if (!owner || resource->version() < 4 || (rate == lastRate && delayMs == lastDelayMs)) return; - m_lastRate = rate; - m_lastDelayMs = delayMs; + lastRate = rate; + lastDelayMs = delayMs; - m_resource->sendRepeatInfo(rate, delayMs); + resource->sendRepeatInfo(rate, delayMs); } -CWLSeatResource::CWLSeatResource(SP resource_) : m_resource(resource_) { +CWLSeatResource::CWLSeatResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWlSeat* r) { - m_events.destroy.emit(); + resource->setOnDestroy([this](CWlSeat* r) { + events.destroy.emit(); PROTO::seat->destroyResource(this); }); - m_resource->setRelease([this](CWlSeat* r) { - m_events.destroy.emit(); + resource->setRelease([this](CWlSeat* r) { + events.destroy.emit(); PROTO::seat->destroyResource(this); }); - m_client = m_resource->client(); + pClient = resource->client(); - m_resource->setGetKeyboard([this](CWlSeat* r, uint32_t id) { - const auto RESOURCE = PROTO::seat->m_keyboards.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_self.lock())); + resource->setGetKeyboard([this](CWlSeat* r, uint32_t id) { + const auto RESOURCE = PROTO::seat->m_vKeyboards.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::seat->m_keyboards.pop_back(); + PROTO::seat->m_vKeyboards.pop_back(); return; } - m_keyboards.emplace_back(RESOURCE); + keyboards.emplace_back(RESOURCE); }); - m_resource->setGetPointer([this](CWlSeat* r, uint32_t id) { - const auto RESOURCE = PROTO::seat->m_pointers.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_self.lock())); + resource->setGetPointer([this](CWlSeat* r, uint32_t id) { + const auto RESOURCE = PROTO::seat->m_vPointers.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::seat->m_pointers.pop_back(); + PROTO::seat->m_vPointers.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - - m_pointers.emplace_back(RESOURCE); + pointers.emplace_back(RESOURCE); }); - m_resource->setGetTouch([this](CWlSeat* r, uint32_t id) { - const auto RESOURCE = PROTO::seat->m_touches.emplace_back(makeShared(makeShared(r->client(), r->version(), id), m_self.lock())); + resource->setGetTouch([this](CWlSeat* r, uint32_t id) { + const auto RESOURCE = PROTO::seat->m_vTouches.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::seat->m_touches.pop_back(); + PROTO::seat->m_vTouches.pop_back(); return; } - m_touches.emplace_back(RESOURCE); + touches.emplace_back(RESOURCE); }); - if (m_resource->version() >= 2) - m_resource->sendName(HL_SEAT_NAME); + if (resource->version() >= 2) + resource->sendName(HL_SEAT_NAME); - sendCapabilities(PROTO::seat->m_currentCaps); + sendCapabilities(PROTO::seat->currentCaps); } CWLSeatResource::~CWLSeatResource() { - m_events.destroy.emit(); + events.destroy.emit(); } void CWLSeatResource::sendCapabilities(uint32_t caps) { @@ -509,15 +471,15 @@ void CWLSeatResource::sendCapabilities(uint32_t caps) { if (caps & eHIDCapabilityType::HID_INPUT_CAPABILITY_TOUCH) wlCaps |= WL_SEAT_CAPABILITY_TOUCH; - m_resource->sendCapabilities(sc(wlCaps)); + resource->sendCapabilities((wl_seat_capability)wlCaps); } bool CWLSeatResource::good() { - return m_resource->resource(); + return resource->resource(); } wl_client* CWLSeatResource::client() { - return m_client; + return pClient; } CWLSeatProtocol::CWLSeatProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -525,68 +487,68 @@ CWLSeatProtocol::CWLSeatProtocol(const wl_interface* iface, const int& ver, cons } void CWLSeatProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_seatResources.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vSeatResources.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_seatResources.pop_back(); + m_vSeatResources.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; - LOGM(Log::DEBUG, "New seat resource bound at {:x}", (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New seat resource bound at {:x}", (uintptr_t)RESOURCE.get()); - m_events.newSeatResource.emit(RESOURCE); + events.newSeatResource.emit(RESOURCE); } void CWLSeatProtocol::destroyResource(CWLSeatResource* seat) { - std::erase_if(m_seatResources, [&](const auto& other) { return other.get() == seat; }); + std::erase_if(m_vSeatResources, [&](const auto& other) { return other.get() == seat; }); } void CWLSeatProtocol::destroyResource(CWLKeyboardResource* resource) { - std::erase_if(m_keyboards, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vKeyboards, [&](const auto& other) { return other.get() == resource; }); } void CWLSeatProtocol::destroyResource(CWLPointerResource* resource) { - std::erase_if(m_pointers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vPointers, [&](const auto& other) { return other.get() == resource; }); } void CWLSeatProtocol::destroyResource(CWLTouchResource* resource) { - std::erase_if(m_touches, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vTouches, [&](const auto& other) { return other.get() == resource; }); } void CWLSeatProtocol::updateCapabilities(uint32_t caps) { - if (caps == m_currentCaps) + if (caps == currentCaps) return; - m_currentCaps = caps; + currentCaps = caps; - for (auto const& s : m_seatResources) { + for (auto const& s : m_vSeatResources) { s->sendCapabilities(caps); } } void CWLSeatProtocol::updateKeymap() { - if (!(m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + if (!(currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) return; - for (auto const& k : m_keyboards) { - k->sendKeymap(g_pSeatManager->m_keyboard.lock()); + for (auto const& k : m_vKeyboards) { + k->sendKeymap(g_pSeatManager->keyboard.lock()); } } void CWLSeatProtocol::updateRepeatInfo(uint32_t rate, uint32_t delayMs) { - if (!(m_currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) + if (!(currentCaps & eHIDCapabilityType::HID_INPUT_CAPABILITY_KEYBOARD)) return; - for (auto const& k : m_keyboards) { + for (auto const& k : m_vKeyboards) { k->repeatInfo(rate, delayMs); } } SP CWLSeatProtocol::seatResourceForClient(wl_client* client) { - for (auto const& r : m_seatResources) { + for (auto const& r : m_vSeatResources) { if (r->client() == client) return r; } @@ -595,8 +557,8 @@ SP CWLSeatProtocol::seatResourceForClient(wl_client* client) { } std::vector& CCursorSurfaceRole::cursorPixelData(SP surface) { - RASSERT(surface->m_role->role() == SURFACE_ROLE_CURSOR, "cursorPixelData called on a non-cursor surface"); + RASSERT(surface->role->role() == SURFACE_ROLE_CURSOR, "cursorPixelData called on a non-cursor surface"); - auto role = sc(surface->m_role.get()); - return role->m_cursorShmPixelData; + auto role = (CCursorSurfaceRole*)surface->role.get(); + return role->cursorShmPixelData; } diff --git a/src/protocols/core/Seat.hpp b/src/protocols/core/Seat.hpp index 85dc5c39..a4889379 100644 --- a/src/protocols/core/Seat.hpp +++ b/src/protocols/core/Seat.hpp @@ -12,7 +12,6 @@ #include #include "../WaylandProtocol.hpp" #include -#include #include "wayland.hpp" #include "../../helpers/signal/Signal.hpp" #include "../../helpers/math/Math.hpp" @@ -39,7 +38,7 @@ class CCursorSurfaceRole : public ISurfaceRole { static std::vector& cursorPixelData(SP surface); private: - std::vector m_cursorShmPixelData; + std::vector cursorShmPixelData; }; class CWLTouchResource { @@ -55,23 +54,22 @@ class CWLTouchResource { void sendShape(int32_t id, const Vector2D& shape); void sendOrientation(int32_t id, double angle); - WP m_owner; + WP owner; private: - SP m_resource; - WP m_currentSurface; + SP resource; + WP currentSurface; - int m_fingers = 0; + int fingers = 0; struct { CHyprSignalListener destroySurface; - } m_listeners; + } listeners; }; class CWLPointerResource { public: CWLPointerResource(SP resource_, SP owner_); - ~CWLPointerResource(); bool good(); int version(); @@ -87,29 +85,17 @@ class CWLPointerResource { void sendAxisValue120(wl_pointer_axis axis, int32_t value120); void sendAxisRelativeDirection(wl_pointer_axis axis, wl_pointer_axis_relative_direction direction); - WP m_owner; - - struct { - CSignalT<> destroyed; - } m_events; - - // - static SP fromResource(wl_resource* res); + WP owner; private: - SP m_resource; - WP m_currentSurface; - WP m_self; + SP resource; + WP currentSurface; - std::vector m_pressedButtons; - - Vector2D fixPosWithWlFixed(const Vector2D& pos); + std::vector pressedButtons; struct { CHyprSignalListener destroySurface; - } m_listeners; - - friend class CWLSeatResource; + } listeners; }; class CWLKeyboardResource { @@ -118,25 +104,25 @@ class CWLKeyboardResource { bool good(); void sendKeymap(SP keeb); - void sendEnter(SP surface, wl_array* keys); + void sendEnter(SP surface); void sendLeave(); void sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state); void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); void repeatInfo(uint32_t rate, uint32_t delayMs); - WP m_owner; + WP owner; private: - SP m_resource; - WP m_currentSurface; + SP resource; + WP currentSurface; struct { CHyprSignalListener destroySurface; - } m_listeners; + } listeners; - std::string m_lastKeymap = ""; - uint32_t m_lastRate = 0; - uint32_t m_lastDelayMs = 0; + std::string lastKeymap = ""; + uint32_t lastRate = 0; + uint32_t lastDelayMs = 0; }; class CWLSeatResource { @@ -149,19 +135,19 @@ class CWLSeatResource { bool good(); wl_client* client(); - std::vector> m_pointers; - std::vector> m_keyboards; - std::vector> m_touches; + std::vector> pointers; + std::vector> keyboards; + std::vector> touches; - WP m_self; + WP self; struct { - CSignalT<> destroy; - } m_events; + CSignal destroy; + } events; private: - SP m_resource; - wl_client* m_client = nullptr; + SP resource; + wl_client* pClient = nullptr; }; class CWLSeatProtocol : public IWaylandProtocol { @@ -171,8 +157,8 @@ class CWLSeatProtocol : public IWaylandProtocol { virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); struct { - CSignalT> newSeatResource; - } m_events; + CSignal newSeatResource; // SP + } events; private: void updateCapabilities(uint32_t caps); // in IHID caps @@ -185,15 +171,15 @@ class CWLSeatProtocol : public IWaylandProtocol { void destroyResource(CWLPointerResource* resource); // - std::vector> m_seatResources; - std::vector> m_keyboards; - std::vector> m_touches; - std::vector> m_pointers; + std::vector> m_vSeatResources; + std::vector> m_vKeyboards; + std::vector> m_vTouches; + std::vector> m_vPointers; SP seatResourceForClient(wl_client* client); // - uint32_t m_currentCaps = 0; + uint32_t currentCaps = 0; friend class CWLSeatResource; friend class CWLKeyboardResource; diff --git a/src/protocols/core/Shm.cpp b/src/protocols/core/Shm.cpp index 7cca3814..420b4a3f 100644 --- a/src/protocols/core/Shm.cpp +++ b/src/protocols/core/Shm.cpp @@ -9,33 +9,30 @@ #include "../../render/Renderer.hpp" using namespace Hyprutils::OS; -CWLSHMBuffer::CWLSHMBuffer(WP pool_, uint32_t id, int32_t offset_, const Vector2D& size_, int32_t stride_, uint32_t fmt_) { - if UNLIKELY (!pool_) - return; - - if UNLIKELY (!pool_->m_pool->m_data) +CWLSHMBuffer::CWLSHMBuffer(SP pool_, uint32_t id, int32_t offset_, const Vector2D& size_, int32_t stride_, uint32_t fmt_) { + if UNLIKELY (!pool_->pool->data) return; g_pHyprRenderer->makeEGLCurrent(); - size = size_; - m_pool = pool_->m_pool; - m_stride = stride_; - m_fmt = fmt_; - m_offset = offset_; - m_opaque = NFormatUtils::isFormatOpaque(NFormatUtils::shmToDRM(fmt_)); + size = size_; + pool = pool_->pool; + stride = stride_; + fmt = fmt_; + offset = offset_; + opaque = NFormatUtils::isFormatOpaque(NFormatUtils::shmToDRM(fmt_)); - m_resource = CWLBufferResource::create(makeShared(pool_->m_resource->client(), 1, id)); + resource = CWLBufferResource::create(makeShared(pool_->resource->client(), 1, id)); - m_listeners.bufferResourceDestroy = events.destroy.listen([this] { - m_listeners.bufferResourceDestroy.reset(); + listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) { + listeners.bufferResourceDestroy.reset(); PROTO::shm->destroyResource(this); }); } CWLSHMBuffer::~CWLSHMBuffer() { - if (m_resource) - m_resource->sendRelease(); + if (resource) + resource->sendRelease(); } Aquamarine::eBufferCapability CWLSHMBuffer::caps() { @@ -53,16 +50,16 @@ bool CWLSHMBuffer::isSynchronous() { Aquamarine::SSHMAttrs CWLSHMBuffer::shm() { Aquamarine::SSHMAttrs attrs; attrs.success = true; - attrs.fd = m_pool->m_fd.get(); - attrs.format = NFormatUtils::shmToDRM(m_fmt); + attrs.fd = pool->fd.get(); + attrs.format = NFormatUtils::shmToDRM(fmt); attrs.size = size; - attrs.stride = m_stride; - attrs.offset = m_offset; + attrs.stride = stride; + attrs.offset = offset; return attrs; } std::tuple CWLSHMBuffer::beginDataPtr(uint32_t flags) { - return {sc(m_pool->m_data) + m_offset, m_fmt, m_stride * size.y}; + return {(uint8_t*)pool->data + offset, fmt, stride * size.y}; } void CWLSHMBuffer::endDataPtr() { @@ -77,72 +74,70 @@ void CWLSHMBuffer::update(const CRegion& damage) { ; } -CSHMPool::CSHMPool(CFileDescriptor fd_, size_t size_) : m_fd(std::move(fd_)), m_size(size_), m_data(mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd.get(), 0)) { +CSHMPool::CSHMPool(CFileDescriptor fd_, size_t size_) : fd(std::move(fd_)), size(size_), data(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)) { ; } CSHMPool::~CSHMPool() { - if (m_data != MAP_FAILED) - munmap(m_data, m_size); + munmap(data, size); } void CSHMPool::resize(size_t size_) { - LOGM(Log::DEBUG, "Resizing a SHM pool from {} to {}", m_size, size_); + LOGM(LOG, "Resizing a SHM pool from {} to {}", size, size_); - if (m_data != MAP_FAILED) - munmap(m_data, m_size); + if (data) + munmap(data, size); + size = size_; + data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0); - m_size = size_; - m_data = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd.get(), 0); - - if UNLIKELY (m_data == MAP_FAILED) - LOGM(Log::ERR, "Couldn't mmap {} bytes from fd {} of shm client", m_size, m_fd.get()); + if UNLIKELY (data == MAP_FAILED) + LOGM(ERR, "Couldn't mmap {} bytes from fd {} of shm client", size, fd.get()); } static int shmIsSizeValid(CFileDescriptor& fd, size_t size) { struct stat st; if UNLIKELY (fstat(fd.get(), &st) == -1) { - LOGM(Log::ERR, "Couldn't get stat for fd {} of shm client", fd.get()); + LOGM(ERR, "Couldn't get stat for fd {} of shm client", fd.get()); return 0; } - return sc(st.st_size) >= size; + return (size_t)st.st_size >= size; } -CWLSHMPoolResource::CWLSHMPoolResource(UP&& resource_, CFileDescriptor fd_, size_t size_) : m_resource(std::move(resource_)) { +CWLSHMPoolResource::CWLSHMPoolResource(SP resource_, CFileDescriptor fd_, size_t size_) : resource(resource_) { if UNLIKELY (!good()) return; if UNLIKELY (!shmIsSizeValid(fd_, size_)) { - m_resource->error(-1, "The size of the file is not big enough for the shm pool"); + resource_->error(-1, "The size of the file is not big enough for the shm pool"); return; } - m_pool = makeShared(std::move(fd_), size_); + pool = makeShared(std::move(fd_), size_); - m_resource->setDestroy([this](CWlShmPool* r) { PROTO::shm->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlShmPool* r) { PROTO::shm->destroyResource(this); }); + resource->setDestroy([this](CWlShmPool* r) { PROTO::shm->destroyResource(this); }); + resource->setOnDestroy([this](CWlShmPool* r) { PROTO::shm->destroyResource(this); }); - m_resource->setResize([this](CWlShmPool* r, int32_t size_) { - if UNLIKELY (size_ < sc(m_pool->m_size)) { + resource->setResize([this](CWlShmPool* r, int32_t size_) { + if UNLIKELY (size_ < (int32_t)pool->size) { r->error(-1, "Shrinking a shm pool is illegal"); return; } - if UNLIKELY (!shmIsSizeValid(m_pool->m_fd, size_)) { + if UNLIKELY (!shmIsSizeValid(pool->fd, size_)) { r->error(-1, "The size of the file is not big enough for the shm pool"); return; } - m_pool->resize(size_); + pool->resize(size_); }); - m_resource->setCreateBuffer([this](CWlShmPool* r, uint32_t id, int32_t offset, int32_t w, int32_t h, int32_t stride, uint32_t fmt) { - if UNLIKELY (!m_pool || !m_pool->m_data) { + resource->setCreateBuffer([this](CWlShmPool* r, uint32_t id, int32_t offset, int32_t w, int32_t h, int32_t stride, uint32_t fmt) { + if UNLIKELY (!pool || !pool->data) { r->error(-1, "The provided shm pool failed to allocate properly"); return; } - if UNLIKELY (std::ranges::find(PROTO::shm->m_shmFormats, fmt) == PROTO::shm->m_shmFormats.end()) { + if UNLIKELY (std::find(PROTO::shm->shmFormats.begin(), PROTO::shm->shmFormats.end(), fmt) == PROTO::shm->shmFormats.end()) { r->error(WL_SHM_ERROR_INVALID_FORMAT, "Format invalid"); return; } @@ -152,54 +147,53 @@ CWLSHMPoolResource::CWLSHMPoolResource(UP&& resource_, CFileDescript return; } - const auto& RESOURCE = PROTO::shm->m_buffers.emplace_back(makeShared(m_self, id, offset, Vector2D{w, h}, stride, fmt)); + const auto RESOURCE = PROTO::shm->m_vBuffers.emplace_back(makeShared(self.lock(), id, offset, Vector2D{w, h}, stride, fmt)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::shm->m_buffers.pop_back(); + PROTO::shm->m_vBuffers.pop_back(); return; } // append instance so that buffer knows its owner - RESOURCE->m_resource->m_buffer = RESOURCE; + RESOURCE->resource->buffer = RESOURCE; }); - if UNLIKELY (m_pool->m_data == MAP_FAILED) - m_resource->error(WL_SHM_ERROR_INVALID_FD, "Couldn't mmap from fd"); + if UNLIKELY (pool->data == MAP_FAILED) + resource->error(WL_SHM_ERROR_INVALID_FD, "Couldn't mmap from fd"); } bool CWLSHMPoolResource::good() { - return m_resource->resource(); + return resource->resource(); } -CWLSHMResource::CWLSHMResource(UP&& resource_) : m_resource(std::move(resource_)) { +CWLSHMResource::CWLSHMResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setRelease([this](CWlShm* r) { PROTO::shm->destroyResource(this); }); - m_resource->setOnDestroy([this](CWlShm* r) { PROTO::shm->destroyResource(this); }); + resource->setOnDestroy([this](CWlShm* r) { PROTO::shm->destroyResource(this); }); - m_resource->setCreatePool([](CWlShm* r, uint32_t id, int32_t fd, int32_t size) { + resource->setCreatePool([](CWlShm* r, uint32_t id, int32_t fd, int32_t size) { CFileDescriptor poolFd{fd}; - const auto& RESOURCE = PROTO::shm->m_pools.emplace_back(makeUnique(makeUnique(r->client(), r->version(), id), std::move(poolFd), size)); + const auto RESOURCE = PROTO::shm->m_vPools.emplace_back(makeShared(makeShared(r->client(), r->version(), id), std::move(poolFd), size)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::shm->m_pools.pop_back(); + PROTO::shm->m_vPools.pop_back(); return; } - RESOURCE->m_self = RESOURCE; + RESOURCE->self = RESOURCE; }); // send a few supported formats. No need for any other I think? - for (auto const& s : PROTO::shm->m_shmFormats) { - m_resource->sendFormat(sc(s)); + for (auto const& s : PROTO::shm->shmFormats) { + resource->sendFormat((wl_shm_format)s); } } bool CWLSHMResource::good() { - return m_resource->resource(); + return resource->resource(); } CWLSHMProtocol::CWLSHMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -207,36 +201,36 @@ CWLSHMProtocol::CWLSHMProtocol(const wl_interface* iface, const int& ver, const } void CWLSHMProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - if (m_shmFormats.empty()) { - m_shmFormats.push_back(WL_SHM_FORMAT_ARGB8888); - m_shmFormats.push_back(WL_SHM_FORMAT_XRGB8888); + if (shmFormats.empty()) { + shmFormats.push_back(WL_SHM_FORMAT_ARGB8888); + shmFormats.push_back(WL_SHM_FORMAT_XRGB8888); static const std::array supportedShmFourccFormats = { DRM_FORMAT_XBGR8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB2101010, DRM_FORMAT_ARGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_ABGR2101010, }; for (auto const& fmt : supportedShmFourccFormats) { - m_shmFormats.push_back(fmt); + shmFormats.push_back(fmt); } } - const auto& RESOURCE = m_managers.emplace_back(makeUnique(makeUnique(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CWLSHMProtocol::destroyResource(CWLSHMResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CWLSHMProtocol::destroyResource(CWLSHMPoolResource* resource) { - std::erase_if(m_pools, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vPools, [&](const auto& other) { return other.get() == resource; }); } void CWLSHMProtocol::destroyResource(CWLSHMBuffer* resource) { - std::erase_if(m_buffers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; }); } diff --git a/src/protocols/core/Shm.hpp b/src/protocols/core/Shm.hpp index 37210398..61b26f12 100644 --- a/src/protocols/core/Shm.hpp +++ b/src/protocols/core/Shm.hpp @@ -20,20 +20,19 @@ class CWLSHMPoolResource; class CSHMPool { public: - CSHMPool() = delete; CSHMPool(Hyprutils::OS::CFileDescriptor fd, size_t size); ~CSHMPool(); - Hyprutils::OS::CFileDescriptor m_fd; - size_t m_size = 0; - void* m_data = nullptr; + Hyprutils::OS::CFileDescriptor fd; + size_t size = 0; + void* data = nullptr; void resize(size_t size); }; class CWLSHMBuffer : public IHLBuffer { public: - CWLSHMBuffer(WP pool, uint32_t id, int32_t offset, const Vector2D& size, int32_t stride, uint32_t fmt); + CWLSHMBuffer(SP pool, uint32_t id, int32_t offset, const Vector2D& size, int32_t stride, uint32_t fmt); virtual ~CWLSHMBuffer(); virtual Aquamarine::eBufferCapability caps(); @@ -46,41 +45,40 @@ class CWLSHMBuffer : public IHLBuffer { bool good(); - int32_t m_offset = 0; - int32_t m_stride = 0; - uint32_t m_fmt = 0; - SP m_pool; + int32_t offset = 0, stride = 0; + uint32_t fmt = 0; + SP pool; private: struct { CHyprSignalListener bufferResourceDestroy; - } m_listeners; + } listeners; }; class CWLSHMPoolResource { public: - CWLSHMPoolResource(UP&& resource_, Hyprutils::OS::CFileDescriptor fd, size_t size); + CWLSHMPoolResource(SP resource_, Hyprutils::OS::CFileDescriptor fd, size_t size); bool good(); - SP m_pool; + SP pool; - WP m_self; + WP self; private: - UP m_resource; + SP resource; friend class CWLSHMBuffer; }; class CWLSHMResource { public: - CWLSHMResource(UP&& resource_); + CWLSHMResource(SP resource_); bool good(); private: - UP m_resource; + SP resource; }; class CWLSHMProtocol : public IWaylandProtocol { @@ -95,12 +93,12 @@ class CWLSHMProtocol : public IWaylandProtocol { void destroyResource(CWLSHMBuffer* resource); // - std::vector> m_managers; - std::vector> m_pools; - std::vector> m_buffers; + std::vector> m_vManagers; + std::vector> m_vPools; + std::vector> m_vBuffers; // - std::vector m_shmFormats; + std::vector shmFormats; friend class CWLSHMResource; friend class CWLSHMPoolResource; diff --git a/src/protocols/core/Subcompositor.cpp b/src/protocols/core/Subcompositor.cpp index 14dc4666..4edb07ff 100644 --- a/src/protocols/core/Subcompositor.cpp +++ b/src/protocols/core/Subcompositor.cpp @@ -3,150 +3,152 @@ #include CWLSubsurfaceResource::CWLSubsurfaceResource(SP resource_, SP surface_, SP parent_) : - m_surface(surface_), m_parent(parent_), m_resource(resource_) { + surface(surface_), parent(parent_), resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWlSubsurface* r) { destroy(); }); - m_resource->setDestroy([this](CWlSubsurface* r) { destroy(); }); + resource->setOnDestroy([this](CWlSubsurface* r) { destroy(); }); + resource->setDestroy([this](CWlSubsurface* r) { destroy(); }); - m_resource->setSetPosition([this](CWlSubsurface* r, int32_t x, int32_t y) { m_position = {x, y}; }); + resource->setSetPosition([this](CWlSubsurface* r, int32_t x, int32_t y) { position = {x, y}; }); - m_resource->setSetDesync([this](CWlSubsurface* r) { m_sync = false; }); - m_resource->setSetSync([this](CWlSubsurface* r) { m_sync = true; }); + resource->setSetDesync([this](CWlSubsurface* r) { sync = false; }); + resource->setSetSync([this](CWlSubsurface* r) { sync = true; }); - m_resource->setPlaceAbove([this](CWlSubsurface* r, wl_resource* surf) { + resource->setPlaceAbove([this](CWlSubsurface* r, wl_resource* surf) { auto SURF = CWLSurfaceResource::fromResource(surf); - if (!m_parent) + if (!parent) return; - std::erase_if(m_parent->m_subsurfaces, [this](const auto& e) { return e == m_self || !e; }); + auto pushAboveIndex = [this](int idx) -> void { + for (auto const& c : parent->subsurfaces) { + if (c->zIndex >= idx) + c->zIndex++; + } + }; - std::ranges::for_each(m_parent->m_subsurfaces, [](const auto& e) { e->m_zIndex *= 2; }); + std::erase_if(parent->subsurfaces, [this](const auto& e) { return e == self || !e; }); - auto it = std::ranges::find_if(m_parent->m_subsurfaces, [SURF](const auto& s) { return s->m_surface == SURF; }); + auto it = std::find(parent->subsurfaces.begin(), parent->subsurfaces.end(), SURF); - if ((it == m_parent->m_subsurfaces.end() && m_parent != SURF) || SURF == m_surface) { - // protocol error, this is not a valid surface - r->error(-1, "Invalid surface in placeAbove"); - return; - } - - if (it == m_parent->m_subsurfaces.end()) { - // parent surface - m_parent->m_subsurfaces.emplace_back(m_self); - m_zIndex = 1; + if (it == parent->subsurfaces.end()) { + LOGM(ERR, "Invalid surface reference in placeAbove, likely parent"); + pushAboveIndex(1); + parent->subsurfaces.emplace_back(self); + zIndex = 1; } else { - m_zIndex = (*it)->m_zIndex + 1; - m_parent->m_subsurfaces.emplace_back(m_self); + pushAboveIndex((*it)->zIndex); + zIndex = (*it)->zIndex; + parent->subsurfaces.emplace_back(self); } - m_parent->sortSubsurfaces(); + std::sort(parent->subsurfaces.begin(), parent->subsurfaces.end(), [](const auto& a, const auto& b) { return a->zIndex < b->zIndex; }); }); - m_resource->setPlaceBelow([this](CWlSubsurface* r, wl_resource* surf) { + resource->setPlaceBelow([this](CWlSubsurface* r, wl_resource* surf) { auto SURF = CWLSurfaceResource::fromResource(surf); - if (!m_parent) + if (!parent) return; - std::erase_if(m_parent->m_subsurfaces, [this](const auto& e) { return e == m_self || !e; }); + auto pushBelowIndex = [this](int idx) -> void { + for (auto const& c : parent->subsurfaces) { + if (c->zIndex <= idx) + c->zIndex--; + } + }; - std::ranges::for_each(m_parent->m_subsurfaces, [](const auto& e) { e->m_zIndex *= 2; }); + std::erase_if(parent->subsurfaces, [this](const auto& e) { return e == self || !e; }); - auto it = std::ranges::find_if(m_parent->m_subsurfaces, [SURF](const auto& s) { return s->m_surface == SURF; }); + auto it = std::find(parent->subsurfaces.begin(), parent->subsurfaces.end(), SURF); - if ((it == m_parent->m_subsurfaces.end() && m_parent != SURF) || SURF == m_surface) { - // protocol error, this is not a valid surface - r->error(-1, "Invalid surface in placeBelow"); - return; - } - - if (it == m_parent->m_subsurfaces.end()) { - // parent - m_parent->m_subsurfaces.emplace_back(m_self); - m_zIndex = -1; + if (it == parent->subsurfaces.end()) { + LOGM(ERR, "Invalid surface reference in placeBelow, likely parent"); + pushBelowIndex(-1); + parent->subsurfaces.emplace_back(self); + zIndex = -1; } else { - m_zIndex = (*it)->m_zIndex - 1; - m_parent->m_subsurfaces.emplace_back(m_self); + pushBelowIndex((*it)->zIndex); + zIndex = (*it)->zIndex; + parent->subsurfaces.emplace_back(self); } - m_parent->sortSubsurfaces(); + std::sort(parent->subsurfaces.begin(), parent->subsurfaces.end(), [](const auto& a, const auto& b) { return a->zIndex < b->zIndex; }); }); - m_listeners.commitSurface = m_surface->m_events.commit.listen([this] { - if (m_surface->m_current.texture && !m_surface->m_mapped) { - m_surface->map(); - m_surface->m_events.map.emit(); + listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { + if (surface->current.texture && !surface->mapped) { + surface->map(); + surface->events.map.emit(); return; } - if (!m_surface->m_current.texture && m_surface->m_mapped) { - m_surface->m_events.unmap.emit(); - m_surface->unmap(); + if (!surface->current.texture && surface->mapped) { + surface->events.unmap.emit(); + surface->unmap(); return; } }); } CWLSubsurfaceResource::~CWLSubsurfaceResource() { - m_events.destroy.emit(); - if (m_surface) - m_surface->resetRole(); + events.destroy.emit(); + if (surface) + surface->resetRole(); } void CWLSubsurfaceResource::destroy() { - if (m_surface && m_surface->m_mapped) { - m_surface->m_events.unmap.emit(); - m_surface->unmap(); - } - m_events.destroy.emit(); + if (surface && surface->mapped) + surface->unmap(); + events.destroy.emit(); PROTO::subcompositor->destroyResource(this); } Vector2D CWLSubsurfaceResource::posRelativeToParent() { - Vector2D pos = m_position; - SP surf = m_parent.lock(); + Vector2D pos = position; + SP surf = parent.lock(); // some apps might create cycles, which I believe _technically_ are not a protocol error // in some cases, notably firefox likes to do that, so we keep track of what // surfaces we've visited and if we hit a surface we've visited we bail out. std::vector> surfacesVisited; - while (surf->m_role->role() == SURFACE_ROLE_SUBSURFACE && std::ranges::find_if(surfacesVisited, [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { + while (surf->role->role() == SURFACE_ROLE_SUBSURFACE && + std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { surfacesVisited.emplace_back(surf); - auto subsurface = sc(m_parent->m_role.get())->m_subsurface.lock(); - pos += subsurface->m_position; - surf = subsurface->m_parent.lock(); + auto subsurface = ((CSubsurfaceRole*)parent->role.get())->subsurface.lock(); + pos += subsurface->position; + surf = subsurface->parent.lock(); } return pos; } bool CWLSubsurfaceResource::good() { - return m_resource->resource(); + return resource->resource(); } SP CWLSubsurfaceResource::t1Parent() { - SP surf = m_parent.lock(); + SP surf = parent.lock(); std::vector> surfacesVisited; - while (surf->m_role->role() == SURFACE_ROLE_SUBSURFACE && std::ranges::find_if(surfacesVisited, [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { + while (surf->role->role() == SURFACE_ROLE_SUBSURFACE && + std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { surfacesVisited.emplace_back(surf); - auto subsurface = sc(m_parent->m_role.get())->m_subsurface.lock(); - surf = subsurface->m_parent.lock(); + auto subsurface = ((CSubsurfaceRole*)parent->role.get())->subsurface.lock(); + surf = subsurface->parent.lock(); } return surf; } -CWLSubcompositorResource::CWLSubcompositorResource(SP resource_) : m_resource(resource_) { +CWLSubcompositorResource::CWLSubcompositorResource(SP resource_) : resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); }); - m_resource->setDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); }); + resource->setOnDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); }); + resource->setDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); }); - m_resource->setGetSubsurface([](CWlSubcompositor* r, uint32_t id, wl_resource* surface, wl_resource* parent) { + resource->setGetSubsurface([](CWlSubcompositor* r, uint32_t id, wl_resource* surface, wl_resource* parent) { auto SURF = CWLSurfaceResource::fromResource(surface); auto PARENT = CWLSurfaceResource::fromResource(parent); @@ -155,15 +157,15 @@ CWLSubcompositorResource::CWLSubcompositorResource(SP resource return; } - if UNLIKELY (SURF->m_role->role() != SURFACE_ROLE_UNASSIGNED) { + if UNLIKELY (SURF->role->role() != SURFACE_ROLE_UNASSIGNED) { r->error(-1, "Surface already has a different role"); return; } SP t1Parent = nullptr; - if (PARENT->m_role->role() == SURFACE_ROLE_SUBSURFACE) { - auto subsurface = sc(PARENT->m_role.get())->m_subsurface.lock(); + if (PARENT->role->role() == SURFACE_ROLE_SUBSURFACE) { + auto subsurface = ((CSubsurfaceRole*)PARENT->role.get())->subsurface.lock(); t1Parent = subsurface->t1Parent(); } else t1Parent = PARENT; @@ -174,26 +176,26 @@ CWLSubcompositorResource::CWLSubcompositorResource(SP resource } const auto RESOURCE = - PROTO::subcompositor->m_surfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF, PARENT)); + PROTO::subcompositor->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF, PARENT)); if UNLIKELY (!RESOURCE->good()) { r->noMemory(); - PROTO::subcompositor->m_surfaces.pop_back(); + PROTO::subcompositor->m_vSurfaces.pop_back(); return; } - RESOURCE->m_self = RESOURCE; - SURF->m_role = makeShared(RESOURCE); - PARENT->m_subsurfaces.emplace_back(RESOURCE); + RESOURCE->self = RESOURCE; + SURF->role = makeShared(RESOURCE); + PARENT->subsurfaces.emplace_back(RESOURCE); - LOGM(Log::DEBUG, "New wl_subsurface with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); + LOGM(LOG, "New wl_subsurface with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); - PARENT->m_events.newSubsurface.emit(RESOURCE); + PARENT->events.newSubsurface.emit(RESOURCE); }); } bool CWLSubcompositorResource::good() { - return m_resource->resource(); + return resource->resource(); } CWLSubcompositorProtocol::CWLSubcompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -201,23 +203,23 @@ CWLSubcompositorProtocol::CWLSubcompositorProtocol(const wl_interface* iface, co } void CWLSubcompositorProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { - const auto RESOURCE = m_managers.emplace_back(makeShared(makeShared(client, ver, id))); + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); if UNLIKELY (!RESOURCE->good()) { wl_client_post_no_memory(client); - m_managers.pop_back(); + m_vManagers.pop_back(); return; } } void CWLSubcompositorProtocol::destroyResource(CWLSubcompositorResource* resource) { - std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); } void CWLSubcompositorProtocol::destroyResource(CWLSubsurfaceResource* resource) { - std::erase_if(m_surfaces, [&](const auto& other) { return other.get() == resource; }); + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); } -CSubsurfaceRole::CSubsurfaceRole(SP sub) : m_subsurface(sub) { +CSubsurfaceRole::CSubsurfaceRole(SP sub) : subsurface(sub) { ; } diff --git a/src/protocols/core/Subcompositor.hpp b/src/protocols/core/Subcompositor.hpp index 45b84eba..dc713044 100644 --- a/src/protocols/core/Subcompositor.hpp +++ b/src/protocols/core/Subcompositor.hpp @@ -25,7 +25,7 @@ class CSubsurfaceRole : public ISurfaceRole { return SURFACE_ROLE_SUBSURFACE; } - WP m_subsurface; + WP subsurface; }; class CWLSubsurfaceResource { @@ -37,28 +37,28 @@ class CWLSubsurfaceResource { bool good(); SP t1Parent(); - bool m_sync = false; - Vector2D m_position; + bool sync = false; + Vector2D position; - WP m_surface; - WP m_parent; + WP surface; + WP parent; - WP m_self; + WP self; - int m_zIndex = 1; // by default, it's above + int zIndex = 1; // by default, it's above struct { - CSignalT<> destroy; - } m_events; + CSignal destroy; + } events; private: - SP m_resource; + SP resource; void destroy(); struct { CHyprSignalListener commitSurface; - } m_listeners; + } listeners; }; class CWLSubcompositorResource { @@ -68,7 +68,7 @@ class CWLSubcompositorResource { bool good(); private: - SP m_resource; + SP resource; }; class CWLSubcompositorProtocol : public IWaylandProtocol { @@ -82,8 +82,8 @@ class CWLSubcompositorProtocol : public IWaylandProtocol { void destroyResource(CWLSubsurfaceResource* resource); // - std::vector> m_managers; - std::vector> m_surfaces; + std::vector> m_vManagers; + std::vector> m_vSurfaces; friend class CWLSubcompositorResource; friend class CWLSubsurfaceResource; diff --git a/src/protocols/types/Buffer.cpp b/src/protocols/types/Buffer.cpp index 93bd5d20..2623435b 100644 --- a/src/protocols/types/Buffer.cpp +++ b/src/protocols/types/Buffer.cpp @@ -1,126 +1,52 @@ #include "Buffer.hpp" IHLBuffer::~IHLBuffer() { - if (locked() && m_resource) + if (locked() && resource) sendRelease(); } void IHLBuffer::sendRelease() { - m_resource->sendRelease(); - m_syncReleasers.clear(); + resource->sendRelease(); } void IHLBuffer::lock() { - m_locks++; + nLocks++; } void IHLBuffer::unlock() { - m_locks--; + nLocks--; - ASSERT(m_locks >= 0); + ASSERT(nLocks >= 0); - if (m_locks == 0) + if (nLocks == 0) { sendRelease(); + syncReleaser.reset(); + } } bool IHLBuffer::locked() { - return m_locks > 0; + return nLocks > 0; } void IHLBuffer::onBackendRelease(const std::function& fn) { - if (m_hlEvents.backendRelease) { - if (m_backendReleaseQueuedFn) - m_backendReleaseQueuedFn(); - Log::logger->log(Log::DEBUG, "backendRelease emitted early"); + if (hlEvents.backendRelease) { + hlEvents.backendRelease->emit(nullptr); + Debug::log(LOG, "backendRelease emitted early"); } - m_backendReleaseQueuedFn = fn; - - m_hlEvents.backendRelease = events.backendRelease.listen([this] { - if (m_backendReleaseQueuedFn) - m_backendReleaseQueuedFn(); - m_backendReleaseQueuedFn = nullptr; - m_hlEvents.backendRelease.reset(); + hlEvents.backendRelease = events.backendRelease.registerListener([this, fn](std::any) { + fn(); + hlEvents.backendRelease.reset(); }); } -void IHLBuffer::addReleasePoint(CDRMSyncPointState& point) { - ASSERT(locked()); - if (point) - m_syncReleasers.emplace_back(point.createSyncRelease()); -} - -CHLBufferReference::CHLBufferReference() : m_buffer(nullptr) { - ; -} - -CHLBufferReference::CHLBufferReference(const CHLBufferReference& other) : m_buffer(other.m_buffer) { - if (m_buffer) - m_buffer->lock(); -} - -CHLBufferReference::CHLBufferReference(CHLBufferReference&& other) noexcept : m_buffer(std::move(other.m_buffer)) { - ; -} - -CHLBufferReference::CHLBufferReference(SP buffer_) : m_buffer(buffer_) { - if (m_buffer) - m_buffer->lock(); +CHLBufferReference::CHLBufferReference(SP buffer_, SP surface_) : buffer(buffer_), surface(surface_) { + buffer->lock(); } CHLBufferReference::~CHLBufferReference() { - if (m_buffer) - m_buffer->unlock(); -} - -CHLBufferReference& CHLBufferReference::operator=(const CHLBufferReference& other) { - if (m_buffer == other.m_buffer) - return *this; // same buffer, do nothing - - if (other.m_buffer) - other.m_buffer->lock(); - if (m_buffer) - m_buffer->unlock(); - m_buffer = other.m_buffer; - return *this; -} - -CHLBufferReference& CHLBufferReference::operator=(CHLBufferReference&& other) { - if (this != &other) { - if (m_buffer) - m_buffer->unlock(); - m_buffer = other.m_buffer; - other.m_buffer = nullptr; - } - return *this; -} - -bool CHLBufferReference::operator==(const CHLBufferReference& other) const { - return m_buffer == other.m_buffer; -} - -bool CHLBufferReference::operator==(const SP& other) const { - return m_buffer == other; -} - -bool CHLBufferReference::operator==(const SP& other) const { - return m_buffer == other; -} - -SP CHLBufferReference::operator->() const { - return m_buffer; -} - -CHLBufferReference::operator bool() const { - return m_buffer; -} - -void CHLBufferReference::drop() { - if (!m_buffer) + if (!buffer) return; - m_buffer->m_locks--; - ASSERT(m_buffer->m_locks >= 0); - - m_buffer = nullptr; + buffer->unlock(); } diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp index afff11a5..a0b4e9a8 100644 --- a/src/protocols/types/Buffer.hpp +++ b/src/protocols/types/Buffer.hpp @@ -8,7 +8,6 @@ #include class CSyncReleaser; -class CHLBufferReference; class IHLBuffer : public Aquamarine::IBuffer { public: @@ -24,47 +23,32 @@ class IHLBuffer : public Aquamarine::IBuffer { virtual bool locked(); void onBackendRelease(const std::function& fn); - void addReleasePoint(CDRMSyncPointState& point); - SP m_texture; - bool m_opaque = false; - SP m_resource; - std::vector> m_syncReleasers; - Hyprutils::OS::CFileDescriptor m_syncFd; + SP texture; + bool opaque = false; + SP resource; + UP syncReleaser; struct { CHyprSignalListener backendRelease; CHyprSignalListener backendRelease2; // for explicit ds - } m_hlEvents; + } hlEvents; private: - int m_locks = 0; - std::function m_backendReleaseQueuedFn; - - friend class CHLBufferReference; + int nLocks = 0; }; // for ref-counting. Releases in ~dtor +// surface optional class CHLBufferReference { public: - CHLBufferReference(); - CHLBufferReference(const CHLBufferReference& other); - CHLBufferReference(CHLBufferReference&& other) noexcept; - CHLBufferReference(SP buffer); + CHLBufferReference(SP buffer, SP surface); ~CHLBufferReference(); - CHLBufferReference& operator=(const CHLBufferReference& other); - CHLBufferReference& operator=(CHLBufferReference&& other); + SP buffer; + UP acquire; + UP release; - bool operator==(const CHLBufferReference& other) const; - bool operator==(const SP& other) const; - bool operator==(const SP& other) const; - SP operator->() const; - // - operator bool() const; - - // unlock and drop the buffer without sending release - void drop(); - - SP m_buffer; + private: + WP surface; }; diff --git a/src/protocols/types/ColorManagement.cpp b/src/protocols/types/ColorManagement.cpp new file mode 100644 index 00000000..9daff1e2 --- /dev/null +++ b/src/protocols/types/ColorManagement.cpp @@ -0,0 +1,20 @@ +#include "ColorManagement.hpp" + +namespace NColorManagement { + const SPCPRimaries& getPrimaries(ePrimaries name) { + switch (name) { + case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709; + case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020; + case CM_PRIMARIES_PAL_M: return NColorPrimaries::PAL_M; + case CM_PRIMARIES_PAL: return NColorPrimaries::PAL; + case CM_PRIMARIES_NTSC: return NColorPrimaries::NTSC; + case CM_PRIMARIES_GENERIC_FILM: return NColorPrimaries::GENERIC_FILM; + case CM_PRIMARIES_CIE1931_XYZ: return NColorPrimaries::DEFAULT_PRIMARIES; // FIXME + case CM_PRIMARIES_DCI_P3: return NColorPrimaries::DCI_P3; + case CM_PRIMARIES_DISPLAY_P3: return NColorPrimaries::DISPLAY_P3; + case CM_PRIMARIES_ADOBE_RGB: return NColorPrimaries::ADOBE_RGB; + default: return NColorPrimaries::DEFAULT_PRIMARIES; + } + } + +} \ No newline at end of file diff --git a/src/protocols/types/ColorManagement.hpp b/src/protocols/types/ColorManagement.hpp new file mode 100644 index 00000000..143f47ca --- /dev/null +++ b/src/protocols/types/ColorManagement.hpp @@ -0,0 +1,189 @@ +#pragma once + +#include "color-management-v1.hpp" + +namespace NColorManagement { + enum ePrimaries : uint8_t { + CM_PRIMARIES_SRGB = 1, + CM_PRIMARIES_PAL_M = 2, + CM_PRIMARIES_PAL = 3, + CM_PRIMARIES_NTSC = 4, + CM_PRIMARIES_GENERIC_FILM = 5, + CM_PRIMARIES_BT2020 = 6, + CM_PRIMARIES_CIE1931_XYZ = 7, + CM_PRIMARIES_DCI_P3 = 8, + CM_PRIMARIES_DISPLAY_P3 = 9, + CM_PRIMARIES_ADOBE_RGB = 10, + }; + + enum eTransferFunction : uint8_t { + CM_TRANSFER_FUNCTION_BT1886 = 1, + CM_TRANSFER_FUNCTION_GAMMA22 = 2, + CM_TRANSFER_FUNCTION_GAMMA28 = 3, + CM_TRANSFER_FUNCTION_ST240 = 4, + CM_TRANSFER_FUNCTION_EXT_LINEAR = 5, + CM_TRANSFER_FUNCTION_LOG_100 = 6, + CM_TRANSFER_FUNCTION_LOG_316 = 7, + CM_TRANSFER_FUNCTION_XVYCC = 8, + CM_TRANSFER_FUNCTION_SRGB = 9, + CM_TRANSFER_FUNCTION_EXT_SRGB = 10, + CM_TRANSFER_FUNCTION_ST2084_PQ = 11, + CM_TRANSFER_FUNCTION_ST428 = 12, + CM_TRANSFER_FUNCTION_HLG = 13, + }; + + // NOTE should be ok this way. unsupported primaries/tfs must be rejected earlier. supported enum values should be in sync with proto. + // might need a proper switch-case and additional INVALID enum value. + inline wpColorManagerV1Primaries convertPrimaries(ePrimaries primaries) { + return (wpColorManagerV1Primaries)primaries; + } + inline ePrimaries convertPrimaries(wpColorManagerV1Primaries primaries) { + return (ePrimaries)primaries; + } + inline wpColorManagerV1TransferFunction convertTransferFunction(eTransferFunction tf) { + return (wpColorManagerV1TransferFunction)tf; + } + inline eTransferFunction convertTransferFunction(wpColorManagerV1TransferFunction tf) { + return (eTransferFunction)tf; + } + + struct SPCPRimaries { + struct xy { //NOLINT(readability-identifier-naming) + float x = 0; + float y = 0; + + bool operator==(const xy& p2) const { + return x == p2.x && y == p2.y; + } + } red, green, blue, white; + bool operator==(const SPCPRimaries& p2) const { + return red == p2.red && green == p2.green && blue == p2.blue && white == p2.white; + } + }; + + namespace NColorPrimaries { + static const auto DEFAULT_PRIMARIES = SPCPRimaries{}; + + static const auto BT709 = SPCPRimaries{ + .red = {.x = 0.64, .y = 0.33}, + .green = {.x = 0.30, .y = 0.60}, + .blue = {.x = 0.15, .y = 0.06}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + static const auto PAL_M = SPCPRimaries{ + .red = {.x = 0.67, .y = 0.33}, + .green = {.x = 0.21, .y = 0.71}, + .blue = {.x = 0.14, .y = 0.08}, + .white = {.x = 0.310, .y = 0.316}, + }; + static const auto PAL = SPCPRimaries{ + .red = {.x = 0.640, .y = 0.330}, + .green = {.x = 0.290, .y = 0.600}, + .blue = {.x = 0.150, .y = 0.060}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + static const auto NTSC = SPCPRimaries{ + .red = {.x = 0.630, .y = 0.340}, + .green = {.x = 0.310, .y = 0.595}, + .blue = {.x = 0.155, .y = 0.070}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + static const auto GENERIC_FILM = SPCPRimaries{ + .red = {.x = 0.243, .y = 0.692}, + .green = {.x = 0.145, .y = 0.049}, + .blue = {.x = 0.681, .y = 0.319}, // NOLINT(modernize-use-std-numbers) + .white = {.x = 0.310, .y = 0.316}, + }; + static const auto BT2020 = SPCPRimaries{ + .red = {.x = 0.708, .y = 0.292}, + .green = {.x = 0.170, .y = 0.797}, + .blue = {.x = 0.131, .y = 0.046}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + + // FIXME CIE1931_XYZ + + static const auto DCI_P3 = SPCPRimaries{ + .red = {.x = 0.680, .y = 0.320}, + .green = {.x = 0.265, .y = 0.690}, + .blue = {.x = 0.150, .y = 0.060}, + .white = {.x = 0.314, .y = 0.351}, + }; + + static const auto DISPLAY_P3 = SPCPRimaries{ + .red = {.x = 0.680, .y = 0.320}, + .green = {.x = 0.265, .y = 0.690}, + .blue = {.x = 0.150, .y = 0.060}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + static const auto ADOBE_RGB = SPCPRimaries{ + .red = {.x = 0.6400, .y = 0.3300}, + .green = {.x = 0.2100, .y = 0.7100}, + .blue = {.x = 0.1500, .y = 0.0600}, + .white = {.x = 0.3127, .y = 0.3290}, + }; + } + + const SPCPRimaries& getPrimaries(ePrimaries name); + + struct SImageDescription { + uint32_t id = 0; // FIXME needs id setting + + struct SIccFile { + int fd = -1; + uint32_t length = 0; + uint32_t offset = 0; + bool operator==(const SIccFile& i2) const { + return fd == i2.fd; + } + } icc; + + bool windowsScRGB = false; + + eTransferFunction transferFunction = CM_TRANSFER_FUNCTION_SRGB; + float transferFunctionPower = 1.0f; + + bool primariesNameSet = false; + ePrimaries primariesNamed = CM_PRIMARIES_SRGB; + // primaries are stored as FP values with the same scale as standard defines (0.0 - 1.0) + // wayland protocol expects int32_t values multiplied by 1000000 + // xx protocol expects int32_t values multiplied by 10000 + // drm expects uint16_t values multiplied by 50000 + // frog protocol expects drm values + SPCPRimaries primaries, masteringPrimaries; + + // luminances in cd/m² + // protos and drm expect min * 10000 + struct SPCLuminances { + float min = 0.2; // 0.2 cd/m² + uint32_t max = 80; // 80 cd/m² + uint32_t reference = 80; // 80 cd/m² + bool operator==(const SPCLuminances& l2) const { + return min == l2.min && max == l2.max && reference == l2.reference; + } + } luminances; + struct SPCMasteringLuminances { + float min = 0; + uint32_t max = 0; + bool operator==(const SPCMasteringLuminances& l2) const { + return min == l2.min && max == l2.max; + } + } masteringLuminances; + + uint32_t maxCLL = 0; + uint32_t maxFALL = 0; + + bool operator==(const SImageDescription& d2) const { + return (id != 0 && id == d2.id) || + (icc == d2.icc && windowsScRGB == d2.windowsScRGB && transferFunction == d2.transferFunction && transferFunctionPower == d2.transferFunctionPower && + ((primariesNameSet && primariesNamed == d2.primariesNameSet) || (primaries == d2.primaries)) && masteringPrimaries == d2.masteringPrimaries && + luminances == d2.luminances && masteringLuminances == d2.masteringLuminances && maxCLL == d2.maxCLL && maxFALL == d2.maxFALL); + } + + const SPCPRimaries& getPrimaries() const { + if (primariesNameSet || primaries == SPCPRimaries{}) + return NColorManagement::getPrimaries(primariesNamed); + return primaries; + } + }; +} \ No newline at end of file diff --git a/src/protocols/types/ContentType.cpp b/src/protocols/types/ContentType.cpp index b5b0041c..c0a3d30f 100644 --- a/src/protocols/types/ContentType.cpp +++ b/src/protocols/types/ContentType.cpp @@ -12,15 +12,7 @@ namespace NContentType { if (it != table.end()) return it->second; else - return CONTENT_TYPE_NONE; - } - - std::string toString(eContentType type) { - for (const auto& [k, v] : table) { - if (v == type) - return k; - } - return ""; + throw std::invalid_argument(std::format("Unknown content type {}", name)); } eContentType fromWP(wpContentTypeV1Type contentType) { diff --git a/src/protocols/types/ContentType.hpp b/src/protocols/types/ContentType.hpp index 68bc7a41..66fcbca7 100644 --- a/src/protocols/types/ContentType.hpp +++ b/src/protocols/types/ContentType.hpp @@ -13,7 +13,6 @@ namespace NContentType { }; eContentType fromString(const std::string name); - std::string toString(eContentType); eContentType fromWP(wpContentTypeV1Type contentType); uint16_t toDRM(eContentType contentType); } \ No newline at end of file diff --git a/src/protocols/types/DMABuffer.cpp b/src/protocols/types/DMABuffer.cpp index 86db8ca6..2e39bf79 100644 --- a/src/protocols/types/DMABuffer.cpp +++ b/src/protocols/types/DMABuffer.cpp @@ -1,48 +1,43 @@ #include "DMABuffer.hpp" #include "WLBuffer.hpp" -#include "../../desktop/view/LayerSurface.hpp" +#include "../../desktop/LayerSurface.hpp" #include "../../render/Renderer.hpp" #include "../../helpers/Format.hpp" -#if defined(__linux__) -#include -#include -#endif -#include +CDMABuffer::CDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs const& attrs_) : attrs(attrs_) { + g_pHyprRenderer->makeEGLCurrent(); -using namespace Hyprutils::OS; - -CDMABuffer::CDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs const& attrs_) : m_attrs(attrs_) { - m_listeners.resourceDestroy = events.destroy.listen([this] { + listeners.resourceDestroy = events.destroy.registerListener([this](std::any d) { closeFDs(); - m_listeners.resourceDestroy.reset(); + listeners.resourceDestroy.reset(); }); - size = m_attrs.size; - m_resource = CWLBufferResource::create(makeShared(client, 1, id)); - m_opaque = NFormatUtils::isFormatOpaque(m_attrs.format); - m_texture = g_pHyprRenderer->createTexture(m_attrs, m_opaque); // texture takes ownership of the eglImage + size = attrs.size; + resource = CWLBufferResource::create(makeShared(client, 1, id)); - if UNLIKELY (!m_texture) { - Log::logger->log(Log::ERR, "CDMABuffer: failed to import EGLImage, retrying as implicit"); - m_attrs.modifier = DRM_FORMAT_MOD_INVALID; - m_texture = g_pHyprRenderer->createTexture(m_attrs, m_opaque); + auto eglImage = g_pHyprOpenGL->createEGLImage(attrs); - if UNLIKELY (!m_texture) { - Log::logger->log(Log::ERR, "CDMABuffer: failed to import EGLImage"); + if UNLIKELY (!eglImage) { + Debug::log(ERR, "CDMABuffer: failed to import EGLImage, retrying as implicit"); + attrs.modifier = DRM_FORMAT_MOD_INVALID; + eglImage = g_pHyprOpenGL->createEGLImage(attrs); + if UNLIKELY (!eglImage) { + Debug::log(ERR, "CDMABuffer: failed to import EGLImage"); return; } } - m_success = m_texture->ok(); + texture = makeShared(attrs, eglImage); // texture takes ownership of the eglImage + opaque = NFormatUtils::isFormatOpaque(attrs.format); + success = texture->m_iTexID; - if UNLIKELY (!m_success) - Log::logger->log(Log::ERR, "Failed to create a dmabuf: texture is null"); + if UNLIKELY (!success) + Debug::log(ERR, "Failed to create a dmabuf: texture is null"); } CDMABuffer::~CDMABuffer() { - if (m_resource) - m_resource->sendRelease(); + if (resource) + resource->sendRelease(); closeFDs(); } @@ -64,7 +59,7 @@ bool CDMABuffer::isSynchronous() { } Aquamarine::SDMABUFAttrs CDMABuffer::dmabuf() { - return m_attrs; + return attrs; } std::tuple CDMABuffer::beginDataPtr(uint32_t flags) { @@ -77,85 +72,15 @@ void CDMABuffer::endDataPtr() { } bool CDMABuffer::good() { - return m_success; + return success; } void CDMABuffer::closeFDs() { - for (int i = 0; i < m_attrs.planes; ++i) { - if (m_attrs.fds[i] == -1) + for (int i = 0; i < attrs.planes; ++i) { + if (attrs.fds[i] == -1) continue; - close(m_attrs.fds[i]); - m_attrs.fds[i] = -1; + close(attrs.fds[i]); + attrs.fds[i] = -1; } - m_attrs.planes = 0; -} - -static int doIoctl(int fd, unsigned long request, void* arg) { - int ret; - - do { - ret = ioctl(fd, request, arg); - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); - return ret; -} - -// https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html#c.dma_buf_export_sync_file -// returns a sync file that will be signalled when dmabuf is ready to be read -CFileDescriptor CDMABuffer::exportSyncFile() { - if (!good()) - return {}; - -#if !defined(__linux__) - return {}; -#else - std::vector syncFds; - syncFds.reserve(m_attrs.fds.size()); - - for (const auto& fd : m_attrs.fds) { - if (fd == -1) - continue; - - // buffer readability checks are rather slow on some Intel laptops - // see https://gitlab.freedesktop.org/drm/intel/-/issues/9415 - if (g_pHyprRenderer && !g_pHyprRenderer->isIntel()) { - if (CFileDescriptor::isReadable(fd)) - continue; - } - - dma_buf_export_sync_file request{ - .flags = DMA_BUF_SYNC_READ, - .fd = -1, - }; - - if (doIoctl(fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &request) == 0) - syncFds.emplace_back(request.fd); - } - - if (syncFds.empty()) - return {}; - - CFileDescriptor syncFd; - for (auto& fd : syncFds) { - if (!syncFd.isValid()) { - syncFd = std::move(fd); - continue; - } - - const std::string name = "merged release fence"; - struct sync_merge_data data{ - .name = {}, // zero-initialize name[] - .fd2 = fd.get(), - .fence = -1, - }; - - std::ranges::copy_n(name.c_str(), std::min(name.size() + 1, sizeof(data.name)), data.name); - - if (doIoctl(syncFd.get(), SYNC_IOC_MERGE, &data) == 0) - syncFd = CFileDescriptor(data.fence); - else - syncFd = {}; - } - - return syncFd; -#endif + attrs.planes = 0; } diff --git a/src/protocols/types/DMABuffer.hpp b/src/protocols/types/DMABuffer.hpp index 5a0064da..40c935c5 100644 --- a/src/protocols/types/DMABuffer.hpp +++ b/src/protocols/types/DMABuffer.hpp @@ -1,7 +1,6 @@ #pragma once #include "Buffer.hpp" -#include class CDMABuffer : public IHLBuffer { public: @@ -17,13 +16,13 @@ class CDMABuffer : public IHLBuffer { virtual void endDataPtr(); bool good(); void closeFDs(); - Hyprutils::OS::CFileDescriptor exportSyncFile(); - bool m_success = false; + + bool success = false; private: - Aquamarine::SDMABUFAttrs m_attrs; + Aquamarine::SDMABUFAttrs attrs; struct { CHyprSignalListener resourceDestroy; - } m_listeners; + } listeners; }; diff --git a/src/protocols/types/DataDevice.cpp b/src/protocols/types/DataDevice.cpp index 8c94b5b1..fef11b64 100644 --- a/src/protocols/types/DataDevice.cpp +++ b/src/protocols/types/DataDevice.cpp @@ -9,11 +9,11 @@ bool IDataSource::dndDone() { } bool IDataSource::used() { - return m_wasUsed; + return wasUsed; } void IDataSource::markUsed() { - m_wasUsed = true; + wasUsed = true; } eDataSourceType IDataSource::type() { diff --git a/src/protocols/types/DataDevice.hpp b/src/protocols/types/DataDevice.hpp index 9a9579d3..cbb4b271 100644 --- a/src/protocols/types/DataDevice.hpp +++ b/src/protocols/types/DataDevice.hpp @@ -41,11 +41,11 @@ class IDataSource { virtual void sendDndAction(wl_data_device_manager_dnd_action a); struct { - CSignalT<> destroy; - } m_events; + CSignal destroy; + } events; private: - bool m_wasUsed = false; + bool wasUsed = false; }; class IDataOffer { diff --git a/src/protocols/types/SurfaceState.cpp b/src/protocols/types/SurfaceState.cpp index da98d3fb..6f9636ff 100644 --- a/src/protocols/types/SurfaceState.cpp +++ b/src/protocols/types/SurfaceState.cpp @@ -1,7 +1,6 @@ #include "SurfaceState.hpp" #include "helpers/Format.hpp" #include "protocols/types/Buffer.hpp" -#include "render/Renderer.hpp" #include "render/Texture.hpp" Vector2D SSurfaceState::sourceSize() { @@ -30,97 +29,30 @@ CRegion SSurfaceState::accumulateBufferDamage() { Vector2D trc = transform % 2 == 1 ? Vector2D{bufferSize.y, bufferSize.x} : bufferSize; - bufferDamage = surfaceDamage.scale(scale).transform(Math::wlTransformToHyprutils(Math::invertTransform(transform)), trc.x, trc.y).add(bufferDamage); + bufferDamage = surfaceDamage.scale(scale).transform(wlTransformToHyprutils(invertTransform(transform)), trc.x, trc.y).add(bufferDamage); damage.clear(); return bufferDamage; } -void SSurfaceState::updateSynchronousTexture(SP lastTexture) { - auto [dataPtr, fmt, size] = buffer->beginDataPtr(0); +void SSurfaceState::updateSynchronousTexture(SP lastTexture) { + auto [dataPtr, fmt, size] = buffer->buffer->beginDataPtr(0); if (dataPtr) { auto drmFmt = NFormatUtils::shmToDRM(fmt); auto stride = bufferSize.y ? size / bufferSize.y : 0; - if (lastTexture && lastTexture->m_isSynchronous && lastTexture->m_size == bufferSize) { + if (lastTexture && lastTexture->m_isSynchronous && lastTexture->m_vSize == bufferSize) { texture = lastTexture; texture->update(drmFmt, dataPtr, stride, accumulateBufferDamage()); } else - texture = g_pHyprRenderer->createTexture(drmFmt, dataPtr, stride, bufferSize); + texture = makeShared(drmFmt, dataPtr, stride, bufferSize); } - buffer->endDataPtr(); + buffer->buffer->endDataPtr(); } void SSurfaceState::reset() { - updated.all = false; - - // After commit, there is no pending buffer until the next attach. - buffer = {}; - - // applies only to the buffer that is attached to the surface - acquire = {}; - - // wl_surface.commit assigns pending ... and clears pending damage. damage.clear(); bufferDamage.clear(); - - callbacks.clear(); - lockMask = LOCK_REASON_NONE; - - barrierSet = false; - surfaceLocked = false; - fifoScheduled = false; - - pendingTimeout.reset(); - timer.reset(); // CEventLoopManager::nudgeTimers should handle it eventually -} - -void SSurfaceState::updateFrom(SSurfaceState& ref) { - updated = ref.updated; - - if (ref.updated.bits.buffer) { - buffer = ref.buffer; - texture = ref.texture; - size = ref.size; - bufferSize = ref.bufferSize; - } - - if (ref.updated.bits.damage) { - damage = ref.damage; - bufferDamage = ref.bufferDamage; - } else { - // damage is always relative to the current commit - damage.clear(); - bufferDamage.clear(); - } - - if (ref.updated.bits.input) - input = ref.input; - - if (ref.updated.bits.opaque) - opaque = ref.opaque; - - if (ref.updated.bits.offset) - offset = ref.offset; - - if (ref.updated.bits.scale) - scale = ref.scale; - - if (ref.updated.bits.transform) - transform = ref.transform; - - if (ref.updated.bits.viewport) - viewport = ref.viewport; - - if (ref.updated.bits.acquire) - acquire = ref.acquire; - - if (ref.updated.bits.acked) - ackedSize = ref.ackedSize; - - if (ref.updated.bits.frame) { - callbacks.insert(callbacks.end(), std::make_move_iterator(ref.callbacks.begin()), std::make_move_iterator(ref.callbacks.end())); - ref.callbacks.clear(); - } - - if (ref.barrierSet) - barrierSet = ref.barrierSet; + transform = WL_OUTPUT_TRANSFORM_NORMAL; + scale = 1; + offset = {}; + size = {}; } diff --git a/src/protocols/types/SurfaceState.hpp b/src/protocols/types/SurfaceState.hpp index d5b7e4b9..b5093759 100644 --- a/src/protocols/types/SurfaceState.hpp +++ b/src/protocols/types/SurfaceState.hpp @@ -1,107 +1,31 @@ #pragma once #include "../../helpers/math/Math.hpp" -#include "../../helpers/time/Time.hpp" -#include "../../managers/eventLoop/EventLoopTimer.hpp" #include "../WaylandProtocol.hpp" -#include "./Buffer.hpp" -class ITexture; -class CDRMSyncPointState; -class CWLCallbackResource; - -enum eLockReason : uint8_t { - LOCK_REASON_NONE = 0, - LOCK_REASON_FENCE = 1 << 0, - LOCK_REASON_FIFO = 1 << 1, - LOCK_REASON_TIMER = 1 << 2 -}; - -inline eLockReason operator|(eLockReason a, eLockReason b) { - return sc(sc(a) | sc(b)); -} -inline eLockReason operator&(eLockReason a, eLockReason b) { - return sc(sc(a) & sc(b)); -} -inline eLockReason& operator|=(eLockReason& a, eLockReason b) { - a = a | b; - return a; -} -inline eLockReason& operator&=(eLockReason& a, eLockReason b) { - a = a & b; - return a; -} -inline eLockReason operator~(eLockReason a) { - return sc(~sc(a)); -} +class CHLBufferReference; +class CTexture; struct SSurfaceState { - union { - uint16_t all = 0; - struct { - bool buffer : 1; - bool damage : 1; - bool opaque : 1; - bool input : 1; - bool transform : 1; - bool scale : 1; - bool offset : 1; - bool viewport : 1; - bool acquire : 1; - bool acked : 1; - bool frame : 1; - bool fifo : 1; - } bits; - } updated; - - bool rejected = false; - - // initial values, copied from protocol text - CHLBufferReference buffer = {}; // The initial surface contents are void - CRegion damage, bufferDamage; // The initial value for pending damage is empty - CRegion opaque; // The initial value for an opaque region is empty - CRegion input = CBox{{}, {INT32_MAX - 1, INT32_MAX - 1}}; // The initial value for an input region is infinite - wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; // A newly created surface has its buffer transformation set to normal - int scale = 1; // A newly created surface has its buffer scale set to 1 - - // these don't have well defined initial values in the protocol, but these work - Vector2D size, bufferSize; - Vector2D offset; - - // for xdg_shell resizing - Vector2D ackedSize; - - // for wl_surface::frame callbacks. - std::vector> callbacks; - - // viewporter protocol surface state + CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + int scale = 1; + SP buffer; // buffer ref will be released once the buffer is no longer locked. For checking if a buffer is attached to this state, check texture. + SP texture; + Vector2D offset; + Vector2D size, bufferSize; struct { bool hasDestination = false; bool hasSource = false; Vector2D destination; CBox source; } viewport; + bool rejected = false; + bool newBuffer = false; + Vector2D sourceSize(); - - // drm syncobj protocol surface state - CDRMSyncPointState acquire; - eLockReason lockMask = LOCK_REASON_NONE; - - // texture of surface content, used for rendering - SP texture; - void updateSynchronousTexture(SP lastTexture); - - // fifo - bool barrierSet = false; - bool surfaceLocked = false; - bool fifoScheduled = false; - - // commit timing - std::optional pendingTimeout; - SP timer; - - // helpers - CRegion accumulateBufferDamage(); // transforms state.damage and merges it into state.bufferDamage - void updateFrom(SSurfaceState& ref); // updates this state based on a reference state. - void reset(); // resets pending state after commit + // Translates damage into bufferDamage, clearing damage and returning the updated bufferDamage + CRegion accumulateBufferDamage(); + void updateSynchronousTexture(SP lastTexture); + void reset(); }; diff --git a/src/protocols/types/SurfaceStateQueue.cpp b/src/protocols/types/SurfaceStateQueue.cpp deleted file mode 100644 index 82a04878..00000000 --- a/src/protocols/types/SurfaceStateQueue.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "SurfaceStateQueue.hpp" -#include "../core/Compositor.hpp" -#include "SurfaceState.hpp" - -CSurfaceStateQueue::CSurfaceStateQueue(WP surf) : m_surface(std::move(surf)) {} - -void CSurfaceStateQueue::clear() { - m_queue.clear(); -} - -WP CSurfaceStateQueue::enqueue(UP&& state) { - return m_queue.emplace_back(std::move(state)); -} - -void CSurfaceStateQueue::dropState(const WP& state) { - auto it = find(state); - if (it == m_queue.end()) - return; - - m_queue.erase(it); -} - -void CSurfaceStateQueue::lock(const WP& weakState, eLockReason reason) { - ASSERT(reason != LOCK_REASON_NONE); - auto it = find(weakState); - if (it == m_queue.end()) - return; - - it->get()->lockMask |= reason; -} - -void CSurfaceStateQueue::unlock(const WP& state, eLockReason reason) { - ASSERT(reason != LOCK_REASON_NONE); - auto it = find(state); - if (it == m_queue.end()) - return; - - it->get()->lockMask &= ~reason; - tryProcess(); -} - -void CSurfaceStateQueue::unlockFirst(eLockReason reason) { - ASSERT(reason != LOCK_REASON_NONE); - for (auto& it : m_queue) { - if ((it->lockMask & reason) != LOCK_REASON_NONE) { - it->lockMask &= ~reason; - break; - } - } - - tryProcess(); -} - -auto CSurfaceStateQueue::find(const WP& state) -> std::deque>::iterator { - if (state.expired()) - return m_queue.end(); - - auto* raw = state.get(); // get raw pointer - - for (auto it = m_queue.begin(); it != m_queue.end(); ++it) { - if (it->get() == raw) - return it; - } - - return m_queue.end(); -} - -void CSurfaceStateQueue::tryProcess() { - while (!m_queue.empty()) { - auto& front = m_queue.front(); - if (front->lockMask & LOCK_REASON_FIFO && !m_surface->m_current.barrierSet) - front->lockMask &= ~LOCK_REASON_FIFO; - - if (front->lockMask != LOCK_REASON_NONE) - return; - - m_surface->commitState(*front); - m_queue.pop_front(); - } -} diff --git a/src/protocols/types/SurfaceStateQueue.hpp b/src/protocols/types/SurfaceStateQueue.hpp deleted file mode 100644 index 8d9db7a5..00000000 --- a/src/protocols/types/SurfaceStateQueue.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "../../helpers/memory/Memory.hpp" -#include "SurfaceState.hpp" -#include - -class CWLSurfaceResource; - -class CSurfaceStateQueue { - public: - CSurfaceStateQueue() = default; - explicit CSurfaceStateQueue(WP surf); - - void clear(); - WP enqueue(UP&& state); - void dropState(const WP& state); - void lock(const WP& state, eLockReason reason); - void unlock(const WP& state, eLockReason reason); - void unlockFirst(eLockReason reason); - void tryProcess(); - - private: - std::deque> m_queue; - WP m_surface; - - typename std::deque>::iterator find(const WP& state); -}; diff --git a/src/protocols/types/WLBuffer.cpp b/src/protocols/types/WLBuffer.cpp index 8b8e730e..f2b81c00 100644 --- a/src/protocols/types/WLBuffer.cpp +++ b/src/protocols/types/WLBuffer.cpp @@ -5,43 +5,43 @@ #include "../../helpers/sync/SyncTimeline.hpp" #include -CWLBufferResource::CWLBufferResource(WP resource_) : m_resource(resource_.lock()) { +CWLBufferResource::CWLBufferResource(WP resource_) : resource(resource_.lock()) { if UNLIKELY (!good()) return; - m_resource->setOnDestroy([this](CWlBuffer* r) { - if (m_buffer.expired()) + resource->setOnDestroy([this](CWlBuffer* r) { + if (buffer.expired()) return; - m_buffer->events.destroy.emit(); + buffer->events.destroy.emit(); }); - m_resource->setDestroy([this](CWlBuffer* r) { - if (m_buffer.expired()) + resource->setDestroy([this](CWlBuffer* r) { + if (buffer.expired()) return; - m_buffer->events.destroy.emit(); + buffer->events.destroy.emit(); }); - m_resource->setData(this); + resource->setData(this); } bool CWLBufferResource::good() { - return m_resource->resource(); + return resource->resource(); } void CWLBufferResource::sendRelease() { - m_resource->sendRelease(); + resource->sendRelease(); } wl_resource* CWLBufferResource::getResource() { - return m_resource->resource(); + return resource->resource(); } SP CWLBufferResource::fromResource(wl_resource* res) { - auto data = sc(sc(wl_resource_get_user_data(res))->data()); - return data ? data->m_self.lock() : nullptr; + auto data = (CWLBufferResource*)(((CWlBuffer*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; } SP CWLBufferResource::create(WP resource) { - auto p = SP(new CWLBufferResource(resource)); - p->m_self = p; + auto p = SP(new CWLBufferResource(resource)); + p->self = p; return p; } diff --git a/src/protocols/types/WLBuffer.hpp b/src/protocols/types/WLBuffer.hpp index 2e823c5e..c7e21d4c 100644 --- a/src/protocols/types/WLBuffer.hpp +++ b/src/protocols/types/WLBuffer.hpp @@ -18,14 +18,14 @@ class CWLBufferResource { void sendRelease(); wl_resource* getResource(); - WP m_buffer; + WP buffer; - WP m_self; + WP self; private: CWLBufferResource(WP resource_); - SP m_resource; + SP resource; friend class IHLBuffer; }; diff --git a/src/render/AsyncResourceGatherer.hpp b/src/render/AsyncResourceGatherer.hpp deleted file mode 100644 index 98f7525e..00000000 --- a/src/render/AsyncResourceGatherer.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include - -#include "../helpers/memory/Memory.hpp" - -inline UP g_pAsyncResourceGatherer; \ No newline at end of file diff --git a/src/render/Framebuffer.cpp b/src/render/Framebuffer.cpp index b2ff7e68..6905eb36 100644 --- a/src/render/Framebuffer.cpp +++ b/src/render/Framebuffer.cpp @@ -1,30 +1,136 @@ #include "Framebuffer.hpp" +#include "OpenGL.hpp" -IFramebuffer::IFramebuffer(const std::string& name) : m_name(name) {} - -bool IFramebuffer::alloc(int w, int h, uint32_t format) { - RASSERT((w > 0 && h > 0), "cannot alloc a FB with negative / zero size! (attempted {}x{})", w, h); - - const bool sizeChanged = (m_size != Vector2D(w, h)); - const bool formatChanged = (format != m_drmFormat); - - if (m_fbAllocated && !sizeChanged && !formatChanged) - return true; - - m_size = {w, h}; - m_drmFormat = format; - m_fbAllocated = internalAlloc(w, h, format); - return m_fbAllocated; +CFramebuffer::CFramebuffer() { + ; } -bool IFramebuffer::isAllocated() { - return m_fbAllocated && m_tex; +bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) { + bool firstAlloc = false; + RASSERT((w > 1 && h > 1), "cannot alloc a FB with negative / zero size! (attempted {}x{})", w, h); + + uint32_t glFormat = NFormatUtils::drmFormatToGL(drmFormat); + uint32_t glType = NFormatUtils::glFormatToType(glFormat); + + if (!m_cTex) { + m_cTex = makeShared(); + m_cTex->allocate(); + glBindTexture(GL_TEXTURE_2D, m_cTex->m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + firstAlloc = true; + } + + if (!m_iFbAllocated) { + glGenFramebuffers(1, &m_iFb); + m_iFbAllocated = true; + firstAlloc = true; + } + + if (firstAlloc || m_vSize != Vector2D(w, h)) { + glBindTexture(GL_TEXTURE_2D, m_cTex->m_iTexID); + glTexImage2D(GL_TEXTURE_2D, 0, glFormat, w, h, 0, GL_RGBA, glType, nullptr); + glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_cTex->m_iTexID, 0); + +// TODO: Allow this with gles2 +#ifndef GLES2 + if (m_pStencilTex) { + glBindTexture(GL_TEXTURE_2D, m_pStencilTex->m_iTexID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_pStencilTex->m_iTexID, 0); + } +#endif + + auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Framebuffer incomplete, couldn't create! (FB status: {}, GL Error: 0x{:x})", status, (int)glGetError()); + + Debug::log(LOG, "Framebuffer created, status {}", status); + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + m_vSize = Vector2D(w, h); + + return true; } -SP IFramebuffer::getTexture() { - return m_tex; +void CFramebuffer::addStencil(SP tex) { + // TODO: Allow this with gles2 +#ifndef GLES2 + m_pStencilTex = tex; + glBindTexture(GL_TEXTURE_2D, m_pStencilTex->m_iTexID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_vSize.x, m_vSize.y, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); + + glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_pStencilTex->m_iTexID, 0); + + auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Failed adding a stencil to fbo!", status); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif } -SP IFramebuffer::getStencilTex() { - return m_stencilTex; +void CFramebuffer::bind() { +#ifndef GLES2 + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iFb); +#else + glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); +#endif + + if (g_pHyprOpenGL) + glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y); + else + glViewport(0, 0, m_vSize.x, m_vSize.y); +} + +void CFramebuffer::unbind() { +#ifndef GLES2 + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +#else + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif +} + +void CFramebuffer::release() { + if (m_iFbAllocated) { + glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glDeleteFramebuffers(1, &m_iFb); + m_iFbAllocated = false; + m_iFb = 0; + } + + if (m_cTex) + m_cTex.reset(); + + m_vSize = Vector2D(); +} + +CFramebuffer::~CFramebuffer() { + release(); +} + +bool CFramebuffer::isAllocated() { + return m_iFbAllocated && m_cTex; +} + +SP CFramebuffer::getTexture() { + return m_cTex; +} + +GLuint CFramebuffer::getFBID() { + return m_iFbAllocated ? m_iFb : 0; +} + +SP CFramebuffer::getStencilTex() { + return m_pStencilTex; } diff --git a/src/render/Framebuffer.hpp b/src/render/Framebuffer.hpp index 7e33f227..092e548e 100644 --- a/src/render/Framebuffer.hpp +++ b/src/render/Framebuffer.hpp @@ -1,40 +1,32 @@ #pragma once #include "../defines.hpp" -#include "../helpers/Format.hpp" #include "Texture.hpp" -#include -#include -class CHLBufferReference; - -class IFramebuffer { +class CFramebuffer { public: - IFramebuffer() = default; - IFramebuffer(const std::string& name); - virtual ~IFramebuffer() = default; - - virtual bool alloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888); - virtual void release() = 0; - virtual bool readPixels(CHLBufferReference buffer, uint32_t offsetX = 0, uint32_t offsetY = 0, uint32_t width = 0, uint32_t height = 0) = 0; - - virtual void bind() = 0; + CFramebuffer(); + ~CFramebuffer(); + bool alloc(int w, int h, uint32_t format = GL_RGBA); + void addStencil(SP tex); + void bind(); + void unbind(); + void release(); + void reset(); bool isAllocated(); - SP getTexture(); - SP getStencilTex(); + SP getTexture(); + SP getStencilTex(); + GLuint getFBID(); - virtual void addStencil(SP tex) = 0; + Vector2D m_vSize; - Vector2D m_size; - DRMFormat m_drmFormat = DRM_FORMAT_INVALID; + private: + SP m_cTex; + GLuint m_iFb = -1; + bool m_iFbAllocated = false; - protected: - virtual bool internalAlloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888) = 0; + SP m_pStencilTex; - SP m_tex; - bool m_fbAllocated = false; - - SP m_stencilTex; - std::string m_name; // name for logging + friend class CRenderbuffer; }; diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 83ad05ca..6f148ba9 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1,78 +1,52 @@ -#include -#include -#include -#include -#include -#include #include #include +#include "Shaders.hpp" #include "OpenGL.hpp" #include "Renderer.hpp" #include "../Compositor.hpp" #include "../helpers/MiscFunctions.hpp" -#include "../helpers/CursorShapes.hpp" -#include "../helpers/TransferFunction.hpp" #include "../config/ConfigValue.hpp" -#include "../config/ConfigManager.hpp" -#include "../managers/PointerManager.hpp" -#include "../desktop/view/LayerSurface.hpp" -#include "../desktop/state/FocusState.hpp" +#include "../desktop/LayerSurface.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/core/Compositor.hpp" #include "../protocols/ColorManagement.hpp" -#include "../helpers/cm/ColorManagement.hpp" +#include "../managers/HookSystemManager.hpp" #include "../managers/input/InputManager.hpp" -#include "../managers/eventLoop/EventLoopManager.hpp" -#include "../managers/CursorManager.hpp" -#include "../helpers/fs/FsUtils.hpp" -#include "../helpers/env/Env.hpp" -#include "../helpers/MainLoopExecutor.hpp" -#include "../i18n/Engine.hpp" -#include "../event/EventBus.hpp" -#include "../managers/screenshare/ScreenshareManager.hpp" -#include "debug/HyprNotificationOverlay.hpp" -#include "hyprerror/HyprError.hpp" -#include "macros.hpp" #include "pass/TexPassElement.hpp" #include "pass/RectPassElement.hpp" #include "pass/PreBlurElement.hpp" #include "pass/ClearPassElement.hpp" -#include "render/Shader.hpp" -#include "AsyncResourceGatherer.hpp" -#include -#include -#include -#include #include #include #include -#include -#include "ShaderLoader.hpp" -#include "Texture.hpp" #include -#include "gl/GLFramebuffer.hpp" -#include "gl/GLTexture.hpp" - using namespace Hyprutils::OS; using namespace NColorManagement; -using namespace Render; + +const std::vector ASSET_PATHS = { +#ifdef DATAROOTDIR + DATAROOTDIR, +#endif + "/usr/share", + "/usr/local/share", +}; static inline void loadGLProc(void* pProc, const char* name) { - void* proc = rc(eglGetProcAddress(name)); + void* proc = (void*)eglGetProcAddress(name); if (proc == nullptr) { - Log::logger->log(Log::CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name); + Debug::log(CRIT, "[Tracy GPU Profiling] eglGetProcAddress({}) failed", name); abort(); } - *sc(pProc) = proc; + *(void**)pProc = proc; } -static enum Hyprutils::CLI::eLogLevel eglLogToLevel(EGLint type) { +static enum eLogLevel eglLogToLevel(EGLint type) { switch (type) { - case EGL_DEBUG_MSG_CRITICAL_KHR: return Log::CRIT; - case EGL_DEBUG_MSG_ERROR_KHR: return Log::ERR; - case EGL_DEBUG_MSG_WARN_KHR: return Log::WARN; - case EGL_DEBUG_MSG_INFO_KHR: return Log::DEBUG; - default: return Log::DEBUG; + case EGL_DEBUG_MSG_CRITICAL_KHR: return CRIT; + case EGL_DEBUG_MSG_ERROR_KHR: return ERR; + case EGL_DEBUG_MSG_WARN_KHR: return WARN; + case EGL_DEBUG_MSG_INFO_KHR: return LOG; + default: return LOG; } } @@ -99,7 +73,7 @@ static const char* eglErrorToString(EGLint error) { } static void eglLog(EGLenum error, const char* command, EGLint type, EGLLabelKHR thread, EGLLabelKHR obj, const char* msg) { - Log::logger->log(eglLogToLevel(type), "[EGL] Command {} errored out with {} (0x{}): {}", command, eglErrorToString(error), error, msg); + Debug::log(eglLogToLevel(type), "[EGL] Command {} errored out with {} (0x{}): {}", command, eglErrorToString(error), error, msg); } static int openRenderNode(int drmFd) { @@ -109,88 +83,91 @@ static int openRenderNode(int drmFd) { // primary node renderName = drmGetPrimaryDeviceNameFromFd(drmFd); if (!renderName) { - Log::logger->log(Log::ERR, "drmGetPrimaryDeviceNameFromFd failed"); + Debug::log(ERR, "drmGetPrimaryDeviceNameFromFd failed"); return -1; } - Log::logger->log(Log::DEBUG, "DRM dev {} has no render node, falling back to primary", renderName); + Debug::log(LOG, "DRM dev {} has no render node, falling back to primary", renderName); drmVersion* render_version = drmGetVersion(drmFd); if (render_version && render_version->name) { - Log::logger->log(Log::DEBUG, "DRM dev versionName", render_version->name); + Debug::log(LOG, "DRM dev versionName", render_version->name); if (strcmp(render_version->name, "evdi") == 0) { - free(renderName); // NOLINT(cppcoreguidelines-no-malloc) - renderName = strdup("/dev/dri/card0"); + free(renderName); + renderName = (char*)malloc(sizeof(char) * 15); + strcpy(renderName, "/dev/dri/card0"); } drmFreeVersion(render_version); } } - Log::logger->log(Log::DEBUG, "openRenderNode got drm device {}", renderName); + Debug::log(LOG, "openRenderNode got drm device {}", renderName); int renderFD = open(renderName, O_RDWR | O_CLOEXEC); if (renderFD < 0) - Log::logger->log(Log::ERR, "openRenderNode failed to open drm device {}", renderName); + Debug::log(ERR, "openRenderNode failed to open drm device {}", renderName); - free(renderName); // NOLINT(cppcoreguidelines-no-malloc) + free(renderName); return renderFD; } void CHyprOpenGLImpl::initEGL(bool gbm) { std::vector attrs; - if (m_exts.KHR_display_reference) { + if (m_sExts.KHR_display_reference) { attrs.push_back(EGL_TRACK_REFERENCES_KHR); attrs.push_back(EGL_TRUE); } attrs.push_back(EGL_NONE); - m_eglDisplay = m_proc.eglGetPlatformDisplayEXT(gbm ? EGL_PLATFORM_GBM_KHR : EGL_PLATFORM_DEVICE_EXT, gbm ? m_gbmDevice : m_eglDevice, attrs.data()); - if (m_eglDisplay == EGL_NO_DISPLAY) + m_pEglDisplay = m_sProc.eglGetPlatformDisplayEXT(gbm ? EGL_PLATFORM_GBM_KHR : EGL_PLATFORM_DEVICE_EXT, gbm ? m_pGbmDevice : m_pEglDevice, attrs.data()); + if (m_pEglDisplay == EGL_NO_DISPLAY) RASSERT(false, "EGL: failed to create a platform display"); attrs.clear(); EGLint major, minor; - if (eglInitialize(m_eglDisplay, &major, &minor) == EGL_FALSE) + if (eglInitialize(m_pEglDisplay, &major, &minor) == EGL_FALSE) RASSERT(false, "EGL: failed to initialize a platform display"); - const std::string EGLEXTENSIONS = eglQueryString(m_eglDisplay, EGL_EXTENSIONS); + const std::string EGLEXTENSIONS = (const char*)eglQueryString(m_pEglDisplay, EGL_EXTENSIONS); - m_exts.IMG_context_priority = EGLEXTENSIONS.contains("IMG_context_priority"); - m_exts.EXT_create_context_robustness = EGLEXTENSIONS.contains("EXT_create_context_robustness"); - m_exts.EXT_image_dma_buf_import = EGLEXTENSIONS.contains("EXT_image_dma_buf_import"); - m_exts.EXT_image_dma_buf_import_modifiers = EGLEXTENSIONS.contains("EXT_image_dma_buf_import_modifiers"); - m_exts.KHR_context_flush_control = EGLEXTENSIONS.contains("EGL_KHR_context_flush_control"); + m_sExts.IMG_context_priority = EGLEXTENSIONS.contains("IMG_context_priority"); + m_sExts.EXT_create_context_robustness = EGLEXTENSIONS.contains("EXT_create_context_robustness"); + m_sExts.EXT_image_dma_buf_import = EGLEXTENSIONS.contains("EXT_image_dma_buf_import"); + m_sExts.EXT_image_dma_buf_import_modifiers = EGLEXTENSIONS.contains("EXT_image_dma_buf_import_modifiers"); - if (m_exts.IMG_context_priority) { - Log::logger->log(Log::DEBUG, "EGL: IMG_context_priority supported, requesting high"); + if (m_sExts.IMG_context_priority) { + Debug::log(LOG, "EGL: IMG_context_priority supported, requesting high"); attrs.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); attrs.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); } - if (m_exts.EXT_create_context_robustness) { - Log::logger->log(Log::DEBUG, "EGL: EXT_create_context_robustness supported, requesting lose on reset"); + if (m_sExts.EXT_create_context_robustness) { + Debug::log(LOG, "EGL: EXT_create_context_robustness supported, requesting lose on reset"); attrs.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT); attrs.push_back(EGL_LOSE_CONTEXT_ON_RESET_EXT); } - if (m_exts.KHR_context_flush_control) { - Log::logger->log(Log::DEBUG, "EGL: Using KHR_context_flush_control"); - attrs.push_back(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR); - attrs.push_back(EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); // or _FLUSH_KHR - } - auto attrsNoVer = attrs; +#ifndef GLES2 attrs.push_back(EGL_CONTEXT_MAJOR_VERSION); attrs.push_back(3); attrs.push_back(EGL_CONTEXT_MINOR_VERSION); attrs.push_back(2); +#else + attrs.push_back(EGL_CONTEXT_CLIENT_VERSION); + attrs.push_back(2); +#endif + attrs.push_back(EGL_NONE); - m_eglContext = eglCreateContext(m_eglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); - if (m_eglContext == EGL_NO_CONTEXT) { - Log::logger->log(Log::WARN, "EGL: Failed to create a context with GLES3.2, retrying 3.0"); + m_pEglContext = eglCreateContext(m_pEglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); + if (m_pEglContext == EGL_NO_CONTEXT) { +#ifdef GLES2 + RASSERT(false, "EGL: failed to create a context with GLES2.0"); +#endif + Debug::log(WARN, "EGL: Failed to create a context with GLES3.2, retrying 3.0"); attrs = attrsNoVer; attrs.push_back(EGL_CONTEXT_MAJOR_VERSION); @@ -199,23 +176,22 @@ void CHyprOpenGLImpl::initEGL(bool gbm) { attrs.push_back(0); attrs.push_back(EGL_NONE); - m_eglContext = eglCreateContext(m_eglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); - m_eglContextVersion = EGL_CONTEXT_GLES_3_0; + m_pEglContext = eglCreateContext(m_pEglDisplay, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); - if (m_eglContext == EGL_NO_CONTEXT) + if (m_pEglContext == EGL_NO_CONTEXT) RASSERT(false, "EGL: failed to create a context with either GLES3.2 or 3.0"); } - if (m_exts.IMG_context_priority) { + if (m_sExts.IMG_context_priority) { EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; - eglQueryContext(m_eglDisplay, m_eglContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority); + eglQueryContext(m_pEglDisplay, m_pEglContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority); if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG) - Log::logger->log(Log::ERR, "EGL: Failed to obtain a high priority context"); + Debug::log(ERR, "EGL: Failed to obtain a high priority context"); else - Log::logger->log(Log::DEBUG, "EGL: Got a high priority context"); + Debug::log(LOG, "EGL: Got a high priority context"); } - eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, m_eglContext); + eglMakeCurrent(m_pEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, m_pEglContext); } static bool drmDeviceHasName(const drmDevice* device, const std::string& name) { @@ -231,110 +207,109 @@ static bool drmDeviceHasName(const drmDevice* device, const std::string& name) { EGLDeviceEXT CHyprOpenGLImpl::eglDeviceFromDRMFD(int drmFD) { EGLint nDevices = 0; - if (!m_proc.eglQueryDevicesEXT(0, nullptr, &nDevices)) { - Log::logger->log(Log::ERR, "eglDeviceFromDRMFD: eglQueryDevicesEXT failed"); + if (!m_sProc.eglQueryDevicesEXT(0, nullptr, &nDevices)) { + Debug::log(ERR, "eglDeviceFromDRMFD: eglQueryDevicesEXT failed"); return EGL_NO_DEVICE_EXT; } if (nDevices <= 0) { - Log::logger->log(Log::ERR, "eglDeviceFromDRMFD: no devices"); + Debug::log(ERR, "eglDeviceFromDRMFD: no devices"); return EGL_NO_DEVICE_EXT; } std::vector devices; devices.resize(nDevices); - if (!m_proc.eglQueryDevicesEXT(nDevices, devices.data(), &nDevices)) { - Log::logger->log(Log::ERR, "eglDeviceFromDRMFD: eglQueryDevicesEXT failed (2)"); + if (!m_sProc.eglQueryDevicesEXT(nDevices, devices.data(), &nDevices)) { + Debug::log(ERR, "eglDeviceFromDRMFD: eglQueryDevicesEXT failed (2)"); return EGL_NO_DEVICE_EXT; } drmDevice* drmDev = nullptr; if (int ret = drmGetDevice(drmFD, &drmDev); ret < 0) { - Log::logger->log(Log::ERR, "eglDeviceFromDRMFD: drmGetDevice failed"); + Debug::log(ERR, "eglDeviceFromDRMFD: drmGetDevice failed"); return EGL_NO_DEVICE_EXT; } for (auto const& d : devices) { - auto devName = m_proc.eglQueryDeviceStringEXT(d, EGL_DRM_DEVICE_FILE_EXT); + auto devName = m_sProc.eglQueryDeviceStringEXT(d, EGL_DRM_DEVICE_FILE_EXT); if (!devName) continue; if (drmDeviceHasName(drmDev, devName)) { - Log::logger->log(Log::DEBUG, "eglDeviceFromDRMFD: Using device {}", devName); + Debug::log(LOG, "eglDeviceFromDRMFD: Using device {}", devName); drmFreeDevice(&drmDev); return d; } } drmFreeDevice(&drmDev); - Log::logger->log(Log::DEBUG, "eglDeviceFromDRMFD: No drm devices found"); + Debug::log(LOG, "eglDeviceFromDRMFD: No drm devices found"); return EGL_NO_DEVICE_EXT; } -CHyprOpenGLImpl::CHyprOpenGLImpl() : m_drmFD(g_pCompositor->m_drmRenderNode.fd >= 0 ? g_pCompositor->m_drmRenderNode.fd : g_pCompositor->m_drm.fd) { - const std::string EGLEXTENSIONS = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); +CHyprOpenGLImpl::CHyprOpenGLImpl() : m_iDRMFD(g_pCompositor->m_iDRMFD) { + const std::string EGLEXTENSIONS = (const char*)eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - Log::logger->log(Log::DEBUG, "Supported EGL global extensions: ({}) {}", std::ranges::count(EGLEXTENSIONS, ' '), EGLEXTENSIONS); + Debug::log(LOG, "Supported EGL extensions: ({}) {}", std::count(EGLEXTENSIONS.begin(), EGLEXTENSIONS.end(), ' '), EGLEXTENSIONS); - m_exts.KHR_display_reference = EGLEXTENSIONS.contains("KHR_display_reference"); + m_sExts.KHR_display_reference = EGLEXTENSIONS.contains("KHR_display_reference"); - loadGLProc(&m_proc.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); - loadGLProc(&m_proc.eglCreateImageKHR, "eglCreateImageKHR"); - loadGLProc(&m_proc.eglDestroyImageKHR, "eglDestroyImageKHR"); - loadGLProc(&m_proc.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT"); - loadGLProc(&m_proc.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT"); - loadGLProc(&m_proc.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); - loadGLProc(&m_proc.eglDebugMessageControlKHR, "eglDebugMessageControlKHR"); - loadGLProc(&m_proc.eglGetPlatformDisplayEXT, "eglGetPlatformDisplayEXT"); - loadGLProc(&m_proc.eglCreateSyncKHR, "eglCreateSyncKHR"); - loadGLProc(&m_proc.eglDestroySyncKHR, "eglDestroySyncKHR"); - loadGLProc(&m_proc.eglDupNativeFenceFDANDROID, "eglDupNativeFenceFDANDROID"); - loadGLProc(&m_proc.eglWaitSyncKHR, "eglWaitSyncKHR"); + loadGLProc(&m_sProc.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); + loadGLProc(&m_sProc.eglCreateImageKHR, "eglCreateImageKHR"); + loadGLProc(&m_sProc.eglDestroyImageKHR, "eglDestroyImageKHR"); + loadGLProc(&m_sProc.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT"); + loadGLProc(&m_sProc.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT"); + loadGLProc(&m_sProc.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); + loadGLProc(&m_sProc.eglDebugMessageControlKHR, "eglDebugMessageControlKHR"); + loadGLProc(&m_sProc.eglGetPlatformDisplayEXT, "eglGetPlatformDisplayEXT"); + loadGLProc(&m_sProc.eglCreateSyncKHR, "eglCreateSyncKHR"); + loadGLProc(&m_sProc.eglDestroySyncKHR, "eglDestroySyncKHR"); + loadGLProc(&m_sProc.eglDupNativeFenceFDANDROID, "eglDupNativeFenceFDANDROID"); + loadGLProc(&m_sProc.eglWaitSyncKHR, "eglWaitSyncKHR"); - RASSERT(m_proc.eglCreateSyncKHR, "Display driver doesn't support eglCreateSyncKHR"); - RASSERT(m_proc.eglDupNativeFenceFDANDROID, "Display driver doesn't support eglDupNativeFenceFDANDROID"); - RASSERT(m_proc.eglWaitSyncKHR, "Display driver doesn't support eglWaitSyncKHR"); + RASSERT(m_sProc.eglCreateSyncKHR, "Display driver doesn't support eglCreateSyncKHR"); + RASSERT(m_sProc.eglDupNativeFenceFDANDROID, "Display driver doesn't support eglDupNativeFenceFDANDROID"); + RASSERT(m_sProc.eglWaitSyncKHR, "Display driver doesn't support eglWaitSyncKHR"); if (EGLEXTENSIONS.contains("EGL_EXT_device_base") || EGLEXTENSIONS.contains("EGL_EXT_device_enumeration")) - loadGLProc(&m_proc.eglQueryDevicesEXT, "eglQueryDevicesEXT"); + loadGLProc(&m_sProc.eglQueryDevicesEXT, "eglQueryDevicesEXT"); if (EGLEXTENSIONS.contains("EGL_EXT_device_base") || EGLEXTENSIONS.contains("EGL_EXT_device_query")) { - loadGLProc(&m_proc.eglQueryDeviceStringEXT, "eglQueryDeviceStringEXT"); - loadGLProc(&m_proc.eglQueryDisplayAttribEXT, "eglQueryDisplayAttribEXT"); + loadGLProc(&m_sProc.eglQueryDeviceStringEXT, "eglQueryDeviceStringEXT"); + loadGLProc(&m_sProc.eglQueryDisplayAttribEXT, "eglQueryDisplayAttribEXT"); } - static const auto GLDEBUG = CConfigValue("debug:gl_debugging"); - if (EGLEXTENSIONS.contains("EGL_KHR_debug") && *GLDEBUG) { - loadGLProc(&m_proc.eglDebugMessageControlKHR, "eglDebugMessageControlKHR"); + if (EGLEXTENSIONS.contains("EGL_KHR_debug")) { + loadGLProc(&m_sProc.eglDebugMessageControlKHR, "eglDebugMessageControlKHR"); static const EGLAttrib debugAttrs[] = { EGL_DEBUG_MSG_CRITICAL_KHR, EGL_TRUE, EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE, EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE, EGL_DEBUG_MSG_INFO_KHR, EGL_TRUE, EGL_NONE, }; - m_proc.eglDebugMessageControlKHR(::eglLog, debugAttrs); + m_sProc.eglDebugMessageControlKHR(::eglLog, debugAttrs); } RASSERT(eglBindAPI(EGL_OPENGL_ES_API) != EGL_FALSE, "Couldn't bind to EGL's opengl ES API. This means your gpu driver f'd up. This is not a hyprland issue."); bool success = false; - if (EGLEXTENSIONS.contains("EXT_platform_device") || !m_proc.eglQueryDevicesEXT || !m_proc.eglQueryDeviceStringEXT) { - m_eglDevice = eglDeviceFromDRMFD(m_drmFD); + if (EGLEXTENSIONS.contains("EXT_platform_device") || !m_sProc.eglQueryDevicesEXT || !m_sProc.eglQueryDeviceStringEXT) { + m_pEglDevice = eglDeviceFromDRMFD(m_iDRMFD); - if (m_eglDevice != EGL_NO_DEVICE_EXT) { + if (m_pEglDevice != EGL_NO_DEVICE_EXT) { success = true; initEGL(false); } } if (!success) { - Log::logger->log(Log::WARN, "EGL: EXT_platform_device or EGL_EXT_device_query not supported, using gbm"); + Debug::log(WARN, "EGL: EXT_platform_device or EGL_EXT_device_query not supported, using gbm"); if (EGLEXTENSIONS.contains("KHR_platform_gbm")) { - success = true; - m_gbmFD = CFileDescriptor{openRenderNode(m_drmFD)}; - if (!m_gbmFD.isValid()) + success = true; + m_iGBMFD = CFileDescriptor{openRenderNode(m_iDRMFD)}; + if (!m_iGBMFD.isValid()) RASSERT(false, "Couldn't open a gbm fd"); - m_gbmDevice = gbm_create_device(m_gbmFD.get()); - if (!m_gbmDevice) + m_pGbmDevice = gbm_create_device(m_iGBMFD.get()); + if (!m_pGbmDevice) RASSERT(false, "Couldn't open a gbm device"); initEGL(true); @@ -343,39 +318,25 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() : m_drmFD(g_pCompositor->m_drmRenderNode.fd > RASSERT(success, "EGL does not support KHR_platform_gbm or EXT_platform_device, this is an issue with your gpu driver."); - auto* const EXTENSIONS = rc(glGetString(GL_EXTENSIONS)); + auto* const EXTENSIONS = (const char*)glGetString(GL_EXTENSIONS); RASSERT(EXTENSIONS, "Couldn't retrieve openGL extensions!"); - m_extensions = EXTENSIONS; + m_szExtensions = EXTENSIONS; - Log::logger->log(Log::DEBUG, "Creating the Hypr OpenGL Renderer!"); - Log::logger->log(Log::DEBUG, "Using: {}", rc(glGetString(GL_VERSION))); - Log::logger->log(Log::DEBUG, "Vendor: {}", rc(glGetString(GL_VENDOR))); - Log::logger->log(Log::DEBUG, "Renderer: {}", rc(glGetString(GL_RENDERER))); - Log::logger->log(Log::DEBUG, "Supported extensions: ({}) {}", std::ranges::count(m_extensions, ' '), m_extensions); + Debug::log(LOG, "Creating the Hypr OpenGL Renderer!"); + Debug::log(LOG, "Using: {}", (char*)glGetString(GL_VERSION)); + Debug::log(LOG, "Vendor: {}", (char*)glGetString(GL_VENDOR)); + Debug::log(LOG, "Renderer: {}", (char*)glGetString(GL_RENDERER)); + Debug::log(LOG, "Supported extensions: ({}) {}", std::count(m_szExtensions.begin(), m_szExtensions.end(), ' '), m_szExtensions); - m_exts.EXT_read_format_bgra = m_extensions.contains("GL_EXT_read_format_bgra"); + m_sExts.EXT_read_format_bgra = m_szExtensions.contains("GL_EXT_read_format_bgra"); - RASSERT(m_extensions.contains("GL_EXT_texture_format_BGRA8888"), "GL_EXT_texture_format_BGRA8888 support by the GPU driver is required"); + RASSERT(m_szExtensions.contains("GL_EXT_texture_format_BGRA8888"), "GL_EXT_texture_format_BGRA8888 support by the GPU driver is required"); - if (!m_exts.EXT_read_format_bgra) - Log::logger->log(Log::WARN, "Your GPU does not support GL_EXT_read_format_bgra, this may cause issues with texture importing"); - if (!m_exts.EXT_image_dma_buf_import || !m_exts.EXT_image_dma_buf_import_modifiers) - Log::logger->log(Log::WARN, "Your GPU does not support DMABUFs, this will possibly cause issues and will take a hit on the performance."); - - const std::string EGLEXTENSIONS_DISPLAY = eglQueryString(m_eglDisplay, EGL_EXTENSIONS); - - Log::logger->log(Log::DEBUG, "Supported EGL display extensions: ({}) {}", std::ranges::count(EGLEXTENSIONS_DISPLAY, ' '), EGLEXTENSIONS_DISPLAY); - -#if defined(__linux__) - m_exts.EGL_ANDROID_native_fence_sync_ext = EGLEXTENSIONS_DISPLAY.contains("EGL_ANDROID_native_fence_sync"); - - if (!m_exts.EGL_ANDROID_native_fence_sync_ext) - Log::logger->log(Log::WARN, "Your GPU does not support explicit sync via the EGL_ANDROID_native_fence_sync extension."); -#else - m_exts.EGL_ANDROID_native_fence_sync_ext = false; - Log::logger->log(Log::WARN, "Forcefully disabling explicit sync: BSD is missing support for proper timeline export"); -#endif + if (!m_sExts.EXT_read_format_bgra) + Debug::log(WARN, "Your GPU does not support GL_EXT_read_format_bgra, this may cause issues with texture importing"); + if (!m_sExts.EXT_image_dma_buf_import || !m_sExts.EXT_image_dma_buf_import_modifiers) + Debug::log(WARN, "Your GPU does not support DMABUFs, this will possibly cause issues and will take a hit on the performance."); #ifdef USE_TRACY_GPU @@ -387,83 +348,43 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() : m_drmFD(g_pCompositor->m_drmRenderNode.fd > TRACY_GPU_CONTEXT; +#ifdef GLES2 + Debug::log(WARN, "!RENDERER: Using the legacy GLES2 renderer!"); +#endif + initDRMFormats(); initAssets(); - static auto P = Event::bus()->m_events.render.pre.listen([&](PHLMONITOR mon) { preRender(mon); }); + static auto P = g_pHookSystem->hookDynamic("preRender", [&](void* self, SCallbackInfo& info, std::any data) { preRender(std::any_cast(data)); }); - RASSERT(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT), "Couldn't unset current EGL!"); + RASSERT(eglMakeCurrent(m_pEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT), "Couldn't unset current EGL!"); - m_globalTimer.reset(); - - pushMonitorTransformEnabled(false); - - static auto addLastPressToHistory = [this](const Vector2D& pos, bool killing, bool touch) { - // shift the new pos and time in - std::ranges::rotate(m_pressedHistoryPositions, m_pressedHistoryPositions.end() - 1); - m_pressedHistoryPositions[0] = pos; - - std::ranges::rotate(m_pressedHistoryTimers, m_pressedHistoryTimers.end() - 1); - m_pressedHistoryTimers[0].reset(); - - // shift killed flag in - m_pressedHistoryKilled <<= 1; - m_pressedHistoryKilled |= killing ? 1 : 0; -#if POINTER_PRESSED_HISTORY_LENGTH < 32 - m_pressedHistoryKilled &= (1 >> POINTER_PRESSED_HISTORY_LENGTH) - 1; -#endif - - // shift touch flag in - m_pressedHistoryTouched <<= 1; - m_pressedHistoryTouched |= touch ? 1 : 0; -#if POINTER_PRESSED_HISTORY_LENGTH < 32 - m_pressedHistoryTouched &= (1 >> POINTER_PRESSED_HISTORY_LENGTH) - 1; -#endif - }; - - static auto P2 = Event::bus()->m_events.input.mouse.button.listen([](IPointer::SButtonEvent e, Event::SCallbackInfo&) { - if (e.state != WL_POINTER_BUTTON_STATE_PRESSED) - return; - - addLastPressToHistory(g_pInputManager->getMouseCoordsInternal(), g_pInputManager->getClickMode() == CLICKMODE_KILL, false); - }); - - static auto P3 = Event::bus()->m_events.input.touch.down.listen([](ITouch::SDownEvent e, Event::SCallbackInfo&) { - auto PMONITOR = g_pCompositor->getMonitorFromName(!e.device->m_boundOutput.empty() ? e.device->m_boundOutput : ""); - - PMONITOR = PMONITOR ? PMONITOR : Desktop::focusState()->monitor(); - - const auto TOUCH_COORDS = PMONITOR->m_position + (e.pos * PMONITOR->m_size); - - addLastPressToHistory(TOUCH_COORDS, g_pInputManager->getClickMode() == CLICKMODE_KILL, true); - }); - - m_finalScreenShader = makeShared(); + m_tGlobalTimer.reset(); } CHyprOpenGLImpl::~CHyprOpenGLImpl() { - if (m_eglDisplay && m_eglContext != EGL_NO_CONTEXT) - eglDestroyContext(m_eglDisplay, m_eglContext); + if (m_pEglDisplay && m_pEglContext != EGL_NO_CONTEXT) + eglDestroyContext(m_pEglDisplay, m_pEglContext); - if (m_eglDisplay) - eglTerminate(m_eglDisplay); + if (m_pEglDisplay) + eglTerminate(m_pEglDisplay); eglReleaseThread(); - if (m_gbmDevice) - gbm_device_destroy(m_gbmDevice); + if (m_pGbmDevice) + gbm_device_destroy(m_pGbmDevice); } std::optional> CHyprOpenGLImpl::getModsForFormat(EGLint format) { // TODO: return std::expected when clang supports it - if (!m_exts.EXT_image_dma_buf_import_modifiers) + if (!m_sExts.EXT_image_dma_buf_import_modifiers) return std::nullopt; EGLint len = 0; - if (!m_proc.eglQueryDmaBufModifiersEXT(m_eglDisplay, format, 0, nullptr, nullptr, &len)) { - Log::logger->log(Log::ERR, "EGL: Failed to query mods"); + if (!m_sProc.eglQueryDmaBufModifiersEXT(m_pEglDisplay, format, 0, nullptr, nullptr, &len)) { + Debug::log(ERR, "EGL: Failed to query mods"); return std::nullopt; } @@ -476,7 +397,7 @@ std::optional> CHyprOpenGLImpl::getModsForFormat(EGLint fo mods.resize(len); external.resize(len); - m_proc.eglQueryDmaBufModifiersEXT(m_eglDisplay, format, len, mods.data(), external.data(), &len); + m_sProc.eglQueryDmaBufModifiersEXT(m_pEglDisplay, format, len, mods.data(), external.data(), &len); std::vector result; // reserve number of elements to avoid reallocations @@ -494,41 +415,41 @@ std::optional> CHyprOpenGLImpl::getModsForFormat(EGLint fo } // if the driver doesn't mark linear as external, add it. It's allowed unless the driver says otherwise. (e.g. nvidia) - if (!linearIsExternal && std::ranges::find(mods, DRM_FORMAT_MOD_LINEAR) == mods.end()) - result.push_back(DRM_FORMAT_MOD_LINEAR); + if (!linearIsExternal && std::find(mods.begin(), mods.end(), DRM_FORMAT_MOD_LINEAR) == mods.end() && mods.size() == 0) + mods.push_back(DRM_FORMAT_MOD_LINEAR); return result; } void CHyprOpenGLImpl::initDRMFormats() { - const auto DISABLE_MODS = Env::envEnabled("HYPRLAND_EGL_NO_MODIFIERS"); + const auto DISABLE_MODS = envEnabled("HYPRLAND_EGL_NO_MODIFIERS"); if (DISABLE_MODS) - Log::logger->log(Log::WARN, "HYPRLAND_EGL_NO_MODIFIERS set, disabling modifiers"); + Debug::log(WARN, "HYPRLAND_EGL_NO_MODIFIERS set, disabling modifiers"); - if (!m_exts.EXT_image_dma_buf_import) { - Log::logger->log(Log::ERR, "EGL: No dmabuf import, DMABufs will not work."); + if (!m_sExts.EXT_image_dma_buf_import) { + Debug::log(ERR, "EGL: No dmabuf import, DMABufs will not work."); return; } std::vector formats; - if (!m_exts.EXT_image_dma_buf_import_modifiers || !m_proc.eglQueryDmaBufFormatsEXT) { + if (!m_sExts.EXT_image_dma_buf_import_modifiers || !m_sProc.eglQueryDmaBufFormatsEXT) { formats.push_back(DRM_FORMAT_ARGB8888); formats.push_back(DRM_FORMAT_XRGB8888); - Log::logger->log(Log::WARN, "EGL: No mod support"); + Debug::log(WARN, "EGL: No mod support"); } else { EGLint len = 0; - m_proc.eglQueryDmaBufFormatsEXT(m_eglDisplay, 0, nullptr, &len); + m_sProc.eglQueryDmaBufFormatsEXT(m_pEglDisplay, 0, nullptr, &len); formats.resize(len); - m_proc.eglQueryDmaBufFormatsEXT(m_eglDisplay, len, formats.data(), &len); + m_sProc.eglQueryDmaBufFormatsEXT(m_pEglDisplay, len, formats.data(), &len); } - if (formats.empty()) { - Log::logger->log(Log::ERR, "EGL: Failed to get formats, DMABufs will not work."); + if (formats.size() == 0) { + Debug::log(ERR, "EGL: Failed to get formats, DMABufs will not work."); return; } - Log::logger->log(Log::DEBUG, "Supported DMA-BUF formats:"); + Debug::log(LOG, "Supported DMA-BUF formats:"); std::vector dmaFormats; // reserve number of elements to avoid reallocations @@ -545,7 +466,7 @@ void CHyprOpenGLImpl::initDRMFormats() { } else mods = {DRM_FORMAT_MOD_LINEAR}; - m_hasModifiers = m_hasModifiers || !mods.empty(); + m_bHasModifiers = m_bHasModifiers || mods.size() > 0; // EGL can always do implicit modifiers. mods.push_back(DRM_FORMAT_MOD_INVALID); @@ -560,16 +481,16 @@ void CHyprOpenGLImpl::initDRMFormats() { modifierData.reserve(mods.size()); auto fmtName = drmGetFormatName(fmt); - Log::logger->log(Log::DEBUG, "EGL: GPU Supports Format {} (0x{:x})", fmtName ? fmtName : "?unknown?", fmt); + Debug::log(LOG, "EGL: GPU Supports Format {} (0x{:x})", fmtName ? fmtName : "?unknown?", fmt); for (auto const& mod : mods) { auto modName = drmGetFormatModifierName(mod); modifierData.emplace_back(std::make_pair<>(mod, modName ? modName : "?unknown?")); - free(modName); // NOLINT(cppcoreguidelines-no-malloc) + free(modName); } - free(fmtName); // NOLINT(cppcoreguidelines-no-malloc) + free(fmtName); mods.clear(); - std::ranges::sort(modifierData, [](const auto& a, const auto& b) { + std::sort(modifierData.begin(), modifierData.end(), [](const auto& a, const auto& b) { if (a.first == 0) return false; if (a.second.contains("DCC")) @@ -578,30 +499,29 @@ void CHyprOpenGLImpl::initDRMFormats() { }); for (auto const& [m, name] : modifierData) { - Log::logger->log(Log::DEBUG, "EGL: | with modifier {} (0x{:x})", name, m); + Debug::log(LOG, "EGL: | with modifier {} (0x{:x})", name, m); mods.emplace_back(m); } } - Log::logger->log(Log::DEBUG, "EGL: {} formats found in total. Some modifiers may be omitted as they are external-only.", dmaFormats.size()); + Debug::log(LOG, "EGL: {} formats found in total. Some modifiers may be omitted as they are external-only.", dmaFormats.size()); - if (dmaFormats.empty()) - Log::logger->log( - Log::WARN, "EGL: WARNING: No dmabuf formats were found, dmabuf will be disabled. This will degrade performance, but is most likely a driver issue or a very old GPU."); + if (dmaFormats.size() == 0) + Debug::log(WARN, + "EGL: WARNING: No dmabuf formats were found, dmabuf will be disabled. This will degrade performance, but is most likely a driver issue or a very old GPU."); - m_drmFormats = dmaFormats; + drmFormats = dmaFormats; } EGLImageKHR CHyprOpenGLImpl::createEGLImage(const Aquamarine::SDMABUFAttrs& attrs) { - std::array attribs; - size_t idx = 0; + std::vector attribs; - attribs[idx++] = EGL_WIDTH; - attribs[idx++] = attrs.size.x; - attribs[idx++] = EGL_HEIGHT; - attribs[idx++] = attrs.size.y; - attribs[idx++] = EGL_LINUX_DRM_FOURCC_EXT; - attribs[idx++] = attrs.format; + attribs.push_back(EGL_WIDTH); + attribs.push_back(attrs.size.x); + attribs.push_back(EGL_HEIGHT); + attribs.push_back(attrs.size.y); + attribs.push_back(EGL_LINUX_DRM_FOURCC_EXT); + attribs.push_back(attrs.format); struct { EGLint fd; @@ -615,40 +535,126 @@ EGLImageKHR CHyprOpenGLImpl::createEGLImage(const Aquamarine::SDMABUFAttrs& attr {EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT}, {EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT}}; - for (int i = 0; i < attrs.planes; ++i) { - attribs[idx++] = attrNames[i].fd; - attribs[idx++] = attrs.fds[i]; - attribs[idx++] = attrNames[i].offset; - attribs[idx++] = attrs.offsets[i]; - attribs[idx++] = attrNames[i].pitch; - attribs[idx++] = attrs.strides[i]; - - if (m_hasModifiers && attrs.modifier != DRM_FORMAT_MOD_INVALID) { - attribs[idx++] = attrNames[i].modlo; - attribs[idx++] = sc(attrs.modifier & 0xFFFFFFFF); - attribs[idx++] = attrNames[i].modhi; - attribs[idx++] = sc(attrs.modifier >> 32); + for (int i = 0; i < attrs.planes; i++) { + attribs.push_back(attrNames[i].fd); + attribs.push_back(attrs.fds[i]); + attribs.push_back(attrNames[i].offset); + attribs.push_back(attrs.offsets[i]); + attribs.push_back(attrNames[i].pitch); + attribs.push_back(attrs.strides[i]); + if (m_bHasModifiers && attrs.modifier != DRM_FORMAT_MOD_INVALID) { + attribs.push_back(attrNames[i].modlo); + attribs.push_back(attrs.modifier & 0xFFFFFFFF); + attribs.push_back(attrNames[i].modhi); + attribs.push_back(attrs.modifier >> 32); } } - attribs[idx++] = EGL_IMAGE_PRESERVED_KHR; - attribs[idx++] = EGL_TRUE; - attribs[idx++] = EGL_NONE; + attribs.push_back(EGL_IMAGE_PRESERVED_KHR); + attribs.push_back(EGL_TRUE); - RASSERT(idx <= attribs.size(), "createEglImage: attribs array out of bounds."); + attribs.push_back(EGL_NONE); - EGLImageKHR image = m_proc.eglCreateImageKHR(m_eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data()); + EGLImageKHR image = m_sProc.eglCreateImageKHR(m_pEglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, (int*)attribs.data()); if (image == EGL_NO_IMAGE_KHR) { - Log::logger->log(Log::ERR, "EGL: EGLCreateImageKHR failed: {}", eglGetError()); + Debug::log(ERR, "EGL: EGLCreateImageKHR failed: {}", eglGetError()); return EGL_NO_IMAGE_KHR; } return image; } -void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP rb, SP fb) { - m_renderData.pMonitor = pMonitor; +void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program) { + GLint maxLength = 0; + if (program) + glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + else + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + std::vector errorLog(maxLength); + if (program) + glGetProgramInfoLog(shader, maxLength, &maxLength, errorLog.data()); + else + glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data()); + std::string errorStr(errorLog.begin(), errorLog.end()); + + const auto FULLERROR = (program ? "Screen shader parser: Error linking program:" : "Screen shader parser: Error compiling shader: ") + errorStr; + + Debug::log(ERR, "Failed to link shader: {}", FULLERROR); + + g_pConfigManager->addParseError(FULLERROR); +} + +GLuint CHyprOpenGLImpl::createProgram(const std::string& vert, const std::string& frag, bool dynamic) { + auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic); + if (dynamic) { + if (vertCompiled == 0) + return 0; + } else + RASSERT(vertCompiled, "Compiling shader failed. VERTEX nullptr! Shader source:\n\n{}", vert); + + auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag, dynamic); + if (dynamic) { + if (fragCompiled == 0) + return 0; + } else + RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT nullptr! Shader source:\n\n{}", frag); + + auto prog = glCreateProgram(); + glAttachShader(prog, vertCompiled); + glAttachShader(prog, fragCompiled); + glLinkProgram(prog); + + glDetachShader(prog, vertCompiled); + glDetachShader(prog, fragCompiled); + glDeleteShader(vertCompiled); + glDeleteShader(fragCompiled); + + GLint ok; + glGetProgramiv(prog, GL_LINK_STATUS, &ok); + if (dynamic) { + if (ok == GL_FALSE) { + logShaderError(prog, true); + return 0; + } + } else { + if (ok != GL_TRUE) + logShaderError(prog, true); + RASSERT(ok != GL_FALSE, "createProgram() failed! GL_LINK_STATUS not OK!"); + } + + return prog; +} + +GLuint CHyprOpenGLImpl::compileShader(const GLuint& type, std::string src, bool dynamic) { + auto shader = glCreateShader(type); + + auto shaderSource = src.c_str(); + + glShaderSource(shader, 1, (const GLchar**)&shaderSource, nullptr); + glCompileShader(shader); + + GLint ok; + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + + if (dynamic) { + if (ok == GL_FALSE) { + logShaderError(shader, false); + return 0; + } + } else { + if (ok != GL_TRUE) + logShaderError(shader, false); + RASSERT(ok != GL_FALSE, "compileShader() failed! GL_COMPILE_STATUS not OK!"); + } + + return shader; +} + +void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP rb, CFramebuffer* fb) { + m_RenderData.pMonitor = pMonitor; + +#ifndef GLES2 const GLenum RESETSTATUS = glGetGraphicsResetStatus(); if (RESETSTATUS != GL_NO_ERROR) { std::string errStr = ""; @@ -661,47 +667,46 @@ void CHyprOpenGLImpl::beginSimple(PHLMONITOR pMonitor, const CRegion& damage, SP RASSERT(false, "Aborting, glGetGraphicsResetStatus returned {}. Cannot continue until proper GPU reset handling is implemented.", errStr); return; } +#endif TRACY_GPU_ZONE("RenderBeginSimple"); const auto FBO = rb ? rb->getFB() : fb; - setViewport(0, 0, pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y); + glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); - m_renderData.projection = Mat3x3::outputProjection(pMonitor->m_pixelSize, HYPRUTILS_TRANSFORM_NORMAL); + m_RenderData.projection = Mat3x3::outputProjection(pMonitor->vecPixelSize, HYPRUTILS_TRANSFORM_NORMAL); - m_renderData.monitorProjection = Mat3x3::identity(); - if (pMonitor->m_transform != WL_OUTPUT_TRANSFORM_NORMAL) { - const Vector2D tfmd = pMonitor->m_transform % 2 == 1 ? Vector2D{FBO->m_size.y, FBO->m_size.x} : FBO->m_size; - m_renderData.monitorProjection.translate(FBO->m_size / 2.0).transform(Math::wlTransformToHyprutils(pMonitor->m_transform)).translate(-tfmd / 2.0); + m_RenderData.monitorProjection = Mat3x3::identity(); + if (pMonitor->transform != WL_OUTPUT_TRANSFORM_NORMAL) { + const Vector2D tfmd = pMonitor->transform % 2 == 1 ? Vector2D{FBO->m_vSize.y, FBO->m_vSize.x} : FBO->m_vSize; + m_RenderData.monitorProjection.translate(FBO->m_vSize / 2.0).transform(wlTransformToHyprutils(pMonitor->transform)).translate(-tfmd / 2.0); } - m_renderData.pCurrentMonData = &m_monitorRenderResources[pMonitor]; + m_RenderData.pCurrentMonData = &m_mMonitorRenderResources[pMonitor]; - if (!m_shadersInitialized) + if (!m_RenderData.pCurrentMonData->m_bShadersInitialized) initShaders(); - m_renderData.transformDamage = true; - m_renderData.damage.set(damage); - m_renderData.finalDamage.set(damage); + m_RenderData.damage.set(damage); + m_RenderData.finalDamage.set(damage); - m_fakeFrame = true; + m_bFakeFrame = true; - m_renderData.currentFB = FBO; + m_RenderData.currentFB = FBO; FBO->bind(); - m_offloadedFramebuffer = false; + m_bOffloadedFramebuffer = false; - m_renderData.mainFB = m_renderData.currentFB; - m_renderData.outFB = FBO; + m_RenderData.mainFB = m_RenderData.currentFB; + m_RenderData.outFB = FBO; - m_renderData.simplePass = true; - - pushMonitorTransformEnabled(false); + m_RenderData.simplePass = true; } -void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SP fb, std::optional finalDamage) { - m_renderData.pMonitor = pMonitor; +void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, CFramebuffer* fb, std::optional finalDamage) { + m_RenderData.pMonitor = pMonitor; +#ifndef GLES2 const GLenum RESETSTATUS = glGetGraphicsResetStatus(); if (RESETSTATUS != GL_NO_ERROR) { std::string errStr = ""; @@ -714,230 +719,361 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SPm_pixelSize.x, pMonitor->m_pixelSize.y); + glViewport(0, 0, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); - m_renderData.projection = Mat3x3::outputProjection(pMonitor->m_pixelSize, HYPRUTILS_TRANSFORM_NORMAL); + m_RenderData.projection = Mat3x3::outputProjection(pMonitor->vecPixelSize, HYPRUTILS_TRANSFORM_NORMAL); - m_renderData.monitorProjection = pMonitor->m_projMatrix; + m_RenderData.monitorProjection = pMonitor->projMatrix; - if (m_monitorRenderResources.contains(pMonitor) && - (!m_monitorRenderResources.at(pMonitor).offloadFB || m_monitorRenderResources.at(pMonitor).offloadFB->m_size != pMonitor->m_pixelSize)) + if (m_mMonitorRenderResources.contains(pMonitor) && m_mMonitorRenderResources.at(pMonitor).offloadFB.m_vSize != pMonitor->vecPixelSize) destroyMonitorResources(pMonitor); - m_renderData.pCurrentMonData = &m_monitorRenderResources[pMonitor]; + m_RenderData.pCurrentMonData = &m_mMonitorRenderResources[pMonitor]; - if (!m_shadersInitialized) + if (!m_RenderData.pCurrentMonData->m_bShadersInitialized) initShaders(); - const auto DRM_FORMAT = fb ? fb->m_drmFormat : pMonitor->m_output->state->state().drmFormat; - // ensure a framebuffer for the monitor exists - if (!m_renderData.pCurrentMonData->offloadFB || m_renderData.pCurrentMonData->offloadFB->m_size != pMonitor->m_pixelSize || - DRM_FORMAT != m_renderData.pCurrentMonData->offloadFB->m_drmFormat) { - m_renderData.pCurrentMonData->stencilTex = g_pHyprRenderer->createStencilTexture(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y); - m_renderData.pCurrentMonData->offloadFB = g_pHyprRenderer->createFB(); - m_renderData.pCurrentMonData->mirrorFB = g_pHyprRenderer->createFB(); - m_renderData.pCurrentMonData->mirrorSwapFB = g_pHyprRenderer->createFB(); + if (m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) { + m_RenderData.pCurrentMonData->stencilTex->allocate(); - m_renderData.pCurrentMonData->offloadFB->addStencil(m_renderData.pCurrentMonData->stencilTex); - m_renderData.pCurrentMonData->mirrorFB->addStencil(m_renderData.pCurrentMonData->stencilTex); - m_renderData.pCurrentMonData->mirrorSwapFB->addStencil(m_renderData.pCurrentMonData->stencilTex); + m_RenderData.pCurrentMonData->offloadFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat); + m_RenderData.pCurrentMonData->mirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat); + m_RenderData.pCurrentMonData->mirrorSwapFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat); - m_renderData.pCurrentMonData->offloadFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); - m_renderData.pCurrentMonData->mirrorFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); - m_renderData.pCurrentMonData->mirrorSwapFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, DRM_FORMAT); + m_RenderData.pCurrentMonData->offloadFB.addStencil(m_RenderData.pCurrentMonData->stencilTex); + m_RenderData.pCurrentMonData->mirrorFB.addStencil(m_RenderData.pCurrentMonData->stencilTex); + m_RenderData.pCurrentMonData->mirrorSwapFB.addStencil(m_RenderData.pCurrentMonData->stencilTex); } - const bool HAS_MIRROR_FB = m_renderData.pCurrentMonData->monitorMirrorFB && m_renderData.pCurrentMonData->monitorMirrorFB->isAllocated(); - const bool NEEDS_COPY_FB = needsACopyFB(m_renderData.pMonitor.lock()); + if (m_RenderData.pCurrentMonData->monitorMirrorFB.isAllocated() && m_RenderData.pMonitor->mirrors.empty()) + m_RenderData.pCurrentMonData->monitorMirrorFB.release(); - if (HAS_MIRROR_FB && !NEEDS_COPY_FB) - m_renderData.pCurrentMonData->monitorMirrorFB->release(); - else if (!HAS_MIRROR_FB && NEEDS_COPY_FB && m_renderData.pCurrentMonData->monitorMirrorFB) - m_renderData.pCurrentMonData->monitorMirrorFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, - m_renderData.pMonitor->m_output->state->state().drmFormat); + m_RenderData.damage.set(damage_); + m_RenderData.finalDamage.set(finalDamage.value_or(damage_)); - m_renderData.transformDamage = true; - if (HAS_MIRROR_FB != NEEDS_COPY_FB) { - // force full damage because the mirror fb will be empty - m_renderData.damage.set({0, 0, INT32_MAX, INT32_MAX}); - m_renderData.finalDamage.set(m_renderData.damage); - } else { - m_renderData.damage.set(damage_); - m_renderData.finalDamage.set(finalDamage.value_or(damage_)); - } + m_bFakeFrame = fb; - m_fakeFrame = fb; - - if (m_reloadScreenShader) { - m_reloadScreenShader = false; - static auto PSHADER = CConfigValue("decoration:screen_shader"); + if (m_bReloadScreenShader) { + m_bReloadScreenShader = false; + static auto PSHADER = CConfigValue("decoration:screen_shader"); applyScreenShader(*PSHADER); } - m_renderData.pCurrentMonData->offloadFB->bind(); - m_renderData.currentFB = m_renderData.pCurrentMonData->offloadFB; - m_offloadedFramebuffer = true; + m_RenderData.pCurrentMonData->offloadFB.bind(); + m_RenderData.currentFB = &m_RenderData.pCurrentMonData->offloadFB; + m_bOffloadedFramebuffer = true; - m_renderData.mainFB = m_renderData.currentFB; - m_renderData.outFB = fb ? fb : g_pHyprRenderer->getCurrentRBO()->getFB(); - - pushMonitorTransformEnabled(false); + m_RenderData.mainFB = m_RenderData.currentFB; + m_RenderData.outFB = fb ? fb : g_pHyprRenderer->getCurrentRBO()->getFB(); } void CHyprOpenGLImpl::end() { - static auto PZOOMDISABLEAA = CConfigValue("cursor:zoom_disable_aa"); + static auto PZOOMRIGID = CConfigValue("cursor:zoom_rigid"); TRACY_GPU_ZONE("RenderEnd"); - m_renderData.currentWindow.reset(); - m_renderData.surface.reset(); - m_renderData.currentLS.reset(); - m_renderData.clipBox = {}; - m_renderData.clipRegion.clear(); - // end the render, copy the data to the main framebuffer - if LIKELY (m_offloadedFramebuffer) { - m_renderData.damage = m_renderData.finalDamage; - pushMonitorTransformEnabled(true); + if (m_bOffloadedFramebuffer) { + m_RenderData.damage = m_RenderData.finalDamage; + m_bEndFrame = true; - CBox monbox = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; + CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; - if LIKELY (g_pHyprRenderer->m_renderMode == RENDER_MODE_NORMAL && m_renderData.mouseZoomFactor == 1.0f) - m_renderData.pMonitor->m_zoomController.m_resetCameraState = true; - m_renderData.pMonitor->m_zoomController.applyZoomTransform(monbox, m_renderData); + if (m_RenderData.mouseZoomFactor != 1.f) { + const auto ZOOMCENTER = m_RenderData.mouseZoomUseMouse ? + (g_pInputManager->getMouseCoordsInternal() - m_RenderData.pMonitor->vecPosition) * m_RenderData.pMonitor->scale : + m_RenderData.pMonitor->vecTransformedSize / 2.f; - m_applyFinalShader = !m_renderData.blockScreenShader; - if UNLIKELY (m_renderData.mouseZoomFactor != 1.F && m_renderData.mouseZoomUseMouse && *PZOOMDISABLEAA) - m_renderData.useNearestNeighbor = true; + monbox.translate(-ZOOMCENTER).scale(m_RenderData.mouseZoomFactor).translate(*PZOOMRIGID ? m_RenderData.pMonitor->vecTransformedSize / 2.0 : ZOOMCENTER); + + if (monbox.x > 0) + monbox.x = 0; + if (monbox.y > 0) + monbox.y = 0; + if (monbox.x + monbox.width < m_RenderData.pMonitor->vecTransformedSize.x) + monbox.x = m_RenderData.pMonitor->vecTransformedSize.x - monbox.width; + if (monbox.y + monbox.height < m_RenderData.pMonitor->vecTransformedSize.y) + monbox.y = m_RenderData.pMonitor->vecTransformedSize.y - monbox.height; + } + + m_bApplyFinalShader = !m_RenderData.blockScreenShader; + if (m_RenderData.mouseZoomUseMouse) + m_RenderData.useNearestNeighbor = true; // copy the damaged areas into the mirror buffer - // we can't use the offloadFB for mirroring / ss, as it contains artifacts from blurring - if UNLIKELY (needsACopyFB(m_renderData.pMonitor.lock()) && !m_fakeFrame) + // we can't use the offloadFB for mirroring, as it contains artifacts from blurring + if (!m_RenderData.pMonitor->mirrors.empty() && !m_bFakeFrame) saveBufferForMirror(monbox); - m_renderData.outFB->bind(); + m_RenderData.outFB->bind(); blend(false); - const auto PRIMITIVE_BLOCKED = - m_finalScreenShader->program() >= 1 || g_pHyprRenderer->m_crashingInProgress || m_renderData.pMonitor->m_imageDescription->value() != SImageDescription{}; - - if LIKELY (!PRIMITIVE_BLOCKED || g_pHyprRenderer->m_renderMode != RENDER_MODE_NORMAL) - renderTexturePrimitive(m_renderData.pCurrentMonData->offloadFB->getTexture(), monbox); - else // we need to use renderTexture if we do any CM whatsoever. - renderTexture(m_renderData.pCurrentMonData->offloadFB->getTexture(), monbox, {.finalMonitorCM = true}); + if (m_sFinalScreenShader.program < 1 && !g_pHyprRenderer->m_bCrashingInProgress) + renderTexturePrimitive(m_RenderData.pCurrentMonData->offloadFB.getTexture(), monbox); + else + renderTexture(m_RenderData.pCurrentMonData->offloadFB.getTexture(), monbox, 1.f); blend(true); - m_renderData.useNearestNeighbor = false; - m_applyFinalShader = false; - popMonitorTransformEnabled(); - } - - // invalidate our render FBs to signal to the driver we don't need them anymore - if (m_renderData.pCurrentMonData->mirrorFB) { - m_renderData.pCurrentMonData->mirrorFB->bind(); - GLFB(m_renderData.pCurrentMonData->mirrorFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (m_renderData.pCurrentMonData->mirrorSwapFB) { - m_renderData.pCurrentMonData->mirrorSwapFB->bind(); - GLFB(m_renderData.pCurrentMonData->mirrorSwapFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (m_renderData.pCurrentMonData->offloadFB) { - m_renderData.pCurrentMonData->offloadFB->bind(); - GLFB(m_renderData.pCurrentMonData->offloadFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); - } - if (m_renderData.pCurrentMonData->offMainFB) { - m_renderData.pCurrentMonData->offMainFB->bind(); - GLFB(m_renderData.pCurrentMonData->offMainFB)->invalidate({GL_STENCIL_ATTACHMENT, GL_COLOR_ATTACHMENT0}); + m_RenderData.useNearestNeighbor = false; + m_bApplyFinalShader = false; + m_bEndFrame = false; } // reset our data - m_renderData.pMonitor.reset(); - m_renderData.mouseZoomFactor = 1.f; - m_renderData.mouseZoomUseMouse = true; - m_renderData.blockScreenShader = false; - m_renderData.currentFB = nullptr; - m_renderData.mainFB = nullptr; - m_renderData.outFB = nullptr; - popMonitorTransformEnabled(); + m_RenderData.pMonitor.reset(); + m_RenderData.mouseZoomFactor = 1.f; + m_RenderData.mouseZoomUseMouse = true; + m_RenderData.blockScreenShader = false; + m_RenderData.currentFB = nullptr; + m_RenderData.mainFB = nullptr; + m_RenderData.outFB = nullptr; // if we dropped to offMain, release it now. // if there is a plugin constantly using it, this might be a bit slow, - // but I haven't seen a single plugin yet use these, so it's better to drop a bit of vram. - if UNLIKELY (m_renderData.pCurrentMonData->offMainFB && m_renderData.pCurrentMonData->offMainFB->isAllocated()) - m_renderData.pCurrentMonData->offMainFB->release(); + // but I havent seen a single plugin yet use these, so it's better to drop a bit of vram. + if (m_RenderData.pCurrentMonData->offMainFB.isAllocated()) + m_RenderData.pCurrentMonData->offMainFB.release(); - static const auto GLDEBUG = CConfigValue("debug:gl_debugging"); + // check for gl errors + const GLenum ERR = glGetError(); - if (*GLDEBUG) { - // check for gl errors - const GLenum ERR = glGetError(); - - if UNLIKELY (ERR == GL_CONTEXT_LOST) /* We don't have infra to recover from this */ - RASSERT(false, "glGetError at Opengl::end() returned GL_CONTEXT_LOST. Cannot continue until proper GPU reset handling is implemented."); - } -} - -bool CHyprOpenGLImpl::needsACopyFB(PHLMONITOR mon) { - return !mon->m_mirrors.empty() || Screenshare::mgr()->isOutputBeingSSd(mon); +#ifdef GLES2 + if (ERR == GL_CONTEXT_LOST_KHR) /* We don't have infra to recover from this */ +#else + if (ERR == GL_CONTEXT_LOST) /* We don't have infra to recover from this */ +#endif + RASSERT(false, "glGetError at Opengl::end() returned GL_CONTEXT_LOST. Cannot continue until proper GPU reset handling is implemented."); } void CHyprOpenGLImpl::setDamage(const CRegion& damage_, std::optional finalDamage) { - m_renderData.damage.set(damage_); - m_renderData.finalDamage.set(finalDamage.value_or(damage_)); + m_RenderData.damage.set(damage_); + m_RenderData.finalDamage.set(finalDamage.value_or(damage_)); } -static const std::vector SHADER_INCLUDES = { - "defines.h", "constants.h", "cm_helpers.glsl", "rounding.glsl", "CM.glsl", "tonemap.glsl", "gain.glsl", - "border.glsl", "shadow.glsl", "blurprepare.glsl", "blur1.glsl", "blur2.glsl", "blurFinish.glsl", -}; +void CHyprOpenGLImpl::initShaders() { + GLuint prog = createProgram(QUADVERTSRC, QUADFRAGSRC); + m_RenderData.pCurrentMonData->m_shQUAD.program = prog; + m_RenderData.pCurrentMonData->m_shQUAD.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shQUAD.color = glGetUniformLocation(prog, "color"); + m_RenderData.pCurrentMonData->m_shQUAD.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shQUAD.topLeft = glGetUniformLocation(prog, "topLeft"); + m_RenderData.pCurrentMonData->m_shQUAD.fullSize = glGetUniformLocation(prog, "fullSize"); + m_RenderData.pCurrentMonData->m_shQUAD.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shQUAD.roundingPower = glGetUniformLocation(prog, "roundingPower"); -// order matters, see ePreparedFragmentShader -const std::array FRAG_SHADERS = { - "quad.frag", "passthru.frag", "rgbamatte.frag", "ext.frag", "blur1.frag", "blur2.frag", - "blurprepare.frag", "blurfinish.frag", "shadow.frag", "surface.frag", "border.frag", "glitch.frag", -}; - -bool CHyprOpenGLImpl::initShaders(const std::string& path) { - auto shaders = makeShared(); - static const auto PCM = CConfigValue("render:cm_enabled"); - - try { - auto shaderLoader = makeUnique(SHADER_INCLUDES, FRAG_SHADERS, path); - - shaders->TEXVERTSRC = shaderLoader->process("tex300.vert"); - shaders->TEXVERTSRC320 = shaderLoader->process("tex320.vert"); - - m_cmSupported = *PCM; - - g_pShaderLoader = std::move(shaderLoader); - - } catch (const std::exception& e) { - if (!m_shadersInitialized) - throw e; - - Log::logger->log(Log::ERR, "Shaders update failed: {}", e.what()); - return false; +#ifndef GLES2 + prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM, true); + m_bCMSupported = prog > 0; + if (m_bCMSupported) { + m_RenderData.pCurrentMonData->m_shCM.program = prog; + m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType"); + m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF"); + m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF"); + m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries"); + m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries"); + m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance"); + m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance"); + m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance"); + m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation"); + m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier"); + m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte"); + m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha"); + m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte"); + m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque"); + m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha"); + m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue"); + m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft"); + m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize"); + m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower"); + m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint"); + m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint"); + m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte"); + } else { + Debug::log( + ERR, + "WARNING: CM Shader failed compiling, color management will not work. It's likely because your GPU is an old piece of garbage, don't file bug reports about this!"); } +#endif - m_shaders = shaders; - m_shadersInitialized = true; + prog = createProgram(TEXVERTSRC, TEXFRAGSRCRGBA); + m_RenderData.pCurrentMonData->m_shRGBA.program = prog; + m_RenderData.pCurrentMonData->m_shRGBA.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shRGBA.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shRGBA.alphaMatte = glGetUniformLocation(prog, "texMatte"); + m_RenderData.pCurrentMonData->m_shRGBA.alpha = glGetUniformLocation(prog, "alpha"); + m_RenderData.pCurrentMonData->m_shRGBA.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shRGBA.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte"); + m_RenderData.pCurrentMonData->m_shRGBA.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shRGBA.discardOpaque = glGetUniformLocation(prog, "discardOpaque"); + m_RenderData.pCurrentMonData->m_shRGBA.discardAlpha = glGetUniformLocation(prog, "discardAlpha"); + m_RenderData.pCurrentMonData->m_shRGBA.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue"); + m_RenderData.pCurrentMonData->m_shRGBA.topLeft = glGetUniformLocation(prog, "topLeft"); + m_RenderData.pCurrentMonData->m_shRGBA.fullSize = glGetUniformLocation(prog, "fullSize"); + m_RenderData.pCurrentMonData->m_shRGBA.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shRGBA.roundingPower = glGetUniformLocation(prog, "roundingPower"); + m_RenderData.pCurrentMonData->m_shRGBA.applyTint = glGetUniformLocation(prog, "applyTint"); + m_RenderData.pCurrentMonData->m_shRGBA.tint = glGetUniformLocation(prog, "tint"); + m_RenderData.pCurrentMonData->m_shRGBA.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte"); - Log::logger->log(Log::DEBUG, "Shaders initialized successfully."); - return true; + prog = createProgram(TEXVERTSRC, TEXFRAGSRCRGBAPASSTHRU); + m_RenderData.pCurrentMonData->m_shPASSTHRURGBA.program = prog; + m_RenderData.pCurrentMonData->m_shPASSTHRURGBA.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shPASSTHRURGBA.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shPASSTHRURGBA.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shPASSTHRURGBA.posAttrib = glGetAttribLocation(prog, "pos"); + + prog = createProgram(TEXVERTSRC, TEXFRAGSRCRGBAMATTE); + m_RenderData.pCurrentMonData->m_shMATTE.program = prog; + m_RenderData.pCurrentMonData->m_shMATTE.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shMATTE.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shMATTE.alphaMatte = glGetUniformLocation(prog, "texMatte"); + m_RenderData.pCurrentMonData->m_shMATTE.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shMATTE.posAttrib = glGetAttribLocation(prog, "pos"); + + prog = createProgram(TEXVERTSRC, FRAGGLITCH); + m_RenderData.pCurrentMonData->m_shGLITCH.program = prog; + m_RenderData.pCurrentMonData->m_shGLITCH.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shGLITCH.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shGLITCH.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shGLITCH.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shGLITCH.distort = glGetUniformLocation(prog, "distort"); + m_RenderData.pCurrentMonData->m_shGLITCH.time = glGetUniformLocation(prog, "time"); + m_RenderData.pCurrentMonData->m_shGLITCH.fullSize = glGetUniformLocation(prog, "screenSize"); + + prog = createProgram(TEXVERTSRC, TEXFRAGSRCRGBX); + m_RenderData.pCurrentMonData->m_shRGBX.program = prog; + m_RenderData.pCurrentMonData->m_shRGBX.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shRGBX.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shRGBX.alpha = glGetUniformLocation(prog, "alpha"); + m_RenderData.pCurrentMonData->m_shRGBX.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shRGBX.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shRGBX.discardOpaque = glGetUniformLocation(prog, "discardOpaque"); + m_RenderData.pCurrentMonData->m_shRGBX.discardAlpha = glGetUniformLocation(prog, "discardAlpha"); + m_RenderData.pCurrentMonData->m_shRGBX.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue"); + m_RenderData.pCurrentMonData->m_shRGBX.topLeft = glGetUniformLocation(prog, "topLeft"); + m_RenderData.pCurrentMonData->m_shRGBX.fullSize = glGetUniformLocation(prog, "fullSize"); + m_RenderData.pCurrentMonData->m_shRGBX.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shRGBX.roundingPower = glGetUniformLocation(prog, "roundingPower"); + m_RenderData.pCurrentMonData->m_shRGBX.applyTint = glGetUniformLocation(prog, "applyTint"); + m_RenderData.pCurrentMonData->m_shRGBX.tint = glGetUniformLocation(prog, "tint"); + + prog = createProgram(TEXVERTSRC, TEXFRAGSRCEXT); + m_RenderData.pCurrentMonData->m_shEXT.program = prog; + m_RenderData.pCurrentMonData->m_shEXT.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shEXT.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shEXT.alpha = glGetUniformLocation(prog, "alpha"); + m_RenderData.pCurrentMonData->m_shEXT.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shEXT.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shEXT.discardOpaque = glGetUniformLocation(prog, "discardOpaque"); + m_RenderData.pCurrentMonData->m_shEXT.discardAlpha = glGetUniformLocation(prog, "discardAlpha"); + m_RenderData.pCurrentMonData->m_shEXT.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue"); + m_RenderData.pCurrentMonData->m_shEXT.topLeft = glGetUniformLocation(prog, "topLeft"); + m_RenderData.pCurrentMonData->m_shEXT.fullSize = glGetUniformLocation(prog, "fullSize"); + m_RenderData.pCurrentMonData->m_shEXT.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shEXT.roundingPower = glGetUniformLocation(prog, "roundingPower"); + m_RenderData.pCurrentMonData->m_shEXT.applyTint = glGetUniformLocation(prog, "applyTint"); + m_RenderData.pCurrentMonData->m_shEXT.tint = glGetUniformLocation(prog, "tint"); + + prog = createProgram(TEXVERTSRC, FRAGBLUR1); + m_RenderData.pCurrentMonData->m_shBLUR1.program = prog; + m_RenderData.pCurrentMonData->m_shBLUR1.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shBLUR1.alpha = glGetUniformLocation(prog, "alpha"); + m_RenderData.pCurrentMonData->m_shBLUR1.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shBLUR1.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shBLUR1.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shBLUR1.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shBLUR1.halfpixel = glGetUniformLocation(prog, "halfpixel"); + m_RenderData.pCurrentMonData->m_shBLUR1.passes = glGetUniformLocation(prog, "passes"); + m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy = glGetUniformLocation(prog, "vibrancy"); + m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy_darkness = glGetUniformLocation(prog, "vibrancy_darkness"); + + prog = createProgram(TEXVERTSRC, FRAGBLUR2); + m_RenderData.pCurrentMonData->m_shBLUR2.program = prog; + m_RenderData.pCurrentMonData->m_shBLUR2.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shBLUR2.alpha = glGetUniformLocation(prog, "alpha"); + m_RenderData.pCurrentMonData->m_shBLUR2.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shBLUR2.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shBLUR2.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shBLUR2.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shBLUR2.halfpixel = glGetUniformLocation(prog, "halfpixel"); + + prog = createProgram(TEXVERTSRC, FRAGBLURPREPARE); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.program = prog; + m_RenderData.pCurrentMonData->m_shBLURPREPARE.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.contrast = glGetUniformLocation(prog, "contrast"); + m_RenderData.pCurrentMonData->m_shBLURPREPARE.brightness = glGetUniformLocation(prog, "brightness"); + + prog = createProgram(TEXVERTSRC, FRAGBLURFINISH); + m_RenderData.pCurrentMonData->m_shBLURFINISH.program = prog; + m_RenderData.pCurrentMonData->m_shBLURFINISH.tex = glGetUniformLocation(prog, "tex"); + m_RenderData.pCurrentMonData->m_shBLURFINISH.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shBLURFINISH.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shBLURFINISH.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shBLURFINISH.brightness = glGetUniformLocation(prog, "brightness"); + m_RenderData.pCurrentMonData->m_shBLURFINISH.noise = glGetUniformLocation(prog, "noise"); + + prog = createProgram(QUADVERTSRC, FRAGSHADOW); + m_RenderData.pCurrentMonData->m_shSHADOW.program = prog; + m_RenderData.pCurrentMonData->m_shSHADOW.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shSHADOW.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shSHADOW.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shSHADOW.topLeft = glGetUniformLocation(prog, "topLeft"); + m_RenderData.pCurrentMonData->m_shSHADOW.bottomRight = glGetUniformLocation(prog, "bottomRight"); + m_RenderData.pCurrentMonData->m_shSHADOW.fullSize = glGetUniformLocation(prog, "fullSize"); + m_RenderData.pCurrentMonData->m_shSHADOW.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shSHADOW.roundingPower = glGetUniformLocation(prog, "roundingPower"); + m_RenderData.pCurrentMonData->m_shSHADOW.range = glGetUniformLocation(prog, "range"); + m_RenderData.pCurrentMonData->m_shSHADOW.shadowPower = glGetUniformLocation(prog, "shadowPower"); + m_RenderData.pCurrentMonData->m_shSHADOW.color = glGetUniformLocation(prog, "color"); + + prog = createProgram(QUADVERTSRC, FRAGBORDER1); + m_RenderData.pCurrentMonData->m_shBORDER1.program = prog; + m_RenderData.pCurrentMonData->m_shBORDER1.proj = glGetUniformLocation(prog, "proj"); + m_RenderData.pCurrentMonData->m_shBORDER1.thick = glGetUniformLocation(prog, "thick"); + m_RenderData.pCurrentMonData->m_shBORDER1.posAttrib = glGetAttribLocation(prog, "pos"); + m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib = glGetAttribLocation(prog, "texcoord"); + m_RenderData.pCurrentMonData->m_shBORDER1.topLeft = glGetUniformLocation(prog, "topLeft"); + m_RenderData.pCurrentMonData->m_shBORDER1.bottomRight = glGetUniformLocation(prog, "bottomRight"); + m_RenderData.pCurrentMonData->m_shBORDER1.fullSize = glGetUniformLocation(prog, "fullSize"); + m_RenderData.pCurrentMonData->m_shBORDER1.fullSizeUntransformed = glGetUniformLocation(prog, "fullSizeUntransformed"); + m_RenderData.pCurrentMonData->m_shBORDER1.radius = glGetUniformLocation(prog, "radius"); + m_RenderData.pCurrentMonData->m_shBORDER1.radiusOuter = glGetUniformLocation(prog, "radiusOuter"); + m_RenderData.pCurrentMonData->m_shBORDER1.roundingPower = glGetUniformLocation(prog, "roundingPower"); + m_RenderData.pCurrentMonData->m_shBORDER1.gradient = glGetUniformLocation(prog, "gradient"); + m_RenderData.pCurrentMonData->m_shBORDER1.gradient2 = glGetUniformLocation(prog, "gradient2"); + m_RenderData.pCurrentMonData->m_shBORDER1.gradientLength = glGetUniformLocation(prog, "gradientLength"); + m_RenderData.pCurrentMonData->m_shBORDER1.gradient2Length = glGetUniformLocation(prog, "gradient2Length"); + m_RenderData.pCurrentMonData->m_shBORDER1.angle = glGetUniformLocation(prog, "angle"); + m_RenderData.pCurrentMonData->m_shBORDER1.angle2 = glGetUniformLocation(prog, "angle2"); + m_RenderData.pCurrentMonData->m_shBORDER1.gradientLerp = glGetUniformLocation(prog, "gradientLerp"); + m_RenderData.pCurrentMonData->m_shBORDER1.alpha = glGetUniformLocation(prog, "alpha"); + + m_RenderData.pCurrentMonData->m_bShadersInitialized = true; + + Debug::log(LOG, "Shaders initialized successfully."); } void CHyprOpenGLImpl::applyScreenShader(const std::string& path) { static auto PDT = CConfigValue("debug:damage_tracking"); - m_finalScreenShader->destroy(); + m_sFinalScreenShader.destroy(); - if (path.empty() || path == STRVAL_EMPTY) + if (path == "" || path == STRVAL_EMPTY) return; std::ifstream infile(absolutePath(path, g_pConfigManager->getMainConfigPath())); @@ -949,106 +1085,80 @@ void CHyprOpenGLImpl::applyScreenShader(const std::string& path) { std::string fragmentShader((std::istreambuf_iterator(infile)), (std::istreambuf_iterator())); - if (!m_finalScreenShader->createProgram( // - fragmentShader.starts_with("#version 320 es") // do not break existing custom shaders - ? - m_shaders->TEXVERTSRC320 : - m_shaders->TEXVERTSRC, - fragmentShader, true)) { + m_sFinalScreenShader.program = createProgram(fragmentShader.starts_with("#version 320 es") ? TEXVERTSRC320 : TEXVERTSRC, fragmentShader, true); + + if (!m_sFinalScreenShader.program) { // Error will have been sent by now by the underlying cause return; } - if (m_finalScreenShader->getUniformLocation(SHADER_TIME) != -1) - m_finalScreenShader->setInitialTime(m_globalTimer.getSeconds()); - - static auto uniformRequireNoDamage = [this](eShaderUniform uniform, const std::string& name) { - if (*PDT == 0) - return; - if (m_finalScreenShader->getUniformLocation(uniform) == -1) - return; - - // The screen shader uses the uniform + m_sFinalScreenShader.proj = glGetUniformLocation(m_sFinalScreenShader.program, "proj"); + m_sFinalScreenShader.tex = glGetUniformLocation(m_sFinalScreenShader.program, "tex"); + m_sFinalScreenShader.time = glGetUniformLocation(m_sFinalScreenShader.program, "time"); + if (m_sFinalScreenShader.time != -1) + m_sFinalScreenShader.initialTime = m_tGlobalTimer.getSeconds(); + m_sFinalScreenShader.wl_output = glGetUniformLocation(m_sFinalScreenShader.program, "wl_output"); + m_sFinalScreenShader.fullSize = glGetUniformLocation(m_sFinalScreenShader.program, "screen_size"); + if (m_sFinalScreenShader.fullSize == -1) + m_sFinalScreenShader.fullSize = glGetUniformLocation(m_sFinalScreenShader.program, "screenSize"); + if (m_sFinalScreenShader.time != -1 && *PDT != 0 && !g_pHyprRenderer->m_bCrashingInProgress) { + // The screen shader uses the "time" uniform // Since the screen shader could change every frame, damage tracking *needs* to be disabled - g_pConfigManager->addParseError(std::format("Screen shader: Screen shader uses uniform '{}', which requires debug:damage_tracking to be switched off.\n" - "WARNING:(Disabling damage tracking will *massively* increase GPU utilization!", - name)); - }; - - // Allow glitch shader to use time uniform whighout damage tracking - if (!g_pHyprRenderer->m_crashingInProgress) - uniformRequireNoDamage(SHADER_TIME, "time"); - - uniformRequireNoDamage(SHADER_POINTER, "pointer_position"); - uniformRequireNoDamage(SHADER_POINTER_PRESSED_POSITIONS, "pointer_pressed_positions"); - uniformRequireNoDamage(SHADER_POINTER_PRESSED_TIMES, "pointer_pressed_times"); - uniformRequireNoDamage(SHADER_POINTER_PRESSED_KILLED, "pointer_pressed_killed"); - uniformRequireNoDamage(SHADER_POINTER_PRESSED_TOUCHED, "pointer_pressed_touched"); - uniformRequireNoDamage(SHADER_POINTER_LAST_ACTIVE, "pointer_last_active"); - uniformRequireNoDamage(SHADER_POINTER_HIDDEN, "pointer_hidden"); - uniformRequireNoDamage(SHADER_POINTER_KILLING, "pointer_killing"); - uniformRequireNoDamage(SHADER_POINTER_SHAPE, "pointer_shape"); - uniformRequireNoDamage(SHADER_POINTER_SHAPE_PREVIOUS, "pointer_shape_previous"); + g_pConfigManager->addParseError("Screen shader: Screen shader uses uniform 'time', which requires debug:damage_tracking to be switched off.\n" + "WARNING: Disabling damage tracking will *massively* increase GPU utilization!"); + } + m_sFinalScreenShader.texAttrib = glGetAttribLocation(m_sFinalScreenShader.program, "texcoord"); + m_sFinalScreenShader.posAttrib = glGetAttribLocation(m_sFinalScreenShader.program, "pos"); } void CHyprOpenGLImpl::clear(const CHyprColor& color) { - RASSERT(m_renderData.pMonitor, "Tried to render without begin()!"); + RASSERT(m_RenderData.pMonitor, "Tried to render without begin()!"); TRACY_GPU_ZONE("RenderClear"); - GLCALL(glClearColor(color.r, color.g, color.b, color.a)); + glClearColor(color.r, color.g, color.b, color.a); - if (!m_renderData.damage.empty()) { - m_renderData.damage.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + if (!m_RenderData.damage.empty()) { + for (auto const& RECT : m_RenderData.damage.getRects()) { + scissor(&RECT); glClear(GL_COLOR_BUFFER_BIT); - }); + } } + + scissor(nullptr); } void CHyprOpenGLImpl::blend(bool enabled) { if (enabled) { - setCapStatus(GL_BLEND, true); - GLCALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); // everything is premultiplied + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // everything is premultiplied } else - setCapStatus(GL_BLEND, false); + glDisable(GL_BLEND); - m_blend = enabled; + m_bBlend = enabled; } void CHyprOpenGLImpl::scissor(const CBox& originalBox, bool transform) { - RASSERT(m_renderData.pMonitor, "Tried to scissor without begin()!"); - - // only call glScissor if the box has changed - static CBox m_lastScissorBox = {}; + RASSERT(m_RenderData.pMonitor, "Tried to scissor without begin()!"); if (transform) { CBox box = originalBox; - const auto TR = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); - box.transform(TR, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y); - - if (box != m_lastScissorBox) { - GLCALL(glScissor(box.x, box.y, box.width, box.height)); - m_lastScissorBox = box; - } - - setCapStatus(GL_SCISSOR_TEST, true); + const auto TR = wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)); + box.transform(TR, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y); + glScissor(box.x, box.y, box.width, box.height); + glEnable(GL_SCISSOR_TEST); return; } - if (originalBox != m_lastScissorBox) { - GLCALL(glScissor(originalBox.x, originalBox.y, originalBox.width, originalBox.height)); - m_lastScissorBox = originalBox; - } - - setCapStatus(GL_SCISSOR_TEST, true); + glScissor(originalBox.x, originalBox.y, originalBox.width, originalBox.height); + glEnable(GL_SCISSOR_TEST); } void CHyprOpenGLImpl::scissor(const pixman_box32* pBox, bool transform) { - RASSERT(m_renderData.pMonitor, "Tried to scissor without begin()!"); + RASSERT(m_RenderData.pMonitor, "Tried to scissor without begin()!"); if (!pBox) { - setCapStatus(GL_SCISSOR_TEST, false); + glDisable(GL_SCISSOR_TEST); return; } @@ -1062,628 +1172,509 @@ void CHyprOpenGLImpl::scissor(const int x, const int y, const int w, const int h scissor(box, transform); } -void CHyprOpenGLImpl::renderRect(const CBox& box, const CHyprColor& col, SRectRenderData data) { - if (!data.damage) - data.damage = &m_renderData.damage; - - if (data.blur) - renderRectWithBlurInternal(box, col, data); - else - renderRectWithDamageInternal(box, col, data); +void CHyprOpenGLImpl::renderRect(const CBox& box, const CHyprColor& col, int round, float roundingPower) { + if (!m_RenderData.damage.empty()) + renderRectWithDamage(box, col, m_RenderData.damage, round, roundingPower); } -void CHyprOpenGLImpl::renderRectWithBlurInternal(const CBox& box, const CHyprColor& col, const SRectRenderData& data) { - if (data.damage->empty()) +void CHyprOpenGLImpl::renderRectWithBlur(const CBox& box, const CHyprColor& col, int round, float roundingPower, float blurA, bool xray) { + if (m_RenderData.damage.empty()) return; - CRegion damage{m_renderData.damage}; + CRegion damage{m_RenderData.damage}; damage.intersect(box); - auto POUTFB = data.xray ? m_renderData.pCurrentMonData->blurFB : blurMainFramebufferWithDamage(data.blurA, &damage); + CFramebuffer* POUTFB = xray ? &m_RenderData.pCurrentMonData->blurFB : blurMainFramebufferWithDamage(blurA, &damage); - m_renderData.currentFB->bind(); + m_RenderData.currentFB->bind(); - CBox MONITORBOX = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; - pushMonitorTransformEnabled(true); - const auto SAVEDRENDERMODIF = m_renderData.renderModif; - m_renderData.renderModif = {}; // fix shit - renderTexture(POUTFB->getTexture(), MONITORBOX, - STextureRenderData{.damage = &damage, .a = data.blurA, .round = data.round, .roundingPower = 2.F, .allowCustomUV = false, .allowDim = false, .noAA = false}); - popMonitorTransformEnabled(); - m_renderData.renderModif = SAVEDRENDERMODIF; + // make a stencil for rounded corners to work with blur + scissor(nullptr); // allow the entire window and stencil to render + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); - renderRectWithDamageInternal(box, col, data); + glEnable(GL_STENCIL_TEST); + + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + renderRect(box, CHyprColor(0, 0, 0, 0), round, roundingPower); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + scissor(box); + CBox MONITORBOX = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; + m_bEndFrame = true; // fix transformed + const auto SAVEDRENDERMODIF = m_RenderData.renderModif; + m_RenderData.renderModif = {}; // fix shit + renderTextureInternalWithDamage(POUTFB->getTexture(), MONITORBOX, blurA, damage, 0, 2.0f, false, false, false); + m_bEndFrame = false; + m_RenderData.renderModif = SAVEDRENDERMODIF; + + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glDisable(GL_STENCIL_TEST); + glStencilMask(0xFF); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + scissor(nullptr); + + renderRectWithDamage(box, col, m_RenderData.damage, round, roundingPower); } -void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprColor& col, const SRectRenderData& data) { +void CHyprOpenGLImpl::renderRectWithDamage(const CBox& box, const CHyprColor& col, const CRegion& damage, int round, float roundingPower) { RASSERT((box.width > 0 && box.height > 0), "Tried to render rect with width/height < 0!"); - RASSERT(m_renderData.pMonitor, "Tried to render rect without begin()!"); + RASSERT(m_RenderData.pMonitor, "Tried to render rect without begin()!"); TRACY_GPU_ZONE("RenderRectWithDamage"); CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); + m_RenderData.renderModif.applyToBox(newBox); - Mat3x3 matrix = m_renderData.monitorProjection.projectBox( - newBox, Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)), newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + Mat3x3 matrix = m_RenderData.monitorProjection.projectBox( + newBox, wlTransformToHyprutils(invertTransform(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform)), newBox.rot); + Mat3x3 glMatrix = m_RenderData.projection.copy().multiply(matrix); - auto shader = useShader(getShaderVariant(SH_FRAG_QUAD, data.round > 0 ? SH_FEAT_ROUNDING : 0)); - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); + glUseProgram(m_RenderData.pCurrentMonData->m_shQUAD.program); + +#ifndef GLES2 + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shQUAD.proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shQUAD.proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif // premultiply the color as well as we don't work with straight alpha - shader->setUniformFloat4(SHADER_COLOR, col.r * col.a, col.g * col.a, col.b * col.a, col.a); + glUniform4f(m_RenderData.pCurrentMonData->m_shQUAD.color, col.r * col.a, col.g * col.a, col.b * col.a, col.a); CBox transformedBox = box; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); + transformedBox.transform(wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)), m_RenderData.pMonitor->vecTransformedSize.x, + m_RenderData.pMonitor->vecTransformedSize.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); // Rounded corners - shader->setUniformFloat2(SHADER_TOP_LEFT, sc(TOPLEFT.x), sc(TOPLEFT.y)); - shader->setUniformFloat2(SHADER_FULL_SIZE, sc(FULLSIZE.x), sc(FULLSIZE.y)); - shader->setUniformFloat(SHADER_RADIUS, data.round); - shader->setUniformFloat(SHADER_ROUNDING_POWER, data.roundingPower); + glUniform2f(m_RenderData.pCurrentMonData->m_shQUAD.topLeft, (float)TOPLEFT.x, (float)TOPLEFT.y); + glUniform2f(m_RenderData.pCurrentMonData->m_shQUAD.fullSize, (float)FULLSIZE.x, (float)FULLSIZE.y); + glUniform1f(m_RenderData.pCurrentMonData->m_shQUAD.radius, round); + glUniform1f(m_RenderData.pCurrentMonData->m_shQUAD.roundingPower, roundingPower); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shQUAD.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - if (m_renderData.clipBox.width != 0 && m_renderData.clipBox.height != 0) { - CRegion damageClip{m_renderData.clipBox.x, m_renderData.clipBox.y, m_renderData.clipBox.width, m_renderData.clipBox.height}; - damageClip.intersect(*data.damage); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shQUAD.posAttrib); + + if (m_RenderData.clipBox.width != 0 && m_RenderData.clipBox.height != 0) { + CRegion damageClip{m_RenderData.clipBox.x, m_RenderData.clipBox.y, m_RenderData.clipBox.width, m_RenderData.clipBox.height}; + damageClip.intersect(damage); if (!damageClip.empty()) { - damageClip.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + for (auto const& RECT : damageClip.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } } else { - data.damage->forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + for (auto const& RECT : damage.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } - glBindVertexArray(0); - scissor(nullptr); -} - -void CHyprOpenGLImpl::renderTexture(SP tex, const CBox& box, STextureRenderData data) { - RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - - if (!data.damage) { - if (m_renderData.damage.empty()) - return; - - data.damage = &m_renderData.damage; - } - - if (data.blur) - renderTextureWithBlurInternal(tex, box, data); - else - renderTextureInternal(tex, box, data); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shQUAD.posAttrib); scissor(nullptr); } -static std::map, std::array> primariesConversionCache; +void CHyprOpenGLImpl::renderTexture(SP tex, const CBox& box, float alpha, int round, float roundingPower, bool discardActive, bool allowCustomUV) { + RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); -static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { - // might be too strict - return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || - imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22) && - (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || - targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG); + renderTextureInternalWithDamage(tex, box, alpha, m_RenderData.damage, round, roundingPower, discardActive, false, allowCustomUV, true); + + scissor(nullptr); } -static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { - // might be too strict - return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || - imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG) && - (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || - targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22); +void CHyprOpenGLImpl::renderTextureWithDamage(SP tex, const CBox& box, const CRegion& damage, float alpha, int round, float roundingPower, bool discardActive, + bool allowCustomUV) { + RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); + + renderTextureInternalWithDamage(tex, box, alpha, damage, round, roundingPower, discardActive, false, allowCustomUV, true); + + scissor(nullptr); } -void CHyprOpenGLImpl::passCMUniforms(WP shader, const NColorManagement::PImageDescription imageDescription, - const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { - const auto settings = g_pHyprRenderer->getCMSettings(imageDescription, targetImageDescription, m_renderData.surface.valid() ? m_renderData.surface.lock() : nullptr, modifySDR, - sdrMinLuminance, sdrMaxLuminance); - - shader->setUniformInt(SHADER_SOURCE_TF, settings.sourceTF); - shader->setUniformInt(SHADER_TARGET_TF, settings.targetTF); - shader->setUniformFloat2(SHADER_SRC_TF_RANGE, settings.srcTFRange.min, settings.srcTFRange.max); - shader->setUniformFloat2(SHADER_DST_TF_RANGE, settings.dstTFRange.min, settings.dstTFRange.max); - shader->setUniformFloat(SHADER_SRC_REF_LUMINANCE, settings.srcRefLuminance); - shader->setUniformFloat(SHADER_DST_REF_LUMINANCE, settings.dstRefLuminance); - shader->setUniformFloat(SHADER_MAX_LUMINANCE, settings.maxLuminance); - shader->setUniformFloat(SHADER_DST_MAX_LUMINANCE, settings.dstMaxLuminance); - shader->setUniformFloat(SHADER_SDR_SATURATION, settings.sdrSaturation); - shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, settings.sdrBrightnessMultiplier); - - if (!targetImageDescription->value().icc.present) { - const auto cacheKey = std::make_pair(imageDescription->id(), targetImageDescription->id()); - if (!primariesConversionCache.contains(cacheKey)) { - const auto& mat = settings.convertMatrix; - const std::array glConvertMatrix = { - mat[0][0], mat[1][0], mat[2][0], // - mat[0][1], mat[1][1], mat[2][1], // - mat[0][2], mat[1][2], mat[2][2], // - }; - primariesConversionCache.insert(std::make_pair(cacheKey, glConvertMatrix)); - } - shader->setUniformMatrix3fv(SHADER_CONVERT_MATRIX, 1, false, primariesConversionCache[cacheKey]); - - const auto mat = settings.dstPrimaries2XYZ; - const std::array glTargetPrimariesXYZ = { - mat[0][0], mat[1][0], mat[2][0], // - mat[0][1], mat[1][1], mat[2][1], // - mat[0][2], mat[1][2], mat[2][2], // - }; - shader->setUniformMatrix3fv(SHADER_TARGET_PRIMARIES_XYZ, 1, false, glTargetPrimariesXYZ); - } else { - // TODO: this sucks - GLCALL(glActiveTexture(GL_TEXTURE8)); - targetImageDescription->value().icc.lutTexture->bind(); - - shader->setUniformInt(SHADER_LUT_3D, 8); - shader->setUniformFloat(SHADER_LUT_SIZE, targetImageDescription->value().icc.lutSize); - - GLCALL(glActiveTexture(GL_TEXTURE0)); - } -} - -void CHyprOpenGLImpl::passCMUniforms(WP shader, const PImageDescription imageDescription) { - passCMUniforms(shader, imageDescription, g_pHyprRenderer->workBufferImageDescription(), true, m_renderData.pMonitor->m_sdrMinLuminance, - m_renderData.pMonitor->m_sdrMaxLuminance); -} - -WP CHyprOpenGLImpl::renderToOutputInternal() { - static const auto PDT = CConfigValue("debug:damage_tracking"); - static const auto PCURSORTIMEOUT = CConfigValue("cursor:inactive_timeout"); - - WP shader = - g_pHyprRenderer->m_crashingInProgress ? getShaderVariant(SH_FRAG_GLITCH) : (m_finalScreenShader->program() ? m_finalScreenShader : getShaderVariant(SH_FRAG_PASSTHRURGBA)); - - shader = useShader(shader); - - if (*PDT == 0 || g_pHyprRenderer->m_crashingInProgress) - shader->setUniformFloat(SHADER_TIME, m_globalTimer.getSeconds() - shader->getInitialTime()); - else - shader->setUniformFloat(SHADER_TIME, 0.f); - - shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); - shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); - shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); - shader->setUniformInt(SHADER_POINTER_HIDDEN, g_pHyprRenderer->m_cursorHiddenByCondition); - shader->setUniformInt(SHADER_POINTER_KILLING, g_pInputManager->getClickMode() == CLICKMODE_KILL); - shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape); - shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious); - shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); - - if (*PDT == 0) { - PHLMONITORREF pMonitor = m_renderData.pMonitor; - Vector2D p = ((g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position) * pMonitor->m_scale); - p = p.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); - shader->setUniformFloat2(SHADER_POINTER, p.x / pMonitor->m_pixelSize.x, p.y / pMonitor->m_pixelSize.y); - - std::vector pressedPos = m_pressedHistoryPositions | std::views::transform([&](const Vector2D& vec) { - Vector2D pPressed = ((vec - pMonitor->m_position) * pMonitor->m_scale); - pPressed = pPressed.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); - return std::array{pPressed.x / pMonitor->m_pixelSize.x, pPressed.y / pMonitor->m_pixelSize.y}; - }) | - std::views::join | std::ranges::to>(); - - shader->setUniform2fv(SHADER_POINTER_PRESSED_POSITIONS, pressedPos.size(), pressedPos); - - std::vector pressedTime = - m_pressedHistoryTimers | std::views::transform([](const CTimer& timer) { return timer.getSeconds(); }) | std::ranges::to>(); - - shader->setUniform1fv(SHADER_POINTER_PRESSED_TIMES, pressedTime.size(), pressedTime); - - shader->setUniformInt(SHADER_POINTER_PRESSED_KILLED, m_pressedHistoryKilled); - shader->setUniformInt(SHADER_POINTER_PRESSED_TOUCHED, m_pressedHistoryTouched); - - shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, g_pInputManager->m_lastCursorMovement.getSeconds()); - shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, g_pHyprRenderer->m_lastCursorData.switchedTimer.getSeconds()); - - } else { - shader->setUniformFloat2(SHADER_POINTER, 0.f, 0.f); - - static const std::vector pressedPosDefault(POINTER_PRESSED_HISTORY_LENGTH * 2uz, 0.f); - static const std::vector pressedTimeDefault(POINTER_PRESSED_HISTORY_LENGTH, 0.f); - - shader->setUniform2fv(SHADER_POINTER_PRESSED_POSITIONS, pressedPosDefault.size(), pressedPosDefault); - shader->setUniform1fv(SHADER_POINTER_PRESSED_TIMES, pressedTimeDefault.size(), pressedTimeDefault); - shader->setUniformInt(SHADER_POINTER_PRESSED_KILLED, 0); - - shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, 0.f); - shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, 0.f); - } - - if (g_pHyprRenderer->m_crashingInProgress) { - shader->setUniformFloat(SHADER_DISTORT, g_pHyprRenderer->m_crashingDistort); - shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); - } - - return shader; -} - -WP CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox) { - static const auto PPASS = CConfigValue("render:cm_fs_passthrough"); - static const auto PENABLECM = CConfigValue("render:cm_enabled"); - static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); - - float alpha = std::clamp(data.a, 0.f, 1.f); - - WP shader; - ShaderFeatureFlags shaderFeatures = 0; - - switch (texType) { - case TEXTURE_RGBA: shaderFeatures |= SH_FEAT_RGBA; break; - case TEXTURE_RGBX: shaderFeatures &= ~SH_FEAT_RGBA; break; - - // TODO set correct features - case TEXTURE_EXTERNAL: shader = getShaderVariant(SH_FRAG_EXT, SH_FEAT_ROUNDING | SH_FEAT_DISCARD | SH_FEAT_TINT); break; // might be unused - default: RASSERT(false, "tex->m_iTarget unsupported!"); - } - - if (data.finalMonitorCM || (m_renderData.currentWindow && m_renderData.currentWindow->m_ruleApplicator->RGBX().valueOrDefault())) - shaderFeatures &= ~SH_FEAT_RGBA; - - const auto surface = m_renderData.surface; - const bool isHDRSurface = surface.valid() && surface->m_colorManagement.valid() ? surface->m_colorManagement->isHDR() : false; - const bool canPassHDRSurface = isHDRSurface && !surface->m_colorManagement->isWindowsScRGB(); // windows scRGB requires CM shader - - const auto WORK_BUFFER_IMAGE_DESCRIPTION = g_pHyprRenderer->workBufferImageDescription(); - - // chosenSdrEotf contains the valid eotf for this display - - const auto SOURCE_IMAGE_DESCRIPTION = [&] { - // if valid CM surface, use that as a source - if (m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid()) - return CImageDescription::from(m_renderData.surface->m_colorManagement->imageDescription()); - - // otherwise, if we are CM'ing back into source, use chosen, because that's what our work buffer is in - // the same applies to the final monitor CM - if (data.cmBackToSRGB || data.finalMonitorCM) // NOLINTNEXTLINE - return WORK_BUFFER_IMAGE_DESCRIPTION; - - // otherwise, default - return DEFAULT_IMAGE_DESCRIPTION; - }(); - - const auto TARGET_IMAGE_DESCRIPTION = [&] { - // if we are CM'ing back, use default sRGB - if (data.cmBackToSRGB) - return DEFAULT_IMAGE_DESCRIPTION; - - // for final CM, use the target description - if (data.finalMonitorCM) - return m_renderData.pMonitor->m_imageDescription; - // otherwise, use chosen, we're drawing into the work buffer - // NOLINTNEXTLINE - return WORK_BUFFER_IMAGE_DESCRIPTION; - }(); - - if (data.blur && *PBLEND && data.blurredBG) - shaderFeatures |= SH_FEAT_BLUR; - - if (data.discardActive) - shaderFeatures |= SH_FEAT_DISCARD; - - const bool CANT_CHECK_CM_EQUALITY = data.cmBackToSRGB || data.finalMonitorCM || (!m_renderData.surface || !m_renderData.surface->m_colorManagement); - - const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ - || m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */ - || (SOURCE_IMAGE_DESCRIPTION->id() == TARGET_IMAGE_DESCRIPTION->id() && !CANT_CHECK_CM_EQUALITY) /* Source and target have the same image description */ - || (((*PPASS && canPassHDRSurface) || - (*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && - m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; - - if (data.allowDim && m_renderData.currentWindow && (m_renderData.currentWindow->m_notRespondingTint->value() > 0 || m_renderData.currentWindow->m_dimPercent->value() > 0)) - shaderFeatures |= SH_FEAT_TINT; - - if (data.round > 0) - shaderFeatures |= SH_FEAT_ROUNDING; - - if (!skipCM) { - shaderFeatures |= SH_FEAT_CM; - - if (TARGET_IMAGE_DESCRIPTION->value().icc.present) - shaderFeatures |= SH_FEAT_ICC; - else { - const bool needsSDRmod = isSDR2HDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const float maxLuminance = needsHDRmod ? - SOURCE_IMAGE_DESCRIPTION->value().getTFMaxLuminance(-1) : - (SOURCE_IMAGE_DESCRIPTION->value().luminances.max > 0 ? SOURCE_IMAGE_DESCRIPTION->value().luminances.max : SOURCE_IMAGE_DESCRIPTION->value().luminances.reference); - const auto dstMaxLuminance = TARGET_IMAGE_DESCRIPTION->value().luminances.max > 0 ? TARGET_IMAGE_DESCRIPTION->value().luminances.max : 10000; - - if (maxLuminance >= dstMaxLuminance * 1.01) - shaderFeatures |= SH_FEAT_TONEMAP; - - if (data.finalMonitorCM && - (SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || - SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && - TARGET_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && - ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || - (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f))) - shaderFeatures |= SH_FEAT_SDR_MOD; - } - } - - if (!shader) - shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures); - shader = useShader(shader); - - if (!skipCM) { - if (data.finalMonitorCM || data.cmBackToSRGB) - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance); - else - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION); - } - - shader->setUniformFloat(SHADER_ALPHA, alpha); - - if (shaderFeatures & SH_FEAT_BLUR) { - shader->setUniformInt(SHADER_BLURRED_BG, 1); - // shader->setUniformFloat2(SHADER_UV_OFFSET, 0, 0); - shader->setUniformFloat2(SHADER_UV_OFFSET, newBox.x / m_renderData.pMonitor->m_transformedSize.x, newBox.y / m_renderData.pMonitor->m_transformedSize.y); - shader->setUniformFloat2(SHADER_UV_SIZE, newBox.width / m_renderData.pMonitor->m_transformedSize.x, newBox.height / m_renderData.pMonitor->m_transformedSize.y); - - glActiveTexture(GL_TEXTURE0 + 1); - data.blurredBG->bind(); - } - - if (data.discardActive) { - shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(m_renderData.discardMode & DISCARD_OPAQUE)); - shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(m_renderData.discardMode & DISCARD_ALPHA)); - shader->setUniformFloat(SHADER_DISCARD_ALPHA_VALUE, m_renderData.discardOpacity); - } else { - shader->setUniformInt(SHADER_DISCARD_OPAQUE, 0); - shader->setUniformInt(SHADER_DISCARD_ALPHA, 0); - } - - CBox transformedBox = newBox; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); - - const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); - const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); - // Rounded corners - shader->setUniformFloat2(SHADER_TOP_LEFT, TOPLEFT.x, TOPLEFT.y); - shader->setUniformFloat2(SHADER_FULL_SIZE, FULLSIZE.x, FULLSIZE.y); - shader->setUniformFloat(SHADER_RADIUS, data.round); - shader->setUniformFloat(SHADER_ROUNDING_POWER, data.roundingPower); - - if (data.allowDim && m_renderData.currentWindow) { - if (m_renderData.currentWindow->m_notRespondingTint->value() > 0) { - const auto DIM = m_renderData.currentWindow->m_notRespondingTint->value(); - shader->setUniformInt(SHADER_APPLY_TINT, 1); - shader->setUniformFloat3(SHADER_TINT, 1.f - DIM, 1.f - DIM, 1.f - DIM); - } else if (m_renderData.currentWindow->m_dimPercent->value() > 0) { - shader->setUniformInt(SHADER_APPLY_TINT, 1); - const auto DIM = m_renderData.currentWindow->m_dimPercent->value(); - shader->setUniformFloat3(SHADER_TINT, 1.f - DIM, 1.f - DIM, 1.f - DIM); - } else - shader->setUniformInt(SHADER_APPLY_TINT, 0); - } else - shader->setUniformInt(SHADER_APPLY_TINT, 0); - - return shader; -} - -void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, const STextureRenderData& data) { - RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->ok()), "Attempted to draw nullptr texture!"); +void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP tex, const CBox& box, float alpha, const CRegion& damage, int round, float roundingPower, bool discardActive, + bool noAA, bool allowCustomUV, bool allowDim) { + RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); + RASSERT((tex->m_iTexID > 0), "Attempted to draw nullptr texture!"); TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); - if (data.damage->empty()) + alpha = std::clamp(alpha, 0.f, 1.f); + + if (damage.empty()) return; CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); + m_RenderData.renderModif.applyToBox(newBox); + + static const auto PDT = CConfigValue("debug:damage_tracking"); + static const auto PPASS = CConfigValue("render:cm_fs_passthrough"); + static const auto PENABLECM = CConfigValue("render:cm_enabled"); // get the needed transform for this texture - const auto MONITOR_INVERTED = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); - Hyprutils::Math::eTransform TRANSFORM = tex->m_transform; + const bool TRANSFORMS_MATCH = wlTransformToHyprutils(m_RenderData.pMonitor->transform) == tex->m_eTransform; // FIXME: combine them properly!!! + eTransform TRANSFORM = HYPRUTILS_TRANSFORM_NORMAL; + if (m_bEndFrame || TRANSFORMS_MATCH) + TRANSFORM = wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)); - if (m_monitorTransformEnabled) - TRANSFORM = Math::composeTransform(MONITOR_INVERTED, TRANSFORM); + Mat3x3 matrix = m_RenderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); + Mat3x3 glMatrix = m_RenderData.projection.copy().multiply(matrix); - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + CShader* shader = nullptr; - const bool renderToOutput = m_applyFinalShader && g_pHyprRenderer->workBufferImageDescription()->id() == m_renderData.pMonitor->m_imageDescription->id(); + bool usingFinalShader = false; + + const bool CRASHING = m_bApplyFinalShader && g_pHyprRenderer->m_bCrashingInProgress; + + auto texType = tex->m_iType; + + if (CRASHING) { + shader = &m_RenderData.pCurrentMonData->m_shGLITCH; + usingFinalShader = true; + } else if (m_bApplyFinalShader && m_sFinalScreenShader.program) { + shader = &m_sFinalScreenShader; + usingFinalShader = true; + } else { + if (m_bApplyFinalShader) { + shader = &m_RenderData.pCurrentMonData->m_shPASSTHRURGBA; + usingFinalShader = true; + } else { + switch (tex->m_iType) { + case TEXTURE_RGBA: shader = &m_RenderData.pCurrentMonData->m_shRGBA; break; + case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shRGBX; break; + + case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break; // might be unused + default: RASSERT(false, "tex->m_iTarget unsupported!"); + } + } + } + + if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault()) + texType = TEXTURE_RGBX; glActiveTexture(GL_TEXTURE0); - tex->bind(); + glBindTexture(tex->m_iTarget, tex->m_iTexID); - tex->setTexParameter(GL_TEXTURE_WRAP_S, data.wrapX); - tex->setTexParameter(GL_TEXTURE_WRAP_T, data.wrapY); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - if (m_renderData.useNearestNeighbor) { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + if (m_RenderData.useNearestNeighbor) { + glTexParameteri(tex->m_iTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, tex->magFilter); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } - auto shader = renderToOutput ? renderToOutputInternal() : renderToFBInternal(data, tex->m_type, newBox); + const auto imageDescription = + m_RenderData.surface.valid() && m_RenderData.surface->colorManagement.valid() ? m_RenderData.surface->colorManagement->imageDescription() : SImageDescription{}; - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformInt(SHADER_TEX, 0); - GLCALL(glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO))); - GLCALL(glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_VBO))); + const bool skipCM = !*PENABLECM /* CM disabled by the user */ + || !m_RenderData.surface /* No surface - no point in CM */ + || !m_bCMSupported /* CM unsupported - hw failed to compile the shader probably */ + || (imageDescription == m_RenderData.pMonitor->imageDescription) /* Source and target have the same image description */ + || ((*PPASS == 1 || (*PPASS == 2 && imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ)) && m_RenderData.pMonitor->activeWorkspace && + m_RenderData.pMonitor->activeWorkspace->m_bHasFullscreenWindow && + m_RenderData.pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) /* Fullscreen window with pass cm enabled */; - // this tells GPU can keep reading the old block for previous draws while the CPU writes to a new one. - // to avoid stalls if renderTextureInternal is called multiple times on same renderpass - // at the cost of some temporar vram usage. - glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), nullptr, GL_DYNAMIC_DRAW); + glUseProgram(shader->program); - auto verts = fullVerts; +#ifndef GLES2 + if (!skipCM && !usingFinalShader && (texType == TEXTURE_RGBA || texType == TEXTURE_RGBX)) { + shader = &m_RenderData.pCurrentMonData->m_shCM; + glUseProgram(shader->program); + glUniform1i(shader->texType, texType); + const auto imageDescription = + m_RenderData.surface && m_RenderData.surface->colorManagement ? m_RenderData.surface->colorManagement->imageDescription() : SImageDescription{}; + glUniform1i(shader->sourceTF, imageDescription.transferFunction); + glUniform1i(shader->targetTF, m_RenderData.pMonitor->imageDescription.transferFunction); + const auto sourcePrimaries = + imageDescription.primariesNameSet || imageDescription.primaries == SPCPRimaries{} ? getPrimaries(imageDescription.primariesNamed) : imageDescription.primaries; + const auto targetPrimaries = m_RenderData.pMonitor->imageDescription.primariesNameSet || m_RenderData.pMonitor->imageDescription.primaries == SPCPRimaries{} ? + getPrimaries(m_RenderData.pMonitor->imageDescription.primariesNamed) : + m_RenderData.pMonitor->imageDescription.primaries; - if (data.allowCustomUV && m_renderData.primarySurfaceUVTopLeft != Vector2D(-1, -1)) { - const float u0 = m_renderData.primarySurfaceUVTopLeft.x; - const float v0 = m_renderData.primarySurfaceUVTopLeft.y; - const float u1 = m_renderData.primarySurfaceUVBottomRight.x; - const float v1 = m_renderData.primarySurfaceUVBottomRight.y; + const GLfloat glSourcePrimaries[8] = { + sourcePrimaries.red.x, sourcePrimaries.red.y, sourcePrimaries.green.x, sourcePrimaries.green.y, + sourcePrimaries.blue.x, sourcePrimaries.blue.y, sourcePrimaries.white.x, sourcePrimaries.white.y, + }; + const GLfloat glTargetPrimaries[8] = { + targetPrimaries.red.x, targetPrimaries.red.y, targetPrimaries.green.x, targetPrimaries.green.y, + targetPrimaries.blue.x, targetPrimaries.blue.y, targetPrimaries.white.x, targetPrimaries.white.y, + }; + glUniformMatrix4x2fv(shader->sourcePrimaries, 1, false, glSourcePrimaries); + glUniformMatrix4x2fv(shader->targetPrimaries, 1, false, glTargetPrimaries); - verts[0].u = u0; - verts[0].v = v0; - verts[1].u = u0; - verts[1].v = v1; - verts[2].u = u1; - verts[2].v = v0; - verts[3].u = u1; - verts[3].v = v1; + const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference; + glUniform1f(shader->maxLuminance, maxLuminance * m_RenderData.pMonitor->imageDescription.luminances.reference / imageDescription.luminances.reference); + glUniform1f(shader->dstMaxLuminance, m_RenderData.pMonitor->imageDescription.luminances.max > 0 ? m_RenderData.pMonitor->imageDescription.luminances.max : 10000); + glUniform1f(shader->dstRefLuminance, m_RenderData.pMonitor->imageDescription.luminances.reference); + glUniform1f(shader->sdrSaturation, + m_RenderData.pMonitor->sdrSaturation > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_RenderData.pMonitor->sdrSaturation : + 1.0f); + glUniform1f(shader->sdrBrightness, + m_RenderData.pMonitor->sdrBrightness > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_RenderData.pMonitor->sdrBrightness : + 1.0f); + } +#endif + +#ifndef GLES2 + glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif + glUniform1i(shader->tex, 0); + + if ((usingFinalShader && *PDT == 0) || CRASHING) { + glUniform1f(shader->time, m_tGlobalTimer.getSeconds() - shader->initialTime); + } else if (usingFinalShader && shader->time != -1) { + // Don't let time be unitialised + glUniform1f(shader->time, 0.f); } - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts.data()); + if (usingFinalShader && shader->wl_output != -1) + glUniform1i(shader->wl_output, m_RenderData.pMonitor->ID); + if (usingFinalShader && shader->fullSize != -1) + glUniform2f(shader->fullSize, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y); - if (!m_renderData.clipBox.empty() || !m_renderData.clipRegion.empty()) { - CRegion damageClip = m_renderData.clipBox; + if (CRASHING) { + glUniform1f(shader->distort, g_pHyprRenderer->m_fCrashingDistort); + glUniform2f(shader->fullSize, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y); + } - if (!m_renderData.clipRegion.empty()) { - if (m_renderData.clipBox.empty()) - damageClip = m_renderData.clipRegion; + if (!usingFinalShader) { + glUniform1f(shader->alpha, alpha); + + if (discardActive) { + glUniform1i(shader->discardOpaque, !!(m_RenderData.discardMode & DISCARD_OPAQUE)); + glUniform1i(shader->discardAlpha, !!(m_RenderData.discardMode & DISCARD_ALPHA)); + glUniform1f(shader->discardAlphaValue, m_RenderData.discardOpacity); + } else { + glUniform1i(shader->discardOpaque, 0); + glUniform1i(shader->discardAlpha, 0); + } + } + + CBox transformedBox = newBox; + transformedBox.transform(wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)), m_RenderData.pMonitor->vecTransformedSize.x, + m_RenderData.pMonitor->vecTransformedSize.y); + + const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); + const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); + + if (!usingFinalShader) { + // Rounded corners + glUniform2f(shader->topLeft, TOPLEFT.x, TOPLEFT.y); + glUniform2f(shader->fullSize, FULLSIZE.x, FULLSIZE.y); + glUniform1f(shader->radius, round); + glUniform1f(shader->roundingPower, roundingPower); + + if (allowDim && m_RenderData.currentWindow) { + if (m_RenderData.currentWindow->m_notRespondingTint->value() > 0) { + const auto DIM = m_RenderData.currentWindow->m_notRespondingTint->value(); + glUniform1i(shader->applyTint, 1); + glUniform3f(shader->tint, 1.f - DIM, 1.f - DIM, 1.f - DIM); + } else if (m_RenderData.currentWindow->m_fDimPercent->value() > 0) { + glUniform1i(shader->applyTint, 1); + const auto DIM = m_RenderData.currentWindow->m_fDimPercent->value(); + glUniform3f(shader->tint, 1.f - DIM, 1.f - DIM, 1.f - DIM); + } else + glUniform1i(shader->applyTint, 0); + } else + glUniform1i(shader->applyTint, 0); + } + + const float verts[] = { + m_RenderData.primarySurfaceUVBottomRight.x, m_RenderData.primarySurfaceUVTopLeft.y, // top right + m_RenderData.primarySurfaceUVTopLeft.x, m_RenderData.primarySurfaceUVTopLeft.y, // top left + m_RenderData.primarySurfaceUVBottomRight.x, m_RenderData.primarySurfaceUVBottomRight.y, // bottom right + m_RenderData.primarySurfaceUVTopLeft.x, m_RenderData.primarySurfaceUVBottomRight.y, // bottom left + }; + + glVertexAttribPointer(shader->posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + if (allowCustomUV && m_RenderData.primarySurfaceUVTopLeft != Vector2D(-1, -1)) + glVertexAttribPointer(shader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, verts); + else + glVertexAttribPointer(shader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + glEnableVertexAttribArray(shader->posAttrib); + glEnableVertexAttribArray(shader->texAttrib); + + if (!m_RenderData.clipBox.empty() || !m_RenderData.clipRegion.empty()) { + CRegion damageClip = m_RenderData.clipBox; + + if (!m_RenderData.clipRegion.empty()) { + if (m_RenderData.clipBox.empty()) + damageClip = m_RenderData.clipRegion; else - damageClip.intersect(m_renderData.clipRegion); + damageClip.intersect(m_RenderData.clipRegion); } if (!damageClip.empty()) { - damageClip.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + for (auto const& RECT : damageClip.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } } else { - data.damage->forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + for (auto const& RECT : damage.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } - GLCALL(glBindVertexArray(0)); - GLCALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); - tex->unbind(); + glDisableVertexAttribArray(shader->posAttrib); + glDisableVertexAttribArray(shader->texAttrib); + + glBindTexture(tex->m_iTarget, 0); } -void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, const CBox& box) { - RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->ok()), "Attempted to draw nullptr texture!"); +void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, const CBox& box) { + RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); + RASSERT((tex->m_iTexID > 0), "Attempted to draw nullptr texture!"); TRACY_GPU_ZONE("RenderTexturePrimitive"); - if (m_renderData.damage.empty()) + if (m_RenderData.damage.empty()) return; CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); + m_RenderData.renderModif.applyToBox(newBox); // get transform - const auto TRANSFORM = Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)); - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + const auto TRANSFORM = wlTransformToHyprutils(invertTransform(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform)); + Mat3x3 matrix = m_RenderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); + Mat3x3 glMatrix = m_RenderData.projection.copy().multiply(matrix); + + CShader* shader = &m_RenderData.pCurrentMonData->m_shPASSTHRURGBA; glActiveTexture(GL_TEXTURE0); - tex->bind(); + glBindTexture(tex->m_iTarget, tex->m_iTexID); - // ensure the final blit uses the desired sampling filter - // when cursor zoom is active we want nearest-neighbor (no anti-aliasing) - if (m_renderData.useNearestNeighbor) { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, tex->magFilter); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); + glUseProgram(shader->program); + +#ifndef GLES2 + glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif + glUniform1i(shader->tex, 0); + + glVertexAttribPointer(shader->posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(shader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + glEnableVertexAttribArray(shader->posAttrib); + glEnableVertexAttribArray(shader->texAttrib); + + for (auto const& RECT : m_RenderData.damage.getRects()) { + scissor(&RECT); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } - auto shader = useShader(getShaderVariant(SH_FRAG_PASSTHRURGBA)); - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformInt(SHADER_TEX, 0); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); - - m_renderData.damage.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); - scissor(nullptr); - glBindVertexArray(0); - tex->unbind(); + + glDisableVertexAttribArray(shader->posAttrib); + glDisableVertexAttribArray(shader->texAttrib); + + glBindTexture(tex->m_iTarget, 0); } -void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, SP matte) { - RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->ok()), "Attempted to draw nullptr texture!"); +void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, CFramebuffer& matte) { + RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); + RASSERT((tex->m_iTexID > 0), "Attempted to draw nullptr texture!"); TRACY_GPU_ZONE("RenderTextureMatte"); - if (m_renderData.damage.empty()) + if (m_RenderData.damage.empty()) return; CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); + m_RenderData.renderModif.applyToBox(newBox); // get transform - const auto TRANSFORM = Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)); - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + const auto TRANSFORM = wlTransformToHyprutils(invertTransform(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform)); + Mat3x3 matrix = m_RenderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); + Mat3x3 glMatrix = m_RenderData.projection.copy().multiply(matrix); - auto shader = useShader(getShaderVariant(SH_FRAG_MATTE)); - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformInt(SHADER_TEX, 0); - shader->setUniformInt(SHADER_ALPHA_MATTE, 1); + CShader* shader = &m_RenderData.pCurrentMonData->m_shMATTE; + + glUseProgram(shader->program); + +#ifndef GLES2 + glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif + glUniform1i(shader->tex, 0); + glUniform1i(shader->alphaMatte, 1); glActiveTexture(GL_TEXTURE0); - tex->bind(); + glBindTexture(tex->m_iTarget, tex->m_iTexID); glActiveTexture(GL_TEXTURE0 + 1); - auto matteTex = matte->getTexture(); - matteTex->bind(); + auto matteTex = matte.getTexture(); + glBindTexture(matteTex->m_iTarget, matteTex->m_iTexID); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glVertexAttribPointer(shader->posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(shader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - m_renderData.damage.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + glEnableVertexAttribArray(shader->posAttrib); + glEnableVertexAttribArray(shader->texAttrib); + + for (auto const& RECT : m_RenderData.damage.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } scissor(nullptr); - glBindVertexArray(0); - tex->unbind(); + + glDisableVertexAttribArray(shader->posAttrib); + glDisableVertexAttribArray(shader->texAttrib); + + glBindTexture(tex->m_iTarget, 0); } // This probably isn't the fastest // but it works... well, I guess? // // Dual (or more) kawase blur -SP CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* originalDamage) { - if (!m_renderData.currentFB->getTexture()) { - Log::logger->log(Log::ERR, "BUG THIS: null fb texture while attempting to blur main fb?! (introspection off?!)"); - return m_renderData.pCurrentMonData->mirrorFB; // return something to sample from at least +CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* originalDamage) { + + if (!m_RenderData.currentFB->getTexture()) { + Debug::log(ERR, "BUG THIS: null fb texture while attempting to blur main fb?! (introspection off?!)"); + return &m_RenderData.pCurrentMonData->mirrorFB; // return something to sample from at least } - return blurFramebufferWithDamage(a, originalDamage, *GLFB(m_renderData.currentFB)); -} + TRACY_GPU_ZONE("RenderBlurMainFramebufferWithDamage"); -SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* originalDamage, CGLFramebuffer& source) { - TRACY_GPU_ZONE("RenderBlurFramebufferWithDamage"); - - const auto BLENDBEFORE = m_blend; + const auto BLENDBEFORE = m_bBlend; blend(false); - setCapStatus(GL_STENCIL_TEST, false); + glDisable(GL_STENCIL_TEST); // get transforms for the full monitor - const auto TRANSFORM = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); - CBox MONITORBOX = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(MONITORBOX, TRANSFORM); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + const auto TRANSFORM = wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)); + CBox MONITORBOX = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; + Mat3x3 matrix = m_RenderData.monitorProjection.projectBox(MONITORBOX, TRANSFORM); + Mat3x3 glMatrix = m_RenderData.projection.copy().multiply(matrix); // get the config settings static auto PBLURSIZE = CConfigValue("decoration:blur:size"); @@ -1691,78 +1682,67 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or static auto PBLURVIBRANCY = CConfigValue("decoration:blur:vibrancy"); static auto PBLURVIBRANCYDARKNESS = CConfigValue("decoration:blur:vibrancy_darkness"); - const auto BLUR_PASSES = std::clamp(*PBLURPASSES, sc(1), sc(8)); - // prep damage CRegion damage{*originalDamage}; - damage.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); - damage.expand(std::clamp(*PBLURSIZE, sc(1), sc(40)) * pow(2, BLUR_PASSES)); + damage.transform(wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)), m_RenderData.pMonitor->vecTransformedSize.x, + m_RenderData.pMonitor->vecTransformedSize.y); + damage.expand(*PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES)); // helper - const auto PMIRRORFB = m_renderData.pCurrentMonData->mirrorFB; - const auto PMIRRORSWAPFB = m_renderData.pCurrentMonData->mirrorSwapFB; + const auto PMIRRORFB = &m_RenderData.pCurrentMonData->mirrorFB; + const auto PMIRRORSWAPFB = &m_RenderData.pCurrentMonData->mirrorSwapFB; - auto currentRenderToFB = PMIRRORFB; + CFramebuffer* currentRenderToFB = PMIRRORFB; // Begin with base color adjustments - global brightness and contrast // TODO: make this a part of the first pass maybe to save on a drawcall? { static auto PBLURCONTRAST = CConfigValue("decoration:blur:contrast"); static auto PBLURBRIGHTNESS = CConfigValue("decoration:blur:brightness"); - static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); PMIRRORSWAPFB->bind(); glActiveTexture(GL_TEXTURE0); - auto currentTex = source.getTexture(); + auto currentTex = m_RenderData.currentFB->getTexture(); - currentTex->bind(); - currentTex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glBindTexture(currentTex->m_iTarget, currentTex->m_iTexID); - WP shader; + glTexParameteri(currentTex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - // From FB to sRGB - const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); - if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE, SH_FEAT_CM)); - passCMUniforms(shader, m_renderData.pMonitor->m_imageDescription, DEFAULT_IMAGE_DESCRIPTION); - shader->setUniformFloat(SHADER_SDR_SATURATION, - m_renderData.pMonitor->m_sdrSaturation > 0 && - m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? - m_renderData.pMonitor->m_sdrSaturation : - 1.0f); - shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, - m_renderData.pMonitor->m_sdrBrightness > 0 && - m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? - m_renderData.pMonitor->m_sdrBrightness : - 1.0f); - } else - shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE)); + glUseProgram(m_RenderData.pCurrentMonData->m_shBLURPREPARE.program); - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(MONITORBOX, *PBLEND ? HYPRUTILS_TRANSFORM_NORMAL : TRANSFORM); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformFloat(SHADER_CONTRAST, *PBLURCONTRAST); - shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); - shader->setUniformInt(SHADER_TEX, 0); +#ifndef GLES2 + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURPREPARE.proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURPREPARE.proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURPREPARE.contrast, *PBLURCONTRAST); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURPREPARE.brightness, *PBLURBRIGHTNESS); + glUniform1i(m_RenderData.pCurrentMonData->m_shBLURPREPARE.tex, 0); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURPREPARE.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURPREPARE.texAttrib); if (!damage.empty()) { - damage.forEachRect([this](const auto& RECT) { + for (auto const& RECT : damage.getRects()) { scissor(&RECT, false /* this region is already transformed */); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } - glBindVertexArray(0); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURPREPARE.posAttrib); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURPREPARE.texAttrib); + currentRenderToFB = PMIRRORSWAPFB; } // declare the draw func - auto drawPass = [&](WP shader, ePreparedFragmentShader frag, CRegion* pDamage) { + auto drawPass = [&](CShader* pShader, CRegion* pDamage) { if (currentRenderToFB == PMIRRORFB) PMIRRORSWAPFB->bind(); else @@ -1772,32 +1752,46 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or auto currentTex = currentRenderToFB->getTexture(); - currentTex->bind(); + glBindTexture(currentTex->m_iTarget, currentTex->m_iTexID); - currentTex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(currentTex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glUseProgram(pShader->program); // prep two shaders - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformFloat(SHADER_RADIUS, *PBLURSIZE * a); // this makes the blursize change with a - if (frag == SH_FRAG_BLUR1) { - shader->setUniformFloat2(SHADER_HALFPIXEL, 0.5f / (m_renderData.pMonitor->m_pixelSize.x / 2.f), 0.5f / (m_renderData.pMonitor->m_pixelSize.y / 2.f)); - shader->setUniformInt(SHADER_PASSES, BLUR_PASSES); - shader->setUniformFloat(SHADER_VIBRANCY, *PBLURVIBRANCY); - shader->setUniformFloat(SHADER_VIBRANCY_DARKNESS, *PBLURVIBRANCYDARKNESS); +#ifndef GLES2 + glUniformMatrix3fv(pShader->proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(pShader->proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif + glUniform1f(pShader->radius, *PBLURSIZE * a); // this makes the blursize change with a + if (pShader == &m_RenderData.pCurrentMonData->m_shBLUR1) { + glUniform2f(m_RenderData.pCurrentMonData->m_shBLUR1.halfpixel, 0.5f / (m_RenderData.pMonitor->vecPixelSize.x / 2.f), + 0.5f / (m_RenderData.pMonitor->vecPixelSize.y / 2.f)); + glUniform1i(m_RenderData.pCurrentMonData->m_shBLUR1.passes, *PBLURPASSES); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy, *PBLURVIBRANCY); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLUR1.vibrancy_darkness, *PBLURVIBRANCYDARKNESS); } else - shader->setUniformFloat2(SHADER_HALFPIXEL, 0.5f / (m_renderData.pMonitor->m_pixelSize.x * 2.f), 0.5f / (m_renderData.pMonitor->m_pixelSize.y * 2.f)); - shader->setUniformInt(SHADER_TEX, 0); + glUniform2f(m_RenderData.pCurrentMonData->m_shBLUR2.halfpixel, 0.5f / (m_RenderData.pMonitor->vecPixelSize.x * 2.f), + 0.5f / (m_RenderData.pMonitor->vecPixelSize.y * 2.f)); + glUniform1i(pShader->tex, 0); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glVertexAttribPointer(pShader->posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(pShader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + glEnableVertexAttribArray(pShader->posAttrib); + glEnableVertexAttribArray(pShader->texAttrib); if (!pDamage->empty()) { - pDamage->forEachRect([this](const auto& RECT) { + for (auto const& RECT : pDamage->getRects()) { scissor(&RECT, false /* this region is already transformed */); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } - glBindVertexArray(0); + glDisableVertexAttribArray(pShader->posAttrib); + glDisableVertexAttribArray(pShader->texAttrib); if (currentRenderToFB != PMIRRORFB) currentRenderToFB = PMIRRORFB; @@ -1808,22 +1802,20 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or // draw the things. // first draw is swap -> mirr PMIRRORFB->bind(); - PMIRRORSWAPFB->getTexture()->bind(); + glBindTexture(PMIRRORSWAPFB->getTexture()->m_iTarget, PMIRRORSWAPFB->getTexture()->m_iTexID); // damage region will be scaled, make a temp CRegion tempDamage{damage}; // and draw - auto shader = useShader(getShaderVariant(SH_FRAG_BLUR1)); - for (auto i = 1; i <= BLUR_PASSES; ++i) { + for (auto i = 1; i <= *PBLURPASSES; ++i) { tempDamage = damage.copy().scale(1.f / (1 << i)); - drawPass(shader, SH_FRAG_BLUR1, &tempDamage); // down + drawPass(&m_RenderData.pCurrentMonData->m_shBLUR1, &tempDamage); // down } - shader = useShader(getShaderVariant(SH_FRAG_BLUR2)); - for (auto i = BLUR_PASSES - 1; i >= 0; --i) { - tempDamage = damage.copy().scale(1.f / (1 << i)); // when upsampling we make the region twice as big - drawPass(shader, SH_FRAG_BLUR2, &tempDamage); // up + for (auto i = *PBLURPASSES - 1; i >= 0; --i) { + tempDamage = damage.copy().scale(1.f / (1 << i)); // when upsampling we make the region twice as big + drawPass(&m_RenderData.pCurrentMonData->m_shBLUR2, &tempDamage); // up } // finalize the image @@ -1840,27 +1832,38 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or auto currentTex = currentRenderToFB->getTexture(); - currentTex->bind(); + glBindTexture(currentTex->m_iTarget, currentTex->m_iTexID); - currentTex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(currentTex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - auto shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH)); - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformFloat(SHADER_NOISE, *PBLURNOISE); - shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); + glUseProgram(m_RenderData.pCurrentMonData->m_shBLURFINISH.program); - shader->setUniformInt(SHADER_TEX, 0); +#ifndef GLES2 + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURFINISH.proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBLURFINISH.proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.noise, *PBLURNOISE); + glUniform1f(m_RenderData.pCurrentMonData->m_shBLURFINISH.brightness, *PBLURBRIGHTNESS); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glUniform1i(m_RenderData.pCurrentMonData->m_shBLURFINISH.tex, 0); + + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURFINISH.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBLURFINISH.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURFINISH.posAttrib); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURFINISH.texAttrib); if (!damage.empty()) { - damage.forEachRect([this](const auto& RECT) { + for (auto const& RECT : damage.getRects()) { scissor(&RECT, false /* this region is already transformed */); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } - glBindVertexArray(0); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURFINISH.posAttrib); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBLURFINISH.texAttrib); if (currentRenderToFB != PMIRRORFB) currentRenderToFB = PMIRRORFB; @@ -1869,7 +1872,7 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or } // finish - PMIRRORFB->getTexture()->unbind(); + glBindTexture(PMIRRORFB->getTexture()->m_iTarget, 0); blend(BLENDBEFORE); @@ -1877,7 +1880,7 @@ SP CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or } void CHyprOpenGLImpl::markBlurDirtyForMonitor(PHLMONITOR pMonitor) { - m_monitorRenderResources[pMonitor].blurFBDirty = true; + m_mMonitorRenderResources[pMonitor].blurFBDirty = true; } void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) { @@ -1885,11 +1888,11 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) { static auto PBLURXRAY = CConfigValue("decoration:blur:xray"); static auto PBLUR = CConfigValue("decoration:blur:enabled"); - if (!*PBLURNEWOPTIMIZE || !m_monitorRenderResources[pMonitor].blurFBDirty || !*PBLUR) + if (!*PBLURNEWOPTIMIZE || !m_mMonitorRenderResources[pMonitor].blurFBDirty || !*PBLUR) return; // ignore if solitary present, nothing to blur - if (!pMonitor->m_solitaryClient.expired()) + if (!pMonitor->solitaryClient.expired()) return; // check if we need to update the blur fb @@ -1900,16 +1903,16 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) { if (!pWindow) return false; - if (pWindow->m_ruleApplicator->noBlur().valueOrDefault()) + if (pWindow->m_sWindowData.noBlur.valueOrDefault()) return false; - if (pWindow->wlSurface()->small() && !pWindow->wlSurface()->m_fillIgnoreSmall) + if (pWindow->m_pWLSurface->small() && !pWindow->m_pWLSurface->m_bFillIgnoreSmall) return true; - const auto PSURFACE = pWindow->wlSurface()->resource(); + const auto PSURFACE = pWindow->m_pWLSurface->resource(); - const auto PWORKSPACE = pWindow->m_workspace; - const float A = pWindow->m_alpha->value() * pWindow->m_activeInactiveAlpha->value() * PWORKSPACE->m_alpha->value(); + const auto PWORKSPACE = pWindow->m_pWorkspace; + const float A = pWindow->m_fAlpha->value() * pWindow->m_fActiveInactiveAlpha->value() * PWORKSPACE->m_fAlpha->value(); if (A >= 1.f) { // if (PSURFACE->opaque) @@ -1917,9 +1920,9 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) { CRegion inverseOpaque; - pixman_box32_t surfbox = {0, 0, PSURFACE->m_current.size.x, PSURFACE->m_current.size.y}; - CRegion opaqueRegion{PSURFACE->m_current.opaque}; - inverseOpaque.set(opaqueRegion).invert(&surfbox).intersect(0, 0, PSURFACE->m_current.size.x, PSURFACE->m_current.size.y); + pixman_box32_t surfbox = {0, 0, PSURFACE->current.size.x, PSURFACE->current.size.y}; + CRegion opaqueRegion{PSURFACE->current.opaque}; + inverseOpaque.set(opaqueRegion).invert(&surfbox).intersect(0, 0, PSURFACE->current.size.x, PSURFACE->current.size.y); if (inverseOpaque.empty()) return false; @@ -1929,8 +1932,8 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) { }; bool hasWindows = false; - for (auto const& w : g_pCompositor->m_windows) { - if (w->m_workspace == pMonitor->m_activeWorkspace && !w->isHidden() && w->m_isMapped && (!w->m_isFloating || *PBLURXRAY)) { + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace == pMonitor->activeWorkspace && !w->isHidden() && w->m_bIsMapped && (!w->m_bIsFloating || *PBLURXRAY)) { // check if window is valid if (!windowShouldBeBlurred(w)) @@ -1941,10 +1944,10 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) { } } - for (auto const& m : g_pCompositor->m_monitors) { - for (auto const& lsl : m->m_layerSurfaceLayers) { + for (auto const& m : g_pCompositor->m_vMonitors) { + for (auto const& lsl : m->m_aLayerSurfaceLayers) { for (auto const& ls : lsl) { - if (!ls->m_layerSurface || ls->m_ruleApplicator->xray().valueOrDefault() != 1) + if (!ls->layerSurface || ls->xray != 1) continue; // if (ls->layerSurface->surface->opaque && ls->alpha->value() >= 1.f) @@ -1960,246 +1963,206 @@ void CHyprOpenGLImpl::preRender(PHLMONITOR pMonitor) { return; g_pHyprRenderer->damageMonitor(pMonitor); - m_monitorRenderResources[pMonitor].blurFBShouldRender = true; + m_mMonitorRenderResources[pMonitor].blurFBShouldRender = true; } void CHyprOpenGLImpl::preBlurForCurrentMonitor() { TRACY_GPU_ZONE("RenderPreBlurForCurrentMonitor"); - const auto SAVEDRENDERMODIF = m_renderData.renderModif; - m_renderData.renderModif = {}; // fix shit + const auto SAVEDRENDERMODIF = m_RenderData.renderModif; + m_RenderData.renderModif = {}; // fix shit // make the fake dmg - CRegion fakeDamage{0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; + CRegion fakeDamage{0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; const auto POUTFB = blurMainFramebufferWithDamage(1, &fakeDamage); // render onto blurFB - if (!m_renderData.pCurrentMonData->blurFB) - m_renderData.pCurrentMonData->blurFB = g_pHyprRenderer->createFB(); - - m_renderData.pCurrentMonData->blurFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, - m_renderData.pMonitor->m_output->state->state().drmFormat); - m_renderData.pCurrentMonData->blurFB->bind(); + m_RenderData.pCurrentMonData->blurFB.alloc(m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y, + m_RenderData.pMonitor->output->state->state().drmFormat); + m_RenderData.pCurrentMonData->blurFB.bind(); clear(CHyprColor(0, 0, 0, 0)); - pushMonitorTransformEnabled(true); - renderTextureInternal(POUTFB->getTexture(), CBox{0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}, - STextureRenderData{.damage = &fakeDamage, .a = 1, .round = 0, .roundingPower = 2.F, .discardActive = false, .allowCustomUV = false, .noAA = true}); - popMonitorTransformEnabled(); + m_bEndFrame = true; // fix transformed + renderTextureInternalWithDamage(POUTFB->getTexture(), CBox{0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}, 1, fakeDamage, 0, + 2.0f, false, true, false); + m_bEndFrame = false; - m_renderData.currentFB->bind(); + m_RenderData.currentFB->bind(); - m_renderData.pCurrentMonData->blurFBDirty = false; + m_RenderData.pCurrentMonData->blurFBDirty = false; - m_renderData.renderModif = SAVEDRENDERMODIF; + m_RenderData.renderModif = SAVEDRENDERMODIF; - m_monitorRenderResources[m_renderData.pMonitor].blurFBShouldRender = false; + m_mMonitorRenderResources[m_RenderData.pMonitor].blurFBShouldRender = false; } void CHyprOpenGLImpl::preWindowPass() { if (!preBlurQueued()) return; - g_pHyprRenderer->m_renderPass.add(makeUnique()); + g_pHyprRenderer->m_sRenderPass.add(makeShared()); } bool CHyprOpenGLImpl::preBlurQueued() { static auto PBLURNEWOPTIMIZE = CConfigValue("decoration:blur:new_optimizations"); static auto PBLUR = CConfigValue("decoration:blur:enabled"); - return m_renderData.pCurrentMonData->blurFBDirty && *PBLURNEWOPTIMIZE && *PBLUR && m_renderData.pCurrentMonData->blurFBShouldRender; + return m_RenderData.pCurrentMonData->blurFBDirty && *PBLURNEWOPTIMIZE && *PBLUR && m_RenderData.pCurrentMonData->blurFBShouldRender; } bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow) { static auto PBLURNEWOPTIMIZE = CConfigValue("decoration:blur:new_optimizations"); static auto PBLURXRAY = CConfigValue("decoration:blur:xray"); - if (!m_renderData.pCurrentMonData->blurFB || !m_renderData.pCurrentMonData->blurFB->getTexture()) + if (!m_RenderData.pCurrentMonData->blurFB.getTexture()) return false; - if (pWindow && pWindow->m_ruleApplicator->xray().hasValue() && !pWindow->m_ruleApplicator->xray().valueOrDefault()) + if (pWindow && pWindow->m_sWindowData.xray.hasValue() && !pWindow->m_sWindowData.xray.valueOrDefault()) return false; - if (pLayer && pLayer->m_ruleApplicator->xray().valueOrDefault() == 0) + if (pLayer && pLayer->xray == 0) return false; - if ((*PBLURNEWOPTIMIZE && pWindow && !pWindow->m_isFloating && !pWindow->onSpecialWorkspace()) || *PBLURXRAY) + if ((*PBLURNEWOPTIMIZE && pWindow && !pWindow->m_bIsFloating && !pWindow->onSpecialWorkspace()) || *PBLURXRAY) return true; - if ((pLayer && pLayer->m_ruleApplicator->xray().valueOrDefault() == 1) || (pWindow && pWindow->m_ruleApplicator->xray().valueOrDefault())) + if ((pLayer && pLayer->xray == 1) || (pWindow && pWindow->m_sWindowData.xray.valueOrDefault())) return true; return false; } -void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox& box, const STextureRenderData& data) { - RASSERT(m_renderData.pMonitor, "Tried to render texture with blur without begin()!"); +void CHyprOpenGLImpl::renderTextureWithBlur(SP tex, const CBox& box, float a, SP pSurface, int round, float roundingPower, bool blockBlurOptimization, + float blurA, float overallA) { + RASSERT(m_RenderData.pMonitor, "Tried to render texture with blur without begin()!"); TRACY_GPU_ZONE("RenderTextureWithBlur"); - static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); - // make a damage region for this window - CRegion texDamage{m_renderData.damage}; + CRegion texDamage{m_RenderData.damage}; texDamage.intersect(box.x, box.y, box.width, box.height); // While renderTextureInternalWithDamage will clip the blur as well, // clipping texDamage here allows blur generation to be optimized. - if (!m_renderData.clipRegion.empty()) - texDamage.intersect(m_renderData.clipRegion); + if (!m_RenderData.clipRegion.empty()) + texDamage.intersect(m_RenderData.clipRegion); if (texDamage.empty()) return; - m_renderData.renderModif.applyToRegion(texDamage); + m_RenderData.renderModif.applyToRegion(texDamage); // amazing hack: the surface has an opaque region! CRegion inverseOpaque; - if (data.a >= 1.f && data.surface && std::round(data.surface->m_current.size.x * m_renderData.pMonitor->m_scale) == box.w && - std::round(data.surface->m_current.size.y * m_renderData.pMonitor->m_scale) == box.h) { - pixman_box32_t surfbox = {0, 0, data.surface->m_current.size.x * data.surface->m_current.scale, data.surface->m_current.size.y * data.surface->m_current.scale}; - inverseOpaque = data.surface->m_current.opaque; - inverseOpaque.invert(&surfbox).intersect(0, 0, data.surface->m_current.size.x * data.surface->m_current.scale, - data.surface->m_current.size.y * data.surface->m_current.scale); + if (a >= 1.f && std::round(pSurface->current.size.x * m_RenderData.pMonitor->scale) == box.w && std::round(pSurface->current.size.y * m_RenderData.pMonitor->scale) == box.h) { + pixman_box32_t surfbox = {0, 0, pSurface->current.size.x * pSurface->current.scale, pSurface->current.size.y * pSurface->current.scale}; + inverseOpaque = pSurface->current.opaque; + inverseOpaque.invert(&surfbox).intersect(0, 0, pSurface->current.size.x * pSurface->current.scale, pSurface->current.size.y * pSurface->current.scale); if (inverseOpaque.empty()) { - renderTextureInternal(tex, box, data); + renderTexture(tex, box, a, round, roundingPower, false, true); return; } } else inverseOpaque = {0, 0, box.width, box.height}; - inverseOpaque.scale(m_renderData.pMonitor->m_scale); + inverseOpaque.scale(m_RenderData.pMonitor->scale); // vvv TODO: layered blur fbs? - const bool USENEWOPTIMIZE = shouldUseNewBlurOptimizations(m_renderData.currentLS.lock(), m_renderData.currentWindow.lock()) && !data.blockBlurOptimization; + const bool USENEWOPTIMIZE = shouldUseNewBlurOptimizations(m_RenderData.currentLS.lock(), m_RenderData.currentWindow.lock()) && !blockBlurOptimization; - SP POUTFB = nullptr; + CFramebuffer* POUTFB = nullptr; if (!USENEWOPTIMIZE) { inverseOpaque.translate(box.pos()); - m_renderData.renderModif.applyToRegion(inverseOpaque); + m_RenderData.renderModif.applyToRegion(inverseOpaque); inverseOpaque.intersect(texDamage); - POUTFB = blurMainFramebufferWithDamage(data.a, &inverseOpaque); + + POUTFB = blurMainFramebufferWithDamage(a, &inverseOpaque); } else - POUTFB = m_renderData.pCurrentMonData->blurFB; + POUTFB = &m_RenderData.pCurrentMonData->blurFB; - m_renderData.currentFB->bind(); + m_RenderData.currentFB->bind(); - auto blurredBG = POUTFB->getTexture(); + // make a stencil for rounded corners to work with blur + scissor(nullptr); // allow the entire window and stencil to render + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); - const auto NEEDS_STENCIL = m_renderData.discardMode != 0 && (!data.blockBlurOptimization || (m_renderData.discardMode & DISCARD_ALPHA)); - if (!*PBLEND) { + glEnable(GL_STENCIL_TEST); - if (NEEDS_STENCIL) { - scissor(nullptr); // allow the entire window and stencil to render - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - setCapStatus(GL_STENCIL_TEST, true); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + if (USENEWOPTIMIZE && !(m_RenderData.discardMode & DISCARD_ALPHA)) + renderRect(box, CHyprColor(0, 0, 0, 0), round, roundingPower); + else + renderTexture(tex, box, a, round, roundingPower, true, true); // discard opaque + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - renderTexture(tex, box, - STextureRenderData{.a = data.a, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = true, - .allowCustomUV = true, - .wrapX = data.wrapX, - .wrapY = data.wrapY}); // discard opaque and alpha < discardOpacity + // stencil done. Render everything. + const auto LASTTL = m_RenderData.primarySurfaceUVTopLeft; + const auto LASTBR = m_RenderData.primarySurfaceUVBottomRight; - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + CBox transformedBox = box; + transformedBox.transform(wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)), m_RenderData.pMonitor->vecTransformedSize.x, + m_RenderData.pMonitor->vecTransformedSize.y); - glStencilFunc(GL_EQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - } + CBox monitorSpaceBox = {transformedBox.pos().x / m_RenderData.pMonitor->vecPixelSize.x * m_RenderData.pMonitor->vecTransformedSize.x, + transformedBox.pos().y / m_RenderData.pMonitor->vecPixelSize.y * m_RenderData.pMonitor->vecTransformedSize.y, + transformedBox.width / m_RenderData.pMonitor->vecPixelSize.x * m_RenderData.pMonitor->vecTransformedSize.x, + transformedBox.height / m_RenderData.pMonitor->vecPixelSize.y * m_RenderData.pMonitor->vecTransformedSize.y}; - // stencil done. Render everything. - const auto LASTTL = m_renderData.primarySurfaceUVTopLeft; - const auto LASTBR = m_renderData.primarySurfaceUVBottomRight; + m_RenderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_RenderData.pMonitor->vecTransformedSize; + m_RenderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_RenderData.pMonitor->vecTransformedSize; - CBox transformedBox = box; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); + static auto PBLURIGNOREOPACITY = CConfigValue("decoration:blur:ignore_opacity"); + setMonitorTransformEnabled(true); + if (!USENEWOPTIMIZE) + setRenderModifEnabled(false); + renderTextureInternalWithDamage(POUTFB->getTexture(), box, (*PBLURIGNOREOPACITY ? blurA : a * blurA) * overallA, texDamage, round, roundingPower, false, false, true); + if (!USENEWOPTIMIZE) + setRenderModifEnabled(true); + setMonitorTransformEnabled(false); - CBox monitorSpaceBox = {transformedBox.pos().x / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, - transformedBox.pos().y / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y, - transformedBox.width / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, - transformedBox.height / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y}; + m_RenderData.primarySurfaceUVTopLeft = LASTTL; + m_RenderData.primarySurfaceUVBottomRight = LASTBR; - m_renderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize; - m_renderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_renderData.pMonitor->m_transformedSize; - - static auto PBLURIGNOREOPACITY = CConfigValue("decoration:blur:ignore_opacity"); - pushMonitorTransformEnabled(true); - bool renderModif = m_renderData.renderModif.enabled; - if (!USENEWOPTIMIZE) - setRenderModifEnabled(false); - renderTextureInternal(blurredBG, box, - STextureRenderData{ - .damage = &texDamage, - .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = false, - .allowCustomUV = true, - .noAA = false, - .wrapX = data.wrapX, - .wrapY = data.wrapY, - }); - if (!USENEWOPTIMIZE) - setRenderModifEnabled(renderModif); - popMonitorTransformEnabled(); - - if (NEEDS_STENCIL) - setCapStatus(GL_STENCIL_TEST, false); - - m_renderData.primarySurfaceUVTopLeft = LASTTL; - m_renderData.primarySurfaceUVBottomRight = LASTBR; - } + // render the window, but clear stencil + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); // draw window - renderTextureInternal(tex, box, - STextureRenderData{ - .damage = &texDamage, - .a = data.a * data.overallA, - .blur = *PBLEND, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = *PBLEND && NEEDS_STENCIL, - .allowCustomUV = true, - .allowDim = true, - .noAA = false, - .wrapX = data.wrapX, - .wrapY = data.wrapY, - .blurredBG = blurredBG, - }); + glDisable(GL_STENCIL_TEST); + renderTextureInternalWithDamage(tex, box, a * overallA, texDamage, round, roundingPower, false, false, true, true); - GLFB(m_renderData.currentFB)->invalidate({GL_STENCIL_ATTACHMENT}); + glStencilMask(0xFF); + glStencilFunc(GL_ALWAYS, 1, 0xFF); scissor(nullptr); } -void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& grad, SBorderRenderData data) { +void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& grad, int round, float roundingPower, int borderSize, float a, int outerRound) { RASSERT((box.width > 0 && box.height > 0), "Tried to render rect with width/height < 0!"); - RASSERT(m_renderData.pMonitor, "Tried to render rect without begin()!"); + RASSERT(m_RenderData.pMonitor, "Tried to render rect without begin()!"); TRACY_GPU_ZONE("RenderBorder"); - if (m_renderData.damage.empty()) + if (m_RenderData.damage.empty() || (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.noBorder.valueOrDefault())) return; CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); + m_RenderData.renderModif.applyToBox(newBox); - if (data.borderSize < 1) + if (borderSize < 1) return; - int scaledBorderSize = std::round(data.borderSize * m_renderData.pMonitor->m_scale); - scaledBorderSize = std::round(scaledBorderSize * m_renderData.renderModif.combinedScale()); + int scaledBorderSize = std::round(borderSize * m_RenderData.pMonitor->scale); + scaledBorderSize = std::round(scaledBorderSize * m_RenderData.renderModif.combinedScale()); // adjust box newBox.x -= scaledBorderSize; @@ -2207,86 +2170,92 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr newBox.width += 2 * scaledBorderSize; newBox.height += 2 * scaledBorderSize; - float round = data.round + (data.round == 0 ? 0 : scaledBorderSize); + round += round == 0 ? 0 : scaledBorderSize; - Mat3x3 matrix = m_renderData.monitorProjection.projectBox( - newBox, Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)), newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + Mat3x3 matrix = m_RenderData.monitorProjection.projectBox( + newBox, wlTransformToHyprutils(invertTransform(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform)), newBox.rot); + Mat3x3 glMatrix = m_RenderData.projection.copy().multiply(matrix); - const auto BLEND = m_blend; + const auto BLEND = m_bBlend; blend(true); - WP shader; + glUseProgram(m_RenderData.pCurrentMonData->m_shBORDER1.program); - const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; - const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); - if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); - passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); - } else - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); +#ifndef GLES2 + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBORDER1.proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBORDER1.proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniform4fv(SHADER_GRADIENT, grad.m_colorsOkLabA.size() / 4, grad.m_colorsOkLabA); - shader->setUniformInt(SHADER_GRADIENT_LENGTH, grad.m_colorsOkLabA.size() / 4); - shader->setUniformFloat(SHADER_ANGLE, sc(grad.m_angle / (std::numbers::pi / 180.0)) % 360 * (std::numbers::pi / 180.0)); - shader->setUniformFloat(SHADER_ALPHA, data.a); - shader->setUniformInt(SHADER_GRADIENT2_LENGTH, 0); + glUniform4fv(m_RenderData.pCurrentMonData->m_shBORDER1.gradient, grad.m_vColorsOkLabA.size() / 4, (float*)grad.m_vColorsOkLabA.data()); + glUniform1i(m_RenderData.pCurrentMonData->m_shBORDER1.gradientLength, grad.m_vColorsOkLabA.size() / 4); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.angle, (int)(grad.m_fAngle / (PI / 180.0)) % 360 * (PI / 180.0)); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.alpha, a); + glUniform1i(m_RenderData.pCurrentMonData->m_shBORDER1.gradient2Length, 0); CBox transformedBox = newBox; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); + transformedBox.transform(wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)), m_RenderData.pMonitor->vecTransformedSize.x, + m_RenderData.pMonitor->vecTransformedSize.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); - shader->setUniformFloat2(SHADER_TOP_LEFT, sc(TOPLEFT.x), sc(TOPLEFT.y)); - shader->setUniformFloat2(SHADER_FULL_SIZE, sc(FULLSIZE.x), sc(FULLSIZE.y)); - shader->setUniformFloat2(SHADER_FULL_SIZE_UNTRANSFORMED, sc(newBox.width), sc(newBox.height)); - shader->setUniformFloat(SHADER_RADIUS, round); - shader->setUniformFloat(SHADER_RADIUS_OUTER, data.outerRound == -1 ? round : data.outerRound); - shader->setUniformFloat(SHADER_ROUNDING_POWER, data.roundingPower); - shader->setUniformFloat(SHADER_THICK, scaledBorderSize); + glUniform2f(m_RenderData.pCurrentMonData->m_shBORDER1.topLeft, (float)TOPLEFT.x, (float)TOPLEFT.y); + glUniform2f(m_RenderData.pCurrentMonData->m_shBORDER1.fullSize, (float)FULLSIZE.x, (float)FULLSIZE.y); + glUniform2f(m_RenderData.pCurrentMonData->m_shBORDER1.fullSizeUntransformed, (float)newBox.width, (float)newBox.height); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.radius, round); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.radiusOuter, outerRound == -1 ? round : outerRound); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.roundingPower, roundingPower); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.thick, scaledBorderSize); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBORDER1.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - // calculate the border's region, which we need to render over. No need to run the shader on - // things outside there - CRegion borderRegion = m_renderData.damage.copy().intersect(newBox); - borderRegion.subtract(box.copy().expand(-scaledBorderSize - round)); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.posAttrib); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib); - if (m_renderData.clipBox.width != 0 && m_renderData.clipBox.height != 0) - borderRegion.intersect(m_renderData.clipBox); + if (m_RenderData.clipBox.width != 0 && m_RenderData.clipBox.height != 0) { + CRegion damageClip{m_RenderData.clipBox.x, m_RenderData.clipBox.y, m_RenderData.clipBox.width, m_RenderData.clipBox.height}; + damageClip.intersect(m_RenderData.damage); - if (!borderRegion.empty()) { - borderRegion.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + if (!damageClip.empty()) { + for (auto const& RECT : damageClip.getRects()) { + scissor(&RECT); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + } + } else { + for (auto const& RECT : m_RenderData.damage.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } - glBindVertexArray(0); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.posAttrib); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib); blend(BLEND); } -void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& grad1, const CGradientValueData& grad2, float lerp, SBorderRenderData data) { +void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& grad1, const CGradientValueData& grad2, float lerp, int round, float roundingPower, int borderSize, + float a, int outerRound) { RASSERT((box.width > 0 && box.height > 0), "Tried to render rect with width/height < 0!"); - RASSERT(m_renderData.pMonitor, "Tried to render rect without begin()!"); + RASSERT(m_RenderData.pMonitor, "Tried to render rect without begin()!"); TRACY_GPU_ZONE("RenderBorder2"); - if (m_renderData.damage.empty()) + if (m_RenderData.damage.empty() || (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.noBorder.valueOrDefault())) return; CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); + m_RenderData.renderModif.applyToBox(newBox); - if (data.borderSize < 1) + if (borderSize < 1) return; - int scaledBorderSize = std::round(data.borderSize * m_renderData.pMonitor->m_scale); - scaledBorderSize = std::round(scaledBorderSize * m_renderData.renderModif.combinedScale()); + int scaledBorderSize = std::round(borderSize * m_RenderData.pMonitor->scale); + scaledBorderSize = std::round(scaledBorderSize * m_RenderData.renderModif.combinedScale()); // adjust box newBox.x -= scaledBorderSize; @@ -2294,196 +2263,200 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr newBox.width += 2 * scaledBorderSize; newBox.height += 2 * scaledBorderSize; - float round = data.round + (data.round == 0 ? 0 : scaledBorderSize); + round += round == 0 ? 0 : scaledBorderSize; - Mat3x3 matrix = m_renderData.monitorProjection.projectBox( - newBox, Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)), newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + Mat3x3 matrix = m_RenderData.monitorProjection.projectBox( + newBox, wlTransformToHyprutils(invertTransform(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform)), newBox.rot); + Mat3x3 glMatrix = m_RenderData.projection.copy().multiply(matrix); - const auto BLEND = m_blend; + const auto BLEND = m_bBlend; blend(true); - WP shader; - const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; - const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); - if (!skipCM) { - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); - passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); - } else - shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); + glUseProgram(m_RenderData.pCurrentMonData->m_shBORDER1.program); - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniform4fv(SHADER_GRADIENT, grad1.m_colorsOkLabA.size() / 4, grad1.m_colorsOkLabA); - shader->setUniformInt(SHADER_GRADIENT_LENGTH, grad1.m_colorsOkLabA.size() / 4); - shader->setUniformFloat(SHADER_ANGLE, sc(grad1.m_angle / (std::numbers::pi / 180.0)) % 360 * (std::numbers::pi / 180.0)); - if (!grad2.m_colorsOkLabA.empty()) - shader->setUniform4fv(SHADER_GRADIENT2, grad2.m_colorsOkLabA.size() / 4, grad2.m_colorsOkLabA); - shader->setUniformInt(SHADER_GRADIENT2_LENGTH, grad2.m_colorsOkLabA.size() / 4); - shader->setUniformFloat(SHADER_ANGLE2, sc(grad2.m_angle / (std::numbers::pi / 180.0)) % 360 * (std::numbers::pi / 180.0)); - shader->setUniformFloat(SHADER_ALPHA, data.a); - shader->setUniformFloat(SHADER_GRADIENT_LERP, lerp); +#ifndef GLES2 + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBORDER1.proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBORDER1.proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif + + glUniform4fv(m_RenderData.pCurrentMonData->m_shBORDER1.gradient, grad1.m_vColorsOkLabA.size() / 4, (float*)grad1.m_vColorsOkLabA.data()); + glUniform1i(m_RenderData.pCurrentMonData->m_shBORDER1.gradientLength, grad1.m_vColorsOkLabA.size() / 4); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.angle, (int)(grad1.m_fAngle / (PI / 180.0)) % 360 * (PI / 180.0)); + if (grad2.m_vColorsOkLabA.size() > 0) + glUniform4fv(m_RenderData.pCurrentMonData->m_shBORDER1.gradient2, grad2.m_vColorsOkLabA.size() / 4, (float*)grad2.m_vColorsOkLabA.data()); + glUniform1i(m_RenderData.pCurrentMonData->m_shBORDER1.gradient2Length, grad2.m_vColorsOkLabA.size() / 4); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.angle2, (int)(grad2.m_fAngle / (PI / 180.0)) % 360 * (PI / 180.0)); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.alpha, a); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.gradientLerp, lerp); CBox transformedBox = newBox; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); + transformedBox.transform(wlTransformToHyprutils(invertTransform(m_RenderData.pMonitor->transform)), m_RenderData.pMonitor->vecTransformedSize.x, + m_RenderData.pMonitor->vecTransformedSize.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); - shader->setUniformFloat2(SHADER_TOP_LEFT, sc(TOPLEFT.x), sc(TOPLEFT.y)); - shader->setUniformFloat2(SHADER_FULL_SIZE, sc(FULLSIZE.x), sc(FULLSIZE.y)); - shader->setUniformFloat2(SHADER_FULL_SIZE_UNTRANSFORMED, sc(newBox.width), sc(newBox.height)); - shader->setUniformFloat(SHADER_RADIUS, round); - shader->setUniformFloat(SHADER_RADIUS_OUTER, data.outerRound == -1 ? round : data.outerRound); - shader->setUniformFloat(SHADER_ROUNDING_POWER, data.roundingPower); - shader->setUniformFloat(SHADER_THICK, scaledBorderSize); + glUniform2f(m_RenderData.pCurrentMonData->m_shBORDER1.topLeft, (float)TOPLEFT.x, (float)TOPLEFT.y); + glUniform2f(m_RenderData.pCurrentMonData->m_shBORDER1.fullSize, (float)FULLSIZE.x, (float)FULLSIZE.y); + glUniform2f(m_RenderData.pCurrentMonData->m_shBORDER1.fullSizeUntransformed, (float)newBox.width, (float)newBox.height); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.radius, round); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.radiusOuter, outerRound == -1 ? round : outerRound); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.roundingPower, roundingPower); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.thick, scaledBorderSize); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBORDER1.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - // calculate the border's region, which we need to render over. No need to run the shader on - // things outside there - CRegion borderRegion = m_renderData.damage.copy().intersect(newBox); - borderRegion.subtract(box.copy().expand(-scaledBorderSize - round)); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.posAttrib); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib); - if (m_renderData.clipBox.width != 0 && m_renderData.clipBox.height != 0) - borderRegion.intersect(m_renderData.clipBox); + if (m_RenderData.clipBox.width != 0 && m_RenderData.clipBox.height != 0) { + CRegion damageClip{m_RenderData.clipBox.x, m_RenderData.clipBox.y, m_RenderData.clipBox.width, m_RenderData.clipBox.height}; + damageClip.intersect(m_RenderData.damage); - if (!borderRegion.empty()) { - borderRegion.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + if (!damageClip.empty()) { + for (auto const& RECT : damageClip.getRects()) { + scissor(&RECT); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + } + } else { + for (auto const& RECT : m_RenderData.damage.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } - glBindVertexArray(0); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.posAttrib); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib); + blend(BLEND); } void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roundingPower, int range, const CHyprColor& color, float a) { - RASSERT(m_renderData.pMonitor, "Tried to render shadow without begin()!"); + RASSERT(m_RenderData.pMonitor, "Tried to render shadow without begin()!"); RASSERT((box.width > 0 && box.height > 0), "Tried to render shadow with width/height < 0!"); + RASSERT(m_RenderData.currentWindow, "Tried to render shadow without a window!"); - if (m_renderData.damage.empty()) + if (m_RenderData.damage.empty()) return; TRACY_GPU_ZONE("RenderShadow"); CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); + m_RenderData.renderModif.applyToBox(newBox); static auto PSHADOWPOWER = CConfigValue("decoration:shadow:render_power"); - const auto SHADOWPOWER = std::clamp(sc(*PSHADOWPOWER), 1, 4); + const auto SHADOWPOWER = std::clamp((int)*PSHADOWPOWER, 1, 4); const auto col = color; - Mat3x3 matrix = m_renderData.monitorProjection.projectBox( - newBox, Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)), newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + Mat3x3 matrix = m_RenderData.monitorProjection.projectBox( + newBox, wlTransformToHyprutils(invertTransform(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform)), newBox.rot); + Mat3x3 glMatrix = m_RenderData.projection.copy().multiply(matrix); blend(true); - const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; - const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); - auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); - if (!skipCM) - passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); + glUseProgram(m_RenderData.pCurrentMonData->m_shSHADOW.program); - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformFloat4(SHADER_COLOR, col.r, col.g, col.b, col.a * a); +#ifndef GLES2 + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shSHADOW.proj, 1, GL_TRUE, glMatrix.getMatrix().data()); +#else + glMatrix.transpose(); + glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shSHADOW.proj, 1, GL_FALSE, glMatrix.getMatrix().data()); +#endif + glUniform4f(m_RenderData.pCurrentMonData->m_shSHADOW.color, col.r, col.g, col.b, col.a * a); const auto TOPLEFT = Vector2D(range + round, range + round); const auto BOTTOMRIGHT = Vector2D(newBox.width - (range + round), newBox.height - (range + round)); const auto FULLSIZE = Vector2D(newBox.width, newBox.height); // Rounded corners - shader->setUniformFloat2(SHADER_TOP_LEFT, sc(TOPLEFT.x), sc(TOPLEFT.y)); - shader->setUniformFloat2(SHADER_BOTTOM_RIGHT, sc(BOTTOMRIGHT.x), sc(BOTTOMRIGHT.y)); - shader->setUniformFloat2(SHADER_FULL_SIZE, sc(FULLSIZE.x), sc(FULLSIZE.y)); - shader->setUniformFloat(SHADER_RADIUS, range + round); - shader->setUniformFloat(SHADER_ROUNDING_POWER, roundingPower); - shader->setUniformFloat(SHADER_RANGE, range); - shader->setUniformFloat(SHADER_SHADOW_POWER, SHADOWPOWER); + glUniform2f(m_RenderData.pCurrentMonData->m_shSHADOW.topLeft, (float)TOPLEFT.x, (float)TOPLEFT.y); + glUniform2f(m_RenderData.pCurrentMonData->m_shSHADOW.bottomRight, (float)BOTTOMRIGHT.x, (float)BOTTOMRIGHT.y); + glUniform2f(m_RenderData.pCurrentMonData->m_shSHADOW.fullSize, (float)FULLSIZE.x, (float)FULLSIZE.y); + glUniform1f(m_RenderData.pCurrentMonData->m_shSHADOW.radius, range + round); + glUniform1f(m_RenderData.pCurrentMonData->m_shSHADOW.roundingPower, roundingPower); + glUniform1f(m_RenderData.pCurrentMonData->m_shSHADOW.range, range); + glUniform1f(m_RenderData.pCurrentMonData->m_shSHADOW.shadowPower, SHADOWPOWER); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shSHADOW.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); + glVertexAttribPointer(m_RenderData.pCurrentMonData->m_shSHADOW.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); - if (m_renderData.clipBox.width != 0 && m_renderData.clipBox.height != 0) { - CRegion damageClip{m_renderData.clipBox.x, m_renderData.clipBox.y, m_renderData.clipBox.width, m_renderData.clipBox.height}; - damageClip.intersect(m_renderData.damage); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shSHADOW.posAttrib); + glEnableVertexAttribArray(m_RenderData.pCurrentMonData->m_shSHADOW.texAttrib); + + if (m_RenderData.clipBox.width != 0 && m_RenderData.clipBox.height != 0) { + CRegion damageClip{m_RenderData.clipBox.x, m_RenderData.clipBox.y, m_RenderData.clipBox.width, m_RenderData.clipBox.height}; + damageClip.intersect(m_RenderData.damage); if (!damageClip.empty()) { - damageClip.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + for (auto const& RECT : damageClip.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } } else { - m_renderData.damage.forEachRect([this](const auto& RECT) { - scissor(&RECT, m_renderData.transformDamage); + for (auto const& RECT : m_RenderData.damage.getRects()) { + scissor(&RECT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - }); + } } - glBindVertexArray(0); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shSHADOW.posAttrib); + glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shSHADOW.texAttrib); } void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) { - if (!m_renderData.pCurrentMonData->monitorMirrorFB) - m_renderData.pCurrentMonData->monitorMirrorFB = g_pHyprRenderer->createFB(); - if (!m_renderData.pCurrentMonData->monitorMirrorFB->isAllocated()) - m_renderData.pCurrentMonData->monitorMirrorFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, - m_renderData.pMonitor->m_output->state->state().drmFormat); + if (!m_RenderData.pCurrentMonData->monitorMirrorFB.isAllocated()) + m_RenderData.pCurrentMonData->monitorMirrorFB.alloc(m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y, + m_RenderData.pMonitor->output->state->state().drmFormat); - m_renderData.pCurrentMonData->monitorMirrorFB->bind(); + m_RenderData.pCurrentMonData->monitorMirrorFB.bind(); blend(false); - renderTexture(m_renderData.currentFB->getTexture(), box, - STextureRenderData{ - .damage = &m_renderData.finalDamage, - .a = 1.F, - .round = 0, - .discardActive = false, - .allowCustomUV = false, - .cmBackToSRGB = true, - }); + renderTexture(m_RenderData.currentFB->getTexture(), box, 1.f, 0, 2.0f, false, false); blend(true); - m_renderData.currentFB->bind(); + m_RenderData.currentFB->bind(); } void CHyprOpenGLImpl::renderMirrored() { - auto monitor = m_renderData.pMonitor; - auto mirrored = monitor->m_mirrorOf; + auto monitor = m_RenderData.pMonitor; + auto mirrored = monitor->pMirrorOf; - const double scale = std::min(monitor->m_transformedSize.x / mirrored->m_transformedSize.x, monitor->m_transformedSize.y / mirrored->m_transformedSize.y); - CBox monbox = {0, 0, mirrored->m_transformedSize.x * scale, mirrored->m_transformedSize.y * scale}; + const double scale = std::min(monitor->vecTransformedSize.x / mirrored->vecTransformedSize.x, monitor->vecTransformedSize.y / mirrored->vecTransformedSize.y); + CBox monbox = {0, 0, mirrored->vecTransformedSize.x * scale, mirrored->vecTransformedSize.y * scale}; // transform box as it will be drawn on a transformed projection - monbox.transform(Math::wlTransformToHyprutils(mirrored->m_transform), mirrored->m_transformedSize.x * scale, mirrored->m_transformedSize.y * scale); + monbox.transform(wlTransformToHyprutils(mirrored->transform), mirrored->vecTransformedSize.x * scale, mirrored->vecTransformedSize.y * scale); - monbox.x = (monitor->m_transformedSize.x - monbox.w) / 2; - monbox.y = (monitor->m_transformedSize.y - monbox.h) / 2; + monbox.x = (monitor->vecTransformedSize.x - monbox.w) / 2; + monbox.y = (monitor->vecTransformedSize.y - monbox.h) / 2; - auto PFB = m_monitorRenderResources[mirrored].monitorMirrorFB; + const auto PFB = &m_mMonitorRenderResources[mirrored].monitorMirrorFB; if (!PFB->isAllocated() || !PFB->getTexture()) return; - g_pHyprRenderer->m_renderPass.add(makeUnique(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)})); + g_pHyprRenderer->m_sRenderPass.add(makeShared(CClearPassElement::SClearData{CHyprColor(0, 0, 0, 0)})); CTexPassElement::SRenderData data; data.tex = PFB->getTexture(); data.box = monbox; data.replaceProjection = Mat3x3::identity() - .translate(monitor->m_pixelSize / 2.0) - .transform(Math::wlTransformToHyprutils(monitor->m_transform)) - .transform(Math::wlTransformToHyprutils(Math::invertTransform(mirrored->m_transform))) - .translate(-monitor->m_transformedSize / 2.0); + .translate(monitor->vecPixelSize / 2.0) + .transform(wlTransformToHyprutils(monitor->transform)) + .transform(wlTransformToHyprutils(invertTransform(mirrored->transform))) + .translate(-monitor->vecTransformedSize / 2.0); - g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const CAIROSURFACE, double offsetY, const Vector2D& size) { @@ -2492,7 +2465,7 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const static auto FALLBACKFONT = CConfigValue("misc:font_family"); const auto FONTFAMILY = *PSPLASHFONT != STRVAL_EMPTY ? *PSPLASHFONT : *FALLBACKFONT; - const auto FONTSIZE = sc(size.y / 76); + const auto FONTSIZE = (int)(size.y / 76); const auto COLOR = CHyprColor(*PSPLASHCOLOR); PangoLayout* layoutText = pango_cairo_create_layout(CAIRO); @@ -2507,7 +2480,7 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a); int textW = 0, textH = 0; - pango_layout_set_text(layoutText, g_pCompositor->m_currentSplash.c_str(), -1); + pango_layout_set_text(layoutText, g_pCompositor->m_szCurrentSplash.c_str(), -1); pango_layout_get_size(layoutText, &textW, &textH); textW /= PANGO_SCALE; textH /= PANGO_SCALE; @@ -2521,7 +2494,8 @@ void CHyprOpenGLImpl::renderSplash(cairo_t* const CAIRO, cairo_surface_t* const cairo_surface_flush(CAIROSURFACE); } -std::string CHyprOpenGLImpl::resolveAssetPath(const std::string& filename) { +SP CHyprOpenGLImpl::loadAsset(const std::string& filename) { + std::string fullPath; for (auto& e : ASSET_PATHS) { std::string p = std::string{e} + "/hypr/" + filename; @@ -2530,67 +2504,58 @@ std::string CHyprOpenGLImpl::resolveAssetPath(const std::string& filename) { fullPath = p; break; } else - Log::logger->log(Log::DEBUG, "resolveAssetPath: looking at {} unsuccessful: ec {}", filename, ec.message()); + Debug::log(LOG, "loadAsset: looking at {} unsuccessful: ec {}", filename, ec.message()); } if (fullPath.empty()) { - m_failedAssetsNo++; - Log::logger->log(Log::ERR, "resolveAssetPath: looking for {} failed (no provider found)", filename); - return ""; + failedAssetsNo++; + Debug::log(ERR, "loadAsset: looking for {} failed (no provider found)", filename); + return m_pMissingAssetTexture; } - return fullPath; -} - -SP CHyprOpenGLImpl::loadAsset(const std::string& filename) { - - const std::string fullPath = resolveAssetPath(filename); - - if (fullPath.empty()) - return m_missingAssetTexture; - const auto CAIROSURFACE = cairo_image_surface_create_from_png(fullPath.c_str()); if (!CAIROSURFACE) { - m_failedAssetsNo++; - Log::logger->log(Log::ERR, "loadAsset: failed to load {} (corrupt / inaccessible / not png)", fullPath); - return m_missingAssetTexture; + failedAssetsNo++; + Debug::log(ERR, "loadAsset: failed to load {} (corrupt / inaccessible / not png)", fullPath); + return m_pMissingAssetTexture; } - auto tex = texFromCairo(CAIROSURFACE); + const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROSURFACE); + auto tex = makeShared(); + + tex->allocate(); + tex->m_vSize = {cairo_image_surface_get_width(CAIROSURFACE), cairo_image_surface_get_height(CAIROSURFACE)}; + + const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? +#ifdef GLES2 + GL_RGB32F_EXT : +#else + GL_RGB32F : +#endif + GL_RGBA; + const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; + const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; + + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#ifndef GLES2 + if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { + 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, glIFormat, tex->m_vSize.x, tex->m_vSize.y, 0, glFormat, glType, DATA); cairo_surface_destroy(CAIROSURFACE); return tex; } -SP CHyprOpenGLImpl::texFromCairo(cairo_surface_t* cairo) { - const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); - auto tex = makeShared(); - - tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); - - const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; - const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; - const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; - - const auto DATA = cairo_image_surface_get_data(cairo); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - } - - glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); - - return tex; -} - -SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col, int pt, bool italic, const std::string& fontFamily, int maxWidth, int weight) { - SP tex = makeShared(); +SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col, int pt, bool italic, const std::string& fontFamily, int maxWidth) { + SP tex = makeShared(); static auto FONT = CConfigValue("misc:font_family"); @@ -2607,7 +2572,7 @@ SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col pango_font_description_set_family_static(pangoFD, FONTFAMILY.c_str()); pango_font_description_set_absolute_size(pangoFD, FONTSIZE * PANGO_SCALE); pango_font_description_set_style(pangoFD, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); - pango_font_description_set_weight(pangoFD, sc(weight)); + pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL); pango_layout_set_font_description(layoutText, pangoFD); cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a); @@ -2638,7 +2603,7 @@ SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col pango_font_description_set_family_static(pangoFD, FONTFAMILY.c_str()); pango_font_description_set_absolute_size(pangoFD, FONTSIZE * PANGO_SCALE); pango_font_description_set_style(pangoFD, italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); - pango_font_description_set_weight(pangoFD, sc(weight)); + pango_font_description_set_weight(pangoFD, PANGO_WEIGHT_NORMAL); pango_layout_set_font_description(layoutText, pangoFD); pango_layout_set_text(layoutText, text.c_str(), -1); @@ -2652,15 +2617,18 @@ SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col cairo_surface_flush(CAIROSURFACE); - tex->allocate({cairo_image_surface_get_width(CAIROSURFACE), cairo_image_surface_get_height(CAIROSURFACE)}); + tex->allocate(); + tex->m_vSize = {cairo_image_surface_get_width(CAIROSURFACE), cairo_image_surface_get_height(CAIROSURFACE)}; const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex->m_size.x, tex->m_size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#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, tex->m_vSize.x, tex->m_vSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); cairo_destroy(CAIRO); cairo_surface_destroy(CAIROSURFACE); @@ -2669,8 +2637,8 @@ SP CHyprOpenGLImpl::renderText(const std::string& text, CHyprColor col } void CHyprOpenGLImpl::initMissingAssetTexture() { - SP tex = makeShared(); - tex->allocate({512, 512}); + SP tex = makeShared(); + tex->allocate(); const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 512, 512); const auto CAIRO = cairo_create(CAIROSURFACE); @@ -2689,81 +2657,55 @@ void CHyprOpenGLImpl::initMissingAssetTexture() { cairo_surface_flush(CAIROSURFACE); - tex->m_size = {512, 512}; + tex->m_vSize = {512, 512}; // copy the data to an OpenGL texture we have const GLint glFormat = GL_RGBA; const GLint glType = GL_UNSIGNED_BYTE; const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); + glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#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, glFormat, tex->m_vSize.x, tex->m_vSize.y, 0, glFormat, glType, DATA); cairo_surface_destroy(CAIROSURFACE); cairo_destroy(CAIRO); - m_missingAssetTexture = tex; -} - -WP CHyprOpenGLImpl::useShader(WP prog) { - if (m_currentProgram == prog->program()) - return prog; - - glUseProgram(prog->program()); - m_currentProgram = prog->program(); - - return prog; + m_pMissingAssetTexture = tex; } void CHyprOpenGLImpl::initAssets() { initMissingAssetTexture(); - m_screencopyDeniedTexture = renderText("Permission denied to share screen", Colors::WHITE, 20); + m_pLockDeadTexture = loadAsset("lockdead.png"); + m_pLockDead2Texture = loadAsset("lockdead2.png"); + + m_pLockTtyTextTexture = renderText(std::format("Running on tty {}", + g_pCompositor->m_pAqBackend->hasSession() && g_pCompositor->m_pAqBackend->session->vt > 0 ? + std::to_string(g_pCompositor->m_pAqBackend->session->vt) : + "unknown"), + CHyprColor{0.9F, 0.9F, 0.9F, 0.7F}, 20, true); + + ensureBackgroundTexturePresence(); } -void CHyprOpenGLImpl::ensureLockTexturesRendered(bool load) { - static bool loaded = false; - - if (loaded == load) - return; - - loaded = load; - - if (load) { - // this will cause a small hitch. I don't think we can do much, other than wasting VRAM and having this loaded all the time. - m_lockDeadTexture = loadAsset("lockdead.png"); - m_lockDead2Texture = loadAsset("lockdead2.png"); - - const auto VT = g_pCompositor->getVTNr(); - - m_lockTtyTextTexture = renderText(std::format("Running on tty {}", VT.has_value() ? std::to_string(*VT) : "unknown"), CHyprColor{0.9F, 0.9F, 0.9F, 0.7F}, 20, true); - } else { - m_lockDeadTexture.reset(); - m_lockDead2Texture.reset(); - m_lockTtyTextTexture.reset(); - } -} - -void CHyprOpenGLImpl::requestBackgroundResource() { - if (m_backgroundResource) - return; - +void CHyprOpenGLImpl::ensureBackgroundTexturePresence() { static auto PNOWALLPAPER = CConfigValue("misc:disable_hyprland_logo"); static auto PFORCEWALLPAPER = CConfigValue("misc:force_default_wallpaper"); - const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, sc(-1), sc(2)); + const auto FORCEWALLPAPER = std::clamp(*PFORCEWALLPAPER, static_cast(-1L), static_cast(2L)); if (*PNOWALLPAPER) - return; + m_pBackgroundTexture.reset(); + else if (!m_pBackgroundTexture) { + // create the default background texture + std::string texPath = "wall"; - static bool once = true; - static std::string texPath = "wall"; - - if (once) { // get the adequate tex if (FORCEWALLPAPER == -1) { std::mt19937_64 engine(time(nullptr)); @@ -2771,72 +2713,40 @@ void CHyprOpenGLImpl::requestBackgroundResource() { texPath += std::to_string(distribution(engine)); } else - texPath += std::to_string(std::clamp(*PFORCEWALLPAPER, sc(0), sc(2))); + texPath += std::to_string(std::clamp(*PFORCEWALLPAPER, (int64_t)0, (int64_t)2)); texPath += ".png"; - texPath = resolveAssetPath(texPath); - - once = false; + m_pBackgroundTexture = loadAsset(texPath); } - - if (texPath.empty()) { - m_backgroundResourceFailed = true; - return; - } - - m_backgroundResource = makeAtomicShared(texPath); - - // doesn't have to be ASP as it's passed - SP executor = makeShared([] { - for (const auto& m : g_pCompositor->m_monitors) { - g_pHyprRenderer->damageMonitor(m); - } - }); - - m_backgroundResource->m_events.finished.listenStatic([executor] { - // this is in the worker thread. - executor->signal(); - }); - - g_pAsyncResourceGatherer->enqueue(m_backgroundResource); } void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { - RASSERT(m_renderData.pMonitor, "Tried to createBGTex without begin()!"); + RASSERT(m_RenderData.pMonitor, "Tried to createBGTex without begin()!"); - Log::logger->log(Log::DEBUG, "Creating a texture for BGTex"); + Debug::log(LOG, "Creating a texture for BGTex"); static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); static auto PNOSPLASH = CConfigValue("misc:disable_splash_rendering"); - if (*PRENDERTEX || m_backgroundResourceFailed) + if (*PRENDERTEX) return; - if (!m_backgroundResource) { - // queue the asset to be created - requestBackgroundResource(); - return; - } - - if (!m_backgroundResource->m_ready) - return; - - if (!m_monitorBGFBs.contains(pMonitor)) - m_monitorBGFBs[pMonitor] = g_pHyprRenderer->createFB(); - // release the last tex if exists - auto PFB = m_monitorBGFBs[pMonitor]; + const auto PFB = &m_mMonitorBGFBs[pMonitor]; PFB->release(); - PFB->alloc(pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y, pMonitor->m_output->state->state().drmFormat); + PFB->alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->output->state->state().drmFormat); + + if (!m_pBackgroundTexture) // ?!?!?! + return; // create a new one with cairo - SP tex = makeShared(); + SP tex = makeShared(); - tex->allocate(pMonitor->m_pixelSize); + tex->allocate(); - const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pMonitor->m_pixelSize.x, pMonitor->m_pixelSize.y); + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y); const auto CAIRO = cairo_create(CAIROSURFACE); cairo_set_antialias(CAIRO, CAIRO_ANTIALIAS_GOOD); @@ -2847,24 +2757,25 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { cairo_restore(CAIRO); if (!*PNOSPLASH) - renderSplash(CAIRO, CAIROSURFACE, 0.02 * pMonitor->m_pixelSize.y, pMonitor->m_pixelSize); + renderSplash(CAIRO, CAIROSURFACE, 0.02 * pMonitor->vecPixelSize.y, pMonitor->vecPixelSize); cairo_surface_flush(CAIROSURFACE); - tex->m_size = pMonitor->m_pixelSize; + tex->m_vSize = pMonitor->vecPixelSize; // copy the data to an OpenGL texture we have const GLint glFormat = GL_RGBA; const GLint glType = GL_UNSIGNED_BYTE; const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - - glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); + glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +#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, glFormat, tex->m_vSize.x, tex->m_vSize.y, 0, glFormat, glType, DATA); cairo_surface_destroy(CAIROSURFACE); cairo_destroy(CAIRO); @@ -2876,237 +2787,168 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(PHLMONITOR pMonitor) { blend(true); clear(CHyprColor{0, 0, 0, 1}); - SP backgroundTexture = texFromCairo(m_backgroundResource->m_asset.cairoSurface->cairo()); - // first render the background - if (backgroundTexture) { - const double MONRATIO = m_renderData.pMonitor->m_transformedSize.x / m_renderData.pMonitor->m_transformedSize.y; - const double WPRATIO = backgroundTexture->m_size.x / backgroundTexture->m_size.y; + if (m_pBackgroundTexture) { + const double MONRATIO = m_RenderData.pMonitor->vecTransformedSize.x / m_RenderData.pMonitor->vecTransformedSize.y; + const double WPRATIO = m_pBackgroundTexture->m_vSize.x / m_pBackgroundTexture->m_vSize.y; Vector2D origin; double scale = 1.0; if (MONRATIO > WPRATIO) { - scale = m_renderData.pMonitor->m_transformedSize.x / backgroundTexture->m_size.x; - origin.y = (m_renderData.pMonitor->m_transformedSize.y - backgroundTexture->m_size.y * scale) / 2.0; + scale = m_RenderData.pMonitor->vecTransformedSize.x / m_pBackgroundTexture->m_vSize.x; + origin.y = (m_RenderData.pMonitor->vecTransformedSize.y - m_pBackgroundTexture->m_vSize.y * scale) / 2.0; } else { - scale = m_renderData.pMonitor->m_transformedSize.y / backgroundTexture->m_size.y; - origin.x = (m_renderData.pMonitor->m_transformedSize.x - backgroundTexture->m_size.x * scale) / 2.0; + scale = m_RenderData.pMonitor->vecTransformedSize.y / m_pBackgroundTexture->m_vSize.y; + origin.x = (m_RenderData.pMonitor->vecTransformedSize.x - m_pBackgroundTexture->m_vSize.x * scale) / 2.0; } - CBox texbox = CBox{origin, backgroundTexture->m_size * scale}; - renderTextureInternal(backgroundTexture, texbox, {.damage = &fakeDamage, .a = 1.0}); + CBox texbox = CBox{origin, m_pBackgroundTexture->m_vSize * scale}; + renderTextureInternalWithDamage(m_pBackgroundTexture, texbox, 1.0, fakeDamage); } - CBox monbox = {{}, pMonitor->m_pixelSize}; - renderTextureInternal(tex, monbox, {.damage = &fakeDamage, .a = 1.0}); + CBox monbox = {{}, pMonitor->vecPixelSize}; + renderTextureInternalWithDamage(tex, monbox, 1.0, fakeDamage); // bind back - if (m_renderData.currentFB) - m_renderData.currentFB->bind(); + if (m_RenderData.currentFB) + m_RenderData.currentFB->bind(); - Log::logger->log(Log::DEBUG, "Background created for monitor {}", pMonitor->m_name); - - // clear the resource after we're done using it - g_pEventLoopManager->doLater([this] { m_backgroundResource.reset(); }); - - // set the animation to start for fading this background in nicely - pMonitor->m_backgroundOpacity->setValueAndWarp(0.F); - *pMonitor->m_backgroundOpacity = 1.F; + Debug::log(LOG, "Background created for monitor {}", pMonitor->szName); } void CHyprOpenGLImpl::clearWithTex() { - RASSERT(m_renderData.pMonitor, "Tried to render BGtex without begin()!"); + RASSERT(m_RenderData.pMonitor, "Tried to render BGtex without begin()!"); - static auto PBACKGROUNDCOLOR = CConfigValue("misc:background_color"); + auto TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor); - auto TEXIT = m_monitorBGFBs.find(m_renderData.pMonitor); - - if (TEXIT == m_monitorBGFBs.end()) { - createBGTextureForMonitor(m_renderData.pMonitor.lock()); - g_pHyprRenderer->m_renderPass.add(makeUnique(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); + if (TEXIT == m_mMonitorBGFBs.end()) { + createBGTextureForMonitor(m_RenderData.pMonitor.lock()); + TEXIT = m_mMonitorBGFBs.find(m_RenderData.pMonitor); } - if (TEXIT != m_monitorBGFBs.end()) { + if (TEXIT != m_mMonitorBGFBs.end()) { CTexPassElement::SRenderData data; - data.box = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; - data.a = m_renderData.pMonitor->m_backgroundOpacity->value(); + data.box = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; data.flipEndFrame = true; - data.tex = TEXIT->second->getTexture(); - g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); + data.tex = TEXIT->second.getTexture(); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } } -void CHyprOpenGLImpl::destroyMonitorResources(PHLMONITORREF pMonitor) { +void CHyprOpenGLImpl::destroyMonitorResources(PHLMONITOR pMonitor) { g_pHyprRenderer->makeEGLCurrent(); if (!g_pHyprOpenGL) return; - auto RESIT = g_pHyprOpenGL->m_monitorRenderResources.find(pMonitor); - if (RESIT != g_pHyprOpenGL->m_monitorRenderResources.end()) { - RESIT->second.mirrorFB.reset(); - RESIT->second.offloadFB.reset(); - RESIT->second.mirrorSwapFB.reset(); - RESIT->second.monitorMirrorFB.reset(); - RESIT->second.blurFB.reset(); - RESIT->second.offMainFB.reset(); - RESIT->second.stencilTex.reset(); - g_pHyprOpenGL->m_monitorRenderResources.erase(RESIT); + auto RESIT = g_pHyprOpenGL->m_mMonitorRenderResources.find(pMonitor); + if (RESIT != g_pHyprOpenGL->m_mMonitorRenderResources.end()) { + RESIT->second.mirrorFB.release(); + RESIT->second.offloadFB.release(); + RESIT->second.mirrorSwapFB.release(); + RESIT->second.monitorMirrorFB.release(); + RESIT->second.blurFB.release(); + RESIT->second.offMainFB.release(); + RESIT->second.stencilTex->destroyTexture(); + g_pHyprOpenGL->m_mMonitorRenderResources.erase(RESIT); } - auto TEXIT = g_pHyprOpenGL->m_monitorBGFBs.find(pMonitor); - if (TEXIT != g_pHyprOpenGL->m_monitorBGFBs.end()) { - TEXIT->second.reset(); - g_pHyprOpenGL->m_monitorBGFBs.erase(TEXIT); + auto TEXIT = g_pHyprOpenGL->m_mMonitorBGFBs.find(pMonitor); + if (TEXIT != g_pHyprOpenGL->m_mMonitorBGFBs.end()) { + TEXIT->second.release(); + g_pHyprOpenGL->m_mMonitorBGFBs.erase(TEXIT); } - if (pMonitor) - Log::logger->log(Log::DEBUG, "Monitor {} -> destroyed all render data", pMonitor->m_name); + Debug::log(LOG, "Monitor {} -> destroyed all render data", pMonitor->szName); } void CHyprOpenGLImpl::saveMatrix() { - m_renderData.savedProjection = m_renderData.projection; + m_RenderData.savedProjection = m_RenderData.projection; } void CHyprOpenGLImpl::setMatrixScaleTranslate(const Vector2D& translate, const float& scale) { - m_renderData.projection.scale(scale).translate(translate); + m_RenderData.projection.scale(scale).translate(translate); } void CHyprOpenGLImpl::restoreMatrix() { - m_renderData.projection = m_renderData.savedProjection; + m_RenderData.projection = m_RenderData.savedProjection; } void CHyprOpenGLImpl::bindOffMain() { - if (!m_renderData.pCurrentMonData->offMainFB) - m_renderData.pCurrentMonData->offMainFB = g_pHyprRenderer->createFB(); + if (!m_RenderData.pCurrentMonData->offMainFB.isAllocated()) { + m_RenderData.pCurrentMonData->offMainFB.alloc(m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y, + m_RenderData.pMonitor->output->state->state().drmFormat); - if (!m_renderData.pCurrentMonData->offMainFB->isAllocated()) { - m_renderData.pCurrentMonData->offMainFB->addStencil(m_renderData.pCurrentMonData->stencilTex); - m_renderData.pCurrentMonData->offMainFB->alloc(m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y, - m_renderData.pMonitor->m_output->state->state().drmFormat); + m_RenderData.pCurrentMonData->offMainFB.addStencil(m_RenderData.pCurrentMonData->stencilTex); } - m_renderData.pCurrentMonData->offMainFB->bind(); + m_RenderData.pCurrentMonData->offMainFB.bind(); clear(CHyprColor(0, 0, 0, 0)); - m_renderData.currentFB = m_renderData.pCurrentMonData->offMainFB; + m_RenderData.currentFB = &m_RenderData.pCurrentMonData->offMainFB; } -void CHyprOpenGLImpl::renderOffToMain(CGLFramebuffer* off) { - CBox monbox = {0, 0, m_renderData.pMonitor->m_transformedSize.x, m_renderData.pMonitor->m_transformedSize.y}; +void CHyprOpenGLImpl::renderOffToMain(CFramebuffer* off) { + CBox monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y}; renderTexturePrimitive(off->getTexture(), monbox); } void CHyprOpenGLImpl::bindBackOnMain() { - m_renderData.mainFB->bind(); - m_renderData.currentFB = m_renderData.mainFB; + m_RenderData.mainFB->bind(); + m_RenderData.currentFB = m_RenderData.mainFB; } -void CHyprOpenGLImpl::pushMonitorTransformEnabled(bool enabled) { - m_monitorTransformStack.push(enabled); - m_monitorTransformEnabled = enabled; -} - -void CHyprOpenGLImpl::popMonitorTransformEnabled() { - m_monitorTransformStack.pop(); - m_monitorTransformEnabled = m_monitorTransformStack.top(); +void CHyprOpenGLImpl::setMonitorTransformEnabled(bool enabled) { + m_bEndFrame = enabled; } void CHyprOpenGLImpl::setRenderModifEnabled(bool enabled) { - m_renderData.renderModif.enabled = enabled; + m_RenderData.renderModif.enabled = enabled; } -void CHyprOpenGLImpl::setViewport(GLint x, GLint y, GLsizei width, GLsizei height) { - if (m_lastViewport.x == x && m_lastViewport.y == y && m_lastViewport.width == width && m_lastViewport.height == height) - return; - - glViewport(x, y, width, height); - m_lastViewport = {.x = x, .y = y, .width = width, .height = height}; -} - -void CHyprOpenGLImpl::setCapStatus(int cap, bool status) { - const auto getCapIndex = [cap]() { - switch (cap) { - case GL_BLEND: return CAP_STATUS_BLEND; - case GL_SCISSOR_TEST: return CAP_STATUS_SCISSOR_TEST; - case GL_STENCIL_TEST: return CAP_STATUS_STENCIL_TEST; - default: return CAP_STATUS_END; - } - }; - - auto idx = getCapIndex(); - - if (idx == CAP_STATUS_END) { - if (status) - GLCALL(glEnable(cap)) - else - GLCALL(glDisable(cap)); - - return; - } - - if (m_capStatus[idx] == status) - return; - - if (status) { - m_capStatus[idx] = status; - GLCALL(glEnable(cap)); - } else { - m_capStatus[idx] = status; - GLCALL(glDisable(cap)); - } -} - -DRMFormat CHyprOpenGLImpl::getPreferredReadFormat(PHLMONITOR pMonitor) { - static const auto PFORCE8BIT = CConfigValue("misc:screencopy_force_8b"); - - auto monFmt = pMonitor->m_output->state->state().drmFormat; - - if (*PFORCE8BIT) - if (monFmt == DRM_FORMAT_BGRA1010102 || monFmt == DRM_FORMAT_ARGB2101010 || monFmt == DRM_FORMAT_XRGB2101010 || monFmt == DRM_FORMAT_BGRX1010102 || - monFmt == DRM_FORMAT_XBGR2101010) - monFmt = DRM_FORMAT_XRGB8888; - - return monFmt; -} - -std::vector CHyprOpenGLImpl::getDRMFormatModifiers(DRMFormat drmFormat) { - SDRMFormat format; - - for (const auto& fmt : m_drmFormats) { - if (fmt.drmFormat == drmFormat) { - format = fmt; - break; - } - } - - return format.modifiers; -} - -bool CHyprOpenGLImpl::explicitSyncSupported() { - return m_exts.EGL_ANDROID_native_fence_sync_ext; -} - -WP CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features) { - if (!m_shaders->fragVariants[frag].contains(features)) { - auto shader = makeShared(); - - Log::logger->log(Log::INFO, "compiling feature set {} for {}", features, FRAG_SHADERS[frag]); - - const auto fragSrc = g_pShaderLoader->getVariantSource(frag, features); - - if (!shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) - Log::logger->log(Log::ERR, "shader features {} failed for {}", features, FRAG_SHADERS[frag]); - - m_shaders->fragVariants[frag][features] = shader; - return shader; - } - - ASSERT(m_shaders->fragVariants[frag][features]); - return m_shaders->fragVariants[frag][features]; +uint32_t CHyprOpenGLImpl::getPreferredReadFormat(PHLMONITOR pMonitor) { + return pMonitor->output->state->state().drmFormat; } std::vector CHyprOpenGLImpl::getDRMFormats() { - return m_drmFormats; + return drmFormats; +} + +SP CHyprOpenGLImpl::createEGLSync(int fenceFD) { + std::vector attribs; + CFileDescriptor dupFd; + if (fenceFD >= 0) { + dupFd = CFileDescriptor{fcntl(fenceFD, F_DUPFD_CLOEXEC, 0)}; + if (!dupFd.isValid()) { + Debug::log(ERR, "createEGLSync: dup failed"); + return nullptr; + } + // reserve number of elements to avoid reallocations + attribs.reserve(3); + attribs.push_back(EGL_SYNC_NATIVE_FENCE_FD_ANDROID); + attribs.push_back(dupFd.get()); + attribs.push_back(EGL_NONE); + } + + EGLSyncKHR sync = m_sProc.eglCreateSyncKHR(m_pEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs.data()); + if (sync == EGL_NO_SYNC_KHR) { + Debug::log(ERR, "eglCreateSyncKHR failed"); + return nullptr; + } else + dupFd.take(); // eglCreateSyncKHR only takes ownership on success + + // we need to flush otherwise we might not get a valid fd + glFlush(); + + int fd = g_pHyprOpenGL->m_sProc.eglDupNativeFenceFDANDROID(g_pHyprOpenGL->m_pEglDisplay, sync); + if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + Debug::log(ERR, "eglDupNativeFenceFDANDROID failed"); + return nullptr; + } + + auto eglsync = SP(new CEGLSync); + eglsync->sync = sync; + eglsync->m_iFd = CFileDescriptor{fd}; + return eglsync; } void SRenderModifData::applyToBox(CBox& box) { @@ -3130,7 +2972,7 @@ void SRenderModifData::applyToBox(CBox& box) { box.y = OLDPOS.y * COS + OLDPOS.x * SIN; } } - } catch (std::bad_any_cast& e) { Log::logger->log(Log::ERR, "BUG THIS OR PLUGIN ERROR: caught a bad_any_cast in SRenderModifData::applyToBox!"); } + } catch (std::bad_any_cast& e) { Debug::log(ERR, "BUG THIS OR PLUGIN ERROR: caught a bad_any_cast in SRenderModifData::applyToBox!"); } } } @@ -3147,7 +2989,7 @@ void SRenderModifData::applyToRegion(CRegion& rg) { case RMOD_TYPE_ROTATE: /* TODO */ case RMOD_TYPE_ROTATECENTER: break; } - } catch (std::bad_any_cast& e) { Log::logger->log(Log::ERR, "BUG THIS OR PLUGIN ERROR: caught a bad_any_cast in SRenderModifData::applyToRegion!"); } + } catch (std::bad_any_cast& e) { Debug::log(ERR, "BUG THIS OR PLUGIN ERROR: caught a bad_any_cast in SRenderModifData::applyToRegion!"); } } } @@ -3165,54 +3007,23 @@ float SRenderModifData::combinedScale() { case RMOD_TYPE_ROTATE: case RMOD_TYPE_ROTATECENTER: break; } - } catch (std::bad_any_cast& e) { Log::logger->log(Log::ERR, "BUG THIS OR PLUGIN ERROR: caught a bad_any_cast in SRenderModifData::combinedScale!"); } + } catch (std::bad_any_cast& e) { Debug::log(ERR, "BUG THIS OR PLUGIN ERROR: caught a bad_any_cast in SRenderModifData::combinedScale!"); } } return scale; } -UP CEGLSync::create() { - RASSERT(g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext, "Tried to create an EGL sync when syncs are not supported on the gpu"); - - EGLSyncKHR sync = g_pHyprOpenGL->m_proc.eglCreateSyncKHR(g_pHyprOpenGL->m_eglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - - if UNLIKELY (sync == EGL_NO_SYNC_KHR) { - Log::logger->log(Log::ERR, "eglCreateSyncKHR failed"); - return nullptr; - } - - // we need to flush otherwise we might not get a valid fd - glFlush(); - - int fd = g_pHyprOpenGL->m_proc.eglDupNativeFenceFDANDROID(g_pHyprOpenGL->m_eglDisplay, sync); - if UNLIKELY (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - Log::logger->log(Log::ERR, "eglDupNativeFenceFDANDROID failed"); - return nullptr; - } - - UP eglSync(new CEGLSync); - eglSync->m_fd = CFileDescriptor(fd); - eglSync->m_sync = sync; - eglSync->m_valid = true; - - return eglSync; -} - CEGLSync::~CEGLSync() { - if UNLIKELY (m_sync == EGL_NO_SYNC_KHR) + if (sync == EGL_NO_SYNC_KHR) return; - if UNLIKELY (g_pHyprOpenGL && g_pHyprOpenGL->m_proc.eglDestroySyncKHR(g_pHyprOpenGL->m_eglDisplay, m_sync) != EGL_TRUE) - Log::logger->log(Log::ERR, "eglDestroySyncKHR failed"); + if (g_pHyprOpenGL->m_sProc.eglDestroySyncKHR(g_pHyprOpenGL->m_pEglDisplay, sync) != EGL_TRUE) + Debug::log(ERR, "eglDestroySyncKHR failed"); +} + +CFileDescriptor&& CEGLSync::takeFD() { + return std::move(m_iFd); } CFileDescriptor& CEGLSync::fd() { - return m_fd; -} - -CFileDescriptor&& CEGLSync::takeFd() { - return std::move(m_fd); -} - -bool CEGLSync::isValid() { - return m_valid && m_sync != EGL_NO_SYNC_KHR && m_fd.isValid(); + return m_iFd; } diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index c9008447..fa6c575d 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -3,15 +3,13 @@ #include "../defines.hpp" #include "../helpers/Monitor.hpp" #include "../helpers/Color.hpp" -#include "../helpers/time/Timer.hpp" +#include "../helpers/Timer.hpp" #include "../helpers/math/Math.hpp" #include "../helpers/Format.hpp" #include "../helpers/sync/SyncTimeline.hpp" -#include #include #include -#include -#include +#include #include #include @@ -20,7 +18,6 @@ #include "Texture.hpp" #include "Framebuffer.hpp" #include "Renderbuffer.hpp" -#include "desktop/DesktopTypes.hpp" #include "pass/Pass.hpp" #include @@ -28,33 +25,20 @@ #include #include #include -#include #include "../debug/TracyDefines.hpp" #include "../protocols/core/Compositor.hpp" -#include "render/ShaderLoader.hpp" -#include "render/gl/GLFramebuffer.hpp" -#include "render/gl/GLRenderbuffer.hpp" -#include "render/gl/GLTexture.hpp" - -#define GLFB(ifb) dc(ifb.get()) struct gbm_device; -class IHyprRenderer; +class CHyprRenderer; -struct SVertex { - float x, y; // position - float u, v; // uv +inline const float fullVerts[] = { + 1, 0, // top right + 0, 0, // top left + 1, 1, // bottom right + 0, 1, // bottom left }; - -constexpr std::array fullVerts = {{ - {0.0f, 0.0f, 0.0f, 0.0f}, // top-left - {0.0f, 1.0f, 0.0f, 1.0f}, // bottom-left - {1.0f, 0.0f, 1.0f, 0.0f}, // top-right - {1.0f, 1.0f, 1.0f, 1.0f}, // bottom-right -}}; - -inline const float fanVertsFull[] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f}; +inline const float fanVertsFull[] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f}; enum eDiscardMode : uint8_t { DISCARD_OPAQUE = 1, @@ -94,50 +78,47 @@ enum eMonitorExtraRenderFBs : uint8_t { FB_MONITOR_RENDER_EXTRA_BLUR, }; -struct SFragShaderDesc { - Render::ePreparedFragmentShader id; - const char* file; -}; - -struct SPreparedShaders { - // SPreparedShaders() { - // for (auto& f : frag) { - // f = makeShared(); - // } - // } - - std::string TEXVERTSRC; - std::string TEXVERTSRC320; - // std::array, SH_FRAG_LAST> frag; - // std::map> fragVariants; - std::array>, Render::SH_FRAG_LAST> fragVariants; -}; - struct SMonitorRenderData { - SP offloadFB; - SP mirrorFB; // these are used for some effects, - SP mirrorSwapFB; // etc - SP offMainFB; - SP monitorMirrorFB; // used for mirroring outputs, does not contain artifacts like offloadFB - SP blurFB; + CFramebuffer offloadFB; + CFramebuffer mirrorFB; // these are used for some effects, + CFramebuffer mirrorSwapFB; // etc + CFramebuffer offMainFB; + CFramebuffer monitorMirrorFB; // used for mirroring outputs, does not contain artifacts like offloadFB + CFramebuffer blurFB; - SP stencilTex = makeShared(); + SP stencilTex = makeShared(); - bool blurFBDirty = true; - bool blurFBShouldRender = false; + bool blurFBDirty = true; + bool blurFBShouldRender = false; + + // Shaders + bool m_bShadersInitialized = false; + CShader m_shQUAD; + CShader m_shRGBA; + CShader m_shPASSTHRURGBA; + CShader m_shMATTE; + CShader m_shRGBX; + CShader m_shEXT; + CShader m_shBLUR1; + CShader m_shBLUR2; + CShader m_shBLURPREPARE; + CShader m_shBLURFINISH; + CShader m_shSHADOW; + CShader m_shBORDER1; + CShader m_shGLITCH; + CShader m_shCM; }; struct SCurrentRenderData { - PHLMONITORREF pMonitor; - Mat3x3 projection; - Mat3x3 savedProjection; - Mat3x3 monitorProjection; + PHLMONITORREF pMonitor; + Mat3x3 projection; + Mat3x3 savedProjection; + Mat3x3 monitorProjection; - // FIXME: raw pointer galore! SMonitorRenderData* pCurrentMonData = nullptr; - SP currentFB = nullptr; // current rendering to - SP mainFB = nullptr; // main to render to - SP outFB = nullptr; // out to render to (if offloaded, etc) + CFramebuffer* currentFB = nullptr; // current rendering to + CFramebuffer* mainFB = nullptr; // main to render to + CFramebuffer* outFB = nullptr; // out to render to (if offloaded, etc) CRegion damage; CRegion finalDamage; // damage used for funal off -> main @@ -148,8 +129,6 @@ struct SCurrentRenderData { bool useNearestNeighbor = false; bool blockScreenShader = false; bool simplePass = false; - bool transformDamage = true; - bool noSimplify = false; Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1); Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1); @@ -167,20 +146,17 @@ struct SCurrentRenderData { class CEGLSync { public: - static UP create(); - ~CEGLSync(); + EGLSyncKHR sync = nullptr; + + Hyprutils::OS::CFileDescriptor&& takeFD(); Hyprutils::OS::CFileDescriptor& fd(); - Hyprutils::OS::CFileDescriptor&& takeFd(); - bool isValid(); private: CEGLSync() = default; - Hyprutils::OS::CFileDescriptor m_fd; - EGLSyncKHR m_sync = EGL_NO_SYNC_KHR; - bool m_valid = false; + Hyprutils::OS::CFileDescriptor m_iFd; friend class CHyprOpenGLImpl; }; @@ -192,134 +168,85 @@ class CHyprOpenGLImpl { CHyprOpenGLImpl(); ~CHyprOpenGLImpl(); - struct SRectRenderData { - const CRegion* damage = nullptr; - int round = 0; - float roundingPower = 2.F; - bool blur = false; - float blurA = 1.F; - bool xray = false; - }; + void begin(PHLMONITOR, const CRegion& damage, CFramebuffer* fb = nullptr, std::optional finalDamage = {}); + void beginSimple(PHLMONITOR, const CRegion& damage, SP rb = nullptr, CFramebuffer* fb = nullptr); + void end(); - struct STextureRenderData { - const CRegion* damage = nullptr; - SP surface = nullptr; - float a = 1.F; - bool blur = false; - float blurA = 1.F, overallA = 1.F; - int round = 0; - float roundingPower = 2.F; - bool discardActive = false; - bool allowCustomUV = false; - bool allowDim = true; - bool noAA = false; - bool blockBlurOptimization = false; - GLenum wrapX = GL_CLAMP_TO_EDGE, wrapY = GL_CLAMP_TO_EDGE; - bool cmBackToSRGB = false; - bool noCM = false; - bool finalMonitorCM = false; - SP cmBackToSRGBSource; - SP blurredBG; - }; + void renderRect(const CBox&, const CHyprColor&, int round = 0, float roundingPower = 2.0f); + void renderRectWithBlur(const CBox&, const CHyprColor&, int round = 0, float roundingPower = 2.0f, float blurA = 1.f, bool xray = false); + void renderRectWithDamage(const CBox&, const CHyprColor&, const CRegion& damage, int round = 0, float roundingPower = 2.0f); + void renderTexture(SP, const CBox&, float a, int round = 0, float roundingPower = 2.0f, bool discardActive = false, bool allowCustomUV = false); + void renderTextureWithDamage(SP, const CBox&, const CRegion& damage, float a, int round = 0, float roundingPower = 2.0f, bool discardActive = false, + bool allowCustomUV = false); + void renderTextureWithBlur(SP, const CBox&, float a, SP pSurface, int round = 0, float roundingPower = 2.0f, bool blockBlurOptimization = false, + float blurA = 1.f, float overallA = 1.f); + void renderRoundedShadow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, float a = 1.0); + void renderBorder(const CBox&, const CGradientValueData&, int round, float roundingPower, int borderSize, float a = 1.0, int outerRound = -1 /* use round */); + void renderBorder(const CBox&, const CGradientValueData&, const CGradientValueData&, float lerp, int round, float roundingPower, int borderSize, float a = 1.0, + int outerRound = -1 /* use round */); + void renderTextureMatte(SP tex, const CBox& pBox, CFramebuffer& matte); - struct SBorderRenderData { - int round = 0; - float roundingPower = 2.F; - int borderSize = 1; - float a = 1.0; - int outerRound = -1; /* use round */ - }; + void setMonitorTransformEnabled(bool enabled); + void setRenderModifEnabled(bool enabled); - void begin(PHLMONITOR, const CRegion& damage, SP fb = nullptr, std::optional finalDamage = {}); - void beginSimple(PHLMONITOR, const CRegion& damage, SP rb = nullptr, SP fb = nullptr); - void end(); + void saveMatrix(); + void setMatrixScaleTranslate(const Vector2D& translate, const float& scale); + void restoreMatrix(); - void renderRect(const CBox&, const CHyprColor&, SRectRenderData data); - void renderTexture(SP, const CBox&, STextureRenderData data); - void renderRoundedShadow(const CBox&, int round, float roundingPower, int range, const CHyprColor& color, float a = 1.0); - void renderBorder(const CBox&, const CGradientValueData&, SBorderRenderData data); - void renderBorder(const CBox&, const CGradientValueData&, const CGradientValueData&, float lerp, SBorderRenderData data); - void renderTextureMatte(SP tex, const CBox& pBox, SP matte); - void renderTexturePrimitive(SP tex, const CBox& box); + void blend(bool enabled); - void pushMonitorTransformEnabled(bool enabled); - void popMonitorTransformEnabled(); + bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow); - void setRenderModifEnabled(bool enabled); - void setViewport(GLint x, GLint y, GLsizei width, GLsizei height); - void setCapStatus(int cap, bool status); + void clear(const CHyprColor&); + void clearWithTex(); + void scissor(const CBox&, bool transform = true); + void scissor(const pixman_box32*, bool transform = true); + void scissor(const int x, const int y, const int w, const int h, bool transform = true); - void saveMatrix(); - void setMatrixScaleTranslate(const Vector2D& translate, const float& scale); - void restoreMatrix(); + void destroyMonitorResources(PHLMONITOR); - void blend(bool enabled); + void markBlurDirtyForMonitor(PHLMONITOR); - bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow); + void preWindowPass(); + bool preBlurQueued(); + void preRender(PHLMONITOR); - void clear(const CHyprColor&); - void clearWithTex(); - void scissor(const CBox&, bool transform = true); - void scissor(const pixman_box32*, bool transform = true); - void scissor(const int x, const int y, const int w, const int h, bool transform = true); + void saveBufferForMirror(const CBox&); + void renderMirrored(); - void destroyMonitorResources(PHLMONITORREF); + void applyScreenShader(const std::string& path); - void markBlurDirtyForMonitor(PHLMONITOR); + void bindOffMain(); + void renderOffToMain(CFramebuffer* off); + void bindBackOnMain(); - void preWindowPass(); - bool preBlurQueued(); - void preRender(PHLMONITOR); + SP loadAsset(const std::string& file); + SP renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0); - void saveBufferForMirror(const CBox&); - void renderMirrored(); + void setDamage(const CRegion& damage, std::optional finalDamage = {}); - void applyScreenShader(const std::string& path); + void ensureBackgroundTexturePresence(); - void bindOffMain(); - void renderOffToMain(CGLFramebuffer* off); - void bindBackOnMain(); + uint32_t getPreferredReadFormat(PHLMONITOR pMonitor); + std::vector getDRMFormats(); + EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); + SP createEGLSync(int fence = -1); - bool needsACopyFB(PHLMONITOR mon); + SCurrentRenderData m_RenderData; - std::string resolveAssetPath(const std::string& file); - SP loadAsset(const std::string& file); - SP texFromCairo(cairo_surface_t* cairo); - SP renderText(const std::string& text, CHyprColor col, int pt, bool italic = false, const std::string& fontFamily = "", int maxWidth = 0, int weight = 400); + Hyprutils::OS::CFileDescriptor m_iGBMFD; + gbm_device* m_pGbmDevice = nullptr; + EGLContext m_pEglContext = nullptr; + EGLDisplay m_pEglDisplay = nullptr; + EGLDeviceEXT m_pEglDevice = nullptr; + uint failedAssetsNo = 0; - void setDamage(const CRegion& damage, std::optional finalDamage = {}); + bool m_bReloadScreenShader = true; // at launch it can be set - DRMFormat getPreferredReadFormat(PHLMONITOR pMonitor); - std::vector getDRMFormats(); - std::vector getDRMFormatModifiers(DRMFormat format); - EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); - - bool initShaders(const std::string& path = ""); - - WP useShader(WP prog); - - bool explicitSyncSupported(); - WP getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0); - - bool m_shadersInitialized = false; - SP m_shaders; - - SCurrentRenderData m_renderData; - - Hyprutils::OS::CFileDescriptor m_gbmFD; - gbm_device* m_gbmDevice = nullptr; - EGLContext m_eglContext = nullptr; - EGLDisplay m_eglDisplay = nullptr; - EGLDeviceEXT m_eglDevice = nullptr; - uint m_failedAssetsNo = 0; - - bool m_reloadScreenShader = true; // at launch it can be set - - std::map> m_windowFramebuffers; - std::map> m_layerFramebuffers; - std::map, SP> m_popupFramebuffers; - std::map m_monitorRenderResources; - std::map> m_monitorBGFBs; + std::map m_mWindowFramebuffers; + std::map m_mLayerFramebuffers; + std::map m_mMonitorRenderResources; + std::map m_mMonitorBGFBs; struct { PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; @@ -337,105 +264,62 @@ class CHyprOpenGLImpl { PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = nullptr; PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = nullptr; PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR = nullptr; - } m_proc; + } m_sProc; struct { bool EXT_read_format_bgra = false; bool EXT_image_dma_buf_import = false; bool EXT_image_dma_buf_import_modifiers = false; - bool KHR_context_flush_control = false; bool KHR_display_reference = false; bool IMG_context_priority = false; bool EXT_create_context_robustness = false; - bool EGL_ANDROID_native_fence_sync_ext = false; - } m_exts; - - SP m_screencopyDeniedTexture; - - enum eEGLContextVersion : uint8_t { - EGL_CONTEXT_GLES_2_0 = 0, - EGL_CONTEXT_GLES_3_0, - EGL_CONTEXT_GLES_3_2, - }; - - eEGLContextVersion m_eglContextVersion = EGL_CONTEXT_GLES_3_2; - - enum eCachedCapStatus : uint8_t { - CAP_STATUS_BLEND = 0, - CAP_STATUS_SCISSOR_TEST, - CAP_STATUS_STENCIL_TEST, - CAP_STATUS_END - }; + } m_sExts; private: - struct { - GLint x = 0; - GLint y = 0; - GLsizei width = 0; - GLsizei height = 0; - } m_lastViewport; + std::list m_lBuffers; + std::list m_lTextures; - std::array m_capStatus = {}; + std::vector drmFormats; + bool m_bHasModifiers = false; - std::vector m_drmFormats; - bool m_hasModifiers = false; + int m_iDRMFD = -1; + std::string m_szExtensions; - int m_drmFD = -1; - std::string m_extensions; + bool m_bFakeFrame = false; + bool m_bEndFrame = false; + bool m_bApplyFinalShader = false; + bool m_bBlend = false; + bool m_bOffloadedFramebuffer = false; + bool m_bCMSupported = true; - bool m_fakeFrame = false; - bool m_applyFinalShader = false; - bool m_blend = false; - bool m_offloadedFramebuffer = false; - bool m_cmSupported = true; + CShader m_sFinalScreenShader; + CTimer m_tGlobalTimer; - bool m_monitorTransformEnabled = false; // do not modify directly - std::stack m_monitorTransformStack; - SP m_missingAssetTexture; - SP m_lockDeadTexture; - SP m_lockDead2Texture; - SP m_lockTtyTextTexture; - SP m_finalScreenShader; - CTimer m_globalTimer; - GLuint m_currentProgram; - ASP m_backgroundResource; - bool m_backgroundResourceFailed = false; + SP m_pMissingAssetTexture, m_pBackgroundTexture, m_pLockDeadTexture, m_pLockDead2Texture, m_pLockTtyTextTexture; // TODO: don't always load lock - void createBGTextureForMonitor(PHLMONITOR); - void initDRMFormats(); - void initEGL(bool gbm); - EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); - void initAssets(); - void ensureLockTexturesRendered(bool load); - void initMissingAssetTexture(); - void requestBackgroundResource(); - - // for the final shader - std::array m_pressedHistoryTimers = {}; - std::array m_pressedHistoryPositions = {}; - GLint m_pressedHistoryKilled = 0; - GLint m_pressedHistoryTouched = 0; + void logShaderError(const GLuint&, bool program = false); + GLuint createProgram(const std::string&, const std::string&, bool dynamic = false); + GLuint compileShader(const GLuint&, std::string, bool dynamic = false); + void createBGTextureForMonitor(PHLMONITOR); + void initShaders(); + void initDRMFormats(); + void initEGL(bool gbm); + EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); + void initAssets(); + void initMissingAssetTexture(); // std::optional> getModsForFormat(EGLint format); // returns the out FB, can be either Mirror or MirrorSwap - SP blurMainFramebufferWithDamage(float a, CRegion* damage); - SP blurFramebufferWithDamage(float a, CRegion* damage, CGLFramebuffer& source); + CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage); - void passCMUniforms(WP, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, - bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); - void passCMUniforms(WP, const NColorManagement::PImageDescription imageDescription); - void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size); - void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); - void renderRectWithBlurInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); - void renderRectWithDamageInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); - WP renderToOutputInternal(); - WP renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox); - void renderTextureInternal(SP, const CBox&, const STextureRenderData& data); - void renderTextureWithBlurInternal(SP, const CBox&, const STextureRenderData& data); + void renderTextureInternalWithDamage(SP, const CBox& box, float a, const CRegion& damage, int round = 0, float roundingPower = 2.0f, bool discardOpaque = false, + bool noAA = false, bool allowCustomUV = false, bool allowDim = false); + void renderTexturePrimitive(SP tex, const CBox& box); + void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size); - void preBlurForCurrentMonitor(); + void preBlurForCurrentMonitor(); friend class CHyprRenderer; friend class CTexPassElement; diff --git a/src/render/Renderbuffer.cpp b/src/render/Renderbuffer.cpp index bab4f73e..1ea9f785 100644 --- a/src/render/Renderbuffer.cpp +++ b/src/render/Renderbuffer.cpp @@ -1,21 +1,76 @@ #include "Renderbuffer.hpp" -#include "Framebuffer.hpp" -#include "render/Renderer.hpp" -#include "render/gl/GLRenderbuffer.hpp" -#include +#include "Renderer.hpp" +#include "OpenGL.hpp" +#include "../Compositor.hpp" +#include "../protocols/types/Buffer.hpp" #include #include #include -IRenderbuffer::IRenderbuffer(SP buffer, uint32_t format) : m_hlBuffer(buffer) { - m_listeners.destroyBuffer = buffer->events.destroy.listen([this] { g_pHyprRenderer->onRenderbufferDestroy(dc(this)); }); +CRenderbuffer::~CRenderbuffer() { + if (!g_pCompositor || g_pCompositor->m_bIsShuttingDown || !g_pHyprRenderer) + return; + + g_pHyprRenderer->makeEGLCurrent(); + + unbind(); + m_sFramebuffer.release(); + glDeleteRenderbuffers(1, &m_iRBO); + + g_pHyprOpenGL->m_sProc.eglDestroyImageKHR(g_pHyprOpenGL->m_pEglDisplay, m_iImage); } -bool IRenderbuffer::good() { - return m_good; +CRenderbuffer::CRenderbuffer(SP buffer, uint32_t format) : m_pHLBuffer(buffer), m_uDrmFormat(format) { + auto dma = buffer->dmabuf(); + + m_iImage = g_pHyprOpenGL->createEGLImage(dma); + if (m_iImage == EGL_NO_IMAGE_KHR) { + Debug::log(ERR, "rb: createEGLImage failed"); + return; + } + + glGenRenderbuffers(1, &m_iRBO); + glBindRenderbuffer(GL_RENDERBUFFER, m_iRBO); + g_pHyprOpenGL->m_sProc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)m_iImage); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glGenFramebuffers(1, &m_sFramebuffer.m_iFb); + m_sFramebuffer.m_iFbAllocated = true; + m_sFramebuffer.m_vSize = buffer->size; + m_sFramebuffer.bind(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_iRBO); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + Debug::log(ERR, "rbo: glCheckFramebufferStatus failed"); + return; + } + + m_sFramebuffer.unbind(); + + listeners.destroyBuffer = buffer->events.destroy.registerListener([this](std::any d) { g_pHyprRenderer->onRenderbufferDestroy(this); }); + + m_bGood = true; } -SP IRenderbuffer::getFB() { - return m_framebuffer; +bool CRenderbuffer::good() { + return m_bGood; +} + +void CRenderbuffer::bind() { + glBindRenderbuffer(GL_RENDERBUFFER, m_iRBO); + bindFB(); +} + +void CRenderbuffer::bindFB() { + m_sFramebuffer.bind(); +} + +void CRenderbuffer::unbind() { + glBindRenderbuffer(GL_RENDERBUFFER, 0); + m_sFramebuffer.unbind(); +} + +CFramebuffer* CRenderbuffer::getFB() { + return &m_sFramebuffer; } diff --git a/src/render/Renderbuffer.hpp b/src/render/Renderbuffer.hpp index c33144d3..ff06bd5a 100644 --- a/src/render/Renderbuffer.hpp +++ b/src/render/Renderbuffer.hpp @@ -5,24 +5,30 @@ #include "Framebuffer.hpp" #include -class IRenderbuffer { +class CMonitor; + +class CRenderbuffer { public: - IRenderbuffer(SP buffer, uint32_t format); - virtual ~IRenderbuffer() = default; + CRenderbuffer(SP buffer, uint32_t format); + ~CRenderbuffer(); bool good(); - SP getFB(); + void bind(); + void bindFB(); + void unbind(); + CFramebuffer* getFB(); + uint32_t getFormat(); - virtual void bind() = 0; - virtual void unbind() = 0; + WP m_pHLBuffer; - WP m_hlBuffer; - - protected: - SP m_framebuffer; - bool m_good = false; + private: + void* m_iImage = nullptr; + GLuint m_iRBO = 0; + CFramebuffer m_sFramebuffer; + uint32_t m_uDrmFormat = 0; + bool m_bGood = false; struct { CHyprSignalListener destroyBuffer; - } m_listeners; -}; + } listeners; +}; \ No newline at end of file diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 165f580a..6f78e896 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1,20 +1,19 @@ #include "Renderer.hpp" #include "../Compositor.hpp" #include "../helpers/math/Math.hpp" +#include "../helpers/sync/SyncReleaser.hpp" #include #include -#include #include #include "../config/ConfigValue.hpp" -#include "../config/ConfigManager.hpp" #include "../managers/CursorManager.hpp" #include "../managers/PointerManager.hpp" #include "../managers/input/InputManager.hpp" -#include "../managers/animation/AnimationManager.hpp" -#include "../desktop/view/Window.hpp" -#include "../desktop/view/LayerSurface.hpp" -#include "../desktop/view/GlobalViewMethods.hpp" -#include "../desktop/state/FocusState.hpp" +#include "../managers/HookSystemManager.hpp" +#include "../managers/AnimationManager.hpp" +#include "../managers/LayoutManager.hpp" +#include "../desktop/Window.hpp" +#include "../desktop/LayerSurface.hpp" #include "../protocols/SessionLock.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/XDGShell.hpp" @@ -27,36 +26,14 @@ #include "../hyprerror/HyprError.hpp" #include "../debug/HyprDebugOverlay.hpp" #include "../debug/HyprNotificationOverlay.hpp" -#include "../layout/LayoutManager.hpp" -#include "../layout/space/Space.hpp" -#include "../i18n/Engine.hpp" -#include "desktop/DesktopTypes.hpp" -#include "../event/EventBus.hpp" -#include "helpers/CursorShapes.hpp" -#include "helpers/MainLoopExecutor.hpp" -#include "helpers/Monitor.hpp" -#include "macros.hpp" #include "pass/TexPassElement.hpp" #include "pass/ClearPassElement.hpp" #include "pass/RectPassElement.hpp" #include "pass/RendererHintsPassElement.hpp" #include "pass/SurfacePassElement.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #include "../protocols/ColorManagement.hpp" #include "../protocols/types/ContentType.hpp" -#include "../helpers/MiscFunctions.hpp" -#include "render/AsyncResourceGatherer.hpp" -#include "render/Framebuffer.hpp" -#include "render/OpenGL.hpp" -#include "render/Texture.hpp" -#include "render/gl/GLFramebuffer.hpp" -#include "render/gl/GLTexture.hpp" -#include -#include -#include -#include -#include -#include #include using namespace Hyprutils::Utils; @@ -70,203 +47,190 @@ extern "C" { static int cursorTicker(void* data) { g_pHyprRenderer->ensureCursorRenderingMode(); - wl_event_source_timer_update(g_pHyprRenderer->m_cursorTicker, 500); + wl_event_source_timer_update(g_pHyprRenderer->m_pCursorTicker, 500); return 0; } CHyprRenderer::CHyprRenderer() { - if (g_pCompositor->m_aqBackend->hasSession()) { - size_t drmDevices = 0; - for (auto const& dev : g_pCompositor->m_aqBackend->session->sessionDevices) { + if (g_pCompositor->m_pAqBackend->hasSession()) { + for (auto const& dev : g_pCompositor->m_pAqBackend->session->sessionDevices) { const auto DRMV = drmGetVersion(dev->fd); if (!DRMV) continue; - drmDevices++; std::string name = std::string{DRMV->name, DRMV->name_len}; - std::ranges::transform(name, name.begin(), tolower); + std::transform(name.begin(), name.end(), name.begin(), tolower); if (name.contains("nvidia")) - m_nvidia = true; - else if (name.contains("i915")) - m_intel = true; - else if (name.contains("softpipe") || name.contains("Software Rasterizer") || name.contains("llvmpipe")) - m_software = true; + m_bNvidia = true; - Log::logger->log(Log::DEBUG, "DRM driver information: {} v{}.{}.{} from {} description {}", name, DRMV->version_major, DRMV->version_minor, DRMV->version_patchlevel, - std::string{DRMV->date, DRMV->date_len}, std::string{DRMV->desc, DRMV->desc_len}); + Debug::log(LOG, "DRM driver information: {} v{}.{}.{} from {} description {}", name, DRMV->version_major, DRMV->version_minor, DRMV->version_patchlevel, + std::string{DRMV->date, DRMV->date_len}, std::string{DRMV->desc, DRMV->desc_len}); drmFreeVersion(DRMV); } - m_mgpu = drmDevices > 1; } else { - Log::logger->log(Log::DEBUG, "Aq backend has no session, omitting full DRM node checks"); + Debug::log(LOG, "Aq backend has no session, omitting full DRM node checks"); - const auto DRMV = drmGetVersion(g_pCompositor->m_drm.fd); + const auto DRMV = drmGetVersion(g_pCompositor->m_iDRMFD); if (DRMV) { std::string name = std::string{DRMV->name, DRMV->name_len}; - std::ranges::transform(name, name.begin(), tolower); + std::transform(name.begin(), name.end(), name.begin(), tolower); if (name.contains("nvidia")) - m_nvidia = true; - else if (name.contains("i915")) - m_intel = true; - else if (name.contains("softpipe") || name.contains("Software Rasterizer") || name.contains("llvmpipe")) - m_software = true; + m_bNvidia = true; - Log::logger->log(Log::DEBUG, "Primary DRM driver information: {} v{}.{}.{} from {} description {}", name, DRMV->version_major, DRMV->version_minor, - DRMV->version_patchlevel, std::string{DRMV->date, DRMV->date_len}, std::string{DRMV->desc, DRMV->desc_len}); + Debug::log(LOG, "Primary DRM driver information: {} v{}.{}.{} from {} description {}", name, DRMV->version_major, DRMV->version_minor, DRMV->version_patchlevel, + std::string{DRMV->date, DRMV->date_len}, std::string{DRMV->desc, DRMV->desc_len}); } else { - Log::logger->log(Log::DEBUG, "No primary DRM driver information found"); + Debug::log(LOG, "No primary DRM driver information found"); } drmFreeVersion(DRMV); } - if (m_nvidia) - Log::logger->log(Log::WARN, "NVIDIA detected, please remember to follow nvidia instructions on the wiki"); + if (m_bNvidia) + Debug::log(WARN, "NVIDIA detected, please remember to follow nvidia instructions on the wiki"); // cursor hiding stuff - static auto P = Event::bus()->m_events.input.keyboard.key.listen([&](IKeyboard::SKeyEvent e, Event::SCallbackInfo&) { - if (m_cursorHiddenConditions.hiddenOnKeyboard) + static auto P = g_pHookSystem->hookDynamic("keyPress", [&](void* self, SCallbackInfo& info, std::any param) { + if (m_sCursorHiddenConditions.hiddenOnKeyboard) return; - m_cursorHiddenConditions.hiddenOnKeyboard = true; + m_sCursorHiddenConditions.hiddenOnKeyboard = true; ensureCursorRenderingMode(); }); - static auto P2 = Event::bus()->m_events.input.mouse.move.listen([&](Vector2D pos, Event::SCallbackInfo&) { - if (!m_cursorHiddenConditions.hiddenOnKeyboard && m_cursorHiddenConditions.hiddenOnTouch == g_pInputManager->m_lastInputTouch && - m_cursorHiddenConditions.hiddenOnTablet == g_pInputManager->m_lastInputTablet && !m_cursorHiddenConditions.hiddenOnTimeout) + static auto P2 = g_pHookSystem->hookDynamic("mouseMove", [&](void* self, SCallbackInfo& info, std::any param) { + if (!m_sCursorHiddenConditions.hiddenOnKeyboard && m_sCursorHiddenConditions.hiddenOnTouch == g_pInputManager->m_bLastInputTouch && + !m_sCursorHiddenConditions.hiddenOnTimeout) return; - m_cursorHiddenConditions.hiddenOnKeyboard = false; - m_cursorHiddenConditions.hiddenOnTimeout = false; - m_cursorHiddenConditions.hiddenOnTouch = g_pInputManager->m_lastInputTouch; - m_cursorHiddenConditions.hiddenOnTablet = g_pInputManager->m_lastInputTablet; + m_sCursorHiddenConditions.hiddenOnKeyboard = false; + m_sCursorHiddenConditions.hiddenOnTimeout = false; + m_sCursorHiddenConditions.hiddenOnTouch = g_pInputManager->m_bLastInputTouch; ensureCursorRenderingMode(); }); - static auto P3 = Event::bus()->m_events.monitor.focused.listen([&](PHLMONITOR mon) { + static auto P3 = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) { g_pEventLoopManager->doLater([this]() { if (!g_pHyprError->active()) return; - for (auto& m : g_pCompositor->m_monitors) { - arrangeLayersForMonitor(m->m_id); + for (auto& m : g_pCompositor->m_vMonitors) { + arrangeLayersForMonitor(m->ID); } }); }); - static auto P4 = Event::bus()->m_events.window.updateRules.listen([&](PHLWINDOW window) { - if (window->m_ruleApplicator->renderUnfocused().valueOrDefault()) - addWindowToRenderUnfocused(window); - }); + m_pCursorTicker = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, cursorTicker, nullptr); + wl_event_source_timer_update(m_pCursorTicker, 500); - m_cursorTicker = wl_event_loop_add_timer(g_pCompositor->m_wlEventLoop, cursorTicker, nullptr); - wl_event_source_timer_update(m_cursorTicker, 500); - - m_renderUnfocusedTimer = makeShared( + m_tRenderUnfocusedTimer = makeShared( std::nullopt, [this](SP self, void* data) { static auto PFPS = CConfigValue("misc:render_unfocused_fps"); - if (m_renderUnfocused.empty()) + if (m_vRenderUnfocused.empty()) return; + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + bool dirty = false; - for (auto& w : m_renderUnfocused) { + for (auto& w : m_vRenderUnfocused) { if (!w) { dirty = true; continue; } - if (!w->wlSurface() || !w->wlSurface()->resource() || shouldRenderWindow(w.lock())) + if (!w->m_pWLSurface || !w->m_pWLSurface->resource() || shouldRenderWindow(w.lock())) continue; - w->wlSurface()->resource()->frame(Time::steadyNow()); - auto FEEDBACK = makeUnique(w->wlSurface()->resource()); - FEEDBACK->attachMonitor(Desktop::focusState()->monitor()); + w->m_pWLSurface->resource()->frame(&now); + auto FEEDBACK = makeShared(w->m_pWLSurface->resource()); + FEEDBACK->attachMonitor(g_pCompositor->m_pLastMonitor.lock()); FEEDBACK->discarded(); - PROTO::presentation->queueData(std::move(FEEDBACK)); + PROTO::presentation->queueData(FEEDBACK); } if (dirty) - std::erase_if(m_renderUnfocused, [](const auto& e) { return !e || !e->m_ruleApplicator->renderUnfocused().valueOr(false); }); + std::erase_if(m_vRenderUnfocused, [](const auto& e) { return !e || !e->m_sWindowData.renderUnfocused.valueOr(false); }); - if (!m_renderUnfocused.empty()) - m_renderUnfocusedTimer->updateTimeout(std::chrono::milliseconds(1000 / *PFPS)); + if (!m_vRenderUnfocused.empty()) + m_tRenderUnfocusedTimer->updateTimeout(std::chrono::milliseconds(1000 / *PFPS)); }, nullptr); - g_pEventLoopManager->addTimer(m_renderUnfocusedTimer); + g_pEventLoopManager->addTimer(m_tRenderUnfocusedTimer); } CHyprRenderer::~CHyprRenderer() { - if (m_cursorTicker) - wl_event_source_remove(m_cursorTicker); + if (m_pCursorTicker) + wl_event_source_remove(m_pCursorTicker); } bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor) { if (!pWindow->visibleOnMonitor(pMonitor)) return false; - if (!pWindow->m_workspace && !pWindow->m_fadingOut) + if (!pWindow->m_pWorkspace && !pWindow->m_bFadingOut) return false; - if (!pWindow->m_workspace && pWindow->m_fadingOut) - return pWindow->workspaceID() == pMonitor->activeWorkspaceID() || pWindow->workspaceID() == pMonitor->activeSpecialWorkspaceID(); + if (!pWindow->m_pWorkspace && pWindow->m_bFadingOut) + return pWindow->workspaceID() == pMonitor->activeWorkspaceID(); - if (pWindow->m_pinned) + if (pWindow->m_bPinned) return true; // if the window is being moved to a workspace that is not invisible, and the alpha is > 0.F, render it. - if (pWindow->m_monitorMovedFrom != -1 && pWindow->m_movingToWorkspaceAlpha->isBeingAnimated() && pWindow->m_movingToWorkspaceAlpha->value() > 0.F && pWindow->m_workspace && - !pWindow->m_workspace->isVisible()) + if (pWindow->m_iMonitorMovedFrom != -1 && pWindow->m_fMovingToWorkspaceAlpha->isBeingAnimated() && pWindow->m_fMovingToWorkspaceAlpha->value() > 0.F && pWindow->m_pWorkspace && + !pWindow->m_pWorkspace->isVisible()) return true; - const auto PWINDOWWORKSPACE = pWindow->m_workspace; - if (PWINDOWWORKSPACE && PWINDOWWORKSPACE->m_monitor == pMonitor) { - if (PWINDOWWORKSPACE->m_renderOffset->isBeingAnimated() || PWINDOWWORKSPACE->m_alpha->isBeingAnimated() || PWINDOWWORKSPACE->m_forceRendering) + const auto PWINDOWWORKSPACE = pWindow->m_pWorkspace; + if (PWINDOWWORKSPACE && PWINDOWWORKSPACE->m_pMonitor == pMonitor) { + if (PWINDOWWORKSPACE->m_vRenderOffset->isBeingAnimated() || PWINDOWWORKSPACE->m_fAlpha->isBeingAnimated() || PWINDOWWORKSPACE->m_bForceRendering) return true; // if hidden behind fullscreen - if (PWINDOWWORKSPACE->m_hasFullscreenWindow && !pWindow->isFullscreen() && (!pWindow->m_isFloating || !pWindow->m_createdOverFullscreen) && pWindow->m_alpha->value() == 0) + if (PWINDOWWORKSPACE->m_bHasFullscreenWindow && !pWindow->isFullscreen() && (!pWindow->m_bIsFloating || !pWindow->m_bCreatedOverFullscreen) && + pWindow->m_fAlpha->value() == 0) return false; - if (!PWINDOWWORKSPACE->m_renderOffset->isBeingAnimated() && !PWINDOWWORKSPACE->m_alpha->isBeingAnimated() && !PWINDOWWORKSPACE->isVisible()) + if (!PWINDOWWORKSPACE->m_vRenderOffset->isBeingAnimated() && !PWINDOWWORKSPACE->m_fAlpha->isBeingAnimated() && !PWINDOWWORKSPACE->isVisible()) return false; } - if (pWindow->m_monitor == pMonitor) + if (pWindow->m_pMonitor == pMonitor) return true; - if ((!pWindow->m_workspace || !pWindow->m_workspace->isVisible()) && pWindow->m_monitor != pMonitor) + if ((!pWindow->m_pWorkspace || !pWindow->m_pWorkspace->isVisible()) && pWindow->m_pMonitor != pMonitor) return false; // if not, check if it maybe is active on a different monitor. - if (pWindow->m_workspace && pWindow->m_workspace->isVisible() && pWindow->m_isFloating /* tiled windows can't be multi-ws */) + if (pWindow->m_pWorkspace && pWindow->m_pWorkspace->isVisible() && pWindow->m_bIsFloating /* tiled windows can't be multi-ws */) return !pWindow->isFullscreen(); // Do not draw fullscreen windows on other monitors - if (pMonitor->m_activeSpecialWorkspace == pWindow->m_workspace) + if (pMonitor->activeSpecialWorkspace == pWindow->m_pWorkspace) return true; // if window is tiled and it's flying in, don't render on other mons (for slide) - if (!pWindow->m_isFloating && pWindow->m_realPosition->isBeingAnimated() && pWindow->m_animatingIn && pWindow->m_monitor != pMonitor) + if (!pWindow->m_bIsFloating && pWindow->m_vRealPosition->isBeingAnimated() && pWindow->m_bAnimatingIn && pWindow->m_pMonitor != pMonitor) return false; - if (pWindow->m_realPosition->isBeingAnimated()) { - if (PWINDOWWORKSPACE && !PWINDOWWORKSPACE->m_isSpecialWorkspace && PWINDOWWORKSPACE->m_renderOffset->isBeingAnimated()) + if (pWindow->m_vRealPosition->isBeingAnimated()) { + if (PWINDOWWORKSPACE && !PWINDOWWORKSPACE->m_bIsSpecialWorkspace && PWINDOWWORKSPACE->m_vRenderOffset->isBeingAnimated()) return false; // render window if window and monitor intersect // (when moving out of or through a monitor) CBox windowBox = pWindow->getFullWindowBoundingBox(); - if (PWINDOWWORKSPACE && PWINDOWWORKSPACE->m_renderOffset->isBeingAnimated()) - windowBox.translate(PWINDOWWORKSPACE->m_renderOffset->value()); - windowBox.translate(pWindow->m_floatingOffset); + if (PWINDOWWORKSPACE && PWINDOWWORKSPACE->m_vRenderOffset->isBeingAnimated()) + windowBox.translate(PWINDOWWORKSPACE->m_vRenderOffset->value()); + windowBox.translate(pWindow->m_vFloatingOffset); - const CBox monitorBox = {pMonitor->m_position, pMonitor->m_size}; - if (!windowBox.intersection(monitorBox).empty() && (pWindow->workspaceID() == pMonitor->activeWorkspaceID() || pWindow->m_monitorMovedFrom != -1)) + const CBox monitorBox = {pMonitor->vecPosition, pMonitor->vecSize}; + if (!windowBox.intersection(monitorBox).empty() && (pWindow->workspaceID() == pMonitor->activeWorkspaceID() || pWindow->m_iMonitorMovedFrom != -1)) return true; } @@ -278,92 +242,92 @@ bool CHyprRenderer::shouldRenderWindow(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return false; - const auto PWORKSPACE = pWindow->m_workspace; + const auto PWORKSPACE = pWindow->m_pWorkspace; - if (!pWindow->m_workspace) + if (!pWindow->m_pWorkspace) return false; - if (pWindow->m_pinned || PWORKSPACE->m_forceRendering) + if (pWindow->m_bPinned || PWORKSPACE->m_bForceRendering) return true; if (PWORKSPACE && PWORKSPACE->isVisible()) return true; - for (auto const& m : g_pCompositor->m_monitors) { - if (PWORKSPACE && PWORKSPACE->m_monitor == m && (PWORKSPACE->m_renderOffset->isBeingAnimated() || PWORKSPACE->m_alpha->isBeingAnimated())) + for (auto const& m : g_pCompositor->m_vMonitors) { + if (PWORKSPACE && PWORKSPACE->m_pMonitor == m && (PWORKSPACE->m_vRenderOffset->isBeingAnimated() || PWORKSPACE->m_fAlpha->isBeingAnimated())) return true; - if (m->m_activeSpecialWorkspace && pWindow->onSpecialWorkspace()) + if (m->activeSpecialWorkspace && pWindow->onSpecialWorkspace()) return true; } return false; } -void CHyprRenderer::renderWorkspaceWindowsFullscreen(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time) { +void CHyprRenderer::renderWorkspaceWindowsFullscreen(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* time) { PHLWINDOW pWorkspaceWindow = nullptr; - Event::bus()->m_events.render.stage.emit(RENDER_PRE_WINDOWS); + EMIT_HOOK_EVENT("render", RENDER_PRE_WINDOWS); // loop over the tiled windows that are fading out - for (auto const& w : g_pCompositor->m_windows) { + for (auto const& w : g_pCompositor->m_vWindows) { if (!shouldRenderWindow(w, pMonitor)) continue; - if (w->m_alpha->value() == 0.f) + if (w->m_fAlpha->value() == 0.f) continue; - if (w->isFullscreen() || w->m_isFloating) + if (w->isFullscreen() || w->m_bIsFloating) continue; - if (pWorkspace->m_isSpecialWorkspace != w->onSpecialWorkspace()) + if (pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) continue; renderWindow(w, pMonitor, time, true, RENDER_PASS_ALL); } // and floating ones too - for (auto const& w : g_pCompositor->m_windows) { + for (auto const& w : g_pCompositor->m_vWindows) { if (!shouldRenderWindow(w, pMonitor)) continue; - if (w->m_alpha->value() == 0.f) + if (w->m_fAlpha->value() == 0.f) continue; - if (w->isFullscreen() || !w->m_isFloating) + if (w->isFullscreen() || !w->m_bIsFloating) continue; - if (w->m_monitor == pWorkspace->m_monitor && pWorkspace->m_isSpecialWorkspace != w->onSpecialWorkspace()) + if (w->m_pMonitor == pWorkspace->m_pMonitor && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) continue; - if (pWorkspace->m_isSpecialWorkspace && w->m_monitor != pWorkspace->m_monitor) + if (pWorkspace->m_bIsSpecialWorkspace && w->m_pMonitor != pWorkspace->m_pMonitor) continue; // special on another are rendered as a part of the base pass renderWindow(w, pMonitor, time, true, RENDER_PASS_ALL); } // TODO: this pass sucks - for (auto const& w : g_pCompositor->m_windows) { - const auto PWORKSPACE = w->m_workspace; + for (auto const& w : g_pCompositor->m_vWindows) { + const auto PWORKSPACE = w->m_pWorkspace; - if (w->m_workspace != pWorkspace || !w->isFullscreen()) { - if (!(PWORKSPACE && (PWORKSPACE->m_renderOffset->isBeingAnimated() || PWORKSPACE->m_alpha->isBeingAnimated() || PWORKSPACE->m_forceRendering))) + if (w->m_pWorkspace != pWorkspace || !w->isFullscreen()) { + if (!(PWORKSPACE && (PWORKSPACE->m_vRenderOffset->isBeingAnimated() || PWORKSPACE->m_fAlpha->isBeingAnimated() || PWORKSPACE->m_bForceRendering))) continue; - if (w->m_monitor != pMonitor) + if (w->m_pMonitor != pMonitor) continue; } if (!w->isFullscreen()) continue; - if (w->m_monitor == pWorkspace->m_monitor && pWorkspace->m_isSpecialWorkspace != w->onSpecialWorkspace()) + if (w->m_pMonitor == pWorkspace->m_pMonitor && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) continue; if (shouldRenderWindow(w, pMonitor)) - renderWindow(w, pMonitor, time, pWorkspace->m_fullscreenMode != FSMODE_FULLSCREEN, RENDER_PASS_ALL); + renderWindow(w, pMonitor, time, pWorkspace->m_efFullscreenMode != FSMODE_FULLSCREEN, RENDER_PASS_ALL); - if (w->m_workspace != pWorkspace) + if (w->m_pWorkspace != pWorkspace) continue; pWorkspaceWindow = w; @@ -371,36 +335,36 @@ void CHyprRenderer::renderWorkspaceWindowsFullscreen(PHLMONITOR pMonitor, PHLWOR if (!pWorkspaceWindow) { // ?? happens sometimes... - pWorkspace->m_hasFullscreenWindow = false; + pWorkspace->m_bHasFullscreenWindow = false; return; // this will produce one blank frame. Oh well. } // then render windows over fullscreen. - for (auto const& w : g_pCompositor->m_windows) { - if (w->workspaceID() != pWorkspaceWindow->workspaceID() || !w->m_isFloating || (!w->m_createdOverFullscreen && !w->m_pinned) || (!w->m_isMapped && !w->m_fadingOut) || + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->m_pWorkspace != pWorkspaceWindow->m_pWorkspace || !w->m_bIsFloating || (!w->m_bCreatedOverFullscreen && !w->m_bPinned) || (!w->m_bIsMapped && !w->m_bFadingOut) || w->isFullscreen()) continue; - if (w->m_monitor == pWorkspace->m_monitor && pWorkspace->m_isSpecialWorkspace != w->onSpecialWorkspace()) + if (w->m_pMonitor == pWorkspace->m_pMonitor && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) continue; - if (pWorkspace->m_isSpecialWorkspace && w->m_monitor != pWorkspace->m_monitor) + if (pWorkspace->m_bIsSpecialWorkspace && w->m_pMonitor != pWorkspace->m_pMonitor) continue; // special on another are rendered as a part of the base pass renderWindow(w, pMonitor, time, true, RENDER_PASS_ALL); } } -void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time) { +void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* time) { PHLWINDOW lastWindow; - Event::bus()->m_events.render.stage.emit(RENDER_PRE_WINDOWS); + EMIT_HOOK_EVENT("render", RENDER_PRE_WINDOWS); - std::vector windows, tiledFadingOut; - windows.reserve(g_pCompositor->m_windows.size()); + std::vector windows; + windows.reserve(g_pCompositor->m_vWindows.size()); - for (auto const& w : g_pCompositor->m_windows) { - if (w->isHidden() || (!w->m_isMapped && !w->m_fadingOut)) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->isHidden() || (!w->m_bIsMapped && !w->m_bFadingOut)) continue; if (!shouldRenderWindow(w, pMonitor)) @@ -411,28 +375,21 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo // Non-floating main for (auto& w : windows) { - if (w->m_isFloating) + if (w->m_bIsFloating) continue; // floating are in the second pass // some things may force us to ignore the special/not special disparity - const bool IGNORE_SPECIAL_CHECK = w->m_monitorMovedFrom != -1 && (w->m_workspace && !w->m_workspace->isVisible()); + const bool IGNORE_SPECIAL_CHECK = w->m_iMonitorMovedFrom != -1 && (w->m_pWorkspace && !w->m_pWorkspace->isVisible()); - if (!IGNORE_SPECIAL_CHECK && pWorkspace->m_isSpecialWorkspace != w->onSpecialWorkspace()) + if (!IGNORE_SPECIAL_CHECK && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) continue; // render active window after all others of this pass - if (w == Desktop::focusState()->window()) { + if (w == g_pCompositor->m_pLastWindow) { lastWindow = w.lock(); continue; } - // render tiled fading out after others - if (w->m_fadingOut) { - tiledFadingOut.emplace_back(w); - w.reset(); - continue; - } - // render the bad boy renderWindow(w.lock(), pMonitor, time, true, RENDER_PASS_MAIN); w.reset(); @@ -443,23 +400,18 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo lastWindow.reset(); - // render tiled windows that are fading out after other tiled to not hide them behind - for (auto& w : tiledFadingOut) { - renderWindow(w.lock(), pMonitor, time, true, RENDER_PASS_MAIN); - } - // Non-floating popup for (auto& w : windows) { if (!w) continue; - if (w->m_isFloating) + if (w->m_bIsFloating) continue; // floating are in the second pass // some things may force us to ignore the special/not special disparity - const bool IGNORE_SPECIAL_CHECK = w->m_monitorMovedFrom != -1 && (w->m_workspace && !w->m_workspace->isVisible()); + const bool IGNORE_SPECIAL_CHECK = w->m_iMonitorMovedFrom != -1 && (w->m_pWorkspace && !w->m_pWorkspace->isVisible()); - if (!IGNORE_SPECIAL_CHECK && pWorkspace->m_isSpecialWorkspace != w->onSpecialWorkspace()) + if (!IGNORE_SPECIAL_CHECK && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) continue; // render the bad boy @@ -472,16 +424,16 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo if (!w) continue; - if (!w->m_isFloating || w->m_pinned) + if (!w->m_bIsFloating || w->m_bPinned) continue; // some things may force us to ignore the special/not special disparity - const bool IGNORE_SPECIAL_CHECK = w->m_monitorMovedFrom != -1 && (w->m_workspace && !w->m_workspace->isVisible()); + const bool IGNORE_SPECIAL_CHECK = w->m_iMonitorMovedFrom != -1 && (w->m_pWorkspace && !w->m_pWorkspace->isVisible()); - if (!IGNORE_SPECIAL_CHECK && pWorkspace->m_isSpecialWorkspace != w->onSpecialWorkspace()) + if (!IGNORE_SPECIAL_CHECK && pWorkspace->m_bIsSpecialWorkspace != w->onSpecialWorkspace()) continue; - if (pWorkspace->m_isSpecialWorkspace && w->m_monitor != pWorkspace->m_monitor) + if (pWorkspace->m_bIsSpecialWorkspace && w->m_pMonitor != pWorkspace->m_pMonitor) continue; // special on another are rendered as a part of the base pass // render the bad boy @@ -489,27 +441,28 @@ void CHyprRenderer::renderWorkspaceWindows(PHLMONITOR pMonitor, PHLWORKSPACE pWo } } -void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const Time::steady_tp& time, bool decorate, eRenderPassMode mode, bool ignorePosition, bool standalone) { +void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, timespec* time, bool decorate, eRenderPassMode mode, bool ignorePosition, bool standalone) { if (pWindow->isHidden() && !standalone) return; - if (pWindow->m_fadingOut) { - if (pMonitor == pWindow->m_monitor) // TODO: fix this + if (pWindow->m_bFadingOut) { + if (pMonitor == pWindow->m_pMonitor) // TODO: fix this renderSnapshot(pWindow); return; } - if (!pWindow->m_isMapped) + if (!pWindow->m_bIsMapped) return; TRACY_GPU_ZONE("RenderWindow"); - const auto PWORKSPACE = pWindow->m_workspace; - const auto REALPOS = pWindow->m_realPosition->value() + (pWindow->m_pinned ? Vector2D{} : PWORKSPACE->m_renderOffset->value()); + const auto PWORKSPACE = pWindow->m_pWorkspace; + const auto REALPOS = pWindow->m_vRealPosition->value() + (pWindow->m_bPinned ? Vector2D{} : PWORKSPACE->m_vRenderOffset->value()); static auto PDIMAROUND = CConfigValue("decoration:dim_around"); + static auto PBLUR = CConfigValue("decoration:blur:enabled"); CSurfacePassElement::SRenderData renderdata = {pMonitor, time}; - CBox textureBox = {REALPOS.x, REALPOS.y, std::max(pWindow->m_realSize->value().x, 5.0), std::max(pWindow->m_realSize->value().y, 5.0)}; + CBox textureBox = {REALPOS.x, REALPOS.y, std::max(pWindow->m_vRealSize->value().x, 5.0), std::max(pWindow->m_vRealSize->value().y, 5.0)}; renderdata.pos.x = textureBox.x; renderdata.pos.y = textureBox.y; @@ -517,8 +470,8 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T renderdata.h = textureBox.h; if (ignorePosition) { - renderdata.pos.x = pMonitor->m_position.x; - renderdata.pos.y = pMonitor->m_position.y; + renderdata.pos.x = pMonitor->vecPosition.x; + renderdata.pos.y = pMonitor->vecPosition.y; } else { const bool ANR = pWindow->isNotResponding(); if (ANR && pWindow->m_notRespondingTint->goal() != 0.2F) @@ -531,17 +484,18 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T decorate = false; // whether to use m_fMovingToWorkspaceAlpha, only if fading out into an invisible ws - const bool USE_WORKSPACE_FADE_ALPHA = pWindow->m_monitorMovedFrom != -1 && (!PWORKSPACE || !PWORKSPACE->isVisible()); + const bool USE_WORKSPACE_FADE_ALPHA = pWindow->m_iMonitorMovedFrom != -1 && (!PWORKSPACE || !PWORKSPACE->isVisible()); + const bool DONT_BLUR = pWindow->m_sWindowData.noBlur.valueOrDefault() || pWindow->m_sWindowData.RGBX.valueOrDefault() || pWindow->opaque(); - renderdata.surface = pWindow->wlSurface()->resource(); - renderdata.dontRound = pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN); - renderdata.fadeAlpha = pWindow->m_alpha->value() * (pWindow->m_pinned || USE_WORKSPACE_FADE_ALPHA ? 1.f : PWORKSPACE->m_alpha->value()) * - (USE_WORKSPACE_FADE_ALPHA ? pWindow->m_movingToWorkspaceAlpha->value() : 1.F) * pWindow->m_movingFromWorkspaceAlpha->value(); - renderdata.alpha = pWindow->m_activeInactiveAlpha->value(); - renderdata.decorate = decorate && !pWindow->m_X11DoesntWantBorders && !pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN); - renderdata.rounding = standalone || renderdata.dontRound ? 0 : pWindow->rounding() * pMonitor->m_scale; + renderdata.surface = pWindow->m_pWLSurface->resource(); + renderdata.dontRound = pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN) || pWindow->m_sWindowData.noRounding.valueOrDefault(); + renderdata.fadeAlpha = pWindow->m_fAlpha->value() * (pWindow->m_bPinned || USE_WORKSPACE_FADE_ALPHA ? 1.f : PWORKSPACE->m_fAlpha->value()) * + (USE_WORKSPACE_FADE_ALPHA ? pWindow->m_fMovingToWorkspaceAlpha->value() : 1.F) * pWindow->m_fMovingFromWorkspaceAlpha->value(); + renderdata.alpha = pWindow->m_fActiveInactiveAlpha->value(); + renderdata.decorate = decorate && !pWindow->m_bX11DoesntWantBorders && !pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN); + renderdata.rounding = standalone || renderdata.dontRound ? 0 : pWindow->rounding() * pMonitor->scale; renderdata.roundingPower = standalone || renderdata.dontRound ? 2.0f : pWindow->roundingPower(); - renderdata.blur = !standalone && shouldBlur(pWindow); + renderdata.blur = !standalone && *PBLUR && !DONT_BLUR; renderdata.pWindow = pWindow; if (standalone) { @@ -550,72 +504,67 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T } // apply opaque - if (pWindow->m_ruleApplicator->opaque().valueOrDefault()) + if (pWindow->m_sWindowData.opaque.valueOrDefault()) renderdata.alpha = 1.f; renderdata.pWindow = pWindow; - // for plugins - g_pHyprOpenGL->m_renderData.currentWindow = pWindow; + EMIT_HOOK_EVENT("render", RENDER_PRE_WINDOW); - Event::bus()->m_events.render.stage.emit(RENDER_PRE_WINDOW); - - const auto fullAlpha = renderdata.alpha * renderdata.fadeAlpha; - - if (*PDIMAROUND && pWindow->m_ruleApplicator->dimAround().valueOrDefault() && !m_bRenderingSnapshot && mode != RENDER_PASS_POPUP) { - CBox monbox = {0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.y}; + if (*PDIMAROUND && pWindow->m_sWindowData.dimAround.valueOrDefault() && !m_bRenderingSnapshot && mode != RENDER_PASS_POPUP) { + CBox monbox = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.y}; CRectPassElement::SRectData data; - data.color = CHyprColor(0, 0, 0, *PDIMAROUND * fullAlpha); + data.color = CHyprColor(0, 0, 0, *PDIMAROUND * renderdata.alpha * renderdata.fadeAlpha); data.box = monbox; - m_renderPass.add(makeUnique(data)); + m_sRenderPass.add(makeShared(data)); } - renderdata.pos.x += pWindow->m_floatingOffset.x; - renderdata.pos.y += pWindow->m_floatingOffset.y; + renderdata.pos.x += pWindow->m_vFloatingOffset.x; + renderdata.pos.y += pWindow->m_vFloatingOffset.y; // if window is floating and we have a slide animation, clip it to its full bb - if (!ignorePosition && pWindow->m_isFloating && !pWindow->isFullscreen() && PWORKSPACE->m_renderOffset->isBeingAnimated() && !pWindow->m_pinned) { + if (!ignorePosition && pWindow->m_bIsFloating && !pWindow->isFullscreen() && PWORKSPACE->m_vRenderOffset->isBeingAnimated() && !pWindow->m_bPinned) { CRegion rg = - pWindow->getFullWindowBoundingBox().translate(-pMonitor->m_position + PWORKSPACE->m_renderOffset->value() + pWindow->m_floatingOffset).scale(pMonitor->m_scale); + pWindow->getFullWindowBoundingBox().translate(-pMonitor->vecPosition + PWORKSPACE->m_vRenderOffset->value() + pWindow->m_vFloatingOffset).scale(pMonitor->scale); renderdata.clipBox = rg.getExtents(); } // render window decorations first, if not fullscreen full if (mode == RENDER_PASS_ALL || mode == RENDER_PASS_MAIN) { - const bool TRANSFORMERSPRESENT = !pWindow->m_transformers.empty(); + const bool TRANSFORMERSPRESENT = !pWindow->m_vTransformers.empty(); if (TRANSFORMERSPRESENT) { g_pHyprOpenGL->bindOffMain(); - for (auto const& t : pWindow->m_transformers) { + for (auto const& t : pWindow->m_vTransformers) { t->preWindowRender(&renderdata); } } if (renderdata.decorate) { - for (auto const& wd : pWindow->m_windowDecorations) { + for (auto const& wd : pWindow->m_dWindowDecorations) { if (wd->getDecorationLayer() != DECORATION_LAYER_BOTTOM) continue; - wd->draw(pMonitor, fullAlpha); + wd->draw(pMonitor, renderdata.alpha * renderdata.fadeAlpha); } - for (auto const& wd : pWindow->m_windowDecorations) { + for (auto const& wd : pWindow->m_dWindowDecorations) { if (wd->getDecorationLayer() != DECORATION_LAYER_UNDER) continue; - wd->draw(pMonitor, fullAlpha); + wd->draw(pMonitor, renderdata.alpha * renderdata.fadeAlpha); } } static auto PXWLUSENN = CConfigValue("xwayland:use_nearest_neighbor"); - if ((pWindow->m_isX11 && *PXWLUSENN) || pWindow->m_ruleApplicator->nearestNeighbor().valueOrDefault()) + if ((pWindow->m_bIsX11 && *PXWLUSENN) || pWindow->m_sWindowData.nearestNeighbor.valueOrDefault()) renderdata.useNearestNeighbor = true; - if (pWindow->wlSurface()->small() && !pWindow->wlSurface()->m_fillIgnoreSmall && renderdata.blur) { - CBox wb = {renderdata.pos.x - pMonitor->m_position.x, renderdata.pos.y - pMonitor->m_position.y, renderdata.w, renderdata.h}; - wb.scale(pMonitor->m_scale).round(); + if (!pWindow->m_sWindowData.noBlur.valueOrDefault() && pWindow->m_pWLSurface->small() && !pWindow->m_pWLSurface->m_bFillIgnoreSmall && renderdata.blur && *PBLUR) { + CBox wb = {renderdata.pos.x - pMonitor->vecPosition.x, renderdata.pos.y - pMonitor->vecPosition.y, renderdata.w, renderdata.h}; + wb.scale(pMonitor->scale).round(); CRectPassElement::SRectData data; data.color = CHyprColor(0, 0, 0, 0); data.box = wb; @@ -623,24 +572,18 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T data.blur = true; data.blurA = renderdata.fadeAlpha; data.xray = g_pHyprOpenGL->shouldUseNewBlurOptimizations(nullptr, pWindow); - m_renderPass.add(makeUnique(data)); + m_sRenderPass.add(makeShared(data)); renderdata.blur = false; } renderdata.surfaceCounter = 0; - pWindow->wlSurface()->resource()->breadthfirst( + pWindow->m_pWLSurface->resource()->breadthfirst( [this, &renderdata, &pWindow](SP s, const Vector2D& offset, void* data) { - if (!s->m_current.texture) - return; - - if (s->m_current.size.x < 1 || s->m_current.size.y < 1) - return; - renderdata.localPos = offset; - renderdata.texture = s->m_current.texture; + renderdata.texture = s->current.texture; renderdata.surface = s; - renderdata.mainSurface = s == pWindow->wlSurface()->resource(); - m_renderPass.add(makeUnique(renderdata)); + renderdata.mainSurface = s == pWindow->m_pWLSurface->resource(); + m_sRenderPass.add(makeShared(renderdata)); renderdata.surfaceCounter++; }, nullptr); @@ -648,30 +591,30 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T renderdata.useNearestNeighbor = false; if (renderdata.decorate) { - for (auto const& wd : pWindow->m_windowDecorations) { + for (auto const& wd : pWindow->m_dWindowDecorations) { if (wd->getDecorationLayer() != DECORATION_LAYER_OVER) continue; - wd->draw(pMonitor, fullAlpha); + wd->draw(pMonitor, renderdata.alpha * renderdata.fadeAlpha); } } if (TRANSFORMERSPRESENT) { - IFramebuffer* last = g_pHyprOpenGL->m_renderData.currentFB.get(); - for (auto const& t : pWindow->m_transformers) { + CFramebuffer* last = g_pHyprOpenGL->m_RenderData.currentFB; + for (auto const& t : pWindow->m_vTransformers) { last = t->transform(last); } g_pHyprOpenGL->bindBackOnMain(); - g_pHyprOpenGL->renderOffToMain(dc(last)); + g_pHyprOpenGL->renderOffToMain(last); } } - g_pHyprOpenGL->m_renderData.clipBox = CBox(); + g_pHyprOpenGL->m_RenderData.clipBox = CBox(); if (mode == RENDER_PASS_ALL || mode == RENDER_PASS_POPUP) { - if (!pWindow->m_isX11) { - CBox geom = pWindow->m_xdgSurface->m_current.geometry; + if (!pWindow->m_bIsX11) { + CBox geom = pWindow->m_pXDGSurface->current.geometry; renderdata.pos -= geom.pos(); renderdata.dontRound = true; // don't round popups @@ -679,48 +622,36 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T renderdata.squishOversized = false; // don't squish popups renderdata.popup = true; + static CConfigValue PBLURPOPUPS = CConfigValue("decoration:blur:popups"); static CConfigValue PBLURIGNOREA = CConfigValue("decoration:blur:popups_ignorealpha"); - renderdata.blur = shouldBlur(pWindow->m_popupHead); + renderdata.blur = *PBLURPOPUPS && *PBLUR; if (renderdata.blur) { renderdata.discardMode |= DISCARD_ALPHA; renderdata.discardOpacity = *PBLURIGNOREA; } - if (pWindow->m_ruleApplicator->nearestNeighbor().valueOrDefault()) + if (pWindow->m_sWindowData.nearestNeighbor.valueOrDefault()) renderdata.useNearestNeighbor = true; renderdata.surfaceCounter = 0; - pWindow->m_popupHead->breadthfirst( - [this, &renderdata](WP popup, void* data) { - if (popup->m_fadingOut) { - renderSnapshot(popup); + pWindow->m_pPopupHead->breadthfirst( + [this, &renderdata](WP popup, void* data) { + if (!popup->m_pWLSurface || !popup->m_pWLSurface->resource() || !popup->m_bMapped) return; - } - - if (!popup->aliveAndVisible()) - return; - const auto pos = popup->coordsRelativeToParent(); const Vector2D oldPos = renderdata.pos; renderdata.pos += pos; - renderdata.fadeAlpha = popup->m_alpha->value(); - popup->wlSurface()->resource()->breadthfirst( + popup->m_pWLSurface->resource()->breadthfirst( [this, &renderdata](SP s, const Vector2D& offset, void* data) { - if (!s->m_current.texture) - return; - - if (s->m_current.size.x < 1 || s->m_current.size.y < 1) - return; - renderdata.localPos = offset; - renderdata.texture = s->m_current.texture; + renderdata.texture = s->current.texture; renderdata.surface = s; renderdata.mainSurface = false; - m_renderPass.add(makeUnique(renderdata)); + m_sRenderPass.add(makeShared(renderdata)); renderdata.surfaceCounter++; }, data); @@ -728,115 +659,77 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, PHLMONITOR pMonitor, const T renderdata.pos = oldPos; }, &renderdata); - - renderdata.alpha = 1.F; } if (decorate) { - for (auto const& wd : pWindow->m_windowDecorations) { + for (auto const& wd : pWindow->m_dWindowDecorations) { if (wd->getDecorationLayer() != DECORATION_LAYER_OVERLAY) continue; - wd->draw(pMonitor, fullAlpha); + wd->draw(pMonitor, renderdata.alpha * renderdata.fadeAlpha); } } } - Event::bus()->m_events.render.stage.emit(RENDER_POST_WINDOW); + // for plugins + g_pHyprOpenGL->m_RenderData.currentWindow = pWindow; - g_pHyprOpenGL->m_renderData.currentWindow.reset(); + EMIT_HOOK_EVENT("render", RENDER_POST_WINDOW); + + g_pHyprOpenGL->m_RenderData.currentWindow.reset(); } -SP CHyprRenderer::createTexture(const SP buffer, bool keepDataCopy) { - if (!buffer) - return createTexture(); - - auto attrs = buffer->dmabuf(); - - if (!attrs.success) { - // attempt shm - auto shm = buffer->shm(); - - if (!shm.success) { - Log::logger->log(Log::ERR, "Cannot create a texture: buffer has no dmabuf or shm"); - return createTexture(buffer->opaque); - } - - auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); - - return createTexture(fmt, pixelData, bufLen, shm.size, keepDataCopy, buffer->opaque); - } - - auto tex = createTexture(attrs, buffer->opaque); - - if (!tex) { - Log::logger->log(Log::ERR, "Cannot create a texture: failed to create an Image"); - return createTexture(buffer->opaque); - } - - return tex; -} - -void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::steady_tp& time, bool popups, bool lockscreen) { +void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, timespec* time, bool popups) { if (!pLayer) return; - // skip rendering based on abovelock rule and make sure to not render abovelock layers twice - if ((pLayer->m_ruleApplicator->aboveLock().valueOrDefault() && !lockscreen && g_pSessionLockManager->isSessionLocked()) || - (lockscreen && !pLayer->m_ruleApplicator->aboveLock().valueOrDefault())) - return; - static auto PDIMAROUND = CConfigValue("decoration:dim_around"); - if (*PDIMAROUND && pLayer->m_ruleApplicator->dimAround().valueOrDefault() && !m_bRenderingSnapshot && !popups) { + if (*PDIMAROUND && pLayer->dimAround && !m_bRenderingSnapshot && !popups) { CRectPassElement::SRectData data; - data.box = {0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize.y}; - data.color = CHyprColor(0, 0, 0, *PDIMAROUND * pLayer->m_alpha->value()); - m_renderPass.add(makeUnique(data)); + data.box = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize.y}; + data.color = CHyprColor(0, 0, 0, *PDIMAROUND * pLayer->alpha->value()); + m_sRenderPass.add(makeShared(data)); } - if (pLayer->m_fadingOut) { + if (pLayer->fadingOut) { if (!popups) renderSnapshot(pLayer); return; } + static auto PBLUR = CConfigValue("decoration:blur:enabled"); + TRACY_GPU_ZONE("RenderLayer"); - const auto REALPOS = pLayer->m_realPosition->value(); - const auto REALSIZ = pLayer->m_realSize->value(); + const auto REALPOS = pLayer->realPosition->value(); + const auto REALSIZ = pLayer->realSize->value(); CSurfacePassElement::SRenderData renderdata = {pMonitor, time, REALPOS}; - renderdata.fadeAlpha = pLayer->m_alpha->value(); - renderdata.blur = shouldBlur(pLayer); - renderdata.surface = pLayer->wlSurface()->resource(); + renderdata.fadeAlpha = pLayer->alpha->value(); + renderdata.blur = pLayer->forceBlur && *PBLUR; + renderdata.surface = pLayer->surface->resource(); renderdata.decorate = false; renderdata.w = REALSIZ.x; renderdata.h = REALSIZ.y; renderdata.pLS = pLayer; - renderdata.blockBlurOptimization = pLayer->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM || pLayer->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; + renderdata.blockBlurOptimization = pLayer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM || pLayer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; - renderdata.clipBox = CBox{0, 0, pMonitor->m_size.x, pMonitor->m_size.y}.scale(pMonitor->m_scale); + renderdata.clipBox = CBox{0, 0, pMonitor->vecSize.x, pMonitor->vecSize.y}.scale(pMonitor->scale); - if (renderdata.blur && pLayer->m_ruleApplicator->ignoreAlpha().hasValue()) { + if (renderdata.blur && pLayer->ignoreAlpha) { renderdata.discardMode |= DISCARD_ALPHA; - renderdata.discardOpacity = pLayer->m_ruleApplicator->ignoreAlpha().valueOrDefault(); + renderdata.discardOpacity = pLayer->ignoreAlphaValue; } if (!popups) - pLayer->wlSurface()->resource()->breadthfirst( + pLayer->surface->resource()->breadthfirst( [this, &renderdata, &pLayer](SP s, const Vector2D& offset, void* data) { - if (!s->m_current.texture) - return; - - if (s->m_current.size.x < 1 || s->m_current.size.y < 1) - return; - renderdata.localPos = offset; - renderdata.texture = s->m_current.texture; + renderdata.texture = s->current.texture; renderdata.surface = s; - renderdata.mainSurface = s == pLayer->wlSurface()->resource(); - m_renderPass.add(makeUnique(renderdata)); + renderdata.mainSurface = s == pLayer->surface->resource(); + m_sRenderPass.add(makeShared(renderdata)); renderdata.surfaceCounter++; }, &renderdata); @@ -844,35 +737,27 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, PHLMONITOR pMonitor, const Time::s renderdata.squishOversized = false; // don't squish popups renderdata.dontRound = true; renderdata.popup = true; - renderdata.blur = pLayer->m_ruleApplicator->blurPopups().valueOrDefault(); + renderdata.blur = pLayer->forceBlurPopups; renderdata.surfaceCounter = 0; if (popups) { - pLayer->m_popupHead->breadthfirst( - [this, &renderdata](WP popup, void* data) { - if (!popup->aliveAndVisible()) - return; - - const auto SURF = popup->wlSurface()->resource(); - - if (!SURF->m_current.texture) - return; - - if (SURF->m_current.size.x < 1 || SURF->m_current.size.y < 1) + pLayer->popupHead->breadthfirst( + [this, &renderdata](WP popup, void* data) { + if (!popup->m_pWLSurface || !popup->m_pWLSurface->resource() || !popup->m_bMapped) return; Vector2D pos = popup->coordsRelativeToParent(); renderdata.localPos = pos; - renderdata.texture = SURF->m_current.texture; - renderdata.surface = SURF; + renderdata.texture = popup->m_pWLSurface->resource()->current.texture; + renderdata.surface = popup->m_pWLSurface->resource(); renderdata.mainSurface = false; - m_renderPass.add(makeUnique(renderdata)); + m_sRenderPass.add(makeShared(renderdata)); renderdata.surfaceCounter++; }, &renderdata); } } -void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, const Time::steady_tp& time) { +void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, timespec* time) { const auto POS = pPopup->globalBox().pos(); CSurfacePassElement::SRenderData renderdata = {pMonitor, time, POS}; @@ -881,8 +766,8 @@ void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, con renderdata.surface = SURF; renderdata.decorate = false; - renderdata.w = SURF->m_current.size.x; - renderdata.h = SURF->m_current.size.y; + renderdata.w = SURF->current.size.x; + renderdata.h = SURF->current.size.y; static auto PBLUR = CConfigValue("decoration:blur:enabled"); static auto PBLURIMES = CConfigValue("decoration:blur:input_methods"); @@ -896,64 +781,52 @@ void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, PHLMONITOR pMonitor, con SURF->breadthfirst( [this, &renderdata, &SURF](SP s, const Vector2D& offset, void* data) { - if (!s->m_current.texture) - return; - - if (s->m_current.size.x < 1 || s->m_current.size.y < 1) - return; - renderdata.localPos = offset; - renderdata.texture = s->m_current.texture; + renderdata.texture = s->current.texture; renderdata.surface = s; renderdata.mainSurface = s == SURF; - m_renderPass.add(makeUnique(renderdata)); + m_sRenderPass.add(makeShared(renderdata)); renderdata.surfaceCounter++; }, &renderdata); } -void CHyprRenderer::renderSessionLockSurface(WP pSurface, PHLMONITOR pMonitor, const Time::steady_tp& time) { - CSurfacePassElement::SRenderData renderdata = {pMonitor, time, pMonitor->m_position, pMonitor->m_position}; +void CHyprRenderer::renderSessionLockSurface(WP pSurface, PHLMONITOR pMonitor, timespec* time) { + CSurfacePassElement::SRenderData renderdata = {pMonitor, time, pMonitor->vecPosition, pMonitor->vecPosition}; renderdata.blur = false; renderdata.surface = pSurface->surface->surface(); renderdata.decorate = false; - renderdata.w = pMonitor->m_size.x; - renderdata.h = pMonitor->m_size.y; + renderdata.w = pMonitor->vecSize.x; + renderdata.h = pMonitor->vecSize.y; renderdata.surface->breadthfirst( [this, &renderdata, &pSurface](SP s, const Vector2D& offset, void* data) { - if (!s->m_current.texture) - return; - - if (s->m_current.size.x < 1 || s->m_current.size.y < 1) - return; - renderdata.localPos = offset; - renderdata.texture = s->m_current.texture; + renderdata.texture = s->current.texture; renderdata.surface = s; renderdata.mainSurface = s == pSurface->surface->surface(); - m_renderPass.add(makeUnique(renderdata)); + m_sRenderPass.add(makeShared(renderdata)); renderdata.surfaceCounter++; }, &renderdata); } -void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& time, const Vector2D& translate, const float& scale) { +void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* time, const Vector2D& translate, const float& scale) { static auto PDIMSPECIAL = CConfigValue("decoration:dim_special"); static auto PBLURSPECIAL = CConfigValue("decoration:blur:special"); static auto PBLUR = CConfigValue("decoration:blur:enabled"); + static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); + static auto PBACKGROUNDCOLOR = CConfigValue("misc:background_color"); static auto PXPMODE = CConfigValue("render:xp_mode"); - static auto PSESSIONLOCKXRAY = CConfigValue("misc:session_lock_xray"); - if UNLIKELY (!pMonitor) + if (!pMonitor) return; - if UNLIKELY (g_pSessionLockManager->isSessionLocked() && !*PSESSIONLOCKXRAY) { - // We stop to render workspaces as soon as the lockscreen was sent the "locked" or "finished" (aka denied) event. - // In addition we make sure to stop rendering workspaces after misc:lockdead_screen_delay has passed. - if (g_pSessionLockManager->shallConsiderLockMissing() || g_pSessionLockManager->clientLocked() || g_pSessionLockManager->clientDenied()) - return; + if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSessionLockPresent()) { + // locked with no exclusive, draw only red + renderSessionLockMissing(pMonitor); + return; } // todo: matrices are buggy atm for some reason, but probably would be preferable in the long run @@ -963,54 +836,53 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA SRenderModifData RENDERMODIFDATA; if (translate != Vector2D{0, 0}) RENDERMODIFDATA.modifs.emplace_back(std::make_pair<>(SRenderModifData::eRenderModifType::RMOD_TYPE_TRANSLATE, translate)); - if UNLIKELY (scale != 1.f) + if (scale != 1.f) RENDERMODIFDATA.modifs.emplace_back(std::make_pair<>(SRenderModifData::eRenderModifType::RMOD_TYPE_SCALE, scale)); - if UNLIKELY (!RENDERMODIFDATA.modifs.empty()) - g_pHyprRenderer->m_renderPass.add(makeUnique(CRendererHintsPassElement::SData{RENDERMODIFDATA})); + if (!RENDERMODIFDATA.modifs.empty()) { + g_pHyprRenderer->m_sRenderPass.add(makeShared(CRendererHintsPassElement::SData{RENDERMODIFDATA})); + } CScopeGuard x([&RENDERMODIFDATA] { if (!RENDERMODIFDATA.modifs.empty()) { - g_pHyprRenderer->m_renderPass.add(makeUnique(CRendererHintsPassElement::SData{SRenderModifData{}})); + g_pHyprRenderer->m_sRenderPass.add(makeShared(CRendererHintsPassElement::SData{SRenderModifData{}})); } }); - if UNLIKELY (!pWorkspace) { + if (!pWorkspace) { // allow rendering without a workspace. In this case, just render layers. - renderBackground(pMonitor); + if (*PRENDERTEX /* inverted cfg flag */) + m_sRenderPass.add(makeShared(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); + else + g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" - for (auto const& ls : pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) { + for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) { renderLayer(ls.lock(), pMonitor, time); } - - Event::bus()->m_events.render.stage.emit(RENDER_POST_WALLPAPER); - - for (auto const& ls : pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]) { + for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]) { renderLayer(ls.lock(), pMonitor, time); } - - for (auto const& ls : pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { + for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { renderLayer(ls.lock(), pMonitor, time); } - - for (auto const& ls : pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]) { + for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]) { renderLayer(ls.lock(), pMonitor, time); } return; } - if LIKELY (!*PXPMODE) { - renderBackground(pMonitor); + if (!*PXPMODE) { + if (*PRENDERTEX /* inverted cfg flag */) + m_sRenderPass.add(makeShared(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); + else + g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" - for (auto const& ls : pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) { + for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) { renderLayer(ls.lock(), pMonitor, time); } - - Event::bus()->m_events.render.stage.emit(RENDER_POST_WALLPAPER); - - for (auto const& ls : pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]) { + for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]) { renderLayer(ls.lock(), pMonitor, time); } } @@ -1018,52 +890,55 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA // pre window pass g_pHyprOpenGL->preWindowPass(); - if UNLIKELY /* subjective? */ (pWorkspace->m_hasFullscreenWindow) + if (pWorkspace->m_bHasFullscreenWindow) renderWorkspaceWindowsFullscreen(pMonitor, pWorkspace, time); else renderWorkspaceWindows(pMonitor, pWorkspace, time); // and then special - if UNLIKELY (pMonitor->m_specialFade->value() != 0.F) { - const auto SPECIALANIMPROGRS = pMonitor->m_specialFade->getCurveValue(); - const bool ANIMOUT = !pMonitor->m_activeSpecialWorkspace; + for (auto const& ws : g_pCompositor->m_vWorkspaces) { + if (ws->m_pMonitor == pMonitor && ws->m_fAlpha->value() > 0.f && ws->m_bIsSpecialWorkspace) { + const auto SPECIALANIMPROGRS = ws->m_vRenderOffset->isBeingAnimated() ? ws->m_vRenderOffset->getCurveValue() : ws->m_fAlpha->getCurveValue(); + const bool ANIMOUT = !pMonitor->activeSpecialWorkspace; - if (*PDIMSPECIAL != 0.f) { - CRectPassElement::SRectData data; - data.box = {translate.x, translate.y, pMonitor->m_transformedSize.x * scale, pMonitor->m_transformedSize.y * scale}; - data.color = CHyprColor(0, 0, 0, *PDIMSPECIAL * (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS)); + if (*PDIMSPECIAL != 0.f) { + CRectPassElement::SRectData data; + data.box = {translate.x, translate.y, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale}; + data.color = CHyprColor(0, 0, 0, *PDIMSPECIAL * (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS)); - g_pHyprRenderer->m_renderPass.add(makeUnique(data)); - } + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); + } - if (*PBLURSPECIAL && *PBLUR) { - CRectPassElement::SRectData data; - data.box = {translate.x, translate.y, pMonitor->m_transformedSize.x * scale, pMonitor->m_transformedSize.y * scale}; - data.color = CHyprColor(0, 0, 0, 0); - data.blur = true; - data.blurA = (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS); + if (*PBLURSPECIAL && *PBLUR) { + CRectPassElement::SRectData data; + data.box = {translate.x, translate.y, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale}; + data.color = CHyprColor(0, 0, 0, 0); + data.blur = true; + data.blurA = (ANIMOUT ? (1.0 - SPECIALANIMPROGRS) : SPECIALANIMPROGRS); - g_pHyprRenderer->m_renderPass.add(makeUnique(data)); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); + } + + break; } } // special - for (auto const& ws : g_pCompositor->getWorkspaces()) { - if (ws->m_alpha->value() <= 0.F || !ws->m_isSpecialWorkspace) - continue; - - if (ws->m_hasFullscreenWindow) - renderWorkspaceWindowsFullscreen(pMonitor, ws.lock(), time); - else - renderWorkspaceWindows(pMonitor, ws.lock(), time); + for (auto const& ws : g_pCompositor->m_vWorkspaces) { + if (ws->m_fAlpha->value() > 0.f && ws->m_bIsSpecialWorkspace) { + if (ws->m_bHasFullscreenWindow) + renderWorkspaceWindowsFullscreen(pMonitor, ws, time); + else + renderWorkspaceWindows(pMonitor, ws, time); + } } // pinned always above - for (auto const& w : g_pCompositor->m_windows) { - if (w->isHidden() && !w->m_isMapped && !w->m_fadingOut) + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->isHidden() && !w->m_bIsMapped && !w->m_bFadingOut) continue; - if (!w->m_pinned || !w->m_isFloating) + if (!w->m_bPinned || !w->m_bIsFloating) continue; if (!shouldRenderWindow(w, pMonitor)) @@ -1073,23 +948,23 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA renderWindow(w, pMonitor, time, true, RENDER_PASS_ALL); } - Event::bus()->m_events.render.stage.emit(RENDER_POST_WINDOWS); + EMIT_HOOK_EVENT("render", RENDER_POST_WINDOWS); // Render surfaces above windows for monitor - for (auto const& ls : pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { + for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { renderLayer(ls.lock(), pMonitor, time); } // Render IME popups - for (auto const& imep : g_pInputManager->m_relay.m_inputMethodPopups) { + for (auto const& imep : g_pInputManager->m_sIMERelay.m_vIMEPopups) { renderIMEPopup(imep.get(), pMonitor, time); } - for (auto const& ls : pMonitor->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]) { + for (auto const& ls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]) { renderLayer(ls.lock(), pMonitor, time); } - for (auto const& lsl : pMonitor->m_layerSurfaceLayers) { + for (auto const& lsl : pMonitor->m_aLayerSurfaceLayers) { for (auto const& ls : lsl) { renderLayer(ls.lock(), pMonitor, time, true); } @@ -1100,121 +975,62 @@ void CHyprRenderer::renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPA //g_pHyprOpenGL->restoreMatrix(); } -void CHyprRenderer::renderBackground(PHLMONITOR pMonitor) { - static auto PRENDERTEX = CConfigValue("misc:disable_hyprland_logo"); - static auto PBACKGROUNDCOLOR = CConfigValue("misc:background_color"); - - if (*PRENDERTEX /* inverted cfg flag */ || pMonitor->m_backgroundOpacity->isBeingAnimated()) - m_renderPass.add(makeUnique(CClearPassElement::SClearData{CHyprColor(*PBACKGROUNDCOLOR)})); - - if (!*PRENDERTEX) - g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper" -} - -void CHyprRenderer::renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp& now, const CBox& geometry) { +void CHyprRenderer::renderLockscreen(PHLMONITOR pMonitor, timespec* now, const CBox& geometry) { TRACY_GPU_ZONE("RenderLockscreen"); - const bool LOCKED = g_pSessionLockManager->isSessionLocked(); - if (!LOCKED) { - g_pHyprOpenGL->ensureLockTexturesRendered(false); - return; - } + if (g_pSessionLockManager->isSessionLocked()) { + Vector2D translate = {geometry.x, geometry.y}; - const bool RENDERPRIMER = g_pSessionLockManager->shallConsiderLockMissing() || g_pSessionLockManager->clientLocked() || g_pSessionLockManager->clientDenied(); - if (RENDERPRIMER) - renderSessionLockPrimer(pMonitor); - - const auto PSLS = g_pSessionLockManager->getSessionLockSurfaceForMonitor(pMonitor->m_id); - const bool RENDERLOCKMISSING = (PSLS.expired() || g_pSessionLockManager->clientDenied()) && g_pSessionLockManager->shallConsiderLockMissing(); - - g_pHyprOpenGL->ensureLockTexturesRendered(RENDERLOCKMISSING); - - if (RENDERLOCKMISSING) - renderSessionLockMissing(pMonitor); - else if (PSLS) { - renderSessionLockSurface(PSLS, pMonitor, now); - g_pSessionLockManager->onLockscreenRenderedOnMonitor(pMonitor->m_id); - - // render layers and then their popups for abovelock rule - for (auto const& lsl : pMonitor->m_layerSurfaceLayers) { - for (auto const& ls : lsl) { - renderLayer(ls.lock(), pMonitor, now, false, true); - } - } - for (auto const& lsl : pMonitor->m_layerSurfaceLayers) { - for (auto const& ls : lsl) { - renderLayer(ls.lock(), pMonitor, now, true, true); - } + const auto PSLS = g_pSessionLockManager->getSessionLockSurfaceForMonitor(pMonitor->ID); + if (!PSLS) { + if (g_pSessionLockManager->shallConsiderLockMissing()) + renderSessionLockMissing(pMonitor); + } else { + renderSessionLockSurface(PSLS, pMonitor, now); + g_pSessionLockManager->onLockscreenRenderedOnMonitor(pMonitor->ID); } } } -void CHyprRenderer::renderSessionLockPrimer(PHLMONITOR pMonitor) { - static auto PSESSIONLOCKXRAY = CConfigValue("misc:session_lock_xray"); - if (*PSESSIONLOCKXRAY) - return; - - CRectPassElement::SRectData data; - data.color = CHyprColor(0, 0, 0, 1.f); - data.box = CBox{{}, pMonitor->m_pixelSize}; - - m_renderPass.add(makeUnique(std::move(data))); -} - void CHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) { + const auto ALPHA = g_pSessionLockManager->getRedScreenAlphaForMonitor(pMonitor->ID); + + CBox monbox = {{}, pMonitor->vecPixelSize}; + const bool ANY_PRESENT = g_pSessionLockManager->anySessionLockSurfacesPresent(); - // ANY_PRESENT: render image2, without instructions. Lock still "alive", unless texture dead - // else: render image, with instructions. Lock is gone. - CBox monbox = {{}, pMonitor->m_pixelSize}; - CTexPassElement::SRenderData data; - data.tex = (ANY_PRESENT) ? g_pHyprOpenGL->m_lockDead2Texture : g_pHyprOpenGL->m_lockDeadTexture; - data.box = monbox; - data.a = 1; + if (ANY_PRESENT) { + // render image2, without instructions. Lock still "alive", unless texture dead + g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDead2Texture, monbox, ALPHA); + } else { + // render image, with instructions. Lock is gone. + g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockDeadTexture, monbox, ALPHA); - m_renderPass.add(makeUnique(data)); - - if (!ANY_PRESENT && g_pHyprOpenGL->m_lockTtyTextTexture) { // also render text for the tty number - CBox texbox = {{}, g_pHyprOpenGL->m_lockTtyTextTexture->m_size}; - data.tex = g_pHyprOpenGL->m_lockTtyTextTexture; - data.box = texbox; - - m_renderPass.add(makeUnique(std::move(data))); + if (g_pHyprOpenGL->m_pLockTtyTextTexture) { + CBox texbox = {{}, g_pHyprOpenGL->m_pLockTtyTextTexture->m_vSize}; + g_pHyprOpenGL->renderTexture(g_pHyprOpenGL->m_pLockTtyTextTexture, texbox, 1.F); + } } -} -static std::optional getSurfaceExpectedSize(PHLWINDOW pWindow, SP pSurface, PHLMONITOR pMonitor, bool main) { - const auto CAN_USE_WINDOW = pWindow && main; - const auto WINDOW_SIZE_MISALIGN = CAN_USE_WINDOW && pWindow->getReportedSize() != pWindow->wlSurface()->resource()->m_current.size; - - if (pSurface->m_current.viewport.hasDestination) - return (pSurface->m_current.viewport.destination * pMonitor->m_scale).round(); - - if (pSurface->m_current.viewport.hasSource) - return (pSurface->m_current.viewport.source.size() * pMonitor->m_scale).round(); - - if (WINDOW_SIZE_MISALIGN) - return (pSurface->m_current.size * pMonitor->m_scale).round(); - - if (CAN_USE_WINDOW) - return (pWindow->getReportedSize() * pMonitor->m_scale).round(); - - return std::nullopt; + if (ALPHA < 1.f) /* animate */ + damageMonitor(pMonitor); + else + g_pSessionLockManager->onLockscreenRenderedOnMonitor(pMonitor->ID); } void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP pSurface, PHLMONITOR pMonitor, bool main, const Vector2D& projSize, const Vector2D& projSizeUnscaled, bool fixMisalignedFSV1) { - if (!pWindow || !pWindow->m_isX11) { + if (!pWindow || !pWindow->m_bIsX11) { static auto PEXPANDEDGES = CConfigValue("render:expand_undersized_textures"); Vector2D uvTL; Vector2D uvBR = Vector2D(1, 1); - if (pSurface->m_current.viewport.hasSource) { + if (pSurface->current.viewport.hasSource) { // we stretch it to dest. if no dest, to 1,1 - Vector2D const& bufferSize = pSurface->m_current.bufferSize; - auto const& bufferSource = pSurface->m_current.viewport.source; + Vector2D const& bufferSize = pSurface->current.bufferSize; + auto const& bufferSource = pSurface->current.viewport.source; // calculate UV for the basic src_box. Assume dest == size. Scale to dest later uvTL = Vector2D(bufferSource.x / bufferSize.x, bufferSource.y / bufferSize.y); @@ -1229,155 +1045,81 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SPm_current.bufferSize; - const auto& BUFFER_SIZE = pSurface->m_current.bufferSize; - - // compute MISALIGN from the adjusted UV coordinates. - const Vector2D MISALIGNMENT = (uvBR - uvTL) * BUFFER_SIZE - projSize; - + const Vector2D PIXELASUV = Vector2D{1, 1} / pSurface->current.bufferSize; + const Vector2D MISALIGNMENT = pSurface->current.bufferSize - projSize; if (MISALIGNMENT != Vector2D{}) uvBR -= MISALIGNMENT * PIXELASUV; - } else { - // if the surface is smaller than our viewport, extend its edges. - // this will break if later on xdg geometry is hit, but we really try - // to let the apps know to NOT add CSD. Also if source is there. - // there is no way to fix this if that's the case - const auto MONITOR_WL_SCALE = std::ceil(pMonitor->m_scale); - const bool SCALE_UNAWARE = pMonitor->m_scale != 1.f && (MONITOR_WL_SCALE == pSurface->m_current.scale || !pSurface->m_current.viewport.hasDestination); - const auto EXPECTED_SIZE = getSurfaceExpectedSize(pWindow, pSurface, pMonitor, main).value_or((projSize * pMonitor->m_scale).round()); + } - const auto RATIO = projSize / EXPECTED_SIZE; - if (!SCALE_UNAWARE || MONITOR_WL_SCALE == 1) { - if (*PEXPANDEDGES && !SCALE_UNAWARE && (RATIO.x > 1 || RATIO.y > 1)) { - const auto FIX = RATIO.clamp(Vector2D{1, 1}, Vector2D{1000000, 1000000}); - uvBR = uvBR * FIX; - } - - // FIXME: probably do this for in anims on all views... - const auto SHOULD_SKIP = !pWindow || pWindow->m_animatingIn; - if (!SHOULD_SKIP && (RATIO.x < 1 || RATIO.y < 1)) { - const auto FIX = RATIO.clamp(Vector2D{0.0001, 0.0001}, Vector2D{1, 1}); - uvBR = uvBR * FIX; - } + // if the surface is smaller than our viewport, extend its edges. + // this will break if later on xdg geometry is hit, but we really try + // to let the apps know to NOT add CSD. Also if source is there. + // there is no way to fix this if that's the case + if (*PEXPANDEDGES) { + const auto MONITOR_WL_SCALE = std::ceil(pMonitor->scale); + const bool SCALE_UNAWARE = MONITOR_WL_SCALE != pSurface->current.scale && !pSurface->current.viewport.hasDestination; + const auto EXPECTED_SIZE = + ((pSurface->current.viewport.hasDestination ? pSurface->current.viewport.destination : pSurface->current.bufferSize / pSurface->current.scale) * pMonitor->scale) + .round(); + if (!SCALE_UNAWARE && (EXPECTED_SIZE.x < projSize.x || EXPECTED_SIZE.y < projSize.y)) { + // this will not work with shm AFAIK, idk why. + // NOTE: this math is wrong if we have a source... or geom updates later, but I don't think we can do much + const auto FIX = projSize / EXPECTED_SIZE; + uvBR = uvBR * FIX; } } - g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft = uvTL; - g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight = uvBR; + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = uvTL; + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = uvBR; - if (g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft == Vector2D() && g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { + if (g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft == Vector2D() && g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { // No special UV mods needed - g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); - g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); } if (!main || !pWindow) return; - // FIXME: this doesn't work. We always set MAXIMIZED anyways, so this doesn't need to work, but it's problematic. + CBox geom = pWindow->m_pXDGSurface->current.geometry; - // CBox geom = pWindow->m_xdgSurface->m_current.geometry; + // ignore X and Y, adjust uv + if (geom.x != 0 || geom.y != 0 || geom.width > projSizeUnscaled.x || geom.height > projSizeUnscaled.y) { + const auto XPERC = (double)geom.x / (double)pSurface->current.size.x; + const auto YPERC = (double)geom.y / (double)pSurface->current.size.y; + const auto WPERC = (double)(geom.x + geom.width) / (double)pSurface->current.size.x; + const auto HPERC = (double)(geom.y + geom.height) / (double)pSurface->current.size.y; - // // Adjust UV based on the xdg_surface geometry - // if (geom.x != 0 || geom.y != 0 || geom.w != 0 || geom.h != 0) { - // const auto XPERC = geom.x / pSurface->m_current.size.x; - // const auto YPERC = geom.y / pSurface->m_current.size.y; - // const auto WPERC = (geom.x + geom.w ? geom.w : pSurface->m_current.size.x) / pSurface->m_current.size.x; - // const auto HPERC = (geom.y + geom.h ? geom.h : pSurface->m_current.size.y) / pSurface->m_current.size.y; + const auto TOADDTL = Vector2D(XPERC * (uvBR.x - uvTL.x), YPERC * (uvBR.y - uvTL.y)); + uvBR = uvBR - Vector2D((1.0 - WPERC) * (uvBR.x - uvTL.x), (1.0 - HPERC) * (uvBR.y - uvTL.y)); + uvTL = uvTL + TOADDTL; - // const auto TOADDTL = Vector2D(XPERC * (uvBR.x - uvTL.x), YPERC * (uvBR.y - uvTL.y)); - // uvBR = uvBR - Vector2D((1.0 - WPERC) * (uvBR.x - uvTL.x), (1.0 - HPERC) * (uvBR.y - uvTL.y)); - // uvTL = uvTL + TOADDTL; - // } + auto maxSize = projSizeUnscaled; - g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft = uvTL; - g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight = uvBR; + if (pWindow->m_pWLSurface->small() && !pWindow->m_pWLSurface->m_bFillIgnoreSmall) + maxSize = pWindow->m_pWLSurface->getViewporterCorrectedSize(); - if (g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft == Vector2D() && g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { + if (geom.width > maxSize.x) + uvBR.x = uvBR.x * (maxSize.x / geom.width); + if (geom.height > maxSize.y) + uvBR.y = uvBR.y * (maxSize.y / geom.height); + } + + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = uvTL; + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = uvBR; + + if (g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft == Vector2D() && g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight == Vector2D(1, 1)) { // No special UV mods needed - g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); - g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); } } else { - g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); - g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); } } -static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { - // might be too strict - return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || - imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22) && - (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || - targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG); -} - -static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { - // might be too strict - return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || - imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG) && - (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || - targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22); -} - -SCMSettings CHyprRenderer::getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, - SP surface, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { - const auto sdrEOTF = NTransferFunction::fromConfig(); - NColorManagement::eTransferFunction srcTF; - - auto& m_renderData = g_pHyprOpenGL->m_renderData; - if (m_renderData.surface.valid()) { - if (m_renderData.surface->m_colorManagement.valid()) { - if (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB) - srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22; - else - srcTF = imageDescription->value().transferFunction; - } else if (sdrEOTF == NTransferFunction::TF_SRGB) - srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB; - else if (sdrEOTF == NTransferFunction::TF_GAMMA22 || sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22) - srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22; - else - srcTF = imageDescription->value().transferFunction; - } else - srcTF = imageDescription->value().transferFunction; - - const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription->value(), targetImageDescription->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value()); - const float maxLuminance = needsHDRmod ? - imageDescription->value().getTFMaxLuminance(-1) : - (imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference); - const auto dstMaxLuminance = targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000; - - auto matrix = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries()); - auto toXYZ = targetImageDescription->getPrimaries()->value().toXYZ(); - - const bool needsMod = (imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && - targetImageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && - ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || - (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f)); - - return { - .sourceTF = srcTF, - .targetTF = targetImageDescription->value().transferFunction, - .srcTFRange = {.min = imageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), - .max = imageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)}, - .dstTFRange = {.min = targetImageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), - .max = targetImageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)}, - .srcRefLuminance = imageDescription->value().luminances.reference, - .dstRefLuminance = targetImageDescription->value().luminances.reference, - .convertMatrix = matrix.mat(), - - .needsTonemap = maxLuminance >= dstMaxLuminance * 1.01, - .maxLuminance = maxLuminance * targetImageDescription->value().luminances.reference / imageDescription->value().luminances.reference, - .dstMaxLuminance = dstMaxLuminance, - .dstPrimaries2XYZ = toXYZ.mat(), - .needsSDRmod = needsMod, - .sdrSaturation = needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f, - .sdrBrightnessMultiplier = needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f, - }; -} - -void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { +void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) { static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now(); static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now(); static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now(); @@ -1385,89 +1127,132 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { static auto PDEBUGOVERLAY = CConfigValue("debug:overlay"); static auto PDAMAGETRACKINGMODE = CConfigValue("debug:damage_tracking"); static auto PDAMAGEBLINK = CConfigValue("debug:damage_blink"); - static auto PSOLDAMAGE = CConfigValue("debug:render_solitary_wo_damage"); + static auto PDIRECTSCANOUT = CConfigValue("render:direct_scanout"); static auto PVFR = CConfigValue("misc:vfr"); + static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor"); + static auto PANIMENABLED = CConfigValue("animations:enabled"); + static auto PFIRSTLAUNCHANIM = CConfigValue("animations:first_launch_animation"); + static auto PTEARINGENABLED = CConfigValue("general:allow_tearing"); static int damageBlinkCleanup = 0; // because double-buffered - const float ZOOMFACTOR = pMonitor->m_cursorZoom->value(); - - if (pMonitor->m_pixelSize.x < 1 || pMonitor->m_pixelSize.y < 1) { - Log::logger->log(Log::ERR, "Refusing to render a monitor because of an invalid pixel size: {}", pMonitor->m_pixelSize); - return; - } - if (!*PDAMAGEBLINK) damageBlinkCleanup = 0; - if (*PDEBUGOVERLAY == 1) { - renderStart = std::chrono::high_resolution_clock::now(); - g_pDebugOverlay->frameData(pMonitor); + static bool firstLaunch = true; + static bool firstLaunchAnimActive = *PFIRSTLAUNCHANIM; + + float zoomInFactorFirstLaunch = 1.f; + + if (firstLaunch) { + firstLaunch = false; + m_tRenderTimer.reset(); } - if (!g_pCompositor->m_sessionActive) + if (m_tRenderTimer.getSeconds() < 1.5f && firstLaunchAnimActive) { // TODO: make the animation system more damage-flexible so that this can be migrated to there + if (!*PANIMENABLED) { + zoomInFactorFirstLaunch = 1.f; + firstLaunchAnimActive = false; + } else { + zoomInFactorFirstLaunch = 2.f - g_pAnimationManager->getBezier("default")->getYForPoint(m_tRenderTimer.getSeconds() / 1.5); + damageMonitor(pMonitor); + } + } else { + firstLaunchAnimActive = false; + } + + renderStart = std::chrono::high_resolution_clock::now(); + + if (*PDEBUGOVERLAY == 1) + g_pDebugOverlay->frameData(pMonitor); + + if (!g_pCompositor->m_bSessionActive) return; - if (g_pAnimationManager) - g_pAnimationManager->frameTick(); - - if (pMonitor->m_id == m_mostHzMonitor->m_id || + if (pMonitor->ID == m_pMostHzMonitor->ID || *PVFR == 1) { // unfortunately with VFR we don't have the guarantee mostHz is going to be updated all the time, so we have to ignore that + g_pCompositor->sanityCheckWorkspaces(); g_pConfigManager->dispatchExecOnce(); // We exec-once when at least one monitor starts refreshing, meaning stuff has init'd - if (g_pConfigManager->m_wantsMonitorReload) + if (g_pConfigManager->m_bWantsMonitorReload) g_pConfigManager->performMonitorReload(); } - if (pMonitor->m_scheduledRecalc) { - pMonitor->m_scheduledRecalc = false; - if (pMonitor->m_activeWorkspace) // might be missing (mirror) - pMonitor->m_activeWorkspace->m_space->recalculate(); + if (pMonitor->scheduledRecalc) { + pMonitor->scheduledRecalc = false; + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->ID); } - if (!pMonitor->m_output->needsFrame && pMonitor->m_forceFullFrames == 0) + if (!pMonitor->output->needsFrame && pMonitor->forceFullFrames == 0) return; // tearing and DS first - bool shouldTear = pMonitor->updateTearing(); + bool shouldTear = false; + if (pMonitor->tearingState.nextRenderTorn) { + pMonitor->tearingState.nextRenderTorn = false; - if (pMonitor->attemptDirectScanout()) { - pMonitor->m_directScanoutIsActive = true; - return; - } else if (!pMonitor->m_lastScanout.expired() || pMonitor->m_directScanoutIsActive) { - Log::logger->log(Log::DEBUG, "Left a direct scanout."); - pMonitor->m_lastScanout.reset(); - pMonitor->m_directScanoutIsActive = false; + if (!*PTEARINGENABLED) { + Debug::log(WARN, "Tearing commit requested but the master switch general:allow_tearing is off, ignoring"); + return; + } - // reset DRM format, but only if needed since it might modeset - if (pMonitor->m_output->state->state().drmFormat != pMonitor->m_prevDrmFormat) - pMonitor->m_output->state->setFormat(pMonitor->m_prevDrmFormat); + if (g_pHyprOpenGL->m_RenderData.mouseZoomFactor != 1.0) { + Debug::log(WARN, "Tearing commit requested but scale factor is not 1, ignoring"); + return; + } - pMonitor->m_drmFormat = pMonitor->m_prevDrmFormat; + if (!pMonitor->tearingState.canTear) { + Debug::log(WARN, "Tearing commit requested but monitor doesn't support it, ignoring"); + return; + } + + if (!pMonitor->solitaryClient.expired()) + shouldTear = true; } - Event::bus()->m_events.render.pre.emit(pMonitor); + pMonitor->tearingState.activelyTearing = shouldTear; - const auto NOW = Time::steadyNow(); + if ((*PDIRECTSCANOUT == 1 || + (*PDIRECTSCANOUT == 2 && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && + pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN && pMonitor->activeWorkspace->getFullscreenWindow()->getContentType() == CONTENT_TYPE_GAME)) && + !shouldTear) { + if (pMonitor->attemptDirectScanout()) { + return; + } else if (!pMonitor->lastScanout.expired()) { + Debug::log(LOG, "Left a direct scanout."); + pMonitor->lastScanout.reset(); + + // reset DRM format, but only if needed since it might modeset + if (pMonitor->output->state->state().drmFormat != pMonitor->prevDrmFormat) + pMonitor->output->state->setFormat(pMonitor->prevDrmFormat); + + pMonitor->drmFormat = pMonitor->prevDrmFormat; + } + } + + EMIT_HOOK_EVENT("preRender", pMonitor); + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); // check the damage - bool hasChanged = pMonitor->m_output->needsFrame || pMonitor->m_damage.hasChanged(); + bool hasChanged = pMonitor->output->needsFrame || pMonitor->damage.hasChanged(); - if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && pMonitor->m_forceFullFrames == 0 && damageBlinkCleanup == 0) + if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && pMonitor->forceFullFrames == 0 && damageBlinkCleanup == 0) return; if (*PDAMAGETRACKINGMODE == -1) { - Log::logger->log(Log::CRIT, "Damage tracking mode -1 ????"); + Debug::log(CRIT, "Damage tracking mode -1 ????"); return; } - Event::bus()->m_events.render.stage.emit(RENDER_PRE); + EMIT_HOOK_EVENT("render", RENDER_PRE); - pMonitor->m_renderingActive = true; + pMonitor->renderingActive = true; // we need to cleanup fading out when rendering the appropriate context - g_pCompositor->cleanupFadingOut(pMonitor->m_id); + g_pCompositor->cleanupFadingOut(pMonitor->ID); // TODO: this is getting called with extents being 0,0,0,0 should it be? // potentially can save on resources. @@ -1475,154 +1260,147 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { TRACY_GPU_ZONE("Render"); static bool zoomLock = false; - if (zoomLock && ZOOMFACTOR == 1.f) { + if (zoomLock && *PZOOMFACTOR == 1.f) { g_pPointerManager->unlockSoftwareAll(); zoomLock = false; - } else if (!zoomLock && ZOOMFACTOR != 1.f) { + } else if (!zoomLock && *PZOOMFACTOR != 1.f) { g_pPointerManager->lockSoftwareAll(); zoomLock = true; } if (pMonitor == g_pCompositor->getMonitorFromCursor()) - g_pHyprOpenGL->m_renderData.mouseZoomFactor = std::clamp(ZOOMFACTOR, 1.f, INFINITY); + g_pHyprOpenGL->m_RenderData.mouseZoomFactor = std::clamp(*PZOOMFACTOR, 1.f, INFINITY); else - g_pHyprOpenGL->m_renderData.mouseZoomFactor = 1.f; + g_pHyprOpenGL->m_RenderData.mouseZoomFactor = 1.f; - if (pMonitor->m_zoomAnimProgress->value() != 1) { - g_pHyprOpenGL->m_renderData.mouseZoomFactor = 2.0 - pMonitor->m_zoomAnimProgress->value(); // 2x zoom -> 1x zoom - g_pHyprOpenGL->m_renderData.mouseZoomUseMouse = false; - g_pHyprOpenGL->m_renderData.useNearestNeighbor = false; + if (zoomInFactorFirstLaunch > 1.f) { + g_pHyprOpenGL->m_RenderData.mouseZoomFactor = zoomInFactorFirstLaunch; + g_pHyprOpenGL->m_RenderData.mouseZoomUseMouse = false; + g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; + pMonitor->forceFullFrames = 10; } CRegion damage, finalDamage; if (!beginRender(pMonitor, damage, RENDER_MODE_NORMAL)) { - Log::logger->log(Log::ERR, "renderer: couldn't beginRender()!"); + Debug::log(ERR, "renderer: couldn't beginRender()!"); return; } // if we have no tracking or full tracking, invalidate the entire monitor - if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->m_forceFullFrames > 0 || damageBlinkCleanup > 0) - damage = {0, 0, sc(pMonitor->m_transformedSize.x) * 10, sc(pMonitor->m_transformedSize.y) * 10}; + if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || pMonitor->forceFullFrames > 0 || damageBlinkCleanup > 0) + damage = {0, 0, (int)pMonitor->vecTransformedSize.x * 10, (int)pMonitor->vecTransformedSize.y * 10}; finalDamage = damage; // update damage in renderdata as we modified it g_pHyprOpenGL->setDamage(damage, finalDamage); - if (pMonitor->m_forceFullFrames > 0) { - pMonitor->m_forceFullFrames -= 1; - if (pMonitor->m_forceFullFrames > 10) - pMonitor->m_forceFullFrames = 0; + if (pMonitor->forceFullFrames > 0) { + pMonitor->forceFullFrames -= 1; + if (pMonitor->forceFullFrames > 10) + pMonitor->forceFullFrames = 0; } - Event::bus()->m_events.render.stage.emit(RENDER_BEGIN); + EMIT_HOOK_EVENT("render", RENDER_BEGIN); bool renderCursor = true; - if (pMonitor->m_solitaryClient && (!finalDamage.empty() || *PSOLDAMAGE)) - renderWindow(pMonitor->m_solitaryClient.lock(), pMonitor, NOW, false, RENDER_PASS_MAIN /* solitary = no popups */); - else if (!finalDamage.empty()) { - if (pMonitor->isMirror()) { - g_pHyprOpenGL->blend(false); - g_pHyprOpenGL->renderMirrored(); - g_pHyprOpenGL->blend(true); - Event::bus()->m_events.render.stage.emit(RENDER_POST_MIRROR); - renderCursor = false; - } else { - CBox renderBox = {0, 0, sc(pMonitor->m_pixelSize.x), sc(pMonitor->m_pixelSize.y)}; - renderWorkspace(pMonitor, pMonitor->m_activeWorkspace, NOW, renderBox); + if (!finalDamage.empty()) { + if (pMonitor->solitaryClient.expired()) { + if (pMonitor->isMirror()) { + g_pHyprOpenGL->blend(false); + g_pHyprOpenGL->renderMirrored(); + g_pHyprOpenGL->blend(true); + EMIT_HOOK_EVENT("render", RENDER_POST_MIRROR); + renderCursor = false; + } else { + CBox renderBox = {0, 0, (int)pMonitor->vecPixelSize.x, (int)pMonitor->vecPixelSize.y}; + renderWorkspace(pMonitor, pMonitor->activeWorkspace, &now, renderBox); - renderLockscreen(pMonitor, NOW, renderBox); + renderLockscreen(pMonitor, &now, renderBox); - if (pMonitor == Desktop::focusState()->monitor()) { - g_pHyprNotificationOverlay->draw(pMonitor); - g_pHyprError->draw(); + if (pMonitor == g_pCompositor->m_pLastMonitor) { + g_pHyprNotificationOverlay->draw(pMonitor); + g_pHyprError->draw(); + } + + // for drawing the debug overlay + if (pMonitor == g_pCompositor->m_vMonitors.front() && *PDEBUGOVERLAY == 1) { + renderStartOverlay = std::chrono::high_resolution_clock::now(); + g_pDebugOverlay->draw(); + endRenderOverlay = std::chrono::high_resolution_clock::now(); + } + + if (*PDAMAGEBLINK && damageBlinkCleanup == 0) { + CRectPassElement::SRectData data; + data.box = {0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}; + data.color = CHyprColor(1.0, 0.0, 1.0, 100.0 / 255.0); + m_sRenderPass.add(makeShared(data)); + damageBlinkCleanup = 1; + } else if (*PDAMAGEBLINK) { + damageBlinkCleanup++; + if (damageBlinkCleanup > 3) + damageBlinkCleanup = 0; + } } - - // for drawing the debug overlay - if (pMonitor == g_pCompositor->m_monitors.front() && *PDEBUGOVERLAY == 1) { - renderStartOverlay = std::chrono::high_resolution_clock::now(); - g_pDebugOverlay->draw(); - endRenderOverlay = std::chrono::high_resolution_clock::now(); - } - - if (*PDAMAGEBLINK && damageBlinkCleanup == 0) { - CRectPassElement::SRectData data; - data.box = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y}; - data.color = CHyprColor(1.0, 0.0, 1.0, 100.0 / 255.0); - m_renderPass.add(makeUnique(data)); - damageBlinkCleanup = 1; - } else if (*PDAMAGEBLINK) { - damageBlinkCleanup++; - if (damageBlinkCleanup > 3) - damageBlinkCleanup = 0; - } - } + } else + renderWindow(pMonitor->solitaryClient.lock(), pMonitor, &now, false, RENDER_PASS_MAIN /* solitary = no popups */); } else if (!pMonitor->isMirror()) { - sendFrameEventsToWorkspace(pMonitor, pMonitor->m_activeWorkspace, NOW); - if (pMonitor->m_activeSpecialWorkspace) - sendFrameEventsToWorkspace(pMonitor, pMonitor->m_activeSpecialWorkspace, NOW); + sendFrameEventsToWorkspace(pMonitor, pMonitor->activeWorkspace, &now); + if (pMonitor->activeSpecialWorkspace) + sendFrameEventsToWorkspace(pMonitor, pMonitor->activeSpecialWorkspace, &now); } renderCursor = renderCursor && shouldRenderCursor(); if (renderCursor) { TRACY_GPU_ZONE("RenderCursor"); - g_pPointerManager->renderSoftwareCursorsFor(pMonitor->m_self.lock(), NOW, g_pHyprOpenGL->m_renderData.damage); + g_pPointerManager->renderSoftwareCursorsFor(pMonitor->self.lock(), &now, g_pHyprOpenGL->m_RenderData.damage); } - if (pMonitor->m_dpmsBlackOpacity->value() != 0.F) { - // render the DPMS black if we are animating - CRectPassElement::SRectData data; - data.box = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y}; - data.color = Colors::BLACK.modifyA(pMonitor->m_dpmsBlackOpacity->value()); - m_renderPass.add(makeUnique(data)); - } - - Event::bus()->m_events.render.stage.emit(RENDER_LAST_MOMENT); + EMIT_HOOK_EVENT("render", RENDER_LAST_MOMENT); endRender(); TRACY_GPU_COLLECT; - CRegion frameDamage{g_pHyprOpenGL->m_renderData.damage}; + CRegion frameDamage{g_pHyprOpenGL->m_RenderData.damage}; - const auto TRANSFORM = Math::invertTransform(pMonitor->m_transform); - frameDamage.transform(Math::wlTransformToHyprutils(TRANSFORM), pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y); + const auto TRANSFORM = invertTransform(pMonitor->transform); + frameDamage.transform(wlTransformToHyprutils(TRANSFORM), pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y); if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR) - frameDamage.add(0, 0, sc(pMonitor->m_transformedSize.x), sc(pMonitor->m_transformedSize.y)); + frameDamage.add(0, 0, (int)pMonitor->vecTransformedSize.x, (int)pMonitor->vecTransformedSize.y); if (*PDAMAGEBLINK) frameDamage.add(damage); - if (!pMonitor->m_mirrors.empty()) + if (!pMonitor->mirrors.empty()) damageMirrorsWith(pMonitor, frameDamage); - pMonitor->m_renderingActive = false; + pMonitor->renderingActive = false; - Event::bus()->m_events.render.stage.emit(RENDER_POST); + EMIT_HOOK_EVENT("render", RENDER_POST); - pMonitor->m_output->state->addDamage(frameDamage); - pMonitor->m_output->state->setPresentationMode(shouldTear ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE : - Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC); + pMonitor->output->state->addDamage(frameDamage); + pMonitor->output->state->setPresentationMode(shouldTear ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE : + Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC); - if (commit) - commitPendingAndDoExplicitSync(pMonitor); + commitPendingAndDoExplicitSync(pMonitor); if (shouldTear) - pMonitor->m_tearingState.busy = true; + pMonitor->tearingState.busy = true; - if (*PDAMAGEBLINK || *PVFR == 0 || pMonitor->m_pendingFrame) + if (*PDAMAGEBLINK || *PVFR == 0 || pMonitor->pendingFrame) g_pCompositor->scheduleFrameForMonitor(pMonitor, Aquamarine::IOutput::AQ_SCHEDULE_RENDER_MONITOR); - pMonitor->m_pendingFrame = false; + pMonitor->pendingFrame = false; + + const float durationUs = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - renderStart).count() / 1000.f; + g_pDebugOverlay->renderData(pMonitor, durationUs); if (*PDEBUGOVERLAY == 1) { - const float durationUs = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - renderStart).count() / 1000.f; - g_pDebugOverlay->renderData(pMonitor, durationUs); - - if (pMonitor == g_pCompositor->m_monitors.front()) { + if (pMonitor == g_pCompositor->m_vMonitors.front()) { const float noOverlayUs = durationUs - std::chrono::duration_cast(endRenderOverlay - renderStartOverlay).count() / 1000.f; g_pDebugOverlay->renderDataNoOverlay(pMonitor, noOverlayUs); } else @@ -1632,36 +1410,26 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { static const hdr_output_metadata NO_HDR_METADATA = {.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; -static hdr_output_metadata createHDRMetadata(SImageDescription settings, SP monitor) { - uint8_t eotf = 0; - switch (settings.transferFunction) { - case CM_TRANSFER_FUNCTION_GAMMA22: - case CM_TRANSFER_FUNCTION_SRGB: eotf = 0; break; // used to send primaries and luminances to AQ. ignored for now - case CM_TRANSFER_FUNCTION_ST2084_PQ: eotf = 2; break; - case CM_TRANSFER_FUNCTION_EXT_LINEAR: - eotf = 2; - break; // should be Windows scRGB - // case CM_TRANSFER_FUNCTION_HLG: eotf = 3; break; TODO check display capabilities first - default: return NO_HDR_METADATA; // empty metadata for SDR - } +static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) { + if (settings.transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ) + return NO_HDR_METADATA; // empty metadata for SDR - const auto toNits = [](uint32_t value) { return sc(std::round(value)); }; - const auto to16Bit = [](float value) { return sc(std::round(value * 50000)); }; + const auto toNits = [](uint32_t value) { return uint16_t(std::round(value)); }; + const auto to16Bit = [](uint32_t value) { return uint16_t(std::round(value * 50000)); }; - auto colorimetry = settings.getPrimaries(); - auto luminances = settings.masteringLuminances.max > 0 ? settings.masteringLuminances : - (settings.luminances != SImageDescription::SPCLuminances{} ? - SImageDescription::SPCMasteringLuminances{.min = settings.luminances.min, .max = settings.luminances.max} : - SImageDescription::SPCMasteringLuminances{.min = monitor->minLuminance(), .max = monitor->maxLuminance(10000)}); + auto colorimetry = settings.primariesNameSet || settings.primaries == SPCPRimaries{} ? getPrimaries(settings.primariesNamed) : settings.primaries; + auto luminances = settings.masteringLuminances.max > 0 ? + settings.masteringLuminances : + SImageDescription::SPCMasteringLuminances{.min = edid.hdrMetadata->desiredContentMinLuminance, .max = edid.hdrMetadata->desiredContentMaxLuminance}; - Log::logger->log(Log::TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, - colorimetry.blue.x, colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y); - Log::logger->log(Log::TRACE, "ColorManagement min {}, max {}, cll {}, fall {}", luminances.min, luminances.max, settings.maxCLL, settings.maxFALL); + Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x, + colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y); + Debug::log(TRACE, "ColorManagement min {}, max {}, cll {}, fall {}", luminances.min, luminances.max, settings.maxCLL, settings.maxFALL); return hdr_output_metadata{ .metadata_type = 0, .hdmi_metadata_type1 = hdr_metadata_infoframe{ - .eotf = eotf, + .eotf = 2, .metadata_type = 0, .display_primaries = { @@ -1672,160 +1440,117 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, S .white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)}, .max_display_mastering_luminance = toNits(luminances.max), .min_display_mastering_luminance = toNits(luminances.min * 10000), - .max_cll = toNits(settings.maxCLL > 0 ? settings.maxCLL : monitor->maxCLL()), - .max_fall = toNits(settings.maxFALL > 0 ? settings.maxFALL : monitor->maxFALL()), + .max_cll = toNits(settings.maxCLL), + .max_fall = toNits(settings.maxFALL), }, }; } bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { - static auto PCT = CConfigValue("render:send_content_type"); - static auto PPASS = CConfigValue("render:cm_fs_passthrough"); - static auto PAUTOHDR = CConfigValue("render:cm_auto_hdr"); - static auto PNONSHADER = CConfigValue("render:non_shader_cm"); + static auto PPASS = CConfigValue("render:cm_fs_passthrough"); + const bool PHDR = pMonitor->imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ; - const bool configuredHDR = (pMonitor->m_cmType == NCMType::CM_HDR_EDID || pMonitor->m_cmType == NCMType::CM_HDR); - bool wantHDR = configuredHDR; + const bool SUPPORTSPQ = pMonitor->output->parsedEDID.hdrMetadata.has_value() ? pMonitor->output->parsedEDID.hdrMetadata->supportsPQ : false; + Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, SUPPORTSPQ); - const auto FS_WINDOW = pMonitor->getFullscreenWindow(); + if (pMonitor->output->parsedEDID.supportsBT2020 && SUPPORTSPQ) { + if (*PPASS && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) { + const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow(); + const auto ROOT_SURF = WINDOW->m_pWLSurface->resource(); + const auto SURF = + ROOT_SURF->findFirstPreorder([ROOT_SURF](SP surf) { return surf->colorManagement.valid() && surf->extends() == ROOT_SURF->extends(); }); - if (pMonitor->supportsHDR()) { - // HDR metadata determined by - // HDR scRGB - monitor settings - // HDR PQ surface & DS is active - surface settings - // PPASS = 0 monitor settings - // PPASS = 1 - // windowed: monitor settings - // fullscreen surface: surface settings FIXME: fullscreen SDR surface passthrough - pass degamma, gamma if needed - // PPASS = 2 - // windowed: monitor settings - // fullscreen SDR surface: monitor settings - // fullscreen HDR surface: surface settings - - bool hdrIsHandled = false; - if (FS_WINDOW) { - const auto ROOT_SURF = FS_WINDOW->wlSurface()->resource(); - const auto SURF = ROOT_SURF->findWithCM(); - - // we have a surface with image description - if (SURF && SURF->m_colorManagement.valid() && SURF->m_colorManagement->hasImageDescription()) { - const bool surfaceIsHDR = SURF->m_colorManagement->isHDR(); - if (!SURF->m_colorManagement->isWindowsScRGB() && (*PPASS == 1 || ((*PPASS == 2 || !pMonitor->m_lastScanout.expired()) && surfaceIsHDR))) { - // passthrough - bool needsHdrMetadataUpdate = SURF->m_colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != FS_WINDOW || pMonitor->m_needsHDRupdate; - if (SURF->m_colorManagement->needsHdrMetadataUpdate()) { - Log::logger->log(Log::INFO, "[CM] Recreating HDR metadata for surface"); - SURF->m_colorManagement->setHDRMetadata(createHDRMetadata(SURF->m_colorManagement->imageDescription(), pMonitor)); - } - if (needsHdrMetadataUpdate) { - Log::logger->log(Log::INFO, "[CM] Updating HDR metadata from surface"); - pMonitor->m_output->state->setHDRMetadata(SURF->m_colorManagement->hdrMetadata()); - } - hdrIsHandled = true; - pMonitor->m_needsHDRupdate = false; - } else if (*PAUTOHDR && surfaceIsHDR) - wantHDR = true; // auto-hdr: hdr on - } - } - - if (!hdrIsHandled) { - if (pMonitor->inHDR() != wantHDR) { - if (*PAUTOHDR && !(pMonitor->inHDR() && configuredHDR)) { - // modify or restore monitor image description for auto-hdr - // FIXME ok for now, will need some other logic if monitor image description can be modified some other way - const auto targetCM = wantHDR ? (*PAUTOHDR == 2 ? NCMType::CM_HDR_EDID : NCMType::CM_HDR) : pMonitor->m_cmType; - const auto targetSDREOTF = pMonitor->m_sdrEotf; - Log::logger->log(Log::INFO, "[CM] Auto HDR: changing monitor cm to {}", sc(targetCM)); - pMonitor->applyCMType(targetCM, targetSDREOTF); - pMonitor->m_previousFSWindow.reset(); // trigger CTM update - } - Log::logger->log(Log::INFO, wantHDR ? "[CM] Updating HDR metadata from monitor" : "[CM] Restoring SDR mode"); - pMonitor->m_output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->m_imageDescription->value(), pMonitor) : NO_HDR_METADATA); - } - pMonitor->m_needsHDRupdate = true; + const bool wantHDR = PHDR && *PPASS == 2; + if (SURF && SURF->colorManagement.valid() && SURF->colorManagement->hasImageDescription()) { + bool needsHdrMetadataUpdate = SURF->colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW; + if (SURF->colorManagement->needsHdrMetadataUpdate()) + SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID)); + if (needsHdrMetadataUpdate) + pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata()); + } else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != wantHDR) + pMonitor->output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA); + pMonitor->m_previousFSWindow = WINDOW; + } else { + if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR) + pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA); + pMonitor->m_previousFSWindow.reset(); } } - const bool needsWCG = pMonitor->wantsWideColor(); - if (pMonitor->m_output->state->state().wideColorGamut != needsWCG) { - Log::logger->log(Log::TRACE, "Setting wide color gamut {}", needsWCG ? "on" : "off"); - pMonitor->m_output->state->setWideColorGamut(needsWCG); + const bool needsWCG = pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2 || pMonitor->imageDescription.primariesNamed == CM_PRIMARIES_BT2020; + if (pMonitor->output->state->state().wideColorGamut != needsWCG) { + Debug::log(TRACE, "Setting wide color gamut {}", needsWCG ? "on" : "off"); + pMonitor->output->state->setWideColorGamut(needsWCG); // FIXME do not trust enabled10bit, auto switch to 10bit and back if needed - if (needsWCG && !pMonitor->m_enabled10bit) { - Log::logger->log(Log::WARN, "Wide color gamut is enabled but the display is not in 10bit mode"); + if (needsWCG && !pMonitor->enabled10bit) { + Debug::log(WARN, "Wide color gamut is enabled but the display is not in 10bit mode"); static bool shown = false; if (!shown) { - g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, {{"name", pMonitor->m_name}}), CHyprColor{}, 15000, - ICON_WARNING); + g_pHyprNotificationOverlay->addNotification("Wide color gamut is enabled but the display is not in 10bit mode", CHyprColor{}, 15000, ICON_WARNING); shown = true; } } } - if (*PCT) - pMonitor->m_output->state->setContentType(NContentType::toDRM(FS_WINDOW ? FS_WINDOW->getContentType() : CONTENT_TYPE_NONE)); + if (pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) { + const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow(); + pMonitor->output->state->setContentType(NContentType::toDRM(WINDOW->getContentType())); + } else + pMonitor->output->state->setContentType(NContentType::toDRM(CONTENT_TYPE_NONE)); - if (FS_WINDOW != pMonitor->m_previousFSWindow || (!FS_WINDOW && pMonitor->m_noShaderCTM)) { - if (*PNONSHADER == CM_NS_IGNORE || !FS_WINDOW || !pMonitor->needsCM() || !pMonitor->canNoShaderCM() || - (*PNONSHADER == CM_NS_ONDEMAND && pMonitor->m_lastScanout.expired() && *PPASS != 1)) { - if (pMonitor->m_noShaderCTM) { - Log::logger->log(Log::INFO, "[CM] No fullscreen CTM, restoring previous one"); - pMonitor->m_noShaderCTM = false; - pMonitor->m_ctmUpdated = true; - } - } else { - const auto FS_DESC = pMonitor->getFSImageDescription(); - if (FS_DESC.has_value()) { - Log::logger->log(Log::INFO, "[CM] Updating fullscreen CTM"); - pMonitor->m_noShaderCTM = true; - auto conversion = FS_DESC.value()->getPrimaries()->convertMatrix(pMonitor->m_imageDescription->getPrimaries()); - const auto mat = conversion.mat(); - const std::array CTM = { - mat[0][0], mat[0][1], mat[0][2], // - mat[1][0], mat[1][1], mat[1][2], // - mat[2][0], mat[2][1], mat[2][2], // - }; - pMonitor->m_output->state->setCTM(CTM); - } - } + if (pMonitor->ctmUpdated) { + pMonitor->ctmUpdated = false; + pMonitor->output->state->setCTM(pMonitor->ctm); } - if (pMonitor->m_ctmUpdated && !pMonitor->m_noShaderCTM) { - pMonitor->m_ctmUpdated = false; - pMonitor->m_output->state->setCTM(pMonitor->m_ctm); - } - - pMonitor->m_previousFSWindow = FS_WINDOW; - - bool ok = pMonitor->m_state.commit(); + bool ok = pMonitor->state.commit(); if (!ok) { - if (pMonitor->m_inFence.isValid()) { - Log::logger->log(Log::TRACE, "Monitor state commit failed, retrying without a fence"); - pMonitor->m_output->state->resetExplicitFences(); - ok = pMonitor->m_state.commit(); + if (pMonitor->inFence.isValid()) { + Debug::log(TRACE, "Monitor state commit failed, retrying without a fence"); + pMonitor->output->state->resetExplicitFences(); + ok = pMonitor->state.commit(); } if (!ok) { - Log::logger->log(Log::TRACE, "Monitor state commit failed"); + Debug::log(TRACE, "Monitor state commit failed"); // rollback the buffer to avoid writing to the front buffer that is being // displayed - pMonitor->m_output->swapchain->rollback(); - pMonitor->m_damage.damageEntire(); + pMonitor->output->swapchain->rollback(); + pMonitor->damage.damageEntire(); } } + auto explicitOptions = getExplicitSyncSettings(pMonitor->output); + if (!explicitOptions.explicitEnabled) + return ok; + + Debug::log(TRACE, "Explicit: {} presented", explicitPresented.size()); + + if (!pMonitor->eglSync) + Debug::log(TRACE, "Explicit: can't add sync, monitor has no EGLSync"); + else { + for (auto const& e : explicitPresented) { + if (!e->current.buffer || !e->current.buffer->buffer->syncReleaser) + continue; + + e->current.buffer->buffer->syncReleaser->addReleaseSync(pMonitor->eglSync); + } + } + + explicitPresented.clear(); + return ok; } -void CHyprRenderer::renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry) { +void CHyprRenderer::renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* now, const CBox& geometry) { Vector2D translate = {geometry.x, geometry.y}; - float scale = sc(geometry.width) / pMonitor->m_pixelSize.x; + float scale = (float)geometry.width / pMonitor->vecPixelSize.x; TRACY_GPU_ZONE("RenderWorkspace"); - if (!DELTALESSTHAN(sc(geometry.width) / sc(geometry.height), pMonitor->m_pixelSize.x / pMonitor->m_pixelSize.y, 0.01)) { - Log::logger->log(Log::ERR, "Ignoring geometry in renderWorkspace: aspect ratio mismatch"); + if (!DELTALESSTHAN((double)geometry.width / (double)geometry.height, pMonitor->vecPixelSize.x / pMonitor->vecPixelSize.y, 0.01)) { + Debug::log(ERR, "Ignoring geometry in renderWorkspace: aspect ratio mismatch"); scale = 1.f; translate = Vector2D{}; } @@ -1833,12 +1558,24 @@ void CHyprRenderer::renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace renderAllClientsForWorkspace(pMonitor, pWorkspace, now, translate, scale); } -void CHyprRenderer::sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now) { - for (const auto& view : Desktop::View::getViewsForWorkspace(pWorkspace)) { - if (!view->aliveAndVisible()) +void CHyprRenderer::sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* now) { + for (auto const& w : g_pCompositor->m_vWindows) { + if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || !w->m_pWLSurface->resource()) continue; - view->wlSurface()->resource()->frame(now); + if (!shouldRenderWindow(w, pMonitor)) + continue; + + w->m_pWLSurface->resource()->breadthfirst([now](SP r, const Vector2D& offset, void* d) { r->frame(now); }, nullptr); + } + + for (auto const& lsl : pMonitor->m_aLayerSurfaceLayers) { + for (auto const& ls : lsl) { + if (ls->fadingOut || !ls->surface->resource()) + continue; + + ls->surface->resource()->breadthfirst([now](SP r, const Vector2D& offset, void* d) { r->frame(now); }, nullptr); + } } } @@ -1910,14 +1647,14 @@ static void applyExclusive(CBox& usableArea, uint32_t anchor, int32_t exclusive, } void CHyprRenderer::arrangeLayerArray(PHLMONITOR pMonitor, const std::vector& layerSurfaces, bool exclusiveZone, CBox* usableArea) { - CBox full_area = {pMonitor->m_position.x, pMonitor->m_position.y, pMonitor->m_size.x, pMonitor->m_size.y}; + CBox full_area = {pMonitor->vecPosition.x, pMonitor->vecPosition.y, pMonitor->vecSize.x, pMonitor->vecSize.y}; for (auto const& ls : layerSurfaces) { - if (!ls || ls->m_fadingOut || ls->m_readyToDelete || !ls->m_layerSurface || ls->m_noProcess) + if (!ls || ls->fadingOut || ls->readyToDelete || !ls->layerSurface || ls->noProcess) continue; - const auto PLAYER = ls->m_layerSurface; - const auto PSTATE = &PLAYER->m_current; + const auto PLAYER = ls->layerSurface; + const auto PSTATE = &PLAYER->current; if (exclusiveZone != (PSTATE->exclusive > 0)) continue; @@ -1927,7 +1664,7 @@ void CHyprRenderer::arrangeLayerArray(PHLMONITOR pMonitor, const std::vectorm_geometry.width, ls->m_geometry.height}; + const Vector2D OLDSIZE = {ls->geometry.width, ls->geometry.height}; CBox box = {{}, PSTATE->desiredSize}; // Horizontal axis @@ -1978,99 +1715,112 @@ void CHyprRenderer::arrangeLayerArray(PHLMONITOR pMonitor, const std::vectormargin.bottom; if (box.width <= 0 || box.height <= 0) { - Log::logger->log(Log::ERR, "LayerSurface {:x} has a negative/zero w/h???", rc(ls.get())); + Debug::log(ERR, "LayerSurface {:x} has a negative/zero w/h???", (uintptr_t)ls.get()); continue; } box.round(); // fix rounding errors - ls->m_geometry = box; + ls->geometry = box; applyExclusive(*usableArea, PSTATE->anchor, PSTATE->exclusive, PSTATE->exclusiveEdge, PSTATE->margin.top, PSTATE->margin.right, PSTATE->margin.bottom, PSTATE->margin.left); if (Vector2D{box.width, box.height} != OLDSIZE) - ls->m_layerSurface->configure(box.size()); + ls->layerSurface->configure(box.size()); - *ls->m_realPosition = box.pos(); - *ls->m_realSize = box.size(); + *ls->realPosition = box.pos(); + *ls->realSize = box.size(); } } void CHyprRenderer::arrangeLayersForMonitor(const MONITORID& monitor) { - const auto PMONITOR = g_pCompositor->getMonitorFromID(monitor); + const auto PMONITOR = g_pCompositor->getMonitorFromID(monitor); - if (!PMONITOR || PMONITOR->m_size.x <= 0 || PMONITOR->m_size.y <= 0) + static auto BAR_POSITION = CConfigValue("debug:error_position"); + + if (!PMONITOR) return; // Reset the reserved - PMONITOR->m_reservedArea.resetType(Desktop::RESERVED_DYNAMIC_TYPE_LS); + PMONITOR->vecReservedBottomRight = Vector2D(); + PMONITOR->vecReservedTopLeft = Vector2D(); - const CBox ORIGINAL_USABLE_AREA = PMONITOR->logicalBoxMinusReserved(); - CBox usableArea = ORIGINAL_USABLE_AREA; + CBox usableArea = {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; - for (auto& la : PMONITOR->m_layerSurfaceLayers) { - std::ranges::stable_sort( - la, [](const PHLLSREF& a, const PHLLSREF& b) { return a->m_ruleApplicator->order().valueOrDefault() > b->m_ruleApplicator->order().valueOrDefault(); }); + if (g_pHyprError->active() && g_pCompositor->m_pLastMonitor == PMONITOR->self) { + const auto HEIGHT = g_pHyprError->height(); + if (*BAR_POSITION == 0) { + PMONITOR->vecReservedTopLeft.y = HEIGHT; + usableArea.y += HEIGHT; + usableArea.h -= HEIGHT; + } else { + PMONITOR->vecReservedBottomRight.y = HEIGHT; + usableArea.h -= HEIGHT; + } } - for (auto const& la : PMONITOR->m_layerSurfaceLayers) + for (auto& la : PMONITOR->m_aLayerSurfaceLayers) { + std::stable_sort(la.begin(), la.end(), [](const PHLLSREF& a, const PHLLSREF& b) { return a->order > b->order; }); + } + + for (auto const& la : PMONITOR->m_aLayerSurfaceLayers) arrangeLayerArray(PMONITOR, la, true, &usableArea); - for (auto const& la : PMONITOR->m_layerSurfaceLayers) + for (auto const& la : PMONITOR->m_aLayerSurfaceLayers) arrangeLayerArray(PMONITOR, la, false, &usableArea); - PMONITOR->m_reservedArea.addType(Desktop::RESERVED_DYNAMIC_TYPE_LS, Desktop::CReservedArea{ORIGINAL_USABLE_AREA, usableArea}); + PMONITOR->vecReservedTopLeft = Vector2D(usableArea.x, usableArea.y) - PMONITOR->vecPosition; + PMONITOR->vecReservedBottomRight = PMONITOR->vecSize - Vector2D(usableArea.width, usableArea.height) - PMONITOR->vecReservedTopLeft; + + auto ADDITIONALRESERVED = g_pConfigManager->m_mAdditionalReservedAreas.find(PMONITOR->szName); + if (ADDITIONALRESERVED == g_pConfigManager->m_mAdditionalReservedAreas.end()) { + ADDITIONALRESERVED = g_pConfigManager->m_mAdditionalReservedAreas.find(""); // glob wildcard + } + + if (ADDITIONALRESERVED != g_pConfigManager->m_mAdditionalReservedAreas.end()) { + PMONITOR->vecReservedTopLeft = PMONITOR->vecReservedTopLeft + Vector2D(ADDITIONALRESERVED->second.left, ADDITIONALRESERVED->second.top); + PMONITOR->vecReservedBottomRight = PMONITOR->vecReservedBottomRight + Vector2D(ADDITIONALRESERVED->second.right, ADDITIONALRESERVED->second.bottom); + } // damage the monitor if can damageMonitor(PMONITOR); - g_layoutManager->invalidateMonitorGeometries(PMONITOR); + g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitor); } void CHyprRenderer::damageSurface(SP pSurface, double x, double y, double scale) { if (!pSurface) return; // wut? - if (g_pCompositor->m_unsafeState) + if (g_pCompositor->m_bUnsafeState) return; - const auto WLSURF = Desktop::View::CWLSurface::fromResource(pSurface); + const auto WLSURF = CWLSurface::fromResource(pSurface); + CRegion damageBox = WLSURF ? WLSURF->computeDamage() : CRegion{}; if (!WLSURF) { - Log::logger->log(Log::ERR, "BUG THIS: No CWLSurface for surface in damageSurface!!!"); + Debug::log(ERR, "BUG THIS: No CWLSurface for surface in damageSurface!!!"); return; } - // hack: schedule frame events - if (!WLSURF->resource()->m_current.callbacks.empty() && pSurface->m_hlSurface) { - const auto BOX = pSurface->m_hlSurface->getSurfaceBoxGlobal(); - if (BOX && !BOX->empty()) { - for (auto const& m : g_pCompositor->m_monitors) { - if (!m->m_output) - continue; - - if (BOX->overlaps(m->logicalBox())) - g_pCompositor->scheduleFrameForMonitor(m, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); - } - } - } - - CRegion damageBox = WLSURF->computeDamage(); - if (damageBox.empty()) - return; - if (scale != 1.0) damageBox.scale(scale); + // schedule frame events + g_pCompositor->scheduleFrameForMonitor(g_pCompositor->getMonitorFromVector(Vector2D(x, y)), Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE); + + if (damageBox.empty()) + return; + damageBox.translate({x, y}); CRegion damageBoxForEach; - for (auto const& m : g_pCompositor->m_monitors) { - if (!m->m_output) + for (auto const& m : g_pCompositor->m_vMonitors) { + if (!m->output) continue; damageBoxForEach.set(damageBox); - damageBoxForEach.translate({-m->m_position.x, -m->m_position.y}).scale(m->m_scale); + damageBoxForEach.translate({-m->vecPosition.x, -m->vecPosition.y}).scale(m->scale); m->addDamage(damageBoxForEach); } @@ -2078,39 +1828,39 @@ void CHyprRenderer::damageSurface(SP pSurface, double x, dou static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); if (*PLOGDAMAGE) - Log::logger->log(Log::DEBUG, "Damage: Surface (extents): xy: {}, {} wh: {}, {}", damageBox.pixman()->extents.x1, damageBox.pixman()->extents.y1, - damageBox.pixman()->extents.x2 - damageBox.pixman()->extents.x1, damageBox.pixman()->extents.y2 - damageBox.pixman()->extents.y1); + Debug::log(LOG, "Damage: Surface (extents): xy: {}, {} wh: {}, {}", damageBox.pixman()->extents.x1, damageBox.pixman()->extents.y1, + damageBox.pixman()->extents.x2 - damageBox.pixman()->extents.x1, damageBox.pixman()->extents.y2 - damageBox.pixman()->extents.y1); } void CHyprRenderer::damageWindow(PHLWINDOW pWindow, bool forceFull) { - if (g_pCompositor->m_unsafeState) + if (g_pCompositor->m_bUnsafeState) return; CBox windowBox = pWindow->getFullWindowBoundingBox(); - const auto PWINDOWWORKSPACE = pWindow->m_workspace; - if (PWINDOWWORKSPACE && PWINDOWWORKSPACE->m_renderOffset->isBeingAnimated() && !pWindow->m_pinned) - windowBox.translate(PWINDOWWORKSPACE->m_renderOffset->value()); - windowBox.translate(pWindow->m_floatingOffset); + const auto PWINDOWWORKSPACE = pWindow->m_pWorkspace; + if (PWINDOWWORKSPACE && PWINDOWWORKSPACE->m_vRenderOffset->isBeingAnimated() && !pWindow->m_bPinned) + windowBox.translate(PWINDOWWORKSPACE->m_vRenderOffset->value()); + windowBox.translate(pWindow->m_vFloatingOffset); - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { if (forceFull || shouldRenderWindow(pWindow, m)) { // only damage if window is rendered on monitor - CBox fixedDamageBox = {windowBox.x - m->m_position.x, windowBox.y - m->m_position.y, windowBox.width, windowBox.height}; - fixedDamageBox.scale(m->m_scale); + CBox fixedDamageBox = {windowBox.x - m->vecPosition.x, windowBox.y - m->vecPosition.y, windowBox.width, windowBox.height}; + fixedDamageBox.scale(m->scale); m->addDamage(fixedDamageBox); } } - for (auto const& wd : pWindow->m_windowDecorations) + for (auto const& wd : pWindow->m_dWindowDecorations) wd->damageEntire(); static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); if (*PLOGDAMAGE) - Log::logger->log(Log::DEBUG, "Damage: Window ({}): xy: {}, {} wh: {}, {}", pWindow->m_title, windowBox.x, windowBox.y, windowBox.width, windowBox.height); + Debug::log(LOG, "Damage: Window ({}): xy: {}, {} wh: {}, {}", pWindow->m_szTitle, windowBox.x, windowBox.y, windowBox.width, windowBox.height); } void CHyprRenderer::damageMonitor(PHLMONITOR pMonitor) { - if (g_pCompositor->m_unsafeState || pMonitor->isMirror()) + if (g_pCompositor->m_bUnsafeState || pMonitor->isMirror()) return; CBox damageBox = {0, 0, INT16_MAX, INT16_MAX}; @@ -2119,19 +1869,19 @@ void CHyprRenderer::damageMonitor(PHLMONITOR pMonitor) { static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); if (*PLOGDAMAGE) - Log::logger->log(Log::DEBUG, "Damage: Monitor {}", pMonitor->m_name); + Debug::log(LOG, "Damage: Monitor {}", pMonitor->szName); } void CHyprRenderer::damageBox(const CBox& box, bool skipFrameSchedule) { - if (g_pCompositor->m_unsafeState) + if (g_pCompositor->m_bUnsafeState) return; - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { if (m->isMirror()) continue; // don't damage mirrors traditionally if (!skipFrameSchedule) { - CBox damageBox = box.copy().translate(-m->m_position).scale(m->m_scale).round(); + CBox damageBox = box.copy().translate(-m->vecPosition).scale(m->scale); m->addDamage(damageBox); } } @@ -2139,7 +1889,7 @@ void CHyprRenderer::damageBox(const CBox& box, bool skipFrameSchedule) { static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); if (*PLOGDAMAGE) - Log::logger->log(Log::DEBUG, "Damage: Box: xy: {}, {} wh: {}, {}", box.x, box.y, box.w, box.h); + Debug::log(LOG, "Damage: Box: xy: {}, {} wh: {}, {}", box.x, box.y, box.w, box.h); } void CHyprRenderer::damageBox(const int& x, const int& y, const int& w, const int& h) { @@ -2148,11 +1898,13 @@ void CHyprRenderer::damageBox(const int& x, const int& y, const int& w, const in } void CHyprRenderer::damageRegion(const CRegion& rg) { - rg.forEachRect([this](const auto& RECT) { damageBox(RECT.x1, RECT.y1, RECT.x2 - RECT.x1, RECT.y2 - RECT.y1); }); + for (auto const& RECT : rg.getRects()) { + damageBox(RECT.x1, RECT.y1, RECT.x2 - RECT.x1, RECT.y2 - RECT.y1); + } } void CHyprRenderer::damageMirrorsWith(PHLMONITOR pMonitor, const CRegion& pRegion) { - for (auto const& mirror : pMonitor->m_mirrors) { + for (auto const& mirror : pMonitor->mirrors) { // transform the damage here, so it won't get clipped by the monitor damage ring auto monitor = mirror; @@ -2160,13 +1912,13 @@ void CHyprRenderer::damageMirrorsWith(PHLMONITOR pMonitor, const CRegion& pRegio CRegion transformed{pRegion}; // we want to transform to the same box as in CHyprOpenGLImpl::renderMirrored - double scale = std::min(monitor->m_transformedSize.x / pMonitor->m_transformedSize.x, monitor->m_transformedSize.y / pMonitor->m_transformedSize.y); - CBox monbox = {0, 0, pMonitor->m_transformedSize.x * scale, pMonitor->m_transformedSize.y * scale}; - monbox.x = (monitor->m_transformedSize.x - monbox.w) / 2; - monbox.y = (monitor->m_transformedSize.y - monbox.h) / 2; + double scale = std::min(monitor->vecTransformedSize.x / pMonitor->vecTransformedSize.x, monitor->vecTransformedSize.y / pMonitor->vecTransformedSize.y); + CBox monbox = {0, 0, pMonitor->vecTransformedSize.x * scale, pMonitor->vecTransformedSize.y * scale}; + monbox.x = (monitor->vecTransformedSize.x - monbox.w) / 2; + monbox.y = (monitor->vecTransformedSize.y - monbox.h) / 2; transformed.scale(scale); - transformed.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize.x * scale, pMonitor->m_pixelSize.y * scale); + transformed.transform(wlTransformToHyprutils(pMonitor->transform), pMonitor->vecPixelSize.x * scale, pMonitor->vecPixelSize.y * scale); transformed.translate(Vector2D(monbox.x, monbox.y)); mirror->addDamage(transformed); @@ -2175,168 +1927,138 @@ void CHyprRenderer::damageMirrorsWith(PHLMONITOR pMonitor, const CRegion& pRegio } } -void CHyprRenderer::renderDragIcon(PHLMONITOR pMonitor, const Time::steady_tp& time) { +void CHyprRenderer::renderDragIcon(PHLMONITOR pMonitor, timespec* time) { PROTO::data->renderDND(pMonitor, time); } -void CHyprRenderer::setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force) { - m_cursorHasSurface = surf && surf->resource(); +void CHyprRenderer::setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force) { + m_bCursorHasSurface = surf; - m_lastCursorData.name = ""; - m_lastCursorData.surf = surf; - m_lastCursorData.hotspotX = hotspotX; - m_lastCursorData.hotspotY = hotspotY; + m_sLastCursorData.name = ""; + m_sLastCursorData.surf = surf; + m_sLastCursorData.hotspotX = hotspotX; + m_sLastCursorData.hotspotY = hotspotY; - if (m_cursorHidden && !force) + if (m_bCursorHidden && !force) return; g_pCursorManager->setCursorSurface(surf, {hotspotX, hotspotY}); } void CHyprRenderer::setCursorFromName(const std::string& name, bool force) { - m_cursorHasSurface = true; + m_bCursorHasSurface = true; - if (name == m_lastCursorData.name && !force) + if (name == m_sLastCursorData.name && !force) return; - m_lastCursorData.name = name; + m_sLastCursorData.name = name; + m_sLastCursorData.surf.reset(); - static auto getShapeOrDefault = [](std::string_view name) -> wpCursorShapeDeviceV1Shape { - const auto it = std::ranges::find(CURSOR_SHAPE_NAMES, name); - - if (it == CURSOR_SHAPE_NAMES.end()) { - // clang-format off - static const auto overrites = std::unordered_map { - {"top_side", WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE}, - {"bottom_side", WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE}, - {"left_side", WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE}, - {"right_side", WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE}, - {"top_left_corner", WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE}, - {"bottom_left_corner", WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE}, - {"top_right_corner", WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE}, - {"bottom_right_corner", WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE}, - }; - // clang-format on - - if (overrites.contains(name)) - return overrites.at(name); - - return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; - } - - return sc(std::distance(CURSOR_SHAPE_NAMES.begin(), it)); - }; - - const auto newShape = getShapeOrDefault(name); - - if (newShape != m_lastCursorData.shape) { - m_lastCursorData.shapePrevious = m_lastCursorData.shape; - m_lastCursorData.switchedTimer.reset(); - } - - m_lastCursorData.shape = newShape; - - m_lastCursorData.surf.reset(); - - if (m_cursorHidden && !force) + if (m_bCursorHidden && !force) return; g_pCursorManager->setCursorFromName(name); } void CHyprRenderer::ensureCursorRenderingMode() { - static auto PINVISIBLE = CConfigValue("cursor:invisible"); static auto PCURSORTIMEOUT = CConfigValue("cursor:inactive_timeout"); static auto PHIDEONTOUCH = CConfigValue("cursor:hide_on_touch"); - static auto PHIDEONTABLET = CConfigValue("cursor:hide_on_tablet"); static auto PHIDEONKEY = CConfigValue("cursor:hide_on_key_press"); if (*PCURSORTIMEOUT <= 0) - m_cursorHiddenConditions.hiddenOnTimeout = false; + m_sCursorHiddenConditions.hiddenOnTimeout = false; if (*PHIDEONTOUCH == 0) - m_cursorHiddenConditions.hiddenOnTouch = false; - if (*PHIDEONTABLET == 0) - m_cursorHiddenConditions.hiddenOnTablet = false; + m_sCursorHiddenConditions.hiddenOnTouch = false; if (*PHIDEONKEY == 0) - m_cursorHiddenConditions.hiddenOnKeyboard = false; + m_sCursorHiddenConditions.hiddenOnKeyboard = false; if (*PCURSORTIMEOUT > 0) - m_cursorHiddenConditions.hiddenOnTimeout = *PCURSORTIMEOUT < g_pInputManager->m_lastCursorMovement.getSeconds(); + m_sCursorHiddenConditions.hiddenOnTimeout = *PCURSORTIMEOUT < g_pInputManager->m_tmrLastCursorMovement.getSeconds(); - m_cursorHiddenByCondition = - m_cursorHiddenConditions.hiddenOnTimeout || m_cursorHiddenConditions.hiddenOnTouch || m_cursorHiddenConditions.hiddenOnTablet || m_cursorHiddenConditions.hiddenOnKeyboard; + const bool HIDE = m_sCursorHiddenConditions.hiddenOnTimeout || m_sCursorHiddenConditions.hiddenOnTouch || m_sCursorHiddenConditions.hiddenOnKeyboard; - const bool HIDE = m_cursorHiddenByCondition || (*PINVISIBLE != 0); - - if (HIDE == m_cursorHidden) + if (HIDE == m_bCursorHidden) return; - if (HIDE) - Log::logger->log(Log::DEBUG, "Hiding the cursor (hl-mandated)"); - else - Log::logger->log(Log::DEBUG, "Showing the cursor (hl-mandated)"); + if (HIDE) { + Debug::log(LOG, "Hiding the cursor (hl-mandated)"); - for (auto const& m : g_pCompositor->m_monitors) { - if (!g_pPointerManager->softwareLockedFor(m)) - continue; + for (auto const& m : g_pCompositor->m_vMonitors) { + if (!g_pPointerManager->softwareLockedFor(m)) + continue; - g_pPointerManager->damageCursor(m, m->shouldSkipScheduleFrameOnMouseEvent()); + damageMonitor(m); // TODO: maybe just damage the cursor area? + } + + setCursorHidden(true); + + } else { + Debug::log(LOG, "Showing the cursor (hl-mandated)"); + + for (auto const& m : g_pCompositor->m_vMonitors) { + if (!g_pPointerManager->softwareLockedFor(m)) + continue; + + damageMonitor(m); // TODO: maybe just damage the cursor area? + } + + setCursorHidden(false); } - - setCursorHidden(HIDE); } void CHyprRenderer::setCursorHidden(bool hide) { - if (hide == m_cursorHidden) + if (hide == m_bCursorHidden) return; - m_cursorHidden = hide; + m_bCursorHidden = hide; if (hide) { g_pPointerManager->resetCursorImage(); return; } - if (m_lastCursorData.surf.has_value()) - setCursorSurface(m_lastCursorData.surf.value(), m_lastCursorData.hotspotX, m_lastCursorData.hotspotY, true); - else if (!m_lastCursorData.name.empty()) - setCursorFromName(m_lastCursorData.name, true); + if (m_sLastCursorData.surf.has_value()) + setCursorSurface(m_sLastCursorData.surf.value(), m_sLastCursorData.hotspotX, m_sLastCursorData.hotspotY, true); + else if (!m_sLastCursorData.name.empty()) + setCursorFromName(m_sLastCursorData.name, true); else setCursorFromName("left_ptr", true); } bool CHyprRenderer::shouldRenderCursor() { - return !m_cursorHidden && m_cursorHasSurface; + return !m_bCursorHidden && m_bCursorHasSurface; } std::tuple CHyprRenderer::getRenderTimes(PHLMONITOR pMonitor) { - const auto POVERLAY = &g_pDebugOverlay->m_monitorOverlays[pMonitor]; + const auto POVERLAY = &g_pDebugOverlay->m_mMonitorOverlays[pMonitor]; float avgRenderTime = 0; float maxRenderTime = 0; float minRenderTime = 9999; - for (auto const& rt : POVERLAY->m_lastRenderTimes) { - maxRenderTime = std::max(rt, maxRenderTime); - minRenderTime = std::min(rt, minRenderTime); + for (auto const& rt : POVERLAY->m_dLastRenderTimes) { + if (rt > maxRenderTime) + maxRenderTime = rt; + if (rt < minRenderTime) + minRenderTime = rt; avgRenderTime += rt; } - avgRenderTime /= POVERLAY->m_lastRenderTimes.empty() ? 1 : POVERLAY->m_lastRenderTimes.size(); + avgRenderTime /= POVERLAY->m_dLastRenderTimes.size() == 0 ? 1 : POVERLAY->m_dLastRenderTimes.size(); return std::make_tuple<>(avgRenderTime, maxRenderTime, minRenderTime); } static int handleCrashLoop(void* data) { - g_pHyprNotificationOverlay->addNotification("Hyprland will crash in " + std::to_string(10 - sc(g_pHyprRenderer->m_crashingDistort * 2.f)) + "s.", CHyprColor(0), 5000, + g_pHyprNotificationOverlay->addNotification("Hyprland will crash in " + std::to_string(10 - (int)(g_pHyprRenderer->m_fCrashingDistort * 2.f)) + "s.", CHyprColor(0), 5000, ICON_INFO); - g_pHyprRenderer->m_crashingDistort += 0.5f; + g_pHyprRenderer->m_fCrashingDistort += 0.5f; - if (g_pHyprRenderer->m_crashingDistort >= 5.5f) + if (g_pHyprRenderer->m_fCrashingDistort >= 5.5f) raise(SIGABRT); - wl_event_source_timer_update(g_pHyprRenderer->m_crashingLoop, 1000); + wl_event_source_timer_update(g_pHyprRenderer->m_pCrashingLoop, 1000); return 1; } @@ -2344,31 +2066,88 @@ static int handleCrashLoop(void* data) { void CHyprRenderer::initiateManualCrash() { g_pHyprNotificationOverlay->addNotification("Manual crash initiated. Farewell...", CHyprColor(0), 5000, ICON_INFO); - m_crashingLoop = wl_event_loop_add_timer(g_pCompositor->m_wlEventLoop, handleCrashLoop, nullptr); - wl_event_source_timer_update(m_crashingLoop, 1000); + m_pCrashingLoop = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, handleCrashLoop, nullptr); + wl_event_source_timer_update(m_pCrashingLoop, 1000); - m_crashingInProgress = true; - m_crashingDistort = 0.5; + m_bCrashingInProgress = true; + m_fCrashingDistort = 0.5; - g_pHyprOpenGL->m_globalTimer.reset(); + g_pHyprOpenGL->m_tGlobalTimer.reset(); - static auto PDT = rc(g_pConfigManager->getConfigValuePtr("debug:damage_tracking")); + static auto PDT = (Hyprlang::INT* const*)(g_pConfigManager->getConfigValuePtr("debug:damage_tracking")); **PDT = 0; } -SP CHyprRenderer::getOrCreateRenderbuffer(SP buffer, uint32_t fmt) { - auto it = std::ranges::find_if(m_renderbuffers, [&](const auto& other) { return other->m_hlBuffer == buffer; }); +void CHyprRenderer::recheckSolitaryForMonitor(PHLMONITOR pMonitor) { + pMonitor->solitaryClient.reset(); // reset it, if we find one it will be set. - if (it != m_renderbuffers.end()) + if (g_pHyprNotificationOverlay->hasAny() || g_pSessionLockManager->isSessionLocked()) + return; + + const auto PWORKSPACE = pMonitor->activeWorkspace; + + if (!PWORKSPACE || !PWORKSPACE->m_bHasFullscreenWindow || PROTO::data->dndActive() || pMonitor->activeSpecialWorkspace || PWORKSPACE->m_fAlpha->value() != 1.f || + PWORKSPACE->m_vRenderOffset->value() != Vector2D{}) + return; + + const auto PCANDIDATE = PWORKSPACE->getFullscreenWindow(); + + if (!PCANDIDATE) + return; // ???? + + if (!PCANDIDATE->opaque()) + return; + + if (PCANDIDATE->m_vRealSize->value() != pMonitor->vecSize || PCANDIDATE->m_vRealPosition->value() != pMonitor->vecPosition || PCANDIDATE->m_vRealPosition->isBeingAnimated() || + PCANDIDATE->m_vRealSize->isBeingAnimated()) + return; + + if (!pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY].empty()) + return; + + for (auto const& topls : pMonitor->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { + if (topls->alpha->value() != 0.f) + return; + } + + for (auto const& w : g_pCompositor->m_vWindows) { + if (w == PCANDIDATE || (!w->m_bIsMapped && !w->m_bFadingOut) || w->isHidden()) + continue; + + if (w->m_pWorkspace == PCANDIDATE->m_pWorkspace && w->m_bIsFloating && w->m_bCreatedOverFullscreen && w->visibleOnMonitor(pMonitor)) + return; + } + + if (pMonitor->activeSpecialWorkspace) + return; + + // check if it did not open any subsurfaces or shit + int surfaceCount = 0; + if (PCANDIDATE->m_bIsX11) + surfaceCount = 1; + else + surfaceCount = PCANDIDATE->popupsCount() + PCANDIDATE->surfacesCount(); + + if (surfaceCount > 1) + return; + + // found one! + pMonitor->solitaryClient = PCANDIDATE; +} + +SP CHyprRenderer::getOrCreateRenderbuffer(SP buffer, uint32_t fmt) { + auto it = std::find_if(m_vRenderbuffers.begin(), m_vRenderbuffers.end(), [&](const auto& other) { return other->m_pHLBuffer == buffer; }); + + if (it != m_vRenderbuffers.end()) return *it; - auto buf = makeShared(buffer, fmt); + auto buf = makeShared(buffer, fmt); if (!buf->good()) return nullptr; - m_renderbuffers.emplace_back(buf); + m_vRenderbuffers.emplace_back(buf); return buf; } @@ -2376,26 +2155,26 @@ void CHyprRenderer::makeEGLCurrent() { if (!g_pCompositor || !g_pHyprOpenGL) return; - if (eglGetCurrentContext() != g_pHyprOpenGL->m_eglContext) - eglMakeCurrent(g_pHyprOpenGL->m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, g_pHyprOpenGL->m_eglContext); + if (eglGetCurrentContext() != g_pHyprOpenGL->m_pEglContext) + eglMakeCurrent(g_pHyprOpenGL->m_pEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, g_pHyprOpenGL->m_pEglContext); } void CHyprRenderer::unsetEGL() { if (!g_pHyprOpenGL) return; - eglMakeCurrent(g_pHyprOpenGL->m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(g_pHyprOpenGL->m_pEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } -bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode, SP buffer, SP fb, bool simple) { +bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode, SP buffer, CFramebuffer* fb, bool simple) { makeEGLCurrent(); - m_renderPass.clear(); + m_sRenderPass.clear(); - m_renderMode = mode; + m_eRenderMode = mode; - g_pHyprOpenGL->m_renderData.pMonitor = pMonitor; // has to be set cuz allocs + g_pHyprOpenGL->m_RenderData.pMonitor = pMonitor; // has to be set cuz allocs if (mode == RENDER_MODE_FULL_FAKE) { RASSERT(fb, "Cannot render FULL_FAKE without a provided fb!"); @@ -2407,181 +2186,259 @@ bool CHyprRenderer::beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMod return true; } - int bufferAge = 0; + /* This is a constant expression, as we always use double-buffering in our swapchain + TODO: Rewrite the CDamageRing to take advantage of that maybe? It's made to support longer swapchains atm because we used to do wlroots */ + static constexpr const int HL_BUFFER_AGE = 2; if (!buffer) { - m_currentBuffer = pMonitor->m_output->swapchain->next(&bufferAge); - if (!m_currentBuffer) { - Log::logger->log(Log::ERR, "Failed to acquire swapchain buffer for {}", pMonitor->m_name); + m_pCurrentBuffer = pMonitor->output->swapchain->next(nullptr); + if (!m_pCurrentBuffer) { + Debug::log(ERR, "Failed to acquire swapchain buffer for {}", pMonitor->szName); return false; } } else - m_currentBuffer = buffer; + m_pCurrentBuffer = buffer; try { - m_currentRenderbuffer = getOrCreateRenderbuffer(m_currentBuffer, pMonitor->m_output->state->state().drmFormat); + m_pCurrentRenderbuffer = getOrCreateRenderbuffer(m_pCurrentBuffer, pMonitor->output->state->state().drmFormat); } catch (std::exception& e) { - Log::logger->log(Log::ERR, "getOrCreateRenderbuffer failed for {}", pMonitor->m_name); + Debug::log(ERR, "getOrCreateRenderbuffer failed for {}", pMonitor->szName); return false; } - if (!m_currentRenderbuffer) { - Log::logger->log(Log::ERR, "failed to start a render pass for output {}, no RBO could be obtained", pMonitor->m_name); + if (!m_pCurrentRenderbuffer) { + Debug::log(ERR, "failed to start a render pass for output {}, no RBO could be obtained", pMonitor->szName); return false; } if (mode == RENDER_MODE_NORMAL) { - damage = pMonitor->m_damage.getBufferDamage(bufferAge); - pMonitor->m_damage.rotate(); + damage = pMonitor->damage.getBufferDamage(HL_BUFFER_AGE); + pMonitor->damage.rotate(); } - m_currentRenderbuffer->bind(); + m_pCurrentRenderbuffer->bind(); if (simple) - g_pHyprOpenGL->beginSimple(pMonitor, damage, m_currentRenderbuffer); + g_pHyprOpenGL->beginSimple(pMonitor, damage, m_pCurrentRenderbuffer); else g_pHyprOpenGL->begin(pMonitor, damage); return true; } -void CHyprRenderer::endRender(const std::function& renderingDoneCallback) { - const auto PMONITOR = g_pHyprOpenGL->m_renderData.pMonitor; +void CHyprRenderer::endRender() { + const auto PMONITOR = g_pHyprOpenGL->m_RenderData.pMonitor; static auto PNVIDIAANTIFLICKER = CConfigValue("opengl:nvidia_anti_flicker"); - g_pHyprOpenGL->m_renderData.damage = m_renderPass.render(g_pHyprOpenGL->m_renderData.damage); + g_pHyprOpenGL->m_RenderData.damage = m_sRenderPass.render(g_pHyprOpenGL->m_RenderData.damage); auto cleanup = CScopeGuard([this]() { - if (m_currentRenderbuffer) - m_currentRenderbuffer->unbind(); - m_currentRenderbuffer = nullptr; - m_currentBuffer = nullptr; + if (m_pCurrentRenderbuffer) + m_pCurrentRenderbuffer->unbind(); + m_pCurrentRenderbuffer = nullptr; + m_pCurrentBuffer = nullptr; }); - if (m_renderMode != RENDER_MODE_TO_BUFFER_READ_ONLY) + if (m_eRenderMode != RENDER_MODE_TO_BUFFER_READ_ONLY) g_pHyprOpenGL->end(); else { - g_pHyprOpenGL->m_renderData.pMonitor.reset(); - g_pHyprOpenGL->m_renderData.mouseZoomFactor = 1.f; - g_pHyprOpenGL->m_renderData.mouseZoomUseMouse = true; + g_pHyprOpenGL->m_RenderData.pMonitor.reset(); + g_pHyprOpenGL->m_RenderData.mouseZoomFactor = 1.f; + g_pHyprOpenGL->m_RenderData.mouseZoomUseMouse = true; } - if (m_renderMode == RENDER_MODE_FULL_FAKE) + if (m_eRenderMode == RENDER_MODE_FULL_FAKE) return; - if (m_renderMode == RENDER_MODE_NORMAL) - PMONITOR->m_output->state->setBuffer(m_currentBuffer); + if (m_eRenderMode == RENDER_MODE_NORMAL) + PMONITOR->output->state->setBuffer(m_pCurrentBuffer); - if (!g_pHyprOpenGL->explicitSyncSupported()) { - Log::logger->log(Log::TRACE, "renderer: Explicit sync unsupported, falling back to implicit in endRender"); + auto explicitOptions = getExplicitSyncSettings(PMONITOR->output); - // nvidia doesn't have implicit sync, so we have to explicitly wait here, llvmpipe and other software renderer seems to bug out aswell. - if ((isNvidia() && *PNVIDIAANTIFLICKER) || isSoftware()) - glFinish(); - else - glFlush(); // mark an implicit sync point - - m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works - if (renderingDoneCallback) - renderingDoneCallback(); - - return; - } - - UP eglSync = CEGLSync::create(); - if LIKELY (eglSync && eglSync->isValid()) { - for (auto const& buf : m_usedAsyncBuffers) { - for (const auto& releaser : buf->m_syncReleasers) { - releaser->addSyncFileFd(eglSync->fd()); - } + if (PMONITOR->inTimeline && explicitOptions.explicitEnabled) { + PMONITOR->eglSync = g_pHyprOpenGL->createEGLSync(); + if (!PMONITOR->eglSync) { + Debug::log(ERR, "renderer: couldn't create an EGLSync for out in endRender"); + return; } - // release buffer refs with release points now, since syncReleaser handles actual buffer release based on EGLSync - std::erase_if(m_usedAsyncBuffers, [](const auto& buf) { return !buf->m_syncReleasers.empty(); }); + PMONITOR->inTimelinePoint++; + bool ok = PMONITOR->inTimeline->importFromSyncFileFD(PMONITOR->inTimelinePoint, PMONITOR->eglSync->fd()); + if (!ok) { + Debug::log(ERR, "renderer: couldn't import from sync file fd in endRender"); + return; + } - // release buffer refs without release points when EGLSync sync_file/fence is signalled - g_pEventLoopManager->doOnReadable(eglSync->fd().duplicate(), [renderingDoneCallback, prevbfs = std::move(m_usedAsyncBuffers)]() mutable { - prevbfs.clear(); - if (renderingDoneCallback) - renderingDoneCallback(); - }); - m_usedAsyncBuffers.clear(); + if (m_eRenderMode == RENDER_MODE_NORMAL && explicitOptions.explicitKMSEnabled) { + PMONITOR->inFence = CFileDescriptor{PMONITOR->inTimeline->exportAsSyncFileFD(PMONITOR->inTimelinePoint)}; + if (!PMONITOR->inFence.isValid()) { + Debug::log(ERR, "renderer: couldn't export from sync timeline in endRender"); + return; + } - if (m_renderMode == RENDER_MODE_NORMAL) { - PMONITOR->m_inFence = eglSync->takeFd(); - PMONITOR->m_output->state->setExplicitInFence(PMONITOR->m_inFence.get()); + PMONITOR->output->state->setExplicitInFence(PMONITOR->inFence.get()); } } else { - Log::logger->log(Log::ERR, "renderer: Explicit sync failed, releasing resources"); - - m_usedAsyncBuffers.clear(); // release all buffer refs and hope implicit sync works - if (renderingDoneCallback) - renderingDoneCallback(); + if (isNvidia() && *PNVIDIAANTIFLICKER) + glFinish(); + else + glFlush(); } } -void CHyprRenderer::onRenderbufferDestroy(CGLRenderbuffer* rb) { - std::erase_if(m_renderbuffers, [&](const auto& rbo) { return rbo.get() == rb; }); +void CHyprRenderer::onRenderbufferDestroy(CRenderbuffer* rb) { + std::erase_if(m_vRenderbuffers, [&](const auto& rbo) { return rbo.get() == rb; }); } -SP CHyprRenderer::getCurrentRBO() { - return m_currentRenderbuffer; +SP CHyprRenderer::getCurrentRBO() { + return m_pCurrentRenderbuffer; } bool CHyprRenderer::isNvidia() { - return m_nvidia; + return m_bNvidia; } -bool CHyprRenderer::isIntel() { - return m_intel; -} +SExplicitSyncSettings CHyprRenderer::getExplicitSyncSettings(SP output) { + static auto PENABLEEXPLICIT = CConfigValue("render:explicit_sync"); + static auto PENABLEEXPLICITKMS = CConfigValue("render:explicit_sync_kms"); -bool CHyprRenderer::isSoftware() { - return m_software; -} + SExplicitSyncSettings settings; + settings.explicitEnabled = *PENABLEEXPLICIT; + settings.explicitKMSEnabled = *PENABLEEXPLICITKMS; -bool CHyprRenderer::isMgpu() { - return m_mgpu; + if (!output->supportsExplicit) { + settings.explicitEnabled = false; + settings.explicitKMSEnabled = false; + + return settings; + } + + if (*PENABLEEXPLICIT == 2 /* auto */) + settings.explicitEnabled = true; + if (*PENABLEEXPLICITKMS == 2 /* auto */) { + if (!m_bNvidia) + settings.explicitKMSEnabled = true; + else { + + // check nvidia version. Explicit KMS is supported in >=560 + // in the case of an error, driverMajor will stay 0 and explicit KMS will be disabled + static int driverMajor = 0; + + static bool once = true; + if (once) { + once = false; + + Debug::log(LOG, "Renderer: checking for explicit KMS support for nvidia"); + + if (std::filesystem::exists("/sys/module/nvidia_drm/version")) { + Debug::log(LOG, "Renderer: Nvidia version file exists"); + + std::ifstream ifs("/sys/module/nvidia_drm/version"); + if (ifs.good()) { + try { + std::string driverInfo((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); + + Debug::log(LOG, "Renderer: Read nvidia version {}", driverInfo); + + CVarList ver(driverInfo, 0, '.', true); + driverMajor = std::stoi(ver[0]); + + Debug::log(LOG, "Renderer: Parsed nvidia major version: {}", driverMajor); + + } catch (std::exception& e) { settings.explicitKMSEnabled = false; } + + ifs.close(); + } + } + } + + settings.explicitKMSEnabled = driverMajor >= 560; + } + } + + return settings; } void CHyprRenderer::addWindowToRenderUnfocused(PHLWINDOW window) { static auto PFPS = CConfigValue("misc:render_unfocused_fps"); - if (std::ranges::find(m_renderUnfocused, window) != m_renderUnfocused.end()) + if (std::find(m_vRenderUnfocused.begin(), m_vRenderUnfocused.end(), window) != m_vRenderUnfocused.end()) return; - m_renderUnfocused.emplace_back(window); + m_vRenderUnfocused.emplace_back(window); - if (!m_renderUnfocusedTimer->armed()) - m_renderUnfocusedTimer->updateTimeout(std::chrono::milliseconds(1000 / *PFPS)); + if (!m_tRenderUnfocusedTimer->armed()) + m_tRenderUnfocusedTimer->updateTimeout(std::chrono::milliseconds(1000 / *PFPS)); } -void CHyprRenderer::makeSnapshot(PHLWINDOW pWindow) { +void CHyprRenderer::makeRawWindowSnapshot(PHLWINDOW pWindow, CFramebuffer* pFramebuffer) { // we trust the window is valid. - const auto PMONITOR = pWindow->m_monitor.lock(); + const auto PMONITOR = pWindow->m_pMonitor.lock(); - if (!PMONITOR || !PMONITOR->m_output || PMONITOR->m_pixelSize.x <= 0 || PMONITOR->m_pixelSize.y <= 0) + if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0) + return; + + // we need to "damage" the entire monitor + // so that we render the entire window + // this is temporary, doesnt mess with the actual damage + CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y}; + + makeEGLCurrent(); + + pFramebuffer->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat); + pFramebuffer->addStencil(g_pHyprOpenGL->m_RenderData.pCurrentMonData->stencilTex); + + beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, pFramebuffer); + + g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + // this is a hack but it works :P + // we need to disable blur or else we will get a black background, as the shader + // will try to copy the bg to apply blur. + // this isn't entirely correct, but like, oh well. + // small todo: maybe make this correct? :P + static auto* const PBLUR = (Hyprlang::INT* const*)(g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")); + const auto BLURVAL = **PBLUR; + **PBLUR = 0; + + // TODO: how can we make this the size of the window? setting it to window's size makes the entire screen render with the wrong res forever more. odd. + glViewport(0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y); + + g_pHyprOpenGL->m_RenderData.currentFB = pFramebuffer; + + g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC + + renderWindow(pWindow, PMONITOR, &now, false, RENDER_PASS_ALL, true); + + **PBLUR = BLURVAL; + + endRender(); +} + +void CHyprRenderer::makeWindowSnapshot(PHLWINDOW pWindow) { + // we trust the window is valid. + const auto PMONITOR = pWindow->m_pMonitor.lock(); + + if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0) return; if (!shouldRenderWindow(pWindow)) return; // ignore, window is not being rendered - Log::logger->log(Log::DEBUG, "renderer: making a snapshot of {:x}", rc(pWindow.get())); - // we need to "damage" the entire monitor // so that we render the entire window - // this is temporary, doesn't mess with the actual damage - CRegion fakeDamage{0, 0, sc(PMONITOR->m_transformedSize.x), sc(PMONITOR->m_transformedSize.y)}; + // this is temporary, doesnt mess with the actual damage + CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y}; PHLWINDOWREF ref{pWindow}; makeEGLCurrent(); - if (!g_pHyprOpenGL->m_windowFramebuffers.contains(ref)) - g_pHyprOpenGL->m_windowFramebuffers[ref] = g_pHyprRenderer->createFB(); + const auto PFRAMEBUFFER = &g_pHyprOpenGL->m_mWindowFramebuffers[ref]; - const auto PFRAMEBUFFER = g_pHyprOpenGL->m_windowFramebuffers[ref]; - - PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); + PFRAMEBUFFER->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat); beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER); @@ -2589,35 +2446,46 @@ void CHyprRenderer::makeSnapshot(PHLWINDOW pWindow) { g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC - renderWindow(pWindow, PMONITOR, Time::steadyNow(), !pWindow->m_X11DoesntWantBorders, RENDER_PASS_ALL); + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + // this is a hack but it works :P + // we need to disable blur or else we will get a black background, as the shader + // will try to copy the bg to apply blur. + // this isn't entirely correct, but like, oh well. + // small todo: maybe make this correct? :P + static auto* const PBLUR = (Hyprlang::INT* const*)(g_pConfigManager->getConfigValuePtr("decoration:blur:enabled")); + const auto BLURVAL = **PBLUR; + **PBLUR = 0; + + g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC + + renderWindow(pWindow, PMONITOR, &now, !pWindow->m_bX11DoesntWantBorders, RENDER_PASS_ALL); + + **PBLUR = BLURVAL; endRender(); m_bRenderingSnapshot = false; } -void CHyprRenderer::makeSnapshot(PHLLS pLayer) { +void CHyprRenderer::makeLayerSnapshot(PHLLS pLayer) { // we trust the window is valid. - const auto PMONITOR = pLayer->m_monitor.lock(); + const auto PMONITOR = pLayer->monitor.lock(); - if (!PMONITOR || !PMONITOR->m_output || PMONITOR->m_pixelSize.x <= 0 || PMONITOR->m_pixelSize.y <= 0) + if (!PMONITOR || !PMONITOR->output || PMONITOR->vecPixelSize.x <= 0 || PMONITOR->vecPixelSize.y <= 0) return; - Log::logger->log(Log::DEBUG, "renderer: making a snapshot of {:x}", rc(pLayer.get())); - // we need to "damage" the entire monitor // so that we render the entire window - // this is temporary, doesn't mess with the actual damage - CRegion fakeDamage{0, 0, sc(PMONITOR->m_transformedSize.x), sc(PMONITOR->m_transformedSize.y)}; + // this is temporary, doesnt mess with the actual damage + CRegion fakeDamage{0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y}; makeEGLCurrent(); - if (!g_pHyprOpenGL->m_layerFramebuffers.contains(pLayer)) - g_pHyprOpenGL->m_layerFramebuffers[pLayer] = g_pHyprRenderer->createFB(); + const auto PFRAMEBUFFER = &g_pHyprOpenGL->m_mLayerFramebuffers[pLayer]; - const auto PFRAMEBUFFER = g_pHyprOpenGL->m_layerFramebuffers[pLayer]; - - PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); + PFRAMEBUFFER->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->output->state->state().drmFormat); beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER); @@ -2625,68 +2493,16 @@ void CHyprRenderer::makeSnapshot(PHLLS pLayer) { g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + const auto BLURLSSTATUS = pLayer->forceBlur; + pLayer->forceBlur = false; + // draw the layer - renderLayer(pLayer, PMONITOR, Time::steadyNow()); + renderLayer(pLayer, PMONITOR, &now); - endRender(); - - m_bRenderingSnapshot = false; -} - -void CHyprRenderer::makeSnapshot(WP popup) { - // we trust the window is valid. - const auto PMONITOR = popup->getMonitor(); - - if (!PMONITOR || !PMONITOR->m_output || PMONITOR->m_pixelSize.x <= 0 || PMONITOR->m_pixelSize.y <= 0) - return; - - if (!popup->aliveAndVisible()) - return; - - Log::logger->log(Log::DEBUG, "renderer: making a snapshot of {:x}", rc(popup.get())); - - CRegion fakeDamage{0, 0, PMONITOR->m_transformedSize.x, PMONITOR->m_transformedSize.y}; - - makeEGLCurrent(); - - if (!g_pHyprOpenGL->m_popupFramebuffers.contains(popup)) - g_pHyprOpenGL->m_popupFramebuffers[popup] = g_pHyprRenderer->createFB(); - - const auto PFRAMEBUFFER = g_pHyprOpenGL->m_popupFramebuffers[popup]; - - PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888); - - beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, PFRAMEBUFFER); - - m_bRenderingSnapshot = true; - - g_pHyprOpenGL->clear(CHyprColor(0, 0, 0, 0)); // JIC - - CSurfacePassElement::SRenderData renderdata; - renderdata.pos = popup->coordsGlobal(); - renderdata.alpha = 1.F; - renderdata.dontRound = true; // don't round popups - renderdata.pMonitor = PMONITOR; - renderdata.squishOversized = false; // don't squish popups - renderdata.popup = true; - renderdata.blur = false; - - popup->wlSurface()->resource()->breadthfirst( - [this, &renderdata](SP s, const Vector2D& offset, void* data) { - if (!s->m_current.texture) - return; - - if (s->m_current.size.x < 1 || s->m_current.size.y < 1) - return; - - renderdata.localPos = offset; - renderdata.texture = s->m_current.texture; - renderdata.surface = s; - renderdata.mainSurface = false; - m_renderPass.add(makeUnique(renderdata)); - renderdata.surfaceCounter++; - }, - nullptr); + pLayer->forceBlur = BLURLSSTATUS; endRender(); @@ -2698,257 +2514,80 @@ void CHyprRenderer::renderSnapshot(PHLWINDOW pWindow) { PHLWINDOWREF ref{pWindow}; - if (!g_pHyprOpenGL->m_windowFramebuffers.contains(ref)) + if (!g_pHyprOpenGL->m_mWindowFramebuffers.contains(ref)) return; - const auto FBDATA = g_pHyprOpenGL->m_windowFramebuffers.at(ref); + const auto FBDATA = &g_pHyprOpenGL->m_mWindowFramebuffers.at(ref); if (!FBDATA->getTexture()) return; - const auto PMONITOR = pWindow->m_monitor.lock(); + const auto PMONITOR = pWindow->m_pMonitor.lock(); CBox windowBox; // some mafs to figure out the correct box // the originalClosedPos is relative to the monitor's pos - Vector2D scaleXY = Vector2D((PMONITOR->m_scale * pWindow->m_realSize->value().x / (pWindow->m_originalClosedSize.x * PMONITOR->m_scale)), - (PMONITOR->m_scale * pWindow->m_realSize->value().y / (pWindow->m_originalClosedSize.y * PMONITOR->m_scale))); + Vector2D scaleXY = Vector2D((PMONITOR->scale * pWindow->m_vRealSize->value().x / (pWindow->m_vOriginalClosedSize.x * PMONITOR->scale)), + (PMONITOR->scale * pWindow->m_vRealSize->value().y / (pWindow->m_vOriginalClosedSize.y * PMONITOR->scale))); - windowBox.width = PMONITOR->m_transformedSize.x * scaleXY.x; - windowBox.height = PMONITOR->m_transformedSize.y * scaleXY.y; - windowBox.x = ((pWindow->m_realPosition->value().x - PMONITOR->m_position.x) * PMONITOR->m_scale) - ((pWindow->m_originalClosedPos.x * PMONITOR->m_scale) * scaleXY.x); - windowBox.y = ((pWindow->m_realPosition->value().y - PMONITOR->m_position.y) * PMONITOR->m_scale) - ((pWindow->m_originalClosedPos.y * PMONITOR->m_scale) * scaleXY.y); + windowBox.width = PMONITOR->vecTransformedSize.x * scaleXY.x; + windowBox.height = PMONITOR->vecTransformedSize.y * scaleXY.y; + windowBox.x = ((pWindow->m_vRealPosition->value().x - PMONITOR->vecPosition.x) * PMONITOR->scale) - ((pWindow->m_vOriginalClosedPos.x * PMONITOR->scale) * scaleXY.x); + windowBox.y = ((pWindow->m_vRealPosition->value().y - PMONITOR->vecPosition.y) * PMONITOR->scale) - ((pWindow->m_vOriginalClosedPos.y * PMONITOR->scale) * scaleXY.y); - CRegion fakeDamage{0, 0, PMONITOR->m_transformedSize.x, PMONITOR->m_transformedSize.y}; + CRegion fakeDamage{0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y}; + + if (*PDIMAROUND && pWindow->m_sWindowData.dimAround.valueOrDefault()) { - if (*PDIMAROUND && pWindow->m_ruleApplicator->dimAround().valueOrDefault()) { CRectPassElement::SRectData data; - data.box = {0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.y}; - data.color = CHyprColor(0, 0, 0, *PDIMAROUND * pWindow->m_alpha->value()); + data.box = {0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize.y}; + data.color = CHyprColor(0, 0, 0, *PDIMAROUND * pWindow->m_fAlpha->value()); - m_renderPass.add(makeUnique(data)); - } - - if (shouldBlur(pWindow)) { - CRectPassElement::SRectData data; - data.box = CBox{pWindow->m_realPosition->value(), pWindow->m_realSize->value()}.translate(-PMONITOR->m_position).scale(PMONITOR->m_scale).round(); - data.color = CHyprColor{0, 0, 0, 0}; - data.blur = true; - data.blurA = sqrt(pWindow->m_alpha->value()); // sqrt makes the blur fadeout more realistic. - data.round = pWindow->rounding(); - data.roundingPower = pWindow->roundingPower(); - data.xray = pWindow->m_ruleApplicator->xray().valueOr(false); - - m_renderPass.add(makeUnique(std::move(data))); + m_sRenderPass.add(makeShared(data)); + damageMonitor(PMONITOR); } CTexPassElement::SRenderData data; data.flipEndFrame = true; data.tex = FBDATA->getTexture(); data.box = windowBox; - data.a = pWindow->m_alpha->value(); + data.a = pWindow->m_fAlpha->value(); data.damage = fakeDamage; - m_renderPass.add(makeUnique(std::move(data))); + m_sRenderPass.add(makeShared(data)); } void CHyprRenderer::renderSnapshot(PHLLS pLayer) { - if (!g_pHyprOpenGL->m_layerFramebuffers.contains(pLayer)) + if (!g_pHyprOpenGL->m_mLayerFramebuffers.contains(pLayer)) return; - const auto FBDATA = g_pHyprOpenGL->m_layerFramebuffers.at(pLayer); + const auto FBDATA = &g_pHyprOpenGL->m_mLayerFramebuffers.at(pLayer); if (!FBDATA->getTexture()) return; - const auto PMONITOR = pLayer->m_monitor.lock(); + const auto PMONITOR = pLayer->monitor.lock(); CBox layerBox; // some mafs to figure out the correct box // the originalClosedPos is relative to the monitor's pos - Vector2D scaleXY = Vector2D((PMONITOR->m_scale * pLayer->m_realSize->value().x / (pLayer->m_geometry.w * PMONITOR->m_scale)), - (PMONITOR->m_scale * pLayer->m_realSize->value().y / (pLayer->m_geometry.h * PMONITOR->m_scale))); + Vector2D scaleXY = Vector2D((PMONITOR->scale * pLayer->realSize->value().x / (pLayer->geometry.w * PMONITOR->scale)), + (PMONITOR->scale * pLayer->realSize->value().y / (pLayer->geometry.h * PMONITOR->scale))); - layerBox.width = PMONITOR->m_transformedSize.x * scaleXY.x; - layerBox.height = PMONITOR->m_transformedSize.y * scaleXY.y; - layerBox.x = - ((pLayer->m_realPosition->value().x - PMONITOR->m_position.x) * PMONITOR->m_scale) - (((pLayer->m_geometry.x - PMONITOR->m_position.x) * PMONITOR->m_scale) * scaleXY.x); - layerBox.y = - ((pLayer->m_realPosition->value().y - PMONITOR->m_position.y) * PMONITOR->m_scale) - (((pLayer->m_geometry.y - PMONITOR->m_position.y) * PMONITOR->m_scale) * scaleXY.y); + layerBox.width = PMONITOR->vecTransformedSize.x * scaleXY.x; + layerBox.height = PMONITOR->vecTransformedSize.y * scaleXY.y; + layerBox.x = ((pLayer->realPosition->value().x - PMONITOR->vecPosition.x) * PMONITOR->scale) - (((pLayer->geometry.x - PMONITOR->vecPosition.x) * PMONITOR->scale) * scaleXY.x); + layerBox.y = ((pLayer->realPosition->value().y - PMONITOR->vecPosition.y) * PMONITOR->scale) - (((pLayer->geometry.y - PMONITOR->vecPosition.y) * PMONITOR->scale) * scaleXY.y); - CRegion fakeDamage{0, 0, PMONITOR->m_transformedSize.x, PMONITOR->m_transformedSize.y}; - - const bool SHOULD_BLUR = shouldBlur(pLayer); + CRegion fakeDamage{0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y}; CTexPassElement::SRenderData data; data.flipEndFrame = true; data.tex = FBDATA->getTexture(); data.box = layerBox; - data.a = pLayer->m_alpha->value(); + data.a = pLayer->alpha->value(); data.damage = fakeDamage; - data.blur = SHOULD_BLUR; - data.blurA = sqrt(pLayer->m_alpha->value()); // sqrt makes the blur fadeout more realistic. - if (SHOULD_BLUR) - data.ignoreAlpha = pLayer->m_ruleApplicator->ignoreAlpha().valueOr(0.01F) /* ignore the alpha 0 regions */; - m_renderPass.add(makeUnique(std::move(data))); + m_sRenderPass.add(makeShared(data)); } - -void CHyprRenderer::renderSnapshot(WP popup) { - if (!g_pHyprOpenGL->m_popupFramebuffers.contains(popup)) - return; - - static CConfigValue PBLURIGNOREA = CConfigValue("decoration:blur:popups_ignorealpha"); - - const auto FBDATA = g_pHyprOpenGL->m_popupFramebuffers.at(popup); - - if (!FBDATA->getTexture()) - return; - - const auto PMONITOR = popup->getMonitor(); - - if (!PMONITOR) - return; - - CRegion fakeDamage{0, 0, PMONITOR->m_transformedSize.x, PMONITOR->m_transformedSize.y}; - - const bool SHOULD_BLUR = shouldBlur(popup); - - CTexPassElement::SRenderData data; - data.flipEndFrame = true; - data.tex = FBDATA->getTexture(); - data.box = {{}, PMONITOR->m_transformedSize}; - data.a = popup->m_alpha->value(); - data.damage = fakeDamage; - data.blur = SHOULD_BLUR; - data.blurA = sqrt(popup->m_alpha->value()); // sqrt makes the blur fadeout more realistic. - data.blockBlurOptimization = SHOULD_BLUR; // force no xray on this (popups never have xray) - if (SHOULD_BLUR) - data.ignoreAlpha = std::max(*PBLURIGNOREA, 0.01F); /* ignore the alpha 0 regions */ - - m_renderPass.add(makeUnique(std::move(data))); -} - -NColorManagement::PImageDescription CHyprRenderer::workBufferImageDescription() { - const auto& m_renderData = g_pHyprOpenGL->m_renderData; - // TODO - // const bool IS_MONITOR_ICC = m_renderData.pMonitor->m_imageDescription.valid() && m_renderData.pMonitor->m_imageDescription->value().icc.present; - // const auto sdrEOTF = NTransferFunction::fromConfig(IS_MONITOR_ICC); - // const auto CHOSEN_SDR_EOTF = sdrEOTF != NTransferFunction::TF_SRGB ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB; - - return m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); -} - -bool CHyprRenderer::shouldBlur(PHLLS ls) { - if (m_bRenderingSnapshot) - return false; - - static auto PBLUR = CConfigValue("decoration:blur:enabled"); - return *PBLUR && ls->m_ruleApplicator->blur().valueOrDefault(); -} - -bool CHyprRenderer::shouldBlur(PHLWINDOW w) { - if (m_bRenderingSnapshot) - return false; - - static auto PBLUR = CConfigValue("decoration:blur:enabled"); - const bool DONT_BLUR = w->m_ruleApplicator->noBlur().valueOrDefault() || w->m_ruleApplicator->RGBX().valueOrDefault() || w->opaque(); - return *PBLUR && !DONT_BLUR; -} - -bool CHyprRenderer::shouldBlur(WP p) { - static CConfigValue PBLURPOPUPS = CConfigValue("decoration:blur:popups"); - static CConfigValue PBLUR = CConfigValue("decoration:blur:enabled"); - - return *PBLURPOPUPS && *PBLUR; -} - -bool CHyprRenderer::reloadShaders(const std::string& path) { - return g_pHyprOpenGL->initShaders(path); -} - -SP CHyprRenderer::createStencilTexture(const int width, const int height) { - makeEGLCurrent(); - auto tex = makeShared(); - tex->allocate({width, height}); - - return tex; -} - -SP CHyprRenderer::createTexture(bool opaque) { - makeEGLCurrent(); - return makeShared(opaque); -} - -SP CHyprRenderer::createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) { - makeEGLCurrent(); - return makeShared(drmFormat, pixels, stride, size, keepDataCopy, opaque); -} - -SP CHyprRenderer::createTexture(const Aquamarine::SDMABUFAttrs& attrs, bool opaque) { - makeEGLCurrent(); - const auto image = g_pHyprOpenGL->createEGLImage(attrs); - if (!image) - return nullptr; - return makeShared(attrs, image, opaque); -} - -SP CHyprRenderer::createTexture(const int width, const int height, unsigned char* const data) { - makeEGLCurrent(); - SP tex = makeShared(); - - tex->allocate({width, height}); - - tex->m_size = {width, height}; - // copy the data to an OpenGL texture we have - const GLint glFormat = GL_RGBA; - const GLint glType = GL_UNSIGNED_BYTE; - - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - - glTexImage2D(GL_TEXTURE_2D, 0, glFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, data); - tex->unbind(); - - return tex; -} - -SP CHyprRenderer::createTexture(cairo_surface_t* cairo) { - makeEGLCurrent(); - const auto CAIROFORMAT = cairo_image_surface_get_format(cairo); - auto tex = makeShared(); - - tex->allocate({cairo_image_surface_get_width(cairo), cairo_image_surface_get_height(cairo)}); - - const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA; - const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA; - const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; - - const auto DATA = cairo_image_surface_get_data(cairo); - tex->bind(); - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) { - tex->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE); - tex->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED); - } - - glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_size.x, tex->m_size.y, 0, glFormat, glType, DATA); - - return tex; -} - -SP CHyprRenderer::createTexture(std::span lut3D, size_t N) { - makeEGLCurrent(); - return makeShared(lut3D, N); -} - -SP CHyprRenderer::createFB(const std::string& name) { - makeEGLCurrent(); - return makeShared(name); -} \ No newline at end of file diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index bd14c219..731ed926 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -1,41 +1,21 @@ #pragma once #include "../defines.hpp" -#include -#include #include -#include #include "../helpers/Monitor.hpp" -#include "../desktop/view/LayerSurface.hpp" +#include "../desktop/LayerSurface.hpp" #include "OpenGL.hpp" #include "Renderbuffer.hpp" -#include "../helpers/time/Timer.hpp" +#include "../helpers/Timer.hpp" #include "../helpers/math/Math.hpp" -#include "../helpers/time/Time.hpp" -#include "../../protocols/cursor-shape-v1.hpp" -#include "render/Framebuffer.hpp" -#include "render/Texture.hpp" struct SMonitorRule; class CWorkspace; +class CWindow; class CInputPopup; class IHLBuffer; class CEventLoopTimer; -const std::vector ASSET_PATHS = { -#ifdef DATAROOTDIR - DATAROOTDIR, -#endif - "/usr/share", - "/usr/local/share", -}; -class CToplevelExportProtocolManager; -class CInputManager; -struct SSessionLockSurface; -namespace Screenshare { - class CScreenshareFrame; -}; - enum eDamageTrackingModes : int8_t { DAMAGE_TRACKING_INVALID = -1, DAMAGE_TRACKING_NONE = 0, @@ -56,32 +36,12 @@ enum eRenderMode : uint8_t { RENDER_MODE_TO_BUFFER_READ_ONLY = 3, }; -struct SRenderWorkspaceUntilData { - PHLLS ls; - PHLWINDOW w; -}; +class CToplevelExportProtocolManager; +class CInputManager; +struct SSessionLockSurface; -struct STFRange { - float min = 0; - float max = 80; -}; - -struct SCMSettings { - NColorManagement::eTransferFunction sourceTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; - NColorManagement::eTransferFunction targetTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; - STFRange srcTFRange; - STFRange dstTFRange; - float srcRefLuminance = 80; - float dstRefLuminance = 80; - std::array, 3> convertMatrix; - - bool needsTonemap = false; - float maxLuminance = 80; - float dstMaxLuminance = 80; - std::array, 3> dstPrimaries2XYZ; - bool needsSDRmod = false; - float sdrSaturation = 1.0; - float sdrBrightnessMultiplier = 1.0; +struct SExplicitSyncSettings { + bool explicitEnabled = false, explicitKMSEnabled = false; }; class CHyprRenderer { @@ -89,7 +49,7 @@ class CHyprRenderer { CHyprRenderer(); ~CHyprRenderer(); - void renderMonitor(PHLMONITOR pMonitor, bool commit = true); + void renderMonitor(PHLMONITOR pMonitor); void arrangeLayersForMonitor(const MONITORID&); void damageSurface(SP, double, double, double scale = 1.0); void damageWindow(PHLWINDOW, bool forceFull = false); @@ -106,126 +66,93 @@ class CHyprRenderer { void calculateUVForSurface(PHLWINDOW, SP, PHLMONITOR pMonitor, bool main = false, const Vector2D& projSize = {}, const Vector2D& projSizeUnscaled = {}, bool fixMisalignedFSV1 = false); std::tuple getRenderTimes(PHLMONITOR pMonitor); // avg max min - void renderLockscreen(PHLMONITOR pMonitor, const Time::steady_tp& now, const CBox& geometry); - void setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force = false); + void renderLockscreen(PHLMONITOR pMonitor, timespec* now, const CBox& geometry); + void recheckSolitaryForMonitor(PHLMONITOR pMonitor); + void setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force = false); void setCursorFromName(const std::string& name, bool force = false); - void onRenderbufferDestroy(CGLRenderbuffer* rb); - SP getCurrentRBO(); + void onRenderbufferDestroy(CRenderbuffer* rb); + SP getCurrentRBO(); bool isNvidia(); - bool isIntel(); - bool isSoftware(); - bool isMgpu(); void makeEGLCurrent(); void unsetEGL(); + SExplicitSyncSettings getExplicitSyncSettings(SP output); void addWindowToRenderUnfocused(PHLWINDOW window); - void makeSnapshot(PHLWINDOW); - void makeSnapshot(PHLLS); - void makeSnapshot(WP); + void makeWindowSnapshot(PHLWINDOW); + void makeRawWindowSnapshot(PHLWINDOW, CFramebuffer*); + void makeLayerSnapshot(PHLLS); void renderSnapshot(PHLWINDOW); void renderSnapshot(PHLLS); - void renderSnapshot(WP); - - // - NColorManagement::PImageDescription workBufferImageDescription(); // if RENDER_MODE_NORMAL, provided damage will be written to. // otherwise, it will be the one used. - bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP buffer = {}, SP fb = nullptr, bool simple = false); - void endRender(const std::function& renderingDoneCallback = {}); + bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP buffer = {}, CFramebuffer* fb = nullptr, bool simple = false); + void endRender(); bool m_bBlockSurfaceFeedback = false; bool m_bRenderingSnapshot = false; - PHLMONITORREF m_mostHzMonitor; - bool m_directScanoutBlocked = false; + PHLMONITORREF m_pMostHzMonitor; + bool m_bDirectScanoutBlocked = false; - void setSurfaceScanoutMode(SP surface, PHLMONITOR monitor); // nullptr monitor resets - void initiateManualCrash(); + void setSurfaceScanoutMode(SP surface, PHLMONITOR monitor); // nullptr monitor resets + void initiateManualCrash(); - bool m_crashingInProgress = false; - float m_crashingDistort = 0.5f; - wl_event_source* m_crashingLoop = nullptr; - wl_event_source* m_cursorTicker = nullptr; + bool m_bCrashingInProgress = false; + float m_fCrashingDistort = 0.5f; + wl_event_source* m_pCrashingLoop = nullptr; + wl_event_source* m_pCursorTicker = nullptr; - std::vector m_usedAsyncBuffers; + CTimer m_tRenderTimer; + + std::vector> explicitPresented; struct { - int hotspotX = 0; - int hotspotY = 0; - wpCursorShapeDeviceV1Shape shape = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; - wpCursorShapeDeviceV1Shape shapePrevious = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; - CTimer switchedTimer; - std::optional> surf; - std::string name; - } m_lastCursorData; + int hotspotX = 0; + int hotspotY = 0; + std::optional> surf; + std::string name; + } m_sLastCursorData; - CRenderPass m_renderPass = {}; - - SP createStencilTexture(const int width, const int height); - SP createTexture(bool opaque = false); - SP createTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - SP createTexture(const Aquamarine::SDMABUFAttrs&, bool opaque = false); - SP createTexture(const int width, const int height, unsigned char* const); - SP createTexture(cairo_surface_t* cairo); - SP createTexture(const SP buffer, bool keepDataCopy = false); - SP createTexture(std::span lut3D, size_t N); - SP createFB(const std::string& name = ""); - - SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, - SP surface = nullptr, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); - bool reloadShaders(const std::string& path = ""); + CRenderPass m_sRenderPass = {}; private: - void arrangeLayerArray(PHLMONITOR, const std::vector&, bool, CBox*); - void renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry); - void renderWorkspaceWindowsFullscreen(PHLMONITOR, PHLWORKSPACE, const Time::steady_tp&); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special) - void renderWorkspaceWindows(PHLMONITOR, PHLWORKSPACE, const Time::steady_tp&); // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special) - void renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const Vector2D& translate = {0, 0}, const float& scale = 1.f); - void renderWindow(PHLWINDOW, PHLMONITOR, const Time::steady_tp&, bool, eRenderPassMode, bool ignorePosition = false, bool standalone = false); - void renderLayer(PHLLS, PHLMONITOR, const Time::steady_tp&, bool popups = false, bool lockscreen = false); - void renderSessionLockSurface(WP, PHLMONITOR, const Time::steady_tp&); - void renderDragIcon(PHLMONITOR, const Time::steady_tp&); - void renderIMEPopup(CInputPopup*, PHLMONITOR, const Time::steady_tp&); - void sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now); // sends frame displayed events but doesn't actually render anything - void renderSessionLockPrimer(PHLMONITOR pMonitor); - void renderSessionLockMissing(PHLMONITOR pMonitor); - void renderBackground(PHLMONITOR pMonitor); + void arrangeLayerArray(PHLMONITOR, const std::vector&, bool, CBox*); + void renderWorkspaceWindowsFullscreen(PHLMONITOR, PHLWORKSPACE, timespec*); // renders workspace windows (fullscreen) (tiled, floating, pinned, but no special) + void renderWorkspaceWindows(PHLMONITOR, PHLWORKSPACE, timespec*); // renders workspace windows (no fullscreen) (tiled, floating, pinned, but no special) + void renderWindow(PHLWINDOW, PHLMONITOR, timespec*, bool, eRenderPassMode, bool ignorePosition = false, bool standalone = false); + void renderLayer(PHLLS, PHLMONITOR, timespec*, bool popups = false); + void renderSessionLockSurface(WP, PHLMONITOR, timespec*); + void renderDragIcon(PHLMONITOR, timespec*); + void renderIMEPopup(CInputPopup*, PHLMONITOR, timespec*); + void renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* now, const CBox& geometry); + void sendFrameEventsToWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* now); // sends frame displayed events but doesn't actually render anything + void renderAllClientsForWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, timespec* now, const Vector2D& translate = {0, 0}, const float& scale = 1.f); + void renderSessionLockMissing(PHLMONITOR pMonitor); - bool commitPendingAndDoExplicitSync(PHLMONITOR pMonitor); + bool commitPendingAndDoExplicitSync(PHLMONITOR pMonitor); - bool shouldBlur(PHLLS ls); - bool shouldBlur(PHLWINDOW w); - bool shouldBlur(WP p); - - bool m_cursorHidden = false; - bool m_cursorHiddenByCondition = false; - bool m_cursorHasSurface = false; - SP m_currentRenderbuffer = nullptr; - SP m_currentBuffer = nullptr; - eRenderMode m_renderMode = RENDER_MODE_NORMAL; - bool m_nvidia = false; - bool m_intel = false; - bool m_software = false; - bool m_mgpu = false; + bool m_bCursorHidden = false; + bool m_bCursorHasSurface = false; + SP m_pCurrentRenderbuffer = nullptr; + SP m_pCurrentBuffer = nullptr; + eRenderMode m_eRenderMode = RENDER_MODE_NORMAL; + bool m_bNvidia = false; struct { bool hiddenOnTouch = false; - bool hiddenOnTablet = false; bool hiddenOnTimeout = false; bool hiddenOnKeyboard = false; - } m_cursorHiddenConditions; + } m_sCursorHiddenConditions; - SP getOrCreateRenderbuffer(SP buffer, uint32_t fmt); - std::vector> m_renderbuffers; - std::vector m_renderUnfocused; - SP m_renderUnfocusedTimer; + SP getOrCreateRenderbuffer(SP buffer, uint32_t fmt); + std::vector> m_vRenderbuffers; + std::vector m_vRenderUnfocused; + SP m_tRenderUnfocusedTimer; friend class CHyprOpenGLImpl; friend class CToplevelExportFrame; - friend class Screenshare::CScreenshareFrame; friend class CInputManager; friend class CPointerManager; friend class CMonitor; - friend class CMonitorFrameScheduler; }; inline UP g_pHyprRenderer; diff --git a/src/render/Shader.cpp b/src/render/Shader.cpp index ead841a5..984d8ce3 100644 --- a/src/render/Shader.cpp +++ b/src/render/Shader.cpp @@ -1,423 +1,26 @@ #include "Shader.hpp" -#include "../config/ConfigManager.hpp" -#include "render/OpenGL.hpp" -#define EPSILON(x, y) (std::abs((x) - (y)) < 1e-5f) +GLint CShader::getUniformLocation(const std::string& unif) { + const auto itpos = m_muUniforms.find(unif); -static bool compareFloat(auto a, auto b) { - if (a.size() != b.size()) - return false; + if (itpos == m_muUniforms.end()) { + const auto unifLoc = glGetUniformLocation(program, unif.c_str()); + m_muUniforms[unif] = unifLoc; + return unifLoc; + } - for (size_t i = 0; i < a.size(); ++i) - if (std::fabs(a[i] - b[i]) > 1e-5f) - return false; - - return true; -} - -CShader::CShader() { - m_uniformLocations.fill(-1); + return itpos->second; } CShader::~CShader() { destroy(); } -void CShader::logShaderError(const GLuint& shader, bool program, bool silent) { - GLint maxLength = 0; - if (program) - glGetProgramiv(shader, GL_INFO_LOG_LENGTH, &maxLength); - else - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); - - std::vector errorLog(maxLength); - if (program) - glGetProgramInfoLog(shader, maxLength, &maxLength, errorLog.data()); - else - glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data()); - std::string errorStr(errorLog.begin(), errorLog.end()); - - const auto FULLERROR = (program ? "Screen shader parser: Error linking program:" : "Screen shader parser: Error compiling shader: ") + errorStr; - - Log::logger->log(Log::ERR, "Failed to link shader: {}", FULLERROR); - - if (!silent) - g_pConfigManager->addParseError(FULLERROR); -} - -GLuint CShader::compileShader(const GLuint& type, std::string src, bool dynamic, bool silent) { - auto shader = glCreateShader(type); - - auto shaderSource = src.c_str(); - - glShaderSource(shader, 1, &shaderSource, nullptr); - glCompileShader(shader); - - GLint ok; - glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); - - if (dynamic) { - if (ok == GL_FALSE) { - logShaderError(shader, false, silent); - return 0; - } - } else { - if (ok != GL_TRUE) - logShaderError(shader, false); - RASSERT(ok != GL_FALSE, "compileShader() failed! GL_COMPILE_STATUS not OK!"); - } - - return shader; -} - -bool CShader::createProgram(const std::string& vert, const std::string& frag, bool dynamic, bool silent) { - auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert, dynamic, silent); - if (dynamic) { - if (vertCompiled == 0) - return false; - } else - RASSERT(vertCompiled, "Compiling shader failed. VERTEX nullptr! Shader source:\n\n{}", vert); - - auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag, dynamic, silent); - if (dynamic) { - if (fragCompiled == 0) - return false; - } else - RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT nullptr! Shader source:\n\n{}", frag); - - auto prog = glCreateProgram(); - glAttachShader(prog, vertCompiled); - glAttachShader(prog, fragCompiled); - glLinkProgram(prog); - - glDetachShader(prog, vertCompiled); - glDetachShader(prog, fragCompiled); - glDeleteShader(vertCompiled); - glDeleteShader(fragCompiled); - - GLint ok; - glGetProgramiv(prog, GL_LINK_STATUS, &ok); - if (dynamic) { - if (ok == GL_FALSE) { - logShaderError(prog, true, silent); - return false; - } - } else { - if (ok != GL_TRUE) - logShaderError(prog, true); - RASSERT(ok != GL_FALSE, "createProgram() failed! GL_LINK_STATUS not OK!"); - } - - m_program = prog; - - getUniformLocations(); - createVao(); - return true; -} - -// its fine to call glGet on shaders that dont have the uniform -// this however hardcodes the name now. #TODO maybe dont -void CShader::getUniformLocations() { - auto getUniform = [this](const GLchar* name) { return glGetUniformLocation(m_program, name); }; - auto getAttrib = [this](const GLchar* name) { return glGetAttribLocation(m_program, name); }; - - m_uniformLocations[SHADER_PROJ] = getUniform("proj"); - m_uniformLocations[SHADER_COLOR] = getUniform("color"); - m_uniformLocations[SHADER_ALPHA_MATTE] = getUniform("texMatte"); - m_uniformLocations[SHADER_TEX_TYPE] = getUniform("texType"); - - // shader has #include "CM.glsl" - m_uniformLocations[SHADER_SOURCE_TF] = getUniform("sourceTF"); - m_uniformLocations[SHADER_TARGET_TF] = getUniform("targetTF"); - m_uniformLocations[SHADER_SRC_TF_RANGE] = getUniform("srcTFRange"); - m_uniformLocations[SHADER_DST_TF_RANGE] = getUniform("dstTFRange"); - m_uniformLocations[SHADER_TARGET_PRIMARIES_XYZ] = getUniform("targetPrimariesXYZ"); - m_uniformLocations[SHADER_MAX_LUMINANCE] = getUniform("maxLuminance"); - m_uniformLocations[SHADER_SRC_REF_LUMINANCE] = getUniform("srcRefLuminance"); - m_uniformLocations[SHADER_DST_MAX_LUMINANCE] = getUniform("dstMaxLuminance"); - m_uniformLocations[SHADER_DST_REF_LUMINANCE] = getUniform("dstRefLuminance"); - m_uniformLocations[SHADER_SDR_SATURATION] = getUniform("sdrSaturation"); - m_uniformLocations[SHADER_SDR_BRIGHTNESS] = getUniform("sdrBrightnessMultiplier"); - m_uniformLocations[SHADER_CONVERT_MATRIX] = getUniform("convertMatrix"); - m_uniformLocations[SHADER_LUT_3D] = getUniform("iccLut3D"); - m_uniformLocations[SHADER_LUT_SIZE] = getUniform("iccLutSize"); - // - m_uniformLocations[SHADER_TEX] = getUniform("tex"); - m_uniformLocations[SHADER_BLURRED_BG] = getUniform("blurredBG"); - m_uniformLocations[SHADER_UV_SIZE] = getUniform("uvSize"); - m_uniformLocations[SHADER_UV_OFFSET] = getUniform("uvOffset"); - m_uniformLocations[SHADER_ALPHA] = getUniform("alpha"); - m_uniformLocations[SHADER_POS_ATTRIB] = getAttrib("pos"); - m_uniformLocations[SHADER_TEX_ATTRIB] = getAttrib("texcoord"); - m_uniformLocations[SHADER_MATTE_TEX_ATTRIB] = getAttrib("texcoordMatte"); - m_uniformLocations[SHADER_DISCARD_OPAQUE] = getUniform("discardOpaque"); - m_uniformLocations[SHADER_DISCARD_ALPHA] = getUniform("discardAlpha"); - m_uniformLocations[SHADER_DISCARD_ALPHA_VALUE] = getUniform("discardAlphaValue"); - /* set in createVao - m_uniformLocations[SHADER_SHADER_VAO] - m_uniformLocations[SHADER_SHADER_VBO_POS] - m_uniformLocations[SHADER_SHADER_VBO_UV] - */ - m_uniformLocations[SHADER_TOP_LEFT] = getUniform("topLeft"); - m_uniformLocations[SHADER_BOTTOM_RIGHT] = getUniform("bottomRight"); - - // compat for screenshaders - auto fullSize = getUniform("fullSize"); - if (fullSize == -1) - fullSize = getUniform("screen_size"); - if (fullSize == -1) - fullSize = getUniform("screenSize"); - m_uniformLocations[SHADER_FULL_SIZE] = fullSize; - - m_uniformLocations[SHADER_FULL_SIZE_UNTRANSFORMED] = getUniform("fullSizeUntransformed"); - m_uniformLocations[SHADER_RADIUS] = getUniform("radius"); - m_uniformLocations[SHADER_RADIUS_OUTER] = getUniform("radiusOuter"); - m_uniformLocations[SHADER_ROUNDING_POWER] = getUniform("roundingPower"); - m_uniformLocations[SHADER_THICK] = getUniform("thick"); - m_uniformLocations[SHADER_HALFPIXEL] = getUniform("halfpixel"); - m_uniformLocations[SHADER_RANGE] = getUniform("range"); - m_uniformLocations[SHADER_SHADOW_POWER] = getUniform("shadowPower"); - m_uniformLocations[SHADER_USE_ALPHA_MATTE] = getUniform("useAlphaMatte"); - m_uniformLocations[SHADER_APPLY_TINT] = getUniform("applyTint"); - m_uniformLocations[SHADER_TINT] = getUniform("tint"); - m_uniformLocations[SHADER_GRADIENT] = getUniform("gradient"); - m_uniformLocations[SHADER_GRADIENT_LENGTH] = getUniform("gradientLength"); - m_uniformLocations[SHADER_GRADIENT2] = getUniform("gradient2"); - m_uniformLocations[SHADER_GRADIENT2_LENGTH] = getUniform("gradient2Length"); - m_uniformLocations[SHADER_ANGLE] = getUniform("angle"); - m_uniformLocations[SHADER_ANGLE2] = getUniform("angle2"); - m_uniformLocations[SHADER_GRADIENT_LERP] = getUniform("gradientLerp"); - m_uniformLocations[SHADER_TIME] = getUniform("time"); - m_uniformLocations[SHADER_DISTORT] = getUniform("distort"); - m_uniformLocations[SHADER_WL_OUTPUT] = getUniform("wl_output"); - m_uniformLocations[SHADER_CONTRAST] = getUniform("contrast"); - m_uniformLocations[SHADER_PASSES] = getUniform("passes"); - m_uniformLocations[SHADER_VIBRANCY] = getUniform("vibrancy"); - m_uniformLocations[SHADER_VIBRANCY_DARKNESS] = getUniform("vibrancy_darkness"); - m_uniformLocations[SHADER_BRIGHTNESS] = getUniform("brightness"); - m_uniformLocations[SHADER_NOISE] = getUniform("noise"); - m_uniformLocations[SHADER_POINTER] = getUniform("pointer_position"); - m_uniformLocations[SHADER_POINTER_SHAPE] = getUniform("pointer_shape"); - m_uniformLocations[SHADER_POINTER_SWITCH_TIME] = getUniform("pointer_switch_time"); - m_uniformLocations[SHADER_POINTER_SHAPE_PREVIOUS] = getUniform("pointer_shape_previous"); - m_uniformLocations[SHADER_POINTER_PRESSED_POSITIONS] = getUniform("pointer_pressed_positions"); - m_uniformLocations[SHADER_POINTER_HIDDEN] = getUniform("pointer_hidden"); - m_uniformLocations[SHADER_POINTER_KILLING] = getUniform("pointer_killing"); - m_uniformLocations[SHADER_POINTER_PRESSED_TIMES] = getUniform("pointer_pressed_times"); - m_uniformLocations[SHADER_POINTER_PRESSED_KILLED] = getUniform("pointer_pressed_killed"); - m_uniformLocations[SHADER_POINTER_PRESSED_TOUCHED] = getUniform("pointer_pressed_touched"); - m_uniformLocations[SHADER_POINTER_INACTIVE_TIMEOUT] = getUniform("pointer_inactive_timeout"); - m_uniformLocations[SHADER_POINTER_LAST_ACTIVE] = getUniform("pointer_last_active"); - m_uniformLocations[SHADER_POINTER_SIZE] = getUniform("pointer_size"); -} - -void CShader::createVao() { - GLuint shaderVao = 0, shaderVbo = 0; - - glGenVertexArrays(1, &shaderVao); - glBindVertexArray(shaderVao); - - if (m_uniformLocations[SHADER_POS_ATTRIB] != -1) { - glGenBuffers(1, &shaderVbo); - glBindBuffer(GL_ARRAY_BUFFER, shaderVbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(fullVerts), fullVerts.data(), GL_DYNAMIC_DRAW); - glEnableVertexAttribArray(m_uniformLocations[SHADER_POS_ATTRIB]); - glVertexAttribPointer(m_uniformLocations[SHADER_POS_ATTRIB], 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (void*)offsetof(SVertex, x)); - } - - // UV VBO (dynamic, may be updated per frame) - if (m_uniformLocations[SHADER_TEX_ATTRIB] != -1 && shaderVbo != 0) { - glBindBuffer(GL_ARRAY_BUFFER, shaderVbo); - glEnableVertexAttribArray(m_uniformLocations[SHADER_TEX_ATTRIB]); - glVertexAttribPointer(m_uniformLocations[SHADER_TEX_ATTRIB], 2, GL_FLOAT, GL_FALSE, sizeof(SVertex), (void*)offsetof(SVertex, u)); - } - - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - m_uniformLocations[SHADER_SHADER_VAO] = shaderVao; - m_uniformLocations[SHADER_SHADER_VBO] = shaderVbo; - - RASSERT(m_uniformLocations[SHADER_SHADER_VAO] >= 0, "SHADER_SHADER_VAO could not be created"); - RASSERT(m_uniformLocations[SHADER_SHADER_VBO] >= 0, "SHADER_SHADER_VBO_POS could not be created"); -} - -void CShader::setUniformInt(eShaderUniform location, GLint v0) { - if (m_uniformLocations.at(location) == -1) - return; - - auto& cached = uniformStatus.at(location); - - if (cached.index() != 0 && std::get(cached) == v0) - return; - - cached = v0; - - GLCALL(glUniform1i(m_uniformLocations[location], v0)); -} - -void CShader::setUniformFloat(eShaderUniform location, GLfloat v0) { - if (m_uniformLocations.at(location) == -1) - return; - - auto& cached = uniformStatus.at(location); - - if (cached.index() != 0) { - auto val = std::get(cached); - if (EPSILON(val, v0)) - return; - } - - cached = v0; - GLCALL(glUniform1f(m_uniformLocations[location], v0)); -} - -void CShader::setUniformFloat2(eShaderUniform location, GLfloat v0, GLfloat v1) { - if (m_uniformLocations.at(location) == -1) - return; - - auto& cached = uniformStatus.at(location); - - if (cached.index() != 0) { - auto val = std::get>(cached); - if (EPSILON(val[0], v0) && EPSILON(val[1], v1)) - return; - } - - cached = std::array{v0, v1}; - GLCALL(glUniform2f(m_uniformLocations[location], v0, v1)); -} - -void CShader::setUniformFloat3(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2) { - if (m_uniformLocations.at(location) == -1) - return; - - auto& cached = uniformStatus.at(location); - - if (cached.index() != 0) { - auto val = std::get>(cached); - if (EPSILON(val[0], v0) && EPSILON(val[1], v1) && EPSILON(val[2], v2)) - return; - } - - cached = std::array{v0, v1, v2}; - GLCALL(glUniform3f(m_uniformLocations[location], v0, v1, v2)); -} - -void CShader::setUniformFloat4(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { - if (m_uniformLocations.at(location) == -1) - return; - - auto& cached = uniformStatus.at(location); - - if (cached.index() != 0) { - auto val = std::get>(cached); - if (EPSILON(val[0], v0) && EPSILON(val[1], v1) && EPSILON(val[2], v2) && EPSILON(val[3], v3)) - return; - } - - cached = std::array{v0, v1, v2, v3}; - GLCALL(glUniform4f(m_uniformLocations[location], v0, v1, v2, v3)); -} - -void CShader::setUniformMatrix3fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array value) { - if (m_uniformLocations.at(location) == -1) - return; - - auto& cached = uniformStatus.at(location); - - if (cached.index() != 0) { - auto val = std::get(cached); - if (val.count == count && val.transpose == transpose && compareFloat(val.value, value)) - return; - } - - cached = SUniformMatrix3Data{.count = count, .transpose = transpose, .value = value}; - GLCALL(glUniformMatrix3fv(m_uniformLocations[location], count, transpose, value.data())); -} - -void CShader::setUniformMatrix4x2fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array value) { - if (m_uniformLocations.at(location) == -1) - return; - - auto& cached = uniformStatus.at(location); - - if (cached.index() != 0) { - auto val = std::get(cached); - if (val.count == count && val.transpose == transpose && compareFloat(val.value, value)) - return; - } - - cached = SUniformMatrix4Data{.count = count, .transpose = transpose, .value = value}; - GLCALL(glUniformMatrix4x2fv(m_uniformLocations[location], count, transpose, value.data())); -} - -void CShader::setUniformfv(eShaderUniform location, GLsizei count, const std::vector& value, GLsizei vec_size) { - if (m_uniformLocations.at(location) == -1) - return; - - auto& cached = uniformStatus.at(location); - - if (cached.index() != 0) { - auto val = std::get(cached); - if (val.count == count && compareFloat(val.value, value)) - return; - } - - cached = SUniformVData{.count = count, .value = value}; - switch (vec_size) { - case 1: GLCALL(glUniform1fv(m_uniformLocations[location], count, value.data())); break; - case 2: GLCALL(glUniform2fv(m_uniformLocations[location], count, value.data())); break; - case 4: GLCALL(glUniform4fv(m_uniformLocations[location], count, value.data())); break; - default: UNREACHABLE(); - } -} - -void CShader::setUniform1fv(eShaderUniform location, GLsizei count, const std::vector& value) { - setUniformfv(location, count, value, 1); -} - -void CShader::setUniform2fv(eShaderUniform location, GLsizei count, const std::vector& value) { - setUniformfv(location, count, value, 2); -} - -void CShader::setUniform4fv(eShaderUniform location, GLsizei count, const std::vector& value) { - setUniformfv(location, count, value, 4); -} - void CShader::destroy() { - uniformStatus.fill(std::monostate()); - - if (m_program == 0) + if (program == 0) return; - GLuint shaderVao, shaderVbo; + glDeleteProgram(program); - shaderVao = m_uniformLocations[SHADER_SHADER_VAO] == -1 ? 0 : m_uniformLocations[SHADER_SHADER_VAO]; - shaderVbo = m_uniformLocations[SHADER_SHADER_VBO] == -1 ? 0 : m_uniformLocations[SHADER_SHADER_VBO]; - - if (shaderVao) - glDeleteVertexArrays(1, &shaderVao); - - if (shaderVbo) - glDeleteBuffers(1, &shaderVbo); - - glDeleteProgram(m_program); - m_program = 0; -} - -GLint CShader::getUniformLocation(eShaderUniform location) const { - return m_uniformLocations[location]; -} - -GLuint CShader::program() const { - return m_program; -} - -int CShader::getInitialTime() const { - return m_initialTime; -} - -void CShader::setInitialTime(int time) { - m_initialTime = time; + program = 0; } diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index 9b097c44..413f3ef0 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -1,142 +1,83 @@ #pragma once #include "../defines.hpp" -#include -#include - -enum eShaderUniform : uint8_t { - SHADER_PROJ = 0, - SHADER_COLOR, - SHADER_ALPHA_MATTE, - SHADER_TEX_TYPE, - SHADER_SOURCE_TF, - SHADER_TARGET_TF, - SHADER_SRC_TF_RANGE, - SHADER_DST_TF_RANGE, - SHADER_TARGET_PRIMARIES_XYZ, - SHADER_MAX_LUMINANCE, - SHADER_SRC_REF_LUMINANCE, - SHADER_DST_MAX_LUMINANCE, - SHADER_DST_REF_LUMINANCE, - SHADER_SDR_SATURATION, - SHADER_SDR_BRIGHTNESS, - SHADER_CONVERT_MATRIX, - SHADER_TEX, - SHADER_ALPHA, - SHADER_POS_ATTRIB, - SHADER_TEX_ATTRIB, - SHADER_MATTE_TEX_ATTRIB, - SHADER_DISCARD_OPAQUE, - SHADER_DISCARD_ALPHA, - SHADER_DISCARD_ALPHA_VALUE, - SHADER_SHADER_VAO, - SHADER_SHADER_VBO, - SHADER_TOP_LEFT, - SHADER_BOTTOM_RIGHT, - SHADER_FULL_SIZE, - SHADER_FULL_SIZE_UNTRANSFORMED, - SHADER_RADIUS, - SHADER_RADIUS_OUTER, - SHADER_ROUNDING_POWER, - SHADER_THICK, - SHADER_HALFPIXEL, - SHADER_RANGE, - SHADER_SHADOW_POWER, - SHADER_USE_ALPHA_MATTE, - SHADER_APPLY_TINT, - SHADER_TINT, - SHADER_GRADIENT, - SHADER_GRADIENT_LENGTH, - SHADER_ANGLE, - SHADER_GRADIENT2, - SHADER_GRADIENT2_LENGTH, - SHADER_ANGLE2, - SHADER_GRADIENT_LERP, - SHADER_TIME, - SHADER_DISTORT, - SHADER_WL_OUTPUT, - SHADER_CONTRAST, - SHADER_PASSES, - SHADER_VIBRANCY, - SHADER_VIBRANCY_DARKNESS, - SHADER_BRIGHTNESS, - SHADER_NOISE, - SHADER_POINTER, - SHADER_POINTER_SHAPE, - SHADER_POINTER_SWITCH_TIME, - SHADER_POINTER_SHAPE_PREVIOUS, - SHADER_POINTER_PRESSED_POSITIONS, - SHADER_POINTER_HIDDEN, - SHADER_POINTER_KILLING, - SHADER_POINTER_PRESSED_TIMES, - SHADER_POINTER_PRESSED_KILLED, - SHADER_POINTER_PRESSED_TOUCHED, - SHADER_POINTER_INACTIVE_TIMEOUT, - SHADER_POINTER_LAST_ACTIVE, - SHADER_POINTER_SIZE, - SHADER_LUT_3D, - SHADER_LUT_SIZE, - SHADER_BLURRED_BG, - SHADER_UV_SIZE, - SHADER_UV_OFFSET, - - SHADER_LAST, -}; +#include class CShader { public: - CShader(); ~CShader(); - bool createProgram(const std::string& vert, const std::string& frag, bool dynamic = false, bool silent = false); - void setUniformInt(eShaderUniform location, GLint v0); - void setUniformFloat(eShaderUniform location, GLfloat v0); - void setUniformFloat2(eShaderUniform location, GLfloat v0, GLfloat v1); - void setUniformFloat3(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2); - void setUniformFloat4(eShaderUniform location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); - void setUniformMatrix3fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array value); - void setUniformMatrix4x2fv(eShaderUniform location, GLsizei count, GLboolean transpose, std::array value); - void setUniform1fv(eShaderUniform location, GLsizei count, const std::vector& value); - void setUniform2fv(eShaderUniform location, GLsizei count, const std::vector& value); - void setUniform4fv(eShaderUniform location, GLsizei count, const std::vector& value); - void destroy(); - GLuint program() const; - GLint getUniformLocation(eShaderUniform location) const; - int getInitialTime() const; - void setInitialTime(int time); + GLuint program = 0; + GLint proj = -1; + GLint color = -1; + GLint alphaMatte = -1; + GLint texType = -1; + GLint sourceTF = -1; + GLint targetTF = -1; + GLint sourcePrimaries = -1; + GLint targetPrimaries = -1; + GLint maxLuminance = -1; + GLint dstMaxLuminance = -1; + GLint dstRefLuminance = -1; + GLint sdrSaturation = -1; // sdr -> hdr saturation + GLint sdrBrightness = -1; // sdr -> hdr brightness multiplier + GLint tex = -1; + GLint alpha = -1; + GLint posAttrib = -1; + GLint texAttrib = -1; + GLint matteTexAttrib = -1; + GLint discardOpaque = -1; + GLint discardAlpha = -1; + GLfloat discardAlphaValue = -1; + + GLint topLeft = -1; + GLint bottomRight = -1; + GLint fullSize = -1; + GLint fullSizeUntransformed = -1; + GLint radius = -1; + GLint radiusOuter = -1; + GLfloat roundingPower = -1; + + GLint thick = -1; + + GLint halfpixel = -1; + + GLint range = -1; + GLint shadowPower = -1; + GLint useAlphaMatte = -1; // always inverted + + GLint applyTint = -1; + GLint tint = -1; + + GLint gradient = -1; + GLint gradientLength = -1; + GLint angle = -1; + GLint gradient2 = -1; + GLint gradient2Length = -1; + GLint angle2 = -1; + GLint gradientLerp = -1; + + float initialTime = 0; + GLint time = -1; + GLint distort = -1; + GLint wl_output = -1; + + // Blur prepare + GLint contrast = -1; + + // Blur + GLint passes = -1; // Used by `vibrancy` + GLint vibrancy = -1; + GLint vibrancy_darkness = -1; + + // Blur finish + GLint brightness = -1; + GLint noise = -1; + + GLint getUniformLocation(const std::string&); + + void destroy(); private: - GLuint m_program = 0; - float m_initialTime = 0; - std::array m_uniformLocations; - - struct SUniformMatrix3Data { - GLsizei count = 0; - GLboolean transpose = false; - std::array value = {}; - }; - - struct SUniformMatrix4Data { - GLsizei count = 0; - GLboolean transpose = false; - std::array value = {}; - }; - - struct SUniformVData { - GLsizei count = 0; - std::vector value; - }; - - // - std::array, std::array, std::array, SUniformMatrix3Data, SUniformMatrix4Data, - SUniformVData>, - SHADER_LAST> - uniformStatus; - // - - void logShaderError(const GLuint&, bool program = false, bool silent = false); - GLuint compileShader(const GLuint&, std::string, bool dynamic = false, bool silent = false); - void getUniformLocations(); - void createVao(); - void setUniformfv(eShaderUniform location, GLsizei count, const std::vector& value, GLsizei vec_size); + std::unordered_map m_muUniforms; }; diff --git a/src/render/ShaderLoader.cpp b/src/render/ShaderLoader.cpp deleted file mode 100644 index 0d2d0ee4..00000000 --- a/src/render/ShaderLoader.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include "ShaderLoader.hpp" -#include -#include -#include -#include -#include -#include "../debug/log/Logger.hpp" -#include "shaders/Shaders.hpp" -#include "../helpers/fs/FsUtils.hpp" -#include "Renderer.hpp" -#include -#include -#include - -using namespace Render; - -CShaderLoader::CShaderLoader(const std::vector includes, const std::array& frags, const std::string shaderPath) : m_shaderPath(shaderPath) { - m_callbacks = glsl_include_callbacks_t{ - .include_local = - [](void* ctx, const char* header_name, const char* includer_name, size_t include_depth) { - auto shaderLoader = sc(ctx); - auto res = new glsl_include_result_t; - if (shaderLoader->m_overrideDefines.length() && std::string{header_name} == "defines.h") { - res->header_name = header_name; - res->header_data = shaderLoader->m_overrideDefines.c_str(); - res->header_length = shaderLoader->m_overrideDefines.length(); - } else if (shaderLoader->includes().contains(header_name)) { - res->header_name = header_name; - res->header_data = shaderLoader->includes().at(header_name).c_str(); - res->header_length = shaderLoader->includes().at(header_name).length(); - } else { - res->header_name = nullptr; - res->header_data = nullptr; - res->header_length = 0; - } - - shaderLoader->m_includeResults.push_back(res); - return res; - }, - .free_include_result = - [](void* ctx, glsl_include_result_t* result) { - auto shaderLoader = sc(ctx); - std::erase(shaderLoader->m_includeResults, result); - delete result; - return 0; - }, - }; - - for (const auto& inc : includes) { - include(inc); - } - - std::ranges::transform(frags, m_fragFiles.begin(), [&](const auto& filename) { return loadShader(filename); }); -} - -CShaderLoader::~CShaderLoader() { - // glslFreeIncludeResult should leave it empty by this point - for (const auto& res : m_includeResults) { - delete res; - } -} - -void CShaderLoader::include(const std::string& filename) { - m_includes.insert({filename, loadShader(filename)}); -} - -std::string CShaderLoader::getDefines(ShaderFeatureFlags features) { - std::string res = ""; - std::map defines = { - {"USE_RGBA", features & SH_FEAT_RGBA ? "1" : "0"}, {"USE_DISCARD", features & SH_FEAT_DISCARD ? "1" : "0"}, {"USE_TINT", features & SH_FEAT_TINT ? "1" : "0"}, - {"USE_ROUNDING", features & SH_FEAT_ROUNDING ? "1" : "0"}, {"USE_CM", features & SH_FEAT_CM ? "1" : "0"}, {"USE_TONEMAP", features & SH_FEAT_TONEMAP ? "1" : "0"}, - {"USE_SDR_MOD", features & SH_FEAT_SDR_MOD ? "1" : "0"}, {"USE_BLUR", features & SH_FEAT_BLUR ? "1" : "0"}, {"USE_ICC", features & SH_FEAT_ICC ? "1" : "0"}, - }; - for (const auto& [name, value] : defines) { - res += std::format("#define {} {}\n", name, value); - } - return res; -} - -std::string CShaderLoader::processSource(const std::string& source, glslang_stage_t stage) { - const glslang_input_t input = { - .language = GLSLANG_SOURCE_GLSL, - .stage = stage, - .client = GLSLANG_CLIENT_NONE, - .target_language = GLSLANG_TARGET_NONE, - .code = source.c_str(), - .default_version = 100, - .default_profile = GLSLANG_NO_PROFILE, - .force_default_version_and_profile = false, - .forward_compatible = false, - .messages = GLSLANG_MSG_DEFAULT_BIT, - .resource = glslang_default_resource(), - .callbacks = m_callbacks, - .callbacks_ctx = this, - }; - - glslang_shader_t* shader = glslang_shader_create(&input); - - if (!glslang_shader_preprocess(shader, &input)) { - Log::logger->log(Log::ERR, "GLSL preprocessing failed"); - Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_log(shader)); - Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_debug_log(shader)); - Log::logger->log(Log::ERR, "{}", input.code); - glslang_shader_delete(shader); - return source; - } - - std::stringstream stream(glslang_shader_get_preprocessed_code(shader)); - std::string code = ""; - std::string line; - - while (std::getline(stream, line)) { - if (!line.starts_with("#line ")) - code += line + "\n"; - } - - return code; -} - -std::string CShaderLoader::process(const std::string& filename) { - auto source = loadShader(filename); - return processSource(source, filename.ends_with(".vert") ? GLSLANG_STAGE_VERTEX : GLSLANG_STAGE_FRAGMENT); -} - -std::string CShaderLoader::process(const std::string& filename, const std::map& defines) { - m_overrideDefines = ""; - for (const auto& [name, value] : defines) { - m_overrideDefines += std::format("#define {} {}\n", name, value); - } - const auto& res = process(filename); - m_overrideDefines = ""; - return res; -} - -std::string CShaderLoader::getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features) { - static const auto PCM = CConfigValue("render:cm_enabled"); - if (!*PCM) - features &= ~(SH_FEAT_CM | SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD); - - if (!m_fragVariants[frag].contains(features)) { - ASSERT(m_fragFiles[frag].length()); - m_overrideDefines = getDefines(features); - m_fragVariants[frag][features] = processSource(m_fragFiles[frag]); - m_overrideDefines = ""; - } - - return m_fragVariants[frag][features]; -} - -const std::map& CShaderLoader::includes() { - return m_includes; -} - -// TODO notify user if bundled shader is newer than ~/.config override -std::string CShaderLoader::loadShader(const std::string& filename) { - if (m_shaderPath.length()) { - std::filesystem::path path = m_shaderPath; - const auto src = NFsUtils::readFileAsString(path / filename); - if (src.has_value()) - return src.value(); - } - const auto home = Hyprutils::Path::getHome(); - if (home.has_value()) { - const auto src = NFsUtils::readFileAsString(home.value() + "/hypr/shaders/" + filename); - if (src.has_value()) - return src.value(); - } - for (auto& e : ASSET_PATHS) { - const auto src = NFsUtils::readFileAsString(std::string{e} + "/hypr/shaders/" + filename); - if (src.has_value()) - return src.value(); - } - if (SHADERS.contains(filename)) - return SHADERS.at(filename); - throw std::runtime_error(std::format("Couldn't load shader {}", filename)); -} diff --git a/src/render/ShaderLoader.hpp b/src/render/ShaderLoader.hpp deleted file mode 100644 index e522e9fa..00000000 --- a/src/render/ShaderLoader.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "../helpers/memory/Memory.hpp" - -namespace Render { - enum ePreparedFragmentShaderFeature : uint16_t { - SH_FEAT_UNKNOWN = 0, // all features just in case - - SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling - SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling - SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint - SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0 - SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM - SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01 - SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1) - SH_FEAT_BLUR = (1 << 7), // condition: render:use_shader_blur_blend - SH_FEAT_ICC = (1 << 8), // - - // uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD - }; - - using ShaderFeatureFlags = uint16_t; - - enum ePreparedFragmentShader : uint8_t { - SH_FRAG_QUAD = 0, - SH_FRAG_PASSTHRURGBA, - SH_FRAG_MATTE, - SH_FRAG_EXT, - SH_FRAG_BLUR1, - SH_FRAG_BLUR2, - SH_FRAG_BLURPREPARE, - SH_FRAG_BLURFINISH, - SH_FRAG_SHADOW, - SH_FRAG_SURFACE, - SH_FRAG_BORDER1, - SH_FRAG_GLITCH, - - SH_FRAG_LAST, - }; - - class CShaderLoader { - public: - CShaderLoader(const std::vector includes, const std::array& frags, const std::string shaderPath = ""); - ~CShaderLoader(); - - void include(const std::string& filename); - std::string process(const std::string& filename); - std::string process(const std::string& filename, const std::map& defines); - - std::string getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features); - - const std::map& includes(); - - std::vector m_includeResults; - - private: - std::string loadShader(const std::string& filename); - std::string getDefines(ShaderFeatureFlags features); - std::string processSource(const std::string& source, glslang_stage_t stage = GLSLANG_STAGE_FRAGMENT); - - // - std::string m_shaderPath; - std::array m_fragFiles; - std::array, SH_FRAG_LAST> m_fragVariants; - std::map m_includes; - - std::string m_overrideDefines; - glsl_include_callbacks_t m_callbacks; - }; - - inline UP g_pShaderLoader; -} diff --git a/src/render/Shaders.hpp b/src/render/Shaders.hpp new file mode 100644 index 00000000..1849c621 --- /dev/null +++ b/src/render/Shaders.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "shaders/Textures.hpp" +#include "shaders/Shadow.hpp" +#include "shaders/Border.hpp" \ No newline at end of file diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 28ae4b41..fd5b682e 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -1,24 +1,170 @@ #include "Texture.hpp" +#include "Renderer.hpp" +#include "../Compositor.hpp" +#include "../protocols/types/Buffer.hpp" +#include "../helpers/Format.hpp" #include -ITexture::ITexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy, bool opaque) : - m_size(size), m_opaque(opaque), m_drmFormat(drmFormat), m_keepDataCopy(keepDataCopy) { - if (m_keepDataCopy && stride && pixels) { - m_dataCopy.resize(stride * size.y); - memcpy(m_dataCopy.data(), pixels, stride * size.y); +CTexture::CTexture() = default; + +CTexture::~CTexture() { + if (!g_pCompositor || g_pCompositor->m_bIsShuttingDown || !g_pHyprRenderer) + return; + + g_pHyprRenderer->makeEGLCurrent(); + destroyTexture(); +} + +CTexture::CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_, bool keepDataCopy) : m_iDrmFormat(drmFormat), m_bKeepDataCopy(keepDataCopy) { + createFromShm(drmFormat, pixels, stride, size_); +} + +CTexture::CTexture(const Aquamarine::SDMABUFAttrs& attrs, void* image) { + createFromDma(attrs, image); +} + +CTexture::CTexture(const SP buffer, bool keepDataCopy) : m_bKeepDataCopy(keepDataCopy) { + if (!buffer) + return; + + m_bOpaque = buffer->opaque; + + auto attrs = buffer->dmabuf(); + + if (!attrs.success) { + // attempt shm + auto shm = buffer->shm(); + + if (!shm.success) { + Debug::log(ERR, "Cannot create a texture: buffer has no dmabuf or shm"); + return; + } + + auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); + + m_iDrmFormat = fmt; + + createFromShm(fmt, pixelData, bufLen, shm.size); + return; + } + + auto image = g_pHyprOpenGL->createEGLImage(buffer->dmabuf()); + + if (!image) { + Debug::log(ERR, "Cannot create a texture: failed to create an EGLImage"); + return; + } + + createFromDma(attrs, image); +} + +void CTexture::createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_) { + g_pHyprRenderer->makeEGLCurrent(); + + const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); + ASSERT(format); + + m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; + m_vSize = size_; + m_isSynchronous = true; + allocate(); + + GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); +#ifndef GLES2 + if (format->flipRB) { + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED)); + } +#endif + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); + GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, size_.x, size_.y, 0, format->glFormat, format->glType, pixels)); + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); + GLCALL(glBindTexture(GL_TEXTURE_2D, 0)); + + if (m_bKeepDataCopy) { + m_vDataCopy.resize(stride * size_.y); + memcpy(m_vDataCopy.data(), pixels, stride * size_.y); } } -ITexture::ITexture(std::span lut3D, size_t N) : m_type(TEXTURE_3D_LUT), m_size(lut3D.size() / 3, 1), m_isSynchronous(true) {} +void CTexture::createFromDma(const Aquamarine::SDMABUFAttrs& attrs, void* image) { + if (!g_pHyprOpenGL->m_sProc.glEGLImageTargetTexture2DOES) { + Debug::log(ERR, "Cannot create a dmabuf texture: no glEGLImageTargetTexture2DOES"); + return; + } -bool ITexture::ok() { - return false; + m_bOpaque = NFormatUtils::isFormatOpaque(attrs.format); + m_iTarget = GL_TEXTURE_2D; + m_iType = TEXTURE_RGBA; + m_vSize = attrs.size; + m_iType = NFormatUtils::isFormatOpaque(attrs.format) ? TEXTURE_RGBX : TEXTURE_RGBA; + allocate(); + m_pEglImage = image; + + GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GLCALL(g_pHyprOpenGL->m_sProc.glEGLImageTargetTexture2DOES(m_iTarget, image)); + GLCALL(glBindTexture(GL_TEXTURE_2D, 0)); } -bool ITexture::isDMA() { - return false; +void CTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) { + g_pHyprRenderer->makeEGLCurrent(); + + const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); + ASSERT(format); + + glBindTexture(GL_TEXTURE_2D, m_iTexID); + + auto rects = damage.copy().intersect(CBox{{}, m_vSize}).getRects(); + +#ifndef GLES2 + if (format->flipRB) { + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED)); + } +#endif + + for (auto const& rect : rects) { + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, rect.x1)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, rect.y1)); + + int width = rect.x2 - rect.x1; + int height = rect.y2 - rect.y1; + GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x1, rect.y1, width, height, format->glFormat, format->glType, pixels)); + } + + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0)); + + glBindTexture(GL_TEXTURE_2D, 0); + + if (m_bKeepDataCopy) { + m_vDataCopy.resize(stride * m_vSize.y); + memcpy(m_vDataCopy.data(), pixels, stride * m_vSize.y); + } } -const std::vector& ITexture::dataCopy() { - return m_dataCopy; +void CTexture::destroyTexture() { + if (m_iTexID) { + GLCALL(glDeleteTextures(1, &m_iTexID)); + m_iTexID = 0; + } + + if (m_pEglImage) + g_pHyprOpenGL->m_sProc.eglDestroyImageKHR(g_pHyprOpenGL->m_pEglDisplay, m_pEglImage); + m_pEglImage = nullptr; +} + +void CTexture::allocate() { + if (!m_iTexID) + GLCALL(glGenTextures(1, &m_iTexID)); +} + +const std::vector& CTexture::dataCopy() { + return m_vDataCopy; } diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index 38c3ff01..039b5247 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -3,7 +3,6 @@ #include "../defines.hpp" #include #include -#include class IHLBuffer; HYPRUTILS_FORWARD(Math, CRegion); @@ -12,47 +11,45 @@ enum eTextureType : int8_t { TEXTURE_INVALID = -1, // Invalid TEXTURE_RGBA = 0, // 4 channels TEXTURE_RGBX, // discard A - TEXTURE_3D_LUT, // 3D LUT TEXTURE_EXTERNAL, // EGLImage }; -class ITexture { +class CTexture { public: - ITexture(ITexture&) = delete; - ITexture(ITexture&&) = delete; - ITexture(const ITexture&&) = delete; - ITexture(const ITexture&) = delete; + CTexture(); - virtual ~ITexture() = default; + CTexture(CTexture&) = delete; + CTexture(CTexture&&) = delete; + CTexture(const CTexture&&) = delete; + CTexture(const CTexture&) = delete; - virtual void setTexParameter(GLenum pname, GLint param) = 0; - virtual void allocate(const Vector2D& size, uint32_t drmFormat = 0) = 0; - virtual void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) = 0; - virtual void bind() {}; - virtual void unbind() {}; - virtual bool ok(); - virtual bool isDMA(); + CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false); + CTexture(const SP buffer, bool keepDataCopy = false); + // this ctor takes ownership of the eglImage. + CTexture(const Aquamarine::SDMABUFAttrs&, void* image); + ~CTexture(); + + void destroyTexture(); + void allocate(); + void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage); const std::vector& dataCopy(); - eTextureType m_type = TEXTURE_RGBA; - Vector2D m_size = {}; - eTransform m_transform = HYPRUTILS_TRANSFORM_NORMAL; - bool m_opaque = false; - - uint32_t m_drmFormat = 0; // for shm + eTextureType m_iType = TEXTURE_RGBA; + GLenum m_iTarget = GL_TEXTURE_2D; + GLuint m_iTexID = 0; + Vector2D m_vSize = {}; + void* m_pEglImage = nullptr; + eTransform m_eTransform = HYPRUTILS_TRANSFORM_NORMAL; + bool m_bOpaque = false; + uint32_t m_iDrmFormat = 0; // for shm bool m_isSynchronous = false; - // TODO move to GLTexture - GLuint m_texID = 0; - GLenum magFilter = GL_LINEAR; // useNearestNeighbor overwrites these - GLenum minFilter = GL_LINEAR; + private: + void createFromShm(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); + void createFromDma(const Aquamarine::SDMABUFAttrs&, void* image); - protected: - ITexture() = default; - ITexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - ITexture(std::span lut3D, size_t N); + bool m_bKeepDataCopy = false; - bool m_keepDataCopy = false; - std::vector m_dataCopy; -}; + std::vector m_vDataCopy; +}; \ No newline at end of file diff --git a/src/render/Transformer.hpp b/src/render/Transformer.hpp index 8f401859..048b1898 100644 --- a/src/render/Transformer.hpp +++ b/src/render/Transformer.hpp @@ -14,7 +14,7 @@ class IWindowTransformer { // called by Hyprland. For more data about what is being rendered, inspect render data. // returns the out fb. - virtual IFramebuffer* transform(IFramebuffer* in) = 0; + virtual CFramebuffer* transform(CFramebuffer* in) = 0; // called by Hyprland before a window main pass is started. virtual void preWindowRender(CSurfacePassElement::SRenderData* pRenderData); diff --git a/src/render/decorations/CHyprBorderDecoration.cpp b/src/render/decorations/CHyprBorderDecoration.cpp index 3e4f04a9..0d07c2ea 100644 --- a/src/render/decorations/CHyprBorderDecoration.cpp +++ b/src/render/decorations/CHyprBorderDecoration.cpp @@ -4,43 +4,44 @@ #include "../../managers/eventLoop/EventLoopManager.hpp" #include "../pass/BorderPassElement.hpp" #include "../Renderer.hpp" +#include "../../managers/HookSystemManager.hpp" -CHyprBorderDecoration::CHyprBorderDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_window(pWindow) { +CHyprBorderDecoration::CHyprBorderDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_pWindow(pWindow) { ; } SDecorationPositioningInfo CHyprBorderDecoration::getPositioningInfo() { - const auto BORDERSIZE = m_window->getRealBorderSize(); - m_extents = {{BORDERSIZE, BORDERSIZE}, {BORDERSIZE, BORDERSIZE}}; + const auto BORDERSIZE = m_pWindow->getRealBorderSize(); + m_seExtents = {{BORDERSIZE, BORDERSIZE}, {BORDERSIZE, BORDERSIZE}}; if (doesntWantBorders()) - m_extents = {{}, {}}; + m_seExtents = {{}, {}}; SDecorationPositioningInfo info; info.priority = 10000; info.policy = DECORATION_POSITION_STICKY; - info.desiredExtents = m_extents; + info.desiredExtents = m_seExtents; info.reserved = true; info.edges = DECORATION_EDGE_BOTTOM | DECORATION_EDGE_LEFT | DECORATION_EDGE_RIGHT | DECORATION_EDGE_TOP; - m_reportedExtents = m_extents; + m_seReportedExtents = m_seExtents; return info; } void CHyprBorderDecoration::onPositioningReply(const SDecorationPositioningReply& reply) { - m_assignedGeometry = reply.assignedGeometry; + m_bAssignedGeometry = reply.assignedGeometry; } CBox CHyprBorderDecoration::assignedBoxGlobal() { - CBox box = m_assignedGeometry; - box.translate(g_pDecorationPositioner->getEdgeDefinedPoint(DECORATION_EDGE_BOTTOM | DECORATION_EDGE_LEFT | DECORATION_EDGE_RIGHT | DECORATION_EDGE_TOP, m_window)); + CBox box = m_bAssignedGeometry; + box.translate(g_pDecorationPositioner->getEdgeDefinedPoint(DECORATION_EDGE_BOTTOM | DECORATION_EDGE_LEFT | DECORATION_EDGE_RIGHT | DECORATION_EDGE_TOP, m_pWindow.lock())); - const auto PWORKSPACE = m_window->m_workspace; + const auto PWORKSPACE = m_pWindow->m_pWorkspace; if (!PWORKSPACE) return box; - const auto WORKSPACEOFFSET = PWORKSPACE && !m_window->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D(); + const auto WORKSPACEOFFSET = PWORKSPACE && !m_pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset->value() : Vector2D(); return box.translate(WORKSPACEOFFSET); } @@ -48,51 +49,47 @@ void CHyprBorderDecoration::draw(PHLMONITOR pMonitor, float const& a) { if (doesntWantBorders()) return; - if (m_assignedGeometry.width < m_extents.topLeft.x + 1 || m_assignedGeometry.height < m_extents.topLeft.y + 1) + if (m_bAssignedGeometry.width < m_seExtents.topLeft.x + 1 || m_bAssignedGeometry.height < m_seExtents.topLeft.y + 1) return; - CBox windowBox = assignedBoxGlobal().translate(-pMonitor->m_position + m_window->m_floatingOffset).expand(-m_window->getRealBorderSize()).scale(pMonitor->m_scale).round(); + CBox windowBox = assignedBoxGlobal().translate(-pMonitor->vecPosition + m_pWindow->m_vFloatingOffset).expand(-m_pWindow->getRealBorderSize()).scale(pMonitor->scale).round(); if (windowBox.width < 1 || windowBox.height < 1) return; - auto grad = m_window->m_realBorderColor; - const bool ANIMATED = m_window->m_borderFadeAnimationProgress->isBeingAnimated(); + auto grad = m_pWindow->m_cRealBorderColor; + const bool ANIMATED = m_pWindow->m_fBorderFadeAnimationProgress->isBeingAnimated(); - if (m_window->m_borderAngleAnimationProgress->enabled()) { - grad.m_angle += m_window->m_borderAngleAnimationProgress->value() * M_PI * 2; - grad.m_angle = normalizeAngleRad(grad.m_angle); + if (m_pWindow->m_fBorderAngleAnimationProgress->enabled()) { + grad.m_fAngle += m_pWindow->m_fBorderAngleAnimationProgress->value() * M_PI * 2; + grad.m_fAngle = normalizeAngleRad(grad.m_fAngle); // When borderangle is animated, it is counterintuitive to fade between inactive/active gradient angles. // Instead we sync the angles to avoid fading between them and additionally rotating the border angle. if (ANIMATED) - m_window->m_realBorderColorPrevious.m_angle = grad.m_angle; + m_pWindow->m_cRealBorderColorPrevious.m_fAngle = grad.m_fAngle; } - int borderSize = m_window->getRealBorderSize(); - const auto ROUNDINGBASE = m_window->rounding(); - const auto ROUNDING = ROUNDINGBASE * pMonitor->m_scale; - const auto ROUNDINGPOWER = m_window->roundingPower(); - const auto CORRECTIONOFFSET = (borderSize * (M_SQRT2 - 1) * std::max(2.0 - ROUNDINGPOWER, 0.0)); - const auto OUTERROUND = ((ROUNDINGBASE + borderSize) - CORRECTIONOFFSET) * pMonitor->m_scale; + int borderSize = m_pWindow->getRealBorderSize(); + const auto ROUNDING = m_pWindow->rounding() * pMonitor->scale; + const auto ROUNDINGPOWER = m_pWindow->roundingPower(); CBorderPassElement::SBorderData data; data.box = windowBox; data.grad1 = grad; data.round = ROUNDING; - data.outerRound = OUTERROUND; data.roundingPower = ROUNDINGPOWER; data.a = a; data.borderSize = borderSize; if (ANIMATED) { data.hasGrad2 = true; - data.grad1 = m_window->m_realBorderColorPrevious; + data.grad1 = m_pWindow->m_cRealBorderColorPrevious; data.grad2 = grad; - data.lerp = m_window->m_borderFadeAnimationProgress->value(); + data.lerp = m_pWindow->m_fBorderFadeAnimationProgress->value(); } - g_pHyprRenderer->m_renderPass.add(makeUnique(data)); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } eDecorationType CHyprBorderDecoration::getDecorationType() { @@ -100,33 +97,44 @@ eDecorationType CHyprBorderDecoration::getDecorationType() { } void CHyprBorderDecoration::updateWindow(PHLWINDOW) { - auto borderSize = m_window->getRealBorderSize(); + auto borderSize = m_pWindow->getRealBorderSize(); - if (borderSize == m_lastBorderSize) + if (borderSize == m_iLastBorderSize) return; - if (borderSize <= 0 && m_lastBorderSize <= 0) + if (borderSize <= 0 && m_iLastBorderSize <= 0) return; - m_lastBorderSize = borderSize; + m_iLastBorderSize = borderSize; g_pDecorationPositioner->repositionDeco(this); } void CHyprBorderDecoration::damageEntire() { - if (!validMapped(m_window) || m_window->m_fullscreenState.internal == FSMODE_FULLSCREEN) + if (!validMapped(m_pWindow)) return; - const auto GLOBAL_BOX = assignedBoxGlobal(); - const auto ROUNDING = m_window->rounding(); - const auto BORDERSIZE = m_window->getRealBorderSize() + 1; + auto surfaceBox = m_pWindow->getWindowMainSurfaceBox(); + const auto ROUNDING = m_pWindow->rounding(); + const auto ROUNDINGSIZE = ROUNDING - M_SQRT1_2 * ROUNDING + 2; + const auto BORDERSIZE = m_pWindow->getRealBorderSize() + 1; - CRegion borderRegion(GLOBAL_BOX); - borderRegion.subtract(GLOBAL_BOX.copy().expand(-(BORDERSIZE + ROUNDING))); + const auto PWINDOWWORKSPACE = m_pWindow->m_pWorkspace; + if (PWINDOWWORKSPACE && PWINDOWWORKSPACE->m_vRenderOffset->isBeingAnimated() && !m_pWindow->m_bPinned) + surfaceBox.translate(PWINDOWWORKSPACE->m_vRenderOffset->value()); + surfaceBox.translate(m_pWindow->m_vFloatingOffset); - for (auto const& m : g_pCompositor->m_monitors) { - if (!g_pHyprRenderer->shouldRenderWindow(m_window.lock(), m)) { - const CRegion monitorRegion({m->m_position, m->m_size}); + CBox surfaceBoxExpandedBorder = surfaceBox; + surfaceBoxExpandedBorder.expand(BORDERSIZE); + CBox surfaceBoxShrunkRounding = surfaceBox; + surfaceBoxShrunkRounding.expand(-ROUNDINGSIZE); + + CRegion borderRegion(surfaceBoxExpandedBorder); + borderRegion.subtract(surfaceBoxShrunkRounding); + + for (auto const& m : g_pCompositor->m_vMonitors) { + if (!g_pHyprRenderer->shouldRenderWindow(m_pWindow.lock(), m)) { + const CRegion monitorRegion({m->vecPosition, m->vecSize}); borderRegion.subtract(monitorRegion); } } @@ -139,9 +147,7 @@ eDecorationLayer CHyprBorderDecoration::getDecorationLayer() { } uint64_t CHyprBorderDecoration::getDecorationFlags() { - static auto PPARTOFWINDOW = CConfigValue("decoration:border_part_of_window"); - - return *PPARTOFWINDOW && !doesntWantBorders() ? DECORATION_PART_OF_MAIN_WINDOW : 0; + return !doesntWantBorders() ? DECORATION_PART_OF_MAIN_WINDOW : 0; } std::string CHyprBorderDecoration::getDisplayName() { @@ -149,5 +155,5 @@ std::string CHyprBorderDecoration::getDisplayName() { } bool CHyprBorderDecoration::doesntWantBorders() { - return m_window->m_X11DoesntWantBorders || m_window->getRealBorderSize() == 0 || !m_window->m_ruleApplicator->decorate().valueOrDefault(); + return m_pWindow->m_sWindowData.noBorder.valueOrDefault() || m_pWindow->m_bX11DoesntWantBorders || m_pWindow->getRealBorderSize() == 0; } diff --git a/src/render/decorations/CHyprBorderDecoration.hpp b/src/render/decorations/CHyprBorderDecoration.hpp index 607c12cc..332c08b7 100644 --- a/src/render/decorations/CHyprBorderDecoration.hpp +++ b/src/render/decorations/CHyprBorderDecoration.hpp @@ -26,14 +26,17 @@ class CHyprBorderDecoration : public IHyprWindowDecoration { virtual std::string getDisplayName(); private: - SBoxExtents m_extents; - SBoxExtents m_reportedExtents; + SBoxExtents m_seExtents; + SBoxExtents m_seReportedExtents; - PHLWINDOWREF m_window; + PHLWINDOWREF m_pWindow; - CBox m_assignedGeometry = {0}; + Vector2D m_vLastWindowPos; + Vector2D m_vLastWindowSize; - int m_lastBorderSize = -1; + CBox m_bAssignedGeometry = {0}; + + int m_iLastBorderSize = -1; CBox assignedBoxGlobal(); bool doesntWantBorders(); diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp index 5e1b6e8a..60b1b33a 100644 --- a/src/render/decorations/CHyprDropShadowDecoration.cpp +++ b/src/render/decorations/CHyprDropShadowDecoration.cpp @@ -5,7 +5,7 @@ #include "../pass/ShadowPassElement.hpp" #include "../Renderer.hpp" -CHyprDropShadowDecoration::CHyprDropShadowDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_window(pWindow) { +CHyprDropShadowDecoration::CHyprDropShadowDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_pWindow(pWindow) { ; } @@ -16,15 +16,15 @@ eDecorationType CHyprDropShadowDecoration::getDecorationType() { SDecorationPositioningInfo CHyprDropShadowDecoration::getPositioningInfo() { SDecorationPositioningInfo info; info.policy = DECORATION_POSITION_ABSOLUTE; - info.desiredExtents = m_extents; + info.desiredExtents = m_seExtents; info.edges = DECORATION_EDGE_BOTTOM | DECORATION_EDGE_LEFT | DECORATION_EDGE_RIGHT | DECORATION_EDGE_TOP; - m_reportedExtents = m_extents; + m_seReportedExtents = m_seExtents; return info; } void CHyprDropShadowDecoration::onPositioningReply(const SDecorationPositioningReply& reply) { - updateWindow(m_window.lock()); + updateWindow(m_pWindow.lock()); } uint64_t CHyprDropShadowDecoration::getDecorationFlags() { @@ -41,20 +41,16 @@ void CHyprDropShadowDecoration::damageEntire() { if (*PSHADOWS != 1) return; // disabled - const auto PWINDOW = m_window.lock(); - const auto pos = PWINDOW->m_realPosition->value(); - const auto size = PWINDOW->m_realSize->value(); + const auto PWINDOW = m_pWindow.lock(); - CBox shadowBox = {pos.x - m_extents.topLeft.x, pos.y - m_extents.topLeft.y, pos.x + size.x + m_extents.bottomRight.x, pos.y + size.y + m_extents.bottomRight.y}; + CBox shadowBox = {PWINDOW->m_vRealPosition->value().x - m_seExtents.topLeft.x, PWINDOW->m_vRealPosition->value().y - m_seExtents.topLeft.y, + PWINDOW->m_vRealSize->value().x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x, + PWINDOW->m_vRealSize->value().y + m_seExtents.topLeft.y + m_seExtents.bottomRight.y}; - const auto PWORKSPACE = PWINDOW->m_workspace; - const auto applyOffset = [&](CBox& b) { - if (PWORKSPACE && PWORKSPACE->m_renderOffset->isBeingAnimated() && !PWINDOW->m_pinned) - b.translate(PWORKSPACE->m_renderOffset->value()); - b.translate(PWINDOW->m_floatingOffset); - }; - - applyOffset(shadowBox); + const auto PWORKSPACE = PWINDOW->m_pWorkspace; + if (PWORKSPACE && PWORKSPACE->m_vRenderOffset->isBeingAnimated() && !PWINDOW->m_bPinned) + shadowBox.translate(PWORKSPACE->m_vRenderOffset->value()); + shadowBox.translate(PWINDOW->m_vFloatingOffset); static auto PSHADOWIGNOREWINDOW = CConfigValue("decoration:shadow:ignore_window"); const auto ROUNDING = PWINDOW->rounding(); @@ -63,14 +59,16 @@ void CHyprDropShadowDecoration::damageEntire() { CRegion shadowRegion(shadowBox); if (*PSHADOWIGNOREWINDOW) { CBox surfaceBox = PWINDOW->getWindowMainSurfaceBox(); - applyOffset(surfaceBox); + if (PWORKSPACE && PWORKSPACE->m_vRenderOffset->isBeingAnimated() && !PWINDOW->m_bPinned) + surfaceBox.translate(PWORKSPACE->m_vRenderOffset->value()); + surfaceBox.translate(PWINDOW->m_vFloatingOffset); surfaceBox.expand(-ROUNDINGSIZE); shadowRegion.subtract(CRegion(surfaceBox)); } - for (auto const& m : g_pCompositor->m_monitors) { + for (auto const& m : g_pCompositor->m_vMonitors) { if (!g_pHyprRenderer->shouldRenderWindow(PWINDOW, m)) { - const CRegion monitorRegion({m->m_position, m->m_size}); + const CRegion monitorRegion({m->vecPosition, m->vecSize}); shadowRegion.subtract(monitorRegion); } } @@ -79,35 +77,35 @@ void CHyprDropShadowDecoration::damageEntire() { } void CHyprDropShadowDecoration::updateWindow(PHLWINDOW pWindow) { - const auto PWINDOW = m_window.lock(); + const auto PWINDOW = m_pWindow.lock(); - m_lastWindowPos = PWINDOW->m_realPosition->value(); - m_lastWindowSize = PWINDOW->m_realSize->value(); + m_vLastWindowPos = PWINDOW->m_vRealPosition->value(); + m_vLastWindowSize = PWINDOW->m_vRealSize->value(); - m_lastWindowBox = {m_lastWindowPos.x, m_lastWindowPos.y, m_lastWindowSize.x, m_lastWindowSize.y}; - m_lastWindowBoxWithDecos = g_pDecorationPositioner->getBoxWithIncludedDecos(pWindow); + m_bLastWindowBox = {m_vLastWindowPos.x, m_vLastWindowPos.y, m_vLastWindowSize.x, m_vLastWindowSize.y}; + m_bLastWindowBoxWithDecos = g_pDecorationPositioner->getBoxWithIncludedDecos(pWindow); } void CHyprDropShadowDecoration::draw(PHLMONITOR pMonitor, float const& a) { CShadowPassElement::SShadowData data; data.deco = this; data.a = a; - g_pHyprRenderer->m_renderPass.add(makeUnique(data)); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { - const auto PWINDOW = m_window.lock(); + const auto PWINDOW = m_pWindow.lock(); if (!validMapped(PWINDOW)) return; - if (PWINDOW->m_realShadowColor->value() == CHyprColor(0, 0, 0, 0)) + if (PWINDOW->m_cRealShadowColor->value() == CHyprColor(0, 0, 0, 0)) return; // don't draw invisible shadows - if (!PWINDOW->m_ruleApplicator->decorate().valueOrDefault()) + if (!PWINDOW->m_sWindowData.decorate.valueOrDefault()) return; - if (PWINDOW->m_ruleApplicator->noShadow().valueOrDefault()) + if (PWINDOW->m_sWindowData.noShadow.valueOrDefault()) return; static auto PSHADOWS = CConfigValue("decoration:shadow:enabled"); @@ -119,17 +117,15 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { if (*PSHADOWS != 1) return; // disabled - const auto BORDERSIZE = PWINDOW->getRealBorderSize(); - const auto ROUNDINGBASE = PWINDOW->rounding(); - const auto ROUNDINGPOWER = PWINDOW->roundingPower(); - const auto CORRECTIONOFFSET = (BORDERSIZE * (M_SQRT2 - 1) * std::max(2.0 - ROUNDINGPOWER, 0.0)); - const auto ROUNDING = ROUNDINGBASE > 0 ? (ROUNDINGBASE + BORDERSIZE) - CORRECTIONOFFSET : 0; - const auto PWORKSPACE = PWINDOW->m_workspace; - const auto WORKSPACEOFFSET = PWORKSPACE && !PWINDOW->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D(); + const auto ROUNDINGBASE = PWINDOW->rounding(); + const auto ROUNDINGPOWER = PWINDOW->roundingPower(); + const auto ROUNDING = ROUNDINGBASE > 0 ? ROUNDINGBASE + PWINDOW->getRealBorderSize() : 0; + const auto PWORKSPACE = PWINDOW->m_pWorkspace; + const auto WORKSPACEOFFSET = PWORKSPACE && !PWINDOW->m_bPinned ? PWORKSPACE->m_vRenderOffset->value() : Vector2D(); // draw the shadow - CBox fullBox = m_lastWindowBoxWithDecos; - fullBox.translate(-pMonitor->m_position + WORKSPACEOFFSET); + CBox fullBox = m_bLastWindowBoxWithDecos; + fullBox.translate(-pMonitor->vecPosition + WORKSPACEOFFSET); fullBox.x -= *PSHADOWSIZE; fullBox.y -= *PSHADOWSIZE; fullBox.w += 2 * *PSHADOWSIZE; @@ -141,90 +137,89 @@ void CHyprDropShadowDecoration::render(PHLMONITOR pMonitor, float const& a) { fullBox.scaleFromCenter(SHADOWSCALE).translate({(*PSHADOWOFFSET).x, (*PSHADOWOFFSET).y}); updateWindow(PWINDOW); - m_lastWindowPos += WORKSPACEOFFSET; - m_extents = {{m_lastWindowPos.x - fullBox.x - pMonitor->m_position.x + 2, m_lastWindowPos.y - fullBox.y - pMonitor->m_position.y + 2}, - {fullBox.x + fullBox.width + pMonitor->m_position.x - m_lastWindowPos.x - m_lastWindowSize.x + 2, - fullBox.y + fullBox.height + pMonitor->m_position.y - m_lastWindowPos.y - m_lastWindowSize.y + 2}}; + m_vLastWindowPos += WORKSPACEOFFSET; + m_seExtents = {{m_vLastWindowPos.x - fullBox.x - pMonitor->vecPosition.x + 2, m_vLastWindowPos.y - fullBox.y - pMonitor->vecPosition.y + 2}, + {fullBox.x + fullBox.width + pMonitor->vecPosition.x - m_vLastWindowPos.x - m_vLastWindowSize.x + 2, + fullBox.y + fullBox.height + pMonitor->vecPosition.y - m_vLastWindowPos.y - m_vLastWindowSize.y + 2}}; - fullBox.translate(PWINDOW->m_floatingOffset); + fullBox.translate(PWINDOW->m_vFloatingOffset); if (fullBox.width < 1 || fullBox.height < 1) return; // don't draw invisible shadows g_pHyprOpenGL->scissor(nullptr); - g_pHyprOpenGL->m_renderData.currentWindow = m_window; + g_pHyprOpenGL->m_RenderData.currentWindow = m_pWindow; // we'll take the liberty of using this as it should not be used rn - auto alphaFB = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorFB; - auto alphaSwapFB = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorSwapFB; - auto LASTFB = g_pHyprOpenGL->m_renderData.currentFB; + CFramebuffer& alphaFB = g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorFB; + CFramebuffer& alphaSwapFB = g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorSwapFB; + auto* LASTFB = g_pHyprOpenGL->m_RenderData.currentFB; - fullBox.scale(pMonitor->m_scale).round(); + fullBox.scale(pMonitor->scale).round(); if (*PSHADOWIGNOREWINDOW) { - CBox windowBox = m_lastWindowBox; - CBox withDecos = m_lastWindowBoxWithDecos; + CBox windowBox = m_bLastWindowBox; + CBox withDecos = m_bLastWindowBoxWithDecos; // get window box - windowBox.translate(-pMonitor->m_position + WORKSPACEOFFSET); - withDecos.translate(-pMonitor->m_position + WORKSPACEOFFSET); + windowBox.translate(-pMonitor->vecPosition + WORKSPACEOFFSET); + withDecos.translate(-pMonitor->vecPosition + WORKSPACEOFFSET); - windowBox.translate(PWINDOW->m_floatingOffset); - withDecos.translate(PWINDOW->m_floatingOffset); + windowBox.translate(PWINDOW->m_vFloatingOffset); + withDecos.translate(PWINDOW->m_vFloatingOffset); auto scaledExtentss = withDecos.extentsFrom(windowBox); - scaledExtentss = scaledExtentss * pMonitor->m_scale; + scaledExtentss = scaledExtentss * pMonitor->scale; scaledExtentss = scaledExtentss.round(); // add extents - windowBox.scale(pMonitor->m_scale).round().addExtents(scaledExtentss); + windowBox.scale(pMonitor->scale).round().addExtents(scaledExtentss); if (windowBox.width < 1 || windowBox.height < 1) return; // prevent assert failed - CRegion saveDamage = g_pHyprOpenGL->m_renderData.damage; + CRegion saveDamage = g_pHyprOpenGL->m_RenderData.damage; - g_pHyprOpenGL->m_renderData.damage = fullBox; - g_pHyprOpenGL->m_renderData.damage.subtract(windowBox.copy().expand(-ROUNDING * pMonitor->m_scale)).intersect(saveDamage); - g_pHyprOpenGL->m_renderData.renderModif.applyToRegion(g_pHyprOpenGL->m_renderData.damage); + g_pHyprOpenGL->m_RenderData.damage = fullBox; + g_pHyprOpenGL->m_RenderData.damage.subtract(windowBox.copy().expand(-ROUNDING * pMonitor->scale)).intersect(saveDamage); + g_pHyprOpenGL->m_RenderData.renderModif.applyToRegion(g_pHyprOpenGL->m_RenderData.damage); - alphaFB->bind(); + alphaFB.bind(); // build the matte // 10-bit formats have dogshit alpha channels, so we have to use the matte to its fullest. // first, clear region of interest with black (fully transparent) - g_pHyprOpenGL->renderRect(fullBox, CHyprColor(0, 0, 0, 1), {.round = 0}); + g_pHyprOpenGL->renderRect(fullBox, CHyprColor(0, 0, 0, 1), 0); // render white shadow with the alpha of the shadow color (otherwise we clear with alpha later and shit it to 2 bit) - drawShadowInternal(fullBox, ROUNDING * pMonitor->m_scale, ROUNDINGPOWER, *PSHADOWSIZE * pMonitor->m_scale, CHyprColor(1, 1, 1, PWINDOW->m_realShadowColor->value().a), a); + drawShadowInternal(fullBox, ROUNDING * pMonitor->scale, ROUNDINGPOWER, *PSHADOWSIZE * pMonitor->scale, CHyprColor(1, 1, 1, PWINDOW->m_cRealShadowColor->value().a), a); // render black window box ("clip") - g_pHyprOpenGL->renderRect(windowBox, CHyprColor(0, 0, 0, 1.0), - {.round = (ROUNDING + 1 /* This fixes small pixel gaps. */) * pMonitor->m_scale, .roundingPower = ROUNDINGPOWER}); + g_pHyprOpenGL->renderRect(windowBox, CHyprColor(0, 0, 0, 1.0), (ROUNDING + 1 /* This fixes small pixel gaps. */) * pMonitor->scale, ROUNDINGPOWER); - alphaSwapFB->bind(); + alphaSwapFB.bind(); // alpha swap just has the shadow color. It will be the "texture" to render. - g_pHyprOpenGL->renderRect(fullBox, PWINDOW->m_realShadowColor->value().stripA(), {.round = 0}); + g_pHyprOpenGL->renderRect(fullBox, PWINDOW->m_cRealShadowColor->value().stripA(), 0); LASTFB->bind(); - CBox monbox = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y}; + CBox monbox = {0, 0, pMonitor->vecTransformedSize.x, pMonitor->vecTransformedSize.y}; - g_pHyprOpenGL->pushMonitorTransformEnabled(true); + g_pHyprOpenGL->setMonitorTransformEnabled(true); g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTextureMatte(alphaSwapFB->getTexture(), monbox, alphaFB); + g_pHyprOpenGL->renderTextureMatte(alphaSwapFB.getTexture(), monbox, alphaFB); g_pHyprOpenGL->setRenderModifEnabled(true); - g_pHyprOpenGL->popMonitorTransformEnabled(); + g_pHyprOpenGL->setMonitorTransformEnabled(false); - g_pHyprOpenGL->m_renderData.damage = saveDamage; + g_pHyprOpenGL->m_RenderData.damage = saveDamage; } else - drawShadowInternal(fullBox, ROUNDING * pMonitor->m_scale, ROUNDINGPOWER, *PSHADOWSIZE * pMonitor->m_scale, PWINDOW->m_realShadowColor->value(), a); + drawShadowInternal(fullBox, ROUNDING * pMonitor->scale, ROUNDINGPOWER, *PSHADOWSIZE * pMonitor->scale, PWINDOW->m_cRealShadowColor->value(), a); - if (m_extents != m_reportedExtents) + if (m_seExtents != m_seReportedExtents) g_pDecorationPositioner->repositionDeco(this); - g_pHyprOpenGL->m_renderData.currentWindow.reset(); + g_pHyprOpenGL->m_RenderData.currentWindow.reset(); } eDecorationLayer CHyprDropShadowDecoration::getDecorationLayer() { @@ -242,7 +237,7 @@ void CHyprDropShadowDecoration::drawShadowInternal(const CBox& box, int round, f color.a *= a; if (*PSHADOWSHARP) - g_pHyprOpenGL->renderRect(box, color, {.round = round, .roundingPower = roundingPower}); + g_pHyprOpenGL->renderRect(box, color, round, roundingPower); else g_pHyprOpenGL->renderRoundedShadow(box, round, roundingPower, range, color, 1.F); } diff --git a/src/render/decorations/CHyprDropShadowDecoration.hpp b/src/render/decorations/CHyprDropShadowDecoration.hpp index 22887a52..b5ee7276 100644 --- a/src/render/decorations/CHyprDropShadowDecoration.hpp +++ b/src/render/decorations/CHyprDropShadowDecoration.hpp @@ -28,16 +28,16 @@ class CHyprDropShadowDecoration : public IHyprWindowDecoration { void render(PHLMONITOR, float const& a); private: - SBoxExtents m_extents; - SBoxExtents m_reportedExtents; + SBoxExtents m_seExtents; + SBoxExtents m_seReportedExtents; - PHLWINDOWREF m_window; + PHLWINDOWREF m_pWindow; - Vector2D m_lastWindowPos; - Vector2D m_lastWindowSize; + Vector2D m_vLastWindowPos; + Vector2D m_vLastWindowSize; void drawShadowInternal(const CBox& box, int round, float roundingPower, int range, CHyprColor color, float a); - CBox m_lastWindowBox = {0}; - CBox m_lastWindowBoxWithDecos = {0}; + CBox m_bLastWindowBox = {0}; + CBox m_bLastWindowBoxWithDecos = {0}; }; diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index 6ce69261..d7858853 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -1,52 +1,39 @@ #include "CHyprGroupBarDecoration.hpp" #include "../../Compositor.hpp" #include "../../config/ConfigValue.hpp" -#include "../../desktop/state/FocusState.hpp" -#include "../../desktop/view/Group.hpp" +#include "managers/LayoutManager.hpp" #include #include #include "../pass/TexPassElement.hpp" #include "../pass/RectPassElement.hpp" #include "../Renderer.hpp" #include "../../managers/input/InputManager.hpp" -#include "../../layout/LayoutManager.hpp" -#include "../../layout/supplementary/DragController.hpp" // shared things to conserve VRAM -static SP m_tGradientActive; -static SP m_tGradientInactive; -static SP m_tGradientLockedActive; -static SP m_tGradientLockedInactive; +static SP m_tGradientActive = makeShared(); +static SP m_tGradientInactive = makeShared(); +static SP m_tGradientLockedActive = makeShared(); +static SP m_tGradientLockedInactive = makeShared(); constexpr int BAR_TEXT_PAD = 2; -CHyprGroupBarDecoration::CHyprGroupBarDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_window(pWindow) { +CHyprGroupBarDecoration::CHyprGroupBarDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow), m_pWindow(pWindow) { static auto PGRADIENTS = CConfigValue("group:groupbar:enabled"); static auto PENABLED = CConfigValue("group:groupbar:gradients"); - if (!m_tGradientActive) - m_tGradientActive = g_pHyprRenderer->createTexture(); - if (!m_tGradientInactive) - m_tGradientInactive = g_pHyprRenderer->createTexture(); - if (!m_tGradientLockedActive) - m_tGradientLockedActive = g_pHyprRenderer->createTexture(); - if (!m_tGradientLockedInactive) - m_tGradientLockedInactive = g_pHyprRenderer->createTexture(); - - if (!m_tGradientActive->ok() && *PENABLED && *PGRADIENTS) + if (m_tGradientActive->m_iTexID == 0 && *PENABLED && *PGRADIENTS) refreshGroupBarGradients(); } SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() { static auto PHEIGHT = CConfigValue("group:groupbar:height"); - static auto PINDICATORGAP = CConfigValue("group:groupbar:indicator_gap"); static auto PINDICATORHEIGHT = CConfigValue("group:groupbar:indicator_height"); + static auto PENABLED = CConfigValue("group:groupbar:enabled"); static auto PRENDERTITLES = CConfigValue("group:groupbar:render_titles"); static auto PGRADIENTS = CConfigValue("group:groupbar:gradients"); static auto PPRIORITY = CConfigValue("group:groupbar:priority"); static auto PSTACKED = CConfigValue("group:groupbar:stacked"); static auto POUTERGAP = CConfigValue("group:groupbar:gaps_out"); - static auto PKEEPUPPERGAP = CConfigValue("group:groupbar:keep_upper_gap"); SDecorationPositioningInfo info; info.policy = DECORATION_POSITION_STICKY; @@ -54,19 +41,19 @@ SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() { info.priority = *PPRIORITY; info.reserved = true; - if (visible()) { + if (*PENABLED && m_pWindow->m_sWindowData.decorate.valueOrDefault()) { if (*PSTACKED) { - const auto ONEBARHEIGHT = *POUTERGAP + *PINDICATORHEIGHT + *PINDICATORGAP + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0); - info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + (*PKEEPUPPERGAP * *POUTERGAP)}, {0, 0}}; + const auto ONEBARHEIGHT = *POUTERGAP + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0); + info.desiredExtents = {{0, (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + *POUTERGAP}, {0, 0}}; } else - info.desiredExtents = {{0, *POUTERGAP * (1 + *PKEEPUPPERGAP) + *PINDICATORHEIGHT + *PINDICATORGAP + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)}, {0, 0}}; + info.desiredExtents = {{0, *POUTERGAP * 2 + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)}, {0, 0}}; } else info.desiredExtents = {{0, 0}, {0, 0}}; return info; } void CHyprGroupBarDecoration::onPositioningReply(const SDecorationPositioningReply& reply) { - m_assignedBox = reply.assignedGeometry; + m_bAssignedBox = reply.assignedGeometry; } eDecorationType CHyprGroupBarDecoration::getDecorationType() { @@ -76,53 +63,52 @@ eDecorationType CHyprGroupBarDecoration::getDecorationType() { // void CHyprGroupBarDecoration::updateWindow(PHLWINDOW pWindow) { - if (!m_window->m_group) { - m_window->removeWindowDeco(this); + if (m_pWindow->m_sGroupData.pNextWindow.expired()) { + m_pWindow->removeWindowDeco(this); return; } m_dwGroupMembers.clear(); - for (const auto& w : m_window->m_group->windows()) { - m_dwGroupMembers.emplace_back(w); + PHLWINDOW head = pWindow->getGroupHead(); + m_dwGroupMembers.emplace_back(head); + + PHLWINDOW curr = head->m_sGroupData.pNextWindow.lock(); + while (curr != head) { + m_dwGroupMembers.emplace_back(curr); + curr = curr->m_sGroupData.pNextWindow.lock(); } damageEntire(); - if (m_dwGroupMembers.empty()) { - m_window->removeWindowDeco(this); + if (m_dwGroupMembers.size() == 0) { + m_pWindow->removeWindowDeco(this); return; } } void CHyprGroupBarDecoration::damageEntire() { auto box = assignedBoxGlobal(); - box.translate(m_window->m_floatingOffset); + box.translate(m_pWindow->m_vFloatingOffset); g_pHyprRenderer->damageBox(box); } void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) { // get how many bars we will draw - int barsToDraw = m_dwGroupMembers.size(); + int barsToDraw = m_dwGroupMembers.size(); - const bool VISIBLE = visible(); + static auto PENABLED = CConfigValue("group:groupbar:enabled"); - if (VISIBLE != m_bLastVisibilityStatus) - g_pDecorationPositioner->repositionDeco(this); - - if (!VISIBLE) + if (!*PENABLED || !m_pWindow->m_sWindowData.decorate.valueOrDefault()) return; static auto PRENDERTITLES = CConfigValue("group:groupbar:render_titles"); static auto PTITLEFONTSIZE = CConfigValue("group:groupbar:font_size"); static auto PHEIGHT = CConfigValue("group:groupbar:height"); - static auto PINDICATORGAP = CConfigValue("group:groupbar:indicator_gap"); static auto PINDICATORHEIGHT = CConfigValue("group:groupbar:indicator_height"); static auto PGRADIENTS = CConfigValue("group:groupbar:gradients"); static auto PSTACKED = CConfigValue("group:groupbar:stacked"); static auto PROUNDING = CConfigValue("group:groupbar:rounding"); - static auto PROUNDINGPOWER = CConfigValue("group:groupbar:rounding_power"); static auto PGRADIENTROUNDING = CConfigValue("group:groupbar:gradient_rounding"); - static auto PGRADIENTROUNDINGPOWER = CConfigValue("group:groupbar:gradient_rounding_power"); static auto PGRADIENTROUNDINGONLYEDGES = CConfigValue("group:groupbar:gradient_round_only_edges"); static auto PROUNDONLYEDGES = CConfigValue("group:groupbar:round_only_edges"); static auto PGROUPCOLACTIVE = CConfigValue("group:groupbar:col.active"); @@ -131,142 +117,139 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) { static auto PGROUPCOLINACTIVELOCKED = CConfigValue("group:groupbar:col.locked_inactive"); static auto POUTERGAP = CConfigValue("group:groupbar:gaps_out"); static auto PINNERGAP = CConfigValue("group:groupbar:gaps_in"); - static auto PKEEPUPPERGAP = CConfigValue("group:groupbar:keep_upper_gap"); - static auto PTEXTOFFSET = CConfigValue("group:groupbar:text_offset"); - static auto PTEXTPADDING = CConfigValue("group:groupbar:text_padding"); - static auto PBLUR = CConfigValue("group:groupbar:blur"); - auto* const GROUPCOLACTIVE = sc((PGROUPCOLACTIVE.ptr())->getData()); - auto* const GROUPCOLINACTIVE = sc((PGROUPCOLINACTIVE.ptr())->getData()); - auto* const GROUPCOLACTIVELOCKED = sc((PGROUPCOLACTIVELOCKED.ptr())->getData()); - auto* const GROUPCOLINACTIVELOCKED = sc((PGROUPCOLINACTIVELOCKED.ptr())->getData()); + auto* const GROUPCOLACTIVE = (CGradientValueData*)(PGROUPCOLACTIVE.ptr())->getData(); + auto* const GROUPCOLINACTIVE = (CGradientValueData*)(PGROUPCOLINACTIVE.ptr())->getData(); + auto* const GROUPCOLACTIVELOCKED = (CGradientValueData*)(PGROUPCOLACTIVELOCKED.ptr())->getData(); + auto* const GROUPCOLINACTIVELOCKED = (CGradientValueData*)(PGROUPCOLINACTIVELOCKED.ptr())->getData(); const auto ASSIGNEDBOX = assignedBoxGlobal(); - const auto ONEBARHEIGHT = *POUTERGAP + *PINDICATORHEIGHT + *PINDICATORGAP + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0); - m_barWidth = *PSTACKED ? ASSIGNEDBOX.w : (ASSIGNEDBOX.w - *PINNERGAP * (barsToDraw - 1)) / barsToDraw; - m_barHeight = *PSTACKED ? ((ASSIGNEDBOX.h - *POUTERGAP * *PKEEPUPPERGAP) - *POUTERGAP * (barsToDraw)) / barsToDraw : ASSIGNEDBOX.h - *POUTERGAP * *PKEEPUPPERGAP; + const auto ONEBARHEIGHT = *POUTERGAP + *PINDICATORHEIGHT + (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0); + m_fBarWidth = *PSTACKED ? ASSIGNEDBOX.w : (ASSIGNEDBOX.w - *PINNERGAP * (barsToDraw - 1)) / barsToDraw; + m_fBarHeight = *PSTACKED ? ((ASSIGNEDBOX.h - 2 - *POUTERGAP) - *POUTERGAP * (barsToDraw)) / barsToDraw : ASSIGNEDBOX.h - *POUTERGAP; - const auto DESIREDHEIGHT = *PSTACKED ? (ONEBARHEIGHT * m_dwGroupMembers.size()) + *POUTERGAP * *PKEEPUPPERGAP : *POUTERGAP * (1 + *PKEEPUPPERGAP) + ONEBARHEIGHT; + const auto DESIREDHEIGHT = *PSTACKED ? (ONEBARHEIGHT * m_dwGroupMembers.size()) + 2 + *POUTERGAP : *POUTERGAP * 2L + ONEBARHEIGHT; if (DESIREDHEIGHT != ASSIGNEDBOX.h) g_pDecorationPositioner->repositionDeco(this); float xoff = 0; float yoff = 0; - bool blur = *PBLUR != 0; - for (int i = 0; i < barsToDraw; ++i) { const auto WINDOWINDEX = *PSTACKED ? m_dwGroupMembers.size() - i - 1 : i; - CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->m_position.x + m_window->m_floatingOffset.x, - ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - *PINDICATORHEIGHT - *POUTERGAP - pMonitor->m_position.y + m_window->m_floatingOffset.y, m_barWidth, + CBox rect = {ASSIGNEDBOX.x + floor(xoff) - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x, + ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - *PINDICATORHEIGHT - *POUTERGAP - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth, *PINDICATORHEIGHT}; - rect.scale(pMonitor->m_scale).round(); + rect.scale(pMonitor->scale); - const bool GROUPLOCKED = m_window->m_group->locked() || g_pKeybindManager->m_groupsLocked; + const bool GROUPLOCKED = m_pWindow->getGroupHead()->m_sGroupData.locked || g_pKeybindManager->m_bGroupsLocked; const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE; const auto* const PCOLINACTIVE = GROUPLOCKED ? GROUPCOLINACTIVELOCKED : GROUPCOLINACTIVE; - CHyprColor color = m_dwGroupMembers[WINDOWINDEX].lock() == Desktop::focusState()->window() ? PCOLACTIVE->m_colors[0] : PCOLINACTIVE->m_colors[0]; + CHyprColor color = m_dwGroupMembers[WINDOWINDEX].lock() == g_pCompositor->m_pLastWindow.lock() ? PCOLACTIVE->m_vColors[0] : PCOLINACTIVE->m_vColors[0]; color.a *= a; if (!rect.empty()) { CRectPassElement::SRectData rectdata; rectdata.color = color; - rectdata.blur = blur; rectdata.box = rect; if (*PROUNDING) { - rectdata.round = *PROUNDING; - rectdata.roundingPower = *PROUNDINGPOWER; if (*PROUNDONLYEDGES) { - rectdata.round = 0; - const double offset = *PROUNDING * 2; - if (i == 0) { + static constexpr double PADDING = 20; + + if (i == 0 && barsToDraw == 1) + rectdata.round = *PROUNDING; + else if (i == 0) { + double first = rect.w - (*PROUNDING * 2); rectdata.round = *PROUNDING; - rectdata.clipBox = rect; - rectdata.box = CBox{rect.pos(), Vector2D{rect.w + offset, rect.h}}; + rectdata.clipBox = CBox{rect.pos() - Vector2D{PADDING, 0.F}, Vector2D{first + PADDING, rect.h}}; + g_pHyprRenderer->m_sRenderPass.add(makeShared(rectdata)); + rectdata.round = 0; + rectdata.clipBox = CBox{rect.pos() + Vector2D{first, 0.F}, Vector2D{rect.w - first + PADDING, rect.h}}; } else if (i == barsToDraw - 1) { + double first = *PROUNDING * 2; + rectdata.round = 0; + rectdata.clipBox = CBox{rect.pos() - Vector2D{PADDING, 0.F}, Vector2D{first + PADDING, rect.h}}; + g_pHyprRenderer->m_sRenderPass.add(makeShared(rectdata)); rectdata.round = *PROUNDING; - rectdata.clipBox = rect; - rectdata.box = CBox{rect.pos() - Vector2D{offset, 0.F}, Vector2D{rect.w + offset, rect.h}}; + rectdata.clipBox = CBox{rect.pos() + Vector2D{first, 0.F}, Vector2D{rect.w - first + PADDING, rect.h}}; } - } + } else + rectdata.round = *PROUNDING; } - g_pHyprRenderer->m_renderPass.add(makeUnique(rectdata)); + g_pHyprRenderer->m_sRenderPass.add(makeShared(rectdata)); } - rect = {ASSIGNEDBOX.x + xoff - pMonitor->m_position.x + m_window->m_floatingOffset.x, - ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - ONEBARHEIGHT - pMonitor->m_position.y + m_window->m_floatingOffset.y, m_barWidth, + rect = {ASSIGNEDBOX.x + floor(xoff) - pMonitor->vecPosition.x + m_pWindow->m_vFloatingOffset.x, + ASSIGNEDBOX.y + ASSIGNEDBOX.h - floor(yoff) - ONEBARHEIGHT - pMonitor->vecPosition.y + m_pWindow->m_vFloatingOffset.y, m_fBarWidth, (*PGRADIENTS || *PRENDERTITLES ? *PHEIGHT : 0)}; - rect.scale(pMonitor->m_scale); + rect.scale(pMonitor->scale); if (!rect.empty()) { if (*PGRADIENTS) { - const auto GRADIENTTEX = (m_dwGroupMembers[WINDOWINDEX] == Desktop::focusState()->window() ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : - (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); - if (GRADIENTTEX->ok()) { + const auto GRADIENTTEX = (m_dwGroupMembers[WINDOWINDEX] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : + (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); + if (GRADIENTTEX->m_iTexID) { CTexPassElement::SRenderData data; - data.tex = GRADIENTTEX; - data.blur = blur; - data.box = rect; - data.a = a; + data.tex = GRADIENTTEX; + data.box = rect; if (*PGRADIENTROUNDING) { - data.round = *PGRADIENTROUNDING; - data.roundingPower = *PGRADIENTROUNDINGPOWER; if (*PGRADIENTROUNDINGONLYEDGES) { - data.round = 0; - const double offset = *PGRADIENTROUNDING * 2; - if (i == 0) { + static constexpr double PADDING = 20; + + if (i == 0 && barsToDraw == 1) + data.round = *PGRADIENTROUNDING; + else if (i == 0) { + double first = rect.w - (*PGRADIENTROUNDING * 2); data.round = *PGRADIENTROUNDING; - data.clipBox = rect; - data.box = CBox{rect.pos(), Vector2D{rect.w + offset, rect.h}}; + data.clipBox = CBox{rect.pos() - Vector2D{PADDING, 0.F}, Vector2D{first + PADDING, rect.h}}; + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); + data.round = 0; + data.clipBox = CBox{rect.pos() + Vector2D{first, 0.F}, Vector2D{rect.w - first + PADDING, rect.h}}; } else if (i == barsToDraw - 1) { + double first = *PGRADIENTROUNDING * 2; + data.round = 0; + data.clipBox = CBox{rect.pos() - Vector2D{PADDING, 0.F}, Vector2D{first + PADDING, rect.h}}; + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); data.round = *PGRADIENTROUNDING; - data.clipBox = rect; - data.box = CBox{rect.pos() - Vector2D{offset, 0.F}, Vector2D{rect.w + offset, rect.h}}; + data.clipBox = CBox{rect.pos() + Vector2D{first, 0.F}, Vector2D{rect.w - first + PADDING, rect.h}}; } - } + } else + data.round = *PGRADIENTROUNDING; } - g_pHyprRenderer->m_renderPass.add(makeUnique(data)); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } } if (*PRENDERTITLES) { - CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[WINDOWINDEX]->m_title); + CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[WINDOWINDEX]->m_szTitle); if (!pTitleTex) pTitleTex = - m_titleTexs.titleTexs - .emplace_back(makeUnique( - m_dwGroupMembers[WINDOWINDEX].lock(), - Vector2D{(m_barWidth - (*PTEXTPADDING * 2)) * pMonitor->m_scale, (*PTITLEFONTSIZE + 2L * BAR_TEXT_PAD) * pMonitor->m_scale}, pMonitor->m_scale)) + m_sTitleTexs.titleTexs + .emplace_back(makeUnique(m_dwGroupMembers[WINDOWINDEX].lock(), + Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2L * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale)) .get(); - - SP titleTex; - if (m_dwGroupMembers[WINDOWINDEX] == Desktop::focusState()->window()) - titleTex = GROUPLOCKED ? pTitleTex->m_texLockedActive : pTitleTex->m_texActive; - else - titleTex = GROUPLOCKED ? pTitleTex->m_texLockedInactive : pTitleTex->m_texInactive; - - rect.y += std::ceil(((rect.height - titleTex->m_size.y) / 2.0) - (*PTEXTOFFSET * pMonitor->m_scale)); - rect.height = titleTex->m_size.y; - rect.width = titleTex->m_size.x; - rect.x += std::round((((m_barWidth + *PTEXTPADDING) * pMonitor->m_scale) / 2.0) - ((titleTex->m_size.x + *PTEXTPADDING) / 2.0)); + rect.y += std::ceil((rect.height - pTitleTex->texSize.y) / 2.0); + rect.height = pTitleTex->texSize.y; + rect.width = pTitleTex->texSize.x; + rect.x += std::round(((m_fBarWidth * pMonitor->scale) / 2.0) - (pTitleTex->texSize.x / 2.0)); rect.round(); CTexPassElement::SRenderData data; - data.tex = titleTex; + data.tex = pTitleTex->tex; data.box = rect; data.a = a; - g_pHyprRenderer->m_renderPass.add(makeUnique(std::move(data))); + g_pHyprRenderer->m_sRenderPass.add(makeShared(data)); } } if (*PSTACKED) yoff += ONEBARHEIGHT; else - xoff += *PINNERGAP + m_barWidth; + xoff += *PINNERGAP + m_fBarWidth; } if (*PRENDERTITLES) @@ -274,8 +257,8 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) { } CTitleTex* CHyprGroupBarDecoration::textureFromTitle(const std::string& title) { - for (auto const& tex : m_titleTexs.titleTexs) { - if (tex->m_content == title) + for (auto const& tex : m_sTitleTexs.titleTexs) { + if (tex->szContent == title) return tex.get(); } @@ -283,45 +266,30 @@ CTitleTex* CHyprGroupBarDecoration::textureFromTitle(const std::string& title) { } void CHyprGroupBarDecoration::invalidateTextures() { - m_titleTexs.titleTexs.clear(); + m_sTitleTexs.titleTexs.clear(); } -CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale) : m_content(pWindow->m_title), m_windowOwner(pWindow) { - static auto FALLBACKFONT = CConfigValue("misc:font_family"); - static auto PTITLEFONTFAMILY = CConfigValue("group:groupbar:font_family"); - static auto PTITLEFONTSIZE = CConfigValue("group:groupbar:font_size"); - static auto PTEXTCOLORACTIVE = CConfigValue("group:groupbar:text_color"); - static auto PTEXTCOLORINACTIVE = CConfigValue("group:groupbar:text_color_inactive"); - static auto PTEXTCOLORLOCKEDACTIVE = CConfigValue("group:groupbar:text_color_locked_active"); - static auto PTEXTCOLORLOCKEDINACTIVE = CConfigValue("group:groupbar:text_color_locked_inactive"); - - static auto PTITLEFONTWEIGHTACTIVE = CConfigValue("group:groupbar:font_weight_active"); - static auto PTITLEFONTWEIGHTINACTIVE = CConfigValue("group:groupbar:font_weight_inactive"); - - const auto FONTWEIGHTACTIVE = sc((PTITLEFONTWEIGHTACTIVE.ptr())->getData()); - const auto FONTWEIGHTINACTIVE = sc((PTITLEFONTWEIGHTINACTIVE.ptr())->getData()); - - const CHyprColor COLORACTIVE = CHyprColor(*PTEXTCOLORACTIVE); - const CHyprColor COLORINACTIVE = *PTEXTCOLORINACTIVE == -1 ? COLORACTIVE : CHyprColor(*PTEXTCOLORINACTIVE); - const CHyprColor COLORLOCKEDACTIVE = *PTEXTCOLORLOCKEDACTIVE == -1 ? COLORACTIVE : CHyprColor(*PTEXTCOLORLOCKEDACTIVE); - const CHyprColor COLORLOCKEDINACTIVE = *PTEXTCOLORLOCKEDINACTIVE == -1 ? COLORINACTIVE : CHyprColor(*PTEXTCOLORLOCKEDINACTIVE); +CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale) : szContent(pWindow->m_szTitle), pWindowOwner(pWindow) { + static auto FALLBACKFONT = CConfigValue("misc:font_family"); + static auto PTITLEFONTFAMILY = CConfigValue("group:groupbar:font_family"); + static auto PTITLEFONTSIZE = CConfigValue("group:groupbar:font_size"); + static auto PTEXTCOLOR = CConfigValue("group:groupbar:text_color"); + const CHyprColor COLOR = CHyprColor(*PTEXTCOLOR); const auto FONTFAMILY = *PTITLEFONTFAMILY != STRVAL_EMPTY ? *PTITLEFONTFAMILY : *FALLBACKFONT; -#define RENDER_TEXT(color, weight) g_pHyprOpenGL->renderText(pWindow->m_title, (color), *PTITLEFONTSIZE* monitorScale, false, FONTFAMILY, bufferSize.x - 2, (weight)); - m_texActive = RENDER_TEXT(COLORACTIVE, FONTWEIGHTACTIVE->m_value); - m_texInactive = RENDER_TEXT(COLORINACTIVE, FONTWEIGHTINACTIVE->m_value); - m_texLockedActive = RENDER_TEXT(COLORLOCKEDACTIVE, FONTWEIGHTACTIVE->m_value); - m_texLockedInactive = RENDER_TEXT(COLORLOCKEDINACTIVE, FONTWEIGHTINACTIVE->m_value); -#undef RENDER_TEXT + tex = g_pHyprOpenGL->renderText(pWindow->m_szTitle, COLOR, *PTITLEFONTSIZE, false, FONTFAMILY, bufferSize.x - 2 /* some padding yk */); + + if (tex) + texSize = tex->m_vSize; } -static void renderGradientTo(SP tex, CGradientValueData* grad) { +static void renderGradientTo(SP tex, CGradientValueData* grad) { - if (!Desktop::focusState()->monitor()) + if (!g_pCompositor->m_pLastMonitor) return; - const Vector2D& bufferSize = Desktop::focusState()->monitor()->m_pixelSize; + const Vector2D& bufferSize = g_pCompositor->m_pLastMonitor->vecPixelSize; const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); const auto CAIRO = cairo_create(CAIROSURFACE); @@ -335,9 +303,9 @@ static void renderGradientTo(SP tex, CGradientValueData* grad) { cairo_pattern_t* pattern; pattern = cairo_pattern_create_linear(0, 0, 0, bufferSize.y); - for (unsigned long i = 0; i < grad->m_colors.size(); i++) { - cairo_pattern_add_color_stop_rgba(pattern, 1 - sc(i + 1) / (grad->m_colors.size() + 1), grad->m_colors[i].r, grad->m_colors[i].g, grad->m_colors[i].b, - grad->m_colors[i].a); + for (unsigned long i = 0; i < grad->m_vColors.size(); i++) { + cairo_pattern_add_color_stop_rgba(pattern, 1 - (double)(i + 1) / (grad->m_vColors.size() + 1), grad->m_vColors[i].r, grad->m_vColors[i].g, grad->m_vColors[i].b, + grad->m_vColors[i].a); } cairo_rectangle(CAIRO, 0, 0, bufferSize.x, bufferSize.y); @@ -348,7 +316,18 @@ static void renderGradientTo(SP tex, CGradientValueData* grad) { cairo_surface_flush(CAIROSURFACE); // copy the data to an OpenGL texture we have - tex = g_pHyprRenderer->createTexture(CAIROSURFACE); + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + tex->allocate(); + glBindTexture(GL_TEXTURE_2D, tex->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, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); // delete cairo cairo_destroy(CAIRO); @@ -363,16 +342,18 @@ void refreshGroupBarGradients() { static auto PGROUPCOLINACTIVE = CConfigValue("group:groupbar:col.inactive"); static auto PGROUPCOLACTIVELOCKED = CConfigValue("group:groupbar:col.locked_active"); static auto PGROUPCOLINACTIVELOCKED = CConfigValue("group:groupbar:col.locked_inactive"); - auto* const GROUPCOLACTIVE = sc((PGROUPCOLACTIVE.ptr())->getData()); - auto* const GROUPCOLINACTIVE = sc((PGROUPCOLINACTIVE.ptr())->getData()); - auto* const GROUPCOLACTIVELOCKED = sc((PGROUPCOLACTIVELOCKED.ptr())->getData()); - auto* const GROUPCOLINACTIVELOCKED = sc((PGROUPCOLINACTIVELOCKED.ptr())->getData()); + auto* const GROUPCOLACTIVE = (CGradientValueData*)(PGROUPCOLACTIVE.ptr())->getData(); + auto* const GROUPCOLINACTIVE = (CGradientValueData*)(PGROUPCOLINACTIVE.ptr())->getData(); + auto* const GROUPCOLACTIVELOCKED = (CGradientValueData*)(PGROUPCOLACTIVELOCKED.ptr())->getData(); + auto* const GROUPCOLINACTIVELOCKED = (CGradientValueData*)(PGROUPCOLINACTIVELOCKED.ptr())->getData(); - if (m_tGradientActive && m_tGradientActive->ok()) { - m_tGradientActive.reset(); - m_tGradientInactive.reset(); - m_tGradientLockedActive.reset(); - m_tGradientLockedInactive.reset(); + g_pHyprRenderer->makeEGLCurrent(); + + if (m_tGradientActive->m_iTexID != 0) { + m_tGradientActive->destroyTexture(); + m_tGradientInactive->destroyTexture(); + m_tGradientLockedActive->destroyTexture(); + m_tGradientLockedInactive->destroyTexture(); } if (!*PENABLED || !*PGRADIENTS) @@ -388,46 +369,108 @@ bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) { static auto PSTACKED = CConfigValue("group:groupbar:stacked"); static auto POUTERGAP = CConfigValue("group:groupbar:gaps_out"); static auto PINNERGAP = CConfigValue("group:groupbar:gaps_in"); - if (m_window->m_group->size() == 1) + if (m_pWindow.lock() == m_pWindow->m_sGroupData.pNextWindow.lock()) return false; const float BARRELATIVEX = pos.x - assignedBoxGlobal().x; const float BARRELATIVEY = pos.y - assignedBoxGlobal().y; - const int WINDOWINDEX = *PSTACKED ? (BARRELATIVEY / (m_barHeight + *POUTERGAP)) : (BARRELATIVEX) / (m_barWidth + *PINNERGAP); + const int WINDOWINDEX = *PSTACKED ? (BARRELATIVEY / (m_fBarHeight + *POUTERGAP)) : (BARRELATIVEX) / (m_fBarWidth + *PINNERGAP); - if (!*PSTACKED && (BARRELATIVEX - (m_barWidth + *PINNERGAP) * WINDOWINDEX > m_barWidth)) + if (!*PSTACKED && (BARRELATIVEX - (m_fBarWidth + *PINNERGAP) * WINDOWINDEX > m_fBarWidth)) return false; - if (*PSTACKED && (BARRELATIVEY - (m_barHeight + *POUTERGAP) * WINDOWINDEX < *POUTERGAP)) + if (*PSTACKED && (BARRELATIVEY - (m_fBarHeight + *POUTERGAP) * WINDOWINDEX < *POUTERGAP)) return false; - PHLWINDOW pWindow = m_window->m_group->fromIndex(WINDOWINDEX); + PHLWINDOW pWindow = m_pWindow->getGroupWindowByIndex(WINDOWINDEX); - const auto& GROUP = m_window->m_group; + // hack + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); + if (!pWindow->m_bIsFloating) { + const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_bGroupsLocked; + g_pKeybindManager->m_bGroupsLocked = true; + g_pLayoutManager->getCurrentLayout()->onWindowCreated(pWindow); + g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV; + } - // remove the window from the group - GROUP->remove(pWindow); - - // start a move drag on it - g_layoutManager->dragController()->dragBegin(pWindow->layoutTarget(), MBIND_MOVE); + g_pInputManager->currentlyDraggedWindow = pWindow; if (!g_pCompositor->isWindowActive(pWindow)) - Desktop::focusState()->rawWindowFocus(pWindow, Desktop::FOCUS_REASON_CLICK); + g_pCompositor->focusWindow(pWindow); return true; } bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(const Vector2D& pos, PHLWINDOW pDraggedWindow) { + static auto PSTACKED = CConfigValue("group:groupbar:stacked"); static auto PDRAGINTOGROUP = CConfigValue("group:drag_into_group"); static auto PMERGEFLOATEDINTOTILEDONGROUPBAR = CConfigValue("group:merge_floated_into_tiled_on_groupbar"); static auto PMERGEGROUPSONGROUPBAR = CConfigValue("group:merge_groups_on_groupbar"); - const bool FLOATEDINTOTILED = !m_window->m_isFloating && !g_layoutManager->dragController()->draggingTiled(); + static auto POUTERGAP = CConfigValue("group:groupbar:gaps_out"); + static auto PINNERGAP = CConfigValue("group:groupbar:gaps_in"); + const bool FLOATEDINTOTILED = !m_pWindow->m_bIsFloating && !pDraggedWindow->m_bDraggingTiled; - if (!pDraggedWindow->canBeGroupedInto(m_window->m_group) || (*PDRAGINTOGROUP != 1 && *PDRAGINTOGROUP != 2) || (FLOATEDINTOTILED && !*PMERGEFLOATEDINTOTILEDONGROUPBAR) || - (!*PMERGEGROUPSONGROUPBAR && pDraggedWindow->m_group)) + g_pInputManager->m_bWasDraggingWindow = false; + + if (!pDraggedWindow->canBeGroupedInto(m_pWindow.lock()) || (*PDRAGINTOGROUP != 1 && *PDRAGINTOGROUP != 2) || (FLOATEDINTOTILED && !*PMERGEFLOATEDINTOTILEDONGROUPBAR) || + (!*PMERGEGROUPSONGROUPBAR && pDraggedWindow->m_sGroupData.pNextWindow.lock() && m_pWindow->m_sGroupData.pNextWindow.lock())) { + g_pInputManager->m_bWasDraggingWindow = true; return false; + } - m_window->m_group->add(pDraggedWindow); + const float BARRELATIVE = *PSTACKED ? pos.y - assignedBoxGlobal().y - (m_fBarHeight + *POUTERGAP) / 2 : pos.x - assignedBoxGlobal().x - m_fBarWidth / 2; + const float BARSIZE = *PSTACKED ? m_fBarHeight + *POUTERGAP : m_fBarWidth + *PINNERGAP; + const int WINDOWINDEX = BARRELATIVE < 0 ? -1 : BARRELATIVE / BARSIZE; + + PHLWINDOW pWindowInsertAfter = m_pWindow->getGroupWindowByIndex(WINDOWINDEX); + PHLWINDOW pWindowInsertEnd = pWindowInsertAfter->m_sGroupData.pNextWindow.lock(); + PHLWINDOW pDraggedHead = pDraggedWindow->m_sGroupData.pNextWindow.lock() ? pDraggedWindow->getGroupHead() : pDraggedWindow; + + if (!pDraggedWindow->m_sGroupData.pNextWindow.expired()) { + + // stores group data + std::vector members; + PHLWINDOW curr = pDraggedHead; + const bool WASLOCKED = pDraggedHead->m_sGroupData.locked; + do { + members.push_back(curr); + curr = curr->m_sGroupData.pNextWindow.lock(); + } while (curr != members[0]); + + // removes all windows + for (const PHLWINDOW& w : members) { + w->m_sGroupData.pNextWindow.reset(); + w->m_sGroupData.head = false; + w->m_sGroupData.locked = false; + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(w); + } + + // restores the group + for (auto it = members.begin(); it != members.end(); ++it) { + (*it)->m_bIsFloating = pWindowInsertAfter->m_bIsFloating; // match the floating state of group members + *(*it)->m_vRealSize = pWindowInsertAfter->m_vRealSize->goal(); // match the size of group members + *(*it)->m_vRealPosition = pWindowInsertAfter->m_vRealPosition->goal(); // match the position of group members + if (std::next(it) != members.end()) + (*it)->m_sGroupData.pNextWindow = *std::next(it); + else + (*it)->m_sGroupData.pNextWindow = members[0]; + } + members[0]->m_sGroupData.head = true; + members[0]->m_sGroupData.locked = WASLOCKED; + } else + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pDraggedWindow); + + pDraggedWindow->m_bIsFloating = pWindowInsertAfter->m_bIsFloating; // match the floating state of the window + + pWindowInsertAfter->insertWindowToGroup(pDraggedWindow); + + if (WINDOWINDEX == -1) + std::swap(pDraggedHead->m_sGroupData.head, pWindowInsertEnd->m_sGroupData.head); + + m_pWindow->setGroupCurrent(pDraggedWindow); + pDraggedWindow->applyGroupRules(); + pDraggedWindow->updateWindowDecos(); + g_pLayoutManager->getCurrentLayout()->recalculateWindow(pDraggedWindow); if (!pDraggedWindow->getDecorationByType(DECORATION_GROUPBAR)) pDraggedWindow->addWindowDeco(makeUnique(pDraggedWindow)); @@ -439,12 +482,12 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo static auto PSTACKED = CConfigValue("group:groupbar:stacked"); static auto POUTERGAP = CConfigValue("group:groupbar:gaps_out"); static auto PINNERGAP = CConfigValue("group:groupbar:gaps_in"); - if (m_window->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) + if (m_pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) return true; const float BARRELATIVEX = pos.x - assignedBoxGlobal().x; const float BARRELATIVEY = pos.y - assignedBoxGlobal().y; - const int WINDOWINDEX = *PSTACKED ? (BARRELATIVEY / (m_barHeight + *POUTERGAP)) : (BARRELATIVEX) / (m_barWidth + *PINNERGAP); + const int WINDOWINDEX = *PSTACKED ? (BARRELATIVEY / (m_fBarHeight + *POUTERGAP)) : (BARRELATIVEX) / (m_fBarWidth + *PINNERGAP); static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); // close window on middle click @@ -454,7 +497,7 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo if (e.state == WL_POINTER_BUTTON_STATE_PRESSED) pressedCursorPos = pos; else if (e.state == WL_POINTER_BUTTON_STATE_RELEASED && pressedCursorPos == pos) - g_pXWaylandManager->sendCloseWindow(m_window->m_group->fromIndex(WINDOWINDEX)); + g_pXWaylandManager->sendCloseWindow(m_pWindow->getGroupWindowByIndex(WINDOWINDEX)); return true; } @@ -463,23 +506,23 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo return true; // click on padding - const auto TABPAD = !*PSTACKED && (BARRELATIVEX - (m_barWidth + *PINNERGAP) * WINDOWINDEX > m_barWidth); - const auto STACKPAD = *PSTACKED && (BARRELATIVEY - (m_barHeight + *POUTERGAP) * WINDOWINDEX < *POUTERGAP); + const auto TABPAD = !*PSTACKED && (BARRELATIVEX - (m_fBarWidth + *PINNERGAP) * WINDOWINDEX > m_fBarWidth); + const auto STACKPAD = *PSTACKED && (BARRELATIVEY - (m_fBarHeight + *POUTERGAP) * WINDOWINDEX < *POUTERGAP); if (TABPAD || STACKPAD) { - if (!g_pCompositor->isWindowActive(m_window.lock())) - Desktop::focusState()->rawWindowFocus(m_window.lock(), Desktop::FOCUS_REASON_CLICK); + if (!g_pCompositor->isWindowActive(m_pWindow.lock())) + g_pCompositor->focusWindow(m_pWindow.lock()); return true; } - PHLWINDOW pWindow = m_window->m_group->fromIndex(WINDOWINDEX); + PHLWINDOW pWindow = m_pWindow->getGroupWindowByIndex(WINDOWINDEX); - if (pWindow != m_window) - pWindow->m_group->setCurrent(pWindow); + if (pWindow != m_pWindow) + pWindow->setGroupCurrent(pWindow); if (!g_pCompositor->isWindowActive(pWindow) && *PFOLLOWMOUSE != 3) - Desktop::focusState()->rawWindowFocus(pWindow, Desktop::FOCUS_REASON_CLICK); + g_pCompositor->focusWindow(pWindow); - if (pWindow->m_isFloating) + if (pWindow->m_bIsFloating) g_pCompositor->changeWindowZOrder(pWindow, true); return true; @@ -488,13 +531,13 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo bool CHyprGroupBarDecoration::onScrollOnDeco(const Vector2D& pos, const IPointer::SAxisEvent e) { static auto PGROUPBARSCROLLING = CConfigValue("group:groupbar:scrolling"); - if (!*PGROUPBARSCROLLING || !m_window->m_group) + if (!*PGROUPBARSCROLLING || m_pWindow->m_sGroupData.pNextWindow.expired()) return false; if (e.delta > 0) - m_window->m_group->moveCurrent(true); + m_pWindow->setGroupCurrent(m_pWindow->m_sGroupData.pNextWindow.lock()); else - m_window->m_group->moveCurrent(false); + m_pWindow->setGroupCurrent(m_pWindow->getGroupPrevious()); return true; } @@ -522,18 +565,13 @@ std::string CHyprGroupBarDecoration::getDisplayName() { } CBox CHyprGroupBarDecoration::assignedBoxGlobal() { - CBox box = m_assignedBox; - box.translate(g_pDecorationPositioner->getEdgeDefinedPoint(DECORATION_EDGE_TOP, m_window)); + CBox box = m_bAssignedBox; + box.translate(g_pDecorationPositioner->getEdgeDefinedPoint(DECORATION_EDGE_TOP, m_pWindow.lock())); - const auto PWORKSPACE = m_window->m_workspace; + const auto PWORKSPACE = m_pWindow->m_pWorkspace; - if (PWORKSPACE && !m_window->m_pinned) - box.translate(PWORKSPACE->m_renderOffset->value()); + if (PWORKSPACE && !m_pWindow->m_bPinned) + box.translate(PWORKSPACE->m_vRenderOffset->value()); - return box.round(); -} - -bool CHyprGroupBarDecoration::visible() { - static auto PENABLED = CConfigValue("group:groupbar:enabled"); - return *PENABLED && m_window->m_ruleApplicator->decorate().valueOrDefault(); + return box; } diff --git a/src/render/decorations/CHyprGroupBarDecoration.hpp b/src/render/decorations/CHyprGroupBarDecoration.hpp index 5c3f4ae5..0cdf8a6b 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.hpp +++ b/src/render/decorations/CHyprGroupBarDecoration.hpp @@ -12,13 +12,11 @@ class CTitleTex { CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale); ~CTitleTex() = default; - SP m_texActive; - SP m_texInactive; - SP m_texLockedActive; - SP m_texLockedInactive; - std::string m_content; + SP tex; + std::string szContent; + Vector2D texSize; - PHLWINDOWREF m_windowOwner; + PHLWINDOWREF pWindowOwner; }; void refreshGroupBarGradients(); @@ -49,22 +47,21 @@ class CHyprGroupBarDecoration : public IHyprWindowDecoration { virtual std::string getDisplayName(); private: - CBox m_assignedBox = {0}; + SBoxExtents m_seExtents; - PHLWINDOWREF m_window; + CBox m_bAssignedBox = {0}; + + PHLWINDOWREF m_pWindow; std::vector m_dwGroupMembers; - float m_barWidth; - float m_barHeight; - - bool m_bLastVisibilityStatus = true; + float m_fBarWidth; + float m_fBarHeight; CTitleTex* textureFromTitle(const std::string&); void invalidateTextures(); CBox assignedBoxGlobal(); - bool visible(); bool onBeginWindowDragOnDeco(const Vector2D&); bool onEndWindowDragOnDeco(const Vector2D&, PHLWINDOW); @@ -74,5 +71,5 @@ class CHyprGroupBarDecoration : public IHyprWindowDecoration { struct STitleTexs { // STitleTexs* overriden = nullptr; // TODO: make shit shared in-group to decrease VRAM usage. std::vector> titleTexs; - } m_titleTexs; + } m_sTitleTexs; }; diff --git a/src/render/decorations/DecorationPositioner.cpp b/src/render/decorations/DecorationPositioner.cpp index 470b5bb7..9dbb12a6 100644 --- a/src/render/decorations/DecorationPositioner.cpp +++ b/src/render/decorations/DecorationPositioner.cpp @@ -1,19 +1,21 @@ #include "DecorationPositioner.hpp" -#include "../../desktop/view/Window.hpp" -#include "../../layout/target/Target.hpp" -#include "../../event/EventBus.hpp" +#include "../../desktop/Window.hpp" +#include "../../managers/HookSystemManager.hpp" +#include "../../managers/LayoutManager.hpp" CDecorationPositioner::CDecorationPositioner() { - static auto P = Event::bus()->m_events.window.close.listen([this](PHLWINDOW window) { onWindowUnmap(window); }); - static auto P2 = Event::bus()->m_events.window.open.listen([this](PHLWINDOW window) { onWindowMap(window); }); + static auto P = g_pHookSystem->hookDynamic("closeWindow", [this](void* call, SCallbackInfo& info, std::any data) { + auto PWINDOW = std::any_cast(data); + this->onWindowUnmap(PWINDOW); + }); + + static auto P2 = g_pHookSystem->hookDynamic("openWindow", [this](void* call, SCallbackInfo& info, std::any data) { + auto PWINDOW = std::any_cast(data); + this->onWindowMap(PWINDOW); + }); } -Vector2D CDecorationPositioner::getEdgeDefinedPoint(uint32_t edges, PHLWINDOWREF pWindow) { - if (!pWindow) { - Log::logger->log(Log::ERR, "getEdgeDefinedPoint: invalid pWindow"); - return {}; - } - +Vector2D CDecorationPositioner::getEdgeDefinedPoint(uint32_t edges, PHLWINDOW pWindow) { const bool TOP = edges & DECORATION_EDGE_TOP; const bool BOTTOM = edges & DECORATION_EDGE_BOTTOM; const bool LEFT = edges & DECORATION_EDGE_LEFT; @@ -22,7 +24,7 @@ Vector2D CDecorationPositioner::getEdgeDefinedPoint(uint32_t edges, PHLWINDOWREF const int EDGESNO = TOP + BOTTOM + LEFT + RIGHT; if (EDGESNO == 0 || EDGESNO == 3 || EDGESNO > 4) { - Log::logger->log(Log::ERR, "getEdgeDefinedPoint: invalid number of edges"); + Debug::log(ERR, "getEdgeDefinedPoint: invalid number of edges"); return {}; } @@ -40,6 +42,7 @@ Vector2D CDecorationPositioner::getEdgeDefinedPoint(uint32_t edges, PHLWINDOWREF return wb.pos() + Vector2D{0.0, wb.size().y / 2.0}; else if (RIGHT) return wb.pos() + Vector2D{wb.size().x, wb.size().y / 2.0}; + UNREACHABLE(); } else { if (TOP && LEFT) return wb.pos(); @@ -49,16 +52,17 @@ Vector2D CDecorationPositioner::getEdgeDefinedPoint(uint32_t edges, PHLWINDOWREF return wb.pos() + wb.size(); if (BOTTOM && LEFT) return wb.pos() + Vector2D{0.0, wb.size().y}; + UNREACHABLE(); } - Log::logger->log(Log::ERR, "getEdgeDefinedPoint: invalid configuration of edges"); + UNREACHABLE(); return {}; } void CDecorationPositioner::uncacheDecoration(IHyprWindowDecoration* deco) { - std::erase_if(m_windowPositioningDatas, [&](const auto& data) { return !data->pWindow.lock() || data->pDecoration == deco; }); + std::erase_if(m_vWindowPositioningDatas, [&](const auto& data) { return !data->pWindow.lock() || data->pDecoration == deco; }); - const auto WIT = std::ranges::find_if(m_windowDatas, [&](const auto& other) { return other.first.lock() == deco->m_window.lock(); }); - if (WIT == m_windowDatas.end()) + const auto WIT = std::find_if(m_mWindowDatas.begin(), m_mWindowDatas.end(), [&](const auto& other) { return other.first.lock() == deco->m_pWindow.lock(); }); + if (WIT == m_mWindowDatas.end()) return; WIT->second.needsRecalc = true; @@ -66,16 +70,16 @@ void CDecorationPositioner::uncacheDecoration(IHyprWindowDecoration* deco) { void CDecorationPositioner::repositionDeco(IHyprWindowDecoration* deco) { uncacheDecoration(deco); - onWindowUpdate(deco->m_window.lock()); + onWindowUpdate(deco->m_pWindow.lock()); } CDecorationPositioner::SWindowPositioningData* CDecorationPositioner::getDataFor(IHyprWindowDecoration* pDecoration, PHLWINDOW pWindow) { - auto it = std::ranges::find_if(m_windowPositioningDatas, [&](const auto& el) { return el->pDecoration == pDecoration; }); + auto it = std::find_if(m_vWindowPositioningDatas.begin(), m_vWindowPositioningDatas.end(), [&](const auto& el) { return el->pDecoration == pDecoration; }); - if (it != m_windowPositioningDatas.end()) + if (it != m_vWindowPositioningDatas.end()) return it->get(); - const auto DATA = m_windowPositioningDatas.emplace_back(makeUnique(pWindow, pDecoration)).get(); + const auto DATA = m_vWindowPositioningDatas.emplace_back(makeUnique(pWindow, pDecoration)).get(); DATA->positioningInfo = pDecoration->getPositioningInfo(); @@ -83,19 +87,20 @@ CDecorationPositioner::SWindowPositioningData* CDecorationPositioner::getDataFor } void CDecorationPositioner::sanitizeDatas() { - std::erase_if(m_windowDatas, [](const auto& other) { return !valid(other.first); }); - std::erase_if(m_windowPositioningDatas, [](const auto& other) { + std::erase_if(m_mWindowDatas, [](const auto& other) { return !valid(other.first); }); + std::erase_if(m_vWindowPositioningDatas, [](const auto& other) { if (!validMapped(other->pWindow)) return true; - if (std::ranges::find_if(other->pWindow->m_windowDecorations, [&](const auto& el) { return el.get() == other->pDecoration; }) == other->pWindow->m_windowDecorations.end()) + if (std::find_if(other->pWindow->m_dWindowDecorations.begin(), other->pWindow->m_dWindowDecorations.end(), + [&](const auto& el) { return el.get() == other->pDecoration; }) == other->pWindow->m_dWindowDecorations.end()) return true; return false; }); } void CDecorationPositioner::forceRecalcFor(PHLWINDOW pWindow) { - const auto WIT = std::ranges::find_if(m_windowDatas, [&](const auto& other) { return other.first.lock() == pWindow; }); - if (WIT == m_windowDatas.end()) + const auto WIT = std::find_if(m_mWindowDatas.begin(), m_mWindowDatas.end(), [&](const auto& other) { return other.first.lock() == pWindow; }); + if (WIT == m_mWindowDatas.end()) return; const auto WINDOWDATA = &WIT->second; @@ -107,8 +112,8 @@ void CDecorationPositioner::onWindowUpdate(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return; - const auto WIT = std::ranges::find_if(m_windowDatas, [&](const auto& other) { return other.first.lock() == pWindow; }); - if (WIT == m_windowDatas.end()) + const auto WIT = std::find_if(m_mWindowDatas.begin(), m_mWindowDatas.end(), [&](const auto& other) { return other.first.lock() == pWindow; }); + if (WIT == m_mWindowDatas.end()) return; const auto WINDOWDATA = &WIT->second; @@ -118,28 +123,25 @@ void CDecorationPositioner::onWindowUpdate(PHLWINDOW pWindow) { // std::vector datas; // reserve to avoid reallocations - datas.reserve(pWindow->m_windowDecorations.size()); + datas.reserve(pWindow->m_dWindowDecorations.size()); - for (auto const& wd : pWindow->m_windowDecorations) { + for (auto const& wd : pWindow->m_dWindowDecorations) { datas.push_back(getDataFor(wd.get(), pWindow)); } - if (WINDOWDATA->lastWindowSize == pWindow->m_realSize->value() /* position not changed */ - && std::ranges::all_of(m_windowPositioningDatas, [pWindow](const auto& data) { return pWindow != data->pWindow.lock() || !data->needsReposition; }) + if (WINDOWDATA->lastWindowSize == pWindow->m_vRealSize->value() /* position not changed */ + && std::all_of(m_vWindowPositioningDatas.begin(), m_vWindowPositioningDatas.end(), + [pWindow](const auto& data) { return pWindow != data->pWindow.lock() || !data->needsReposition; }) /* all window datas are either not for this window or don't need a reposition */ && !WINDOWDATA->needsRecalc /* window doesn't need recalc */ ) return; - for (auto const& wd : datas) { - wd->positioningInfo = wd->pDecoration->getPositioningInfo(); - } - - WINDOWDATA->lastWindowSize = pWindow->m_realSize->value(); + WINDOWDATA->lastWindowSize = pWindow->m_vRealSize->value(); WINDOWDATA->needsRecalc = false; - const bool EPHEMERAL = pWindow->m_realSize->isBeingAnimated(); + const bool EPHEMERAL = pWindow->m_vRealSize->isBeingAnimated(); - std::ranges::sort(datas, [](const auto& a, const auto& b) { return a->positioningInfo.priority > b->positioningInfo.priority; }); + std::sort(datas.begin(), datas.end(), [](const auto& a, const auto& b) { return a->positioningInfo.priority > b->positioningInfo.priority; }); CBox wb = pWindow->getWindowMainSurfaceBox(); @@ -207,51 +209,51 @@ void CDecorationPositioner::onWindowUpdate(PHLWINDOW pWindow) { continue; } - const auto desiredExtents = wd->positioningInfo.desiredExtents; + auto desiredSize = 0; + if (LEFT) + desiredSize = wd->positioningInfo.desiredExtents.topLeft.x; + else if (RIGHT) + desiredSize = wd->positioningInfo.desiredExtents.bottomRight.x; + else if (TOP) + desiredSize = wd->positioningInfo.desiredExtents.topLeft.y; + else + desiredSize = wd->positioningInfo.desiredExtents.bottomRight.y; const auto EDGEPOINT = getEdgeDefinedPoint(wd->positioningInfo.edges, pWindow); Vector2D pos, size; if (EDGESNO == 4) { - stickyOffsetXL += desiredExtents.topLeft.x; - stickyOffsetXR += desiredExtents.bottomRight.x; - stickyOffsetYT += desiredExtents.topLeft.y; - stickyOffsetYB += desiredExtents.bottomRight.y; + pos = wb.pos() - EDGEPOINT - Vector2D{stickyOffsetXL + desiredSize, stickyOffsetYT + desiredSize}; + size = wb.size() + Vector2D{stickyOffsetXL + stickyOffsetXR + desiredSize * 2, stickyOffsetYB + stickyOffsetYT + desiredSize * 2}; - pos = wb.pos() - EDGEPOINT - Vector2D{stickyOffsetXL, stickyOffsetYT}; - size = wb.size() + Vector2D{stickyOffsetXL + stickyOffsetXR, stickyOffsetYB + stickyOffsetYT}; + stickyOffsetXL += desiredSize; + stickyOffsetXR += desiredSize; + stickyOffsetYT += desiredSize; + stickyOffsetYB += desiredSize; } else if (LEFT) { - const auto desiredSize = desiredExtents.topLeft.x; - pos = wb.pos() - EDGEPOINT - Vector2D{stickyOffsetXL, -stickyOffsetYT}; pos.x -= desiredSize; - size = {sc(desiredSize), wb.size().y + stickyOffsetYB + stickyOffsetYT}; + size = {(double)desiredSize, wb.size().y + stickyOffsetYB + stickyOffsetYT}; if (SOLID) stickyOffsetXL += desiredSize; } else if (RIGHT) { - const auto desiredSize = desiredExtents.bottomRight.x; - pos = wb.pos() + Vector2D{wb.size().x, 0.0} - EDGEPOINT + Vector2D{stickyOffsetXR, -stickyOffsetYT}; - size = {sc(desiredSize), wb.size().y + stickyOffsetYB + stickyOffsetYT}; + size = {(double)desiredSize, wb.size().y + stickyOffsetYB + stickyOffsetYT}; if (SOLID) stickyOffsetXR += desiredSize; } else if (TOP) { - const auto desiredSize = desiredExtents.topLeft.y; - pos = wb.pos() - EDGEPOINT - Vector2D{stickyOffsetXL, stickyOffsetYT}; pos.y -= desiredSize; - size = {wb.size().x + stickyOffsetXL + stickyOffsetXR, sc(desiredSize)}; + size = {wb.size().x + stickyOffsetXL + stickyOffsetXR, (double)desiredSize}; if (SOLID) stickyOffsetYT += desiredSize; } else { - const auto desiredSize = desiredExtents.bottomRight.y; - pos = wb.pos() + Vector2D{0.0, wb.size().y} - EDGEPOINT - Vector2D{stickyOffsetXL, stickyOffsetYB}; - size = {wb.size().x + stickyOffsetXL + stickyOffsetXR, sc(desiredSize)}; + size = {wb.size().x + stickyOffsetXL + stickyOffsetXR, (double)desiredSize}; if (SOLID) stickyOffsetYB += desiredSize; @@ -271,35 +273,35 @@ void CDecorationPositioner::onWindowUpdate(PHLWINDOW pWindow) { if (WINDOWDATA->extents != SBoxExtents{{stickyOffsetXL + reservedXL, stickyOffsetYT + reservedYT}, {stickyOffsetXR + reservedXR, stickyOffsetYB + reservedYB}}) { WINDOWDATA->extents = {{stickyOffsetXL + reservedXL, stickyOffsetYT + reservedYT}, {stickyOffsetXR + reservedXR, stickyOffsetYB + reservedYB}}; - pWindow->layoutTarget()->recalc(); + g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow); } } void CDecorationPositioner::onWindowUnmap(PHLWINDOW pWindow) { - std::erase_if(m_windowPositioningDatas, [&](const auto& data) { return data->pWindow.lock() == pWindow; }); - m_windowDatas.erase(pWindow); + std::erase_if(m_vWindowPositioningDatas, [&](const auto& data) { return data->pWindow.lock() == pWindow; }); + m_mWindowDatas.erase(pWindow); } void CDecorationPositioner::onWindowMap(PHLWINDOW pWindow) { - m_windowDatas[pWindow] = {}; + m_mWindowDatas[pWindow] = {}; } -SBoxExtents CDecorationPositioner::getWindowDecorationReserved(PHLWINDOWREF pWindow) { +SBoxExtents CDecorationPositioner::getWindowDecorationReserved(PHLWINDOW pWindow) { try { - const auto E = m_windowDatas.at(pWindow); + const auto E = m_mWindowDatas.at(pWindow); return E.reserved; } catch (std::out_of_range& e) { return {}; } } -SBoxExtents CDecorationPositioner::getWindowDecorationExtents(PHLWINDOWREF pWindow, bool inputOnly) { +SBoxExtents CDecorationPositioner::getWindowDecorationExtents(PHLWINDOW pWindow, bool inputOnly) { CBox const mainSurfaceBox = pWindow->getWindowMainSurfaceBox(); CBox accum = mainSurfaceBox; - for (auto const& data : m_windowPositioningDatas) { + for (auto const& data : m_vWindowPositioningDatas) { if (!data->pDecoration || (inputOnly && !(data->pDecoration->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))) continue; - auto const window = data->pWindow; + auto const window = data->pWindow.lock(); if (!window || window != pWindow) continue; @@ -343,7 +345,7 @@ SBoxExtents CDecorationPositioner::getWindowDecorationExtents(PHLWINDOWREF pWind CBox CDecorationPositioner::getBoxWithIncludedDecos(PHLWINDOW pWindow) { CBox accum = pWindow->getWindowMainSurfaceBox(); - for (auto const& data : m_windowPositioningDatas) { + for (auto const& data : m_vWindowPositioningDatas) { if (data->pWindow.lock() != pWindow) continue; @@ -379,7 +381,7 @@ CBox CDecorationPositioner::getBoxWithIncludedDecos(PHLWINDOW pWindow) { } CBox CDecorationPositioner::getWindowDecorationBox(IHyprWindowDecoration* deco) { - auto const window = deco->m_window.lock(); + auto const window = deco->m_pWindow.lock(); const auto DATA = getDataFor(deco, window); CBox box = DATA->lastReply.assignedGeometry; diff --git a/src/render/decorations/DecorationPositioner.hpp b/src/render/decorations/DecorationPositioner.hpp index 8fbb44c7..787c6d1f 100644 --- a/src/render/decorations/DecorationPositioner.hpp +++ b/src/render/decorations/DecorationPositioner.hpp @@ -6,6 +6,7 @@ #include "../../helpers/math/Math.hpp" #include "../../desktop/DesktopTypes.hpp" +class CWindow; class IHyprWindowDecoration; enum eDecorationPositioningPolicy : uint8_t { @@ -58,13 +59,13 @@ class CDecorationPositioner { public: CDecorationPositioner(); - Vector2D getEdgeDefinedPoint(uint32_t edges, PHLWINDOWREF pWindow); + Vector2D getEdgeDefinedPoint(uint32_t edges, PHLWINDOW pWindow); // called on resize, or insert/removal of a new deco void onWindowUpdate(PHLWINDOW pWindow); void uncacheDecoration(IHyprWindowDecoration* deco); - SBoxExtents getWindowDecorationReserved(PHLWINDOWREF pWindow); - SBoxExtents getWindowDecorationExtents(PHLWINDOWREF pWindow, bool inputOnly = false); + SBoxExtents getWindowDecorationReserved(PHLWINDOW pWindow); + SBoxExtents getWindowDecorationExtents(PHLWINDOW pWindow, bool inputOnly = false); CBox getBoxWithIncludedDecos(PHLWINDOW pWindow); void repositionDeco(IHyprWindowDecoration* deco); CBox getWindowDecorationBox(IHyprWindowDecoration* deco); @@ -86,8 +87,8 @@ class CDecorationPositioner { bool needsRecalc = false; }; - std::map m_windowDatas; - std::vector> m_windowPositioningDatas; + std::map m_mWindowDatas; + std::vector> m_vWindowPositioningDatas; SWindowPositioningData* getDataFor(IHyprWindowDecoration* pDecoration, PHLWINDOW pWindow); void onWindowUnmap(PHLWINDOW pWindow); @@ -95,4 +96,4 @@ class CDecorationPositioner { void sanitizeDatas(); }; -inline UP g_pDecorationPositioner; +inline UP g_pDecorationPositioner; \ No newline at end of file diff --git a/src/render/decorations/IHyprWindowDecoration.cpp b/src/render/decorations/IHyprWindowDecoration.cpp index 26782098..c71ee186 100644 --- a/src/render/decorations/IHyprWindowDecoration.cpp +++ b/src/render/decorations/IHyprWindowDecoration.cpp @@ -1,6 +1,8 @@ #include "IHyprWindowDecoration.hpp" -IHyprWindowDecoration::IHyprWindowDecoration(PHLWINDOW pWindow) : m_window(pWindow) { +class CWindow; + +IHyprWindowDecoration::IHyprWindowDecoration(PHLWINDOW pWindow) : m_pWindow(pWindow) { ; } diff --git a/src/render/decorations/IHyprWindowDecoration.hpp b/src/render/decorations/IHyprWindowDecoration.hpp index 9916d392..2703eb61 100644 --- a/src/render/decorations/IHyprWindowDecoration.hpp +++ b/src/render/decorations/IHyprWindowDecoration.hpp @@ -26,6 +26,7 @@ enum eDecorationFlags : uint8_t { DECORATION_NON_SOLID = 1 << 2, /* this decoration is not solid. Other decorations should draw on top of it. Example: shadow */ }; +class CWindow; class CMonitor; class CDecorationPositioner; @@ -55,7 +56,7 @@ class IHyprWindowDecoration { virtual std::string getDisplayName(); private: - PHLWINDOWREF m_window; + PHLWINDOWREF m_pWindow; friend class CDecorationPositioner; }; diff --git a/src/render/gl/GLFramebuffer.cpp b/src/render/gl/GLFramebuffer.cpp deleted file mode 100644 index d821f766..00000000 --- a/src/render/gl/GLFramebuffer.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "GLFramebuffer.hpp" -#include "../OpenGL.hpp" -#include "../Renderer.hpp" -#include "macros.hpp" -#include "render/Framebuffer.hpp" - -CGLFramebuffer::CGLFramebuffer() : IFramebuffer() {} -CGLFramebuffer::CGLFramebuffer(const std::string& name) : IFramebuffer(name) {} - -bool CGLFramebuffer::internalAlloc(int w, int h, uint32_t drmFormat) { - g_pHyprRenderer->makeEGLCurrent(); - - bool firstAlloc = false; - - if (!m_tex) { - m_tex = g_pHyprRenderer->createTexture(); - m_tex->allocate({w, h}); - m_tex->bind(); - m_tex->setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - m_tex->setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - m_tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - m_tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - firstAlloc = true; - } - - if (!m_fbAllocated) { - glGenFramebuffers(1, &m_fb); - m_fbAllocated = true; - firstAlloc = true; - } - - if (firstAlloc) { - const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); - m_tex->bind(); - glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, w, h, 0, format->glFormat, format->glType, nullptr); - glBindFramebuffer(GL_FRAMEBUFFER, m_fb); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex->m_texID, 0); - - if (m_stencilTex && m_stencilTex->ok()) { - m_stencilTex->bind(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, w, h, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_stencilTex->m_texID, 0); - - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - } - - auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - RASSERT((status == GL_FRAMEBUFFER_COMPLETE), "Framebuffer incomplete, couldn't create! (FB status: {}, GL Error: 0x{:x})", status, sc(glGetError())); - - if (m_stencilTex && m_stencilTex->ok()) - m_stencilTex->unbind(); - - Log::logger->log(Log::DEBUG, "Framebuffer created, status {}", status); - } - - glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - return true; -} - -void CGLFramebuffer::addStencil(SP tex) { - if (m_stencilTex == tex) - return; - - RASSERT(!m_fbAllocated, "Should add stencil tex prior to FB allocation") - m_stencilTex = tex; -} - -void CGLFramebuffer::bind() { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fb); - - if (g_pHyprOpenGL) - g_pHyprOpenGL->setViewport(0, 0, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.x, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize.y); - else - glViewport(0, 0, m_size.x, m_size.y); -} - -void CGLFramebuffer::unbind() { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); -} - -void CGLFramebuffer::release() { - if (m_fbAllocated) { - glBindFramebuffer(GL_FRAMEBUFFER, m_fb); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - glDeleteFramebuffers(1, &m_fb); - m_fbAllocated = false; - m_fb = 0; - } - - if (m_tex) - m_tex.reset(); - - m_size = Vector2D(); -} - -bool CGLFramebuffer::readPixels(CHLBufferReference buffer, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height) { - auto shm = buffer->shm(); - auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0); // no need for end, cuz it's shm - - const auto PFORMAT = NFormatUtils::getPixelFormatFromDRM(shm.format); - if (!PFORMAT) { - LOGM(Log::ERR, "Can't copy: failed to find a pixel format"); - return false; - } - - g_pHyprRenderer->makeEGLCurrent(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, getFBID()); - bind(); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - uint32_t packStride = NFormatUtils::minStride(PFORMAT, m_size.x); - int glFormat = PFORMAT->glFormat; - - if (glFormat == GL_RGBA) - glFormat = GL_BGRA_EXT; - - if (glFormat != GL_BGRA_EXT && glFormat != GL_RGB) { - if (PFORMAT->swizzle.has_value()) { - std::array RGBA = SWIZZLE_RGBA; - std::array BGRA = SWIZZLE_BGRA; - if (PFORMAT->swizzle == RGBA) - glFormat = GL_RGBA; - else if (PFORMAT->swizzle == BGRA) - glFormat = GL_BGRA_EXT; - else { - LOGM(Log::ERR, "Copied frame via shm might be broken or color flipped"); - glFormat = GL_RGBA; - } - } - } - - // This could be optimized by using a pixel buffer object to make this async, - // but really clients should just use a dma buffer anyways. - if (packStride == sc(shm.stride)) { - glReadPixels(offsetX, offsetY, width > 0 ? width : m_size.x, height > 0 ? height : m_size.y, glFormat, PFORMAT->glType, pixelData); - } else { - const auto h = height > 0 ? height : m_size.y; - for (size_t i = 0; i < h; ++i) { - uint32_t y = i; - glReadPixels(offsetX, offsetY + y, width > 0 ? width : m_size.x, 1, glFormat, PFORMAT->glType, pixelData + i * shm.stride); - } - } - - unbind(); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - return true; -} - -CGLFramebuffer::~CGLFramebuffer() { - release(); -} - -GLuint CGLFramebuffer::getFBID() { - return m_fbAllocated ? m_fb : 0; -} - -void CGLFramebuffer::invalidate(const std::vector& attachments) { - if (!isAllocated()) - return; - - glInvalidateFramebuffer(GL_FRAMEBUFFER, attachments.size(), attachments.data()); -} diff --git a/src/render/gl/GLFramebuffer.hpp b/src/render/gl/GLFramebuffer.hpp deleted file mode 100644 index c171444e..00000000 --- a/src/render/gl/GLFramebuffer.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "../../defines.hpp" -#include "../Texture.hpp" -#include "../Framebuffer.hpp" -#include - -class CGLFramebuffer : public IFramebuffer { - public: - CGLFramebuffer(); - CGLFramebuffer(const std::string& name); - ~CGLFramebuffer(); - - void addStencil(SP tex) override; - void release() override; - bool readPixels(CHLBufferReference buffer, uint32_t offsetX = 0, uint32_t offsetY = 0, uint32_t width = 0, uint32_t height = 0) override; - - void bind() override; - void unbind(); - GLuint getFBID(); - void invalidate(const std::vector& attachments); - - protected: - bool internalAlloc(int w, int h, uint32_t format = DRM_FORMAT_ARGB8888) override; - - private: - GLuint m_fb = -1; - - friend class CGLRenderbuffer; -}; diff --git a/src/render/gl/GLRenderbuffer.cpp b/src/render/gl/GLRenderbuffer.cpp deleted file mode 100644 index 8299d0e4..00000000 --- a/src/render/gl/GLRenderbuffer.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "GLRenderbuffer.hpp" -#include "../Renderer.hpp" -#include "../OpenGL.hpp" -#include "../../Compositor.hpp" -#include "../Framebuffer.hpp" -#include "GLFramebuffer.hpp" -#include "render/Renderbuffer.hpp" -#include -#include -#include - -#include - -CGLRenderbuffer::~CGLRenderbuffer() { - if (!g_pCompositor || g_pCompositor->m_isShuttingDown || !g_pHyprRenderer) - return; - - g_pHyprRenderer->makeEGLCurrent(); - - unbind(); - m_framebuffer->release(); - - if (m_rbo) - glDeleteRenderbuffers(1, &m_rbo); - - if (m_image != EGL_NO_IMAGE_KHR) - g_pHyprOpenGL->m_proc.eglDestroyImageKHR(g_pHyprOpenGL->m_eglDisplay, m_image); -} - -CGLRenderbuffer::CGLRenderbuffer(SP buffer, uint32_t format) : IRenderbuffer(buffer, format) { - auto dma = buffer->dmabuf(); - - m_image = g_pHyprOpenGL->createEGLImage(dma); - if (m_image == EGL_NO_IMAGE_KHR) { - Log::logger->log(Log::ERR, "rb: createEGLImage failed"); - return; - } - - glGenRenderbuffers(1, &m_rbo); - glBindRenderbuffer(GL_RENDERBUFFER, m_rbo); - g_pHyprOpenGL->m_proc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, m_image); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - m_framebuffer = makeShared(); - glGenFramebuffers(1, &GLFB(m_framebuffer)->m_fb); - GLFB(m_framebuffer)->m_fbAllocated = true; - m_framebuffer->m_size = buffer->size; - m_framebuffer->m_drmFormat = dma.format; - m_framebuffer->bind(); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - Log::logger->log(Log::ERR, "rbo: glCheckFramebufferStatus failed"); - return; - } - - GLFB(m_framebuffer)->unbind(); - - m_listeners.destroyBuffer = buffer->events.destroy.listen([this] { g_pHyprRenderer->onRenderbufferDestroy(this); }); - - m_good = true; -} - -void CGLRenderbuffer::bind() { - g_pHyprRenderer->makeEGLCurrent(); - m_framebuffer->bind(); -} - -void CGLRenderbuffer::unbind() { - GLFB(m_framebuffer)->unbind(); -} diff --git a/src/render/gl/GLRenderbuffer.hpp b/src/render/gl/GLRenderbuffer.hpp deleted file mode 100644 index 8367f702..00000000 --- a/src/render/gl/GLRenderbuffer.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "../../helpers/memory/Memory.hpp" -#include "../Renderbuffer.hpp" -#include - -class CMonitor; - -class CGLRenderbuffer : public IRenderbuffer { - public: - CGLRenderbuffer(SP buffer, uint32_t format); - ~CGLRenderbuffer(); - - void bind() override; - void unbind() override; - - private: - void* m_image = nullptr; - GLuint m_rbo = 0; -}; diff --git a/src/render/gl/GLTexture.cpp b/src/render/gl/GLTexture.cpp deleted file mode 100644 index 6a1fb172..00000000 --- a/src/render/gl/GLTexture.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "GLTexture.hpp" -#include "../Renderer.hpp" -#include "../../Compositor.hpp" -#include "../../helpers/Format.hpp" -#include "render/Texture.hpp" -#include - -CGLTexture::CGLTexture(bool opaque) { - m_opaque = opaque; -} - -CGLTexture::~CGLTexture() { - if (!g_pCompositor || g_pCompositor->m_isShuttingDown || !g_pHyprRenderer) - return; - - g_pHyprRenderer->makeEGLCurrent(); - if (m_texID) { - GLCALL(glDeleteTextures(1, &m_texID)); - m_texID = 0; - } - - if (m_eglImage) - g_pHyprOpenGL->m_proc.eglDestroyImageKHR(g_pHyprOpenGL->m_eglDisplay, m_eglImage); - m_eglImage = nullptr; - m_cachedStates.fill(std::nullopt); -} - -CGLTexture::CGLTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_, bool keepDataCopy, bool opaque) : - ITexture(drmFormat, pixels, stride, size_, keepDataCopy, opaque) { - - g_pHyprRenderer->makeEGLCurrent(); - - const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); - ASSERT(format); - - m_type = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; - m_size = size_; - m_isSynchronous = true; - m_target = GL_TEXTURE_2D; - allocate(size_); - bind(); - setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - if (format->swizzle.has_value()) - swizzle(format->swizzle.value()); - - bool alignmentChanged = false; - if (format->bytesPerBlock != 4) { - const GLint alignment = (stride % 4 == 0) ? 4 : 1; - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)); - alignmentChanged = true; - } - - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); - GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, size_.x, size_.y, 0, format->glFormat, format->glType, pixels)); - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); - if (alignmentChanged) - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); - - unbind(); -} - -CGLTexture::CGLTexture(const Aquamarine::SDMABUFAttrs& attrs, void* image, bool opaque) { - m_opaque = opaque; - if (!g_pHyprOpenGL->m_proc.glEGLImageTargetTexture2DOES) { - Log::logger->log(Log::ERR, "Cannot create a dmabuf texture: no glEGLImageTargetTexture2DOES"); - return; - } - - m_opaque = NFormatUtils::isFormatOpaque(attrs.format); - - // #TODO external only formats should be external aswell. - // also needs a seperate color shader. - /*if (NFormatUtils::isFormatYUV(attrs.format)) { - m_target = GL_TEXTURE_EXTERNAL_OES; - m_type = TEXTURE_EXTERNAL; - } else {*/ - m_target = GL_TEXTURE_2D; - m_type = NFormatUtils::isFormatOpaque(attrs.format) ? TEXTURE_RGBX : TEXTURE_RGBA; - //} - - allocate(attrs.size); - m_eglImage = image; - - bind(); - setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLCALL(g_pHyprOpenGL->m_proc.glEGLImageTargetTexture2DOES(m_target, image)); - unbind(); -} - -CGLTexture::CGLTexture(std::span lut3D, size_t N) : ITexture(lut3D, N), m_target(GL_TEXTURE_3D) { - allocate({}); - bind(); - - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - setTexParameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); - setTexParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - setTexParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - setTexParameter(GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - // Expand RGB->RGBA on upload (alpha=1) - std::vector rgba; - rgba.resize(N * N * N * 4); - for (size_t i = 0, j = 0; i < N * N * N; ++i, j += 3) { - rgba[i * 4 + 0] = lut3D[j + 0]; - rgba[i * 4 + 1] = lut3D[j + 1]; - rgba[i * 4 + 2] = lut3D[j + 2]; - rgba[i * 4 + 3] = 1.F; - } - - GLCALL(glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA16F, N, N, N, 0, GL_RGBA, GL_FLOAT, rgba.data())); - - unbind(); -} - -void CGLTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) { - if (damage.empty()) - return; - - g_pHyprRenderer->makeEGLCurrent(); - - const auto format = NFormatUtils::getPixelFormatFromDRM(drmFormat); - ASSERT(format); - - bind(); - - if (format->swizzle.has_value()) - swizzle(format->swizzle.value()); - - bool alignmentChanged = false; - if (format->bytesPerBlock != 4) { - const GLint alignment = (stride % 4 == 0) ? 4 : 1; - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, alignment)); - alignmentChanged = true; - } - - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); - - damage.copy().intersect(CBox{{}, m_size}).forEachRect([&format, &pixels](const auto& rect) { - GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, rect.x1)); - GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, rect.y1)); - - int width = rect.x2 - rect.x1; - int height = rect.y2 - rect.y1; - GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x1, rect.y1, width, height, format->glFormat, format->glType, pixels)); - }); - - if (alignmentChanged) - GLCALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); - - GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); - GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0)); - GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0)); - - unbind(); - - if (m_keepDataCopy) { - m_dataCopy.resize(stride * m_size.y); - memcpy(m_dataCopy.data(), pixels, stride * m_size.y); - } -} - -void CGLTexture::allocate(const Vector2D& size, uint32_t drmFormat) { - if (!m_texID) - GLCALL(glGenTextures(1, &m_texID)); - m_size = size; - m_drmFormat = drmFormat; -} - -void CGLTexture::bind() { - GLCALL(glBindTexture(m_target, m_texID)); -} - -void CGLTexture::unbind() { - GLCALL(glBindTexture(m_target, 0)); -} - -bool CGLTexture::ok() { - return m_texID > 0; -} - -bool CGLTexture::isDMA() { - return m_eglImage; -} - -constexpr std::optional CGLTexture::getCacheStateIndex(GLenum pname) { - switch (pname) { - case GL_TEXTURE_WRAP_S: return TEXTURE_PAR_WRAP_S; - case GL_TEXTURE_WRAP_T: return TEXTURE_PAR_WRAP_T; - case GL_TEXTURE_MAG_FILTER: return TEXTURE_PAR_MAG_FILTER; - case GL_TEXTURE_MIN_FILTER: return TEXTURE_PAR_MIN_FILTER; - case GL_TEXTURE_SWIZZLE_R: return TEXTURE_PAR_SWIZZLE_R; - case GL_TEXTURE_SWIZZLE_B: return TEXTURE_PAR_SWIZZLE_B; - default: return std::nullopt; - } -} - -void CGLTexture::setTexParameter(GLenum pname, GLint param) { - const auto cacheIndex = getCacheStateIndex(pname); - - if (!cacheIndex) { - GLCALL(glTexParameteri(m_target, pname, param)); - return; - } - - const auto idx = cacheIndex.value(); - - if (m_cachedStates[idx] == param) - return; - - m_cachedStates[idx] = param; - GLCALL(glTexParameteri(m_target, pname, param)); -} - -void CGLTexture::swizzle(const std::array& colors) { - setTexParameter(GL_TEXTURE_SWIZZLE_R, colors.at(0)); - setTexParameter(GL_TEXTURE_SWIZZLE_G, colors.at(1)); - setTexParameter(GL_TEXTURE_SWIZZLE_B, colors.at(2)); - setTexParameter(GL_TEXTURE_SWIZZLE_A, colors.at(3)); -} diff --git a/src/render/gl/GLTexture.hpp b/src/render/gl/GLTexture.hpp deleted file mode 100644 index 34510e90..00000000 --- a/src/render/gl/GLTexture.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "../Texture.hpp" -#include -#include - -class CGLTexture : public ITexture { - public: - using ITexture::ITexture; - - CGLTexture(CGLTexture&) = delete; - CGLTexture(CGLTexture&&) = delete; - CGLTexture(const CGLTexture&&) = delete; - CGLTexture(const CGLTexture&) = delete; - - CGLTexture(bool opaque = false); - CGLTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size, bool keepDataCopy = false, bool opaque = false); - CGLTexture(const Aquamarine::SDMABUFAttrs&, void* image, bool opaque = false); - CGLTexture(std::span lut3D, size_t N); - ~CGLTexture(); - - void allocate(const Vector2D& size, uint32_t drmFormat = 0) override; - void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) override; - void bind() override; - void unbind() override; - void setTexParameter(GLenum pname, GLint param) override; - bool ok() override; - bool isDMA() override; - - private: - void* m_eglImage = nullptr; - - enum eTextureParam : uint8_t { - TEXTURE_PAR_WRAP_S = 0, - TEXTURE_PAR_WRAP_T, - TEXTURE_PAR_MAG_FILTER, - TEXTURE_PAR_MIN_FILTER, - TEXTURE_PAR_SWIZZLE_R, - TEXTURE_PAR_SWIZZLE_B, - TEXTURE_PAR_LAST, - }; - - GLenum m_target = GL_TEXTURE_2D; - - void swizzle(const std::array& colors); - constexpr std::optional getCacheStateIndex(GLenum pname); - - std::array, TEXTURE_PAR_LAST> m_cachedStates; -}; diff --git a/src/render/pass/BorderPassElement.cpp b/src/render/pass/BorderPassElement.cpp index 13063290..31042c28 100644 --- a/src/render/pass/BorderPassElement.cpp +++ b/src/render/pass/BorderPassElement.cpp @@ -1,19 +1,15 @@ #include "BorderPassElement.hpp" #include "../OpenGL.hpp" -CBorderPassElement::CBorderPassElement(const CBorderPassElement::SBorderData& data_) : m_data(data_) { +CBorderPassElement::CBorderPassElement(const CBorderPassElement::SBorderData& data_) : data(data_) { ; } void CBorderPassElement::draw(const CRegion& damage) { - if (m_data.hasGrad2) - g_pHyprOpenGL->renderBorder( - m_data.box, m_data.grad1, m_data.grad2, m_data.lerp, - {.round = m_data.round, .roundingPower = m_data.roundingPower, .borderSize = m_data.borderSize, .a = m_data.a, .outerRound = m_data.outerRound}); + if (data.hasGrad2) + g_pHyprOpenGL->renderBorder(data.box, data.grad1, data.grad2, data.lerp, data.round, data.roundingPower, data.borderSize, data.a, data.outerRound); else - g_pHyprOpenGL->renderBorder( - m_data.box, m_data.grad1, - {.round = m_data.round, .roundingPower = m_data.roundingPower, .borderSize = m_data.borderSize, .a = m_data.a, .outerRound = m_data.outerRound}); + g_pHyprOpenGL->renderBorder(data.box, data.grad1, data.round, data.roundingPower, data.borderSize, data.a, data.outerRound); } bool CBorderPassElement::needsLiveBlur() { diff --git a/src/render/pass/BorderPassElement.hpp b/src/render/pass/BorderPassElement.hpp index 238b9ed5..785653ab 100644 --- a/src/render/pass/BorderPassElement.hpp +++ b/src/render/pass/BorderPassElement.hpp @@ -27,5 +27,5 @@ class CBorderPassElement : public IPassElement { } private: - SBorderData m_data; + SBorderData data; }; diff --git a/src/render/pass/ClearPassElement.cpp b/src/render/pass/ClearPassElement.cpp index 256b857f..bafa73c7 100644 --- a/src/render/pass/ClearPassElement.cpp +++ b/src/render/pass/ClearPassElement.cpp @@ -1,12 +1,12 @@ #include "ClearPassElement.hpp" #include "../OpenGL.hpp" -CClearPassElement::CClearPassElement(const CClearPassElement::SClearData& data_) : m_data(data_) { +CClearPassElement::CClearPassElement(const CClearPassElement::SClearData& data_) : data(data_) { ; } void CClearPassElement::draw(const CRegion& damage) { - g_pHyprOpenGL->clear(m_data.color); + g_pHyprOpenGL->clear(data.color); } bool CClearPassElement::needsLiveBlur() { diff --git a/src/render/pass/ClearPassElement.hpp b/src/render/pass/ClearPassElement.hpp index 067b7094..40242457 100644 --- a/src/render/pass/ClearPassElement.hpp +++ b/src/render/pass/ClearPassElement.hpp @@ -21,5 +21,5 @@ class CClearPassElement : public IPassElement { } private: - SClearData m_data; -}; + SClearData data; +}; \ No newline at end of file diff --git a/src/render/pass/FramebufferElement.cpp b/src/render/pass/FramebufferElement.cpp index bc7c686a..8271777f 100644 --- a/src/render/pass/FramebufferElement.cpp +++ b/src/render/pass/FramebufferElement.cpp @@ -1,37 +1,37 @@ #include "FramebufferElement.hpp" #include "../OpenGL.hpp" -CFramebufferElement::CFramebufferElement(const CFramebufferElement::SFramebufferElementData& data_) : m_data(data_) { +CFramebufferElement::CFramebufferElement(const CFramebufferElement::SFramebufferElementData& data_) : data(data_) { ; } void CFramebufferElement::draw(const CRegion& damage) { - SP fb = nullptr; + CFramebuffer* fb = nullptr; - if (m_data.main) { - switch (m_data.framebufferID) { - case FB_MONITOR_RENDER_MAIN: fb = g_pHyprOpenGL->m_renderData.mainFB; break; - case FB_MONITOR_RENDER_CURRENT: fb = g_pHyprOpenGL->m_renderData.currentFB; break; - case FB_MONITOR_RENDER_OUT: fb = g_pHyprOpenGL->m_renderData.outFB; break; + if (data.main) { + switch (data.framebufferID) { + case FB_MONITOR_RENDER_MAIN: fb = g_pHyprOpenGL->m_RenderData.mainFB; break; + case FB_MONITOR_RENDER_CURRENT: fb = g_pHyprOpenGL->m_RenderData.currentFB; break; + case FB_MONITOR_RENDER_OUT: fb = g_pHyprOpenGL->m_RenderData.outFB; break; } if (!fb) { - Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: main but null"); + Debug::log(ERR, "BUG THIS: CFramebufferElement::draw: main but null"); return; } } else { - switch (m_data.framebufferID) { - case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->offloadFB; break; - case FB_MONITOR_RENDER_EXTRA_MIRROR: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorFB; break; - case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->mirrorSwapFB; break; - case FB_MONITOR_RENDER_EXTRA_OFF_MAIN: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->offMainFB; break; - case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->monitorMirrorFB; break; - case FB_MONITOR_RENDER_EXTRA_BLUR: fb = g_pHyprOpenGL->m_renderData.pCurrentMonData->blurFB; break; + switch (data.framebufferID) { + case FB_MONITOR_RENDER_EXTRA_OFFLOAD: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->offloadFB; break; + case FB_MONITOR_RENDER_EXTRA_MIRROR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorFB; break; + case FB_MONITOR_RENDER_EXTRA_MIRROR_SWAP: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->mirrorSwapFB; break; + case FB_MONITOR_RENDER_EXTRA_OFF_MAIN: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->offMainFB; break; + case FB_MONITOR_RENDER_EXTRA_MONITOR_MIRROR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->monitorMirrorFB; break; + case FB_MONITOR_RENDER_EXTRA_BLUR: fb = &g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFB; break; } if (!fb) { - Log::logger->log(Log::ERR, "BUG THIS: CFramebufferElement::draw: not main but null"); + Debug::log(ERR, "BUG THIS: CFramebufferElement::draw: not main but null"); return; } } diff --git a/src/render/pass/FramebufferElement.hpp b/src/render/pass/FramebufferElement.hpp index 515c3380..ab1f1426 100644 --- a/src/render/pass/FramebufferElement.hpp +++ b/src/render/pass/FramebufferElement.hpp @@ -21,5 +21,5 @@ class CFramebufferElement : public IPassElement { } private: - SFramebufferElementData m_data; + SFramebufferElementData data; }; \ No newline at end of file diff --git a/src/render/pass/Pass.cpp b/src/render/pass/Pass.cpp index a4436516..60c1bc30 100644 --- a/src/render/pass/Pass.cpp +++ b/src/render/pass/Pass.cpp @@ -2,13 +2,12 @@ #include "../OpenGL.hpp" #include #include -#include "../../Compositor.hpp" #include "../../config/ConfigValue.hpp" -#include "../../desktop/view/WLSurface.hpp" +#include "../../desktop/WLSurface.hpp" #include "../../managers/SeatManager.hpp" #include "../../managers/eventLoop/EventLoopManager.hpp" #include "../../render/Renderer.hpp" -#include "../../desktop/state/FocusState.hpp" +#include "../../Compositor.hpp" #include "../../protocols/core/Compositor.hpp" bool CRenderPass::empty() const { @@ -16,11 +15,11 @@ bool CRenderPass::empty() const { } bool CRenderPass::single() const { - return m_passElements.size() == 1; + return m_vPassElements.size() == 1; } -void CRenderPass::add(UP&& el) { - m_passElements.emplace_back(makeUnique(CRegion{}, std::move(el))); +void CRenderPass::add(SP el) { + m_vPassElements.emplace_back(makeShared(CRegion{}, el)); } void CRenderPass::simplify() { @@ -29,10 +28,10 @@ void CRenderPass::simplify() { // TODO: use precompute blur for instances where there is nothing in between // if there is live blur, we need to NOT occlude any area where it will be influenced - const auto WILLBLUR = std::ranges::any_of(m_passElements, [](const auto& el) { return el->element->needsLiveBlur(); }); + const auto WILLBLUR = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsLiveBlur(); }); - CRegion newDamage = m_damage.copy().intersect(CBox{{}, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize}); - for (auto& el : m_passElements | std::views::reverse) { + CRegion newDamage = damage.copy().intersect(CBox{{}, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize}); + for (auto& el : m_vPassElements | std::views::reverse) { if (newDamage.empty() && !el->element->undiscardable()) { el->discard = true; @@ -44,7 +43,7 @@ void CRenderPass::simplify() { if (!bb1 || newDamage.empty()) continue; - auto bb = bb1->scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale); + auto bb = bb1->scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale); // drop if empty if (CRegion copy = newDamage.copy(); copy.intersect(bb).empty()) { @@ -55,22 +54,13 @@ void CRenderPass::simplify() { auto opaque = el->element->opaqueRegion(); if (!opaque.empty()) { - // scale and rounding is very particular so we have to use CBoxes scale and round functions - if (opaque.getRects().size() == 1) - opaque = opaque.getExtents().scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale).round(); - else { - CRegion scaledRegion; - opaque.forEachRect([&scaledRegion](const auto& RECT) { - scaledRegion.add(CBox(RECT.x1, RECT.y1, RECT.x2 - RECT.x1, RECT.y2 - RECT.y1).scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale).round()); - }); - opaque = scaledRegion; - } + opaque.scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale); // if this intersects the liveBlur region, allow live blur to operate correctly. // do not occlude a border near it. if (WILLBLUR) { CRegion liveBlurRegion; - for (auto& el2 : m_passElements) { + for (auto& el2 : m_vPassElements) { // if we reach self, no problem, we can break. // if the blur is above us, we don't care, it will work fine. if (el2 == el) @@ -86,7 +76,7 @@ void CRenderPass::simplify() { } // expand the region: this area needs to be proper to blur it right. - liveBlurRegion.scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale).expand(oneBlurRadius() * 2.F); + liveBlurRegion.scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale).expand(oneBlurRadius() * 2.F); if (auto infringement = opaque.copy().intersect(liveBlurRegion); !infringement.empty()) { // eh, this is not the correct solution, but it will do... @@ -96,57 +86,57 @@ void CRenderPass::simplify() { } newDamage.subtract(opaque); if (*PDEBUGPASS) - m_occludedRegions.emplace_back(opaque); + occludedRegions.emplace_back(opaque); } } if (*PDEBUGPASS) { - for (auto& el2 : m_passElements) { + for (auto& el2 : m_vPassElements) { if (!el2->element->needsLiveBlur()) continue; const auto BB = el2->element->boundingBox(); RASSERT(BB, "No bounding box for an element with live blur is illegal"); - m_totalLiveBlurRegion.add(BB->copy().scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale)); + totalLiveBlurRegion.add(BB->copy().scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale)); } } } void CRenderPass::clear() { - m_passElements.clear(); + m_vPassElements.clear(); } CRegion CRenderPass::render(const CRegion& damage_) { static auto PDEBUGPASS = CConfigValue("debug:pass"); - const auto WILLBLUR = std::ranges::any_of(m_passElements, [](const auto& el) { return el->element->needsLiveBlur(); }); + const auto WILLBLUR = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsLiveBlur(); }); - m_damage = *PDEBUGPASS ? CRegion{CBox{{}, {INT32_MAX, INT32_MAX}}} : damage_.copy(); + damage = *PDEBUGPASS ? CRegion{CBox{{}, {INT32_MAX, INT32_MAX}}} : damage_.copy(); if (*PDEBUGPASS) { - m_occludedRegions.clear(); - m_totalLiveBlurRegion = CRegion{}; + occludedRegions.clear(); + totalLiveBlurRegion = CRegion{}; } - if (m_damage.empty()) { - g_pHyprOpenGL->m_renderData.damage = m_damage; - g_pHyprOpenGL->m_renderData.finalDamage = m_damage; - return m_damage; + if (damage.empty()) { + g_pHyprOpenGL->m_RenderData.damage = damage; + g_pHyprOpenGL->m_RenderData.finalDamage = damage; + return damage; } - if (!*PDEBUGPASS && m_debugData.present) - m_debugData = {false}; - else if (*PDEBUGPASS && !m_debugData.present) { - m_debugData.present = true; - m_debugData.keyboardFocusText = g_pHyprOpenGL->renderText("keyboard", Colors::WHITE, 12); - m_debugData.pointerFocusText = g_pHyprOpenGL->renderText("pointer", Colors::WHITE, 12); - m_debugData.lastWindowText = g_pHyprOpenGL->renderText("lastWindow", Colors::WHITE, 12); + if (!*PDEBUGPASS && debugData.present) + debugData = {false}; + else if (*PDEBUGPASS && !debugData.present) { + debugData.present = true; + debugData.keyboardFocusText = g_pHyprOpenGL->renderText("keyboard", Colors::WHITE, 12); + debugData.pointerFocusText = g_pHyprOpenGL->renderText("pointer", Colors::WHITE, 12); + debugData.lastWindowText = g_pHyprOpenGL->renderText("lastWindow", Colors::WHITE, 12); } if (WILLBLUR && !*PDEBUGPASS) { // combine blur regions into one that will be expanded CRegion blurRegion; - for (auto& el : m_passElements) { + for (auto& el : m_vPassElements) { if (!el->element->needsLiveBlur()) continue; @@ -156,71 +146,71 @@ CRegion CRenderPass::render(const CRegion& damage_) { blurRegion.add(*BB); } - blurRegion.scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale); + blurRegion.scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale); - blurRegion.intersect(m_damage).expand(oneBlurRadius()); + blurRegion.intersect(damage).expand(oneBlurRadius()); - g_pHyprOpenGL->m_renderData.finalDamage = blurRegion.copy().add(m_damage); + g_pHyprOpenGL->m_RenderData.finalDamage = blurRegion.copy().add(damage); // FIXME: why does this break on * 1.F ? // used to work when we expand all the damage... I think? Well, before pass. // moving a window over blur shows the edges being wonk. blurRegion.expand(oneBlurRadius() * 1.5F); - m_damage = blurRegion.copy().add(m_damage); + damage = blurRegion.copy().add(damage); } else - g_pHyprOpenGL->m_renderData.finalDamage = m_damage; + g_pHyprOpenGL->m_RenderData.finalDamage = damage; - if (g_pHyprOpenGL->m_renderData.noSimplify || std::ranges::any_of(m_passElements, [](const auto& el) { return el->element->disableSimplification(); })) { - for (auto& el : m_passElements) { - el->elementDamage = m_damage; + if (std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->disableSimplification(); })) { + for (auto& el : m_vPassElements) { + el->elementDamage = damage; } } else simplify(); - g_pHyprOpenGL->m_renderData.pCurrentMonData->blurFBShouldRender = std::ranges::any_of(m_passElements, [](const auto& el) { return el->element->needsPrecomputeBlur(); }); + g_pHyprOpenGL->m_RenderData.pCurrentMonData->blurFBShouldRender = std::ranges::any_of(m_vPassElements, [](const auto& el) { return el->element->needsPrecomputeBlur(); }); - if (m_passElements.empty()) + if (m_vPassElements.empty()) return {}; - for (auto& el : m_passElements) { + for (auto& el : m_vPassElements) { if (el->discard) { el->element->discard(); continue; } - g_pHyprOpenGL->m_renderData.damage = el->elementDamage; + g_pHyprOpenGL->m_RenderData.damage = el->elementDamage; el->element->draw(el->elementDamage); } if (*PDEBUGPASS) { renderDebugData(); g_pEventLoopManager->doLater([] { - for (auto& m : g_pCompositor->m_monitors) { + for (auto& m : g_pCompositor->m_vMonitors) { g_pHyprRenderer->damageMonitor(m); } }); } - g_pHyprOpenGL->m_renderData.damage = m_damage; - return m_damage; + g_pHyprOpenGL->m_RenderData.damage = damage; + return damage; } void CRenderPass::renderDebugData() { - CBox box = {{}, g_pHyprOpenGL->m_renderData.pMonitor->m_transformedSize}; - for (const auto& rg : m_occludedRegions) { - g_pHyprOpenGL->renderRect(box, Colors::RED.modifyA(0.1F), {.damage = &rg}); + CBox box = {{}, g_pHyprOpenGL->m_RenderData.pMonitor->vecTransformedSize}; + for (const auto& rg : occludedRegions) { + g_pHyprOpenGL->renderRectWithDamage(box, Colors::RED.modifyA(0.1F), rg); } - g_pHyprOpenGL->renderRect(box, Colors::GREEN.modifyA(0.1F), {.damage = &m_totalLiveBlurRegion}); + g_pHyprOpenGL->renderRectWithDamage(box, Colors::GREEN.modifyA(0.1F), totalLiveBlurRegion); std::unordered_map offsets; // render focus stuff - auto renderHLSurface = [&offsets](SP texture, SP surface, const CHyprColor& color) { + auto renderHLSurface = [&offsets](SP texture, SP surface, const CHyprColor& color) { if (!surface || !texture) return; - auto hlSurface = Desktop::View::CWLSurface::fromResource(surface); + auto hlSurface = CWLSurface::fromResource(surface); if (!hlSurface) return; @@ -229,64 +219,62 @@ void CRenderPass::renderDebugData() { if (!bb.has_value()) return; - CBox box = bb->copy().translate(-g_pHyprOpenGL->m_renderData.pMonitor->m_position).scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale); + CBox box = bb->copy().translate(-g_pHyprOpenGL->m_RenderData.pMonitor->vecPosition).scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale); - if (box.intersection(CBox{{}, g_pHyprOpenGL->m_renderData.pMonitor->m_size}).empty()) + if (box.intersection(CBox{{}, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize}).empty()) return; - static const auto FULL_REGION = CRegion{0, 0, INT32_MAX, INT32_MAX}; - - g_pHyprOpenGL->renderRect(box, color, {.damage = &FULL_REGION}); + g_pHyprOpenGL->renderRectWithDamage(box, color, CRegion{0, 0, INT32_MAX, INT32_MAX}); if (offsets.contains(surface.get())) box.translate(Vector2D{0.F, offsets[surface.get()]}); else offsets[surface.get()] = 0; - box = {box.pos(), texture->m_size}; - g_pHyprOpenGL->renderRect(box, CHyprColor{0.F, 0.F, 0.F, 0.2F}, {.damage = &FULL_REGION, .round = std::min(5.0, box.size().y)}); - g_pHyprOpenGL->renderTexture(texture, box, {}); + box = {box.pos(), texture->m_vSize}; + g_pHyprOpenGL->renderRectWithDamage(box, CHyprColor{0.F, 0.F, 0.F, 0.2F}, CRegion{0, 0, INT32_MAX, INT32_MAX}, std::min(5.0, box.size().y)); + g_pHyprOpenGL->renderTexture(texture, box, 1.F); - offsets[surface.get()] += texture->m_size.y; + offsets[surface.get()] += texture->m_vSize.y; }; - renderHLSurface(m_debugData.keyboardFocusText, g_pSeatManager->m_state.keyboardFocus.lock(), Colors::PURPLE.modifyA(0.1F)); - renderHLSurface(m_debugData.pointerFocusText, g_pSeatManager->m_state.pointerFocus.lock(), Colors::ORANGE.modifyA(0.1F)); - if (Desktop::focusState()->window()) - renderHLSurface(m_debugData.lastWindowText, Desktop::focusState()->window()->wlSurface()->resource(), Colors::LIGHT_BLUE.modifyA(0.1F)); + renderHLSurface(debugData.keyboardFocusText, g_pSeatManager->state.keyboardFocus.lock(), Colors::PURPLE.modifyA(0.1F)); + renderHLSurface(debugData.pointerFocusText, g_pSeatManager->state.pointerFocus.lock(), Colors::ORANGE.modifyA(0.1F)); + if (g_pCompositor->m_pLastWindow) + renderHLSurface(debugData.lastWindowText, g_pCompositor->m_pLastWindow->m_pWLSurface->resource(), Colors::LIGHT_BLUE.modifyA(0.1F)); - if (g_pSeatManager->m_state.pointerFocus) { - if (g_pSeatManager->m_state.pointerFocus->m_current.input.intersect(CBox{{}, g_pSeatManager->m_state.pointerFocus->m_current.size}).getExtents().size() != - g_pSeatManager->m_state.pointerFocus->m_current.size) { - auto hlSurface = Desktop::View::CWLSurface::fromResource(g_pSeatManager->m_state.pointerFocus.lock()); + if (g_pSeatManager->state.pointerFocus) { + if (g_pSeatManager->state.pointerFocus->current.input.intersect(CBox{{}, g_pSeatManager->state.pointerFocus->current.size}).getExtents().size() != + g_pSeatManager->state.pointerFocus->current.size) { + auto hlSurface = CWLSurface::fromResource(g_pSeatManager->state.pointerFocus.lock()); if (hlSurface) { auto BOX = hlSurface->getSurfaceBoxGlobal(); if (BOX) { - auto region = g_pSeatManager->m_state.pointerFocus->m_current.input.copy() - .scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale) - .translate(BOX->pos() - g_pHyprOpenGL->m_renderData.pMonitor->m_position); - g_pHyprOpenGL->renderRect(box, CHyprColor{0.8F, 0.8F, 0.2F, 0.4F}, {.damage = ®ion}); + auto region = g_pSeatManager->state.pointerFocus->current.input.copy() + .scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale) + .translate(BOX->pos() - g_pHyprOpenGL->m_RenderData.pMonitor->vecPosition); + g_pHyprOpenGL->renderRectWithDamage(box, CHyprColor{0.8F, 0.8F, 0.2F, 0.4F}, region); } } } } - const auto DISCARDED_ELEMENTS = std::ranges::count_if(m_passElements, [](const auto& e) { return e->discard; }); - auto tex = g_pHyprOpenGL->renderText(std::format("occlusion layers: {}\npass elements: {} ({} discarded)\nviewport: {:X0}", m_occludedRegions.size(), m_passElements.size(), - DISCARDED_ELEMENTS, g_pHyprOpenGL->m_renderData.pMonitor->m_pixelSize), + const auto DISCARDED_ELEMENTS = std::count_if(m_vPassElements.begin(), m_vPassElements.end(), [](const auto& e) { return e->discard; }); + auto tex = g_pHyprOpenGL->renderText(std::format("occlusion layers: {}\npass elements: {} ({} discarded)\nviewport: {:X0}", occludedRegions.size(), m_vPassElements.size(), + DISCARDED_ELEMENTS, g_pHyprOpenGL->m_RenderData.pMonitor->vecPixelSize), Colors::WHITE, 12); if (tex) { - box = CBox{{0.F, g_pHyprOpenGL->m_renderData.pMonitor->m_size.y - tex->m_size.y}, tex->m_size}.scale(g_pHyprOpenGL->m_renderData.pMonitor->m_scale); - g_pHyprOpenGL->renderTexture(tex, box, {}); + box = CBox{{0.F, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.y - tex->m_vSize.y}, tex->m_vSize}.scale(g_pHyprOpenGL->m_RenderData.pMonitor->scale); + g_pHyprOpenGL->renderTexture(tex, box, 1.F); } std::string passStructure; auto yn = [](const bool val) -> const char* { return val ? "yes" : "no"; }; auto tick = [](const bool val) -> const char* { return val ? "✔" : "✖"; }; - for (const auto& el : m_passElements | std::views::reverse) { - passStructure += std::format("{} {} (bb: {} op: {}, pb: {}, lb: {})\n", tick(!el->discard), el->element->passName(), yn(el->element->boundingBox().has_value()), - yn(!el->element->opaqueRegion().empty()), yn(el->element->needsPrecomputeBlur()), yn(el->element->needsLiveBlur())); + for (const auto& el : m_vPassElements | std::views::reverse) { + passStructure += std::format("{} {} (bb: {} op: {})\n", tick(!el->discard), el->element->passName(), yn(el->element->boundingBox().has_value()), + yn(!el->element->opaqueRegion().empty())); } if (!passStructure.empty()) @@ -294,9 +282,9 @@ void CRenderPass::renderDebugData() { tex = g_pHyprOpenGL->renderText(passStructure, Colors::WHITE, 12); if (tex) { - box = CBox{{g_pHyprOpenGL->m_renderData.pMonitor->m_size.x - tex->m_size.x, g_pHyprOpenGL->m_renderData.pMonitor->m_size.y - tex->m_size.y}, tex->m_size}.scale( - g_pHyprOpenGL->m_renderData.pMonitor->m_scale); - g_pHyprOpenGL->renderTexture(tex, box, {}); + box = CBox{{g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.x - tex->m_vSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.y - tex->m_vSize.y}, tex->m_vSize}.scale( + g_pHyprOpenGL->m_RenderData.pMonitor->scale); + g_pHyprOpenGL->renderTexture(tex, box, 1.F); } } @@ -304,12 +292,9 @@ float CRenderPass::oneBlurRadius() { // TODO: is this exact range correct? static auto PBLURSIZE = CConfigValue("decoration:blur:size"); static auto PBLURPASSES = CConfigValue("decoration:blur:passes"); - - const auto BLUR_PASSES = std::clamp(*PBLURPASSES, sc(1), sc(8)); - - return std::clamp(*PBLURSIZE, sc(1), sc(40)) * pow(2, BLUR_PASSES); // is this 2^pass? I don't know but it works... I think. + return *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think. } void CRenderPass::removeAllOfType(const std::string& type) { - std::erase_if(m_passElements, [&type](const auto& e) { return e->element->passName() == type; }); + std::erase_if(m_vPassElements, [&type](const auto& e) { return e->element->passName() == type; }); } diff --git a/src/render/pass/Pass.hpp b/src/render/pass/Pass.hpp index b45af88b..bbc55d2c 100644 --- a/src/render/pass/Pass.hpp +++ b/src/render/pass/Pass.hpp @@ -4,31 +4,33 @@ #include "PassElement.hpp" class CGradientValueData; -class ITexture; +class CTexture; class CRenderPass { public: bool empty() const; bool single() const; - void add(UP&& elem); + void add(SP elem); void clear(); void removeAllOfType(const std::string& type); CRegion render(const CRegion& damage_); private: - CRegion m_damage; - std::vector m_occludedRegions; - CRegion m_totalLiveBlurRegion; + CRegion damage; + std::vector occludedRegions; + CRegion totalLiveBlurRegion; struct SPassElementData { CRegion elementDamage; - UP element; + SP element; bool discard = false; }; - std::vector> m_passElements; + std::vector> m_vPassElements; + + SP currentPassInfo = nullptr; void simplify(); float oneBlurRadius(); @@ -36,8 +38,8 @@ class CRenderPass { struct { bool present = false; - SP keyboardFocusText, pointerFocusText, lastWindowText; - } m_debugData; + SP keyboardFocusText, pointerFocusText, lastWindowText; + } debugData; friend class CHyprOpenGLImpl; }; diff --git a/src/render/pass/RectPassElement.cpp b/src/render/pass/RectPassElement.cpp index 6c60741e..aa024577 100644 --- a/src/render/pass/RectPassElement.cpp +++ b/src/render/pass/RectPassElement.cpp @@ -1,46 +1,37 @@ #include "RectPassElement.hpp" #include "../OpenGL.hpp" -CRectPassElement::CRectPassElement(const CRectPassElement::SRectData& data_) : m_data(data_) { +CRectPassElement::CRectPassElement(const CRectPassElement::SRectData& data_) : data(data_) { ; } void CRectPassElement::draw(const CRegion& damage) { - if (m_data.box.w <= 0 || m_data.box.h <= 0) + if (data.box.w <= 0 || data.box.h <= 0) return; - if (!m_data.clipBox.empty()) - g_pHyprOpenGL->m_renderData.clipBox = m_data.clipBox; + if (!data.clipBox.empty()) + g_pHyprOpenGL->m_RenderData.clipBox = data.clipBox; - if (m_data.color.a == 1.F || !m_data.blur) - g_pHyprOpenGL->renderRect(m_data.box, m_data.color, {.damage = &damage, .round = m_data.round, .roundingPower = m_data.roundingPower}); + if (data.color.a == 1.F || !data.blur) + g_pHyprOpenGL->renderRectWithDamage(data.box, data.color, damage, data.round, data.roundingPower); else - g_pHyprOpenGL->renderRect(m_data.box, m_data.color, - {.round = m_data.round, .roundingPower = m_data.roundingPower, .blur = true, .blurA = m_data.blurA, .xray = m_data.xray}); + g_pHyprOpenGL->renderRectWithBlur(data.box, data.color, data.round, data.roundingPower, data.blurA, data.xray); - g_pHyprOpenGL->m_renderData.clipBox = {}; + g_pHyprOpenGL->m_RenderData.clipBox = {}; } bool CRectPassElement::needsLiveBlur() { - return m_data.color.a < 1.F && !m_data.xray && m_data.blur; + return data.color.a < 1.F && !data.xray && data.blur; } bool CRectPassElement::needsPrecomputeBlur() { - return m_data.color.a < 1.F && m_data.xray && m_data.blur; + return data.color.a < 1.F && data.xray && data.blur; } std::optional CRectPassElement::boundingBox() { - return m_data.box.copy().scale(1.F / g_pHyprOpenGL->m_renderData.pMonitor->m_scale).round(); + return data.box.copy().scale(1.F / g_pHyprOpenGL->m_RenderData.pMonitor->scale).round(); } CRegion CRectPassElement::opaqueRegion() { - if (m_data.color.a < 1.F) - return CRegion{}; - - CRegion rg = boundingBox()->expand(-m_data.round); - - if (!m_data.clipBox.empty()) - rg.intersect(m_data.clipBox); - - return rg; + return data.color.a >= 1.F ? boundingBox()->expand(-data.round) : CRegion{}; } diff --git a/src/render/pass/RectPassElement.hpp b/src/render/pass/RectPassElement.hpp index c83d52ec..f798dbf9 100644 --- a/src/render/pass/RectPassElement.hpp +++ b/src/render/pass/RectPassElement.hpp @@ -27,5 +27,5 @@ class CRectPassElement : public IPassElement { } private: - SRectData m_data; + SRectData data; }; diff --git a/src/render/pass/RendererHintsPassElement.cpp b/src/render/pass/RendererHintsPassElement.cpp index 2ad68204..5b6d0098 100644 --- a/src/render/pass/RendererHintsPassElement.cpp +++ b/src/render/pass/RendererHintsPassElement.cpp @@ -1,13 +1,13 @@ #include "RendererHintsPassElement.hpp" #include "../OpenGL.hpp" -CRendererHintsPassElement::CRendererHintsPassElement(const CRendererHintsPassElement::SData& data_) : m_data(data_) { +CRendererHintsPassElement::CRendererHintsPassElement(const CRendererHintsPassElement::SData& data_) : data(data_) { ; } void CRendererHintsPassElement::draw(const CRegion& damage) { - if (m_data.renderModif.has_value()) - g_pHyprOpenGL->m_renderData.renderModif = *m_data.renderModif; + if (data.renderModif.has_value()) + g_pHyprOpenGL->m_RenderData.renderModif = *data.renderModif; } bool CRendererHintsPassElement::needsLiveBlur() { diff --git a/src/render/pass/RendererHintsPassElement.hpp b/src/render/pass/RendererHintsPassElement.hpp index d56a0cd6..a333e031 100644 --- a/src/render/pass/RendererHintsPassElement.hpp +++ b/src/render/pass/RendererHintsPassElement.hpp @@ -22,5 +22,5 @@ class CRendererHintsPassElement : public IPassElement { } private: - SData m_data; + SData data; }; \ No newline at end of file diff --git a/src/render/pass/ShadowPassElement.cpp b/src/render/pass/ShadowPassElement.cpp index 45ad18c8..22910c96 100644 --- a/src/render/pass/ShadowPassElement.cpp +++ b/src/render/pass/ShadowPassElement.cpp @@ -2,12 +2,12 @@ #include "../OpenGL.hpp" #include "../decorations/CHyprDropShadowDecoration.hpp" -CShadowPassElement::CShadowPassElement(const CShadowPassElement::SShadowData& data_) : m_data(data_) { +CShadowPassElement::CShadowPassElement(const CShadowPassElement::SShadowData& data_) : data(data_) { ; } void CShadowPassElement::draw(const CRegion& damage) { - m_data.deco->render(g_pHyprOpenGL->m_renderData.pMonitor.lock(), m_data.a); + data.deco->render(g_pHyprOpenGL->m_RenderData.pMonitor.lock(), data.a); } bool CShadowPassElement::needsLiveBlur() { @@ -16,4 +16,4 @@ bool CShadowPassElement::needsLiveBlur() { bool CShadowPassElement::needsPrecomputeBlur() { return false; -} +} \ No newline at end of file diff --git a/src/render/pass/ShadowPassElement.hpp b/src/render/pass/ShadowPassElement.hpp index 028ac88c..715e7bfb 100644 --- a/src/render/pass/ShadowPassElement.hpp +++ b/src/render/pass/ShadowPassElement.hpp @@ -22,5 +22,5 @@ class CShadowPassElement : public IPassElement { } private: - SShadowData m_data; -}; + SShadowData data; +}; \ No newline at end of file diff --git a/src/render/pass/SurfacePassElement.cpp b/src/render/pass/SurfacePassElement.cpp index c5feb8f7..813ab8ea 100644 --- a/src/render/pass/SurfacePassElement.cpp +++ b/src/render/pass/SurfacePassElement.cpp @@ -1,11 +1,10 @@ #include "SurfacePassElement.hpp" #include "../OpenGL.hpp" -#include "../../desktop/view/WLSurface.hpp" -#include "../../desktop/view/Window.hpp" +#include "../../desktop/WLSurface.hpp" +#include "../../desktop/Window.hpp" #include "../../protocols/core/Compositor.hpp" #include "../../protocols/DRMSyncobj.hpp" #include "../../managers/input/InputManager.hpp" -#include "../../layout/LayoutManager.hpp" #include "../Renderer.hpp" #include @@ -13,59 +12,59 @@ #include using namespace Hyprutils::Utils; -CSurfacePassElement::CSurfacePassElement(const CSurfacePassElement::SRenderData& data_) : m_data(data_) { +CSurfacePassElement::CSurfacePassElement(const CSurfacePassElement::SRenderData& data_) : data(data_) { ; } void CSurfacePassElement::draw(const CRegion& damage) { - g_pHyprOpenGL->m_renderData.currentWindow = m_data.pWindow; - g_pHyprOpenGL->m_renderData.surface = m_data.surface; - g_pHyprOpenGL->m_renderData.currentLS = m_data.pLS; - g_pHyprOpenGL->m_renderData.clipBox = m_data.clipBox; - g_pHyprOpenGL->m_renderData.discardMode = m_data.discardMode; - g_pHyprOpenGL->m_renderData.discardOpacity = m_data.discardOpacity; - g_pHyprOpenGL->m_renderData.useNearestNeighbor = m_data.useNearestNeighbor; - g_pHyprOpenGL->pushMonitorTransformEnabled(m_data.flipEndFrame); + g_pHyprOpenGL->m_RenderData.currentWindow = data.pWindow; + g_pHyprOpenGL->m_RenderData.surface = data.surface; + g_pHyprOpenGL->m_RenderData.currentLS = data.pLS; + g_pHyprOpenGL->m_RenderData.clipBox = data.clipBox; + g_pHyprOpenGL->m_RenderData.discardMode = data.discardMode; + g_pHyprOpenGL->m_RenderData.discardOpacity = data.discardOpacity; + g_pHyprOpenGL->m_RenderData.useNearestNeighbor = data.useNearestNeighbor; + g_pHyprOpenGL->m_bEndFrame = data.flipEndFrame; CScopeGuard x = {[]() { - g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); - g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); - g_pHyprOpenGL->m_renderData.useNearestNeighbor = false; - g_pHyprOpenGL->m_renderData.clipBox = {}; - g_pHyprOpenGL->m_renderData.clipRegion = {}; - g_pHyprOpenGL->m_renderData.discardMode = 0; - g_pHyprOpenGL->m_renderData.discardOpacity = 0; - g_pHyprOpenGL->m_renderData.useNearestNeighbor = false; - g_pHyprOpenGL->popMonitorTransformEnabled(); - g_pHyprOpenGL->m_renderData.currentWindow.reset(); - g_pHyprOpenGL->m_renderData.surface.reset(); - g_pHyprOpenGL->m_renderData.currentLS.reset(); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight = Vector2D(-1, -1); + g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; + g_pHyprOpenGL->m_RenderData.clipBox = {}; + g_pHyprOpenGL->m_RenderData.clipRegion = {}; + g_pHyprOpenGL->m_RenderData.discardMode = 0; + g_pHyprOpenGL->m_RenderData.discardOpacity = 0; + g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; + g_pHyprOpenGL->m_bEndFrame = false; + g_pHyprOpenGL->m_RenderData.currentWindow.reset(); + g_pHyprOpenGL->m_RenderData.surface.reset(); + g_pHyprOpenGL->m_RenderData.currentLS.reset(); }}; - if (!m_data.texture) + if (!data.texture) return; - const auto& TEXTURE = m_data.texture; + const auto& TEXTURE = data.texture; // this is bad, probably has been logged elsewhere. Means the texture failed // uploading to the GPU. - if (!TEXTURE->m_texID) + if (!TEXTURE->m_iTexID) return; - const auto INTERACTIVERESIZEINPROGRESS = m_data.pWindow && g_layoutManager->dragController()->target() && g_layoutManager->dragController()->mode() == MBIND_RESIZE; + const auto INTERACTIVERESIZEINPROGRESS = data.pWindow && g_pInputManager->currentlyDraggedWindow && g_pInputManager->dragMode == MBIND_RESIZE; TRACY_GPU_ZONE("RenderSurface"); - auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface); + auto PSURFACE = CWLSurface::fromResource(data.surface); - const float ALPHA = m_data.alpha * m_data.fadeAlpha * (PSURFACE ? PSURFACE->m_alphaModifier : 1.F); - const float OVERALL_ALPHA = PSURFACE ? PSURFACE->m_overallOpacity : 1.F; - const bool BLUR = m_data.blur && (!TEXTURE->m_opaque || ALPHA < 1.F || OVERALL_ALPHA < 1.F); + const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_fAlphaModifier : 1.F); + const float OVERALL_ALPHA = PSURFACE ? PSURFACE->m_fOverallOpacity : 1.F; + const bool BLUR = data.blur && (!TEXTURE->m_bOpaque || ALPHA < 1.F || OVERALL_ALPHA < 1.F); auto windowBox = getTexBox(); const auto PROJSIZEUNSCALED = windowBox.size(); - windowBox.scale(m_data.pMonitor->m_scale); + windowBox.scale(data.pMonitor->scale); windowBox.round(); if (windowBox.width <= 1 || windowBox.height <= 1) { @@ -73,16 +72,17 @@ void CSurfacePassElement::draw(const CRegion& damage) { return; } - const bool MISALIGNEDFSV1 = std::floor(m_data.pMonitor->m_scale) != m_data.pMonitor->m_scale /* Fractional */ && m_data.surface->m_current.scale == 1 /* fs protocol */ && - windowBox.size() != m_data.surface->m_current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, m_data.surface->m_current.bufferSize.x, 3) && - DELTALESSTHAN(windowBox.height, m_data.surface->m_current.bufferSize.y, 3) /* off by one-or-two */ && - (!m_data.pWindow || (!m_data.pWindow->m_realSize->isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */ && - (!m_data.pLS || (!m_data.pLS->m_realSize->isBeingAnimated())); /* not LS or not animated */ + const bool MISALIGNEDFSV1 = std::floor(data.pMonitor->scale) != data.pMonitor->scale /* Fractional */ && data.surface->current.scale == 1 /* fs protocol */ && + windowBox.size() != data.surface->current.bufferSize /* misaligned */ && DELTALESSTHAN(windowBox.width, data.surface->current.bufferSize.x, 3) && + DELTALESSTHAN(windowBox.height, data.surface->current.bufferSize.y, 3) /* off by one-or-two */ && + (!data.pWindow || (!data.pWindow->m_vRealSize->isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */; - g_pHyprRenderer->calculateUVForSurface(m_data.pWindow, m_data.surface, m_data.pMonitor->m_self.lock(), m_data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1); + if (data.surface->colorManagement.valid()) + Debug::log(TRACE, "FIXME: rendering surface with color management enabled, should apply necessary transformations"); + g_pHyprRenderer->calculateUVForSurface(data.pWindow, data.surface, data.pMonitor->self.lock(), data.mainSurface, windowBox.size(), PROJSIZEUNSCALED, MISALIGNEDFSV1); auto cancelRender = false; - g_pHyprOpenGL->m_renderData.clipRegion = visibleRegion(cancelRender); + g_pHyprOpenGL->m_RenderData.clipRegion = visibleRegion(cancelRender); if (cancelRender) return; @@ -91,20 +91,20 @@ void CSurfacePassElement::draw(const CRegion& damage) { // as long as the window is not animated. During those it'd look weird. // UV will fixup it as well if (MISALIGNEDFSV1) - g_pHyprOpenGL->m_renderData.useNearestNeighbor = true; + g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true; - float rounding = m_data.rounding; - float roundingPower = m_data.roundingPower; + float rounding = data.rounding; + float roundingPower = data.roundingPower; rounding -= 1; // to fix a border issue - if (m_data.dontRound) { + if (data.dontRound) { rounding = 0; roundingPower = 2.0f; } - const bool WINDOWOPAQUE = m_data.pWindow && m_data.pWindow->wlSurface()->resource() == m_data.surface ? m_data.pWindow->opaque() : false; - const bool CANDISABLEBLEND = ALPHA >= 1.f && OVERALL_ALPHA >= 1.f && rounding <= 0 && WINDOWOPAQUE; + const bool WINDOWOPAQUE = data.pWindow && data.pWindow->m_pWLSurface->resource() == data.surface ? data.pWindow->opaque() : false; + const bool CANDISABLEBLEND = ALPHA >= 1.f && OVERALL_ALPHA >= 1.f && rounding == 0 && WINDOWOPAQUE; if (CANDISABLEBLEND) g_pHyprOpenGL->blend(false); @@ -114,138 +114,97 @@ void CSurfacePassElement::draw(const CRegion& damage) { // FIXME: This is wrong and will bug the blur out as shit if the first surface // is a subsurface that does NOT cover the entire frame. In such cases, we probably should fall back // to what we do for misaligned surfaces (blur the entire thing and then render shit without blur) - if (m_data.surfaceCounter == 0 && !m_data.popup) { + if (data.surfaceCounter == 0 && !data.popup) { if (BLUR) - g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, - { - .surface = m_data.surface, - .a = ALPHA, - .blur = true, - .blurA = m_data.fadeAlpha, - .overallA = OVERALL_ALPHA, - .round = rounding, - .roundingPower = roundingPower, - .allowCustomUV = true, - .blockBlurOptimization = m_data.blockBlurOptimization, - }); + g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, windowBox, ALPHA, data.surface, rounding, roundingPower, data.blockBlurOptimization, data.fadeAlpha, OVERALL_ALPHA); else - g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, - {.a = ALPHA * OVERALL_ALPHA, .round = rounding, .roundingPower = roundingPower, .discardActive = false, .allowCustomUV = true}); + g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, ALPHA * OVERALL_ALPHA, rounding, roundingPower, false, true); } else { - if (BLUR && m_data.popup) - g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, - { - .surface = m_data.surface, - .a = ALPHA, - .blur = true, - .blurA = m_data.fadeAlpha, - .overallA = OVERALL_ALPHA, - .round = rounding, - .roundingPower = roundingPower, - .allowCustomUV = true, - .blockBlurOptimization = true, - }); + if (BLUR && data.popup) + g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, windowBox, ALPHA, data.surface, rounding, roundingPower, true, data.fadeAlpha, OVERALL_ALPHA); else - g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, - {.a = ALPHA * OVERALL_ALPHA, .round = rounding, .roundingPower = roundingPower, .discardActive = false, .allowCustomUV = true}); + g_pHyprOpenGL->renderTexture(TEXTURE, windowBox, ALPHA * OVERALL_ALPHA, rounding, roundingPower, false, true); } if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) - m_data.surface->presentFeedback(m_data.when, m_data.pMonitor->m_self.lock()); - - // add async (dmabuf) buffers to usedBuffers so we can handle release later - // sync (shm) buffers will be released in commitState, so no need to track them here - if (m_data.surface->m_current.buffer && !m_data.surface->m_current.buffer->isSynchronous()) - g_pHyprRenderer->m_usedAsyncBuffers.emplace_back(m_data.surface->m_current.buffer); + data.surface->presentFeedback(data.when, data.pMonitor->self.lock()); g_pHyprOpenGL->blend(true); } CBox CSurfacePassElement::getTexBox() { - const double outputX = -m_data.pMonitor->m_position.x, outputY = -m_data.pMonitor->m_position.y; + const double outputX = -data.pMonitor->vecPosition.x, outputY = -data.pMonitor->vecPosition.y; - const auto INTERACTIVERESIZEINPROGRESS = m_data.pWindow && g_layoutManager->dragController()->target() && g_layoutManager->dragController()->mode() == MBIND_RESIZE; - auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface); + const auto INTERACTIVERESIZEINPROGRESS = data.pWindow && g_pInputManager->currentlyDraggedWindow && g_pInputManager->dragMode == MBIND_RESIZE; + auto PSURFACE = CWLSurface::fromResource(data.surface); CBox windowBox; - if (m_data.surface && m_data.mainSurface) { - windowBox = {sc(outputX) + m_data.pos.x + m_data.localPos.x, sc(outputY) + m_data.pos.y + m_data.localPos.y, m_data.w, m_data.h}; + if (data.surface && data.mainSurface) { + windowBox = {(int)outputX + data.pos.x + data.localPos.x, (int)outputY + data.pos.y + data.localPos.y, data.w, data.h}; // however, if surface buffer w / h < box, we need to adjust them - const auto PWINDOW = PSURFACE ? Desktop::View::CWindow::fromView(PSURFACE->view()) : nullptr; + const auto PWINDOW = PSURFACE ? PSURFACE->getWindow() : nullptr; // center the surface if it's smaller than the viewport we assign it - if (PSURFACE && !PSURFACE->m_fillIgnoreSmall && PSURFACE->small() /* guarantees PWINDOW */) { - const auto CORRECT = PSURFACE->correctSmallVec(); - const auto SIZE = PSURFACE->getViewporterCorrectedSize(); - const auto REPORTED = PWINDOW->getReportedSize(); + if (PSURFACE && !PSURFACE->m_bFillIgnoreSmall && PSURFACE->small() /* guarantees PWINDOW */) { + const auto CORRECT = PSURFACE->correctSmallVec(); + const auto SIZE = PSURFACE->getViewporterCorrectedSize(); if (!INTERACTIVERESIZEINPROGRESS) { windowBox.translate(CORRECT); - windowBox.width = SIZE.x * (PWINDOW->m_realSize->value().x / REPORTED.x); - windowBox.height = SIZE.y * (PWINDOW->m_realSize->value().y / REPORTED.y); + windowBox.width = SIZE.x * (PWINDOW->m_vRealSize->value().x / PWINDOW->m_vReportedSize.x); + windowBox.height = SIZE.y * (PWINDOW->m_vRealSize->value().y / PWINDOW->m_vReportedSize.y); } else { windowBox.width = SIZE.x; windowBox.height = SIZE.y; } } + } else { // here we clamp to 2, these might be some tiny specks - - const auto SURFSIZE = m_data.surface->m_current.size; - - windowBox = {sc(outputX) + m_data.pos.x + m_data.localPos.x, sc(outputY) + m_data.pos.y + m_data.localPos.y, std::max(sc(SURFSIZE.x), 2.F), - std::max(sc(SURFSIZE.y), 2.F)}; - if (m_data.pWindow && m_data.pWindow->m_realSize->isBeingAnimated() && m_data.surface && !m_data.mainSurface && m_data.squishOversized /* subsurface */) { + windowBox = {(int)outputX + data.pos.x + data.localPos.x, (int)outputY + data.pos.y + data.localPos.y, std::max((float)data.surface->current.size.x, 2.F), + std::max((float)data.surface->current.size.y, 2.F)}; + if (data.pWindow && data.pWindow->m_vRealSize->isBeingAnimated() && data.surface && !data.mainSurface && data.squishOversized /* subsurface */) { // adjust subsurfaces to the window - const auto REPORTED = m_data.pWindow->getReportedSize(); - if (REPORTED.x != 0 && REPORTED.y != 0) { - windowBox.width = (windowBox.width / REPORTED.x) * m_data.pWindow->m_realSize->value().x; - windowBox.height = (windowBox.height / REPORTED.y) * m_data.pWindow->m_realSize->value().y; - } + windowBox.width = (windowBox.width / data.pWindow->m_vReportedSize.x) * data.pWindow->m_vRealSize->value().x; + windowBox.height = (windowBox.height / data.pWindow->m_vReportedSize.y) * data.pWindow->m_vRealSize->value().y; } } - if (m_data.squishOversized) { - if (m_data.localPos.x + windowBox.width > m_data.w) - windowBox.width = m_data.w - m_data.localPos.x; - if (m_data.localPos.y + windowBox.height > m_data.h) - windowBox.height = m_data.h - m_data.localPos.y; + if (data.squishOversized) { + if (data.localPos.x + windowBox.width > data.w) + windowBox.width = data.w - data.localPos.x; + if (data.localPos.y + windowBox.height > data.h) + windowBox.height = data.h - data.localPos.y; } return windowBox; } bool CSurfacePassElement::needsLiveBlur() { - auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface); + auto PSURFACE = CWLSurface::fromResource(data.surface); - const float ALPHA = m_data.alpha * m_data.fadeAlpha * (PSURFACE ? PSURFACE->m_alphaModifier * PSURFACE->m_overallOpacity : 1.F); - const bool BLUR = m_data.blur && (!m_data.texture || !m_data.texture->m_opaque || ALPHA < 1.F); + const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_fAlphaModifier * PSURFACE->m_fOverallOpacity : 1.F); + const bool BLUR = data.blur && (!data.texture || !data.texture->m_bOpaque || ALPHA < 1.F); - if (!m_data.pLS && !m_data.pWindow) + if (!data.pLS && !data.pWindow) return BLUR; - if (m_data.popup) - return BLUR; - - const bool NEWOPTIM = g_pHyprOpenGL->shouldUseNewBlurOptimizations(m_data.pLS, m_data.pWindow); + const bool NEWOPTIM = g_pHyprOpenGL->shouldUseNewBlurOptimizations(data.pLS, data.pWindow); return BLUR && !NEWOPTIM; } bool CSurfacePassElement::needsPrecomputeBlur() { - auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface); + auto PSURFACE = CWLSurface::fromResource(data.surface); - const float ALPHA = m_data.alpha * m_data.fadeAlpha * (PSURFACE ? PSURFACE->m_alphaModifier * PSURFACE->m_overallOpacity : 1.F); - const bool BLUR = m_data.blur && (!m_data.texture || !m_data.texture->m_opaque || ALPHA < 1.F); + const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_fAlphaModifier * PSURFACE->m_fOverallOpacity : 1.F); + const bool BLUR = data.blur && (!data.texture || !data.texture->m_bOpaque || ALPHA < 1.F); - if (!m_data.pLS && !m_data.pWindow) + if (!data.pLS && !data.pWindow) return BLUR; - if (m_data.popup) - return false; - - const bool NEWOPTIM = g_pHyprOpenGL->shouldUseNewBlurOptimizations(m_data.pLS, m_data.pWindow); + const bool NEWOPTIM = g_pHyprOpenGL->shouldUseNewBlurOptimizations(data.pLS, data.pWindow); return BLUR && NEWOPTIM; } @@ -255,29 +214,29 @@ std::optional CSurfacePassElement::boundingBox() { } CRegion CSurfacePassElement::opaqueRegion() { - auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface); + auto PSURFACE = CWLSurface::fromResource(data.surface); - const float ALPHA = m_data.alpha * m_data.fadeAlpha * (PSURFACE ? PSURFACE->m_alphaModifier * PSURFACE->m_overallOpacity : 1.F); + const float ALPHA = data.alpha * data.fadeAlpha * (PSURFACE ? PSURFACE->m_fAlphaModifier * PSURFACE->m_fOverallOpacity : 1.F); if (ALPHA < 1.F) return {}; - if (m_data.surface && m_data.surface->m_current.size == Vector2D{m_data.w, m_data.h}) { - CRegion opaqueSurf = m_data.surface->m_current.opaque.copy().intersect(CBox{{}, {m_data.w, m_data.h}}); + if (data.surface && data.surface->current.size == Vector2D{data.w, data.h}) { + CRegion opaqueSurf = data.surface->current.opaque.copy().intersect(CBox{{}, {data.w, data.h}}); const auto texBox = getTexBox(); - opaqueSurf.scale(texBox.size() / Vector2D{m_data.w, m_data.h}); - return opaqueSurf.translate(m_data.pos + m_data.localPos - m_data.pMonitor->m_position).expand(-m_data.rounding); + opaqueSurf.scale(texBox.size() / Vector2D{data.w, data.h}); + return opaqueSurf.translate(data.pos + data.localPos - data.pMonitor->vecPosition).expand(-data.rounding); } - return m_data.texture && m_data.texture->m_opaque ? boundingBox()->expand(-m_data.rounding) : CRegion{}; + return data.texture && data.texture->m_bOpaque ? boundingBox()->expand(-data.rounding) : CRegion{}; } CRegion CSurfacePassElement::visibleRegion(bool& cancel) { - auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface); + auto PSURFACE = CWLSurface::fromResource(data.surface); if (!PSURFACE) return {}; - const auto& bufferSize = m_data.surface->m_current.bufferSize; + const auto& bufferSize = data.surface->current.bufferSize; auto visibleRegion = PSURFACE->m_visibleRegion.copy(); if (visibleRegion.empty()) @@ -293,8 +252,8 @@ CRegion CSurfacePassElement::visibleRegion(bool& cancel) { // deal with any rounding errors that might come from scaling visibleRegion.expand(1); - auto uvTL = g_pHyprOpenGL->m_renderData.primarySurfaceUVTopLeft; - auto uvBR = g_pHyprOpenGL->m_renderData.primarySurfaceUVBottomRight; + auto uvTL = g_pHyprOpenGL->m_RenderData.primarySurfaceUVTopLeft; + auto uvBR = g_pHyprOpenGL->m_RenderData.primarySurfaceUVBottomRight; if (uvTL == Vector2D(-1, -1)) uvTL = Vector2D(0, 0); @@ -305,18 +264,18 @@ CRegion CSurfacePassElement::visibleRegion(bool& cancel) { visibleRegion.translate(-uvTL * bufferSize); auto texBox = getTexBox(); - texBox.scale(m_data.pMonitor->m_scale); + texBox.scale(data.pMonitor->scale); texBox.round(); visibleRegion.scale((Vector2D(1, 1) / (uvBR - uvTL)) * (texBox.size() / bufferSize)); - visibleRegion.translate((m_data.pos + m_data.localPos - m_data.pMonitor->m_position) * m_data.pMonitor->m_scale); + visibleRegion.translate((data.pos + data.localPos) * data.pMonitor->scale - data.pMonitor->vecPosition); return visibleRegion; } void CSurfacePassElement::discard() { if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) { - Log::logger->log(Log::TRACE, "discard for invisible surface"); - m_data.surface->presentFeedback(m_data.when, m_data.pMonitor->m_self.lock(), true); + Debug::log(TRACE, "discard for invisible surface"); + data.surface->presentFeedback(data.when, data.pMonitor->self.lock(), true); } } diff --git a/src/render/pass/SurfacePassElement.hpp b/src/render/pass/SurfacePassElement.hpp index 058744de..1b6ddb0b 100644 --- a/src/render/pass/SurfacePassElement.hpp +++ b/src/render/pass/SurfacePassElement.hpp @@ -1,22 +1,21 @@ #pragma once #include "PassElement.hpp" #include -#include "../../helpers/time/Time.hpp" class CWLSurfaceResource; -class ITexture; +class CTexture; class CSyncTimeline; class CSurfacePassElement : public IPassElement { public: struct SRenderData { PHLMONITORREF pMonitor; - Time::steady_tp when = Time::steadyNow(); + timespec* when = nullptr; Vector2D pos, localPos; void* data = nullptr; SP surface = nullptr; - SP texture = nullptr; + SP texture = nullptr; bool mainSurface = true; double w = 0, h = 0; int rounding = 0; @@ -65,7 +64,7 @@ class CSurfacePassElement : public IPassElement { } private: - SRenderData m_data; + SRenderData data; CBox getTexBox(); }; diff --git a/src/render/pass/TexPassElement.cpp b/src/render/pass/TexPassElement.cpp index 39853a64..8b577373 100644 --- a/src/render/pass/TexPassElement.cpp +++ b/src/render/pass/TexPassElement.cpp @@ -4,53 +4,28 @@ #include using namespace Hyprutils::Utils; -CTexPassElement::CTexPassElement(const SRenderData& data) : m_data(data) { - ; -} - -CTexPassElement::CTexPassElement(CTexPassElement::SRenderData&& data) : m_data(std::move(data)) { +CTexPassElement::CTexPassElement(const CTexPassElement::SRenderData& data_) : data(data_) { ; } void CTexPassElement::draw(const CRegion& damage) { - g_pHyprOpenGL->pushMonitorTransformEnabled(m_data.flipEndFrame); + g_pHyprOpenGL->m_bEndFrame = data.flipEndFrame; - CScopeGuard x = {[this]() { + CScopeGuard x = {[]() { // - g_pHyprOpenGL->popMonitorTransformEnabled(); - g_pHyprOpenGL->m_renderData.clipBox = {}; - if (m_data.replaceProjection) - g_pHyprOpenGL->m_renderData.monitorProjection = g_pHyprOpenGL->m_renderData.pMonitor->m_projMatrix; - if (m_data.ignoreAlpha.has_value()) - g_pHyprOpenGL->m_renderData.discardMode = 0; + g_pHyprOpenGL->m_bEndFrame = false; + g_pHyprOpenGL->m_RenderData.clipBox = {}; }}; - if (!m_data.clipBox.empty()) - g_pHyprOpenGL->m_renderData.clipBox = m_data.clipBox; + if (!data.clipBox.empty()) + g_pHyprOpenGL->m_RenderData.clipBox = data.clipBox; - if (m_data.replaceProjection) - g_pHyprOpenGL->m_renderData.monitorProjection = *m_data.replaceProjection; - - if (m_data.ignoreAlpha.has_value()) { - g_pHyprOpenGL->m_renderData.discardMode = DISCARD_ALPHA; - g_pHyprOpenGL->m_renderData.discardOpacity = *m_data.ignoreAlpha; - } - - if (m_data.blur) { - g_pHyprOpenGL->renderTexture(m_data.tex, m_data.box, - { - .a = m_data.a, - .blur = true, - .blurA = m_data.blurA, - .overallA = 1.F, - .round = m_data.round, - .roundingPower = m_data.roundingPower, - .blockBlurOptimization = m_data.blockBlurOptimization.value_or(false), - }); - } else { - g_pHyprOpenGL->renderTexture(m_data.tex, m_data.box, - {.damage = m_data.damage.empty() ? &damage : &m_data.damage, .a = m_data.a, .round = m_data.round, .roundingPower = m_data.roundingPower}); - } + if (data.replaceProjection) + g_pHyprOpenGL->m_RenderData.monitorProjection = *data.replaceProjection; + g_pHyprOpenGL->renderTextureInternalWithDamage(data.tex, data.box, data.a, data.damage.empty() ? damage : data.damage, data.round, data.roundingPower, data.syncTimeline, + data.syncPoint); + if (data.replaceProjection) + g_pHyprOpenGL->m_RenderData.monitorProjection = g_pHyprOpenGL->m_RenderData.pMonitor->projMatrix; } bool CTexPassElement::needsLiveBlur() { @@ -62,7 +37,7 @@ bool CTexPassElement::needsPrecomputeBlur() { } std::optional CTexPassElement::boundingBox() { - return m_data.box.copy().scale(1.F / g_pHyprOpenGL->m_renderData.pMonitor->m_scale).round(); + return data.box.copy().scale(1.F / g_pHyprOpenGL->m_RenderData.pMonitor->scale).round(); } CRegion CTexPassElement::opaqueRegion() { diff --git a/src/render/pass/TexPassElement.hpp b/src/render/pass/TexPassElement.hpp index 770e8b05..6faa0872 100644 --- a/src/render/pass/TexPassElement.hpp +++ b/src/render/pass/TexPassElement.hpp @@ -3,29 +3,26 @@ #include class CWLSurfaceResource; -class ITexture; +class CTexture; class CSyncTimeline; class CTexPassElement : public IPassElement { public: struct SRenderData { - SP tex; + SP tex; CBox box; - float a = 1.F; - float blurA = 1.F; + float a = 1.F; CRegion damage; int round = 0; float roundingPower = 2.0f; bool flipEndFrame = false; + SP syncTimeline; + int64_t syncPoint = 0; std::optional replaceProjection; CBox clipBox; - bool blur = false; - std::optional ignoreAlpha; - std::optional blockBlurOptimization; }; CTexPassElement(const SRenderData& data); - CTexPassElement(SRenderData&& data); virtual ~CTexPassElement() = default; virtual void draw(const CRegion& damage); @@ -40,5 +37,5 @@ class CTexPassElement : public IPassElement { } private: - SRenderData m_data; + SRenderData data; }; diff --git a/src/render/pass/TextureMatteElement.cpp b/src/render/pass/TextureMatteElement.cpp index 8023df8b..3d927357 100644 --- a/src/render/pass/TextureMatteElement.cpp +++ b/src/render/pass/TextureMatteElement.cpp @@ -1,19 +1,19 @@ #include "TextureMatteElement.hpp" #include "../OpenGL.hpp" -CTextureMatteElement::CTextureMatteElement(const CTextureMatteElement::STextureMatteData& data_) : m_data(data_) { +CTextureMatteElement::CTextureMatteElement(const CTextureMatteElement::STextureMatteData& data_) : data(data_) { ; } void CTextureMatteElement::draw(const CRegion& damage) { - if (m_data.disableTransformAndModify) { - g_pHyprOpenGL->pushMonitorTransformEnabled(true); + if (data.disableTransformAndModify) { + g_pHyprOpenGL->setMonitorTransformEnabled(true); g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb); + g_pHyprOpenGL->renderTextureMatte(data.tex, data.box, *data.fb); g_pHyprOpenGL->setRenderModifEnabled(true); - g_pHyprOpenGL->popMonitorTransformEnabled(); + g_pHyprOpenGL->setMonitorTransformEnabled(false); } else - g_pHyprOpenGL->renderTextureMatte(m_data.tex, m_data.box, m_data.fb); + g_pHyprOpenGL->renderTextureMatte(data.tex, data.box, *data.fb); } bool CTextureMatteElement::needsLiveBlur() { diff --git a/src/render/pass/TextureMatteElement.hpp b/src/render/pass/TextureMatteElement.hpp index 273c6474..bf673946 100644 --- a/src/render/pass/TextureMatteElement.hpp +++ b/src/render/pass/TextureMatteElement.hpp @@ -2,14 +2,14 @@ #include "PassElement.hpp" #include "../Framebuffer.hpp" -class ITexture; +class CTexture; class CTextureMatteElement : public IPassElement { public: struct STextureMatteData { CBox box; - SP tex; - SP fb; + SP tex; + SP fb; bool disableTransformAndModify = false; }; @@ -25,5 +25,5 @@ class CTextureMatteElement : public IPassElement { } private: - STextureMatteData m_data; + STextureMatteData data; }; \ No newline at end of file diff --git a/src/render/shaders/Border.hpp b/src/render/shaders/Border.hpp new file mode 100644 index 00000000..3b9a91f0 --- /dev/null +++ b/src/render/shaders/Border.hpp @@ -0,0 +1,187 @@ +#pragma once + +#include +#include +#include "SharedValues.hpp" + +// makes a stencil without corners +inline const std::string FRAGBORDER1 = R"#( +precision highp float; +varying vec4 v_color; +varying vec2 v_texcoord; + +uniform vec2 topLeft; +uniform vec2 fullSize; +uniform vec2 fullSizeUntransformed; +uniform float radius; +uniform float radiusOuter; +uniform float roundingPower; +uniform float thick; + +// Gradients are in OkLabA!!!! {l, a, b, alpha} +uniform vec4 gradient[10]; +uniform vec4 gradient2[10]; +uniform int gradientLength; +uniform int gradient2Length; +uniform float angle; +uniform float angle2; +uniform float gradientLerp; +uniform float alpha; + +float linearToGamma(float x) { + return x >= 0.0031308 ? 1.055 * pow(x, 0.416666666) - 0.055 : 12.92 * x; +} + +vec4 okLabAToSrgb(vec4 lab) { + float l = pow(lab[0] + lab[1] * 0.3963377774 + lab[2] * 0.2158037573, 3.0); + float m = pow(lab[0] + lab[1] * (-0.1055613458) + lab[2] * (-0.0638541728), 3.0); + float s = pow(lab[0] + lab[1] * (-0.0894841775) + lab[2] * (-1.2914855480), 3.0); + + return vec4(linearToGamma(l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292), + linearToGamma(l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965)), + linearToGamma(l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010), + lab[3]); +} + +vec4 getOkColorForCoordArray1(vec2 normalizedCoord) { + if (gradientLength < 2) + return gradient[0]; + + float finalAng = 0.0; + + if (angle > 4.71 /* 270 deg */) { + normalizedCoord[1] = 1.0 - normalizedCoord[1]; + finalAng = 6.28 - angle; + } else if (angle > 3.14 /* 180 deg */) { + normalizedCoord[0] = 1.0 - normalizedCoord[0]; + normalizedCoord[1] = 1.0 - normalizedCoord[1]; + finalAng = angle - 3.14; + } else if (angle > 1.57 /* 90 deg */) { + normalizedCoord[0] = 1.0 - normalizedCoord[0]; + finalAng = 3.14 - angle; + } else { + finalAng = angle; + } + + float sine = sin(finalAng); + + float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradientLength - 1); + int bottom = int(floor(progress)); + int top = bottom + 1; + + return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress); +} + +vec4 getOkColorForCoordArray2(vec2 normalizedCoord) { + if (gradient2Length < 2) + return gradient2[0]; + + float finalAng = 0.0; + + if (angle2 > 4.71 /* 270 deg */) { + normalizedCoord[1] = 1.0 - normalizedCoord[1]; + finalAng = 6.28 - angle; + } else if (angle2 > 3.14 /* 180 deg */) { + normalizedCoord[0] = 1.0 - normalizedCoord[0]; + normalizedCoord[1] = 1.0 - normalizedCoord[1]; + finalAng = angle - 3.14; + } else if (angle2 > 1.57 /* 90 deg */) { + normalizedCoord[0] = 1.0 - normalizedCoord[0]; + finalAng = 3.14 - angle2; + } else { + finalAng = angle2; + } + + float sine = sin(finalAng); + + float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradient2Length - 1); + int bottom = int(floor(progress)); + int top = bottom + 1; + + return gradient2[top] * (progress - float(bottom)) + gradient2[bottom] * (float(top) - progress); +} + +vec4 getColorForCoord(vec2 normalizedCoord) { + vec4 result1 = getOkColorForCoordArray1(normalizedCoord); + + if (gradient2Length <= 0) + return okLabAToSrgb(result1); + + vec4 result2 = getOkColorForCoordArray2(normalizedCoord); + + return okLabAToSrgb(mix(result1, result2, gradientLerp)); +} + +void main() { + + highp vec2 pixCoord = vec2(gl_FragCoord); + highp vec2 pixCoordOuter = pixCoord; + highp vec2 originalPixCoord = v_texcoord; + originalPixCoord *= fullSizeUntransformed; + float additionalAlpha = 1.0; + + vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); + + bool done = false; + + pixCoord -= topLeft + fullSize * 0.5; + pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; + pixCoordOuter = pixCoord; + pixCoord -= fullSize * 0.5 - radius; + pixCoordOuter -= fullSize * 0.5 - radiusOuter; + + // center the pixes dont make it top-left + pixCoord += vec2(1.0, 1.0) / fullSize; + pixCoordOuter += vec2(1.0, 1.0) / fullSize; + + if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { + // smoothing constant for the edge: more = blurrier, but smoother + const float SMOOTHING_CONSTANT = )#" + + std::format("{:.7f}", SHADER_ROUNDED_SMOOTHING_FACTOR) + R"#(; + + float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower); + float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower); + float h = (thick / 2.0); + + if (dist < radius - h) { + // lower + float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + additionalAlpha *= normalized; + done = true; + } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { + // higher + float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + additionalAlpha *= normalized; + done = true; + } else if (distOuter < radiusOuter - h) { + additionalAlpha = 1.0; + done = true; + } + } + + // now check for other shit + if (!done) { + // distance to all straight bb borders + float distanceT = originalPixCoord[1]; + float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; + float distanceL = originalPixCoord[0]; + float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; + + // get the smallest + float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); + + if (smallest > thick) + discard; + } + + if (additionalAlpha == 0.0) + discard; + + pixColor = getColorForCoord(v_texcoord); + pixColor.rgb *= pixColor[3]; + + pixColor *= alpha * additionalAlpha; + + gl_FragColor = pixColor; +} +)#"; diff --git a/src/render/shaders/CM.frag b/src/render/shaders/CM.frag new file mode 100644 index 00000000..aa26f97d --- /dev/null +++ b/src/render/shaders/CM.frag @@ -0,0 +1,440 @@ +R"#( +#version 320 es +//#extension GL_OES_EGL_image_external : require + +precision highp float; +in vec2 v_texcoord; +uniform sampler2D tex; +//uniform samplerExternalOES texture0; + +uniform int texType; // eTextureType: 0 - rgba, 1 - rgbx, 2 - ext +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction +uniform mat4x2 sourcePrimaries; +uniform mat4x2 targetPrimaries; +uniform float maxLuminance; +uniform float dstMaxLuminance; +uniform float dstRefLuminance; +uniform float sdrSaturation; +uniform float sdrBrightnessMultiplier; + +uniform float alpha; + +uniform vec2 topLeft; +uniform vec2 fullSize; +uniform float radius; +uniform float roundingPower; + +uniform int discardOpaque; +uniform int discardAlpha; +uniform float discardAlphaValue; + +uniform int applyTint; +uniform vec3 tint; + +//enum eTransferFunction +#define CM_TRANSFER_FUNCTION_BT1886 1 +#define CM_TRANSFER_FUNCTION_GAMMA22 2 +#define CM_TRANSFER_FUNCTION_GAMMA28 3 +#define CM_TRANSFER_FUNCTION_ST240 4 +#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5 +#define CM_TRANSFER_FUNCTION_LOG_100 6 +#define CM_TRANSFER_FUNCTION_LOG_316 7 +#define CM_TRANSFER_FUNCTION_XVYCC 8 +#define CM_TRANSFER_FUNCTION_SRGB 9 +#define CM_TRANSFER_FUNCTION_EXT_SRGB 10 +#define CM_TRANSFER_FUNCTION_ST2084_PQ 11 +#define CM_TRANSFER_FUNCTION_ST428 12 +#define CM_TRANSFER_FUNCTION_HLG 13 + +// sRGB constants +#define SRGB_POW 2.4 +#define SRGB_INV_POW (1.0 / SRGB_POW) +#define SRGB_D_CUT 0.04045 +#define SRGB_E_CUT 0.0031308 +#define SRGB_LO 12.92 +#define SRGB_HI 1.055 +#define SRGB_HI_ADD 0.055 + +// PQ constants +#define PQ_M1 0.1593017578125 +#define PQ_M2 78.84375 +#define PQ_INV_M1 (1.0 / PQ_M1) +#define PQ_INV_M2 (1.0 / PQ_M2) +#define PQ_C1 0.8359375 +#define PQ_C2 18.8515625 +#define PQ_C3 18.6875 + +// HLG constants +#define HLG_D_CUT (1.0 / 12.0) +#define HLG_E_CUT (sqrt(3.0) * pow(HLG_D_CUT, 0.5)) +#define HLG_A 0.17883277 +#define HLG_B 0.28466892 +#define HLG_C 0.55991073 + +#define SDR_MIN_LUMINANCE 0.2 +#define SDR_MAX_LUMINANCE 80.0 +#define HDR_MIN_LUMINANCE 0.005 +#define HDR_MAX_LUMINANCE 10000.0 +#define HLG_MAX_LUMINANCE 1000.0 + +// smoothing constant for the edge: more = blurrier, but smoother +#define M_PI 3.1415926535897932384626433832795 +#define M_E 2.718281828459045 +#define SMOOTHING_CONSTANT (M_PI / 5.34665792551) + +vec4 rounding(vec4 color) { + highp vec2 pixCoord = vec2(gl_FragCoord); + pixCoord -= topLeft + fullSize * 0.5; + pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; + pixCoord -= fullSize * 0.5 - radius; + pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix dont make it top-left + + if (pixCoord.x + pixCoord.y > radius) { + float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower); + + if (dist > radius + SMOOTHING_CONSTANT) + discard; + + float normalized = 1.0 - smoothstep(0.0, 1.0, (dist - radius + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + + color *= normalized; + } + + return color; +} + +vec3 xy2xyz(vec2 xy) { + if (xy.y == 0.0) + return vec3(0.0, 0.0, 0.0); + + return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y); +} + +vec4 saturate(vec4 color, mat3 primaries, float saturation) { + if (saturation == 1.0) + return color; + vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]); + float Y = dot(color.rgb, brightness); + return vec4(mix(vec3(Y), color.rgb, saturation), color[3]); +} + +vec3 toLinearRGB(vec3 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + return color; + + bvec3 isLow; + vec3 lo; + vec3 hi; + switch (tf) { + case CM_TRANSFER_FUNCTION_ST2084_PQ: + vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2)); + return pow( + (max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E), + vec3(PQ_INV_M1) + ); + case CM_TRANSFER_FUNCTION_GAMMA22: + return pow(max(color.rgb, vec3(0.0)), vec3(2.2)); + case CM_TRANSFER_FUNCTION_GAMMA28: + return pow(max(color.rgb, vec3(0.0)), vec3(2.8)); + case CM_TRANSFER_FUNCTION_HLG: + isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT)); + lo = sqrt(3.0) * pow(color.rgb, vec3(0.5)); + hi = HLG_A * log(12.0 * color.rgb - HLG_B) + HLG_C; + return mix(hi, lo, isLow); + case CM_TRANSFER_FUNCTION_BT1886: + case CM_TRANSFER_FUNCTION_ST240: + case CM_TRANSFER_FUNCTION_LOG_100: + case CM_TRANSFER_FUNCTION_LOG_316: + case CM_TRANSFER_FUNCTION_XVYCC: + case CM_TRANSFER_FUNCTION_EXT_SRGB: + case CM_TRANSFER_FUNCTION_ST428: + + case CM_TRANSFER_FUNCTION_SRGB: + default: + isLow = lessThanEqual(color.rgb, vec3(SRGB_D_CUT)); + lo = color.rgb / SRGB_LO; + hi = pow((color.rgb + SRGB_HI_ADD) / SRGB_HI, vec3(SRGB_POW)); + return mix(hi, lo, isLow); + } +} + +vec4 toLinear(vec4 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + return color; + + color.rgb /= max(color.a, 0.001); + color.rgb = toLinearRGB(color.rgb, tf); + color.rgb *= color.a; + return color; +} + +vec4 toNit(vec4 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + color.rgb = color.rgb * SDR_MAX_LUMINANCE; + else { + + switch (tf) { + case CM_TRANSFER_FUNCTION_ST2084_PQ: + color.rgb = color.rgb * (HDR_MAX_LUMINANCE - HDR_MIN_LUMINANCE) + HDR_MIN_LUMINANCE; break; + case CM_TRANSFER_FUNCTION_HLG: + color.rgb = color.rgb * (HLG_MAX_LUMINANCE - HDR_MIN_LUMINANCE) + HDR_MIN_LUMINANCE; break; + case CM_TRANSFER_FUNCTION_GAMMA22: + case CM_TRANSFER_FUNCTION_GAMMA28: + case CM_TRANSFER_FUNCTION_BT1886: + case CM_TRANSFER_FUNCTION_ST240: + case CM_TRANSFER_FUNCTION_LOG_100: + case CM_TRANSFER_FUNCTION_LOG_316: + case CM_TRANSFER_FUNCTION_XVYCC: + case CM_TRANSFER_FUNCTION_EXT_SRGB: + case CM_TRANSFER_FUNCTION_ST428: + case CM_TRANSFER_FUNCTION_SRGB: + default: + color.rgb = color.rgb * (SDR_MAX_LUMINANCE - SDR_MIN_LUMINANCE) + SDR_MIN_LUMINANCE; break; + } + } + return color; +} + +vec3 fromLinearRGB(vec3 color, int tf) { + bvec3 isLow; + vec3 lo; + vec3 hi; + + switch (tf) { + case CM_TRANSFER_FUNCTION_EXT_LINEAR: + return color; + case CM_TRANSFER_FUNCTION_ST2084_PQ: + vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1)); + return pow( + (vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E), + vec3(PQ_M2) + ); + break; + case CM_TRANSFER_FUNCTION_GAMMA22: + return pow(max(color.rgb, vec3(0.0)), vec3(1.0 / 2.2)); + case CM_TRANSFER_FUNCTION_GAMMA28: + return pow(max(color.rgb, vec3(0.0)), vec3(1.0 / 2.8)); + case CM_TRANSFER_FUNCTION_HLG: + isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT)); + lo = pow(color.rgb / sqrt(3.0), vec3(2.0)); + hi = (pow(vec3(M_E), (color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0; + return mix(hi, lo, isLow); + case CM_TRANSFER_FUNCTION_BT1886: + case CM_TRANSFER_FUNCTION_ST240: + case CM_TRANSFER_FUNCTION_LOG_100: + case CM_TRANSFER_FUNCTION_LOG_316: + case CM_TRANSFER_FUNCTION_XVYCC: + case CM_TRANSFER_FUNCTION_EXT_SRGB: + case CM_TRANSFER_FUNCTION_ST428: + + case CM_TRANSFER_FUNCTION_SRGB: + default: + isLow = lessThanEqual(color.rgb, vec3(SRGB_E_CUT)); + lo = color.rgb * SRGB_LO; + hi = pow(color.rgb, vec3(SRGB_INV_POW)) * SRGB_HI - SRGB_HI_ADD; + return mix(hi, lo, isLow); + } +} + +vec4 fromLinear(vec4 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + return color; + + color.rgb /= max(color.a, 0.001); + color.rgb = fromLinearRGB(color.rgb, tf); + color.rgb *= color.a; + return color; +} + +vec4 fromLinearNit(vec4 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + color.rgb = color.rgb / SDR_MAX_LUMINANCE; + else { + color.rgb /= max(color.a, 0.001); + + switch (tf) { + case CM_TRANSFER_FUNCTION_ST2084_PQ: + color.rgb = (color.rgb - HDR_MIN_LUMINANCE) / (HDR_MAX_LUMINANCE - HDR_MIN_LUMINANCE); break; + case CM_TRANSFER_FUNCTION_HLG: + color.rgb = (color.rgb - HDR_MIN_LUMINANCE) / (HLG_MAX_LUMINANCE - HDR_MIN_LUMINANCE); break; + case CM_TRANSFER_FUNCTION_GAMMA22: + case CM_TRANSFER_FUNCTION_GAMMA28: + case CM_TRANSFER_FUNCTION_BT1886: + case CM_TRANSFER_FUNCTION_ST240: + case CM_TRANSFER_FUNCTION_LOG_100: + case CM_TRANSFER_FUNCTION_LOG_316: + case CM_TRANSFER_FUNCTION_XVYCC: + case CM_TRANSFER_FUNCTION_EXT_SRGB: + case CM_TRANSFER_FUNCTION_ST428: + case CM_TRANSFER_FUNCTION_SRGB: + default: + color.rgb = (color.rgb - SDR_MIN_LUMINANCE) / (SDR_MAX_LUMINANCE - SDR_MIN_LUMINANCE); break; + } + + color.rgb = fromLinearRGB(color.rgb, tf); + + color.rgb *= color.a; + } + return color; +} + +mat3 primaries2xyz(mat4x2 primaries) { + vec3 r = xy2xyz(primaries[0]); + vec3 g = xy2xyz(primaries[1]); + vec3 b = xy2xyz(primaries[2]); + vec3 w = xy2xyz(primaries[3]); + + mat3 invMat = inverse( + mat3( + r.x, r.y, r.z, + g.x, g.y, g.z, + b.x, b.y, b.z + ) + ); + + vec3 s = invMat * w; + + return mat3( + s.r * r.x, s.r * r.y, s.r * r.z, + s.g * g.x, s.g * g.y, s.g * g.z, + s.b * b.x, s.b * b.y, s.b * b.z + ); +} + +// const vec2 D65 = vec2(0.3127, 0.3290); +const mat3 Bradford = mat3( + 0.8951, 0.2664, -0.1614, + -0.7502, 1.7135, 0.0367, + 0.0389, -0.0685, 1.0296 +); +const mat3 BradfordInv = inverse(Bradford); + +mat3 adaptWhite(vec2 src, vec2 dst) { + if (src == dst) + return mat3( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + ); + + vec3 srcXYZ = xy2xyz(src); + vec3 dstXYZ = xy2xyz(dst); + vec3 factors = (Bradford * dstXYZ) / (Bradford * srcXYZ); + + return BradfordInv * mat3( + factors.x, 0.0, 0.0, + 0.0, factors.y, 0.0, + 0.0, 0.0, factors.z + ) * Bradford; +} + +vec4 convertPrimaries(vec4 color, mat3 src, vec2 srcWhite, mat3 dst, vec2 dstWhite) { + mat3 convMat = inverse(dst) * adaptWhite(srcWhite, dstWhite) * src; + return vec4(convMat * color.rgb, color[3]); +} + +const mat3 BT2020toLMS = mat3( + 0.3592, 0.6976, -0.0358, + -0.1922, 1.1004, 0.0755, + 0.0070, 0.0749, 0.8434 +); +const mat3 LMStoBT2020 = inverse(BT2020toLMS); + +const mat3 ICtCpPQ = transpose(mat3( + 2048.0, 2048.0, 0.0, + 6610.0, -13613.0, 7003.0, + 17933.0, -17390.0, -543.0 +) / 4096.0); +const mat3 ICtCpPQInv = inverse(ICtCpPQ); + +const mat3 ICtCpHLG = transpose(mat3( + 2048.0, 2048.0, 0.0, + 3625.0, -7465.0, 3840.0, + 9500.0, -9212.0, -288.0 +) / 4096.0); +const mat3 ICtCpHLGInv = inverse(ICtCpHLG); + +vec4 tonemap(vec4 color, mat3 dstXYZ) { + if (maxLuminance < dstMaxLuminance * 1.01) + return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]); + + mat3 toLMS = BT2020toLMS * dstXYZ; + mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020; + + vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb; + vec3 ICtCp = ICtCpPQ * lms; + + float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2); + float luminance = pow( + (max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), + PQ_INV_M1 + ) * HDR_MAX_LUMINANCE; + + float srcScale = maxLuminance / dstRefLuminance; + float dstScale = dstMaxLuminance / dstRefLuminance; + + float minScale = min(srcScale, 1.5); + float dimming = 1.0 / clamp(minScale / dstScale, 1.0, minScale); + float refLuminance = dstRefLuminance * dimming; + + float low = min(luminance * dimming, refLuminance); + float highlight = clamp((luminance / dstRefLuminance - 1.0) / (srcScale - 1.0), 0.0, 1.0); + float high = log(highlight * (M_E - 1.0) + 1.0) * (dstMaxLuminance - refLuminance); + luminance = low + high; + + E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_M1); + ICtCp[0] = pow( + (PQ_C1 + PQ_C2 * E) / (1.0 + PQ_C3 * E), + PQ_M2 + ) / HDR_MAX_LUMINANCE; + return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE, color[3]); +} + +layout(location = 0) out vec4 fragColor; +void main() { + vec4 pixColor; + if (texType == 1) + pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0); +// else if (texType == 2) +// pixColor = texture(texture0, v_texcoord); + else // assume rgba + pixColor = texture(tex, v_texcoord); + + if (discardOpaque == 1 && pixColor[3] * alpha == 1.0) + discard; + + if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue) + discard; + + pixColor.rgb /= max(pixColor.a, 0.001); + pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF); + mat3 srcxyz = primaries2xyz(sourcePrimaries); + mat3 dstxyz; + + if (sourcePrimaries == targetPrimaries) + dstxyz = srcxyz; + else { + dstxyz = primaries2xyz(targetPrimaries); + pixColor = convertPrimaries(pixColor, srcxyz, sourcePrimaries[3], dstxyz, targetPrimaries[3]); + } + + pixColor = toNit(pixColor, sourceTF); + pixColor.rgb *= pixColor.a; + pixColor = tonemap(pixColor, dstxyz); + + if (sourceTF == CM_TRANSFER_FUNCTION_SRGB && targetTF == CM_TRANSFER_FUNCTION_ST2084_PQ) + pixColor = saturate(pixColor, srcxyz, sdrSaturation); + + pixColor *= sdrBrightnessMultiplier; + pixColor = fromLinearNit(pixColor, targetTF); + + if (applyTint == 1) + pixColor = vec4(pixColor.rgb * tint.rgb, pixColor[3]); + + if (radius > 0.0) + pixColor = rounding(pixColor); + + fragColor = pixColor * alpha; +} +)#" diff --git a/src/render/shaders/Shadow.hpp b/src/render/shaders/Shadow.hpp new file mode 100644 index 00000000..c04d4bf3 --- /dev/null +++ b/src/render/shaders/Shadow.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include + +inline const std::string FRAGSHADOW = R"#( +precision highp float; +varying vec4 v_color; +varying vec2 v_texcoord; + +uniform vec2 topLeft; +uniform vec2 bottomRight; +uniform vec2 fullSize; +uniform float radius; +uniform float roundingPower; +uniform float range; +uniform float shadowPower; + +float pixAlphaRoundedDistance(float distanceToCorner) { + if (distanceToCorner > radius) { + return 0.0; + } + + if (distanceToCorner > radius - range) { + return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think? + } + + return 1.0; +} + +float modifiedLength(vec2 a) { + return pow(pow(abs(a.x),roundingPower)+pow(abs(a.y),roundingPower),1.0/roundingPower); +} + +void main() { + + vec4 pixColor = v_color; + float originalAlpha = pixColor[3]; + + bool done = false; + + vec2 pixCoord = fullSize * v_texcoord; + + // ok, now we check the distance to a border. + + if (pixCoord[0] < topLeft[0]) { + if (pixCoord[1] < topLeft[1]) { + // top left + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft)); + done = true; + } else if (pixCoord[1] > bottomRight[1]) { + // bottom left + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1]))); + done = true; + } + } else if (pixCoord[0] > bottomRight[0]) { + if (pixCoord[1] < topLeft[1]) { + // top right + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1]))); + done = true; + } else if (pixCoord[1] > bottomRight[1]) { + // bottom right + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - bottomRight)); + done = true; + } + } + + if (!done) { + // distance to all straight bb borders + float distanceT = pixCoord[1]; + float distanceB = fullSize[1] - pixCoord[1]; + float distanceL = pixCoord[0]; + float distanceR = fullSize[0] - pixCoord[0]; + + // get the smallest + float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); + + if (smallest < range) { + pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower); + } + } + + if (pixColor[3] == 0.0) { + discard; return; + } + + // premultiply + pixColor.rgb *= pixColor[3]; + + gl_FragColor = pixColor; +})#"; diff --git a/src/render/shaders/SharedValues.hpp b/src/render/shaders/SharedValues.hpp new file mode 100644 index 00000000..9a74c892 --- /dev/null +++ b/src/render/shaders/SharedValues.hpp @@ -0,0 +1,3 @@ +#pragma once + +constexpr float SHADER_ROUNDED_SMOOTHING_FACTOR = M_PI / 5.34665792551; \ No newline at end of file diff --git a/src/render/shaders/Textures.hpp b/src/render/shaders/Textures.hpp new file mode 100644 index 00000000..002c3459 --- /dev/null +++ b/src/render/shaders/Textures.hpp @@ -0,0 +1,541 @@ +#pragma once + +#include +#include +#include "SharedValues.hpp" + +inline static constexpr auto ROUNDED_SHADER_FUNC = [](const std::string colorVarName) -> std::string { + return R"#( + + // shoutout me: I fixed this shader being a bit pixelated while watching hentai + + highp vec2 pixCoord = vec2(gl_FragCoord); + pixCoord -= topLeft + fullSize * 0.5; + pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; + pixCoord -= fullSize * 0.5 - radius; + pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix dont make it top-left + + // smoothing constant for the edge: more = blurrier, but smoother + const float SMOOTHING_CONSTANT = )#" + + std::format("{:.7f}", SHADER_ROUNDED_SMOOTHING_FACTOR) + R"#(; + + if (pixCoord.x + pixCoord.y > radius) { + + float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower); + + if (dist > radius + SMOOTHING_CONSTANT) + discard; + + float normalized = 1.0 - smoothstep(0.0, 1.0, (dist - radius + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + + )#" + + colorVarName + R"#( = )#" + colorVarName + R"#( * normalized; + } +)#"; +}; + +inline const std::string QUADVERTSRC = R"#( +uniform mat3 proj; +uniform vec4 color; +attribute vec2 pos; +attribute vec2 texcoord; +attribute vec2 texcoordMatte; +varying vec4 v_color; +varying vec2 v_texcoord; +varying vec2 v_texcoordMatte; + +void main() { + gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); + v_color = color; + v_texcoord = texcoord; + v_texcoordMatte = texcoordMatte; +})#"; + +inline const std::string QUADFRAGSRC = R"#( +precision highp float; +varying vec4 v_color; + +uniform vec2 topLeft; +uniform vec2 fullSize; +uniform float radius; +uniform float roundingPower; + +void main() { + + vec4 pixColor = v_color; + + if (radius > 0.0) { + )#" + + ROUNDED_SHADER_FUNC("pixColor") + R"#( + } + + gl_FragColor = pixColor; +})#"; + +inline const std::string TEXVERTSRC = R"#( +uniform mat3 proj; +attribute vec2 pos; +attribute vec2 texcoord; +varying vec2 v_texcoord; + +void main() { + gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); + v_texcoord = texcoord; +})#"; + +inline const std::string TEXVERTSRC320 = R"#(#version 320 es +uniform mat3 proj; +in vec2 pos; +in vec2 texcoord; +out vec2 v_texcoord; + +void main() { + gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); + v_texcoord = texcoord; +})#"; + +inline const std::string TEXFRAGSRCCM = +#include "CM.frag" + ; + +inline const std::string TEXFRAGSRCRGBA = R"#( +precision highp float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; +uniform float alpha; + +uniform vec2 topLeft; +uniform vec2 fullSize; +uniform float radius; +uniform float roundingPower; + +uniform int discardOpaque; +uniform int discardAlpha; +uniform float discardAlphaValue; + +uniform int applyTint; +uniform vec3 tint; + +void main() { + + vec4 pixColor = texture2D(tex, v_texcoord); + + if (discardOpaque == 1 && pixColor[3] * alpha == 1.0) + discard; + + if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue) + discard; + + if (applyTint == 1) { + pixColor[0] = pixColor[0] * tint[0]; + pixColor[1] = pixColor[1] * tint[1]; + pixColor[2] = pixColor[2] * tint[2]; + } + + if (radius > 0.0) { + )#" + + ROUNDED_SHADER_FUNC("pixColor") + R"#( + } + + gl_FragColor = pixColor * alpha; +})#"; + +inline const std::string TEXFRAGSRCRGBAPASSTHRU = R"#( +precision highp float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +void main() { + gl_FragColor = texture2D(tex, v_texcoord); +})#"; + +inline const std::string TEXFRAGSRCRGBAMATTE = R"#( +precision highp float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; +uniform sampler2D texMatte; + +void main() { + gl_FragColor = texture2D(tex, v_texcoord) * texture2D(texMatte, v_texcoord)[0]; // I know it only uses R, but matte should be black/white anyways. +})#"; + +inline const std::string TEXFRAGSRCRGBX = R"#( +precision highp float; +varying vec2 v_texcoord; +uniform sampler2D tex; +uniform float alpha; + +uniform vec2 topLeft; +uniform vec2 fullSize; +uniform float radius; +uniform float roundingPower; + +uniform int discardOpaque; +uniform int discardAlpha; +uniform int discardAlphaValue; + +uniform int applyTint; +uniform vec3 tint; + +void main() { + + if (discardOpaque == 1 && alpha == 1.0) + discard; + + vec4 pixColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0); + + if (applyTint == 1) { + pixColor[0] = pixColor[0] * tint[0]; + pixColor[1] = pixColor[1] * tint[1]; + pixColor[2] = pixColor[2] * tint[2]; + } + + if (radius > 0.0) { + )#" + + ROUNDED_SHADER_FUNC("pixColor") + R"#( + } + + gl_FragColor = pixColor * alpha; +})#"; + +inline const std::string FRAGBLUR1 = R"#( +#version 100 +precision highp float; +varying highp vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +uniform float radius; +uniform vec2 halfpixel; +uniform int passes; +uniform float vibrancy; +uniform float vibrancy_darkness; + +// see http://alienryderflex.com/hsp.html +const float Pr = 0.299; +const float Pg = 0.587; +const float Pb = 0.114; + +// Y is "v" ( brightness ). X is "s" ( saturation ) +// see https://www.desmos.com/3d/a88652b9a4 +// Determines if high brightness or high saturation is more important +const float a = 0.93; +const float b = 0.11; +const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors +// + +// http://www.flong.com/archive/texts/code/shapers_circ/ +float doubleCircleSigmoid(float x, float a) { + a = clamp(a, 0.0, 1.0); + + float y = .0; + if (x <= a) { + y = a - sqrt(a * a - x * x); + } else { + y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.)); + } + return y; +} + +vec3 rgb2hsl(vec3 col) { + float red = col.r; + float green = col.g; + float blue = col.b; + + float minc = min(col.r, min(col.g, col.b)); + float maxc = max(col.r, max(col.g, col.b)); + float delta = maxc - minc; + + float lum = (minc + maxc) * 0.5; + float sat = 0.0; + float hue = 0.0; + + if (lum > 0.0 && lum < 1.0) { + float mul = (lum < 0.5) ? (lum) : (1.0 - lum); + sat = delta / (mul * 2.0); + } + + if (delta > 0.0) { + vec3 maxcVec = vec3(maxc); + vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red))); + vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta; + + hue += dot(adds, masks); + hue /= 6.0; + + if (hue < 0.0) + hue += 1.0; + } + + return vec3(hue, sat, lum); +} + +vec3 hsl2rgb(vec3 col) { + const float onethird = 1.0 / 3.0; + const float twothird = 2.0 / 3.0; + const float rcpsixth = 6.0; + + float hue = col.x; + float sat = col.y; + float lum = col.z; + + vec3 xt = vec3(0.0); + + if (hue < onethird) { + xt.r = rcpsixth * (onethird - hue); + xt.g = rcpsixth * hue; + xt.b = 0.0; + } else if (hue < twothird) { + xt.r = 0.0; + xt.g = rcpsixth * (twothird - hue); + xt.b = rcpsixth * (hue - onethird); + } else + xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue)); + + xt = min(xt, 1.0); + + float sat2 = 2.0 * sat; + float satinv = 1.0 - sat; + float luminv = 1.0 - lum; + float lum2m1 = (2.0 * lum) - 1.0; + vec3 ct = (sat2 * xt) + satinv; + + vec3 rgb; + if (lum >= 0.5) + rgb = (luminv * ct) + lum2m1; + else + rgb = lum * ct; + + return rgb; +} + +void main() { + vec2 uv = v_texcoord * 2.0; + + vec4 sum = texture2D(tex, uv) * 4.0; + sum += texture2D(tex, uv - halfpixel.xy * radius); + sum += texture2D(tex, uv + halfpixel.xy * radius); + sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); + sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); + + vec4 color = sum / 8.0; + + if (vibrancy == 0.0) { + gl_FragColor = color; + } else { + // Invert it so that it correctly maps to the config setting + float vibrancy_darkness1 = 1.0 - vibrancy_darkness; + + // Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest. + vec3 hsl = rgb2hsl(color.rgb); + // Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow + float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1); + + float b1 = b * vibrancy_darkness1; + float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0; + + float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0); + + vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2])); + + gl_FragColor = vec4(newColor, color[3]); + } +} +)#"; + +inline const std::string FRAGBLUR2 = R"#( +#version 100 +precision highp float; +varying highp vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +uniform float radius; +uniform vec2 halfpixel; + +void main() { + vec2 uv = v_texcoord / 2.0; + + vec4 sum = texture2D(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); + + sum += texture2D(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; + sum += texture2D(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); + sum += texture2D(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; + sum += texture2D(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); + sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; + sum += texture2D(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); + sum += texture2D(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; + + gl_FragColor = sum / 12.0; +} +)#"; + +inline const std::string FRAGBLURPREPARE = R"#( +precision highp float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +uniform float contrast; +uniform float brightness; + +float gain(float x, float k) { + float a = 0.5 * pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k); + return (x < 0.5) ? a : 1.0 - a; +} + +void main() { + vec4 pixColor = texture2D(tex, v_texcoord); + + // contrast + if (contrast != 1.0) { + pixColor.r = gain(pixColor.r, contrast); + pixColor.g = gain(pixColor.g, contrast); + pixColor.b = gain(pixColor.b, contrast); + } + + // brightness + if (brightness > 1.0) { + pixColor.rgb *= brightness; + } + + gl_FragColor = pixColor; +} +)#"; + +inline const std::string FRAGBLURFINISH = R"#( +precision highp float; +varying vec2 v_texcoord; // is in 0-1 +uniform sampler2D tex; + +uniform float noise; +uniform float brightness; + +float hash(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * 1689.1984); + p3 += dot(p3, p3.yzx + 33.33); + return fract((p3.x + p3.y) * p3.z); +} + +void main() { + vec4 pixColor = texture2D(tex, v_texcoord); + + // noise + float noiseHash = hash(v_texcoord); + float noiseAmount = (mod(noiseHash, 1.0) - 0.5); + pixColor.rgb += noiseAmount * noise; + + // brightness + if (brightness < 1.0) { + pixColor.rgb *= brightness; + } + + gl_FragColor = pixColor; +} +)#"; + +inline const std::string TEXFRAGSRCEXT = R"#( +#extension GL_OES_EGL_image_external : require + +precision highp float; +varying vec2 v_texcoord; +uniform samplerExternalOES texture0; +uniform float alpha; + +uniform vec2 topLeft; +uniform vec2 fullSize; +uniform float radius; +uniform float roundingPower; + +uniform int discardOpaque; +uniform int discardAlpha; +uniform int discardAlphaValue; + +uniform int applyTint; +uniform vec3 tint; + +void main() { + + vec4 pixColor = texture2D(texture0, v_texcoord); + + if (discardOpaque == 1 && pixColor[3] * alpha == 1.0) + discard; + + if (applyTint == 1) { + pixColor[0] = pixColor[0] * tint[0]; + pixColor[1] = pixColor[1] * tint[1]; + pixColor[2] = pixColor[2] * tint[2]; + } + + if (radius > 0.0) { + )#" + + ROUNDED_SHADER_FUNC("pixColor") + R"#( + } + + gl_FragColor = pixColor * alpha; +} +)#"; + +static const std::string FRAGGLITCH = R"#( +precision highp float; +varying vec2 v_texcoord; +uniform sampler2D tex; +uniform float time; // quirk: time is set to 0 at the beginning, should be around 10 when crash. +uniform float distort; +uniform vec2 screenSize; + +float rand(float co) { + return fract(sin(dot(vec2(co, co), vec2(12.9898, 78.233))) * 43758.5453); +} + +float rand(vec2 co) { + return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); +} + +float noise(vec2 point) { + vec2 floored = floor(point); + vec2 fractal = fract(point); + fractal = fractal * fractal * (3.0 - 2.0 * fractal); + + float mixed = mix( + mix(rand(floored), rand(floored + vec2(1.0, 0.0)), fractal.x), + mix(rand(floored + vec2(0.0,1.0)), rand(floored + vec2(1.0,1.0)), fractal.x), fractal.y); + return mixed * mixed; +} + +void main() { + float ABERR_OFFSET = 4.0 * (distort / 5.5) * time; + float TEAR_AMOUNT = 9000.0 * (1.0 - (distort / 5.5)); + float TEAR_BANDS = 108.0 / 2.0 * (distort / 5.5) * 2.0; + float MELT_AMOUNT = (distort * 8.0) / screenSize.y; + + float NOISE = abs(mod(noise(v_texcoord) * distort * time * 2.771, 1.0)) * time / 10.0; + if (time < 2.0) + NOISE = 0.0; + + float offset = (mod(rand(floor(v_texcoord.y * TEAR_BANDS)) * 318.772 * time, 20.0) - 10.0) / TEAR_AMOUNT; + + vec2 blockOffset = vec2(((abs(mod(rand(floor(v_texcoord.x * 37.162)) * 721.43, 100.0))) - 50.0) / 200000.0 * pow(time, 3.0), + ((abs(mod(rand(floor(v_texcoord.y * 45.882)) * 733.923, 100.0))) - 50.0) / 200000.0 * pow(time, 3.0)); + if (time < 3.0) + blockOffset = vec2(0,0); + + float meltSeed = abs(mod(rand(floor(v_texcoord.x * screenSize.x * 17.719)) * 281.882, 1.0)); + if (meltSeed < 0.8) { + meltSeed = 0.0; + } else { + meltSeed *= 25.0 * NOISE; + } + float meltAmount = MELT_AMOUNT * meltSeed; + + vec2 pixCoord = vec2(v_texcoord.x + offset + NOISE * 3.0 / screenSize.x + blockOffset.x, v_texcoord.y - meltAmount + 0.02 * NOISE / screenSize.x + NOISE * 3.0 / screenSize.y + blockOffset.y); + + vec4 pixColor = texture2D(tex, pixCoord); + vec4 pixColorLeft = texture2D(tex, pixCoord + vec2(ABERR_OFFSET / screenSize.x, 0)); + vec4 pixColorRight = texture2D(tex, pixCoord + vec2(-ABERR_OFFSET / screenSize.x, 0)); + + pixColor[0] = pixColorLeft[0]; + pixColor[2] = pixColorRight[2]; + + pixColor[0] += distort / 90.0; + + gl_FragColor = pixColor; +} +)#"; diff --git a/src/render/shaders/glsl/CM.glsl b/src/render/shaders/glsl/CM.glsl deleted file mode 100644 index 323a3008..00000000 --- a/src/render/shaders/glsl/CM.glsl +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#include "cm_helpers.glsl" - -uniform vec2 srcTFRange; -uniform vec2 dstTFRange; - -uniform float srcRefLuminance; -uniform mat3 convertMatrix; - -#if USE_ICC -uniform highp sampler3D iccLut3D; -uniform float iccLutSize; -#endif - -#if USE_SDR_MOD -uniform float sdrSaturation; -uniform float sdrBrightnessMultiplier; -#endif - -#if USE_TONEMAP -uniform float maxLuminance; -uniform float dstMaxLuminance; -uniform float dstRefLuminance; -#endif diff --git a/src/render/shaders/glsl/blur1.frag b/src/render/shaders/glsl/blur1.frag deleted file mode 100644 index 044df3cc..00000000 --- a/src/render/shaders/glsl/blur1.frag +++ /dev/null @@ -1,21 +0,0 @@ -#version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable - -precision highp float; -uniform sampler2D tex; - -uniform float radius; -uniform vec2 halfpixel; -uniform int passes; -uniform float vibrancy; -uniform float vibrancy_darkness; - -in vec2 v_texcoord; -layout(location = 0) out vec4 fragColor; - -#include "blur1.glsl" - -void main() { - fragColor = blur1(v_texcoord, tex, radius, halfpixel, passes, vibrancy, vibrancy_darkness); -} diff --git a/src/render/shaders/glsl/blur1.glsl b/src/render/shaders/glsl/blur1.glsl deleted file mode 100644 index 36e7d660..00000000 --- a/src/render/shaders/glsl/blur1.glsl +++ /dev/null @@ -1,130 +0,0 @@ -// see http://alienryderflex.com/hsp.html -const float Pr = 0.299; -const float Pg = 0.587; -const float Pb = 0.114; - -// Y is "v" ( brightness ). X is "s" ( saturation ) -// see https://www.desmos.com/3d/a88652b9a4 -// Determines if high brightness or high saturation is more important -const float a = 0.93; -const float b = 0.11; -const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors -// - -// http://www.flong.com/archive/texts/code/shapers_circ/ -float doubleCircleSigmoid(float x, float a) { - a = clamp(a, 0.0, 1.0); - - float y = .0; - if (x <= a) { - y = a - sqrt(a * a - x * x); - } else { - y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.)); - } - return y; -} - -vec3 rgb2hsl(vec3 col) { - float red = col.r; - float green = col.g; - float blue = col.b; - - float minc = min(col.r, min(col.g, col.b)); - float maxc = max(col.r, max(col.g, col.b)); - float delta = maxc - minc; - - float lum = (minc + maxc) * 0.5; - float sat = 0.0; - float hue = 0.0; - - if (lum > 0.0 && lum < 1.0) { - float mul = (lum < 0.5) ? (lum) : (1.0 - lum); - sat = delta / (mul * 2.0); - } - - if (delta > 0.0) { - vec3 maxcVec = vec3(maxc); - vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red))); - vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta; - - hue += dot(adds, masks); - hue /= 6.0; - - if (hue < 0.0) - hue += 1.0; - } - - return vec3(hue, sat, lum); -} - -vec3 hsl2rgb(vec3 col) { - const float onethird = 1.0 / 3.0; - const float twothird = 2.0 / 3.0; - const float rcpsixth = 6.0; - - float hue = col.x; - float sat = col.y; - float lum = col.z; - - vec3 xt = vec3(0.0); - - if (hue < onethird) { - xt.r = rcpsixth * (onethird - hue); - xt.g = rcpsixth * hue; - xt.b = 0.0; - } else if (hue < twothird) { - xt.r = 0.0; - xt.g = rcpsixth * (twothird - hue); - xt.b = rcpsixth * (hue - onethird); - } else - xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue)); - - xt = min(xt, 1.0); - - float sat2 = 2.0 * sat; - float satinv = 1.0 - sat; - float luminv = 1.0 - lum; - float lum2m1 = (2.0 * lum) - 1.0; - vec3 ct = (sat2 * xt) + satinv; - - vec3 rgb; - if (lum >= 0.5) - rgb = (luminv * ct) + lum2m1; - else - rgb = lum * ct; - - return rgb; -} - -vec4 blur1(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel, int passes, float vibrancy, float vibrancy_darkness) { - vec2 uv = v_texcoord * 2.0; - - vec4 sum = texture(tex, uv) * 4.0; - sum += texture(tex, uv - halfpixel.xy * radius); - sum += texture(tex, uv + halfpixel.xy * radius); - sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); - sum += texture(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); - - vec4 color = sum / 8.0; - - if (vibrancy == 0.0) { - return color; - } else { - // Invert it so that it correctly maps to the config setting - float vibrancy_darkness1 = 1.0 - vibrancy_darkness; - - // Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest. - vec3 hsl = rgb2hsl(color.rgb); - // Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow - float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1); - - float b1 = b * vibrancy_darkness1; - float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0; - - float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0); - - vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2])); - - return vec4(newColor, color[3]); - } -} diff --git a/src/render/shaders/glsl/blur2.frag b/src/render/shaders/glsl/blur2.frag deleted file mode 100644 index 62caae56..00000000 --- a/src/render/shaders/glsl/blur2.frag +++ /dev/null @@ -1,18 +0,0 @@ -#version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable - -precision highp float; - -uniform sampler2D tex; -uniform float radius; -uniform vec2 halfpixel; - -in vec2 v_texcoord; -layout(location = 0) out vec4 fragColor; - -#include "blur2.glsl" - -void main() { - fragColor = blur2(v_texcoord, tex, radius, halfpixel); -} diff --git a/src/render/shaders/glsl/blur2.glsl b/src/render/shaders/glsl/blur2.glsl deleted file mode 100644 index e73e90e3..00000000 --- a/src/render/shaders/glsl/blur2.glsl +++ /dev/null @@ -1,15 +0,0 @@ -vec4 blur2(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel) { - vec2 uv = v_texcoord / 2.0; - - vec4 sum = texture(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); - - sum += texture(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); - sum += texture(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); - sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); - sum += texture(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; - - return sum / 12.0; -} diff --git a/src/render/shaders/glsl/blurFinish.glsl b/src/render/shaders/glsl/blurFinish.glsl deleted file mode 100644 index f3d225c3..00000000 --- a/src/render/shaders/glsl/blurFinish.glsl +++ /dev/null @@ -1,17 +0,0 @@ -float hash(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * 1689.1984); - p3 += dot(p3, p3.yzx + 33.33); - return fract((p3.x + p3.y) * p3.z); -} - -vec4 blurFinish(vec4 pixColor, vec2 v_texcoord, float noise, float brightness) { - // noise - float noiseHash = hash(v_texcoord); - float noiseAmount = noiseHash - 0.5; - pixColor.rgb += noiseAmount * noise; - - // brightness - pixColor.rgb *= min(1.0, brightness); - - return pixColor; -} diff --git a/src/render/shaders/glsl/blurfinish.frag b/src/render/shaders/glsl/blurfinish.frag deleted file mode 100644 index 0342646b..00000000 --- a/src/render/shaders/glsl/blurfinish.frag +++ /dev/null @@ -1,19 +0,0 @@ -#version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable - -precision highp float; -in vec2 v_texcoord; // is in 0-1 -uniform sampler2D tex; - -uniform float noise; -uniform float brightness; - -#include "blurFinish.glsl" - -layout(location = 0) out vec4 fragColor; -void main() { - vec4 pixColor = texture(tex, v_texcoord); - - fragColor = blurFinish(pixColor, v_texcoord, noise, brightness); -} diff --git a/src/render/shaders/glsl/blurprepare.frag b/src/render/shaders/glsl/blurprepare.frag deleted file mode 100644 index e96c54bb..00000000 --- a/src/render/shaders/glsl/blurprepare.frag +++ /dev/null @@ -1,38 +0,0 @@ -#version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable - -#include "defines.h" - -precision highp float; -in vec2 v_texcoord; // is in 0-1 -uniform sampler2D tex; - -uniform float contrast; -uniform float brightness; - -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction - -#if USE_CM -uniform vec2 srcTFRange; -uniform vec2 dstTFRange; - -uniform float srcRefLuminance; -uniform mat3 convertMatrix; - -uniform float sdrBrightnessMultiplier; -#include "cm_helpers.glsl" -#endif - -#include "blurprepare.glsl" - -layout(location = 0) out vec4 fragColor; -void main() { - fragColor = fragColor = blurPrepare(texture(tex, v_texcoord), contrast, brightness -#if USE_CM - , - sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange, srcRefLuminance, sdrBrightnessMultiplier -#endif - ); -} diff --git a/src/render/shaders/glsl/blurprepare.glsl b/src/render/shaders/glsl/blurprepare.glsl deleted file mode 100644 index e4a0daad..00000000 --- a/src/render/shaders/glsl/blurprepare.glsl +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif - -#include "defines.h" - -#if USE_CM -#include "cm_helpers.glsl" -#endif - -#include "gain.glsl" - -vec4 blurPrepare(vec4 pixColor, float contrast, float brightness -#if USE_CM - , - int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange, float srcRefLuminance, float sdrBrightnessMultiplier -#endif -) { -#if USE_CM - if (sourceTF == CM_TRANSFER_FUNCTION_ST2084_PQ) { - pixColor.rgb /= sdrBrightnessMultiplier; - } - pixColor.rgb = convertMatrix * toLinearRGB(pixColor.rgb, sourceTF); - pixColor = toNit(pixColor, vec2(srcTFRange[0], srcRefLuminance)); - pixColor = fromLinearNit(pixColor, targetTF, dstTFRange); -#endif - - // contrast - if (contrast != 1.0) - pixColor.rgb = gain(pixColor.rgb, contrast); - - // brightness - pixColor.rgb *= max(1.0, brightness); - - return pixColor; -} diff --git a/src/render/shaders/glsl/border.frag b/src/render/shaders/glsl/border.frag deleted file mode 100644 index 151593c1..00000000 --- a/src/render/shaders/glsl/border.frag +++ /dev/null @@ -1,60 +0,0 @@ -#version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable - -precision highp float; -in vec2 v_texcoord; - -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction -uniform mat3 targetPrimariesXYZ; - -uniform vec2 fullSizeUntransformed; -uniform float radiusOuter; -uniform float thick; - -// Gradients are in OkLabA!!!! {l, a, b, alpha} -uniform vec4 gradient[10]; -uniform vec4 gradient2[10]; -uniform int gradientLength; -uniform int gradient2Length; -uniform float angle; -uniform float angle2; -uniform float gradientLerp; -uniform float alpha; - -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; -#include "rounding.glsl" -#include "CM.glsl" -#include "border.glsl" - -layout(location = 0) out vec4 fragColor; -void main() { - fragColor = getBorder(v_texcoord, alpha, fullSizeUntransformed, radiusOuter, thick, radius, roundingPower, topLeft, fullSize, gradientLength, gradient, angle, gradient2Length, - gradient2, angle2, gradientLerp -#if USE_CM - , - sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif -#endif - ); -} diff --git a/src/render/shaders/glsl/border.glsl b/src/render/shaders/glsl/border.glsl deleted file mode 100644 index fa2a6980..00000000 --- a/src/render/shaders/glsl/border.glsl +++ /dev/null @@ -1,203 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#include "cm_helpers.glsl" -#if USE_ROUNDING -#include "rounding.glsl" -#endif - -vec4 okLabAToSrgb(vec4 lab) { - float l = pow(lab[0] + lab[1] * 0.3963377774 + lab[2] * 0.2158037573, 3.0); - float m = pow(lab[0] + lab[1] * (-0.1055613458) + lab[2] * (-0.0638541728), 3.0); - float s = pow(lab[0] + lab[1] * (-0.0894841775) + lab[2] * (-1.2914855480), 3.0); - - return vec4(fromLinearRGB(vec3(l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292, l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965), - l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010), - CM_TRANSFER_FUNCTION_GAMMA22), - lab[3]); -} - -vec4 getOkColorForCoordArray1(vec2 normalizedCoord, int gradientLength, vec4 gradient[10], float angle) { - if (gradientLength < 2) - return gradient[0]; - - float finalAng = 0.0; - - if (angle > 4.71 /* 270 deg */) { - normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = 6.28 - angle; - } else if (angle > 3.14 /* 180 deg */) { - normalizedCoord[0] = 1.0 - normalizedCoord[0]; - normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = angle - 3.14; - } else if (angle > 1.57 /* 90 deg */) { - normalizedCoord[0] = 1.0 - normalizedCoord[0]; - finalAng = 3.14 - angle; - } else { - finalAng = angle; - } - - float sine = sin(finalAng); - - float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradientLength - 1); - int bottom = int(floor(progress)); - int top = bottom + 1; - - return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress); -} - -vec4 getOkColorForCoordArray2(vec2 normalizedCoord, float angle, int gradient2Length, vec4 gradient2[10], float angle2) { - if (gradient2Length < 2) - return gradient2[0]; - - float finalAng = 0.0; - - if (angle2 > 4.71 /* 270 deg */) { - normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = 6.28 - angle; - } else if (angle2 > 3.14 /* 180 deg */) { - normalizedCoord[0] = 1.0 - normalizedCoord[0]; - normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = angle - 3.14; - } else if (angle2 > 1.57 /* 90 deg */) { - normalizedCoord[0] = 1.0 - normalizedCoord[0]; - finalAng = 3.14 - angle2; - } else { - finalAng = angle2; - } - - float sine = sin(finalAng); - - float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradient2Length - 1); - int bottom = int(floor(progress)); - int top = bottom + 1; - - return gradient2[top] * (progress - float(bottom)) + gradient2[bottom] * (float(top) - progress); -} - -vec4 getColorForCoord(vec2 normalizedCoord, int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp) { - vec4 result1 = getOkColorForCoordArray1(normalizedCoord, gradientLength, gradient, angle); - - if (gradient2Length <= 0) - return okLabAToSrgb(result1); - - vec4 result2 = getOkColorForCoordArray2(normalizedCoord, angle, gradient2Length, gradient2, angle2); - - return okLabAToSrgb(mix(result1, result2, gradientLerp)); -} - -vec4 getBorder(vec2 v_texcoord, float alpha, vec2 fullSizeUntransformed, float radiusOuter, float thick, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, - int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp -#if USE_CM - , - int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange -#if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - mat3 targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance -#endif -#if USE_SDR_MOD - , - float sdrSaturation, float sdrBrightnessMultiplier -#endif -#endif -#endif -) { - vec2 pixCoord = vec2(gl_FragCoord); - vec2 pixCoordOuter = pixCoord; - vec2 originalPixCoord = v_texcoord; - originalPixCoord *= fullSizeUntransformed; - float additionalAlpha = 1.0; - - vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); - - bool done = false; - - pixCoord -= topLeft + fullSize * 0.5; - pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; - pixCoordOuter = pixCoord; - pixCoord -= fullSize * 0.5 - radius; - pixCoordOuter -= fullSize * 0.5 - radiusOuter; - - // center the pixes don't make it top-left - pixCoord += vec2(1.0, 1.0) / fullSize; - pixCoordOuter += vec2(1.0, 1.0) / fullSize; - -#if USE_ROUNDING - if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { - float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0 / roundingPower); - float distOuter = pow(pow(pixCoordOuter.x, roundingPower) + pow(pixCoordOuter.y, roundingPower), 1.0 / roundingPower); - float h = (thick / 2.0); - - if (dist < radius - h) { - // lower - float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - additionalAlpha *= normalized; - done = true; - } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { - // higher - float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - additionalAlpha *= normalized; - done = true; - } else if (distOuter < radiusOuter - h) { - additionalAlpha = 1.0; - done = true; - } - } -#endif - - // now check for other shit - if (!done) { - // distance to all straight bb borders - float distanceT = originalPixCoord[1]; - float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; - float distanceL = originalPixCoord[0]; - float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; - - // get the smallest - float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); - - if (smallest > thick) - discard; - } - - if (additionalAlpha == 0.0) - discard; - - pixColor = getColorForCoord(v_texcoord, gradientLength, gradient, angle, gradient2Length, gradient2, angle2, gradientLerp); - pixColor.rgb *= pixColor[3]; - -#if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif - ); -#endif - - pixColor *= alpha * additionalAlpha; - - return pixColor; -} diff --git a/src/render/shaders/glsl/cm_helpers.glsl b/src/render/shaders/glsl/cm_helpers.glsl deleted file mode 100644 index 5e0d14f6..00000000 --- a/src/render/shaders/glsl/cm_helpers.glsl +++ /dev/null @@ -1,248 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#ifndef CM_HELPERS_GLSL -#define CM_HELPERS_GLSL - -#include "defines.h" -#include "constants.h" - -#if USE_SDR_MOD -vec4 saturate(vec4 color, mat3 primaries, float saturation) { - if (saturation == 1.0) - return color; - vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]); - float Y = dot(color.rgb, brightness); - return vec4(mix(vec3(Y), color.rgb, saturation), color[3]); -} -#endif - -vec3 applyIcc3DLut(vec3 linearRgb01, highp sampler3D iccLut3D, float iccLutSize) { - vec3 x = clamp(linearRgb01, 0.0, 1.0); - - // Map [0..1] to texel centers to avoid edge issues - float N = iccLutSize; - vec3 coord = (x * (N - 1.0) + 0.5) / N; - - return texture(iccLut3D, coord).rgb; -} - -vec3 xy2xyz(vec2 xy) { - if (xy.y == 0.0) - return vec3(0.0, 0.0, 0.0); - - return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y); -} - -// The primary source for these transfer functions is https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.1361-0-199802-W!!PDF-E.pdf -vec3 tfInvPQ(vec3 color) { - vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2)); - return pow((max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E), vec3(PQ_INV_M1)); -} - -vec3 tfInvHLG(vec3 color) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT)); - vec3 lo = color.rgb * color.rgb / 3.0; - vec3 hi = (exp((color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0; - return mix(hi, lo, isLow); -} - -// Many transfer functions (including sRGB) follow the same pattern: a linear -// segment for small values and a power function for larger values. The -// following function implements this pattern from which sRGB, BT.1886, and -// others can be derived by plugging in the right constants. -vec3 tfInvLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(thres * scale)); - vec3 lo = color.rgb / scale; - vec3 hi = pow((color.rgb + alpha - 1.0) / alpha, vec3(gamma)); - return mix(hi, lo, isLow); -} - -vec3 tfInvSRGB(vec3 color) { - return tfInvLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); -} - -vec3 tfInvExtSRGB(vec3 color) { - // EXT sRGB is the sRGB transfer function mirrored around 0. - return sign(color) * tfInvSRGB(abs(color)); -} - -vec3 tfInvBT1886(vec3 color) { - return tfInvLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); -} - -vec3 tfInvXVYCC(vec3 color) { - // The inverse transfer function for XVYCC is the BT1886 transfer function mirrored around 0, - // same as what EXT sRGB is to sRGB. - return sign(color) * tfInvBT1886(abs(color)); -} - -vec3 tfInvST240(vec3 color) { - return tfInvLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); -} - -// Forward transfer functions corresponding to the inverse functions above. -vec3 tfPQ(vec3 color) { - vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1)); - return pow((vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E), vec3(PQ_M2)); -} - -vec3 tfHLG(vec3 color) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT)); - vec3 lo = sqrt(max(color.rgb, vec3(0.0)) * 3.0); - vec3 hi = HLG_A * log(max(12.0 * color.rgb - HLG_B, vec3(0.0001))) + HLG_C; - return mix(hi, lo, isLow); -} - -vec3 tfLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(thres)); - vec3 lo = color.rgb * scale; - vec3 hi = pow(color.rgb, vec3(1.0 / gamma)) * alpha - (alpha - 1.0); - return mix(hi, lo, isLow); -} - -vec3 tfSRGB(vec3 color) { - return tfLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); -} - -vec3 tfExtSRGB(vec3 color) { - // EXT sRGB is the sRGB transfer function mirrored around 0. - return sign(color) * tfSRGB(abs(color)); -} - -vec3 tfBT1886(vec3 color) { - return tfLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); -} - -vec3 tfXVYCC(vec3 color) { - // The transfer function for XVYCC is the BT1886 transfer function mirrored around 0, - // same as what EXT sRGB is to sRGB. - return sign(color) * tfBT1886(abs(color)); -} - -vec3 tfST240(vec3 color) { - return tfLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); -} - -vec3 toLinearRGB(vec3 color, int tf) { - switch (tf) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color; - case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfInvPQ(color); - case CM_TRANSFER_FUNCTION_GAMMA22: return pow(max(color, vec3(0.0)), vec3(2.2)); - case CM_TRANSFER_FUNCTION_GAMMA28: return pow(max(color, vec3(0.0)), vec3(2.8)); - case CM_TRANSFER_FUNCTION_HLG: return tfInvHLG(color); - case CM_TRANSFER_FUNCTION_EXT_SRGB: return tfInvExtSRGB(color); - case CM_TRANSFER_FUNCTION_BT1886: return tfInvBT1886(color); - case CM_TRANSFER_FUNCTION_ST240: return tfInvST240(color); - case CM_TRANSFER_FUNCTION_LOG_100: return mix(exp((color - 1.0) * 2.0 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); - case CM_TRANSFER_FUNCTION_LOG_316: return mix(exp((color - 1.0) * 2.5 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); - case CM_TRANSFER_FUNCTION_XVYCC: return tfInvXVYCC(color); - case CM_TRANSFER_FUNCTION_ST428: return pow(max(color, vec3(0.0)), vec3(ST428_POW)) * ST428_SCALE; - case CM_TRANSFER_FUNCTION_SRGB: - default: return tfInvSRGB(color); - } -} - -vec4 toLinear(vec4 color, int tf) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - return color; - - color.rgb /= max(color.a, 0.001); - color.rgb = toLinearRGB(color.rgb, tf); - color.rgb *= color.a; - return color; -} - -vec4 toNit(vec4 color, vec2 range) { - color.rgb = color.rgb * (range[1] - range[0]) + range[0]; - return color; -} - -vec3 fromLinearRGB(vec3 color, int tf) { - switch (tf) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color; - case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfPQ(color); - case CM_TRANSFER_FUNCTION_GAMMA22: return pow(max(color, vec3(0.0)), vec3(1.0 / 2.2)); - case CM_TRANSFER_FUNCTION_GAMMA28: return pow(max(color, vec3(0.0)), vec3(1.0 / 2.8)); - case CM_TRANSFER_FUNCTION_HLG: return tfHLG(color); - case CM_TRANSFER_FUNCTION_EXT_SRGB: return tfExtSRGB(color); - case CM_TRANSFER_FUNCTION_BT1886: return tfBT1886(color); - case CM_TRANSFER_FUNCTION_ST240: return tfST240(color); - case CM_TRANSFER_FUNCTION_LOG_100: return mix(1.0 + log(color) / log(10.0) / 2.0, vec3(0.0), lessThanEqual(color, vec3(0.01))); - case CM_TRANSFER_FUNCTION_LOG_316: return mix(1.0 + log(color) / log(10.0) / 2.5, vec3(0.0), lessThanEqual(color, vec3(sqrt(10.0) / 1000.0))); - case CM_TRANSFER_FUNCTION_XVYCC: return tfXVYCC(color); - case CM_TRANSFER_FUNCTION_ST428: return pow(max(color, vec3(0.0)) / ST428_SCALE, vec3(1.0 / ST428_POW)); - case CM_TRANSFER_FUNCTION_SRGB: - default: return tfSRGB(color); - } -} - -vec4 fromLinear(vec4 color, int tf) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - return color; - - color.rgb /= max(color.a, 0.001); - color.rgb = fromLinearRGB(color.rgb, tf); - color.rgb *= color.a; - return color; -} - -vec4 fromLinearNit(vec4 color, int tf, vec2 range) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - color.rgb = color.rgb / SDR_MAX_LUMINANCE; - else { - color.rgb /= max(color.a, 0.001); - color.rgb = (color.rgb - range[0]) / (range[1] - range[0]); - color.rgb = fromLinearRGB(color.rgb, tf); - color.rgb *= color.a; - } - return color; -} - -#if USE_TONEMAP -#include "tonemap.glsl" -#endif - -vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange -#if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - mat3 dstxyz -#endif -#if USE_TONEMAP - , - float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance -#endif -#if USE_SDR_MOD - , - float sdrSaturation, float sdrBrightnessMultiplier -#endif -#endif -) { - pixColor.rgb /= max(pixColor.a, 0.001); - pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF); -#if USE_ICC - pixColor.rgb = applyIcc3DLut(pixColor.rgb, iccLut3D, iccLutSize); - pixColor.rgb *= pixColor.a; -#else - pixColor.rgb = convertMatrix * pixColor.rgb; - pixColor = toNit(pixColor, srcTFRange); - pixColor.rgb *= pixColor.a; -#if USE_TONEMAP - pixColor = tonemap(pixColor, dstxyz, maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance); -#endif - pixColor = fromLinearNit(pixColor, dstTF, dstTFRange); -#if USE_SDR_MOD - pixColor = saturate(pixColor, dstxyz, sdrSaturation); - pixColor.rgb *= sdrBrightnessMultiplier; -#endif -#endif - - return pixColor; -} - -#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/constants.h b/src/render/shaders/glsl/constants.h deleted file mode 100644 index bbab5284..00000000 --- a/src/render/shaders/glsl/constants.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef CONSTANTS_H -#define CONSTANTS_H -//enum eTransferFunction -#define CM_TRANSFER_FUNCTION_BT1886 1 -#define CM_TRANSFER_FUNCTION_GAMMA22 2 -#define CM_TRANSFER_FUNCTION_GAMMA28 3 -#define CM_TRANSFER_FUNCTION_ST240 4 -#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5 -#define CM_TRANSFER_FUNCTION_LOG_100 6 -#define CM_TRANSFER_FUNCTION_LOG_316 7 -#define CM_TRANSFER_FUNCTION_XVYCC 8 -#define CM_TRANSFER_FUNCTION_SRGB 9 -#define CM_TRANSFER_FUNCTION_EXT_SRGB 10 -#define CM_TRANSFER_FUNCTION_ST2084_PQ 11 -#define CM_TRANSFER_FUNCTION_ST428 12 -#define CM_TRANSFER_FUNCTION_HLG 13 - -// sRGB constants -#define SRGB_POW 2.4 -#define SRGB_CUT 0.0031308 -#define SRGB_SCALE 12.92 -#define SRGB_ALPHA 1.055 - -#define BT1886_POW (1.0 / 0.45) -#define BT1886_CUT 0.018053968510807 -#define BT1886_SCALE 4.5 -#define BT1886_ALPHA (1.0 + 5.5 * BT1886_CUT) - -// See http://car.france3.mars.free.fr/HD/INA-%2026%20jan%2006/SMPTE%20normes%20et%20confs/s240m.pdf -#define ST240_POW (1.0 / 0.45) -#define ST240_CUT 0.0228 -#define ST240_SCALE 4.0 -#define ST240_ALPHA 1.1115 - -#define ST428_POW 2.6 -#define ST428_SCALE (52.37 / 48.0) - -// PQ constants -#define PQ_M1 0.1593017578125 -#define PQ_M2 78.84375 -#define PQ_INV_M1 (1.0 / PQ_M1) -#define PQ_INV_M2 (1.0 / PQ_M2) -#define PQ_C1 0.8359375 -#define PQ_C2 18.8515625 -#define PQ_C3 18.6875 - -// HLG constants -#define HLG_D_CUT (1.0 / 12.0) -#define HLG_E_CUT 0.5 -#define HLG_A 0.17883277 -#define HLG_B 0.28466892 -#define HLG_C 0.55991073 - -#define SDR_MIN_LUMINANCE 0.2 -#define SDR_MAX_LUMINANCE 80.0 -#define HDR_MIN_LUMINANCE 0.005 -#define HDR_MAX_LUMINANCE 10000.0 -#define HLG_MAX_LUMINANCE 1000.0 - -#define M_E 2.718281828459045 - -#endif diff --git a/src/render/shaders/glsl/defines.h b/src/render/shaders/glsl/defines.h deleted file mode 100644 index 31b120a4..00000000 --- a/src/render/shaders/glsl/defines.h +++ /dev/null @@ -1,10 +0,0 @@ -// DO NOT EDIT. Will be overwritten in runtime -#define USE_RGBA 1 -#define USE_DISCARD 1 -#define USE_TINT 1 -#define USE_ROUNDING 1 -#define USE_CM 1 -#define USE_TONEMAP 1 -#define USE_SDR_MOD 1 -#define USE_BLUR 1 -#define USE_ICC 1 diff --git a/src/render/shaders/glsl/ext.frag b/src/render/shaders/glsl/ext.frag deleted file mode 100644 index 1c614bd3..00000000 --- a/src/render/shaders/glsl/ext.frag +++ /dev/null @@ -1,43 +0,0 @@ -#version 300 es - -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#extension GL_OES_EGL_image_external_essl3 : require - -precision highp float; -in vec2 v_texcoord; -uniform samplerExternalOES tex; -uniform float alpha; - -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; -#include "rounding.glsl" - -uniform int discardOpaque; -uniform int discardAlpha; -uniform int discardAlphaValue; - -uniform int applyTint; -uniform vec3 tint; - -layout(location = 0) out vec4 fragColor; -void main() { - - vec4 pixColor = texture(tex, v_texcoord); - - if (discardOpaque == 1 && pixColor[3] * alpha == 1.0) - discard; - - if (applyTint == 1) { - pixColor[0] = pixColor[0] * tint[0]; - pixColor[1] = pixColor[1] * tint[1]; - pixColor[2] = pixColor[2] * tint[2]; - } - - if (radius > 0.0) - pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); - - fragColor = pixColor * alpha; -} diff --git a/src/render/shaders/glsl/gain.glsl b/src/render/shaders/glsl/gain.glsl deleted file mode 100644 index 2bdc0002..00000000 --- a/src/render/shaders/glsl/gain.glsl +++ /dev/null @@ -1,6 +0,0 @@ -vec3 gain(vec3 x, float k) { - vec3 t = step(0.5, x); - vec3 y = mix(x, 1.0 - x, t); - vec3 a = 0.5 * pow(2.0 * y, vec3(k)); - return mix(a, 1.0 - a, t); -} diff --git a/src/render/shaders/glsl/glitch.frag b/src/render/shaders/glsl/glitch.frag deleted file mode 100644 index d7259cc4..00000000 --- a/src/render/shaders/glsl/glitch.frag +++ /dev/null @@ -1,67 +0,0 @@ -#version 300 es - -precision highp float; -in vec2 v_texcoord; -uniform sampler2D tex; -uniform float time; // quirk: time is set to 0 at the beginning, should be around 10 when crash. -uniform float distort; -uniform vec2 fullSize; - -float rand(float co) { - return fract(sin(dot(vec2(co, co), vec2(12.9898, 78.233))) * 43758.5453); -} - -float rand(vec2 co) { - return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); -} - -float noise(vec2 point) { - vec2 floored = floor(point); - vec2 fractal = fract(point); - fractal = fractal * fractal * (3.0 - 2.0 * fractal); - - float mixed = mix( - mix(rand(floored), rand(floored + vec2(1.0, 0.0)), fractal.x), - mix(rand(floored + vec2(0.0,1.0)), rand(floored + vec2(1.0,1.0)), fractal.x), fractal.y); - return mixed * mixed; -} - -layout(location = 0) out vec4 fragColor; -void main() { - float ABERR_OFFSET = 4.0 * (distort / 5.5) * time; - float TEAR_AMOUNT = 9000.0 * (1.0 - (distort / 5.5)); - float TEAR_BANDS = 108.0 / 2.0 * (distort / 5.5) * 2.0; - float MELT_AMOUNT = (distort * 8.0) / fullSize.y; - - float NOISE = abs(mod(noise(v_texcoord) * distort * time * 2.771, 1.0)) * time / 10.0; - if (time < 2.0) - NOISE = 0.0; - - float offset = (mod(rand(floor(v_texcoord.y * TEAR_BANDS)) * 318.772 * time, 20.0) - 10.0) / TEAR_AMOUNT; - - vec2 blockOffset = vec2(((abs(mod(rand(floor(v_texcoord.x * 37.162)) * 721.43, 100.0))) - 50.0) / 200000.0 * pow(time, 3.0), - ((abs(mod(rand(floor(v_texcoord.y * 45.882)) * 733.923, 100.0))) - 50.0) / 200000.0 * pow(time, 3.0)); - if (time < 3.0) - blockOffset = vec2(0,0); - - float meltSeed = abs(mod(rand(floor(v_texcoord.x * fullSize.x * 17.719)) * 281.882, 1.0)); - if (meltSeed < 0.8) { - meltSeed = 0.0; - } else { - meltSeed *= 25.0 * NOISE; - } - float meltAmount = MELT_AMOUNT * meltSeed; - - vec2 pixCoord = vec2(v_texcoord.x + offset + NOISE * 3.0 / fullSize.x + blockOffset.x, v_texcoord.y - meltAmount + 0.02 * NOISE / fullSize.x + NOISE * 3.0 / fullSize.y + blockOffset.y); - - vec4 pixColor = texture(tex, pixCoord); - vec4 pixColorLeft = texture(tex, pixCoord + vec2(ABERR_OFFSET / fullSize.x, 0)); - vec4 pixColorRight = texture(tex, pixCoord + vec2(-ABERR_OFFSET / fullSize.x, 0)); - - pixColor[0] = pixColorLeft[0]; - pixColor[2] = pixColorRight[2]; - - pixColor[0] += distort / 90.0; - - fragColor = pixColor; -} diff --git a/src/render/shaders/glsl/passthru.frag b/src/render/shaders/glsl/passthru.frag deleted file mode 100644 index e33b7121..00000000 --- a/src/render/shaders/glsl/passthru.frag +++ /dev/null @@ -1,10 +0,0 @@ -#version 300 es - -precision highp float; -in vec2 v_texcoord; // is in 0-1 -uniform sampler2D tex; - -layout(location = 0) out vec4 fragColor; -void main() { - fragColor = texture(tex, v_texcoord); -} diff --git a/src/render/shaders/glsl/quad.frag b/src/render/shaders/glsl/quad.frag deleted file mode 100644 index 61895a60..00000000 --- a/src/render/shaders/glsl/quad.frag +++ /dev/null @@ -1,27 +0,0 @@ -#version 300 es - -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#include "defines.h" - -precision highp float; -in vec4 v_color; - -#if USE_ROUNDING -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; -#include "rounding.glsl" -#endif - -layout(location = 0) out vec4 fragColor; -void main() { - vec4 pixColor = v_color; - -#if USE_ROUNDING - pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); -#endif - - fragColor = pixColor; -} diff --git a/src/render/shaders/glsl/rgbamatte.frag b/src/render/shaders/glsl/rgbamatte.frag deleted file mode 100644 index a7213cfe..00000000 --- a/src/render/shaders/glsl/rgbamatte.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 300 es - -precision highp float; -in vec2 v_texcoord; // is in 0-1 -uniform sampler2D tex; -uniform sampler2D texMatte; - -layout(location = 0) out vec4 fragColor; -void main() { - fragColor = texture(tex, v_texcoord) * texture(texMatte, v_texcoord)[0]; // I know it only uses R, but matte should be black/white anyways. -} diff --git a/src/render/shaders/glsl/rounding.glsl b/src/render/shaders/glsl/rounding.glsl deleted file mode 100644 index 61a0bb9c..00000000 --- a/src/render/shaders/glsl/rounding.glsl +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef ROUNDING_GLSL -#define ROUNDING_GLSL -// smoothing constant for the edge: more = blurrier, but smoother -#define M_PI 3.1415926535897932384626433832795 -#define SMOOTHING_CONSTANT (M_PI / 5.34665792551) - -vec4 rounding(vec4 color, float radius, float roundingPower, vec2 topLeft, vec2 fullSize) { - vec2 pixCoord = vec2(gl_FragCoord); - pixCoord -= topLeft + fullSize * 0.5; - pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; - pixCoord -= fullSize * 0.5 - radius; - pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix don't make it top-left - - if (pixCoord.x + pixCoord.y > radius) { - float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0 / roundingPower); - - if (dist > radius + SMOOTHING_CONSTANT) - discard; - - float normalized = 1.0 - smoothstep(0.0, 1.0, (dist - radius + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - - color *= normalized; - } - - return color; -} -#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/shadow.frag b/src/render/shaders/glsl/shadow.frag deleted file mode 100644 index c23ebd5d..00000000 --- a/src/render/shaders/glsl/shadow.frag +++ /dev/null @@ -1,57 +0,0 @@ -#version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable - -#include "defines.h" - -precision highp float; -in vec4 v_color; -in vec2 v_texcoord; - -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction -uniform mat3 targetPrimariesXYZ; - -uniform vec2 topLeft; -uniform vec2 bottomRight; -uniform vec2 fullSize; -uniform float radius; -uniform float roundingPower; -uniform float range; -uniform float shadowPower; - -#if USE_CM -#include "cm_helpers.glsl" -#include "CM.glsl" -#endif - -#include "shadow.glsl" - -layout(location = 0) out vec4 fragColor; -void main() { - vec4 pixColor = v_color; - - fragColor = getShadow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight -#if USE_CM - , - sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif -#endif - ); -} \ No newline at end of file diff --git a/src/render/shaders/glsl/shadow.glsl b/src/render/shaders/glsl/shadow.glsl deleted file mode 100644 index 48cde562..00000000 --- a/src/render/shaders/glsl/shadow.glsl +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#ifndef SHADOW_GLSL -#define SHADOW_GLSL - -#include "cm_helpers.glsl" - -float pixAlphaRoundedDistance(float distanceToCorner, float radius, float range, float shadowPower) { - if (distanceToCorner > radius) { - return 0.0; - } - - if (distanceToCorner > radius - range) { - return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think? - } - - return 1.0; -} - -float modifiedLength(vec2 a, float roundingPower) { - return pow(pow(abs(a.x), roundingPower) + pow(abs(a.y), roundingPower), 1.0 / roundingPower); -} - -vec4 getShadow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float shadowPower, vec2 bottomRight -#if USE_CM - , - int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange -#if USE_ICC - , - highp sampler3D iccLut3D, float iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - mat3 targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance -#endif -#if USE_SDR_MOD - , - float sdrSaturation, float sdrBrightnessMultiplier -#endif -#endif -#endif -) { - float originalAlpha = pixColor[3]; - - bool done = false; - - vec2 pixCoord = fullSize * v_texcoord; - - // ok, now we check the distance to a border. - - if (pixCoord[0] < topLeft[0]) { - if (pixCoord[1] < topLeft[1]) { - // top left - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft, roundingPower), radius, range, shadowPower); - done = true; - } else if (pixCoord[1] > bottomRight[1]) { - // bottom left - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1]), roundingPower), radius, range, shadowPower); - done = true; - } - } else if (pixCoord[0] > bottomRight[0]) { - if (pixCoord[1] < topLeft[1]) { - // top right - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1]), roundingPower), radius, range, shadowPower); - done = true; - } else if (pixCoord[1] > bottomRight[1]) { - // bottom right - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - bottomRight, roundingPower), radius, range, shadowPower); - done = true; - } - } - - if (!done) { - // distance to all straight bb borders - float distanceT = pixCoord[1]; - float distanceB = fullSize[1] - pixCoord[1]; - float distanceL = pixCoord[0]; - float distanceR = fullSize[0] - pixCoord[0]; - - // get the smallest - float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); - - if (smallest < range) { - pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower); - } - } - - if (pixColor[3] == 0.0) { - discard; - return pixColor; - } - - // premultiply - pixColor.rgb *= pixColor[3]; - -#if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif - ); -#endif - - return pixColor; -} -#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/surface.frag b/src/render/shaders/glsl/surface.frag deleted file mode 100644 index 30023bc8..00000000 --- a/src/render/shaders/glsl/surface.frag +++ /dev/null @@ -1,104 +0,0 @@ -#version 300 es -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable - -#include "defines.h" - -precision highp float; -in vec2 v_texcoord; -uniform sampler2D tex; -#if USE_BLUR -uniform vec2 uvSize; -uniform vec2 uvOffset; -uniform sampler2D blurredBG; -#endif - -uniform float alpha; - -#if USE_DISCARD -uniform bool discardOpaque; -uniform bool discardAlpha; -uniform float discardAlphaValue; -#endif - -#if USE_TINT -uniform vec3 tint; -#endif - -#if USE_ROUNDING -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; -#include "rounding.glsl" -#endif - -#if USE_CM -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction - -#if USE_TONEMAP || USE_SDR_MOD -uniform mat3 targetPrimariesXYZ; -#else -const mat3 targetPrimariesXYZ = mat3(0.0); -#endif - -#include "CM.glsl" -#endif - -layout(location = 0) out vec4 fragColor; -void main() { -#if USE_RGBA - vec4 pixColor = texture(tex, v_texcoord); -#else - vec4 pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0); -#endif - -#if USE_DISCARD && !USE_BLUR - if (discardOpaque && pixColor.a * alpha == 1.0) - discard; - - if (discardAlpha && pixColor.a <= discardAlphaValue) - discard; -#endif - -#if USE_CM - pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange -#if USE_ICC - , - iccLut3D, iccLutSize -#else -#if USE_TONEMAP || USE_SDR_MOD - , - targetPrimariesXYZ -#endif -#if USE_TONEMAP - , - maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance -#endif -#if USE_SDR_MOD - , - sdrSaturation, sdrBrightnessMultiplier -#endif -#endif - ); -#endif - -#if USE_TINT - pixColor.rgb = pixColor.rgb * tint; -#endif - -#if USE_ROUNDING - pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); -#endif -#if USE_BLUR -#if USE_DISCARD - pixColor = mix(pixColor, vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, pixColor.rgb, pixColor.a), 1.0), - discardAlpha && (pixColor.a <= discardAlphaValue) ? 0.0 : 1.0); -#else - pixColor = vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, pixColor.rgb, pixColor.a), 1.0); -#endif -#endif - - fragColor = pixColor * alpha; -} diff --git a/src/render/shaders/glsl/tex300.vert b/src/render/shaders/glsl/tex300.vert deleted file mode 100644 index e20e7356..00000000 --- a/src/render/shaders/glsl/tex300.vert +++ /dev/null @@ -1,19 +0,0 @@ -#version 300 es - -uniform mat3 proj; -uniform vec4 color; - -in vec2 pos; -in vec2 texcoord; -in vec2 texcoordMatte; - -out vec4 v_color; -out vec2 v_texcoord; -out vec2 v_texcoordMatte; - -void main() { - gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); - v_color = color; - v_texcoord = texcoord; - v_texcoordMatte = texcoordMatte; -} diff --git a/src/render/shaders/glsl/tex320.vert b/src/render/shaders/glsl/tex320.vert deleted file mode 100644 index b99e2ba7..00000000 --- a/src/render/shaders/glsl/tex320.vert +++ /dev/null @@ -1,19 +0,0 @@ -#version 320 es - -uniform mat3 proj; -uniform vec4 color; - -in vec2 pos; -in vec2 texcoord; -in vec2 texcoordMatte; - -out vec4 v_color; -out vec2 v_texcoord; -out vec2 v_texcoordMatte; - -void main() { - gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); - v_color = color; - v_texcoord = texcoord; - v_texcoordMatte = texcoordMatte; -} diff --git a/src/render/shaders/glsl/tonemap.glsl b/src/render/shaders/glsl/tonemap.glsl deleted file mode 100644 index a0ba24ef..00000000 --- a/src/render/shaders/glsl/tonemap.glsl +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef ALLOW_INCLUDES -#define ALLOW_INCLUDES -#extension GL_ARB_shading_language_include : enable -#endif -#include "constants.h" - -const mat3 BT2020toLMS = mat3(0.3592, 0.6976, -0.0358, -0.1922, 1.1004, 0.0755, 0.0070, 0.0749, 0.8434); -//const mat3 LMStoBT2020 = inverse(BT2020toLMS); -const mat3 LMStoBT2020 = mat3( // - 2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081, // - 0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409, // - -0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394 // -); - -// const mat3 ICtCpPQ = transpose(mat3( -// 2048.0, 2048.0, 0.0, -// 6610.0, -13613.0, 7003.0, -// 17933.0, -17390.0, -543.0 -// ) / 4096.0); -const mat3 ICtCpPQ = mat3( // - 0.5, 1.61376953125, 4.378173828125, // - 0.5, -3.323486328125, -4.24560546875, // - 0.0, 1.709716796875, -0.132568359375 // -); -//const mat3 ICtCpPQInv = inverse(ICtCpPQ); -const mat3 ICtCpPQInv = mat3( // - 1.0, 1.0, 1.0, // - 0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118, // - 0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185 // -); - -// unused for now -// const mat3 ICtCpHLG = transpose(mat3( -// 2048.0, 2048.0, 0.0, -// 3625.0, -7465.0, 3840.0, -// 9500.0, -9212.0, -288.0 -// ) / 4096.0); -// const mat3 ICtCpHLGInv = inverse(ICtCpHLG); - -vec4 tonemap(vec4 color, mat3 dstXYZ, float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance) { - if (maxLuminance < dstMaxLuminance * 1.01) - return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]); - - mat3 toLMS = BT2020toLMS * dstXYZ; - mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020; - - vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb; - vec3 ICtCp = ICtCpPQ * lms; - - float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2); - float luminance = pow((max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), PQ_INV_M1) * HDR_MAX_LUMINANCE; - - float linearPart = min(luminance, dstRefLuminance); - float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0); - float maxExcessLuminance = max(maxLuminance - dstRefLuminance, 1.0); - float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0)); - float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance); - float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance); - - // scale src to dst reference - float refScale = dstRefLuminance / srcRefLuminance; - - return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]); -} diff --git a/src/debug/crash/SignalSafe.cpp b/src/signal-safe.cpp similarity index 78% rename from src/debug/crash/SignalSafe.cpp rename to src/signal-safe.cpp index 22717f1b..baee7b44 100644 --- a/src/debug/crash/SignalSafe.cpp +++ b/src/signal-safe.cpp @@ -1,4 +1,4 @@ -#include "SignalSafe.hpp" +#include "signal-safe.hpp" #ifndef __GLIBC__ #include @@ -7,13 +7,11 @@ #include #include -using namespace SignalSafe; - // NOLINTNEXTLINE extern "C" char** environ; // -char const* SignalSafe::getenv(char const* name) { +char const* sigGetenv(char const* name) { const size_t len = strlen(name); for (char** var = environ; *var != nullptr; var++) { if (strncmp(*var, name, len) == 0 && (*var)[len] == '=') { @@ -23,7 +21,7 @@ char const* SignalSafe::getenv(char const* name) { return nullptr; } -char const* SignalSafe::strsignal(int sig) { +char const* sigStrsignal(int sig) { #ifdef __GLIBC__ return sigabbrev_np(sig); #elif defined(__DragonFly__) || defined(__FreeBSD__) diff --git a/src/signal-safe.hpp b/src/signal-safe.hpp new file mode 100644 index 00000000..ac7514fc --- /dev/null +++ b/src/signal-safe.hpp @@ -0,0 +1,175 @@ +#pragma once + +#include "defines.hpp" +#include + +template +class CMaxLengthCString { + public: + CMaxLengthCString() { + m_str[0] = '\0'; + } + void operator+=(char const* rhs) { + write(rhs, strlen(rhs)); + } + void write(char const* data, size_t len) { + if (m_boundsExceeded || m_strPos + len >= N) { + m_boundsExceeded = true; + return; + } + memcpy(m_str + m_strPos, data, len); + m_strPos += len; + m_str[m_strPos] = '\0'; + } + void write(char c) { + if (m_boundsExceeded || m_strPos + 1 >= N) { + m_boundsExceeded = true; + return; + } + m_str[m_strPos] = c; + m_strPos++; + } + void writeNum(size_t num) { + size_t d = 1; + while (num / 10 >= d) + d *= 10; + while (num > 0) { + char c = '0' + (num / d); + write(c); + num %= d; + d /= 10; + } + } + char const* getStr() { + return m_str; + }; + bool boundsExceeded() { + return m_boundsExceeded; + }; + + private: + char m_str[N]; + size_t m_strPos = 0; + bool m_boundsExceeded = false; +}; + +template +class CBufFileWriter { + public: + CBufFileWriter(int fd_) : m_fd(fd_) {} + ~CBufFileWriter() { + flush(); + } + void write(char const* data, size_t len) { + while (len > 0) { + size_t to_add = std::min(len, (size_t)BUFSIZE - m_writeBufPos); + memcpy(m_writeBuf + m_writeBufPos, data, to_add); + data += to_add; + len -= to_add; + m_writeBufPos += to_add; + if (m_writeBufPos == BUFSIZE) + flush(); + } + } + void write(char c) { + if (m_writeBufPos == BUFSIZE) + flush(); + m_writeBuf[m_writeBufPos] = c; + m_writeBufPos++; + } + void operator+=(char const* str) { + write(str, strlen(str)); + } + void operator+=(std::string_view str) { + write(str.data(), str.size()); + } + void operator+=(char c) { + write(c); + } + void writeNum(size_t num) { + size_t d = 1; + while (num / 10 >= d) + d *= 10; + while (num > 0) { + char c = '0' + (num / d); + write(c); + num %= d; + d /= 10; + } + } + void writeCmdOutput(const char* cmd) { + int pipefd[2]; + if (pipe(pipefd) < 0) { + *this += " failmsg(pipefd[1]); + failmsg += " 0) { + write(readbuf, len); + } + if (len < 0) { + *this += "m_wm->getConnection()); - xcb_send_event(conn, 0, targetWindow, XCB_EVENT_MASK_NO_EVENT, rc(&event)); - xcb_flush(conn); + + xcb_send_event(g_pXWayland->pWM->connection, 0, targetWindow, XCB_EVENT_MASK_NO_EVENT, (const char*)&event); + xcb_flush(g_pXWayland->pWM->connection); } xcb_window_t CX11DataDevice::getProxyWindow(xcb_window_t window) { xcb_window_t targetWindow = window; xcb_get_property_cookie_t proxyCookie = - xcb_get_property((g_pXWayland->m_wm->getConnection()), PROPERTY_OFFSET, window, HYPRATOMS["XdndProxy"], XCB_ATOM_WINDOW, PROPERTY_OFFSET, PROPERTY_LENGTH); - xcb_get_property_reply_t* proxyReply = xcb_get_property_reply(g_pXWayland->m_wm->getConnection(), proxyCookie, nullptr); + xcb_get_property(g_pXWayland->pWM->connection, PROPERTY_OFFSET, window, HYPRATOMS["XdndProxy"], XCB_ATOM_WINDOW, PROPERTY_OFFSET, PROPERTY_LENGTH); + xcb_get_property_reply_t* proxyReply = xcb_get_property_reply(g_pXWayland->pWM->connection, proxyCookie, nullptr); const auto isValidPropertyReply = [](xcb_get_property_reply_t* reply) { return reply && reply->type == XCB_ATOM_WINDOW && reply->format == PROPERTY_FORMAT_32BIT && xcb_get_property_value_length(reply) == sizeof(xcb_window_t); }; if (isValidPropertyReply(proxyReply)) { - xcb_window_t proxyWindow = *sc(xcb_get_property_value(proxyReply)); + xcb_window_t proxyWindow = *(xcb_window_t*)xcb_get_property_value(proxyReply); xcb_get_property_cookie_t proxyVerifyCookie = - xcb_get_property(g_pXWayland->m_wm->getConnection(), PROPERTY_OFFSET, proxyWindow, HYPRATOMS["XdndProxy"], XCB_ATOM_WINDOW, PROPERTY_OFFSET, PROPERTY_LENGTH); - xcb_get_property_reply_t* proxyVerifyReply = xcb_get_property_reply(g_pXWayland->m_wm->getConnection(), proxyVerifyCookie, nullptr); + xcb_get_property(g_pXWayland->pWM->connection, PROPERTY_OFFSET, proxyWindow, HYPRATOMS["XdndProxy"], XCB_ATOM_WINDOW, PROPERTY_OFFSET, PROPERTY_LENGTH); + xcb_get_property_reply_t* proxyVerifyReply = xcb_get_property_reply(g_pXWayland->pWM->connection, proxyVerifyCookie, nullptr); if (isValidPropertyReply(proxyVerifyReply)) { - xcb_window_t verifyWindow = *sc(xcb_get_property_value(proxyVerifyReply)); + xcb_window_t verifyWindow = *(xcb_window_t*)xcb_get_property_value(proxyVerifyReply); if (verifyWindow == proxyWindow) { targetWindow = proxyWindow; - Log::logger->log(Log::DEBUG, "Using XdndProxy window {:x} for window {:x}", proxyWindow, window); + Debug::log(LOG, "Using XdndProxy window {:x} for window {:x}", proxyWindow, window); } } - free(proxyVerifyReply); // NOLINT(cppcoreguidelines-no-malloc) + free(proxyVerifyReply); } - free(proxyReply); // NOLINT(cppcoreguidelines-no-malloc) + free(proxyReply); return targetWindow; } @@ -81,16 +81,16 @@ SP CX11DataOffer::getWayland() { } SP CX11DataOffer::getX11() { - return m_self.lock(); + return self.lock(); } SP CX11DataOffer::getSource() { - return m_source.lock(); + return source.lock(); } void CX11DataOffer::markDead() { #ifndef NO_XWAYLAND - std::erase(g_pXWayland->m_wm->m_dndDataOffers, m_self); + std::erase(g_pXWayland->pWM->dndDataOffers, self); #endif } @@ -100,17 +100,17 @@ void CX11DataDevice::sendDataOffer(SP offer) { void CX11DataDevice::sendEnter(uint32_t serial, SP surf, const Vector2D& local, SP offer) { #ifndef NO_XWAYLAND - auto XSURF = g_pXWayland->m_wm->windowForWayland(surf); + auto XSURF = g_pXWayland->pWM->windowForWayland(surf); if (!XSURF) { - Log::logger->log(Log::ERR, "CX11DataDevice::sendEnter: No xwayland surface for destination"); + Debug::log(ERR, "CX11DataDevice::sendEnter: No xwayland surface for destination"); return; } auto SOURCE = offer->getSource(); if (!SOURCE) { - Log::logger->log(Log::ERR, "CX11DataDevice::sendEnter: No source"); + Debug::log(ERR, "CX11DataDevice::sendEnter: No source"); return; } @@ -118,54 +118,54 @@ void CX11DataDevice::sendEnter(uint32_t serial, SP surf, con // reserve to avoid reallocations targets.reserve(SOURCE->mimes().size()); for (auto const& m : SOURCE->mimes()) { - targets.push_back(g_pXWayland->m_wm->mimeToAtom(m)); + targets.push_back(g_pXWayland->pWM->mimeToAtom(m)); } - xcb_change_property(g_pXWayland->m_wm->getConnection(), XCB_PROP_MODE_REPLACE, g_pXWayland->m_wm->m_dndSelection.window, HYPRATOMS["XdndTypeList"], XCB_ATOM_ATOM, 32, - targets.size(), targets.data()); + xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, g_pXWayland->pWM->dndSelection.window, HYPRATOMS["XdndTypeList"], XCB_ATOM_ATOM, 32, targets.size(), + targets.data()); - xcb_set_selection_owner(g_pXWayland->m_wm->getConnection(), g_pXWayland->m_wm->m_dndSelection.window, HYPRATOMS["XdndSelection"], XCB_TIME_CURRENT_TIME); - xcb_flush(g_pXWayland->m_wm->getConnection()); + xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->dndSelection.window, HYPRATOMS["XdndSelection"], XCB_TIME_CURRENT_TIME); + xcb_flush(g_pXWayland->pWM->connection); - xcb_window_t targetWindow = getProxyWindow(XSURF->m_xID); + xcb_window_t targetWindow = getProxyWindow(XSURF->xID); xcb_client_message_data_t data = {{0}}; - data.data32[0] = g_pXWayland->m_wm->m_dndSelection.window; + data.data32[0] = g_pXWayland->pWM->dndSelection.window; data.data32[1] = XDND_VERSION << 24; data.data32[1] |= 1; sendDndEvent(targetWindow, HYPRATOMS["XdndEnter"], data); - m_lastSurface = XSURF; - m_lastOffer = offer; + lastSurface = XSURF; + lastOffer = offer; - auto hlSurface = XSURF->m_surface.lock(); + auto hlSurface = XSURF->surface.lock(); if (!hlSurface) { - Log::logger->log(Log::ERR, "CX11DataDevice::sendEnter: Non desktop x surface?!"); - m_lastSurfaceCoords = {}; + Debug::log(ERR, "CX11DataDevice::sendEnter: Non desktop x surface?!"); + lastSurfaceCoords = {}; return; } - m_lastSurfaceCoords = g_pXWaylandManager->xwaylandToWaylandCoords(XSURF->m_geometry.pos()); + lastSurfaceCoords = g_pXWaylandManager->xwaylandToWaylandCoords(XSURF->geometry.pos()); #endif } void CX11DataDevice::cleanupState() { - m_lastSurface.reset(); - m_lastOffer.reset(); - m_lastSurfaceCoords = {}; - m_lastTime = 0; + lastSurface.reset(); + lastOffer.reset(); + lastSurfaceCoords = {}; + lastTime = 0; } void CX11DataDevice::sendLeave() { #ifndef NO_XWAYLAND - if (!m_lastSurface) + if (!lastSurface) return; - xcb_window_t targetWindow = getProxyWindow(m_lastSurface->m_xID); + xcb_window_t targetWindow = getProxyWindow(lastSurface->xID); xcb_client_message_data_t data = {{0}}; - data.data32[0] = g_pXWayland->m_wm->m_dndSelection.window; + data.data32[0] = g_pXWayland->pWM->dndSelection.window; sendDndEvent(targetWindow, HYPRATOMS["XdndLeave"], data); @@ -175,38 +175,38 @@ void CX11DataDevice::sendLeave() { void CX11DataDevice::sendMotion(uint32_t timeMs, const Vector2D& local) { #ifndef NO_XWAYLAND - if (!m_lastSurface || !m_lastOffer || !m_lastOffer->getSource()) + if (!lastSurface || !lastOffer || !lastOffer->getSource()) return; - xcb_window_t targetWindow = getProxyWindow(m_lastSurface->m_xID); + xcb_window_t targetWindow = getProxyWindow(lastSurface->xID); - const auto XCOORDS = g_pXWaylandManager->waylandToXWaylandCoords(m_lastSurfaceCoords + local); - const uint32_t coords = (sc(XCOORDS.x) << 16) | sc(XCOORDS.y); + const auto XCOORDS = g_pXWaylandManager->waylandToXWaylandCoords(lastSurfaceCoords + local); + const uint32_t coords = ((uint32_t)XCOORDS.x << 16) | (uint32_t)XCOORDS.y; xcb_client_message_data_t data = {{0}}; - data.data32[0] = g_pXWayland->m_wm->m_dndSelection.window; + data.data32[0] = g_pXWayland->pWM->dndSelection.window; data.data32[2] = coords; data.data32[3] = timeMs; - data.data32[4] = dndActionToAtom(m_lastOffer->getSource()->actions()); + data.data32[4] = dndActionToAtom(lastOffer->getSource()->actions()); sendDndEvent(targetWindow, HYPRATOMS["XdndPosition"], data); - m_lastTime = timeMs; + lastTime = timeMs; #endif } void CX11DataDevice::sendDrop() { #ifndef NO_XWAYLAND - if (!m_lastSurface || !m_lastOffer) { - Log::logger->log(Log::ERR, "CX11DataDevice::sendDrop: No surface or offer"); + if (!lastSurface || !lastOffer) { + Debug::log(ERR, "CX11DataDevice::sendDrop: No surface or offer"); return; } - xcb_window_t targetWindow = getProxyWindow(m_lastSurface->m_xID); + xcb_window_t targetWindow = getProxyWindow(lastSurface->xID); xcb_client_message_data_t data = {{0}}; - data.data32[0] = g_pXWayland->m_wm->m_dndSelection.window; - data.data32[2] = m_lastTime; + data.data32[0] = g_pXWayland->pWM->dndSelection.window; + data.data32[2] = lastTime; sendDndEvent(targetWindow, HYPRATOMS["XdndDrop"], data); @@ -227,11 +227,11 @@ SP CX11DataDevice::getWayland() { } SP CX11DataDevice::getX11() { - return m_self.lock(); + return self.lock(); } std::vector CX11DataSource::mimes() { - return m_mimeTypes; + return mimeTypes; } void CX11DataSource::send(const std::string& mime, CFileDescriptor fd) { @@ -243,30 +243,30 @@ void CX11DataSource::accepted(const std::string& mime) { } void CX11DataSource::cancelled() { - m_dndSuccess = false; - m_dropped = false; + dndSuccess = false; + dropped = false; } bool CX11DataSource::hasDnd() { - return m_dnd; + return dnd; } bool CX11DataSource::dndDone() { - return m_dropped; + return dropped; } void CX11DataSource::error(uint32_t code, const std::string& msg) { - Log::logger->log(Log::ERR, "CX11DataSource error: code {} msg {}", code, msg); - m_dndSuccess = false; - m_dropped = false; + Debug::log(ERR, "CX11DataSource error: code {} msg {}", code, msg); + dndSuccess = false; + dropped = false; } void CX11DataSource::sendDndFinished() { - m_dndSuccess = true; + dndSuccess = true; } uint32_t CX11DataSource::actions() { - return m_supportedActions; + return supportedActions; } eDataSourceType CX11DataSource::type() { @@ -274,7 +274,7 @@ eDataSourceType CX11DataSource::type() { } void CX11DataSource::sendDndDropPerformed() { - m_dropped = true; + dropped = true; } void CX11DataSource::sendDndAction(wl_data_device_manager_dnd_action a) { @@ -283,16 +283,16 @@ void CX11DataSource::sendDndAction(wl_data_device_manager_dnd_action a) { void CX11DataDevice::forceCleanupDnd() { #ifndef NO_XWAYLAND - if (m_lastOffer) { - auto source = m_lastOffer->getSource(); + if (lastOffer) { + auto source = lastOffer->getSource(); if (source) { - source->sendDndFinished(); source->cancelled(); + source->sendDndFinished(); } } - xcb_set_selection_owner(g_pXWayland->m_wm->getConnection(), XCB_ATOM_NONE, HYPRATOMS["XdndSelection"], XCB_TIME_CURRENT_TIME); - xcb_flush(g_pXWayland->m_wm->getConnection()); + xcb_set_selection_owner(g_pXWayland->pWM->connection, XCB_ATOM_NONE, HYPRATOMS["XdndSelection"], XCB_TIME_CURRENT_TIME); + xcb_flush(g_pXWayland->pWM->connection); cleanupState(); diff --git a/src/xwayland/Dnd.hpp b/src/xwayland/Dnd.hpp index 33c841a5..1b97f5cc 100644 --- a/src/xwayland/Dnd.hpp +++ b/src/xwayland/Dnd.hpp @@ -5,9 +5,7 @@ #include "../managers/input/InputManager.hpp" #include #include -#ifndef NO_XWAYLAND #include -#endif #define XDND_VERSION 5 @@ -24,9 +22,15 @@ class CX11DataOffer : public IDataOffer { virtual SP getSource(); virtual void markDead(); - WP m_source; - WP m_self; - WP m_xwaylandSurface; + WP source; + WP self; + WP xwaylandSurface; + + bool dead = false; + bool accepted = false; + bool recvd = false; + + uint32_t actions = 0; }; class CX11DataSource : public IDataSource { @@ -47,12 +51,15 @@ class CX11DataSource : public IDataSource { virtual void sendDndDropPerformed(); virtual void sendDndAction(wl_data_device_manager_dnd_action a); - bool m_dnd = true; - bool m_dndSuccess = false; - bool m_dropped = false; + bool used = false; + bool dnd = true; + bool dndSuccess = false; + bool dropped = false; - std::vector m_mimeTypes; - uint32_t m_supportedActions = 0; + WP self; + + std::vector mimeTypes; + uint32_t supportedActions = 0; }; class CX11DataDevice : public IDataDevice { @@ -70,7 +77,7 @@ class CX11DataDevice : public IDataDevice { virtual eDataSourceType type(); void forceCleanupDnd(); - WP m_self; + WP self; private: void cleanupState(); @@ -78,8 +85,8 @@ class CX11DataDevice : public IDataDevice { xcb_window_t getProxyWindow(xcb_window_t window); void sendDndEvent(xcb_window_t targetWindow, xcb_atom_t type, xcb_client_message_data_t& data); #endif - WP m_lastSurface; - WP m_lastOffer; - Vector2D m_lastSurfaceCoords; - uint32_t m_lastTime = 0; + WP lastSurface; + WP lastOffer; + Vector2D lastSurfaceCoords; + uint32_t lastTime = 0; }; diff --git a/src/xwayland/Server.cpp b/src/xwayland/Server.cpp index 1ece7454..6f057f9e 100644 --- a/src/xwayland/Server.cpp +++ b/src/xwayland/Server.cpp @@ -21,7 +21,7 @@ #include "Server.hpp" #include "XWayland.hpp" #include "config/ConfigValue.hpp" -#include "debug/log/Logger.hpp" +#include "debug/Log.hpp" #include "../defines.hpp" #include "../Compositor.hpp" #include "../managers/CursorManager.hpp" @@ -41,20 +41,20 @@ static CFileDescriptor createSocket(struct sockaddr_un* addr, size_t pathSize) { socklen_t size = offsetof(struct sockaddr_un, sun_path) + pathSize + 1; CFileDescriptor fd{socket(AF_UNIX, SOCK_STREAM, 0)}; if (!fd.isValid()) { - Log::logger->log(Log::ERR, "Failed to create socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); + Debug::log(ERR, "Failed to create socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); return {}; } if (!fd.setFlags(fd.getFlags() | FD_CLOEXEC)) { - Log::logger->log(Log::ERR, "Failed to set flags for socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); + Debug::log(ERR, "Failed to set flags for socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); return {}; } if (isRegularSocket) unlink(addr->sun_path); - if (bind(fd.get(), rc(addr), size) < 0) { - Log::logger->log(Log::ERR, "Failed to bind socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); + if (bind(fd.get(), (struct sockaddr*)addr, size) < 0) { + Debug::log(ERR, "Failed to bind socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); if (isRegularSocket) unlink(addr->sun_path); return {}; @@ -66,11 +66,11 @@ static CFileDescriptor createSocket(struct sockaddr_un* addr, size_t pathSize) { if (isRegularSocket && chmod(addr->sun_path, 0666) < 0) { // We are only extending the default permissions, // and I don't see the reason to make a full stop in case of a failed operation. - Log::logger->log(Log::ERR, "Failed to set permission mode for socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); + Debug::log(ERR, "Failed to set permission mode for socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); } if (listen(fd.get(), SOCKET_BACKLOG) < 0) { - Log::logger->log(Log::ERR, "Failed to listen to socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); + Debug::log(ERR, "Failed to listen to socket {}{}", dbgSocketPathPrefix, dbgSocketPathRem); if (isRegularSocket) unlink(addr->sun_path); return {}; @@ -83,23 +83,23 @@ static bool checkPermissionsForSocketDir() { struct stat buf; if (lstat("/tmp/.X11-unix", &buf)) { - Log::logger->log(Log::ERR, "Failed to stat X11 socket dir"); + Debug::log(ERR, "Failed to stat X11 socket dir"); return false; } if (!(buf.st_mode & S_IFDIR)) { - Log::logger->log(Log::ERR, "X11 socket dir is not a directory"); + Debug::log(ERR, "X11 socket dir is not a directory"); return false; } if ((buf.st_uid != 0) && (buf.st_uid != getuid())) { - Log::logger->log(Log::ERR, "X11 socket dir is not owned by root or current user"); + Debug::log(ERR, "X11 socket dir is not owned by root or current user"); return false; } if (!(buf.st_mode & S_ISVTX)) { if ((buf.st_mode & (S_IWGRP | S_IWOTH))) { - Log::logger->log(Log::ERR, "X11 socket dir is writable by others"); + Debug::log(ERR, "X11 socket dir is writable by others"); return false; } } @@ -112,7 +112,7 @@ static bool ensureSocketDirExists() { if (errno == EEXIST) return checkPermissionsForSocketDir(); else { - Log::logger->log(Log::ERR, "XWayland: Couldn't create socket dir"); + Debug::log(ERR, "XWayland: Couldn't create socket dir"); return false; } } @@ -149,7 +149,7 @@ static bool openSockets(std::array& sockets, int display) { } #else if (*CREATEABSTRACTSOCKET) { - Log::logger->log(Log::WARN, "The abstract XWayland Unix domain socket might be used only on Linux systems. A regular one'll be created instead."); + Debug::log(WARN, "The abstract XWayland Unix domain socket might be used only on Linux systems. A regular one'll be created insted."); } path = getSocketPath(display, false); strncpy(addr.sun_path, path.c_str(), path.length() + 1); @@ -172,18 +172,19 @@ static bool openSockets(std::array& sockets, int display) { } static void startServer(void* data) { - if (!g_pXWayland->m_server->start()) - Log::logger->log(Log::ERR, "The XWayland server could not start! XWayland will not work..."); + if (!g_pXWayland->pServer->start()) + Debug::log(ERR, "The XWayland server could not start! XWayland will not work..."); } static int xwaylandReady(int fd, uint32_t mask, void* data) { - return g_pXWayland->m_server->ready(fd, mask); + CFileDescriptor xwlFd{fd}; + return g_pXWayland->pServer->ready(std::move(xwlFd), mask); } static bool safeRemove(const std::string& path) { try { return std::filesystem::remove(path); - } catch (const std::exception& e) { Log::logger->log(Log::ERR, "[XWayland] Failed to remove {}", path); } + } catch (const std::exception& e) { Debug::log(ERR, "[XWayland] Failed to remove {}", path); } return false; } @@ -194,20 +195,19 @@ bool CXWaylandServer::tryOpenSockets() { CFileDescriptor fd{open(lockPath.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, LOCK_FILE_MODE)}; if (fd.isValid()) { // we managed to open the lock - if (!openSockets(m_xFDs, i)) { + if (!openSockets(xFDs, i)) { safeRemove(lockPath); continue; } - const std::string pidStr = std::format("{:010d}\n", getpid()); - ASSERT(pidStr.length() == 11); - if (write(fd.get(), pidStr.c_str(), 11) != 11L) { + const std::string pidStr = std::to_string(getpid()); + if (write(fd.get(), pidStr.c_str(), pidStr.length()) != (long)pidStr.length()) { safeRemove(lockPath); continue; } - m_display = i; - m_displayName = std::format(":{}", m_display); + display = i; + displayName = std::format(":{}", display); break; } @@ -231,12 +231,12 @@ bool CXWaylandServer::tryOpenSockets() { } } - if (m_display < 0) { - Log::logger->log(Log::ERR, "Failed to find a suitable socket for XWayland"); + if (display < 0) { + Debug::log(ERR, "Failed to find a suitable socket for XWayland"); return false; } - Log::logger->log(Log::DEBUG, "XWayland found a suitable display socket at DISPLAY: {}", m_displayName); + Debug::log(LOG, "XWayland found a suitable display socket at DISPLAY: {}", displayName); return true; } @@ -246,118 +246,118 @@ CXWaylandServer::CXWaylandServer() { CXWaylandServer::~CXWaylandServer() { die(); - if (m_display < 0) + if (display < 0) return; - std::string lockPath = std::format("/tmp/.X{}-lock", m_display); + std::string lockPath = std::format("/tmp/.X{}-lock", display); safeRemove(lockPath); std::string path; for (bool isLinux : {true, false}) { - path = getSocketPath(m_display, isLinux); + path = getSocketPath(display, isLinux); safeRemove(path); } } void CXWaylandServer::die() { - if (m_display < 0) + if (display < 0) return; - if (m_xFDReadEvents[0]) { - wl_event_source_remove(m_xFDReadEvents[0]); - wl_event_source_remove(m_xFDReadEvents[1]); - m_xFDReadEvents = {nullptr, nullptr}; + if (xFDReadEvents[0]) { + wl_event_source_remove(xFDReadEvents[0]); + wl_event_source_remove(xFDReadEvents[1]); + xFDReadEvents = {nullptr, nullptr}; } - if (m_pipeSource) - wl_event_source_remove(m_pipeSource); + if (pipeSource) + wl_event_source_remove(pipeSource); // possible crash. Better to leak a bit. //if (xwaylandClient) // wl_client_destroy(xwaylandClient); - m_xwaylandClient = nullptr; + xwaylandClient = nullptr; } bool CXWaylandServer::create() { if (!tryOpenSockets()) return false; - setenv("DISPLAY", m_displayName.c_str(), true); + setenv("DISPLAY", displayName.c_str(), true); // TODO: lazy mode - m_idleSource = wl_event_loop_add_idle(g_pCompositor->m_wlEventLoop, ::startServer, nullptr); + idleSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, ::startServer, nullptr); return true; } void CXWaylandServer::runXWayland(CFileDescriptor& notifyFD) { - if (!m_xFDs[0].setFlags(m_xFDs[0].getFlags() & ~FD_CLOEXEC) || !m_xFDs[1].setFlags(m_xFDs[1].getFlags() & ~FD_CLOEXEC) || - !m_waylandFDs[1].setFlags(m_waylandFDs[1].getFlags() & ~FD_CLOEXEC) || !m_xwmFDs[1].setFlags(m_xwmFDs[1].getFlags() & ~FD_CLOEXEC)) { - Log::logger->log(Log::ERR, "Failed to unset cloexec on fds"); + if (!xFDs[0].setFlags(xFDs[0].getFlags() & ~FD_CLOEXEC) || !xFDs[1].setFlags(xFDs[1].getFlags() & ~FD_CLOEXEC) || + !waylandFDs[1].setFlags(waylandFDs[1].getFlags() & ~FD_CLOEXEC) || !xwmFDs[1].setFlags(xwmFDs[1].getFlags() & ~FD_CLOEXEC)) { + Debug::log(ERR, "Failed to unset cloexec on fds"); _exit(EXIT_FAILURE); } - auto cmd = std::format("exec Xwayland {} -rootless -core -listenfd {} -listenfd {} -displayfd {} -wm {}", m_displayName, m_xFDs[0].get(), m_xFDs[1].get(), notifyFD.get(), - m_xwmFDs[1].get()); + auto cmd = + std::format("Xwayland {} -rootless -core -listenfd {} -listenfd {} -displayfd {} -wm {}", displayName, xFDs[0].get(), xFDs[1].get(), notifyFD.get(), xwmFDs[1].get()); - auto waylandSocket = std::format("{}", m_waylandFDs[1].get()); + auto waylandSocket = std::format("{}", waylandFDs[1].get()); setenv("WAYLAND_SOCKET", waylandSocket.c_str(), true); - Log::logger->log(Log::DEBUG, "Starting XWayland with \"{}\", bon voyage!", cmd); + Debug::log(LOG, "Starting XWayland with \"{}\", bon voyage!", cmd); execl("/bin/sh", "/bin/sh", "-c", cmd.c_str(), nullptr); - Log::logger->log(Log::ERR, "XWayland failed to open"); + Debug::log(ERR, "XWayland failed to open"); _exit(1); } bool CXWaylandServer::start() { - m_idleSource = nullptr; + idleSource = nullptr; int wlPair[2] = {-1, -1}; if (socketpair(AF_UNIX, SOCK_STREAM, 0, wlPair) != 0) { - Log::logger->log(Log::ERR, "socketpair failed (1)"); + Debug::log(ERR, "socketpair failed (1)"); die(); return false; } - m_waylandFDs[0] = CFileDescriptor{wlPair[0]}; - m_waylandFDs[1] = CFileDescriptor{wlPair[1]}; + waylandFDs[0] = CFileDescriptor{wlPair[0]}; + waylandFDs[1] = CFileDescriptor{wlPair[1]}; - if (!m_waylandFDs[0].setFlags(m_waylandFDs[0].getFlags() | FD_CLOEXEC) || !m_waylandFDs[1].setFlags(m_waylandFDs[1].getFlags() | FD_CLOEXEC)) { - Log::logger->log(Log::ERR, "set_cloexec failed (1)"); + if (!waylandFDs[0].setFlags(waylandFDs[0].getFlags() | FD_CLOEXEC) || !waylandFDs[1].setFlags(waylandFDs[1].getFlags() | FD_CLOEXEC)) { + Debug::log(ERR, "set_cloexec failed (1)"); die(); return false; } int xwmPair[2] = {-1, -1}; if (socketpair(AF_UNIX, SOCK_STREAM, 0, xwmPair) != 0) { - Log::logger->log(Log::ERR, "socketpair failed (2)"); + Debug::log(ERR, "socketpair failed (2)"); die(); return false; } - m_xwmFDs[0] = CFileDescriptor{xwmPair[0]}; - m_xwmFDs[1] = CFileDescriptor{xwmPair[1]}; + xwmFDs[0] = CFileDescriptor{xwmPair[0]}; + xwmFDs[1] = CFileDescriptor{xwmPair[1]}; - if (!m_xwmFDs[0].setFlags(m_xwmFDs[0].getFlags() | FD_CLOEXEC) || !m_xwmFDs[1].setFlags(m_xwmFDs[1].getFlags() | FD_CLOEXEC)) { - Log::logger->log(Log::ERR, "set_cloexec failed (2)"); + if (!xwmFDs[0].setFlags(xwmFDs[0].getFlags() | FD_CLOEXEC) || !xwmFDs[1].setFlags(xwmFDs[1].getFlags() | FD_CLOEXEC)) { + Debug::log(ERR, "set_cloexec failed (2)"); die(); return false; } - m_xwaylandClient = wl_client_create(g_pCompositor->m_wlDisplay, m_waylandFDs[0].get()); - if (!m_xwaylandClient) { - Log::logger->log(Log::ERR, "wl_client_create failed"); + xwaylandClient = wl_client_create(g_pCompositor->m_sWLDisplay, waylandFDs[0].get()); + if (!xwaylandClient) { + Debug::log(ERR, "wl_client_create failed"); die(); return false; } - m_waylandFDs[0].take(); // wl_client owns this fd now + waylandFDs[0].take(); // does this leak? int notify[2] = {-1, -1}; if (pipe(notify) < 0) { - Log::logger->log(Log::ERR, "pipe failed"); + Debug::log(ERR, "pipe failed"); die(); return false; } @@ -365,55 +365,68 @@ bool CXWaylandServer::start() { CFileDescriptor notifyFds[2] = {CFileDescriptor{notify[0]}, CFileDescriptor{notify[1]}}; if (!notifyFds[0].setFlags(notifyFds[0].getFlags() | FD_CLOEXEC)) { - Log::logger->log(Log::ERR, "set_cloexec failed (3)"); + Debug::log(ERR, "set_cloexec failed (3)"); die(); return false; } - m_pipeSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, notifyFds[0].get(), WL_EVENT_READABLE, ::xwaylandReady, nullptr); - m_pipeFd = std::move(notifyFds[0]); + pipeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, notifyFds[0].get(), WL_EVENT_READABLE, ::xwaylandReady, nullptr); + pipeFd = std::move(notifyFds[0]); - auto serverPID = fork(); + serverPID = fork(); if (serverPID < 0) { - Log::logger->log(Log::ERR, "fork failed"); + Debug::log(ERR, "fork failed"); die(); return false; } else if (serverPID == 0) { - runXWayland(notifyFds[1]); + pid_t pid = fork(); + if (pid < 0) { + Debug::log(ERR, "second fork failed"); + _exit(1); + } else if (pid == 0) + runXWayland(notifyFds[1]); + _exit(0); } return true; } -int CXWaylandServer::ready(int fd, uint32_t mask) { +int CXWaylandServer::ready(CFileDescriptor fd, uint32_t mask) { if (mask & WL_EVENT_READABLE) { // xwayland writes twice char buf[64]; - ssize_t n = read(fd, buf, sizeof(buf)); + ssize_t n = read(fd.get(), buf, sizeof(buf)); if (n < 0 && errno != EINTR) { - Log::logger->log(Log::ERR, "Xwayland: read from displayFd failed"); + Debug::log(ERR, "Xwayland: read from displayFd failed"); mask = 0; } else if (n <= 0 || buf[n - 1] != '\n') return 1; } - // if we don't have readable here, it failed - if (!(mask & WL_EVENT_READABLE)) { - Log::logger->log(Log::ERR, "Xwayland: startup failed, not setting up xwm"); - g_pXWayland->m_server.reset(); + while (waitpid(serverPID, nullptr, 0) < 0) { + if (errno == EINTR) + continue; + Debug::log(ERR, "Xwayland: waitpid for fork failed"); + g_pXWayland->pServer.reset(); return 1; } - Log::logger->log(Log::DEBUG, "XWayland is ready"); + // if we don't have readable here, it failed + if (!(mask & WL_EVENT_READABLE)) { + Debug::log(ERR, "Xwayland: startup failed, not setting up xwm"); + g_pXWayland->pServer.reset(); + return 1; + } - wl_event_source_remove(m_pipeSource); - m_pipeFd.reset(); - m_pipeSource = nullptr; + Debug::log(LOG, "XWayland is ready"); + + wl_event_source_remove(pipeSource); + pipeSource = nullptr; // start the wm - if (!g_pXWayland->m_wm) - g_pXWayland->m_wm = makeUnique(); + if (!g_pXWayland->pWM) + g_pXWayland->pWM = makeUnique(); g_pCursorManager->setXWaylandCursor(); diff --git a/src/xwayland/Server.hpp b/src/xwayland/Server.hpp index 7266a9b0..ccbcf6ea 100644 --- a/src/xwayland/Server.hpp +++ b/src/xwayland/Server.hpp @@ -20,25 +20,31 @@ class CXWaylandServer { bool start(); // called on ready - int ready(int fd, uint32_t mask); + int ready(Hyprutils::OS::CFileDescriptor fd, uint32_t mask); - void die(); + void die(); - wl_client* m_xwaylandClient = nullptr; + struct { + CSignal ready; + } events; + + wl_client* xwaylandClient = nullptr; private: bool tryOpenSockets(); void runXWayland(Hyprutils::OS::CFileDescriptor& notifyFD); - std::string m_displayName; - int m_display = -1; - std::array m_xFDs; - std::array m_xFDReadEvents = {nullptr, nullptr}; - wl_event_source* m_idleSource = nullptr; - wl_event_source* m_pipeSource = nullptr; - Hyprutils::OS::CFileDescriptor m_pipeFd; - std::array m_xwmFDs; - std::array m_waylandFDs; + pid_t serverPID = 0; + + std::string displayName; + int display = -1; + std::array xFDs; + std::array xFDReadEvents = {nullptr, nullptr}; + wl_event_source* idleSource = nullptr; + wl_event_source* pipeSource = nullptr; + Hyprutils::OS::CFileDescriptor pipeFd; + std::array xwmFDs; + std::array waylandFDs; friend class CXWM; }; diff --git a/src/xwayland/XDataSource.cpp b/src/xwayland/XDataSource.cpp index 5d34a9d8..003f6c9f 100644 --- a/src/xwayland/XDataSource.cpp +++ b/src/xwayland/XDataSource.cpp @@ -7,45 +7,45 @@ #include using namespace Hyprutils::OS; -CXDataSource::CXDataSource(SXSelection& sel_) : m_selection(sel_) { - xcb_get_property_cookie_t cookie = xcb_get_property(g_pXWayland->m_wm->getConnection(), +CXDataSource::CXDataSource(SXSelection& sel_) : selection(sel_) { + xcb_get_property_cookie_t cookie = xcb_get_property(g_pXWayland->pWM->connection, 1, // delete - m_selection.window, HYPRATOMS["_WL_SELECTION"], XCB_GET_PROPERTY_TYPE_ANY, 0, 4096); + selection.window, HYPRATOMS["_WL_SELECTION"], XCB_GET_PROPERTY_TYPE_ANY, 0, 4096); - xcb_get_property_reply_t* reply = xcb_get_property_reply(g_pXWayland->m_wm->getConnection(), cookie, nullptr); + xcb_get_property_reply_t* reply = xcb_get_property_reply(g_pXWayland->pWM->connection, cookie, nullptr); if (!reply) return; if (reply->type != XCB_ATOM_ATOM) { - free(reply); // NOLINT(cppcoreguidelines-no-malloc) + free(reply); return; } - auto value = sc(xcb_get_property_value(reply)); + auto value = (xcb_atom_t*)xcb_get_property_value(reply); for (uint32_t i = 0; i < reply->value_len; i++) { if (value[i] == HYPRATOMS["UTF8_STRING"]) - m_mimeTypes.emplace_back("text/plain;charset=utf-8"); + mimeTypes.emplace_back("text/plain;charset=utf-8"); else if (value[i] == HYPRATOMS["TEXT"]) - m_mimeTypes.emplace_back("text/plain"); + mimeTypes.emplace_back("text/plain"); else if (value[i] != HYPRATOMS["TARGETS"] && value[i] != HYPRATOMS["TIMESTAMP"]) { - auto type = g_pXWayland->m_wm->mimeFromAtom(value[i]); + auto type = g_pXWayland->pWM->mimeFromAtom(value[i]); if (type == "INVALID") continue; - m_mimeTypes.push_back(type); + mimeTypes.push_back(type); } else continue; - m_mimeAtoms.push_back(value[i]); + mimeAtoms.push_back(value[i]); } - free(reply); // NOLINT(cppcoreguidelines-no-malloc) + free(reply); } std::vector CXDataSource::mimes() { - return m_mimeTypes; + return mimeTypes; } void CXDataSource::send(const std::string& mime, CFileDescriptor fd) { @@ -56,53 +56,47 @@ void CXDataSource::send(const std::string& mime, CFileDescriptor fd) { else if (mime == "text/plain;charset=utf-8") mimeAtom = HYPRATOMS["UTF8_STRING"]; else { - for (size_t i = 0; i < m_mimeTypes.size(); ++i) { - if (m_mimeTypes[i] == mime) { - mimeAtom = m_mimeAtoms[i]; + for (size_t i = 0; i < mimeTypes.size(); ++i) { + if (mimeTypes[i] == mime) { + mimeAtom = mimeAtoms[i]; break; } } } if (!mimeAtom) { - Log::logger->log(Log::ERR, "[XDataSource] mime atom not found"); + Debug::log(ERR, "[XDataSource] mime atom not found"); return; } - Log::logger->log(Log::DEBUG, "[XDataSource] send with mime {} to fd {}", mime, fd.get()); + Debug::log(LOG, "[XDataSource] send with mime {} to fd {}", mime, fd.get()); - auto transfer = makeUnique(m_selection); - transfer->incomingWindow = xcb_generate_id(g_pXWayland->m_wm->getConnection()); + auto transfer = makeUnique(selection); + transfer->incomingWindow = xcb_generate_id(g_pXWayland->pWM->connection); const uint32_t MASK = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; - xcb_create_window(g_pXWayland->m_wm->getConnection(), XCB_COPY_FROM_PARENT, transfer->incomingWindow, g_pXWayland->m_wm->m_screen->root, 0, 0, 10, 10, 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, g_pXWayland->m_wm->m_screen->root_visual, XCB_CW_EVENT_MASK, &MASK); + xcb_create_window(g_pXWayland->pWM->connection, XCB_COPY_FROM_PARENT, transfer->incomingWindow, g_pXWayland->pWM->screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, + g_pXWayland->pWM->screen->root_visual, XCB_CW_EVENT_MASK, &MASK); - xcb_atom_t selection_atom = HYPRATOMS["CLIPBOARD"]; - if (&m_selection == &g_pXWayland->m_wm->m_primarySelection) - selection_atom = HYPRATOMS["PRIMARY"]; - else if (&m_selection == &g_pXWayland->m_wm->m_dndSelection) - selection_atom = HYPRATOMS["XdndSelection"]; + xcb_convert_selection(g_pXWayland->pWM->connection, transfer->incomingWindow, HYPRATOMS["CLIPBOARD"], mimeAtom, HYPRATOMS["_WL_SELECTION"], XCB_TIME_CURRENT_TIME); - xcb_convert_selection(g_pXWayland->m_wm->getConnection(), transfer->incomingWindow, selection_atom, mimeAtom, HYPRATOMS["_WL_SELECTION"], XCB_TIME_CURRENT_TIME); + xcb_flush(g_pXWayland->pWM->connection); - xcb_flush(g_pXWayland->m_wm->getConnection()); - - //TODO: make CFileDescriptor setflags take SETFL as well + //TODO: make CFileDescriptor setflags take SETFL aswell fcntl(fd.get(), F_SETFL, O_WRONLY | O_NONBLOCK); transfer->wlFD = std::move(fd); - m_selection.transfers.emplace_back(std::move(transfer)); + selection.transfers.emplace_back(std::move(transfer)); } void CXDataSource::accepted(const std::string& mime) { - Log::logger->log(Log::DEBUG, "[XDataSource] accepted is a stub"); + Debug::log(LOG, "[XDataSource] accepted is a stub"); } void CXDataSource::cancelled() { - Log::logger->log(Log::DEBUG, "[XDataSource] cancelled is a stub"); + Debug::log(LOG, "[XDataSource] cancelled is a stub"); } void CXDataSource::error(uint32_t code, const std::string& msg) { - Log::logger->log(Log::DEBUG, "[XDataSource] error is a stub: err {}: {}", code, msg); + Debug::log(LOG, "[XDataSource] error is a stub: err {}: {}", code, msg); } eDataSourceType CXDataSource::type() { diff --git a/src/xwayland/XDataSource.hpp b/src/xwayland/XDataSource.hpp index 686991d5..a61ffcc9 100644 --- a/src/xwayland/XDataSource.hpp +++ b/src/xwayland/XDataSource.hpp @@ -17,7 +17,7 @@ class CXDataSource : public IDataSource { virtual eDataSourceType type(); private: - SXSelection& m_selection; - std::vector m_mimeTypes; // these two have shared idx - std::vector m_mimeAtoms; // + SXSelection& selection; + std::vector mimeTypes; // these two have shared idx + std::vector mimeAtoms; // }; diff --git a/src/xwayland/XSurface.cpp b/src/xwayland/XSurface.cpp index 5c5f3b5c..30e38ac2 100644 --- a/src/xwayland/XSurface.cpp +++ b/src/xwayland/XSurface.cpp @@ -3,23 +3,24 @@ #include "../protocols/XWaylandShell.hpp" #include "../protocols/core/Compositor.hpp" #include "../managers/ANRManager.hpp" -#include "../helpers/time/Time.hpp" #ifndef NO_XWAYLAND -CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : m_xID(xID_), m_geometry(geometry_), m_overrideRedirect(OR) { +#include + +CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : xID(xID_), geometry(geometry_), overrideRedirect(OR) { xcb_res_query_client_ids_cookie_t client_id_cookie = {0}; - if (g_pXWayland->m_wm->m_xres) { - xcb_res_client_id_spec_t spec = {.client = m_xID, .mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID}; - client_id_cookie = xcb_res_query_client_ids(g_pXWayland->m_wm->getConnection(), 1, &spec); + if (g_pXWayland->pWM->xres) { + xcb_res_client_id_spec_t spec = {.client = xID, .mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID}; + client_id_cookie = xcb_res_query_client_ids(g_pXWayland->pWM->connection, 1, &spec); } uint32_t values[1]; values[0] = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE; - xcb_change_window_attributes(g_pXWayland->m_wm->getConnection(), m_xID, XCB_CW_EVENT_MASK, values); + xcb_change_window_attributes(g_pXWayland->pWM->connection, xID, XCB_CW_EVENT_MASK, values); - if (g_pXWayland->m_wm->m_xres) { - xcb_res_query_client_ids_reply_t* reply = xcb_res_query_client_ids_reply(g_pXWayland->m_wm->getConnection(), client_id_cookie, nullptr); + if (g_pXWayland->pWM->xres) { + xcb_res_query_client_ids_reply_t* reply = xcb_res_query_client_ids_reply(g_pXWayland->pWM->connection, client_id_cookie, nullptr); if (!reply) return; @@ -33,148 +34,114 @@ CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : m_x xcb_res_client_id_value_next(&iter); } if (!ppid) { - free(reply); // NOLINT(cppcoreguidelines-no-malloc) + free(reply); return; } - m_pid = *ppid; - free(reply); // NOLINT(cppcoreguidelines-no-malloc) + pid = *ppid; + free(reply); } - // FIXME: this is a race, we need to listen to props changed - recheckSupportedProps(); - - m_events.resourceChange.listenStatic([this] { ensureListeners(); }); -} - -void CXWaylandSurface::recheckSupportedProps() { - m_supportedProps.clear(); - - auto listCookie = xcb_list_properties(g_pXWayland->m_wm->getConnection(), m_xID); - auto* listReply = xcb_list_properties_reply(g_pXWayland->m_wm->getConnection(), listCookie, nullptr); - auto getCookie = xcb_get_property(g_pXWayland->m_wm->getConnection(), 0, m_xID, HYPRATOMS["WM_PROTOCOLS"], XCB_ATOM_ATOM, 0, 32); - auto* getReply = xcb_get_property_reply(g_pXWayland->m_wm->getConnection(), getCookie, nullptr); - - if (listReply) { - const auto* atoms = xcb_list_properties_atoms(listReply); - auto len = xcb_list_properties_atoms_length(listReply); - - for (auto i = 0; i < len; ++i) { - m_supportedProps[atoms[i]] = true; - } - - free(listReply); - } - - if (getReply) { - const auto* protocols = sc(xcb_get_property_value(getReply)); - const auto len = xcb_get_property_value_length(getReply) / sizeof(xcb_atom_t); - - for (auto i = 0u; i < len; ++i) { - m_supportedProps[protocols[i]] = true; - } - - free(getReply); - } + events.resourceChange.registerStaticListener([this](void* data, std::any d) { ensureListeners(); }, nullptr); } void CXWaylandSurface::ensureListeners() { - bool connected = m_listeners.destroySurface; + bool connected = listeners.destroySurface; - if (connected && !m_surface) { - m_listeners.destroySurface.reset(); - m_listeners.commitSurface.reset(); - } else if (!connected && m_surface) { - m_listeners.destroySurface = m_surface->m_events.destroy.listen([this] { - if (m_mapped) + if (connected && !surface) { + listeners.destroySurface.reset(); + listeners.commitSurface.reset(); + } else if (!connected && surface) { + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { + if (mapped) unmap(); - m_surface.reset(); - m_listeners.destroySurface.reset(); - m_listeners.commitSurface.reset(); - m_events.resourceChange.emit(); + surface.reset(); + listeners.destroySurface.reset(); + listeners.commitSurface.reset(); + events.resourceChange.emit(); }); - m_listeners.commitSurface = m_surface->m_events.commit.listen([this] { - if (m_surface->m_current.texture && !m_mapped) { + listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { + if (surface->current.texture && !mapped) { map(); return; } - if (!m_surface->m_current.texture && m_mapped) { + if (!surface->current.texture && mapped) { unmap(); return; } - m_events.commit.emit(); + events.commit.emit(); }); } - if (m_resource) { - m_listeners.destroyResource = m_resource->events.destroy.listen([this] { + if (resource) { + listeners.destroyResource = resource->events.destroy.registerListener([this](std::any d) { unmap(); - m_surface.reset(); - m_events.resourceChange.emit(); + surface.reset(); + events.resourceChange.emit(); }); } } void CXWaylandSurface::map() { - if (m_mapped) + if (mapped) return; - ASSERT(m_surface); + ASSERT(surface); - g_pXWayland->m_wm->m_mappedSurfaces.emplace_back(m_self); - g_pXWayland->m_wm->m_mappedSurfacesStacking.emplace_back(m_self); + g_pXWayland->pWM->mappedSurfaces.emplace_back(self); + g_pXWayland->pWM->mappedSurfacesStacking.emplace_back(self); - m_mapped = true; - m_surface->map(); + mapped = true; + surface->map(); - Log::logger->log(Log::DEBUG, "XWayland surface {:x} mapping", rc(this)); + Debug::log(LOG, "XWayland surface {:x} mapping", (uintptr_t)this); - m_events.map.emit(); + events.map.emit(); - g_pXWayland->m_wm->updateClientList(); + g_pXWayland->pWM->updateClientList(); } void CXWaylandSurface::unmap() { - if (!m_mapped) + if (!mapped) return; - ASSERT(m_surface); + ASSERT(surface); - std::erase(g_pXWayland->m_wm->m_mappedSurfaces, m_self); - std::erase(g_pXWayland->m_wm->m_mappedSurfacesStacking, m_self); + std::erase(g_pXWayland->pWM->mappedSurfaces, self); + std::erase(g_pXWayland->pWM->mappedSurfacesStacking, self); - m_mapped = false; - m_events.unmap.emit(); - m_surface->unmap(); + mapped = false; + events.unmap.emit(); + surface->unmap(); - Log::logger->log(Log::DEBUG, "XWayland surface {:x} unmapping", rc(this)); + Debug::log(LOG, "XWayland surface {:x} unmapping", (uintptr_t)this); - g_pXWayland->m_wm->updateClientList(); + g_pXWayland->pWM->updateClientList(); } void CXWaylandSurface::considerMap() { - if (m_mapped) + if (mapped) return; - if (!m_surface) { - Log::logger->log(Log::DEBUG, "XWayland surface: considerMap, nope, no surface"); + if (!surface) { + Debug::log(LOG, "XWayland surface: considerMap, nope, no surface"); return; } - if (m_surface->m_current.texture) { - Log::logger->log(Log::DEBUG, "XWayland surface: considerMap, sure, we have a buffer"); + if (surface->current.texture) { + Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer"); map(); return; } - Log::logger->log(Log::DEBUG, "XWayland surface: considerMap, nope, we don't have a buffer"); + Debug::log(LOG, "XWayland surface: considerMap, nope, we don't have a buffer"); } bool CXWaylandSurface::wantsFocus() { - if (m_atoms.empty()) + if (atoms.empty()) return true; const std::array search = { @@ -185,7 +152,7 @@ bool CXWaylandSurface::wantsFocus() { }; for (auto const& searched : search) { - for (auto const& a : m_atoms) { + for (auto const& a : atoms) { if (a == searched) return false; } @@ -195,120 +162,113 @@ bool CXWaylandSurface::wantsFocus() { } void CXWaylandSurface::configure(const CBox& box) { - Vector2D oldSize = m_geometry.size(); + Vector2D oldSize = geometry.size(); - m_geometry = box; + geometry = box; uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH; uint32_t values[] = {box.x, box.y, box.width, box.height, 0}; - xcb_configure_window(g_pXWayland->m_wm->getConnection(), m_xID, mask, values); + xcb_configure_window(g_pXWayland->pWM->connection, xID, mask, values); - if (m_geometry.width == box.width && m_geometry.height == box.height) { + if (geometry.width == box.width && geometry.height == box.height) { // ICCCM requires a synthetic event when window size is not changed xcb_configure_notify_event_t e; e.response_type = XCB_CONFIGURE_NOTIFY; - e.event = m_xID; - e.window = m_xID; + e.event = xID; + e.window = xID; e.x = box.x; e.y = box.y; e.width = box.width; e.height = box.height; e.border_width = 0; e.above_sibling = XCB_NONE; - e.override_redirect = m_overrideRedirect; - xcb_send_event(g_pXWayland->m_wm->getConnection(), false, m_xID, XCB_EVENT_MASK_STRUCTURE_NOTIFY, rc(&e)); + e.override_redirect = overrideRedirect; + xcb_send_event(g_pXWayland->pWM->connection, false, xID, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char*)&e); } - g_pXWayland->m_wm->updateClientList(); + g_pXWayland->pWM->updateClientList(); - xcb_flush(g_pXWayland->m_wm->getConnection()); + xcb_flush(g_pXWayland->pWM->connection); } void CXWaylandSurface::activate(bool activate) { - if (m_overrideRedirect && !activate) + if (overrideRedirect && !activate) return; - setWithdrawn(false); - g_pXWayland->m_wm->activateSurface(m_self.lock(), activate); + g_pXWayland->pWM->activateSurface(self.lock(), activate); } void CXWaylandSurface::setFullscreen(bool fs) { - m_fullscreen = fs; - g_pXWayland->m_wm->sendState(m_self.lock()); + fullscreen = fs; + g_pXWayland->pWM->sendState(self.lock()); } void CXWaylandSurface::setMinimized(bool mz) { - m_minimized = mz; - g_pXWayland->m_wm->sendState(m_self.lock()); + minimized = mz; + g_pXWayland->pWM->sendState(self.lock()); } void CXWaylandSurface::restackToTop() { uint32_t values[1] = {XCB_STACK_MODE_ABOVE}; - xcb_configure_window(g_pXWayland->m_wm->getConnection(), m_xID, XCB_CONFIG_WINDOW_STACK_MODE, values); + xcb_configure_window(g_pXWayland->pWM->connection, xID, XCB_CONFIG_WINDOW_STACK_MODE, values); - auto& stack = g_pXWayland->m_wm->m_mappedSurfacesStacking; - auto it = std::ranges::find(stack, m_self); + auto& stack = g_pXWayland->pWM->mappedSurfacesStacking; + auto it = std::find(stack.begin(), stack.end(), self); if (it != stack.end()) std::rotate(it, it + 1, stack.end()); - g_pXWayland->m_wm->updateClientList(); + g_pXWayland->pWM->updateClientList(); - xcb_flush(g_pXWayland->m_wm->getConnection()); + xcb_flush(g_pXWayland->pWM->connection); } void CXWaylandSurface::close() { - - // Recheck the supported props, check if we maybe have WM_DELETE_WINDOW. - recheckSupportedProps(); - - if (m_supportedProps[HYPRATOMS["WM_DELETE_WINDOW"]]) { - xcb_client_message_data_t msg = {}; - msg.data32[0] = HYPRATOMS["WM_DELETE_WINDOW"]; - msg.data32[1] = XCB_CURRENT_TIME; - g_pXWayland->m_wm->sendWMMessage(m_self.lock(), &msg, XCB_EVENT_MASK_NO_EVENT); - } else { - xcb_kill_client(g_pXWayland->m_wm->getConnection(), m_self->m_xID); - xcb_flush(g_pXWayland->m_wm->getConnection()); - } + xcb_client_message_data_t msg = {}; + msg.data32[0] = HYPRATOMS["WM_DELETE_WINDOW"]; + msg.data32[1] = XCB_CURRENT_TIME; + g_pXWayland->pWM->sendWMMessage(self.lock(), &msg, XCB_EVENT_MASK_NO_EVENT); } void CXWaylandSurface::setWithdrawn(bool withdrawn_) { - m_withdrawn = withdrawn_; + withdrawn = withdrawn_; std::vector props = {XCB_ICCCM_WM_STATE_NORMAL, XCB_WINDOW_NONE}; - if (m_withdrawn) + if (withdrawn) props[0] = XCB_ICCCM_WM_STATE_WITHDRAWN; - else if (m_minimized) + else if (minimized) props[0] = XCB_ICCCM_WM_STATE_ICONIC; else props[0] = XCB_ICCCM_WM_STATE_NORMAL; - xcb_change_property(g_pXWayland->m_wm->getConnection(), XCB_PROP_MODE_REPLACE, m_xID, HYPRATOMS["WM_STATE"], HYPRATOMS["WM_STATE"], 32, props.size(), props.data()); + xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, xID, HYPRATOMS["WM_STATE"], HYPRATOMS["WM_STATE"], 32, props.size(), props.data()); } void CXWaylandSurface::ping() { - bool supportsPing = std::ranges::find(m_protocols, HYPRATOMS["_NET_WM_PING"]) != m_protocols.end(); + bool supportsPing = std::ranges::find(protocols, HYPRATOMS["_NET_WM_PING"]) != protocols.end(); if (!supportsPing) { - Log::logger->log(Log::TRACE, "CXWaylandSurface: XID {} does not support ping, just sending an instant reply", m_xID); - g_pANRManager->onResponse(m_self.lock()); + Debug::log(TRACE, "CXWaylandSurface: XID {} does not support ping, just sending an instant reply", xID); + g_pANRManager->onResponse(self.lock()); return; } + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + xcb_client_message_data_t msg = {}; msg.data32[0] = HYPRATOMS["_NET_WM_PING"]; - msg.data32[1] = Time::millis(Time::steadyNow()); - msg.data32[2] = m_xID; + msg.data32[1] = now.tv_sec * 1000 + now.tv_nsec / 1000000; + msg.data32[2] = xID; - m_lastPingSeq = msg.data32[1]; + lastPingSeq = msg.data32[1]; - g_pXWayland->m_wm->sendWMMessage(m_self.lock(), &msg, XCB_EVENT_MASK_PROPERTY_CHANGE); + g_pXWayland->pWM->sendWMMessage(self.lock(), &msg, XCB_EVENT_MASK_PROPERTY_CHANGE); } #else -CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : m_xID(xID_), m_geometry(geometry_), m_overrideRedirect(OR) { +CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : xID(xID_), geometry(geometry_), overrideRedirect(OR) { ; } diff --git a/src/xwayland/XSurface.hpp b/src/xwayland/XSurface.hpp index a8ccac4d..53777c33 100644 --- a/src/xwayland/XSurface.hpp +++ b/src/xwayland/XSurface.hpp @@ -9,10 +9,9 @@ class CWLSurfaceResource; class CXWaylandSurfaceResource; #ifdef NO_XWAYLAND -using xcb_pixmap_t = uint32_t; -using xcb_window_t = uint32_t; -using xcb_atom_t = uint32_t; -using xcb_icccm_wm_hints_t = struct { +typedef uint32_t xcb_pixmap_t; +typedef uint32_t xcb_window_t; +typedef struct { int32_t flags; uint32_t input; int32_t initial_state; @@ -21,8 +20,8 @@ using xcb_icccm_wm_hints_t = struct { int32_t icon_x, icon_y; xcb_pixmap_t icon_mask; xcb_window_t window_group; -}; -using xcb_size_hints_t = struct { +} xcb_icccm_wm_hints_t; +typedef struct { uint32_t flags; int32_t x, y; int32_t width, height; @@ -33,32 +32,32 @@ using xcb_size_hints_t = struct { int32_t max_aspect_num, max_aspect_den; int32_t base_width, base_height; uint32_t win_gravity; -}; +} xcb_size_hints_t; #else #include #endif class CXWaylandSurface { public: - WP m_surface; - WP m_resource; + WP surface; + WP resource; struct { - CSignalT<> stateChanged; // maximized, fs, minimized, etc. - CSignalT<> metadataChanged; // title, appid - CSignalT<> destroy; + CSignal stateChanged; // maximized, fs, minimized, etc. + CSignal metadataChanged; // title, appid + CSignal destroy; - CSignalT<> resourceChange; // associated / dissociated + CSignal resourceChange; // associated / dissociated - CSignalT<> setGeometry; - CSignalT configureRequest; + CSignal setGeometry; + CSignal configureRequest; // CBox - CSignalT<> map; - CSignalT<> unmap; - CSignalT<> commit; + CSignal map; + CSignal unmap; + CSignal commit; - CSignalT<> activate; - } m_events; + CSignal activate; + } events; struct { std::string title; @@ -68,32 +67,31 @@ class CXWaylandSurface { std::optional requestsMaximize; std::optional requestsFullscreen; std::optional requestsMinimize; - } m_state; + } state; - uint32_t m_xID = 0; - uint64_t m_wlID = 0; - uint64_t m_wlSerial = 0; - uint32_t m_lastPingSeq = 0; - pid_t m_pid = 0; - CBox m_geometry; - bool m_overrideRedirect = false; - bool m_withdrawn = false; - bool m_fullscreen = false; - bool m_maximized = false; - bool m_minimized = false; - bool m_mapped = false; - bool m_modal = false; + uint32_t xID = 0; + uint64_t wlID = 0; + uint64_t wlSerial = 0; + uint32_t lastPingSeq = 0; + pid_t pid = 0; + CBox geometry; + bool overrideRedirect = false; + bool withdrawn = false; + bool fullscreen = false; + bool maximized = false; + bool minimized = false; + bool mapped = false; + bool modal = false; - WP m_parent; - WP m_self; - std::vector> m_children; + WP parent; + WP self; + std::vector> children; - UP m_hints; - UP m_sizeHints; - std::vector m_atoms; - std::vector m_protocols; - std::string m_role = ""; - bool m_transient = false; + UP hints; + UP sizeHints; + std::vector atoms, protocols; + std::string role = ""; + bool transient = false; bool wantsFocus(); void configure(const CBox& box); @@ -112,15 +110,12 @@ class CXWaylandSurface { void unmap(); void considerMap(); void setWithdrawn(bool withdrawn); - void recheckSupportedProps(); struct { CHyprSignalListener destroyResource; CHyprSignalListener destroySurface; CHyprSignalListener commitSurface; - } m_listeners; - - std::unordered_map m_supportedProps; + } listeners; friend class CXWM; }; diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp index 7e20d9b1..efb080d6 100644 --- a/src/xwayland/XWM.cpp +++ b/src/xwayland/XWM.cpp @@ -1,11 +1,8 @@ -#include +#include "helpers/math/Math.hpp" #include -#include -#include -#include -#include #ifndef NO_XWAYLAND +#include #include #include #include @@ -13,40 +10,26 @@ #include #include "XWayland.hpp" +#include "../defines.hpp" #include "../Compositor.hpp" #include "../protocols/core/Seat.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" #include "../managers/SeatManager.hpp" #include "../managers/ANRManager.hpp" -#include "../helpers/env/Env.hpp" #include "../protocols/XWaylandShell.hpp" #include "../protocols/core/Compositor.hpp" -#include "../desktop/state/FocusState.hpp" -using Hyprutils::Memory::CUniquePointer; - using namespace Hyprutils::OS; #define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f -constexpr size_t INCR_CHUNK_SIZE = 64ul * 1024; +#define INCR_CHUNK_SIZE (64 * 1024) -static int onX11Event(int fd, uint32_t mask, void* data) { - return g_pXWayland->m_wm->onEvent(fd, mask); +static int onX11Event(int fd, uint32_t mask, void* data) { + return g_pXWayland->pWM->onEvent(fd, mask); } -static int writeDataSource(int fd, uint32_t mask, void* data); - -struct SFreeDeleter { - void operator()(void* ptr) const { - std::free(ptr); // NOLINT(cppcoreguidelines-no-malloc) - } -}; - -template -using XCBReplyPtr = std::unique_ptr; - SP CXWM::windowForXID(xcb_window_t wid) { - for (auto const& s : m_surfaces) { - if (s->m_xID == wid) + for (auto const& s : surfaces) { + if (s->xID == wid) return s; } @@ -57,26 +40,24 @@ void CXWM::handleCreate(xcb_create_notify_event_t* e) { if (isWMWindow(e->window)) return; - const auto XSURF = m_surfaces.emplace_back(SP(new CXWaylandSurface(e->window, CBox{e->x, e->y, e->width, e->height}, e->override_redirect))); - XSURF->m_self = XSURF; - Log::logger->log(Log::DEBUG, "[xwm] New XSurface at {:x} with xid of {}", rc(XSURF.get()), e->window); + const auto XSURF = surfaces.emplace_back(SP(new CXWaylandSurface(e->window, CBox{e->x, e->y, e->width, e->height}, e->override_redirect))); + XSURF->self = XSURF; + Debug::log(LOG, "[xwm] New XSurface at {:x} with xid of {}", (uintptr_t)XSURF.get(), e->window); - const auto WINDOW = Desktop::View::CWindow::create(XSURF); - g_pCompositor->m_windows.emplace_back(WINDOW); - WINDOW->m_self = WINDOW; - Log::logger->log(Log::DEBUG, "[xwm] New XWayland window at {:x} for surf {:x}", rc(WINDOW.get()), rc(XSURF.get())); + const auto WINDOW = CWindow::create(XSURF); + g_pCompositor->m_vWindows.emplace_back(WINDOW); + WINDOW->m_pSelf = WINDOW; + Debug::log(LOG, "[xwm] New XWayland window at {:x} for surf {:x}", (uintptr_t)WINDOW.get(), (uintptr_t)XSURF.get()); } void CXWM::handleDestroy(xcb_destroy_notify_event_t* e) { - removeTransfersForWindow(e->window); - const auto XSURF = windowForXID(e->window); if (!XSURF) return; - XSURF->m_events.destroy.emit(); - std::erase_if(m_surfaces, [XSURF](const auto& other) { return XSURF == other; }); + XSURF->events.destroy.emit(); + std::erase_if(surfaces, [XSURF](const auto& other) { return XSURF == other; }); } void CXWM::handleConfigureRequest(xcb_configure_request_event_t* e) { @@ -90,9 +71,9 @@ void CXWM::handleConfigureRequest(xcb_configure_request_event_t* e) { if (!(MASK & GEOMETRY)) return; - XSURF->m_events.configureRequest.emit(CBox{MASK & XCB_CONFIG_WINDOW_X ? e->x : XSURF->m_geometry.x, MASK & XCB_CONFIG_WINDOW_Y ? e->y : XSURF->m_geometry.y, - MASK & XCB_CONFIG_WINDOW_WIDTH ? e->width : XSURF->m_geometry.width, - MASK & XCB_CONFIG_WINDOW_HEIGHT ? e->height : XSURF->m_geometry.height}); + XSURF->events.configureRequest.emit(CBox{MASK & XCB_CONFIG_WINDOW_X ? e->x : XSURF->geometry.x, MASK & XCB_CONFIG_WINDOW_Y ? e->y : XSURF->geometry.y, + MASK & XCB_CONFIG_WINDOW_WIDTH ? e->width : XSURF->geometry.width, + MASK & XCB_CONFIG_WINDOW_HEIGHT ? e->height : XSURF->geometry.height}); } void CXWM::handleConfigureNotify(xcb_configure_notify_event_t* e) { @@ -101,12 +82,12 @@ void CXWM::handleConfigureNotify(xcb_configure_notify_event_t* e) { if (!XSURF) return; - if (XSURF->m_geometry == CBox{e->x, e->y, e->width, e->height}) + if (XSURF->geometry == CBox{e->x, e->y, e->width, e->height}) return; - XSURF->m_geometry = {e->x, e->y, e->width, e->height}; + XSURF->geometry = {e->x, e->y, e->width, e->height}; updateOverrideRedirect(XSURF, e->override_redirect); - XSURF->m_events.setGeometry.emit(); + XSURF->events.setGeometry.emit(); } void CXWM::handleMapRequest(xcb_map_request_event_t* e) { @@ -115,21 +96,20 @@ void CXWM::handleMapRequest(xcb_map_request_event_t* e) { if (!XSURF) return; - xcb_map_window(getConnection(), e->window); + xcb_map_window(connection, e->window); XSURF->restackToTop(); const bool SMALL = - XSURF->m_geometry.size() < Vector2D{2, 2} || (XSURF->m_sizeHints && XSURF->m_geometry.size() < Vector2D{XSURF->m_sizeHints->min_width, XSURF->m_sizeHints->min_height}); - const bool HAS_HINTS = XSURF->m_sizeHints && Vector2D{XSURF->m_sizeHints->base_width, XSURF->m_sizeHints->base_height} > Vector2D{5, 5}; - const auto DESIREDSIZE = HAS_HINTS ? Vector2D{XSURF->m_sizeHints->base_width, XSURF->m_sizeHints->base_height} : Vector2D{800, 800}; + XSURF->geometry.size() < Vector2D{2, 2} || (XSURF->sizeHints && XSURF->geometry.size() < Vector2D{XSURF->sizeHints->min_width, XSURF->sizeHints->min_height}); + const bool HAS_HINTS = XSURF->sizeHints && Vector2D{XSURF->sizeHints->base_width, XSURF->sizeHints->base_height} > Vector2D{5, 5}; + const auto DESIREDSIZE = HAS_HINTS ? Vector2D{XSURF->sizeHints->base_width, XSURF->sizeHints->base_height} : Vector2D{800, 800}; // if it's too small, configure it. - if (SMALL && !XSURF->m_overrideRedirect) // default to 800 x 800 - XSURF->configure({XSURF->m_geometry.pos(), DESIREDSIZE}); + if (SMALL && !XSURF->overrideRedirect) // default to 800 x 800 + XSURF->configure({XSURF->geometry.pos(), DESIREDSIZE}); - Log::logger->log(Log::DEBUG, "[xwm] Mapping window {} in X (geometry {}x{} at {}x{}))", e->window, XSURF->m_geometry.width, XSURF->m_geometry.height, XSURF->m_geometry.x, - XSURF->m_geometry.y); + Debug::log(LOG, "[xwm] Mapping window {} in X (geometry {}x{} at {}x{}))", e->window, XSURF->geometry.width, XSURF->geometry.height, XSURF->geometry.x, XSURF->geometry.y); // read data again. Some apps for some reason fail to send WINDOW_TYPE // this shouldn't happen but does, I prolly fucked up somewhere, this is a band-aid @@ -146,12 +126,12 @@ void CXWM::handleMapNotify(xcb_map_notify_event_t* e) { updateOverrideRedirect(XSURF, e->override_redirect); - if (XSURF->m_overrideRedirect) + if (XSURF->overrideRedirect) return; XSURF->setWithdrawn(false); sendState(XSURF); - xcb_flush(getConnection()); + xcb_flush(connection); XSURF->considerMap(); } @@ -165,25 +145,25 @@ void CXWM::handleUnmapNotify(xcb_unmap_notify_event_t* e) { XSURF->unmap(); dissociate(XSURF); - if (XSURF->m_overrideRedirect) + if (XSURF->overrideRedirect) return; XSURF->setWithdrawn(true); sendState(XSURF); - xcb_flush(getConnection()); + xcb_flush(connection); } static bool lookupParentExists(SP XSURF, SP prospectiveChild) { std::vector> visited; - while (XSURF->m_parent) { - if (XSURF->m_parent == prospectiveChild) + while (XSURF->parent) { + if (XSURF->parent == prospectiveChild) return true; visited.emplace_back(XSURF); - XSURF = XSURF->m_parent.lock(); + XSURF = XSURF->parent.lock(); - if (std::ranges::find(visited, XSURF) != visited.end()) + if (std::find(visited.begin(), visited.end(), XSURF) != visited.end()) return false; } @@ -199,167 +179,145 @@ std::string CXWM::getAtomName(uint32_t atom) { } // Get the name of the atom - const auto cookie = xcb_get_atom_name(getConnection(), atom); - XCBReplyPtr reply(xcb_get_atom_name_reply(getConnection(), cookie, nullptr)); + auto const atom_name_cookie = xcb_get_atom_name(connection, atom); + auto* atom_name_reply = xcb_get_atom_name_reply(connection, atom_name_cookie, nullptr); - if (!reply) + if (!atom_name_reply) return "Unknown"; - auto const name_len = xcb_get_atom_name_name_length(reply.get()); - auto* name = xcb_get_atom_name_name(reply.get()); + auto const name_len = xcb_get_atom_name_name_length(atom_name_reply); + auto* name = xcb_get_atom_name_name(atom_name_reply); + free(atom_name_reply); return {name, name_len}; } void CXWM::readProp(SP XSURF, uint32_t atom, xcb_get_property_reply_t* reply) { std::string propName; - if (Env::isTrace()) + if (Debug::trace) propName = getAtomName(atom); - const auto valueLen = xcb_get_property_value_length(reply); - const auto* value = sc(xcb_get_property_value(reply)); - - auto handleWMClass = [&]() { - XSURF->m_state.appid = std::string{value, valueLen}; - if (std::count(XSURF->m_state.appid.begin(), XSURF->m_state.appid.end(), '\000') == 2) - XSURF->m_state.appid = XSURF->m_state.appid.substr(XSURF->m_state.appid.find_first_of('\000') + 1); - - if (!XSURF->m_state.appid.empty()) - XSURF->m_state.appid.pop_back(); - XSURF->m_events.metadataChanged.emit(); - }; - - auto handleWMName = [&]() { + if (atom == XCB_ATOM_WM_CLASS) { + size_t len = xcb_get_property_value_length(reply); + char* string = (char*)xcb_get_property_value(reply); + XSURF->state.appid = std::string{string, len}; + if (std::count(XSURF->state.appid.begin(), XSURF->state.appid.end(), '\000') == 2) + XSURF->state.appid = XSURF->state.appid.substr(XSURF->state.appid.find_first_of('\000') + 1); // fuck you X + if (!XSURF->state.appid.empty()) + XSURF->state.appid.pop_back(); + XSURF->events.metadataChanged.emit(); + } else if (atom == XCB_ATOM_WM_NAME || atom == HYPRATOMS["_NET_WM_NAME"]) { + size_t len = xcb_get_property_value_length(reply); + char* string = (char*)xcb_get_property_value(reply); if (reply->type != HYPRATOMS["UTF8_STRING"] && reply->type != HYPRATOMS["TEXT"] && reply->type != XCB_ATOM_STRING) return; - XSURF->m_state.title = std::string{value, valueLen}; - XSURF->m_events.metadataChanged.emit(); - }; - - auto handleWindowType = [&]() { - auto* atomsArr = rc(value); - XSURF->m_atoms.assign(atomsArr, atomsArr + reply->value_len); - }; - - auto handleWMState = [&]() { - auto* atoms = rc(value); + XSURF->state.title = std::string{string, len}; + XSURF->events.metadataChanged.emit(); + } else if (atom == HYPRATOMS["_NET_WM_WINDOW_TYPE"]) { + xcb_atom_t* atomsArr = (xcb_atom_t*)xcb_get_property_value(reply); + size_t atomsNo = reply->value_len; + XSURF->atoms.clear(); + for (size_t i = 0; i < atomsNo; ++i) { + XSURF->atoms.push_back(atomsArr[i]); + } + } else if (atom == HYPRATOMS["_NET_WM_STATE"]) { + xcb_atom_t* atoms = (xcb_atom_t*)xcb_get_property_value(reply); for (uint32_t i = 0; i < reply->value_len; i++) { if (atoms[i] == HYPRATOMS["_NET_WM_STATE_MODAL"]) - XSURF->m_modal = true; + XSURF->modal = true; } - }; + } else if (atom == HYPRATOMS["WM_HINTS"]) { + if (reply->value_len != 0) { + XSURF->hints = makeUnique(); + xcb_icccm_get_wm_hints_from_reply(XSURF->hints.get(), reply); - auto handleWMHints = [&]() { - if (reply->value_len == 0) - return; - XSURF->m_hints = makeUnique(); - xcb_icccm_get_wm_hints_from_reply(XSURF->m_hints.get(), reply); - if (!(XSURF->m_hints->flags & XCB_ICCCM_WM_HINT_INPUT)) - XSURF->m_hints->input = true; - }; + if (!(XSURF->hints->flags & XCB_ICCCM_WM_HINT_INPUT)) + XSURF->hints->input = true; + } + } else if (atom == HYPRATOMS["WM_WINDOW_ROLE"]) { + size_t len = xcb_get_property_value_length(reply); - auto handleWMRole = [&]() { - if (valueLen <= 0) - XSURF->m_role = ""; + if (len <= 0) + XSURF->role = ""; else { - XSURF->m_role = std::string{value, valueLen}; - XSURF->m_role = XSURF->m_role.substr(0, XSURF->m_role.find_first_of('\000')); + XSURF->role = std::string{(char*)xcb_get_property_value(reply), len}; + XSURF->role = XSURF->role.substr(0, XSURF->role.find_first_of('\000')); } - }; - - auto handleTransientFor = [&]() { - if (reply->type != XCB_ATOM_WINDOW) - return; - const auto XID = rc(value); - XSURF->m_transient = XID; - if (!XID) - return; - - if (const auto NEWXSURF = windowForXID(*XID); NEWXSURF && !lookupParentExists(XSURF, NEWXSURF)) { - XSURF->m_parent = NEWXSURF; - NEWXSURF->m_children.emplace_back(XSURF); - } else - Log::logger->log(Log::DEBUG, "[xwm] Denying transient because it would create a loop"); - }; - - auto handleSizeHints = [&]() { - if (reply->type != HYPRATOMS["WM_SIZE_HINTS"] || reply->value_len == 0) - return; - - XSURF->m_sizeHints = makeUnique(); - std::memset(XSURF->m_sizeHints.get(), 0, sizeof(xcb_size_hints_t)); - xcb_icccm_get_wm_size_hints_from_reply(XSURF->m_sizeHints.get(), reply); - - const int32_t FLAGS = XSURF->m_sizeHints->flags; - const bool HASMIN = FLAGS & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE; - const bool HASBASE = FLAGS & XCB_ICCCM_SIZE_HINT_BASE_SIZE; - - if (!HASMIN && !HASBASE) { - XSURF->m_sizeHints->min_width = XSURF->m_sizeHints->min_height = -1; - XSURF->m_sizeHints->base_width = XSURF->m_sizeHints->base_height = -1; - } else if (!HASBASE) { - XSURF->m_sizeHints->base_width = XSURF->m_sizeHints->min_width; - XSURF->m_sizeHints->base_height = XSURF->m_sizeHints->min_height; - } else if (!HASMIN) { - XSURF->m_sizeHints->min_width = XSURF->m_sizeHints->base_width; - XSURF->m_sizeHints->min_height = XSURF->m_sizeHints->base_height; + } else if (atom == XCB_ATOM_WM_TRANSIENT_FOR) { + if (reply->type == XCB_ATOM_WINDOW) { + const auto XID = (xcb_window_t*)xcb_get_property_value(reply); + XSURF->transient = XID; + if (XID) { + if (const auto NEWXSURF = windowForXID(*XID); NEWXSURF && !lookupParentExists(XSURF, NEWXSURF)) { + XSURF->parent = NEWXSURF; + NEWXSURF->children.emplace_back(XSURF); + } else + Debug::log(LOG, "[xwm] Denying transient because it would create a loop"); + } } + } else if (atom == HYPRATOMS["WM_NORMAL_HINTS"]) { + if (reply->type == HYPRATOMS["WM_SIZE_HINTS"] && reply->value_len > 0) { + XSURF->sizeHints = makeUnique(); + std::memset(XSURF->sizeHints.get(), 0, sizeof(xcb_size_hints_t)); - if (!(FLAGS & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) - XSURF->m_sizeHints->max_width = XSURF->m_sizeHints->max_height = -1; - }; + xcb_icccm_get_wm_size_hints_from_reply(XSURF->sizeHints.get(), reply); - auto handleWMProtocols = [&]() { - if (reply->type != XCB_ATOM_ATOM) - return; - auto* atoms = rc(value); - XSURF->m_protocols.assign(atoms, atoms + reply->value_len); - }; + const int32_t FLAGS = XSURF->sizeHints->flags; + const bool HASMIN = (FLAGS & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE); + const bool HASBASE = (FLAGS & XCB_ICCCM_SIZE_HINT_BASE_SIZE); - if (atom == XCB_ATOM_WM_CLASS) - handleWMClass(); - else if (atom == XCB_ATOM_WM_NAME || atom == HYPRATOMS["_NET_WM_NAME"]) - handleWMName(); - else if (atom == HYPRATOMS["_NET_WM_WINDOW_TYPE"]) - handleWindowType(); - else if (atom == HYPRATOMS["_NET_WM_STATE"]) - handleWMState(); - else if (atom == HYPRATOMS["WM_HINTS"]) - handleWMHints(); - else if (atom == HYPRATOMS["WM_WINDOW_ROLE"]) - handleWMRole(); - else if (atom == XCB_ATOM_WM_TRANSIENT_FOR) - handleTransientFor(); - else if (atom == HYPRATOMS["WM_NORMAL_HINTS"]) - handleSizeHints(); - else if (atom == HYPRATOMS["WM_PROTOCOLS"]) - handleWMProtocols(); - else { - Log::logger->log(Log::TRACE, "[xwm] Unhandled prop {} -> {}", atom, propName); + if (!HASMIN && !HASBASE) { + XSURF->sizeHints->min_width = -1; + XSURF->sizeHints->min_height = -1; + XSURF->sizeHints->base_width = -1; + XSURF->sizeHints->base_height = -1; + } else if (!HASBASE) { + XSURF->sizeHints->base_width = XSURF->sizeHints->min_width; + XSURF->sizeHints->base_height = XSURF->sizeHints->min_height; + } else if (!HASMIN) { + XSURF->sizeHints->min_width = XSURF->sizeHints->base_width; + XSURF->sizeHints->min_height = XSURF->sizeHints->base_height; + } + + if (!(FLAGS & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) { + XSURF->sizeHints->max_width = -1; + XSURF->sizeHints->max_height = -1; + } + } + } else if (atom == HYPRATOMS["WM_PROTOCOLS"]) { + if (reply->type == XCB_ATOM_ATOM) { + auto atoms = (xcb_atom_t*)xcb_get_property_value(reply); + std::vector vec; + vec.reserve(reply->value_len); + for (size_t i = 0; i < reply->value_len; ++i) { + vec.emplace_back(atoms[i]); + } + XSURF->protocols = vec; + } + } else { + Debug::log(TRACE, "[xwm] Unhandled prop {} -> {}", atom, propName); return; } - Log::logger->log(Log::TRACE, "[xwm] Handled prop {} -> {}", atom, propName); + Debug::log(TRACE, "[xwm] Handled prop {} -> {}", atom, propName); } void CXWM::handlePropertyNotify(xcb_property_notify_event_t* e) { const auto XSURF = windowForXID(e->window); - if (!XSURF) { - removeTransfersForWindow(e->window); + if (!XSURF) return; - } - - xcb_get_property_cookie_t cookie = xcb_get_property(getConnection(), 0, XSURF->m_xID, e->atom, XCB_ATOM_ANY, 0, 2048); - XCBReplyPtr reply(xcb_get_property_reply(getConnection(), cookie, nullptr)); + xcb_get_property_cookie_t cookie = xcb_get_property(connection, 0, XSURF->xID, e->atom, XCB_ATOM_ANY, 0, 2048); + xcb_get_property_reply_t* reply = xcb_get_property_reply(connection, cookie, nullptr); if (!reply) { - Log::logger->log(Log::ERR, "[xwm] Failed to read property notify cookie for window {}", e->window); - removeTransfersForWindow(e->window); + Debug::log(ERR, "[xwm] Failed to read property notify cookie"); return; } - readProp(XSURF, e->atom, reply.get()); + readProp(XSURF, e->atom, reply); + + free(reply); } void CXWM::handleClientMessage(xcb_client_message_event_t* e) { @@ -371,42 +329,42 @@ void CXWM::handleClientMessage(xcb_client_message_event_t* e) { std::string propName = getAtomName(e->type); if (e->type == HYPRATOMS["WM_PROTOCOLS"]) { - if (e->data.data32[1] == XSURF->m_lastPingSeq && e->data.data32[0] == HYPRATOMS["_NET_WM_PING"]) { + if (e->data.data32[1] == XSURF->lastPingSeq && e->data.data32[0] == HYPRATOMS["_NET_WM_PING"]) { g_pANRManager->onResponse(XSURF); return; } } else if (e->type == HYPRATOMS["WL_SURFACE_ID"]) { - if (XSURF->m_surface) { - Log::logger->log(Log::WARN, "[xwm] Re-assignment of WL_SURFACE_ID"); + if (XSURF->surface) { + Debug::log(WARN, "[xwm] Re-assignment of WL_SURFACE_ID"); dissociate(XSURF); } auto id = e->data.data32[0]; - auto resource = wl_client_get_object(g_pXWayland->m_server->m_xwaylandClient, id); + auto resource = wl_client_get_object(g_pXWayland->pServer->xwaylandClient, id); if (resource) { auto surf = CWLSurfaceResource::fromResource(resource); associate(XSURF, surf); } } else if (e->type == HYPRATOMS["WL_SURFACE_SERIAL"]) { - if (XSURF->m_wlSerial) { - Log::logger->log(Log::WARN, "[xwm] Re-assignment of WL_SURFACE_SERIAL"); + if (XSURF->wlSerial) { + Debug::log(WARN, "[xwm] Re-assignment of WL_SURFACE_SERIAL"); dissociate(XSURF); } uint32_t serialLow = e->data.data32[0]; uint32_t serialHigh = e->data.data32[1]; - XSURF->m_wlSerial = (sc(serialHigh) << 32) | serialLow; + XSURF->wlSerial = ((uint64_t)serialHigh << 32) | serialLow; - Log::logger->log(Log::DEBUG, "[xwm] surface {:x} requests serial {:x}", rc(XSURF.get()), XSURF->m_wlSerial); + Debug::log(LOG, "[xwm] surface {:x} requests serial {:x}", (uintptr_t)XSURF.get(), XSURF->wlSerial); - for (auto const& res : m_shellResources) { + for (auto const& res : shellResources) { if (!res) continue; - if (res->m_serial != XSURF->m_wlSerial || !XSURF->m_wlSerial) + if (res->serial != XSURF->wlSerial || !XSURF->wlSerial) continue; - associate(XSURF, res->m_surface.lock()); + associate(XSURF, res->surface.lock()); break; } @@ -433,27 +391,16 @@ void CXWM::handleClientMessage(xcb_client_message_event_t* e) { }; if (prop == HYPRATOMS["_NET_WM_STATE_FULLSCREEN"]) - XSURF->m_state.requestsFullscreen = updateState(action, XSURF->m_fullscreen); - if (prop == HYPRATOMS["_NET_WM_STATE_HIDDEN"]) - XSURF->m_state.requestsMinimize = updateState(action, XSURF->m_minimized); - if (prop == HYPRATOMS["_NET_WM_STATE_MAXIMIZED_VERT"] || prop == HYPRATOMS["_NET_WM_STATE_MAXIMIZED_HORZ"]) - XSURF->m_state.requestsMaximize = updateState(action, XSURF->m_maximized); + XSURF->state.requestsFullscreen = updateState(action, XSURF->fullscreen); } - XSURF->m_events.stateChanged.emit(); + XSURF->events.stateChanged.emit(); } - } else if (e->type == HYPRATOMS["WM_CHANGE_STATE"]) { - int state = e->data.data32[0]; - if (state == XCB_ICCCM_WM_STATE_ICONIC || state == XCB_ICCCM_WM_STATE_WITHDRAWN) - XSURF->m_state.requestsMinimize = true; - else if (state == XCB_ICCCM_WM_STATE_NORMAL) - XSURF->m_state.requestsMinimize = false; - XSURF->m_events.stateChanged.emit(); } else if (e->type == HYPRATOMS["_NET_ACTIVE_WINDOW"]) { - XSURF->m_events.activate.emit(); + XSURF->events.activate.emit(); } else if (e->type == HYPRATOMS["XdndStatus"]) { - if (m_dndDataOffers.empty() || !m_dndDataOffers.at(0)->getSource()) { - Log::logger->log(Log::TRACE, "[xwm] Rejecting XdndStatus message: nothing to get"); + if (dndDataOffers.empty() || !dndDataOffers.at(0)->getSource()) { + Debug::log(TRACE, "[xwm] Rejecting XdndStatus message: nothing to get"); return; } @@ -461,24 +408,24 @@ void CXWM::handleClientMessage(xcb_client_message_event_t* e) { const bool ACCEPTED = data->data32[1] & 1; if (ACCEPTED) - m_dndDataOffers.at(0)->getSource()->accepted(""); + dndDataOffers.at(0)->getSource()->accepted(""); - Log::logger->log(Log::DEBUG, "[xwm] XdndStatus: accepted: {}"); + Debug::log(LOG, "[xwm] XdndStatus: accepted: {}"); } else if (e->type == HYPRATOMS["XdndFinished"]) { - if (m_dndDataOffers.empty() || !m_dndDataOffers.at(0)->getSource()) { - Log::logger->log(Log::TRACE, "[xwm] Rejecting XdndFinished message: nothing to get"); + if (dndDataOffers.empty() || !dndDataOffers.at(0)->getSource()) { + Debug::log(TRACE, "[xwm] Rejecting XdndFinished message: nothing to get"); return; } - m_dndDataOffers.at(0)->getSource()->sendDndFinished(); + dndDataOffers.at(0)->getSource()->sendDndFinished(); - Log::logger->log(Log::DEBUG, "[xwm] XdndFinished"); + Debug::log(LOG, "[xwm] XdndFinished"); } else { - Log::logger->log(Log::TRACE, "[xwm] Unhandled message prop {} -> {}", e->type, propName); + Debug::log(TRACE, "[xwm] Unhandled message prop {} -> {}", e->type, propName); return; } - Log::logger->log(Log::TRACE, "[xwm] Handled message prop {} -> {}", e->type, propName); + Debug::log(TRACE, "[xwm] Handled message prop {} -> {}", e->type, propName); } void CXWM::handleFocusIn(xcb_focus_in_event_t* e) { @@ -490,22 +437,22 @@ void CXWM::handleFocusIn(xcb_focus_in_event_t* e) { if (!XSURF) return; - if (m_focusedSurface && m_focusedSurface->m_pid == XSURF->m_pid && e->sequence - m_lastFocusSeq <= 255) + if (focusedSurface && focusedSurface->pid == XSURF->pid && e->sequence - lastFocusSeq <= 255) focusWindow(XSURF); else - focusWindow(m_focusedSurface.lock()); + focusWindow(focusedSurface.lock()); } void CXWM::handleFocusOut(xcb_focus_out_event_t* e) { - Log::logger->log(Log::TRACE, "[xwm] focusOut mode={}, detail={}, event={}", e->mode, e->detail, e->event); + Debug::log(TRACE, "[xwm] focusOut mode={}, detail={}, event={}", e->mode, e->detail, e->event); const auto XSURF = windowForXID(e->event); if (!XSURF) return; - Log::logger->log(Log::TRACE, "[xwm] focusOut for {} {} {} surface {}", XSURF->m_mapped ? "mapped" : "unmapped", XSURF->m_fullscreen ? "fullscreen" : "windowed", - XSURF == m_focusedSurface ? "focused" : "unfocused", XSURF->m_state.title); + Debug::log(TRACE, "[xwm] focusOut for {} {} {} surface {}", XSURF->mapped ? "mapped" : "unmapped", XSURF->fullscreen ? "fullscreen" : "windowed", + XSURF == focusedSurface ? "focused" : "unfocused", XSURF->state.title); // do something? } @@ -515,70 +462,70 @@ void CXWM::sendWMMessage(SP surf, xcb_client_message_data_t* d .response_type = XCB_CLIENT_MESSAGE, .format = 32, .sequence = 0, - .window = surf->m_xID, + .window = surf->xID, .type = HYPRATOMS["WM_PROTOCOLS"], .data = *data, }; - xcb_send_event(getConnection(), 0, surf->m_xID, mask, rc(&event)); - xcb_flush(getConnection()); + xcb_send_event(connection, 0, surf->xID, mask, (const char*)&event); + xcb_flush(connection); } void CXWM::focusWindow(SP surf) { - if (surf == m_focusedSurface) + if (surf == focusedSurface) return; - m_focusedSurface = surf; + focusedSurface = surf; // send state to all toplevel surfaces, sometimes we might lose some // that could still stick with the focused atom - for (auto const& s : m_mappedSurfaces) { - if (!s || s->m_overrideRedirect) + for (auto const& s : mappedSurfaces) { + if (!s || s->overrideRedirect) continue; sendState(s.lock()); } if (!surf) { - xcb_set_input_focus_checked(getConnection(), XCB_INPUT_FOCUS_POINTER_ROOT, XCB_NONE, XCB_CURRENT_TIME); + xcb_set_input_focus_checked(connection, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_NONE, XCB_CURRENT_TIME); return; } - if (surf->m_overrideRedirect) + if (surf->overrideRedirect) return; xcb_client_message_data_t msg = {{0}}; msg.data32[0] = HYPRATOMS["WM_TAKE_FOCUS"]; msg.data32[1] = XCB_TIME_CURRENT_TIME; - if (surf->m_hints && !surf->m_hints->input) + if (surf->hints && !surf->hints->input) sendWMMessage(surf, &msg, XCB_EVENT_MASK_NO_EVENT); else { sendWMMessage(surf, &msg, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT); - xcb_void_cookie_t cookie = xcb_set_input_focus(getConnection(), XCB_INPUT_FOCUS_POINTER_ROOT, surf->m_xID, XCB_CURRENT_TIME); - m_lastFocusSeq = cookie.sequence; + xcb_void_cookie_t cookie = xcb_set_input_focus(connection, XCB_INPUT_FOCUS_POINTER_ROOT, surf->xID, XCB_CURRENT_TIME); + lastFocusSeq = cookie.sequence; } } void CXWM::handleError(xcb_value_error_t* e) { - const char* major_name = xcb_errors_get_name_for_major_code(m_errors, e->major_opcode); + const char* major_name = xcb_errors_get_name_for_major_code(errors, e->major_opcode); if (!major_name) { - Log::logger->log(Log::ERR, "xcb error happened, but could not get major name"); + Debug::log(ERR, "xcb error happened, but could not get major name"); return; } - const char* minor_name = xcb_errors_get_name_for_minor_code(m_errors, e->major_opcode, e->minor_opcode); + const char* minor_name = xcb_errors_get_name_for_minor_code(errors, e->major_opcode, e->minor_opcode); const char* extension; - const char* error_name = xcb_errors_get_name_for_error(m_errors, e->error_code, &extension); + const char* error_name = xcb_errors_get_name_for_error(errors, e->error_code, &extension); if (!error_name) { - Log::logger->log(Log::ERR, "xcb error happened, but could not get error name"); + Debug::log(ERR, "xcb error happened, but could not get error name"); return; } - Log::logger->log(Log::ERR, "[xwm] xcb error: {} ({}), code {} ({}), seq {}, val {}", major_name, minor_name ? minor_name : "no minor", error_name, - extension ? extension : "no extension", e->sequence, e->bad_value); + Debug::log(ERR, "[xwm] xcb error: {} ({}), code {} ({}), seq {}, val {}", major_name, minor_name ? minor_name : "no minor", error_name, extension ? extension : "no extension", + e->sequence, e->bad_value); } void CXWM::selectionSendNotify(xcb_selection_request_event_t* e, bool success) { @@ -589,11 +536,11 @@ void CXWM::selectionSendNotify(xcb_selection_request_event_t* e, bool success) { .requestor = e->requestor, .selection = e->selection, .target = e->target, - .property = success ? e->property : sc(XCB_ATOM_NONE), + .property = success ? e->property : (uint32_t)XCB_ATOM_NONE, }; - xcb_send_event(getConnection(), 0, e->requestor, XCB_EVENT_MASK_NO_EVENT, rc(&selection_notify)); - xcb_flush(getConnection()); + xcb_send_event(connection, 0, e->requestor, XCB_EVENT_MASK_NO_EVENT, (const char*)&selection_notify); + xcb_flush(connection); } xcb_atom_t CXWM::mimeToAtom(const std::string& mime) { @@ -602,12 +549,12 @@ xcb_atom_t CXWM::mimeToAtom(const std::string& mime) { if (mime == "text/plain") return HYPRATOMS["TEXT"]; - xcb_intern_atom_cookie_t cookie = xcb_intern_atom(getConnection(), 0, mime.length(), mime.c_str()); - XCBReplyPtr reply(xcb_intern_atom_reply(getConnection(), cookie, nullptr)); - if (!reply.get()) + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, 0, mime.length(), mime.c_str()); + xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(connection, cookie, nullptr); + if (!reply) return XCB_ATOM_NONE; xcb_atom_t atom = reply->atom; - + free(reply); return atom; } @@ -617,30 +564,31 @@ std::string CXWM::mimeFromAtom(xcb_atom_t atom) { if (atom == HYPRATOMS["TEXT"]) return "text/plain"; - xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(getConnection(), atom); - XCBReplyPtr reply(xcb_get_atom_name_reply(getConnection(), cookie, nullptr)); + xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(connection, atom); + xcb_get_atom_name_reply_t* reply = xcb_get_atom_name_reply(connection, cookie, nullptr); if (!reply) return "INVALID"; - size_t len = xcb_get_atom_name_name_length(reply.get()); - char* buf = xcb_get_atom_name_name(reply.get()); // not a C string + size_t len = xcb_get_atom_name_name_length(reply); + char* buf = xcb_get_atom_name_name(reply); // not a C string std::string SZNAME{buf, len}; + free(reply); return SZNAME; } void CXWM::handleSelectionNotify(xcb_selection_notify_event_t* e) { - Log::logger->log(Log::TRACE, "[xwm] Selection notify for {} prop {} target {}", e->selection, e->property, e->target); + Debug::log(TRACE, "[xwm] Selection notify for {} prop {} target {}", e->selection, e->property, e->target); SXSelection* sel = getSelection(e->selection); if (e->property == XCB_ATOM_NONE) { auto it = std::ranges::find_if(sel->transfers, [](const auto& t) { return !t->propertyReply; }); if (it != sel->transfers.end()) { - Log::logger->log(Log::TRACE, "[xwm] converting selection failed"); + Debug::log(TRACE, "[xwm] converting selection failed"); sel->transfers.erase(it); } - } else if (e->target == HYPRATOMS["TARGETS"]) { - if (!m_focusedSurface) { - Log::logger->log(Log::TRACE, "[xwm] denying access to write to clipboard because no X client is in focus"); + } else if (e->target == HYPRATOMS["TARGETS"] && sel == &clipboard) { + if (!focusedSurface) { + Debug::log(TRACE, "[xwm] denying access to write to clipboard because no X client is in focus"); return; } @@ -650,32 +598,16 @@ void CXWM::handleSelectionNotify(xcb_selection_notify_event_t* e) { } bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) { - for (auto* sel : {&m_clipboard, &m_primarySelection, &m_dndSelection}) { - auto it = std::ranges::find_if(sel->transfers, [e](const auto& t) { return t->incomingWindow == e->window; }); - if (it == sel->transfers.end()) - continue; + if (e->state != XCB_PROPERTY_DELETE) + return false; - auto& transfer = *it; - - if (e->state == XCB_PROPERTY_NEW_VALUE) { - if (!transfer->incremental) { - getTransferData(*sel); - return true; - } - - if (!transfer->getIncomingSelectionProp(true) || xcb_get_property_value_length(transfer->propertyReply) == 0) { - sel->transfers.erase(it); - return true; - } - - int result = sel->onWrite(); - - if (result == 1 && !transfer->eventSource) { - transfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, transfer->wlFD.get(), WL_EVENT_WRITABLE, ::writeDataSource, sel); - } - } else if (e->state == XCB_PROPERTY_DELETE) { - getTransferData(*sel); + auto it = std::ranges::find_if(clipboard.transfers, [e](const auto& t) { return t->incomingWindow == e->window; }); + if (it != clipboard.transfers.end()) { + if (!(*it)->getIncomingSelectionProp(true)) { + clipboard.transfers.erase(it); + return false; } + getTransferData(clipboard); return true; } @@ -684,23 +616,21 @@ bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) { SXSelection* CXWM::getSelection(xcb_atom_t atom) { if (atom == HYPRATOMS["CLIPBOARD"]) - return &m_clipboard; - else if (atom == HYPRATOMS["PRIMARY"]) - return &m_primarySelection; + return &clipboard; else if (atom == HYPRATOMS["XdndSelection"]) - return &m_dndSelection; + return &dndSelection; return nullptr; } void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) { - Log::logger->log(Log::TRACE, "[xwm] Selection request for {} prop {} target {} time {} requestor {} selection {}", e->selection, e->property, e->target, e->time, e->requestor, - e->selection); + Debug::log(TRACE, "[xwm] Selection request for {} prop {} target {} time {} requestor {} selection {}", e->selection, e->property, e->target, e->time, e->requestor, + e->selection); SXSelection* sel = getSelection(e->selection); if (!sel) { - Log::logger->log(Log::ERR, "[xwm] No selection"); + Debug::log(ERR, "[xwm] No selection"); selectionSendNotify(e, false); return; } @@ -711,13 +641,13 @@ void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) { } if (sel->window != e->owner && e->time != XCB_CURRENT_TIME && e->time < sel->timestamp) { - Log::logger->log(Log::ERR, "[xwm] outdated selection request. Time {} < {}", e->time, sel->timestamp); + Debug::log(ERR, "[xwm] outdated selection request. Time {} < {}", e->time, sel->timestamp); selectionSendNotify(e, false); return; } - if (!g_pSeatManager->m_state.keyboardFocusResource || g_pSeatManager->m_state.keyboardFocusResource->client() != g_pXWayland->m_server->m_xwaylandClient) { - Log::logger->log(Log::TRACE, "[xwm] Ignoring clipboard access: xwayland not in focus"); + if (!g_pSeatManager->state.keyboardFocusResource || g_pSeatManager->state.keyboardFocusResource->client() != g_pXWayland->pServer->xwaylandClient) { + Debug::log(TRACE, "[xwm] Ignoring clipboard access: xwayland not in focus"); selectionSendNotify(e, false); return; } @@ -725,13 +655,13 @@ void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) { if (e->target == HYPRATOMS["TARGETS"]) { // send mime types std::vector mimes; - if (sel == &m_clipboard && g_pSeatManager->m_selection.currentSelection) - mimes = g_pSeatManager->m_selection.currentSelection->mimes(); - else if (sel == &m_dndSelection && !m_dndDataOffers.empty() && m_dndDataOffers.at(0)->m_source) - mimes = m_dndDataOffers.at(0)->m_source->mimes(); + if (sel == &clipboard && g_pSeatManager->selection.currentSelection) + mimes = g_pSeatManager->selection.currentSelection->mimes(); + else if (sel == &dndSelection && !dndDataOffers.empty() && dndDataOffers.at(0)->source) + mimes = dndDataOffers.at(0)->source->mimes(); if (mimes.empty()) - Log::logger->log(Log::WARN, "[xwm] WARNING: No mimes in TARGETS?"); + Debug::log(WARN, "[xwm] WARNING: No mimes in TARGETS?"); std::vector atoms; // reserve to avoid reallocations @@ -743,10 +673,10 @@ void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) { atoms.push_back(mimeToAtom(m)); } - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, e->requestor, e->property, XCB_ATOM_ATOM, 32, atoms.size(), atoms.data()); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, e->requestor, e->property, XCB_ATOM_ATOM, 32, atoms.size(), atoms.data()); selectionSendNotify(e, true); } else if (e->target == HYPRATOMS["TIMESTAMP"]) { - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, e->requestor, e->property, XCB_ATOM_INTEGER, 32, 1, &sel->timestamp); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, e->requestor, e->property, XCB_ATOM_INTEGER, 32, 1, &sel->timestamp); selectionSendNotify(e, true); } else if (e->target == HYPRATOMS["DELETE"]) { selectionSendNotify(e, true); @@ -754,13 +684,13 @@ void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) { std::string mime = mimeFromAtom(e->target); if (mime == "INVALID") { - Log::logger->log(Log::DEBUG, "[xwm] Ignoring clipboard access: invalid mime atom {}", e->target); + Debug::log(LOG, "[xwm] Ignoring clipboard access: invalid mime atom {}", e->target); selectionSendNotify(e, false); return; } if (!sel->sendData(e, mime)) { - Log::logger->log(Log::DEBUG, "[xwm] Failed to send selection :("); + Debug::log(LOG, "[xwm] Failed to send selection :("); selectionSendNotify(e, false); return; } @@ -768,21 +698,17 @@ void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) { } bool CXWM::handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e) { - Log::logger->log(Log::TRACE, "[xwm] Selection xfixes notify for {}", e->selection); + Debug::log(TRACE, "[xwm] Selection xfixes notify for {}", e->selection); // IMPORTANT: mind the g_pSeatManager below SXSelection* sel = getSelection(e->selection); - if (sel == &m_dndSelection) + if (sel == &dndSelection) return true; if (e->owner == XCB_WINDOW_NONE) { - if (sel->owner != sel->window) { - if (sel == &m_clipboard) - g_pSeatManager->setCurrentSelection(nullptr); - else if (sel == &m_primarySelection) - g_pSeatManager->setCurrentPrimarySelection(nullptr); - } + if (sel->owner != sel->window && sel == &clipboard) + g_pSeatManager->setCurrentSelection(nullptr); sel->owner = 0; return true; @@ -795,11 +721,8 @@ bool CXWM::handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e) { return true; } - if (sel == &m_clipboard) - xcb_convert_selection(getConnection(), sel->window, HYPRATOMS["CLIPBOARD"], HYPRATOMS["TARGETS"], HYPRATOMS["_WL_SELECTION"], e->timestamp); - else if (sel == &m_primarySelection) - xcb_convert_selection(getConnection(), sel->window, HYPRATOMS["PRIMARY"], HYPRATOMS["TARGETS"], HYPRATOMS["_WL_SELECTION"], e->timestamp); - xcb_flush(getConnection()); + xcb_convert_selection(connection, sel->window, HYPRATOMS["CLIPBOARD"], HYPRATOMS["TARGETS"], HYPRATOMS["_WL_SELECTION"], e->timestamp); + xcb_flush(connection); return true; } @@ -807,118 +730,123 @@ bool CXWM::handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e) { bool CXWM::handleSelectionEvent(xcb_generic_event_t* e) { switch (e->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { case XCB_SELECTION_NOTIFY: { - handleSelectionNotify(rc(e)); + handleSelectionNotify((xcb_selection_notify_event_t*)e); return true; } case XCB_PROPERTY_NOTIFY: { - return handleSelectionPropertyNotify(rc(e)); + return handleSelectionPropertyNotify((xcb_property_notify_event_t*)e); } case XCB_SELECTION_REQUEST: { - handleSelectionRequest(rc(e)); + handleSelectionRequest((xcb_selection_request_event_t*)e); return true; } } - if (e->response_type - m_xfixes->first_event == XCB_XFIXES_SELECTION_NOTIFY) - return handleSelectionXFixesNotify(rc(e)); + if (e->response_type - xfixes->first_event == XCB_XFIXES_SELECTION_NOTIFY) + return handleSelectionXFixesNotify((xcb_xfixes_selection_notify_event_t*)e); - return false; + return 0; } int CXWM::onEvent(int fd, uint32_t mask) { if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { - Log::logger->log(Log::ERR, "XWayland has yeeten the xwm off?!"); - Log::logger->log(Log::CRIT, "XWayland has yeeten the xwm off?!"); + Debug::log(ERR, "XWayland has yeeten the xwm off?!"); + Debug::log(CRIT, "XWayland has yeeten the xwm off?!"); + g_pXWayland->pWM.reset(); + g_pXWayland->pServer.reset(); // Attempt to create fresh instance - g_pEventLoopManager->doLater([]() { - g_pXWayland->m_wm.reset(); - g_pXWayland->m_server.reset(); - g_pXWayland = makeUnique(true); - }); + g_pEventLoopManager->doLater([]() { g_pXWayland = makeUnique(true); }); return 0; } - int processedEventCount = 0; - using XCBEventPtr = std::unique_ptr; - while (true) { - XCBEventPtr event(xcb_poll_for_event(getConnection()), &free); + int count = 0; + + while (42069) { + xcb_generic_event_t* event = xcb_poll_for_event(connection); if (!event) break; - processedEventCount++; + count++; - if (handleSelectionEvent(event.get())) + if (handleSelectionEvent(event)) { + free(event); continue; + } switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { - case XCB_CREATE_NOTIFY: handleCreate(rc(event.get())); break; - case XCB_DESTROY_NOTIFY: handleDestroy(rc(event.get())); break; - case XCB_CONFIGURE_REQUEST: handleConfigureRequest(rc(event.get())); break; - case XCB_CONFIGURE_NOTIFY: handleConfigureNotify(rc(event.get())); break; - case XCB_MAP_REQUEST: handleMapRequest(rc(event.get())); break; - case XCB_MAP_NOTIFY: handleMapNotify(rc(event.get())); break; - case XCB_UNMAP_NOTIFY: handleUnmapNotify(rc(event.get())); break; - case XCB_PROPERTY_NOTIFY: handlePropertyNotify(rc(event.get())); break; - case XCB_CLIENT_MESSAGE: handleClientMessage(rc(event.get())); break; - case XCB_FOCUS_IN: handleFocusIn(rc(event.get())); break; - case XCB_FOCUS_OUT: handleFocusOut(rc(event.get())); break; - case 0: handleError(rc(event.get())); break; + case XCB_CREATE_NOTIFY: handleCreate((xcb_create_notify_event_t*)event); break; + case XCB_DESTROY_NOTIFY: handleDestroy((xcb_destroy_notify_event_t*)event); break; + case XCB_CONFIGURE_REQUEST: handleConfigureRequest((xcb_configure_request_event_t*)event); break; + case XCB_CONFIGURE_NOTIFY: handleConfigureNotify((xcb_configure_notify_event_t*)event); break; + case XCB_MAP_REQUEST: handleMapRequest((xcb_map_request_event_t*)event); break; + case XCB_MAP_NOTIFY: handleMapNotify((xcb_map_notify_event_t*)event); break; + case XCB_UNMAP_NOTIFY: handleUnmapNotify((xcb_unmap_notify_event_t*)event); break; + case XCB_PROPERTY_NOTIFY: handlePropertyNotify((xcb_property_notify_event_t*)event); break; + case XCB_CLIENT_MESSAGE: handleClientMessage((xcb_client_message_event_t*)event); break; + case XCB_FOCUS_IN: handleFocusIn((xcb_focus_in_event_t*)event); break; + case XCB_FOCUS_OUT: handleFocusOut((xcb_focus_out_event_t*)event); break; + case 0: handleError((xcb_value_error_t*)event); break; default: { - Log::logger->log(Log::TRACE, "[xwm] unhandled event {}", event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK); + Debug::log(TRACE, "[xwm] unhandled event {}", event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK); } } + free(event); } - if (processedEventCount) - xcb_flush(getConnection()); + if (count) + xcb_flush(connection); - return processedEventCount; + return count; } void CXWM::gatherResources() { - xcb_prefetch_extension_data(getConnection(), &xcb_xfixes_id); - xcb_prefetch_extension_data(getConnection(), &xcb_composite_id); - xcb_prefetch_extension_data(getConnection(), &xcb_res_id); + xcb_prefetch_extension_data(connection, &xcb_xfixes_id); + xcb_prefetch_extension_data(connection, &xcb_composite_id); + xcb_prefetch_extension_data(connection, &xcb_res_id); for (auto& ATOM : HYPRATOMS) { - xcb_intern_atom_cookie_t cookie = xcb_intern_atom(getConnection(), 0, ATOM.first.length(), ATOM.first.c_str()); - XCBReplyPtr reply(xcb_intern_atom_reply(getConnection(), cookie, nullptr)); + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, 0, ATOM.first.length(), ATOM.first.c_str()); + xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(connection, cookie, nullptr); if (!reply) { - Log::logger->log(Log::ERR, "[xwm] Atom failed: {}", ATOM.first); + Debug::log(ERR, "[xwm] Atom failed: {}", ATOM.first); continue; } ATOM.second = reply->atom; + free(reply); } - m_xfixes = xcb_get_extension_data(getConnection(), &xcb_xfixes_id); + xfixes = xcb_get_extension_data(connection, &xcb_xfixes_id); - if (!m_xfixes || !m_xfixes->present) - Log::logger->log(Log::WARN, "XFixes not available"); + if (!xfixes || !xfixes->present) + Debug::log(WARN, "XFixes not available"); - auto xfixes_cookie = xcb_xfixes_query_version(getConnection(), XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION); - XCBReplyPtr xfixes_reply(xcb_xfixes_query_version_reply(getConnection(), xfixes_cookie, nullptr)); + xcb_xfixes_query_version_cookie_t xfixes_cookie; + xcb_xfixes_query_version_reply_t* xfixes_reply; + xfixes_cookie = xcb_xfixes_query_version(connection, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION); + xfixes_reply = xcb_xfixes_query_version_reply(connection, xfixes_cookie, nullptr); - if (xfixes_reply) { - Log::logger->log(Log::DEBUG, "xfixes version: {}.{}", xfixes_reply->major_version, xfixes_reply->minor_version); - m_xfixesMajor = xfixes_reply->major_version; - } + Debug::log(LOG, "xfixes version: {}.{}", xfixes_reply->major_version, xfixes_reply->minor_version); + xfixesMajor = xfixes_reply->major_version; - const auto* xresReply1 = xcb_get_extension_data(getConnection(), &xcb_res_id); + free(xfixes_reply); + + const xcb_query_extension_reply_t* xresReply1 = xcb_get_extension_data(connection, &xcb_res_id); if (!xresReply1 || !xresReply1->present) return; - auto xres_cookie = xcb_res_query_version(getConnection(), XCB_RES_MAJOR_VERSION, XCB_RES_MINOR_VERSION); - XCBReplyPtr xres_reply(xcb_res_query_version_reply(getConnection(), xres_cookie, nullptr)); - if (!xres_reply) + xcb_res_query_version_cookie_t xres_cookie = xcb_res_query_version(connection, XCB_RES_MAJOR_VERSION, XCB_RES_MINOR_VERSION); + xcb_res_query_version_reply_t* xres_reply = xcb_res_query_version_reply(connection, xres_cookie, nullptr); + if (xres_reply == nullptr) return; - Log::logger->log(Log::DEBUG, "xres version: {}.{}", xres_reply->server_major, xres_reply->server_minor); + Debug::log(LOG, "xres version: {}.{}", xres_reply->server_major, xres_reply->server_minor); if (xres_reply->server_major > 1 || (xres_reply->server_major == 1 && xres_reply->server_minor >= 2)) { - m_xres = xresReply1; + xres = xresReply1; } + free(xres_reply); } void CXWM::getVisual() { @@ -926,7 +854,7 @@ void CXWM::getVisual() { xcb_visualtype_iterator_t vt_iter; xcb_visualtype_t* visualtype; - d_iter = xcb_screen_allowed_depths_iterator(m_screen); + d_iter = xcb_screen_allowed_depths_iterator(screen); visualtype = nullptr; while (d_iter.rem > 0) { if (d_iter.data->depth == 32) { @@ -939,26 +867,24 @@ void CXWM::getVisual() { } if (visualtype == nullptr) { - Log::logger->log(Log::DEBUG, "xwm: No 32-bit visualtype"); + Debug::log(LOG, "xwm: No 32-bit visualtype"); return; } - m_visualID = visualtype->visual_id; - m_colormap = xcb_generate_id(getConnection()); - xcb_create_colormap(getConnection(), XCB_COLORMAP_ALLOC_NONE, m_colormap, m_screen->root, m_visualID); + visual_id = visualtype->visual_id; + colormap = xcb_generate_id(connection); + xcb_create_colormap(connection, XCB_COLORMAP_ALLOC_NONE, colormap, screen->root, visual_id); } void CXWM::getRenderFormat() { - auto cookie = xcb_render_query_pict_formats(getConnection()); - XCBReplyPtr reply(xcb_render_query_pict_formats_reply(getConnection(), cookie, nullptr)); - + xcb_render_query_pict_formats_cookie_t cookie = xcb_render_query_pict_formats(connection); + xcb_render_query_pict_formats_reply_t* reply = xcb_render_query_pict_formats_reply(connection, cookie, nullptr); if (!reply) { - Log::logger->log(Log::DEBUG, "xwm: No xcb_render_query_pict_formats_reply_t reply"); + Debug::log(LOG, "xwm: No xcb_render_query_pict_formats_reply_t reply"); return; } - - auto iter = xcb_render_query_pict_formats_formats_iterator(reply.get()); - xcb_render_pictforminfo_t* format = nullptr; + xcb_render_pictforminfo_iterator_t iter = xcb_render_query_pict_formats_formats_iterator(reply); + xcb_render_pictforminfo_t* format = nullptr; while (iter.rem > 0) { if (iter.data->depth == 32) { format = iter.data; @@ -969,32 +895,35 @@ void CXWM::getRenderFormat() { } if (format == nullptr) { - Log::logger->log(Log::DEBUG, "xwm: No 32-bit render format"); + Debug::log(LOG, "xwm: No 32-bit render format"); + free(reply); return; } - m_renderFormatID = format->id; + render_format_id = format->id; + free(reply); } -CXWM::CXWM() : m_connection(makeUnique(g_pXWayland->m_server->m_xwmFDs[0].get())) { +CXWM::CXWM() : connection(g_pXWayland->pServer->xwmFDs[0].get()) { - if (m_connection->hasError()) { - Log::logger->log(Log::ERR, "[xwm] Couldn't start, error {}", m_connection->hasError()); + if (connection.hasError()) { + Debug::log(ERR, "[xwm] Couldn't start, error {}", connection.hasError()); return; } - CXCBErrorContext xcbErrCtx(getConnection()); + CXCBErrorContext xcbErrCtx(connection); if (!xcbErrCtx.isValid()) { - Log::logger->log(Log::ERR, "[xwm] Couldn't allocate errors context"); + Debug::log(ERR, "[xwm] Couldn't allocate errors context"); return; } - m_dndDataDevice->m_self = m_dndDataDevice; + dndDataDevice->self = dndDataDevice; - xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(getConnection())); - m_screen = screen_iterator.data; + xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(connection)); + screen = screen_iterator.data; - m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, g_pXWayland->m_server->m_xwmFDs[0].get(), WL_EVENT_READABLE, ::onX11Event, nullptr); + eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, g_pXWayland->pServer->xwmFDs[0].get(), WL_EVENT_READABLE, ::onX11Event, nullptr); + wl_event_source_check(eventSource); gatherResources(); getVisual(); @@ -1003,134 +932,134 @@ CXWM::CXWM() : m_connection(makeUnique(g_pXWayland->m_server->m_ uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_PROPERTY_CHANGE, }; - xcb_change_window_attributes(getConnection(), m_screen->root, XCB_CW_EVENT_MASK, values); + xcb_change_window_attributes(connection, screen->root, XCB_CW_EVENT_MASK, values); - xcb_composite_redirect_subwindows(getConnection(), m_screen->root, XCB_COMPOSITE_REDIRECT_MANUAL); + xcb_composite_redirect_subwindows(connection, screen->root, XCB_COMPOSITE_REDIRECT_MANUAL); xcb_atom_t supported[] = { HYPRATOMS["_NET_WM_STATE"], HYPRATOMS["_NET_ACTIVE_WINDOW"], HYPRATOMS["_NET_WM_MOVERESIZE"], HYPRATOMS["_NET_WM_STATE_FOCUSED"], HYPRATOMS["_NET_WM_STATE_MODAL"], HYPRATOMS["_NET_WM_STATE_FULLSCREEN"], HYPRATOMS["_NET_WM_STATE_MAXIMIZED_VERT"], HYPRATOMS["_NET_WM_STATE_MAXIMIZED_HORZ"], - HYPRATOMS["_NET_WM_STATE_HIDDEN"], HYPRATOMS["_NET_CLIENT_LIST"], HYPRATOMS["_NET_CLIENT_LIST_STACKING"], HYPRATOMS["_NET_WORKAREA"], + HYPRATOMS["_NET_WM_STATE_HIDDEN"], HYPRATOMS["_NET_CLIENT_LIST"], HYPRATOMS["_NET_CLIENT_LIST_STACKING"], }; - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_SUPPORTED"], XCB_ATOM_ATOM, 32, sizeof(supported) / sizeof(*supported), supported); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_SUPPORTED"], XCB_ATOM_ATOM, 32, sizeof(supported) / sizeof(*supported), supported); setActiveWindow(XCB_WINDOW_NONE); initSelection(); - m_listeners.newWLSurface = PROTO::compositor->m_events.newSurface.listen([this](const auto& surface) { onNewSurface(surface); }); - m_listeners.newXShellSurface = PROTO::xwaylandShell->m_events.newSurface.listen([this](const auto& surface) { onNewResource(surface); }); + listeners.newWLSurface = PROTO::compositor->events.newSurface.registerListener([this](std::any d) { onNewSurface(std::any_cast>(d)); }); + listeners.newXShellSurface = PROTO::xwaylandShell->events.newSurface.registerListener([this](std::any d) { onNewResource(std::any_cast>(d)); }); createWMWindow(); - xcb_flush(getConnection()); + xcb_flush(connection); } CXWM::~CXWM() { - if (m_eventSource) - wl_event_source_remove(m_eventSource); + if (eventSource) + wl_event_source_remove(eventSource); - for (auto const& sr : m_surfaces) { - sr->m_events.destroy.emit(); + for (auto const& sr : surfaces) { + sr->events.destroy.emit(); } } void CXWM::setActiveWindow(xcb_window_t window) { - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_ACTIVE_WINDOW"], HYPRATOMS["WINDOW"], 32, 1, &window); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_ACTIVE_WINDOW"], HYPRATOMS["WINDOW"], 32, 1, &window); } void CXWM::createWMWindow() { constexpr const char* wmName = "Hyprland :D"; - m_wmWindow = xcb_generate_id(getConnection()); - xcb_create_window(getConnection(), XCB_COPY_FROM_PARENT, m_wmWindow, m_screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, m_screen->root_visual, 0, nullptr); - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_wmWindow, HYPRATOMS["_NET_WM_NAME"], HYPRATOMS["UTF8_STRING"], + wmWindow = xcb_generate_id(connection); + xcb_create_window(connection, XCB_COPY_FROM_PARENT, wmWindow, screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, 0, nullptr); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, wmWindow, HYPRATOMS["_NET_WM_NAME"], HYPRATOMS["UTF8_STRING"], 8, // format strlen(wmName), wmName); - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_SUPPORTING_WM_CHECK"], XCB_ATOM_WINDOW, + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_SUPPORTING_WM_CHECK"], XCB_ATOM_WINDOW, 32, // format - 1, &m_wmWindow); - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_wmWindow, HYPRATOMS["_NET_SUPPORTING_WM_CHECK"], XCB_ATOM_WINDOW, + 1, &wmWindow); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, wmWindow, HYPRATOMS["_NET_SUPPORTING_WM_CHECK"], XCB_ATOM_WINDOW, 32, // format - 1, &m_wmWindow); - xcb_set_selection_owner(getConnection(), m_wmWindow, HYPRATOMS["WM_S0"], XCB_CURRENT_TIME); - xcb_set_selection_owner(getConnection(), m_wmWindow, HYPRATOMS["_NET_WM_CM_S0"], XCB_CURRENT_TIME); + 1, &wmWindow); + xcb_set_selection_owner(connection, wmWindow, HYPRATOMS["WM_S0"], XCB_CURRENT_TIME); + xcb_set_selection_owner(connection, wmWindow, HYPRATOMS["_NET_WM_CM_S0"], XCB_CURRENT_TIME); } void CXWM::activateSurface(SP surf, bool activate) { - if ((surf == m_focusedSurface && activate) || (surf && surf->m_overrideRedirect)) + if ((surf == focusedSurface && activate) || (surf && surf->overrideRedirect)) return; - if (!surf || (!activate && Desktop::focusState()->window() && !Desktop::focusState()->window()->m_isX11)) { - setActiveWindow(XCB_WINDOW_NONE); + if (!surf || (!activate && g_pCompositor->m_pLastWindow && !g_pCompositor->m_pLastWindow->m_bIsX11)) { + setActiveWindow((uint32_t)XCB_WINDOW_NONE); focusWindow(nullptr); } else { - setActiveWindow(surf ? surf->m_xID : sc(XCB_WINDOW_NONE)); + setActiveWindow(surf ? surf->xID : (uint32_t)XCB_WINDOW_NONE); focusWindow(surf); } - xcb_flush(getConnection()); + xcb_flush(connection); } void CXWM::sendState(SP surf) { - Log::logger->log(Log::TRACE, "[xwm] sendState for {} {} {} surface {}", surf->m_mapped ? "mapped" : "unmapped", surf->m_fullscreen ? "fullscreen" : "windowed", - surf == m_focusedSurface ? "focused" : "unfocused", surf->m_state.title); - if (surf->m_fullscreen && surf->m_mapped && surf == m_focusedSurface) + Debug::log(TRACE, "[xwm] sendState for {} {} {} surface {}", surf->mapped ? "mapped" : "unmapped", surf->fullscreen ? "fullscreen" : "windowed", + surf == focusedSurface ? "focused" : "unfocused", surf->state.title); + if (surf->fullscreen && surf->mapped && surf == focusedSurface) surf->setWithdrawn(false); // resend normal state - if (surf->m_withdrawn) { - xcb_delete_property(getConnection(), surf->m_xID, HYPRATOMS["_NET_WM_STATE"]); + if (surf->withdrawn) { + xcb_delete_property(connection, surf->xID, HYPRATOMS["_NET_WM_STATE"]); return; } std::vector props; // reserve to avoid reallocations props.reserve(6); // props below - if (surf->m_modal) + if (surf->modal) props.push_back(HYPRATOMS["_NET_WM_STATE_MODAL"]); - if (surf->m_fullscreen) + if (surf->fullscreen) props.push_back(HYPRATOMS["_NET_WM_STATE_FULLSCREEN"]); - if (surf->m_maximized) { - props.push_back(HYPRATOMS["_NET_WM_STATE_MAXIMIZED_VERT"]); - props.push_back(HYPRATOMS["_NET_WM_STATE_MAXIMIZED_HORZ"]); + if (surf->maximized) { + props.push_back(HYPRATOMS["NET_WM_STATE_MAXIMIZED_VERT"]); + props.push_back(HYPRATOMS["NET_WM_STATE_MAXIMIZED_HORZ"]); } - if (surf->m_minimized) + if (surf->minimized) props.push_back(HYPRATOMS["_NET_WM_STATE_HIDDEN"]); - if (surf == m_focusedSurface) + if (surf == focusedSurface) props.push_back(HYPRATOMS["_NET_WM_STATE_FOCUSED"]); - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, surf->m_xID, HYPRATOMS["_NET_WM_STATE"], XCB_ATOM_ATOM, 32, props.size(), props.data()); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, surf->xID, HYPRATOMS["_NET_WM_STATE"], XCB_ATOM_ATOM, 32, props.size(), props.data()); } void CXWM::onNewSurface(SP surf) { - if (surf->client() != g_pXWayland->m_server->m_xwaylandClient) + if (surf->client() != g_pXWayland->pServer->xwaylandClient) return; - Log::logger->log(Log::DEBUG, "[xwm] New XWayland surface at {:x}", rc(surf.get())); + Debug::log(LOG, "[xwm] New XWayland surface at {:x}", (uintptr_t)surf.get()); const auto WLID = surf->id(); - for (auto const& sr : m_surfaces) { - if (sr->m_surface || sr->m_wlID != WLID) + for (auto const& sr : surfaces) { + if (sr->surface || sr->wlID != WLID) continue; associate(sr, surf); return; } - Log::logger->log(Log::WARN, "[xwm] CXWM::onNewSurface: no matching xwaylandSurface"); + Debug::log(WARN, "[xwm] CXWM::onNewSurface: no matching xwaylandSurface"); } void CXWM::onNewResource(SP resource) { - Log::logger->log(Log::DEBUG, "[xwm] New XWayland resource at {:x}", rc(resource.get())); + Debug::log(LOG, "[xwm] New XWayland resource at {:x}", (uintptr_t)resource.get()); - std::erase_if(m_shellResources, [](const auto& e) { return e.expired(); }); - m_shellResources.emplace_back(resource); + std::erase_if(shellResources, [](const auto& e) { return e.expired(); }); + shellResources.emplace_back(resource); - for (auto const& surf : m_surfaces) { - if (surf->m_resource || surf->m_wlSerial != resource->m_serial) + for (auto const& surf : surfaces) { + if (surf->resource || surf->wlSerial != resource->serial) continue; - associate(surf, resource->m_surface.lock()); + associate(surf, resource->surface.lock()); break; } } @@ -1143,19 +1072,20 @@ void CXWM::readWindowData(SP surf) { }; for (size_t i = 0; i < interestingProps.size(); i++) { - xcb_get_property_cookie_t cookie = xcb_get_property(getConnection(), 0, surf->m_xID, interestingProps[i], XCB_ATOM_ANY, 0, 2048); - XCBReplyPtr reply(xcb_get_property_reply(getConnection(), cookie, nullptr)); + xcb_get_property_cookie_t cookie = xcb_get_property(connection, 0, surf->xID, interestingProps[i], XCB_ATOM_ANY, 0, 2048); + xcb_get_property_reply_t* reply = xcb_get_property_reply(connection, cookie, nullptr); if (!reply) { - Log::logger->log(Log::ERR, "[xwm] Failed to get window property"); + Debug::log(ERR, "[xwm] Failed to get window property"); continue; } - readProp(surf, interestingProps[i], reply.get()); + readProp(surf, interestingProps[i], reply); + free(reply); } } SP CXWM::windowForWayland(SP surf) { - for (auto& s : m_surfaces) { - if (s->m_surface == surf) + for (auto& s : surfaces) { + if (s->surface == surf) return s; } @@ -1163,167 +1093,134 @@ SP CXWM::windowForWayland(SP surf) { } void CXWM::associate(SP surf, SP wlSurf) { - if (surf->m_surface) + if (surf->surface) return; - auto existing = std::ranges::find_if(m_surfaces, [wlSurf](const auto& e) { return e->m_surface == wlSurf; }); + auto existing = std::find_if(surfaces.begin(), surfaces.end(), [wlSurf](const auto& e) { return e->surface == wlSurf; }); - if (existing != m_surfaces.end()) { - Log::logger->log(Log::WARN, "[xwm] associate() called but surface is already associated to {:x}, ignoring...", rc(surf.get())); + if (existing != surfaces.end()) { + Debug::log(WARN, "[xwm] associate() called but surface is already associated to {:x}, ignoring...", (uintptr_t)surf.get()); return; } - surf->m_surface = wlSurf; + surf->surface = wlSurf; surf->ensureListeners(); readWindowData(surf); - surf->m_events.resourceChange.emit(); + surf->events.resourceChange.emit(); } void CXWM::dissociate(SP surf) { - if (!surf->m_surface) + if (!surf->surface) return; - if (surf->m_mapped) + if (surf->mapped) surf->unmap(); - surf->m_surface.reset(); - surf->m_events.resourceChange.emit(); + surf->surface.reset(); + surf->events.resourceChange.emit(); - Log::logger->log(Log::DEBUG, "Dissociate for {:x}", rc(surf.get())); + Debug::log(LOG, "Dissociate for {:x}", (uintptr_t)surf.get()); } void CXWM::updateClientList() { std::vector windows; - windows.reserve(m_mappedSurfaces.size()); + windows.reserve(mappedSurfaces.size()); - for (auto const& s : m_mappedSurfaces) { + for (auto const& s : mappedSurfaces) { if (auto surf = s.lock(); surf) - windows.push_back(surf->m_xID); + windows.push_back(surf->xID); } - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_CLIENT_LIST"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data()); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_CLIENT_LIST"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data()); windows.clear(); - windows.reserve(m_mappedSurfacesStacking.size()); + windows.reserve(mappedSurfacesStacking.size()); - for (auto const& s : m_mappedSurfacesStacking) { + for (auto const& s : mappedSurfacesStacking) { if (auto surf = s.lock(); surf) - windows.push_back(surf->m_xID); + windows.push_back(surf->xID); } - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_CLIENT_LIST_STACKING"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data()); -} - -void CXWM::updateWorkArea(int x, int y, int w, int h) { - if (!g_pXWayland || !g_pXWayland->m_wm || !g_pXWayland->m_wm->getConnection() || !m_screen || !m_screen->root) - return; - auto connection = g_pXWayland->m_wm->getConnection(); - - if (w <= 0 || h <= 0) { - xcb_delete_property(connection, m_screen->root, HYPRATOMS["_NET_WORKAREA"]); - xcb_flush(connection); - return; - } - - uint32_t values[4] = {sc(x), sc(y), sc(w), sc(h)}; - xcb_change_property(connection, XCB_PROP_MODE_REPLACE, m_screen->root, HYPRATOMS["_NET_WORKAREA"], XCB_ATOM_CARDINAL, 32, 4, values); - xcb_flush(connection); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_CLIENT_LIST_STACKING"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data()); } bool CXWM::isWMWindow(xcb_window_t w) { - return w == m_wmWindow || w == m_clipboard.window || w == m_dndSelection.window; + return w == wmWindow || w == clipboard.window || w == dndSelection.window; } void CXWM::updateOverrideRedirect(SP surf, bool overrideRedirect) { - if (!surf || surf->m_overrideRedirect == overrideRedirect) + if (!surf || surf->overrideRedirect == overrideRedirect) return; - surf->m_overrideRedirect = overrideRedirect; + surf->overrideRedirect = overrideRedirect; } void CXWM::initSelection() { - const uint32_t windowMask = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; - const uint32_t xfixesMask = + clipboard.window = xcb_generate_id(connection); + uint32_t mask[1] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; + xcb_create_window(connection, XCB_COPY_FROM_PARENT, clipboard.window, screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, XCB_CW_EVENT_MASK, + mask); + xcb_set_selection_owner(connection, clipboard.window, HYPRATOMS["CLIPBOARD_MANAGER"], XCB_TIME_CURRENT_TIME); + + uint32_t mask2 = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; + xcb_xfixes_select_selection_input(connection, clipboard.window, HYPRATOMS["CLIPBOARD"], mask2); - auto createSelectionWindow = [&](xcb_window_t& window, const std::string& atomName, bool inputOnly = false) { - window = xcb_generate_id(getConnection()); - const uint16_t width = inputOnly ? 8192 : 10; - const uint16_t height = inputOnly ? 8192 : 10; + clipboard.listeners.setSelection = g_pSeatManager->events.setSelection.registerListener([this](std::any d) { clipboard.onSelection(); }); + clipboard.listeners.keyboardFocusChange = g_pSeatManager->events.keyboardFocusChange.registerListener([this](std::any d) { clipboard.onKeyboardFocus(); }); - xcb_create_window(getConnection(), XCB_COPY_FROM_PARENT, window, m_screen->root, 0, 0, width, height, 0, - inputOnly ? XCB_WINDOW_CLASS_INPUT_ONLY : XCB_WINDOW_CLASS_INPUT_OUTPUT, m_screen->root_visual, XCB_CW_EVENT_MASK, &windowMask); + dndSelection.window = xcb_generate_id(connection); + xcb_create_window(connection, XCB_COPY_FROM_PARENT, dndSelection.window, screen->root, 0, 0, 8192, 8192, 0, XCB_WINDOW_CLASS_INPUT_ONLY, screen->root_visual, XCB_CW_EVENT_MASK, + mask); - if (!inputOnly) { - xcb_set_selection_owner(getConnection(), window, HYPRATOMS[atomName], XCB_TIME_CURRENT_TIME); - xcb_xfixes_select_selection_input(getConnection(), window, HYPRATOMS[atomName], xfixesMask); - } - - return window; - }; - - createSelectionWindow(m_clipboard.window, "CLIPBOARD_MANAGER"); - createSelectionWindow(m_clipboard.window, "CLIPBOARD"); - m_clipboard.listeners.setSelection = g_pSeatManager->m_events.setSelection.listen([this] { m_clipboard.onSelection(); }); - m_clipboard.listeners.keyboardFocusChange = g_pSeatManager->m_events.keyboardFocusChange.listen([this] { m_clipboard.onKeyboardFocus(); }); - - createSelectionWindow(m_primarySelection.window, "PRIMARY"); - m_primarySelection.listeners.setSelection = g_pSeatManager->m_events.setPrimarySelection.listen([this] { m_primarySelection.onSelection(); }); - m_primarySelection.listeners.keyboardFocusChange = g_pSeatManager->m_events.keyboardFocusChange.listen([this] { m_primarySelection.onKeyboardFocus(); }); - - createSelectionWindow(m_dndSelection.window, "XdndAware", true); - const uint32_t xdndVersion = XDND_VERSION; - xcb_change_property(getConnection(), XCB_PROP_MODE_REPLACE, m_dndSelection.window, HYPRATOMS["XdndAware"], XCB_ATOM_ATOM, 32, 1, &xdndVersion); + uint32_t val1 = XDND_VERSION; + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, dndSelection.window, HYPRATOMS["XdndAware"], XCB_ATOM_ATOM, 32, 1, &val1); } void CXWM::setClipboardToWayland(SXSelection& sel) { auto source = makeShared(sel); if (source->mimes().empty()) { - Log::logger->log(Log::ERR, "[xwm] can't set selection: no MIMEs"); + Debug::log(ERR, "[xwm] can't set clipboard: no MIMEs"); return; } sel.dataSource = source; - Log::logger->log(Log::DEBUG, "[xwm] X selection at {:x} takes {}", rc(sel.dataSource.get()), (&sel == &m_clipboard) ? "clipboard" : "primary selection"); - - if (&sel == &m_clipboard) - g_pSeatManager->setCurrentSelection(sel.dataSource); - else if (&sel == &m_primarySelection) - g_pSeatManager->setCurrentPrimarySelection(sel.dataSource); + Debug::log(LOG, "[xwm] X clipboard at {:x} takes clipboard", (uintptr_t)sel.dataSource.get()); + g_pSeatManager->setCurrentSelection(sel.dataSource); } static int writeDataSource(int fd, uint32_t mask, void* data) { - auto selection = sc(data); + auto selection = (SXSelection*)data; return selection->onWrite(); } void CXWM::getTransferData(SXSelection& sel) { - Log::logger->log(Log::DEBUG, "[xwm] getTransferData"); + Debug::log(LOG, "[xwm] getTransferData"); auto it = std::ranges::find_if(sel.transfers, [](const auto& t) { return !t->propertyReply; }); if (it == sel.transfers.end()) { - Log::logger->log(Log::ERR, "[xwm] No pending transfer found"); + Debug::log(ERR, "[xwm] No pending transfer found"); return; } auto& transfer = *it; if (!transfer || !transfer->incomingWindow) { - Log::logger->log(Log::ERR, "[xwm] Invalid transfer state"); + Debug::log(ERR, "[xwm] Invalid transfer state"); sel.transfers.erase(it); return; } if (!transfer->getIncomingSelectionProp(true)) { - Log::logger->log(Log::ERR, "[xwm] Failed to get property data"); + Debug::log(ERR, "[xwm] Failed to get property data"); sel.transfers.erase(it); return; } if (!transfer->propertyReply) { - Log::logger->log(Log::ERR, "[xwm] No property reply"); + Debug::log(ERR, "[xwm] No property reply"); sel.transfers.erase(it); return; } @@ -1331,251 +1228,162 @@ void CXWM::getTransferData(SXSelection& sel) { if (transfer->propertyReply->type == HYPRATOMS["INCR"]) { transfer->incremental = true; transfer->propertyStart = 0; - free(transfer->propertyReply); // NOLINT(cppcoreguidelines-no-malloc) + free(transfer->propertyReply); transfer->propertyReply = nullptr; return; } - // Store window ID before onWrite() - transfer may be erased during the call - const xcb_window_t targetWindow = transfer->incomingWindow; - int writeResult = sel.onWrite(); + const size_t pos = std::distance(sel.transfers.begin(), it); + sel.onWrite(); - if (writeResult != 1) + if (pos >= sel.transfers.size()) return; - // Re-find the transfer by window ID (safe after potential vector modification) - auto updatedIt = std::ranges::find_if(sel.transfers, [targetWindow](const auto& t) { return t->incomingWindow == targetWindow; }); - if (updatedIt == sel.transfers.end()) + auto newIt = sel.transfers.begin() + pos; + if (newIt == sel.transfers.end() || !(*newIt)) return; - auto& updatedTransfer = *updatedIt; - if (!updatedTransfer) - return; - - if (updatedTransfer->eventSource || updatedTransfer->wlFD.get() == -1) - return; - - updatedTransfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, updatedTransfer->wlFD.get(), WL_EVENT_WRITABLE, ::writeDataSource, &sel); + auto& updatedTransfer = *newIt; + if (updatedTransfer->eventSource && updatedTransfer->wlFD.get() != -1) + updatedTransfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, updatedTransfer->wlFD.get(), WL_EVENT_WRITABLE, ::writeDataSource, &sel); } void CXWM::setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot) { - if (!m_renderFormatID) { - Log::logger->log(Log::ERR, "[xwm] can't set cursor: no render format"); + if (!render_format_id) { + Debug::log(ERR, "[xwm] can't set cursor: no render format"); return; } - if (m_cursorXID) - xcb_free_cursor(getConnection(), m_cursorXID); + if (cursorXID) + xcb_free_cursor(connection, cursorXID); constexpr int CURSOR_DEPTH = 32; - xcb_pixmap_t pix = xcb_generate_id(getConnection()); - xcb_create_pixmap(getConnection(), CURSOR_DEPTH, pix, m_screen->root, size.x, size.y); + xcb_pixmap_t pix = xcb_generate_id(connection); + xcb_create_pixmap(connection, CURSOR_DEPTH, pix, screen->root, size.x, size.y); - xcb_render_picture_t pic = xcb_generate_id(getConnection()); - xcb_render_create_picture(getConnection(), pic, pix, m_renderFormatID, 0, nullptr); + xcb_render_picture_t pic = xcb_generate_id(connection); + xcb_render_create_picture(connection, pic, pix, render_format_id, 0, 0); - xcb_gcontext_t gc = xcb_generate_id(getConnection()); - xcb_create_gc(getConnection(), gc, pix, 0, nullptr); + xcb_gcontext_t gc = xcb_generate_id(connection); + xcb_create_gc(connection, gc, pix, 0, nullptr); - xcb_put_image(getConnection(), XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, size.x, size.y, 0, 0, 0, CURSOR_DEPTH, stride * size.y * sizeof(uint8_t), pixData); - xcb_free_gc(getConnection(), gc); + xcb_put_image(connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, size.x, size.y, 0, 0, 0, CURSOR_DEPTH, stride * size.y * sizeof(uint8_t), pixData); + xcb_free_gc(connection, gc); - m_cursorXID = xcb_generate_id(getConnection()); - xcb_render_create_cursor(getConnection(), m_cursorXID, pic, hotspot.x, hotspot.y); - xcb_free_pixmap(getConnection(), pix); - xcb_render_free_picture(getConnection(), pic); + cursorXID = xcb_generate_id(connection); + xcb_render_create_cursor(connection, cursorXID, pic, hotspot.x, hotspot.y); + xcb_free_pixmap(connection, pix); + xcb_render_free_picture(connection, pic); - uint32_t values[] = {m_cursorXID}; - xcb_change_window_attributes(getConnection(), m_screen->root, XCB_CW_CURSOR, values); - xcb_flush(getConnection()); + uint32_t values[] = {cursorXID}; + xcb_change_window_attributes(connection, screen->root, XCB_CW_CURSOR, values); + xcb_flush(connection); } SP CXWM::getDataDevice() { - return m_dndDataDevice; + return dndDataDevice; } SP CXWM::createX11DataOffer(SP surf, SP source) { auto XSURF = windowForWayland(surf); if (!XSURF) { - Log::logger->log(Log::ERR, "[xwm] No xwayland surface for destination in createX11DataOffer"); + Debug::log(ERR, "[xwm] No xwayland surface for destination in createX11DataOffer"); return nullptr; } // invalidate old - g_pXWayland->m_wm->m_dndDataOffers.clear(); + g_pXWayland->pWM->dndDataOffers.clear(); - auto offer = m_dndDataOffers.emplace_back(makeShared()); - offer->m_self = offer; - offer->m_xwaylandSurface = XSURF; - offer->m_source = source; + auto offer = dndDataOffers.emplace_back(makeShared()); + offer->self = offer; + offer->xwaylandSurface = XSURF; + offer->source = source; return offer; } void SXSelection::onSelection() { - const bool isClipboard = this == &g_pXWayland->m_wm->m_clipboard; - const bool isPrimary = this == &g_pXWayland->m_wm->m_primarySelection; - - auto currentSel = g_pSeatManager->m_selection.currentSelection; - auto currentPrimSel = g_pSeatManager->m_selection.currentPrimarySelection; - - const bool isX11Clipboard = isClipboard && currentSel && currentSel->type() == DATA_SOURCE_TYPE_X11; - const bool isX11Primary = isPrimary && currentPrimSel && currentPrimSel->type() == DATA_SOURCE_TYPE_X11; - - if (isX11Clipboard || isX11Primary) + if (g_pSeatManager->selection.currentSelection && g_pSeatManager->selection.currentSelection->type() == DATA_SOURCE_TYPE_X11) return; - auto conn = g_pXWayland->m_wm->getConnection(); - - if (isClipboard && currentSel) { - xcb_set_selection_owner(conn, g_pXWayland->m_wm->m_clipboard.window, HYPRATOMS["CLIPBOARD"], XCB_TIME_CURRENT_TIME); - xcb_flush(conn); - g_pXWayland->m_wm->m_clipboard.notifyOnFocus = true; - } else if (isPrimary && currentPrimSel) { - xcb_set_selection_owner(conn, g_pXWayland->m_wm->m_primarySelection.window, HYPRATOMS["PRIMARY"], XCB_TIME_CURRENT_TIME); - xcb_flush(conn); - g_pXWayland->m_wm->m_primarySelection.notifyOnFocus = true; + if (g_pSeatManager->selection.currentSelection) { + xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->clipboard.window, HYPRATOMS["CLIPBOARD"], XCB_TIME_CURRENT_TIME); + xcb_flush(g_pXWayland->pWM->connection); + g_pXWayland->pWM->clipboard.notifyOnFocus = true; } } void SXSelection::onKeyboardFocus() { - if (!g_pSeatManager->m_state.keyboardFocusResource || g_pSeatManager->m_state.keyboardFocusResource->client() != g_pXWayland->m_server->m_xwaylandClient) + if (!g_pSeatManager->state.keyboardFocusResource || g_pSeatManager->state.keyboardFocusResource->client() != g_pXWayland->pServer->xwaylandClient) return; - - if (this == &g_pXWayland->m_wm->m_clipboard && g_pXWayland->m_wm->m_clipboard.notifyOnFocus) { + if (g_pXWayland->pWM->clipboard.notifyOnFocus) { onSelection(); - g_pXWayland->m_wm->m_clipboard.notifyOnFocus = false; - } else if (this == &g_pXWayland->m_wm->m_primarySelection && g_pXWayland->m_wm->m_primarySelection.notifyOnFocus) { - onSelection(); - g_pXWayland->m_wm->m_primarySelection.notifyOnFocus = false; + g_pXWayland->pWM->clipboard.notifyOnFocus = false; } } int SXSelection::onRead(int fd, uint32_t mask) { auto it = std::ranges::find_if(transfers, [fd](const auto& t) { return t->wlFD.get() == fd; }); - if (it == transfers.end()) { - Log::logger->log(Log::ERR, "[xwm] No transfer found for fd {}", fd); + Debug::log(ERR, "[xwm] No transfer found for fd {}", fd); return 0; } - auto& transfer = *it; - const size_t oldSize = transfer->data.size(); - transfer->data.resize(oldSize + INCR_CHUNK_SIZE); + auto& transfer = *it; + size_t pre = transfer->data.size(); + transfer->data.resize(INCR_CHUNK_SIZE + pre); - ssize_t bytesRead = read(fd, transfer->data.data() + oldSize, INCR_CHUNK_SIZE - 1); - - if (bytesRead < 0) { - Log::logger->log(Log::ERR, "[xwm] readDataSource died"); - g_pXWayland->m_wm->selectionSendNotify(&transfer->request, false); + auto len = read(fd, transfer->data.data() + pre, INCR_CHUNK_SIZE - 1); + if (len < 0) { + Debug::log(ERR, "[xwm] readDataSource died"); + g_pXWayland->pWM->selectionSendNotify(&transfer->request, false); transfers.erase(it); return 0; } - transfer->data.resize(oldSize + bytesRead); + transfer->data.resize(pre + len); - if (bytesRead == 0) { - if (transfer->data.empty()) { - Log::logger->log(Log::WARN, "[xwm] Transfer ended with zero bytes — rejecting"); - g_pXWayland->m_wm->selectionSendNotify(&transfer->request, false); - transfers.erase(it); - return 0; - } - - Log::logger->log(Log::DEBUG, "[xwm] Transfer complete, total size: {}", transfer->data.size()); - auto conn = g_pXWayland->m_wm->getConnection(); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, transfer->request.requestor, transfer->request.property, transfer->request.target, 8, transfer->data.size(), - transfer->data.data()); - - xcb_flush(conn); - g_pXWayland->m_wm->selectionSendNotify(&transfer->request, true); + if (len == 0) { + Debug::log(LOG, "[xwm] Received all the bytes, final length {}", transfer->data.size()); + xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, transfer->request.requestor, transfer->request.property, transfer->request.target, 8, + transfer->data.size(), transfer->data.data()); + xcb_flush(g_pXWayland->pWM->connection); + g_pXWayland->pWM->selectionSendNotify(&transfer->request, true); transfers.erase(it); } else - Log::logger->log(Log::DEBUG, "[xwm] Received {} bytes, awaiting more...", bytesRead); + Debug::log(LOG, "[xwm] Received {} bytes, waiting...", len); return 1; } static int readDataSource(int fd, uint32_t mask, void* data) { - Log::logger->log(Log::DEBUG, "[xwm] readDataSource on fd {}", fd); + Debug::log(LOG, "[xwm] readDataSource on fd {}", fd); - auto selection = sc(data); + auto selection = (SXSelection*)data; return selection->onRead(fd, mask); } bool SXSelection::sendData(xcb_selection_request_event_t* e, std::string mime) { WP selection; - if (this == &g_pXWayland->m_wm->m_clipboard) - selection = g_pSeatManager->m_selection.currentSelection; - else if (this == &g_pXWayland->m_wm->m_primarySelection) - selection = g_pSeatManager->m_selection.currentPrimarySelection; - else if (!g_pXWayland->m_wm->m_dndDataOffers.empty()) - selection = g_pXWayland->m_wm->m_dndDataOffers.at(0)->getSource(); + if (this == &g_pXWayland->pWM->clipboard) + selection = g_pSeatManager->selection.currentSelection; + else if (!g_pXWayland->pWM->dndDataOffers.empty()) + selection = g_pXWayland->pWM->dndDataOffers.at(0)->getSource(); - if (!selection) { - Log::logger->log(Log::ERR, "[xwm] sendData: no selection source available"); + if (!selection) return false; - } const auto MIMES = selection->mimes(); - if (MIMES.empty()) { - Log::logger->log(Log::ERR, "[xwm] sendData: selection source has no mimes"); + if (MIMES.empty()) return false; - } - if (std::ranges::find(MIMES, mime) == MIMES.end()) { - // try to guess mime, don't just blindly send random-ass shit that the app will have no fucking - // clue what to do with - Log::logger->log(Log::ERR, "[xwm] X client asked for MIME '{}' that this selection doesn't support, guessing.", mime); - - auto needle = mime; - auto selectedMime = *MIMES.begin(); - if (mime.contains('/')) - needle = mime.substr(0, mime.find('/')); - - Log::logger->log(Log::TRACE, "[xwm] X MIME needle '{}'", needle); - - if (Env::isTrace()) { - std::string mimeList = ""; - for (const auto& m : MIMES) { - mimeList += "'" + m + "', "; - } - - if (!MIMES.empty()) - mimeList = mimeList.substr(0, mimeList.size() - 2); - - Log::logger->log(Log::TRACE, "[xwm] X MIME supported: {}", mimeList); - } - - bool found = false; - - for (const auto& m : MIMES) { - if (m.starts_with(needle)) { - selectedMime = m; - Log::logger->log(Log::TRACE, "[xwm] X MIME needle found type '{}'", m); - found = true; - break; - } - } - - if (!found) { - for (const auto& m : MIMES) { - if (m.contains(needle)) { - selectedMime = m; - Log::logger->log(Log::TRACE, "[xwm] X MIME needle found type '{}'", m); - found = true; - break; - } - } - } - - Log::logger->log(Log::ERR, "[xwm] Guessed mime: '{}'. Hopefully we're right enough.", selectedMime); - - mime = selectedMime; + if (std::find(MIMES.begin(), MIMES.end(), mime) == MIMES.end()) { + Debug::log(ERR, "[xwm] X Client asked for an invalid MIME, sending the first advertised. THIS SHIT MAY BREAK!"); + mime = *MIMES.begin(); } auto transfer = makeUnique(*this); @@ -1583,98 +1391,82 @@ bool SXSelection::sendData(xcb_selection_request_event_t* e, std::string mime) { int p[2]; if (pipe(p) == -1) { - Log::logger->log(Log::ERR, "[xwm] sendData: pipe() failed"); + Debug::log(ERR, "[xwm] selection: pipe() failed"); return false; } fcntl(p[0], F_SETFD, FD_CLOEXEC); fcntl(p[0], F_SETFL, O_NONBLOCK); fcntl(p[1], F_SETFD, FD_CLOEXEC); - // Do NOT set O_NONBLOCK on p[1] (wayland clients may block) + // the wayland client might not expect a non-blocking fd + // fcntl(p[1], F_SETFL, O_NONBLOCK); transfer->wlFD = CFileDescriptor{p[0]}; - Log::logger->log(Log::DEBUG, "[xwm] sending wayland selection to xwayland with mime {}, target {}, fds {} {}", mime, e->target, p[0], p[1]); + Debug::log(LOG, "[xwm] sending wayland selection to xwayland with mime {}, target {}, fds {} {}", mime, e->target, p[0], p[1]); selection->send(mime, CFileDescriptor{p[1]}); - transfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, transfer->wlFD.get(), WL_EVENT_READABLE, ::readDataSource, this); - + transfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, transfer->wlFD.get(), WL_EVENT_READABLE, ::readDataSource, this); transfers.emplace_back(std::move(transfer)); + return true; } int SXSelection::onWrite() { auto it = std::ranges::find_if(transfers, [](const auto& t) { return t->propertyReply; }); if (it == transfers.end()) { - Log::logger->log(Log::ERR, "[xwm] No transfer with property data found"); + Debug::log(ERR, "[xwm] No transfer with property data found"); return 0; } auto& transfer = *it; - char* property = sc(xcb_get_property_value(transfer->propertyReply)); + char* property = (char*)xcb_get_property_value(transfer->propertyReply); int remainder = xcb_get_property_value_length(transfer->propertyReply) - transfer->propertyStart; ssize_t len = write(transfer->wlFD.get(), property + transfer->propertyStart, remainder); if (len == -1) { if (errno == EAGAIN) return 1; - Log::logger->log(Log::ERR, "[xwm] write died in transfer get"); + Debug::log(ERR, "[xwm] write died in transfer get"); transfers.erase(it); return 0; } if (len < remainder) { transfer->propertyStart += len; - Log::logger->log(Log::DEBUG, "[xwm] wl client read partially: len {}", len); + Debug::log(LOG, "[xwm] wl client read partially: len {}", len); } else { - Log::logger->log(Log::DEBUG, "[xwm] cb transfer to wl client complete, read {} bytes", len); + Debug::log(LOG, "[xwm] cb transfer to wl client complete, read {} bytes", len); if (!transfer->incremental) { transfers.erase(it); - return 0; } else { - free(transfer->propertyReply); // NOLINT(cppcoreguidelines-no-malloc) + free(transfer->propertyReply); transfer->propertyReply = nullptr; transfer->propertyStart = 0; - if (transfer->eventSource) { - wl_event_source_remove(transfer->eventSource); - transfer->eventSource = nullptr; - } - return 0; } } return 1; } -void SXSelection::removeTransfer(xcb_window_t window) { - std::erase_if(transfers, [window](const auto& t) { return t->incomingWindow == window; }); -} - -void CXWM::removeTransfersForWindow(xcb_window_t window) { - m_clipboard.removeTransfer(window); - m_primarySelection.removeTransfer(window); - m_dndSelection.removeTransfer(window); -} - SXTransfer::~SXTransfer() { if (eventSource) wl_event_source_remove(eventSource); if (incomingWindow) - xcb_destroy_window(*g_pXWayland->m_wm->m_connection, incomingWindow); + xcb_destroy_window(g_pXWayland->pWM->connection, incomingWindow); if (propertyReply) - free(propertyReply); // NOLINT(cppcoreguidelines-no-malloc) + free(propertyReply); } bool SXTransfer::getIncomingSelectionProp(bool erase) { - xcb_get_property_cookie_t cookie = - xcb_get_property(*g_pXWayland->m_wm->m_connection, erase, incomingWindow, HYPRATOMS["_WL_SELECTION"], XCB_GET_PROPERTY_TYPE_ANY, 0, 0x1fffffff); + xcb_get_property_cookie_t cookie = xcb_get_property(g_pXWayland->pWM->connection, erase, incomingWindow, HYPRATOMS["_WL_SELECTION"], XCB_GET_PROPERTY_TYPE_ANY, 0, 0x1fffffff); propertyStart = 0; - propertyReply = xcb_get_property_reply(*g_pXWayland->m_wm->m_connection, cookie, nullptr); + propertyReply = xcb_get_property_reply(g_pXWayland->pWM->connection, cookie, nullptr); if (!propertyReply) { - Log::logger->log(Log::ERR, "[SXTransfer] couldn't get a prop reply"); + Debug::log(ERR, "[SXTransfer] couldn't get a prop reply"); return false; } diff --git a/src/xwayland/XWM.hpp b/src/xwayland/XWM.hpp index af1fa06a..efd2f85a 100644 --- a/src/xwayland/XWM.hpp +++ b/src/xwayland/XWM.hpp @@ -11,8 +11,6 @@ #include #include #include -#include // for PRIxPTR -#include struct wl_event_source; class CXWaylandSurfaceResource; @@ -35,9 +33,9 @@ struct SXTransfer { xcb_selection_request_event_t request; - int propertyStart = 0; - xcb_get_property_reply_t* propertyReply = nullptr; - xcb_window_t incomingWindow = 0; + int propertyStart; + xcb_get_property_reply_t* propertyReply; + xcb_window_t incomingWindow; bool getIncomingSelectionProp(bool erase); }; @@ -54,7 +52,6 @@ struct SXSelection { bool sendData(xcb_selection_request_event_t* e, std::string mime); int onRead(int fd, uint32_t mask); int onWrite(); - void removeTransfer(xcb_window_t window); struct { CHyprSignalListener setSelection; @@ -66,49 +63,45 @@ struct SXSelection { class CXCBConnection { public: - CXCBConnection(int fd) : m_connection{xcb_connect_to_fd(fd, nullptr)} { + CXCBConnection(int fd) : connection{xcb_connect_to_fd(fd, nullptr)} { ; } ~CXCBConnection() { - if (m_connection) { - Log::logger->log(Log::DEBUG, "Disconnecting XCB connection {:x}", rc(m_connection)); - xcb_disconnect(m_connection); - m_connection = nullptr; - } else - Log::logger->log(Log::ERR, "Double xcb_disconnect attempt"); + if (connection) + xcb_disconnect(connection); } bool hasError() const { - return xcb_connection_has_error(m_connection); + return xcb_connection_has_error(connection); } operator xcb_connection_t*() const { - return m_connection; + return connection; } private: - xcb_connection_t* m_connection = nullptr; + xcb_connection_t* connection = nullptr; }; class CXCBErrorContext { public: explicit CXCBErrorContext(xcb_connection_t* connection) { - if (xcb_errors_context_new(connection, &m_errors) != 0) - m_errors = nullptr; + if (xcb_errors_context_new(connection, &errors) != 0) + errors = nullptr; } ~CXCBErrorContext() { - if (m_errors) - xcb_errors_context_free(m_errors); + if (errors) + xcb_errors_context_free(errors); } bool isValid() const { - return m_errors != nullptr; + return errors != nullptr; } private: - xcb_errors_context_t* m_errors = nullptr; + xcb_errors_context_t* errors = nullptr; }; class CXWM { @@ -119,7 +112,6 @@ class CXWM { int onEvent(int fd, uint32_t mask); SP getDataDevice(); SP createX11DataOffer(SP surf, SP source); - void updateWorkArea(int x, int y, int w, int h); private: void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot); @@ -165,8 +157,6 @@ class CXWM { void handleFocusOut(xcb_focus_out_event_t* e); void handleError(xcb_value_error_t* e); - void removeTransfersForWindow(xcb_window_t window); - bool handleSelectionEvent(xcb_generic_event_t* e); void handleSelectionNotify(xcb_selection_notify_event_t* e); bool handleSelectionPropertyNotify(xcb_property_notify_event_t* e); @@ -184,45 +174,41 @@ class CXWM { SXSelection* getSelection(xcb_atom_t atom); // - UP m_connection; - xcb_errors_context_t* m_errors = nullptr; - xcb_screen_t* m_screen = nullptr; + CXCBConnection connection; + xcb_errors_context_t* errors = nullptr; + xcb_screen_t* screen = nullptr; - xcb_window_t m_wmWindow; + xcb_window_t wmWindow; - wl_event_source* m_eventSource = nullptr; + wl_event_source* eventSource = nullptr; - const xcb_query_extension_reply_t* m_xfixes = nullptr; - const xcb_query_extension_reply_t* m_xres = nullptr; - int m_xfixesMajor = 0; + const xcb_query_extension_reply_t* xfixes = nullptr; + const xcb_query_extension_reply_t* xres = nullptr; + int xfixesMajor = 0; - xcb_visualid_t m_visualID; - xcb_colormap_t m_colormap; - uint32_t m_cursorXID = 0; + xcb_visualid_t visual_id; + xcb_colormap_t colormap; + uint32_t cursorXID = 0; - xcb_render_pictformat_t m_renderFormatID; + xcb_render_pictformat_t render_format_id; - std::vector> m_shellResources; - std::vector> m_surfaces; - std::vector> m_mappedSurfaces; // ordered by map time - std::vector> m_mappedSurfacesStacking; // ordered by stacking + std::vector> shellResources; + std::vector> surfaces; + std::vector> mappedSurfaces; // ordered by map time + std::vector> mappedSurfacesStacking; // ordered by stacking - WP m_focusedSurface; - uint64_t m_lastFocusSeq = 0; + WP focusedSurface; + uint64_t lastFocusSeq = 0; - SXSelection m_clipboard; - SXSelection m_primarySelection; - SXSelection m_dndSelection; - SP m_dndDataDevice = makeShared(); - std::vector> m_dndDataOffers; + SXSelection clipboard; + SXSelection dndSelection; + SP dndDataDevice = makeShared(); + std::vector> dndDataOffers; - inline xcb_connection_t* getConnection() { - return m_connection ? *m_connection : nullptr; - } struct { CHyprSignalListener newWLSurface; CHyprSignalListener newXShellSurface; - } m_listeners; + } listeners; friend class CXWaylandSurface; friend class CXWayland; diff --git a/src/xwayland/XWayland.cpp b/src/xwayland/XWayland.cpp index a022217a..9d2a0ea4 100644 --- a/src/xwayland/XWayland.cpp +++ b/src/xwayland/XWayland.cpp @@ -1,15 +1,15 @@ #include "XWayland.hpp" #include "../Compositor.hpp" -#include "../debug/log/Logger.hpp" +#include "../debug/Log.hpp" #include "../helpers/fs/FsUtils.hpp" CXWayland::CXWayland(const bool wantsEnabled) { #ifndef NO_XWAYLAND // Disable Xwayland and clean up if the user disabled it. if (!wantsEnabled) { - Log::logger->log(Log::DEBUG, "XWayland has been disabled, cleaning up..."); - for (auto& w : g_pCompositor->m_windows) { - if (!w->m_isX11) + Debug::log(LOG, "XWayland has been disabled, cleaning up..."); + for (auto& w : g_pCompositor->m_vWindows) { + if (!w->m_bIsX11) continue; g_pCompositor->closeWindow(w); } @@ -20,33 +20,33 @@ CXWayland::CXWayland(const bool wantsEnabled) { if (!NFsUtils::executableExistsInPath("Xwayland")) { // If Xwayland doesn't exist, don't try to start it. - Log::logger->log(Log::DEBUG, "Unable to find XWayland; not starting it."); + Debug::log(LOG, "Unable to find XWayland; not starting it."); return; } - Log::logger->log(Log::DEBUG, "Starting up the XWayland server"); + Debug::log(LOG, "Starting up the XWayland server"); - m_server = makeUnique(); + pServer = makeUnique(); - if (!m_server->create()) { - Log::logger->log(Log::ERR, "XWayland failed to start: it will not work."); + if (!pServer->create()) { + Debug::log(ERR, "XWayland failed to start: it will not work."); return; } m_enabled = true; #else - Log::logger->log(Log::DEBUG, "Not starting XWayland: disabled at compile time"); + Debug::log(LOG, "Not starting XWayland: disabled at compile time"); #endif } void CXWayland::setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot) { #ifndef NO_XWAYLAND - if (!m_wm) { - Log::logger->log(Log::ERR, "Couldn't set XCursor: no XWM yet"); + if (!pWM) { + Debug::log(ERR, "Couldn't set XCursor: no XWM yet"); return; } - m_wm->setCursor(pixData, stride, size, hotspot); + pWM->setCursor(pixData, stride, size, hotspot); #endif } diff --git a/src/xwayland/XWayland.hpp b/src/xwayland/XWayland.hpp index 7fd6e354..9202f19e 100644 --- a/src/xwayland/XWayland.hpp +++ b/src/xwayland/XWayland.hpp @@ -19,13 +19,17 @@ class CXWayland { CXWayland(const bool wantsEnabled); #ifndef NO_XWAYLAND - UP m_server; - UP m_wm; + UP pServer; + UP pWM; #endif bool enabled(); void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot); + struct { + CSignal newSurface; + } events; + private: bool m_enabled = false; }; diff --git a/start/CMakeLists.txt b/start/CMakeLists.txt deleted file mode 100644 index 00b1fded..00000000 --- a/start/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -project(start-hyprland DESCRIPTION "Hyprland watchdog binary") - -include(GNUInstallDirs) - -set(CMAKE_CXX_STANDARD 26) -set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) - -find_package(PkgConfig REQUIRED) - -pkg_check_modules(starthyprland_deps REQUIRED IMPORTED_TARGET hyprutils>=0.10.3) - -file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") - -add_executable(start-hyprland ${SRCFILES}) - -target_link_libraries(start-hyprland PUBLIC PkgConfig::starthyprland_deps) - -install(TARGETS start-hyprland) - -if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) - set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE) -endif() diff --git a/start/src/core/Instance.cpp b/start/src/core/Instance.cpp deleted file mode 100644 index 2f5007bd..00000000 --- a/start/src/core/Instance.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "Instance.hpp" -#include "State.hpp" -#include "../helpers/Logger.hpp" -#include "../helpers/Nix.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__linux__) -#include -#elif defined(__FreeBSD__) -#include -#include -#endif - -#include - -using namespace Hyprutils::OS; -using namespace std::string_literals; - -// -void CHyprlandInstance::runHyprlandThread(bool safeMode) { - std::vector argsStd; - argsStd.emplace_back("--watchdog-fd"); - argsStd.emplace_back(std::format("{}", m_toHlPid.get())); - if (safeMode) - argsStd.emplace_back("--safe-mode"); - - for (const auto& a : g_state->rawArgvNoBinPath) { - argsStd.emplace_back(a); - } - - // spawn a process manually. Hyprutils' Async is detached, while Sync redirects stdout - // TODO: make Sync respect fds? - - std::vector args = {strdup(g_state->customPath.value_or("Hyprland").c_str())}; - for (const auto& a : argsStd) { - args.emplace_back(strdup(a.c_str())); - } - args.emplace_back(nullptr); - - int forkRet = fork(); - if (forkRet == 0) { - // Make hyprland die on our SIGKILL -#if defined(__linux__) - prctl(PR_SET_PDEATHSIG, SIGKILL); -#elif defined(__FreeBSD__) - int sig = SIGKILL; - procctl(P_PID, getpid(), PROC_PDEATHSIG_CTL, &sig); -#endif - - if (Nix::shouldUseNixGL()) { - argsStd.insert(argsStd.begin(), g_state->customPath.value_or("Hyprland")); - args.insert(args.begin(), strdup(argsStd.front().c_str())); - execvp("nixGL", args.data()); - } else - execvp(g_state->customPath.value_or("Hyprland").c_str(), args.data()); - - g_logger->log(Hyprutils::CLI::LOG_ERR, "fork(): execvp failed: {}", strerror(errno)); - std::fflush(stdout); - exit(1); - } else - m_hlPid = forkRet; - - m_hlThread = std::thread([this] { - while (true) { - int status = 0; - int ret = waitpid(m_hlPid, &status, 0); - if (ret == -1) { - g_logger->log(Hyprutils::CLI::LOG_ERR, "Couldn't waitpid for hyprland: {}", strerror(errno)); - break; - } - - if (WIFEXITED(status)) - break; - } - - write(m_wakeupWrite.get(), "vax", 3); - - std::fflush(stdout); - std::fflush(stderr); - }); -} - -void CHyprlandInstance::forceQuit() { - m_hyprlandExiting = true; - kill(m_hlPid, SIGTERM); // gracefully, can get stuck but it's unlikely - - m_hlThread.join(); // needs this otherwise can crash -} - -void CHyprlandInstance::clearFd(const Hyprutils::OS::CFileDescriptor& fd) { - static std::array buf; - read(fd.get(), buf.data(), 1023); -} - -void CHyprlandInstance::dispatchHyprlandEvent() { - std::string recvd = ""; - static std::array buf; - ssize_t n = read(m_fromHlPid.get(), buf.data(), 4096); - if (n < 0) { - g_logger->log(Hyprutils::CLI::LOG_ERR, "Failed dispatching hl events"); - return; - } - - recvd.append(buf.data(), n); - - if (recvd.empty()) - return; - - for (const auto& s : std::views::split(recvd, '\n')) { - const std::string_view sv = std::string_view{s}; - if (sv == "vax") { - // init passed - m_hyprlandInitialized = true; - continue; - } - - if (sv == "end") { - // exiting - m_hyprlandExiting = true; - continue; - } - } -} - -bool CHyprlandInstance::run(bool safeMode) { - int pipefds[2]; - pipe(pipefds); - - m_fromHlPid = CFileDescriptor{pipefds[0]}; - m_toHlPid = CFileDescriptor{pipefds[1]}; - - pipe(pipefds); - - m_wakeupRead = CFileDescriptor{pipefds[0]}; - m_wakeupWrite = CFileDescriptor{pipefds[1]}; - - runHyprlandThread(safeMode); - - pollfd pollfds[2] = { - { - .fd = m_wakeupRead.get(), - .events = POLLIN, - .revents = 0, - }, - { - .fd = m_fromHlPid.get(), - .events = POLLIN, - .revents = 0, - }, - }; - - while (true) { - int ret = poll(pollfds, 2, -1); - - if (ret < 0) { - g_logger->log(Hyprutils::CLI::LOG_ERR, "poll() failed, exiting"); - exit(1); - } - - if (pollfds[1].revents & POLLIN) { - g_logger->log(Hyprutils::CLI::LOG_DEBUG, "got an event from hyprland"); - dispatchHyprlandEvent(); - continue; - } - - if (pollfds[0].revents & POLLIN) { - g_logger->log(Hyprutils::CLI::LOG_DEBUG, "hyprland exit, breaking poll, checking state"); - clearFd(m_wakeupRead); - break; - } - } - - m_hlThread.join(); - - return !m_hyprlandInitialized || m_hyprlandExiting; -} diff --git a/start/src/core/Instance.hpp b/start/src/core/Instance.hpp deleted file mode 100644 index 2c72dc12..00000000 --- a/start/src/core/Instance.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -#include "../helpers/Memory.hpp" - -class CHyprlandInstance { - public: - CHyprlandInstance() = default; - ~CHyprlandInstance() = default; - - CHyprlandInstance(const CHyprlandInstance&) = delete; - CHyprlandInstance(CHyprlandInstance&) = delete; - CHyprlandInstance(CHyprlandInstance&&) = delete; - - bool run(bool safeMode = false); // if returns false, restart. - void forceQuit(); - - private: - void runHyprlandThread(bool safeMode); - void clearFd(const Hyprutils::OS::CFileDescriptor& fd); - void dispatchHyprlandEvent(); - - int m_hlPid = -1; - - Hyprutils::OS::CFileDescriptor m_fromHlPid, m_toHlPid; - Hyprutils::OS::CFileDescriptor m_wakeupRead, m_wakeupWrite; - - bool m_hyprlandInitialized = false; - bool m_hyprlandExiting = false; - - std::thread m_hlThread; -}; - -inline UP g_instance; diff --git a/start/src/core/State.hpp b/start/src/core/State.hpp deleted file mode 100644 index 6a44c8d0..00000000 --- a/start/src/core/State.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "../helpers/Memory.hpp" - -#include -#include - -struct SState { - std::span rawArgvNoBinPath; - std::optional customPath; - bool noNixGl = false; - bool forceNixGl = false; -}; - -inline UP g_state = makeUnique(); \ No newline at end of file diff --git a/start/src/helpers/Logger.hpp b/start/src/helpers/Logger.hpp deleted file mode 100644 index ae771203..00000000 --- a/start/src/helpers/Logger.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -#include "Memory.hpp" - -// we do this to add a from start-hyprland to the logs -inline UP g_loggerMain = makeUnique(); -inline UP g_logger; diff --git a/start/src/helpers/Memory.hpp b/start/src/helpers/Memory.hpp deleted file mode 100644 index 66ba2c1f..00000000 --- a/start/src/helpers/Memory.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -using namespace Hyprutils::Memory; - -template -using SP = Hyprutils::Memory::CSharedPointer; -template -using WP = Hyprutils::Memory::CWeakPointer; -template -using UP = Hyprutils::Memory::CUniquePointer; -template -using ASP = Hyprutils::Memory::CAtomicSharedPointer; diff --git a/start/src/helpers/Nix.cpp b/start/src/helpers/Nix.cpp deleted file mode 100644 index da66183e..00000000 --- a/start/src/helpers/Nix.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "Nix.hpp" - -#include "Logger.hpp" -#include "../core/State.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -using namespace Hyprutils::String; -using namespace Hyprutils::OS; - -using namespace Hyprutils::File; - -static std::optional getFromEtcOsRelease(const std::string_view& sv) { - static std::string content = ""; - static bool once = true; - - if (once) { - once = false; - - auto read = readFileAsString("/etc/os-release"); - content = read.value_or(""); - } - - static CVarList2 vars(std::move(content), 0, '\n', true); - - for (const auto& v : vars) { - if (v.starts_with(sv) && v.contains('=')) { - // found - auto value = trim(v.substr(v.find('=') + 1)); - - if (value.back() == value.front() && value.back() == '"') - value = value.substr(1, value.size() - 2); - - return std::string{value}; - } - } - - return std::nullopt; -} - -static bool executableExistsInPath(const std::string& exe) { - const char* PATHENV = std::getenv("PATH"); - if (!PATHENV) - return false; - - CVarList2 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; -} - -std::expected Nix::nixEnvironmentOk() { - if (!shouldUseNixGL()) - return {}; - - if (!executableExistsInPath("nixGL")) - return std::unexpected( - "Hyprland was installed using Nix, but you're not on NixOS. This requires nixGL to be installed as well.\nYou can install nixGL by running \"nix profile install " - "github:guibou/nixGL --impure\" in your terminal."); - - return {}; -} - -bool Nix::shouldUseNixGL() { - if (g_state->forceNixGl) - return true; - - if (g_state->noNixGl) - return false; - - // check if installed hyprland is nix'd - CProcess proc("Hyprland", {"--version-json"}); - if (!proc.runSync()) { - g_logger->log(Hyprutils::CLI::LOG_ERR, "failed to obtain hyprland version string"); - return false; - } - - auto json = glz::read_json(proc.stdOut()); - if (!json) { - g_logger->log(Hyprutils::CLI::LOG_ERR, "failed to obtain hyprland version string (bad json)"); - return false; - } - - const auto FLAGS = (*json)["flags"].get_array(); - const bool IS_NIX = std::ranges::any_of(FLAGS, [](const auto& e) { return e.get_string() == std::string_view{"nix"}; }); - - if (IS_NIX) { - const auto NAME = getFromEtcOsRelease("NAME"); - - // Hyprland is nix: recommend nixGL iff !NIX && !NIX-OPENGL - - if (*NAME == "NixOS") - return false; - - std::error_code ec; - - if (std::filesystem::exists("/run/opengl-driver", ec) && !ec) // NOLINTNEXTLINE - return false; - - // we are not on nix / no nix opengl driver - return true; - } - - return false; -} diff --git a/start/src/helpers/Nix.hpp b/start/src/helpers/Nix.hpp deleted file mode 100644 index edc01b19..00000000 --- a/start/src/helpers/Nix.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include -#include - -namespace Nix { - std::expected nixEnvironmentOk(); - bool shouldUseNixGL(); -}; \ No newline at end of file diff --git a/start/src/main.cpp b/start/src/main.cpp deleted file mode 100644 index 45a78357..00000000 --- a/start/src/main.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include -#include - -#include "helpers/Logger.hpp" -#include "helpers/Nix.hpp" -#include "core/State.hpp" -#include "core/Instance.hpp" - -using namespace Hyprutils::CLI; - -#define ASSERT(expr) \ - if (!(expr)) { \ - g_logger->log(LOG_CRIT, "Failed assertion at line {} in {}: {} was false", __LINE__, \ - ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find("/src/") + 1); })(), #expr); \ - std::abort(); \ - } - -constexpr const char* HELP_INFO = R"#(start-hyprland - A binary to properly start Hyprland via a watchdog process. -Any arguments after -- are passed to Hyprland. For Hyprland help, run start-hyprland -- --help or Hyprland --help - -Additional arguments for start-hyprland: - --path [path] -> Override Hyprland path - --no-nixgl -> Force disable nixGL - --force-nixgl -> Force enable nixGL -)#"; - -// -static void onSignal(int sig) { - if (!g_instance) - return; - - g_instance->forceQuit(); - g_instance.reset(); - - exit(0); -} - -static void terminateChildOnSignal(int signal) { - struct sigaction sa; - sa.sa_handler = onSignal; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - int ret = sigaction(signal, &sa, nullptr); - if (ret != 0) - g_logger->log(Hyprutils::CLI::LOG_WARN, "Failed to set up handler for signal {}: {}", signal, strerror(errno)); -} - -int main(int argc, const char** argv, const char** envp) { - g_logger = makeUnique(*g_loggerMain); - g_logger->setName("start-hyprland"); - g_logger->setLogLevel(Hyprutils::CLI::LOG_DEBUG); - - terminateChildOnSignal(SIGTERM); - terminateChildOnSignal(SIGINT); - - int startArgv = -1; - - for (int i = 1; i < argc; ++i) { - std::string_view arg = argv[i]; - - if (arg == "--") { - startArgv = i + 1; - break; - } - if (arg == "-h" || arg == "--help") { - std::println("{}", HELP_INFO); - return 0; - } - if (arg == "--path" || arg == "-p") { - if (i + 1 >= argc) { - std::println("{} requires a path", arg); - return 1; - } - - g_state->customPath = argv[++i]; - continue; - } - if (arg == "--no-nixgl") { - g_state->noNixGl = true; - continue; - } - if (arg == "--force-nixgl") { - g_state->forceNixGl = true; - continue; - } - } - - if (startArgv != -1) - g_state->rawArgvNoBinPath = std::span{argv + startArgv, argc - startArgv}; - - if (!g_state->rawArgvNoBinPath.empty()) - g_logger->log(Hyprutils::CLI::LOG_WARN, "Arguments after -- are passed to Hyprland"); - - // check if our environment is OK - if (const auto RET = Nix::nixEnvironmentOk(); !RET) { - g_logger->log(Hyprutils::CLI::LOG_ERR, "Nix environment check failed:\n{}", RET.error()); - return 1; - } - - if (Nix::shouldUseNixGL()) - g_logger->log(Hyprutils::CLI::LOG_DEBUG, "Hyprland was compiled with Nix - will use nixGL"); - - bool safeMode = false; - while (true) { - g_instance = makeUnique(); - const bool RET = g_instance->run(safeMode); - g_instance.reset(); - - if (!RET) { - g_logger->log(Hyprutils::CLI::LOG_ERR, "Hyprland exit not-cleanly, restarting"); - safeMode = true; - continue; - } - - g_logger->log(Hyprutils::CLI::LOG_DEBUG, "Hyprland exit cleanly."); - break; - } - - return 0; -} diff --git a/subprojects/hyprland-protocols b/subprojects/hyprland-protocols index 3a5c2bda..755aef8d 160000 --- a/subprojects/hyprland-protocols +++ b/subprojects/hyprland-protocols @@ -1 +1 @@ -Subproject commit 3a5c2bda1c1a4e55cc1330c782547695a93f05b2 +Subproject commit 755aef8dab49d0fc4663c715fa4ad221b2aedaed diff --git a/subprojects/tracy b/subprojects/tracy index 05cceee0..37aff70d 160000 --- a/subprojects/tracy +++ b/subprojects/tracy @@ -1 +1 @@ -Subproject commit 05cceee0df3b8d7c6fa87e9638af311dbabc63cb +Subproject commit 37aff70dfa50cf6307b3fee6074d627dc2929143 diff --git a/subprojects/tracy.wrap b/subprojects/tracy.wrap new file mode 100644 index 00000000..11b21787 --- /dev/null +++ b/subprojects/tracy.wrap @@ -0,0 +1 @@ +[wrap-file] diff --git a/subprojects/udis86.wrap b/subprojects/udis86.wrap new file mode 100644 index 00000000..dfb63984 --- /dev/null +++ b/subprojects/udis86.wrap @@ -0,0 +1,5 @@ +[wrap-file] +method = cmake + +[provide] +udis86 = libudis86_dep diff --git a/systemd/hyprland-uwsm.desktop b/systemd/hyprland-uwsm.desktop index 2ea70cb6..e00f4aa2 100644 --- a/systemd/hyprland-uwsm.desktop +++ b/systemd/hyprland-uwsm.desktop @@ -1,7 +1,6 @@ [Desktop Entry] Name=Hyprland (uwsm-managed) Comment=An intelligent dynamic tiling Wayland compositor -Exec=uwsm start -e -D Hyprland hyprland.desktop -TryExec=uwsm +Exec=uwsm start -- hyprland.desktop DesktopNames=Hyprland Type=Application diff --git a/systemd/meson.build b/systemd/meson.build new file mode 100644 index 00000000..bc62e95a --- /dev/null +++ b/systemd/meson.build @@ -0,0 +1,7 @@ +if (get_option('uwsm').allowed()) + install_data( + 'hyprland-uwsm.desktop', + install_dir: join_paths(get_option('datadir'), 'wayland-sessions'), + install_tag: 'runtime', + ) +endif diff --git a/tests/desktop/Reserved.cpp b/tests/desktop/Reserved.cpp deleted file mode 100644 index b3942e32..00000000 --- a/tests/desktop/Reserved.cpp +++ /dev/null @@ -1,52 +0,0 @@ - -#include - -#include - -TEST(Desktop, reservedArea) { - Desktop::CReservedArea a{{20, 30}, {40, 50}}; - CBox box = {1000, 1000, 1000, 1000}; - a.applyip(box); - - EXPECT_EQ(box.x, 1020); - EXPECT_EQ(box.y, 1030); - EXPECT_EQ(box.w, 1000 - 20 - 40); - EXPECT_EQ(box.h, 1000 - 30 - 50); - - box = a.apply(CBox{1000, 1000, 1000, 1000}); - - EXPECT_EQ(box.x, 1020); - EXPECT_EQ(box.y, 1030); - EXPECT_EQ(box.w, 1000 - 20 - 40); - EXPECT_EQ(box.h, 1000 - 30 - 50); - - a.addType(Desktop::RESERVED_DYNAMIC_TYPE_LS, {10, 20}, {30, 40}); - - box = a.apply(CBox{1000, 1000, 1000, 1000}); - - EXPECT_EQ(box.x, 1000 + 20 + 10); - EXPECT_EQ(box.y, 1000 + 30 + 20); - EXPECT_EQ(box.w, 1000 - 20 - 40 - 10 - 30); - EXPECT_EQ(box.h, 1000 - 30 - 50 - 20 - 40); - - Desktop::CReservedArea b{CBox{10, 10, 1000, 1000}, CBox{20, 30, 900, 900}}; - - EXPECT_EQ(b.left(), 20 - 10); - EXPECT_EQ(b.top(), 30 - 10); - EXPECT_EQ(b.right(), 1010 - 920); - EXPECT_EQ(b.bottom(), 1010 - 930); - - Desktop::CReservedArea c{CBox{}, CBox{20, 30, 900, 900}}; - - EXPECT_EQ(c.left(), 0); - EXPECT_EQ(c.top(), 0); - EXPECT_EQ(c.right(), 0); - EXPECT_EQ(c.bottom(), 0); - - Desktop::CReservedArea d{CBox{20, 30, 900, 900}, CBox{}}; - - EXPECT_EQ(d.left(), 0); - EXPECT_EQ(d.top(), 0); - EXPECT_EQ(d.right(), 0); - EXPECT_EQ(d.bottom(), 0); -} \ No newline at end of file