File hyprlock-0.9.1.obscpio of Package hyprlock

07070100000000000081A4000000000000000000000001688B5FE90000070E000000000000000000000000000000000000001D00000000hyprlock-0.9.1/.clang-format---
Language: Cpp
BasedOnStyle: LLVM

AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignEscapedNewlines: Right
AlignOperands: false
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
BreakConstructorInitializers: AfterColon
ColumnLimit: 180
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
IncludeBlocks: Preserve
IndentCaseLabels: true
IndentWidth: 4
PointerAlignment: Left
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 4
UseTab: Never

AllowShortEnumsOnASingleLine: false

BraceWrapping:
  AfterEnum: false

AlignConsecutiveDeclarations: AcrossEmptyLines

NamespaceIndentation: All
07070100000001000081A4000000000000000000000001688B5FE900000EBA000000000000000000000000000000000000001B00000000hyprlock-0.9.1/.clang-tidyWarningsAsErrors: '*'
HeaderFilterRegex: '.*\.hpp'
FormatStyle: 'file'
Checks: >
  -*,
  bugprone-*,
  -bugprone-easily-swappable-parameters,
  -bugprone-forward-declaration-namespace,
  -bugprone-forward-declaration-namespace,
  -bugprone-macro-parentheses,
  -bugprone-narrowing-conversions,
  -bugprone-branch-clone,
  -bugprone-assignment-in-if-condition,
  concurrency-*,
  -concurrency-mt-unsafe,
  cppcoreguidelines-*,
  -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-unused-parameters,
  -misc-no-recursion,
  -misc-non-private-member-variables-in-classes,
  -misc-include-cleaner,
  -misc-use-anonymous-namespace,
  -misc-const-correctness,
  modernize-*,
  -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-avoid-endl,
  -performance-unnecessary-value-param,
  portability-std-allocator-const,
  readability-*,
  -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,

CheckOptions:
  performance-for-range-copy.WarnOnAllAutoCopies: true
  performance-inefficient-string-concatenation.StrictMode: true
  readability-braces-around-statements.ShortStatementLines: 0
  readability-identifier-naming.ClassCase: CamelCase
  readability-identifier-naming.ClassIgnoredRegexp: I.*
  readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!?
  readability-identifier-naming.EnumCase: CamelCase
  readability-identifier-naming.EnumPrefix: e
  readability-identifier-naming.EnumConstantCase: UPPER_CASE
  readability-identifier-naming.FunctionCase: camelBack
  readability-identifier-naming.NamespaceCase: CamelCase
  readability-identifier-naming.NamespacePrefix: N
  readability-identifier-naming.StructPrefix: S
  readability-identifier-naming.StructCase: CamelCase
07070100000002000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001700000000hyprlock-0.9.1/.github07070100000003000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000002600000000hyprlock-0.9.1/.github/ISSUE_TEMPLATE07070100000004000081A4000000000000000000000001688B5FE900000B60000000000000000000000000000000000000002E00000000hyprlock-0.9.1/.github/ISSUE_TEMPLATE/bug.ymlname: Bug Report
description: Something is not working right
labels: ["bug"]
body:
  - type: markdown
    attributes:
      value: |
        ## Before opening a new issue, please take a moment to search through the current open and closed issues to check if it already exists.

        ---

  - type: dropdown
    id: type
    attributes:
      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:
        - "Yes"
        - "No"
    validations:
      required: true

  - type: textarea
    id: ver
    attributes:
      label: Hyprlock Info and Version
      description: |
        Please enter your hyprlock version and config.
        Use `hyprlock --version` if it is available (Introduced after v0.4.1).

        Otherwise please figure out the hyprlock version via your distribution.
        If you built it yourself, post the commit hash.

        Provide your hyprlock config. Usually `~/.config/hypr/hyprlock.conf`.
      value: "<Enter your hyprlock version here>

        <details>
        <summary>Hyprlock config</summary>


        ```sh

        <Paste your hyprlock config here>

        ```


        </details>"
    validations:
      required: true

  - type: textarea
    id: compositor
    attributes:
      label: Compositor Info and Version
      description: |
        If you are on hyprland, paste the output of `hyprctl systeminfo` here.
        If you are on another compositor, enter the compositor name and version you are on.
      value: "<details>
        <summary>System/Version info</summary>


        ```sh

        <Paste the output of the command here>

        ```


        </details>"
    validations:
      required: true

  - type: textarea
    id: desc
    attributes:
      label: Description
      description: "What went wrong?"
    validations:
      required: true

  - type: textarea
    id: repro
    attributes:
      label: How to reproduce
      description: "How can someone else reproduce the issue?"
    validations:
      required: true

  - type: textarea
    id: logs
    attributes:
      label: Crash reports, logs, images, videos
      description: |
        Anything that can help. Please always ATTACH and not paste them.
        To get hyprlock logs, start it from the commandline.
        If you are using hypridle/swayidle as a systemd service, you can use `journalctl -b0 --user -u hypridle/swayidle` to get some logs.
        Compositor logs and crashes are sometimes relevant as well.
        Hyprland logs can be found in $XDG_RUNTIME_DIR/hypr.
        Hyprland crash reports are stored in ~/.cache/hyprland or $XDG_CACHE_HOME/hyprland.

07070100000005000081A4000000000000000000000001688B5FE9000001A1000000000000000000000000000000000000003200000000hyprlock-0.9.1/.github/ISSUE_TEMPLATE/feature.ymlname: 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

07070100000006000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000002100000000hyprlock-0.9.1/.github/workflows07070100000007000081A4000000000000000000000001688B5FE900000647000000000000000000000000000000000000002900000000hyprlock-0.9.1/.github/workflows/nix.ymlname: Build

on: [push, pull_request, workflow_dispatch]
jobs:
  nix:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Install Nix
      uses: nixbuild/nix-quick-install-action@v31
      with:
        nix_conf: |
          keep-env-derivations = true
          keep-outputs = true

    - name: Restore and save Nix store
      uses: nix-community/cache-nix-action@v6
      with:
        # restore and save a cache using this key
        primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
        # if there's no cache hit, restore a cache by this prefix
        restore-prefixes-first-match: nix-${{ runner.os }}-
        # collect garbage until the Nix store size (in bytes) is at most this number
        # before trying to save a new cache
        # 1G = 1073741824
        gc-max-store-size-linux: 1G
        # do purge caches
        purge: true
        # purge all versions of the cache
        purge-prefixes: nix-${{ runner.os }}-
        # created more than this number of seconds ago
        purge-created: 0
        # or, last accessed more than this number of seconds ago
        # relative to the start of the `Post Restore and save Nix store` phase
        purge-last-accessed: 0
        # except any version with the key that is the same as the `primary-key`
        purge-primary-key: never

    # not needed (yet)
    # - uses: cachix/cachix-action@v12
    #   with:
    #     name: hyprland
    #     authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'

    - name: Build
      run: nix flake check --print-build-logs --keep-going

07070100000008000081A4000000000000000000000001688B5FE9000000AC000000000000000000000000000000000000001A00000000hyprlock-0.9.1/.gitignore**/.cache
.direnv
.envrc
.vscode/
CMakeCache.txt
CMakeFiles/
Makefile
cmake_install.cmake
build/
compile_commands.json
protocols/*.cpp
protocols/*.hpp
*.kdev4
.gdb_history
07070100000009000081A4000000000000000000000001688B5FE9000012E0000000000000000000000000000000000000001E00000000hyprlock-0.9.1/CMakeLists.txtcmake_minimum_required(VERSION 3.27)

file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
string(STRIP ${VER_RAW} VERSION)

project(
  hyprlock
  DESCRIPTION "A gpu-accelerated screen lock for Hyprland"
  VERSION ${VERSION})

set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
  message(STATUS "Configuring hyprlock in Debug with CMake")
  add_compile_definitions(HYPRLAND_DEBUG)
else()
  add_compile_options(-O3)
  message(STATUS "Configuring hyprlock in Release with CMake")
endif()

include_directories(. "protocols/")

include(GNUInstallDirs)

# configure
set(CMAKE_CXX_STANDARD 23)
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value
                    -Wno-missing-field-initializers -Wno-narrowing)




add_compile_definitions(HYPRLOCK_VERSION="${VERSION}")

if (DEFINED HYPRLOCK_COMMIT)
  add_compile_definitions(HYPRLOCK_COMMIT="${HYPRLOCK_COMMIT}")
else()
  # get git commit
  execute_process(
    OUTPUT_VARIABLE GIT_SHORT_HASH
    OUTPUT_STRIP_TRAILING_WHITESPACE
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    COMMAND git rev-parse --short HEAD
  )
  add_compile_definitions(HYPRLOCK_COMMIT="${GIT_SHORT_HASH}")
endif()

if (DEFINED HYPRLOCK_VERSION_COMMIT)
  add_compile_definitions(HYPRLOCK_VERSION_COMMIT="${HYPRLOCK_VERSION_COMMIT}")
else()
  # get git commit of v$VERSION
  execute_process(
    OUTPUT_VARIABLE GIT_TAG_SHORT_HASH
    OUTPUT_STRIP_TRAILING_WHITESPACE
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    COMMAND git rev-parse --short v${VERSION}
  )
  add_compile_definitions(HYPRLOCK_VERSION_COMMIT="${GIT_TAG_SHORT_HASH}")
endif()

message(STATUS "VERSION COMMIT: ${HYPRLOCK_VERSION_COMMIT}")
message(STATUS "COMMIT: ${HYPRLOCK_COMMIT}")

# position independent build: __FILE__
add_compile_options(-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)

# dependencies
message(STATUS "Checking deps...")

find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED COMPONENTS EGL GLES3)
find_package(hyprwayland-scanner 0.4.4 REQUIRED)
pkg_check_modules(
  deps
  REQUIRED
  IMPORTED_TARGET
  wayland-client
  wayland-protocols>=1.35
  wayland-egl
  hyprlang>=0.6.0
  egl
  xkbcommon
  cairo
  pangocairo
  libdrm
  gbm
  pam
  hyprutils>=0.8.0
  sdbus-c++>=2.0.0
  hyprgraphics)

file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(hyprlock ${SRCFILES})
target_link_libraries(hyprlock PRIVATE pam rt Threads::Threads PkgConfig::deps
                                       OpenGL::EGL OpenGL::GLES3)

# 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}")

function(protocolnew protoPath protoName external)
  if(external)
    set(path ${CMAKE_SOURCE_DIR}/${protoPath})
  else()
    set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
  endif()
  add_custom_command(
    OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp
           ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp
    COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml
            ${CMAKE_SOURCE_DIR}/protocols/
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
  target_sources(hyprlock PRIVATE protocols/${protoName}.cpp
                                   protocols/${protoName}.hpp)
endfunction()
function(protocolWayland)
  add_custom_command(
    OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
           ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
    COMMAND hyprwayland-scanner --wayland-enums --client
            ${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
  target_sources(hyprlock PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
endfunction()

make_directory(${CMAKE_SOURCE_DIR}/protocols) # we don't ship any custom ones so
                                              # the dir won't be there

protocolwayland()

protocolnew("protocols" "wlr-screencopy-unstable-v1" true)
protocolnew("staging/ext-session-lock" "ext-session-lock-v1" false)
protocolnew("stable/linux-dmabuf" "linux-dmabuf-v1" false)
protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
protocolnew("stable/viewporter" "viewporter" false)
protocolnew("staging/cursor-shape" "cursor-shape-v1" false)
protocolnew("stable/tablet" "tablet-v2" false)

# Installation
install(TARGETS hyprlock)

install(FILES ${CMAKE_SOURCE_DIR}/pam/hyprlock
        DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d)

install(
  FILES ${CMAKE_SOURCE_DIR}/assets/example.conf
  DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/hypr
  RENAME hyprlock.conf)
0707010000000A000081A4000000000000000000000001688B5FE9000005DF000000000000000000000000000000000000001700000000hyprlock-0.9.1/LICENSEBSD 3-Clause License

Copyright (c) 2024, 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.
0707010000000B000081A4000000000000000000000001688B5FE9000005C7000000000000000000000000000000000000001900000000hyprlock-0.9.1/README.md# hyprlock
Hyprland's simple, yet multi-threaded and GPU-accelerated screen locking utility.

## Features
 - Uses the ext-session-lock protocol
 - Support for fractional-scale
 - Fully GPU accelerated
 - Multi-threaded resource acquisition
 - Blurred screenshot as the background
 - Native fingerprint support (using libfprint's dbus interface)
 - Some of Hyprland's eyecandy: gradient borders, blur, animations, shadows, etc.
 - and more...

## How it looks

![](https://i.ibb.co/8Bd98BP/20240220-00h12m46s.png)

## Docs / Configuration
[See the wiki](https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/)

## Arch install
```sh
pacman -S hyprlock # binary x86 tagged release
# or
yay -S hyprlock-git # compiles from latest source
```

## Building

### Deps
You need the following dependencies

- cairo
- hyprgraphics
- hyprland-protocols
- hyprlang
- hyprutils
- hyprwayland-scanner
- mesa (required is libgbm, libdrm and the opengl runtime)
- pam
- pango
- sdbus-cpp (>= 2.0.0)
- wayland-client
- wayland-protocols
- xkbcommon

Sometimes distro packages are missing required development files.
Such distros usually offer development versions of library package - commonly suffixed with `-devel` or `-dev`.

### Building

Building:
```sh
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build
cmake --build ./build --config Release --target hyprlock -j`nproc 2>/dev/null || getconf _NPROCESSORS_CONF`
```

Installation:
```sh
sudo cmake --install build
```
0707010000000C000081A4000000000000000000000001688B5FE900000006000000000000000000000000000000000000001700000000hyprlock-0.9.1/VERSION0.9.1
0707010000000D000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001600000000hyprlock-0.9.1/assets0707010000000E000081A4000000000000000000000001688B5FE90000097E000000000000000000000000000000000000002300000000hyprlock-0.9.1/assets/example.conf# sample hyprlock.conf
# for more configuration options, refer https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock
#
# rendered text in all widgets supports pango markup (e.g. <b> or <i> tags)
# ref. https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/#general-remarks
#
# shortcuts to clear password buffer: ESC, Ctrl+U, Ctrl+Backspace
#
# you can get started by copying this config to ~/.config/hypr/hyprlock.conf
#

$font = Monospace

general {
    hide_cursor = false
}

# uncomment to enable fingerprint authentication
# auth {
#     fingerprint {
#         enabled = true
#         ready_message = Scan fingerprint to unlock
#         present_message = Scanning...
#         retry_delay = 250 # in milliseconds
#     }
# }

animations {
    enabled = true
    bezier = linear, 1, 1, 0, 0
    animation = fadeIn, 1, 5, linear
    animation = fadeOut, 1, 5, linear
    animation = inputFieldDots, 1, 2, linear
}

background {
    monitor =
    path = screenshot
    blur_passes = 3
}

input-field {
    monitor =
    size = 20%, 5%
    outline_thickness = 3
    inner_color = rgba(0, 0, 0, 0.0) # no fill

    outer_color = rgba(33ccffee) rgba(00ff99ee) 45deg
    check_color = rgba(00ff99ee) rgba(ff6633ee) 120deg
    fail_color = rgba(ff6633ee) rgba(ff0066ee) 40deg

    font_color = rgb(143, 143, 143)
    fade_on_empty = false
    rounding = 15

    font_family = $font
    placeholder_text = Input password...
    fail_text = $PAMFAIL

    # uncomment to use a letter instead of a dot to indicate the typed password
    # dots_text_format = *
    # dots_size = 0.4
    dots_spacing = 0.3

    # uncomment to use an input indicator that does not show the password length (similar to swaylock's input indicator)
    # hide_input = true

    position = 0, -20
    halign = center
    valign = center
}

# TIME
label {
    monitor =
    text = $TIME # ref. https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/#variable-substitution
    font_size = 90
    font_family = $font

    position = -30, 0
    halign = right
    valign = top
}

# DATE
label {
    monitor =
    text = cmd[update:60000] date +"%A, %d %B %Y" # update every 60 seconds
    font_size = 25
    font_family = $font

    position = -30, -150
    halign = right
    valign = top
}

label {
    monitor =
    text = $LAYOUT[en,ru]
    font_size = 24
    onclick = hyprctl switchxkblayout all next

    position = 250, -20
    halign = center
    valign = center
}
0707010000000F000081A4000000000000000000000001688B5FE900000DAD000000000000000000000000000000000000001A00000000hyprlock-0.9.1/flake.lock{
  "nodes": {
    "hyprgraphics": {
      "inputs": {
        "hyprutils": [
          "hyprutils"
        ],
        "nixpkgs": [
          "nixpkgs"
        ],
        "systems": [
          "systems"
        ]
      },
      "locked": {
        "lastModified": 1750621377,
        "narHash": "sha256-8u6b5oAdX0rCuoR8wFenajBRmI+mzbpNig6hSCuWUzE=",
        "owner": "hyprwm",
        "repo": "hyprgraphics",
        "rev": "b3d628d01693fb9bb0a6690cd4e7b80abda04310",
        "type": "github"
      },
      "original": {
        "owner": "hyprwm",
        "repo": "hyprgraphics",
        "type": "github"
      }
    },
    "hyprlang": {
      "inputs": {
        "hyprutils": [
          "hyprutils"
        ],
        "nixpkgs": [
          "nixpkgs"
        ],
        "systems": [
          "systems"
        ]
      },
      "locked": {
        "lastModified": 1750371198,
        "narHash": "sha256-/iuJ1paQOBoSLqHflRNNGyroqfF/yvPNurxzcCT0cAE=",
        "owner": "hyprwm",
        "repo": "hyprlang",
        "rev": "cee01452bca58d6cadb3224e21e370de8bc20f0b",
        "type": "github"
      },
      "original": {
        "owner": "hyprwm",
        "repo": "hyprlang",
        "type": "github"
      }
    },
    "hyprutils": {
      "inputs": {
        "nixpkgs": [
          "nixpkgs"
        ],
        "systems": [
          "systems"
        ]
      },
      "locked": {
        "lastModified": 1751061882,
        "narHash": "sha256-g9n8Vrbx+2JYM170P9BbvGHN39Wlkr4U+V2WLHQsXL8=",
        "owner": "hyprwm",
        "repo": "hyprutils",
        "rev": "4737241eaf8a1e51671a2a088518071f9a265cf4",
        "type": "github"
      },
      "original": {
        "owner": "hyprwm",
        "repo": "hyprutils",
        "type": "github"
      }
    },
    "hyprwayland-scanner": {
      "inputs": {
        "nixpkgs": [
          "nixpkgs"
        ],
        "systems": [
          "systems"
        ]
      },
      "locked": {
        "lastModified": 1750371869,
        "narHash": "sha256-lGk4gLjgZQ/rndUkzmPYcgbHr8gKU5u71vyrjnwfpB4=",
        "owner": "hyprwm",
        "repo": "hyprwayland-scanner",
        "rev": "aa38edd6e3e277ae6a97ea83a69261a5c3aab9fd",
        "type": "github"
      },
      "original": {
        "owner": "hyprwm",
        "repo": "hyprwayland-scanner",
        "type": "github"
      }
    },
    "nixpkgs": {
      "locked": {
        "lastModified": 1751011381,
        "narHash": "sha256-krGXKxvkBhnrSC/kGBmg5MyupUUT5R6IBCLEzx9jhMM=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "30e2e2857ba47844aa71991daa6ed1fc678bcbb7",
        "type": "github"
      },
      "original": {
        "owner": "NixOS",
        "ref": "nixos-unstable",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "root": {
      "inputs": {
        "hyprgraphics": "hyprgraphics",
        "hyprlang": "hyprlang",
        "hyprutils": "hyprutils",
        "hyprwayland-scanner": "hyprwayland-scanner",
        "nixpkgs": "nixpkgs",
        "systems": "systems"
      }
    },
    "systems": {
      "locked": {
        "lastModified": 1689347949,
        "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
        "owner": "nix-systems",
        "repo": "default-linux",
        "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
        "type": "github"
      },
      "original": {
        "owner": "nix-systems",
        "repo": "default-linux",
        "type": "github"
      }
    }
  },
  "root": "root",
  "version": 7
}
07070100000010000081A4000000000000000000000001688B5FE9000006F4000000000000000000000000000000000000001900000000hyprlock-0.9.1/flake.nix{
  description = "Hyprland's GPU-accelerated screen locking utility";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    systems.url = "github:nix-systems/default-linux";

    hyprgraphics = {
      url = "github:hyprwm/hyprgraphics";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.systems.follows = "systems";
      inputs.hyprutils.follows = "hyprutils";
    };

    hyprutils = {
      url = "github:hyprwm/hyprutils";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.systems.follows = "systems";
    };

    hyprlang = {
      url = "github:hyprwm/hyprlang";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.systems.follows = "systems";
      inputs.hyprutils.follows = "hyprutils";
    };

    hyprwayland-scanner = {
      url = "github:hyprwm/hyprwayland-scanner";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.systems.follows = "systems";
    };
  };

  outputs = {
    self,
    nixpkgs,
    systems,
    ...
  } @ inputs: let
    inherit (nixpkgs) lib;
    eachSystem = lib.genAttrs (import systems);
    pkgsFor = eachSystem (system:
      import nixpkgs {
        localSystem.system = system;
        overlays = with self.overlays; [default];
      });
  in {
    overlays = import ./nix/overlays.nix {inherit inputs lib self;};

    packages = eachSystem (system: {
      default = self.packages.${system}.hyprlock;
      inherit (pkgsFor.${system}) hyprlock;
    });

    homeManagerModules = {
      default = self.homeManagerModules.hyprlock;
      hyprlock = builtins.throw "hyprlock: the flake HM module has been removed. Use the module from Home Manager upstream.";
    };

    checks = eachSystem (system: self.packages.${system});

    formatter = eachSystem (system: pkgsFor.${system}.alejandra);
  };
}
07070100000011000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001300000000hyprlock-0.9.1/nix07070100000012000081A4000000000000000000000001688B5FE90000045E000000000000000000000000000000000000001F00000000hyprlock-0.9.1/nix/default.nix{
  lib,
  stdenv,
  cmake,
  pkg-config,
  cairo,
  libdrm,
  libGL,
  libxkbcommon,
  libgbm,
  hyprgraphics,
  hyprlang,
  hyprutils,
  hyprwayland-scanner,
  pam,
  pango,
  sdbus-cpp,
  systemdLibs,
  wayland,
  wayland-protocols,
  wayland-scanner,
  version ? "git",
  shortRev ? "",
}:
stdenv.mkDerivation {
  pname = "hyprlock";
  inherit version;

  src = ../.;

  nativeBuildInputs = [
    cmake
    pkg-config
    hyprwayland-scanner
    wayland-scanner
  ];

  buildInputs = [
    cairo
    libdrm
    libGL
    libxkbcommon
    libgbm
    hyprgraphics
    hyprlang
    hyprutils
    pam
    pango
    sdbus-cpp
    systemdLibs
    wayland
    wayland-protocols
  ];

  cmakeFlags = lib.mapAttrsToList lib.cmakeFeature {
    HYPRLOCK_COMMIT = shortRev;
    HYPRLOCK_VERSION_COMMIT = ""; # Intentionally left empty (hyprlock --version will always print the commit)
  };

  meta = {
    homepage = "https://github.com/hyprwm/hyprlock";
    description = "A gpu-accelerated screen lock for Hyprland";
    license = lib.licenses.bsd3;
    platforms = lib.platforms.linux;
    mainProgram = "hyprlock";
  };
}
07070100000013000081A4000000000000000000000001688B5FE9000004ED000000000000000000000000000000000000002000000000hyprlock-0.9.1/nix/overlays.nix{
  lib,
  inputs,
  self,
}: 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 {
  default = inputs.self.overlays.hyprlock;

  hyprlock = lib.composeManyExtensions [
    inputs.hyprgraphics.overlays.default
    inputs.hyprlang.overlays.default
    inputs.hyprutils.overlays.default
    inputs.hyprwayland-scanner.overlays.default
    inputs.self.overlays.sdbuscpp
    (final: prev: {
      hyprlock = prev.callPackage ./default.nix {
        stdenv = prev.gcc15Stdenv;
        version = version + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty");
        inherit (final) hyprlang;
        shortRev = self.sourceInfo.shortRev or "dirty";
      };
    })
  ];

  sdbuscpp = final: prev: {
    sdbus-cpp = prev.sdbus-cpp.overrideAttrs (self: super: {
      version = "2.0.0";

      src = final.fetchFromGitHub {
        owner = "Kistler-group";
        repo = "sdbus-cpp";
        rev = "refs/tags/v${self.version}";
        hash = "sha256-W8V5FRhV3jtERMFrZ4gf30OpIQLYoj2yYGpnYOmH2+g=";
      };
    });
  };
}
07070100000014000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001300000000hyprlock-0.9.1/pam07070100000015000081A4000000000000000000000001688B5FE90000007E000000000000000000000000000000000000001C00000000hyprlock-0.9.1/pam/hyprlock# PAM configuration file for hyprlock
# the 'login' configuration file (see /etc/pam.d/login)

auth        include     login

07070100000016000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001900000000hyprlock-0.9.1/protocols07070100000017000081A4000000000000000000000001688B5FE900002585000000000000000000000000000000000000003800000000hyprlock-0.9.1/protocols/wlr-screencopy-unstable-v1.xml<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_screencopy_unstable_v1">
	<copyright>
		Copyright © 2018 Simon Ser
		Copyright © 2019 Andri Yngvason

		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.
	</copyright>

	<description summary="screen content capturing on client buffers">
		This protocol allows clients to ask the compositor to copy part of the
		screen content to a client buffer.

		Warning! The protocol described in this file is experimental and
		backward incompatible changes may be made. Backward compatible changes
		may be added together with the corresponding interface version bump.
		Backward incompatible changes are done by bumping the version number in
		the protocol and interface names and resetting the interface version.
		Once the protocol is to be declared stable, the 'z' prefix and the
		version number in the protocol and interface names are removed and the
		interface version number is reset.
	</description>

	<interface name="zwlr_screencopy_manager_v1" version="3">
		<description summary="manager to inform clients and begin capturing">
			This object is a manager which offers requests to start capturing from a
			source.
		</description>

		<request name="capture_output">
			<description summary="capture an output">
				Capture the next frame of an entire output.
			</description>
			<arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/>
			<arg name="overlay_cursor" type="int"
				summary="composite cursor onto the frame"/>
			<arg name="output" type="object" interface="wl_output"/>
		</request>

		<request name="capture_output_region">
			<description summary="capture an output's region">
				Capture the next frame of an output's region.

				The region is given in output logical coordinates, see
				xdg_output.logical_size. The region will be clipped to the output's
				extents.
			</description>
			<arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/>
			<arg name="overlay_cursor" type="int"
				summary="composite cursor onto the frame"/>
			<arg name="output" type="object" interface="wl_output"/>
			<arg name="x" type="int"/>
			<arg name="y" type="int"/>
			<arg name="width" type="int"/>
			<arg name="height" type="int"/>
		</request>

		<request name="destroy" type="destructor">
			<description summary="destroy the manager">
				All objects created by the manager will still remain valid, until their
				appropriate destroy request has been called.
			</description>
		</request>
	</interface>

	<interface name="zwlr_screencopy_frame_v1" version="3">
		<description summary="a frame ready for copy">
			This object represents a single frame.

			When created, a series of buffer events will be sent, each representing a
			supported buffer type. The "buffer_done" event is sent afterwards to
			indicate that all supported buffer types have been enumerated. The client
			will then be able to send a "copy" request. If the capture is successful,
			the compositor will send a "flags" followed by a "ready" event.

			For objects version 2 or lower, wl_shm buffers are always supported, ie.
			the "buffer" event is guaranteed to be sent.

			If the capture failed, the "failed" event is sent. This can happen anytime
			before the "ready" event.

			Once either a "ready" or a "failed" event is received, the client should
			destroy the frame.
		</description>

		<event name="buffer">
			<description summary="wl_shm buffer information">
				Provides information about wl_shm buffer parameters that need to be
				used for this frame. This event is sent once after the frame is created
				if wl_shm buffers are supported.
			</description>
			<arg name="format" type="uint" enum="wl_shm.format" summary="buffer format"/>
			<arg name="width" type="uint" summary="buffer width"/>
			<arg name="height" type="uint" summary="buffer height"/>
			<arg name="stride" type="uint" summary="buffer stride"/>
		</event>

		<request name="copy">
			<description summary="copy the frame">
				Copy the frame to the supplied buffer. The buffer must have a the
				correct size, see zwlr_screencopy_frame_v1.buffer and
				zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a
				supported format.

				If the frame is successfully copied, a "flags" and a "ready" events are
				sent. Otherwise, a "failed" event is sent.
			</description>
			<arg name="buffer" type="object" interface="wl_buffer"/>
		</request>

		<enum name="error">
			<entry name="already_used" value="0"
				summary="the object has already been used to copy a wl_buffer"/>
			<entry name="invalid_buffer" value="1"
				summary="buffer attributes are invalid"/>
		</enum>

		<enum name="flags" bitfield="true">
			<entry name="y_invert" value="1" summary="contents are y-inverted"/>
		</enum>

		<event name="flags">
			<description summary="frame flags">
				Provides flags about the frame. This event is sent once before the
				"ready" event.
			</description>
			<arg name="flags" type="uint" enum="flags" summary="frame flags"/>
		</event>

		<event name="ready">
			<description summary="indicates frame is available for reading">
				Called as soon as the frame is copied, indicating it is available
				for reading. This event includes the time at which presentation happened
				at.

				The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
				each component being an unsigned 32-bit value. Whole seconds are in
				tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo,
				and the additional fractional part in tv_nsec as nanoseconds. Hence,
				for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part
				may have an arbitrary offset at start.

				After receiving this event, the client should destroy the object.
			</description>
			<arg name="tv_sec_hi" type="uint"
					 summary="high 32 bits of the seconds part of the timestamp"/>
			<arg name="tv_sec_lo" type="uint"
					 summary="low 32 bits of the seconds part of the timestamp"/>
			<arg name="tv_nsec" type="uint"
					 summary="nanoseconds part of the timestamp"/>
		</event>

		<event name="failed">
			<description summary="frame copy failed">
				This event indicates that the attempted frame copy has failed.

				After receiving this event, the client should destroy the object.
			</description>
		</event>

		<request name="destroy" type="destructor">
			<description summary="delete this object, used or not">
				Destroys the frame. This request can be sent at any time by the client.
			</description>
		</request>

		<!-- Version 2 additions -->
		<request name="copy_with_damage" since="2">
			<description summary="copy the frame when it's damaged">
				Same as copy, except it waits until there is damage to copy.
			</description>
			<arg name="buffer" type="object" interface="wl_buffer"/>
		</request>

		<event name="damage" since="2">
			<description summary="carries the coordinates of the damaged region">
				This event is sent right before the ready event when copy_with_damage is
				requested. It may be generated multiple times for each copy_with_damage
				request.

				The arguments describe a box around an area that has changed since the
				last copy request that was derived from the current screencopy manager
				instance.

				The union of all regions received between the call to copy_with_damage
				and a ready event is the total damage since the prior ready event.
			</description>
			<arg name="x" type="uint" summary="damaged x coordinates"/>
			<arg name="y" type="uint" summary="damaged y coordinates"/>
			<arg name="width" type="uint" summary="current width"/>
			<arg name="height" type="uint" summary="current height"/>
		</event>

		<!-- Version 3 additions -->
		<event name="linux_dmabuf" since="3">
			<description summary="linux-dmabuf buffer information">
				Provides information about linux-dmabuf buffer parameters that need to
				be used for this frame. This event is sent once after the frame is
				created if linux-dmabuf buffers are supported.
			</description>
			<arg name="format" type="uint" summary="fourcc pixel format"/>
			<arg name="width" type="uint" summary="buffer width"/>
			<arg name="height" type="uint" summary="buffer height"/>
		</event>

		<event name="buffer_done" since="3">
			<description summary="all buffer types reported">
				This event is sent once after all buffer events have been sent.

				The client should proceed to create a buffer of one of the supported
				types, and send a "copy" request.
			</description>
		</event>
	</interface>
</protocol>
07070100000018000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001300000000hyprlock-0.9.1/src07070100000019000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001800000000hyprlock-0.9.1/src/auth0707010000001A000081A4000000000000000000000001688B5FE900000D9F000000000000000000000000000000000000002100000000hyprlock-0.9.1/src/auth/Auth.cpp#include "Auth.hpp"
#include "Pam.hpp"
#include "Fingerprint.hpp"
#include "../config/ConfigManager.hpp"
#include "../core/hyprlock.hpp"
#include "src/helpers/Log.hpp"

#include <hyprlang.hpp>
#include <memory>

CAuth::CAuth() {
    static const auto ENABLEPAM = g_pConfigManager->getValue<Hyprlang::INT>("auth:pam:enabled");
    if (*ENABLEPAM)
        m_vImpls.emplace_back(makeShared<CPam>());
    static const auto ENABLEFINGERPRINT = g_pConfigManager->getValue<Hyprlang::INT>("auth:fingerprint:enabled");
    if (*ENABLEFINGERPRINT)
        m_vImpls.emplace_back(makeShared<CFingerprint>());

    RASSERT(!m_vImpls.empty(), "At least one authentication method must be enabled!");
}

void CAuth::start() {
    for (const auto& i : m_vImpls) {
        i->init();
    }
}

void CAuth::submitInput(const std::string& input) {
    for (const auto& i : m_vImpls) {
        i->handleInput(input);
    }

    g_pHyprlock->clearPasswordBuffer();
}

bool CAuth::checkWaiting() {
    return std::ranges::any_of(m_vImpls, [](const auto& i) { return i->checkWaiting(); });
}

const std::string& CAuth::getCurrentFailText() {
    return m_sCurrentFail.failText;
}

std::optional<std::string> CAuth::getFailText(eAuthImplementations implType) {
    for (const auto& i : m_vImpls) {
        if (i->getImplType() == implType)
            return i->getLastFailText();
    }
    return std::nullopt;
}

std::optional<std::string> CAuth::getPrompt(eAuthImplementations implType) {
    for (const auto& i : m_vImpls) {
        if (i->getImplType() == implType)
            return i->getLastPrompt();
    }
    return std::nullopt;
}

size_t CAuth::getFailedAttempts() {
    return m_sCurrentFail.failedAttempts;
}

SP<IAuthImplementation> CAuth::getImpl(eAuthImplementations implType) {
    for (const auto& i : m_vImpls) {
        if (i->getImplType() == implType)
            return i;
    }

    return nullptr;
}

void CAuth::terminate() {
    for (const auto& i : m_vImpls) {
        i->terminate();
    }
}

static void unlockCallback(ASP<CTimer> self, void* data) {
    g_pHyprlock->unlock();
}

void CAuth::enqueueUnlock() {
    g_pHyprlock->addTimer(std::chrono::milliseconds(0), unlockCallback, nullptr);
}

static void passwordFailCallback(ASP<CTimer> self, void* data) {
    g_pAuth->m_bDisplayFailText = true;

    g_pHyprlock->enqueueForceUpdateTimers();

    g_pHyprlock->renderAllOutputs();
}

static void displayFailTimeoutCallback(ASP<CTimer> self, void* data) {
    if (g_pAuth->m_bDisplayFailText) {
        g_pAuth->m_bDisplayFailText = false;
        g_pHyprlock->renderAllOutputs();
    }
}

void CAuth::enqueueFail(const std::string& failText, eAuthImplementations implType) {
    static const auto FAILTIMEOUT = g_pConfigManager->getValue<Hyprlang::INT>("general:fail_timeout");

    m_sCurrentFail.failText   = failText;
    m_sCurrentFail.failSource = implType;
    m_sCurrentFail.failedAttempts++;

    Debug::log(LOG, "Failed attempts: {}", m_sCurrentFail.failedAttempts);

    if (m_resetDisplayFailTimer) {
        m_resetDisplayFailTimer->cancel();
        m_resetDisplayFailTimer.reset();
    }

    g_pHyprlock->addTimer(std::chrono::milliseconds(0), passwordFailCallback, nullptr);
    m_resetDisplayFailTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(*FAILTIMEOUT), displayFailTimeoutCallback, nullptr);
}

void CAuth::resetDisplayFail() {
    g_pAuth->m_bDisplayFailText = false;
    m_resetDisplayFailTimer->cancel();
    m_resetDisplayFailTimer.reset();
}
0707010000001B000081A4000000000000000000000001688B5FE900000837000000000000000000000000000000000000002100000000hyprlock-0.9.1/src/auth/Auth.hpp#pragma once

#include <optional>
#include <vector>

#include "../defines.hpp"
#include "../core/Timer.hpp"

enum eAuthImplementations {
    AUTH_IMPL_PAM         = 0,
    AUTH_IMPL_FINGERPRINT = 1,
};

class IAuthImplementation {
  public:
    virtual ~IAuthImplementation() = default;

    virtual eAuthImplementations       getImplType()                         = 0;
    virtual void                       init()                                = 0;
    virtual void                       handleInput(const std::string& input) = 0;
    virtual bool                       checkWaiting()                        = 0;
    virtual std::optional<std::string> getLastFailText()                     = 0;
    virtual std::optional<std::string> getLastPrompt()                       = 0;
    virtual void                       terminate()                           = 0;

    friend class CAuth;
};

class CAuth {
  public:
    CAuth();

    void                       start();

    void                       submitInput(const std::string& input);
    bool                       checkWaiting();

    const std::string&         getCurrentFailText();

    std::optional<std::string> getFailText(eAuthImplementations implType);
    std::optional<std::string> getPrompt(eAuthImplementations implType);
    size_t                     getFailedAttempts();

    SP<IAuthImplementation>    getImpl(eAuthImplementations implType);

    void                       terminate();

    void                       enqueueUnlock();
    void                       enqueueFail(const std::string& failText, eAuthImplementations implType);

    void                       resetDisplayFail();

    // Should only be set via the main thread
    bool m_bDisplayFailText = false;

  private:
    struct {
        std::string          failText       = "";
        eAuthImplementations failSource     = AUTH_IMPL_PAM;
        size_t               failedAttempts = 0;
    } m_sCurrentFail;

    std::vector<SP<IAuthImplementation>> m_vImpls;
    ASP<CTimer>                          m_resetDisplayFailTimer;
};

inline UP<CAuth> g_pAuth;
0707010000001C000081A4000000000000000000000001688B5FE9000028D4000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/auth/Fingerprint.cpp#include "Fingerprint.hpp"
#include "../core/hyprlock.hpp"
#include "../helpers/Log.hpp"
#include "../config/ConfigManager.hpp"

#include <memory>
#include <unistd.h>
#include <pwd.h>

#include <cstring>

static const auto FPRINT        = sdbus::ServiceName{"net.reactivated.Fprint"};
static const auto DEVICE        = sdbus::ServiceName{"net.reactivated.Fprint.Device"};
static const auto MANAGER       = sdbus::ServiceName{"net.reactivated.Fprint.Manager"};
static const auto LOGIN_MANAGER = sdbus::ServiceName{"org.freedesktop.login1.Manager"};

enum MatchResult {
    MATCH_INVALID = 0,
    MATCH_NO_MATCH,
    MATCH_MATCHED,
    MATCH_RETRY,
    MATCH_SWIPE_TOO_SHORT,
    MATCH_FINGER_NOT_CENTERED,
    MATCH_REMOVE_AND_RETRY,
    MATCH_DISCONNECTED,
    MATCH_UNKNOWN_ERROR,
};

static std::map<std::string, MatchResult> s_mapStringToTestType = {{"verify-no-match", MATCH_NO_MATCH},
                                                                   {"verify-match", MATCH_MATCHED},
                                                                   {"verify-retry-scan", MATCH_RETRY},
                                                                   {"verify-swipe-too-short", MATCH_SWIPE_TOO_SHORT},
                                                                   {"verify-finger-not-centered", MATCH_FINGER_NOT_CENTERED},
                                                                   {"verify-remove-and-retry", MATCH_REMOVE_AND_RETRY},
                                                                   {"verify-disconnected", MATCH_DISCONNECTED},
                                                                   {"verify-unknown-error", MATCH_UNKNOWN_ERROR}};

CFingerprint::CFingerprint() {
    static const auto FINGERPRINTREADY   = g_pConfigManager->getValue<Hyprlang::STRING>("auth:fingerprint:ready_message");
    m_sFingerprintReady                  = *FINGERPRINTREADY;
    static const auto FINGERPRINTPRESENT = g_pConfigManager->getValue<Hyprlang::STRING>("auth:fingerprint:present_message");
    m_sFingerprintPresent                = *FINGERPRINTPRESENT;
}

CFingerprint::~CFingerprint() {
    ;
}

void CFingerprint::init() {
    try {
        m_sDBUSState.connection = sdbus::createSystemBusConnection();
        m_sDBUSState.login      = sdbus::createProxy(*m_sDBUSState.connection, sdbus::ServiceName{"org.freedesktop.login1"}, sdbus::ObjectPath{"/org/freedesktop/login1"});
    } catch (sdbus::Error& e) {
        Debug::log(ERR, "fprint: Failed to setup dbus ({})", e.what());
        m_sDBUSState.connection.reset();
        return;
    }

    m_sDBUSState.login->getPropertyAsync("PreparingForSleep").onInterface(LOGIN_MANAGER).uponReplyInvoke([this](std::optional<sdbus::Error> e, sdbus::Variant preparingForSleep) {
        if (e) {
            Debug::log(WARN, "fprint: Failed getting value for PreparingForSleep: {}", e->what());
            return;
        }
        m_sDBUSState.sleeping = preparingForSleep.get<bool>();
        // When entering sleep, the wake signal will trigger startVerify().
        if (m_sDBUSState.sleeping)
            return;
        startVerify();
    });
    m_sDBUSState.login->uponSignal("PrepareForSleep").onInterface(LOGIN_MANAGER).call([this](bool start) {
        Debug::log(LOG, "fprint: PrepareForSleep (start: {})", start);
        m_sDBUSState.sleeping = start;
        if (!m_sDBUSState.sleeping && !m_sDBUSState.verifying)
            startVerify();
    });
}

void CFingerprint::handleInput(const std::string& input) {
    ;
}

std::optional<std::string> CFingerprint::getLastFailText() {
    if (!m_sFailureReason.empty())
        return std::optional(m_sFailureReason);
    return std::nullopt;
}

std::optional<std::string> CFingerprint::getLastPrompt() {
    if (!m_sPrompt.empty())
        return std::optional(m_sPrompt);
    return std::nullopt;
}

bool CFingerprint::checkWaiting() {
    return false;
}

void CFingerprint::terminate() {
    if (!m_sDBUSState.abort)
        releaseDevice();
}

std::shared_ptr<sdbus::IConnection> CFingerprint::getConnection() {
    return m_sDBUSState.connection;
}

bool CFingerprint::createDeviceProxy() {
    auto              proxy = sdbus::createProxy(*m_sDBUSState.connection, FPRINT, sdbus::ObjectPath{"/net/reactivated/Fprint/Manager"});

    sdbus::ObjectPath path;
    try {
        proxy->callMethod("GetDefaultDevice").onInterface(MANAGER).storeResultsTo(path);
    } catch (sdbus::Error& e) {
        Debug::log(WARN, "fprint: couldn't connect to Fprint service ({})", e.what());
        return false;
    }
    Debug::log(LOG, "fprint: using device path {}", path.c_str());
    m_sDBUSState.device = sdbus::createProxy(*m_sDBUSState.connection, FPRINT, path);

    m_sDBUSState.device->uponSignal("VerifyFingerSelected").onInterface(DEVICE).call([](const std::string& finger) { Debug::log(LOG, "fprint: finger selected: {}", finger); });
    m_sDBUSState.device->uponSignal("VerifyStatus").onInterface(DEVICE).call([this](const std::string& result, const bool done) { handleVerifyStatus(result, done); });

    m_sDBUSState.device->uponSignal("PropertiesChanged")
        .onInterface("org.freedesktop.DBus.Properties")
        .call([this](const std::string& interface, const std::map<std::string, sdbus::Variant>& properties) {
            if (interface != DEVICE || m_sDBUSState.done)
                return;

            try {
                const auto presentVariant = properties.at("finger-present");
                bool       isPresent      = presentVariant.get<bool>();
                if (!isPresent)
                    return;
                m_sPrompt = m_sFingerprintPresent;
                g_pHyprlock->enqueueForceUpdateTimers();
            } catch (std::out_of_range& e) {}
        });

    return true;
}

void CFingerprint::handleVerifyStatus(const std::string& result, bool done) {
    Debug::log(LOG, "fprint: handling status {}", result);
    auto matchResult   = s_mapStringToTestType[result];
    bool authenticated = false;
    bool retry         = false;
    if (m_sDBUSState.sleeping) {
        stopVerify();
        Debug::log(LOG, "fprint: device suspended");
        return;
    }
    switch (matchResult) {
        case MATCH_INVALID: Debug::log(WARN, "fprint: unknown status: {}", result); break;
        case MATCH_NO_MATCH:
            stopVerify();
            if (m_sDBUSState.retries >= 3) {
                m_sFailureReason = "Fingerprint auth disabled (too many failed attempts)";
            } else {
                done                         = false;
                static const auto RETRYDELAY = g_pConfigManager->getValue<Hyprlang::INT>("auth:fingerprint:retry_delay");
                g_pHyprlock->addTimer(std::chrono::milliseconds(*RETRYDELAY), [](ASP<CTimer> self, void* data) { ((CFingerprint*)data)->startVerify(true); }, this);
                m_sFailureReason = "Fingerprint did not match";
            }
            break;
        case MATCH_UNKNOWN_ERROR:
            stopVerify();
            m_sFailureReason = "Fingerprint auth disabled (unknown error)";
            break;
        case MATCH_MATCHED:
            stopVerify();
            authenticated = true;
            g_pAuth->enqueueUnlock();
            break;
        case MATCH_RETRY:
            retry     = true;
            m_sPrompt = "Please retry fingerprint scan";
            break;
        case MATCH_SWIPE_TOO_SHORT:
            retry     = true;
            m_sPrompt = "Swipe too short - try again";
            break;
        case MATCH_FINGER_NOT_CENTERED:
            retry     = true;
            m_sPrompt = "Finger not centered - try again";
            break;
        case MATCH_REMOVE_AND_RETRY:
            retry     = true;
            m_sPrompt = "Remove your finger and try again";
            break;
        case MATCH_DISCONNECTED:
            m_sFailureReason   = "Fingerprint device disconnected";
            m_sDBUSState.abort = true;
            break;
    }

    if (!authenticated && !retry)
        g_pAuth->enqueueFail(m_sFailureReason, AUTH_IMPL_FINGERPRINT);
    else if (retry)
        g_pHyprlock->enqueueForceUpdateTimers();

    if (done || m_sDBUSState.abort)
        m_sDBUSState.done = true;
}

void CFingerprint::claimDevice() {
    const auto currentUser = ""; // Empty string means use the caller's id.
    m_sDBUSState.device->callMethodAsync("Claim").onInterface(DEVICE).withArguments(currentUser).uponReplyInvoke([this](std::optional<sdbus::Error> e) {
        if (e)
            Debug::log(WARN, "fprint: could not claim device, {}", e->what());
        else {
            Debug::log(LOG, "fprint: claimed device");
            startVerify();
        }
    });
}

void CFingerprint::startVerify(bool isRetry) {
    m_sDBUSState.verifying = true;
    if (!m_sDBUSState.device) {
        if (!createDeviceProxy())
            return;

        claimDevice();
        return;
    }
    auto finger = "any"; // Any finger.
    m_sDBUSState.device->callMethodAsync("VerifyStart").onInterface(DEVICE).withArguments(finger).uponReplyInvoke([this, isRetry](std::optional<sdbus::Error> e) {
        if (e) {
            Debug::log(WARN, "fprint: could not start verifying, {}", e->what());
            if (isRetry)
                m_sFailureReason = "Fingerprint auth disabled (failed to restart)";

        } else {
            Debug::log(LOG, "fprint: started verifying");
            if (isRetry) {
                m_sDBUSState.retries++;
                m_sPrompt = "Could not match fingerprint. Try again.";
            } else
                m_sPrompt = m_sFingerprintReady;
        }
        g_pHyprlock->enqueueForceUpdateTimers();
    });
}

bool CFingerprint::stopVerify() {
    m_sDBUSState.verifying = false;
    if (!m_sDBUSState.device)
        return false;
    try {
        m_sDBUSState.device->callMethod("VerifyStop").onInterface(DEVICE);
    } catch (sdbus::Error& e) {
        Debug::log(WARN, "fprint: could not stop verifying, {}", e.what());
        return false;
    }
    Debug::log(LOG, "fprint: stopped verification");
    return true;
}

bool CFingerprint::releaseDevice() {
    if (!m_sDBUSState.device)
        return false;
    try {
        m_sDBUSState.device->callMethod("Release").onInterface(DEVICE);
    } catch (sdbus::Error& e) {
        Debug::log(WARN, "fprint: could not release device, {}", e.what());
        return false;
    }
    Debug::log(LOG, "fprint: released device");
    return true;
}
0707010000001D000081A4000000000000000000000001688B5FE90000068F000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/auth/Fingerprint.hpp#pragma once

#include "Auth.hpp"

#include <memory>
#include <optional>
#include <string>
#include <sdbus-c++/sdbus-c++.h>

class CFingerprint : public IAuthImplementation {
  public:
    CFingerprint();

    virtual ~CFingerprint();
    virtual eAuthImplementations getImplType() {
        return AUTH_IMPL_FINGERPRINT;
    }
    virtual void                        init();
    virtual void                        handleInput(const std::string& input);
    virtual bool                        checkWaiting();
    virtual std::optional<std::string>  getLastFailText();
    virtual std::optional<std::string>  getLastPrompt();
    virtual void                        terminate();

    std::shared_ptr<sdbus::IConnection> getConnection();

  private:
    struct SDBUSState {
        std::shared_ptr<sdbus::IConnection> connection;
        std::unique_ptr<sdbus::IProxy>      login;
        std::unique_ptr<sdbus::IProxy>      device;

        bool                                abort     = false;
        bool                                done      = false;
        int                                 retries   = 0;
        bool                                sleeping  = false;
        bool                                verifying = false;
    } m_sDBUSState;

    std::string m_sFingerprintReady;
    std::string m_sFingerprintPresent;

    std::string m_sPrompt{""};
    std::string m_sFailureReason{""};

    void        handleVerifyStatus(const std::string& result, const bool done);

    bool        createDeviceProxy();
    void        claimDevice();
    void        startVerify(bool isRetry = false);
    bool        stopVerify();
    bool        releaseDevice();
};
0707010000001E000081A4000000000000000000000001688B5FE9000019FF000000000000000000000000000000000000002000000000hyprlock-0.9.1/src/auth/Pam.cpp#include "Pam.hpp"
#include "../core/hyprlock.hpp"
#include "../helpers/Log.hpp"
#include "../config/ConfigManager.hpp"

#include <filesystem>
#include <unistd.h>
#include <pwd.h>
#include <security/pam_appl.h>
#if __has_include(<security/pam_misc.h>)
#include <security/pam_misc.h>
#endif

#include <cstring>
#include <thread>

int conv(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr) {
    const auto           CONVERSATIONSTATE = (CPam::SPamConversationState*)appdata_ptr;
    struct pam_response* pamReply          = (struct pam_response*)calloc(num_msg, sizeof(struct pam_response));
    bool                 initialPrompt     = true;

    for (int i = 0; i < num_msg; ++i) {
        switch (msg[i]->msg_style) {
            case PAM_PROMPT_ECHO_OFF:
            case PAM_PROMPT_ECHO_ON: {
                const auto PROMPT        = std::string(msg[i]->msg);
                const auto PROMPTCHANGED = PROMPT != CONVERSATIONSTATE->prompt;
                Debug::log(LOG, "PAM_PROMPT: {}", PROMPT);

                if (PROMPTCHANGED)
                    g_pHyprlock->enqueueForceUpdateTimers();

                // Some pam configurations ask for the password twice for whatever reason (Fedora su for example)
                // When the prompt is the same as the last one, I guess our answer can be the same.
                if (!initialPrompt && PROMPTCHANGED) {
                    CONVERSATIONSTATE->prompt = PROMPT;
                    CONVERSATIONSTATE->waitForInput();
                }

                // Needed for unlocks via SIGUSR1
                if (g_pHyprlock->isUnlocked())
                    return PAM_CONV_ERR;

                pamReply[i].resp = strdup(CONVERSATIONSTATE->input.c_str());
                initialPrompt    = false;
            } break;
            case PAM_ERROR_MSG: Debug::log(ERR, "PAM: {}", msg[i]->msg); break;
            case PAM_TEXT_INFO:
                Debug::log(LOG, "PAM: {}", msg[i]->msg);
                // Targets this log from pam_faillock: https://github.com/linux-pam/linux-pam/blob/fa3295e079dbbc241906f29bde5fb71bc4172771/modules/pam_faillock/pam_faillock.c#L417
                if (const auto MSG = std::string(msg[i]->msg); MSG.contains("left to unlock")) {
                    CONVERSATIONSTATE->failText        = MSG;
                    CONVERSATIONSTATE->failTextFromPam = true;
                }
                break;
        }
    }

    *resp = pamReply;
    return PAM_SUCCESS;
}

CPam::CPam() {
    static const auto PAMMODULE = g_pConfigManager->getValue<Hyprlang::STRING>("auth:pam:module");
    m_sPamModule                = *PAMMODULE;

    if (!std::filesystem::exists(std::filesystem::path("/etc/pam.d/") / m_sPamModule)) {
        Debug::log(ERR, R"(Pam module "/etc/pam.d/{}" does not exist! Falling back to "/etc/pam.d/su")", m_sPamModule);
        m_sPamModule = "su";
    }

    m_sConversationState.waitForInput = [this]() { this->waitForInput(); };
}

CPam::~CPam() {
    ;
}

void CPam::init() {
    m_thread = std::thread([this]() {
        while (true) {
            resetConversation();

            // Initial input
            m_sConversationState.prompt = "Password: ";
            waitForInput();

            // For grace or SIGUSR1 unlocks
            if (g_pHyprlock->isUnlocked())
                return;

            const auto AUTHENTICATED = auth();

            // For SIGUSR1 unlocks
            if (g_pHyprlock->isUnlocked())
                return;

            if (!AUTHENTICATED)
                g_pAuth->enqueueFail(m_sConversationState.failText, AUTH_IMPL_PAM);
            else {
                g_pAuth->enqueueUnlock();
                return;
            }
        }
    });
}

bool CPam::auth() {
    const pam_conv localConv   = {.conv = conv, .appdata_ptr = (void*)&m_sConversationState};
    pam_handle_t*  handle      = nullptr;
    auto           uidPassword = getpwuid(getuid());
    RASSERT(uidPassword && uidPassword->pw_name, "Failed to get username (getpwuid)");

    int ret = pam_start(m_sPamModule.c_str(), uidPassword->pw_name, &localConv, &handle);

    if (ret != PAM_SUCCESS) {
        m_sConversationState.failText = "pam_start failed";
        Debug::log(ERR, "auth: pam_start failed for {}", m_sPamModule);
        return false;
    }

    ret = pam_authenticate(handle, 0);
    pam_end(handle, ret);
    handle = nullptr;

    m_sConversationState.waitingForPamAuth = false;

    if (ret != PAM_SUCCESS) {
        if (!m_sConversationState.failTextFromPam)
            m_sConversationState.failText = ret == PAM_AUTH_ERR ? "Authentication failed" : "pam_authenticate failed";
        Debug::log(ERR, "auth: {} for {}", m_sConversationState.failText, m_sPamModule);
        return false;
    }

    m_sConversationState.failText = "Successfully authenticated";
    Debug::log(LOG, "auth: authenticated for {}", m_sPamModule);

    return true;
}

void CPam::waitForInput() {
    std::unique_lock<std::mutex> lk(m_sConversationState.inputMutex);
    m_bBlockInput                          = false;
    m_sConversationState.waitingForPamAuth = false;
    m_sConversationState.inputRequested    = true;
    m_sConversationState.inputSubmittedCondition.wait(lk, [this] { return !m_sConversationState.inputRequested || g_pHyprlock->m_bTerminate; });
    m_bBlockInput = true;
}

void CPam::handleInput(const std::string& input) {
    std::unique_lock<std::mutex> lk(m_sConversationState.inputMutex);

    if (!m_sConversationState.inputRequested)
        Debug::log(ERR, "SubmitInput called, but the auth thread is not waiting for input!");

    m_sConversationState.input             = input;
    m_sConversationState.inputRequested    = false;
    m_sConversationState.waitingForPamAuth = true;
    m_sConversationState.inputSubmittedCondition.notify_all();
}

std::optional<std::string> CPam::getLastFailText() {
    return m_sConversationState.failText.empty() ? std::nullopt : std::optional(m_sConversationState.failText);
}

std::optional<std::string> CPam::getLastPrompt() {
    return m_sConversationState.prompt.empty() ? std::nullopt : std::optional(m_sConversationState.prompt);
}

bool CPam::checkWaiting() {
    return m_bBlockInput || m_sConversationState.waitingForPamAuth;
}

void CPam::terminate() {
    m_sConversationState.inputSubmittedCondition.notify_all();
    if (m_thread.joinable())
        m_thread.join();
}

void CPam::resetConversation() {
    m_sConversationState.input             = "";
    m_sConversationState.waitingForPamAuth = false;
    m_sConversationState.inputRequested    = false;
    m_sConversationState.failTextFromPam   = false;
}
0707010000001F000081A4000000000000000000000001688B5FE9000005DC000000000000000000000000000000000000002000000000hyprlock-0.9.1/src/auth/Pam.hpp#pragma once

#include "Auth.hpp"

#include <optional>
#include <string>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <thread>

class CPam : public IAuthImplementation {
  public:
    struct SPamConversationState {
        std::string             input    = "";
        std::string             prompt   = "";
        std::string             failText = "";

        std::mutex              inputMutex;
        std::condition_variable inputSubmittedCondition;

        bool                    waitingForPamAuth = false;
        bool                    inputRequested    = false;
        bool                    failTextFromPam   = false;
        std::function<void()>   waitForInput      = []() {};
    };

    CPam();

    void waitForInput();

    virtual ~CPam();
    virtual eAuthImplementations getImplType() {
        return AUTH_IMPL_PAM;
    }
    virtual void                       init();
    virtual void                       handleInput(const std::string& input);
    virtual bool                       checkWaiting();
    virtual std::optional<std::string> getLastFailText();
    virtual std::optional<std::string> getLastPrompt();
    virtual void                       terminate();

  private:
    std::thread           m_thread;
    SPamConversationState m_sConversationState;

    bool                  m_bBlockInput = true;

    std::string           m_sPamModule;

    bool                  auth();
    void                  resetConversation();
};
07070100000020000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001A00000000hyprlock-0.9.1/src/config07070100000021000081A4000000000000000000000001688B5FE900000EA5000000000000000000000000000000000000002F00000000hyprlock-0.9.1/src/config/ConfigDataValues.hpp#pragma once
#include "../helpers/Log.hpp"
#include "../helpers/Color.hpp"
#include <hyprutils/math/Vector2D.hpp>
#include <hyprutils/string/VarList.hpp>
#include <any>
#include <string>
#include <vector>
#include <cmath>

using namespace Hyprutils::String;

enum eConfigValueDataTypes {
    CVD_TYPE_INVALID  = -1,
    CVD_TYPE_LAYOUT   = 0,
    CVD_TYPE_GRADIENT = 1,
};

class ICustomConfigValueData {
  public:
    virtual ~ICustomConfigValueData() = 0;

    virtual eConfigValueDataTypes getDataType() = 0;

    virtual std::string           toString() = 0;
};

class CLayoutValueData : public ICustomConfigValueData {
  public:
    CLayoutValueData() = default;
    virtual ~CLayoutValueData() {};

    virtual eConfigValueDataTypes getDataType() {
        return CVD_TYPE_LAYOUT;
    }

    virtual std::string toString() {
        return std::format("{}{},{}{}", m_vValues.x, (m_sIsRelative.x) ? "%" : "px", m_vValues.y, (m_sIsRelative.y) ? "%" : "px");
    }

    static CLayoutValueData* fromAnyPv(const std::any& v) {
        RASSERT(v.type() == typeid(void*), "Invalid config value type");
        const auto P = (CLayoutValueData*)std::any_cast<void*>(v);
        RASSERT(P, "Empty config value");
        return P;
    }

    Hyprutils::Math::Vector2D getAbsolute(const Hyprutils::Math::Vector2D& viewport) {
        return {
            (m_sIsRelative.x ? (m_vValues.x / 100) * viewport.x : m_vValues.x),
            (m_sIsRelative.y ? (m_vValues.y / 100) * viewport.y : m_vValues.y),
        };
    }

    Hyprutils::Math::Vector2D m_vValues;
    struct {
        bool x = false;
        bool y = false;
    } m_sIsRelative;
};

class CGradientValueData : public ICustomConfigValueData {
  public:
    CGradientValueData() {};
    CGradientValueData(CHyprColor col) {
        m_vColors.push_back(col);
        updateColorsOk();
    };
    virtual ~CGradientValueData() {};

    virtual eConfigValueDataTypes getDataType() {
        return CVD_TYPE_GRADIENT;
    }

    void reset(CHyprColor col) {
        m_vColors.clear();
        m_vColors.emplace_back(col);
        m_fAngle = 0;
        updateColorsOk();
    }

    void updateColorsOk() {
        m_vColorsOkLabA.clear();
        for (auto& c : m_vColors) {
            const auto OKLAB = c.asOkLab();
            m_vColorsOkLabA.emplace_back(OKLAB.l);
            m_vColorsOkLabA.emplace_back(OKLAB.a);
            m_vColorsOkLabA.emplace_back(OKLAB.b);
            m_vColorsOkLabA.emplace_back(c.a);
        }
    }

    /* Vector containing the colors */
    std::vector<CHyprColor> m_vColors;

    /* Vector containing pure colors for shoving into opengl */
    std::vector<float> m_vColorsOkLabA;

    /* Float corresponding to the angle (rad) */
    float m_fAngle = 0;

    /* Whether this gradient stores a fallback value (not exlicitly set) */
    bool m_bIsFallback = false;

    //
    bool operator==(const CGradientValueData& other) const {
        if (other.m_vColors.size() != m_vColors.size() || m_fAngle != other.m_fAngle)
            return false;

        for (size_t i = 0; i < m_vColors.size(); ++i)
            if (m_vColors[i] != other.m_vColors[i])
                return false;

        return true;
    }

    virtual std::string toString() {
        std::string result;
        for (auto& c : m_vColors) {
            result += std::format("{:x} ", c.getAsHex());
        }

        result += std::format("{}deg", (int)(m_fAngle * 180.0 / M_PI));
        return result;
    }

    static CGradientValueData* fromAnyPv(const std::any& v) {
        RASSERT(v.type() == typeid(void*), "Invalid config value type");
        const auto P = (CGradientValueData*)std::any_cast<void*>(v);
        RASSERT(P, "Empty config value");
        return P;
    }
};
07070100000022000081A4000000000000000000000001688B5FE900007D4D000000000000000000000000000000000000002C00000000hyprlock-0.9.1/src/config/ConfigManager.cpp#include "ConfigManager.hpp"
#include "ConfigDataValues.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../helpers/Log.hpp"
#include "../core/AnimationManager.hpp"
#include <hyprlang.hpp>
#include <hyprutils/string/String.hpp>
#include <hyprutils/path/Path.hpp>
#include <filesystem>
#include <glob.h>
#include <cstring>
#include <mutex>

using namespace Hyprutils::String;
using namespace Hyprutils::Animation;

ICustomConfigValueData::~ICustomConfigValueData() {
    ; // empty
}

static Hyprlang::CParseResult handleSource(const char* c, const char* v) {
    const std::string      VALUE   = v;
    const std::string      COMMAND = c;

    const auto             RESULT = g_pConfigManager->handleSource(COMMAND, VALUE);

    Hyprlang::CParseResult result;
    if (RESULT.has_value())
        result.setError(RESULT.value().c_str());
    return result;
}

static Hyprlang::CParseResult handleBezier(const char* c, const char* v) {
    const std::string      VALUE   = v;
    const std::string      COMMAND = c;

    const auto             RESULT = g_pConfigManager->handleBezier(COMMAND, VALUE);

    Hyprlang::CParseResult result;
    if (RESULT.has_value())
        result.setError(RESULT.value().c_str());
    return result;
}

static Hyprlang::CParseResult handleAnimation(const char* c, const char* v) {
    const std::string      VALUE   = v;
    const std::string      COMMAND = c;

    const auto             RESULT = g_pConfigManager->handleAnimation(COMMAND, VALUE);

    Hyprlang::CParseResult result;
    if (RESULT.has_value())
        result.setError(RESULT.value().c_str());
    return result;
}

static Hyprlang::CParseResult configHandleLayoutOption(const char* v, void** data) {
    const std::string      VALUE = v;

    Hyprlang::CParseResult result;

    if (!*data)
        *data = new CLayoutValueData();

    const auto DATA  = (CLayoutValueData*)(*data);
    const auto SPLIT = VALUE.find(',');
    if (SPLIT == std::string::npos) {
        result.setError(std::format("expected two comma seperated values, got {}", VALUE).c_str());
        return result;
    }

    auto lhs = VALUE.substr(0, SPLIT);
    auto rhs = VALUE.substr(SPLIT + 1);
    if (rhs.starts_with(" "))
        rhs = rhs.substr(1);

    if (lhs.contains(",") || rhs.contains(",")) {
        result.setError(std::format("too many arguments in {}", VALUE).c_str());
        return result;
    }

    if (lhs.ends_with("%")) {
        DATA->m_sIsRelative.x = true;
        lhs.pop_back();
    }

    if (rhs.ends_with("%")) {
        DATA->m_sIsRelative.y = true;
        rhs.pop_back();
    }

    DATA->m_vValues = Hyprutils::Math::Vector2D{std::stof(lhs), std::stof(rhs)};

    return result;
}

static void configHandleLayoutOptionDestroy(void** data) {
    if (*data)
        delete reinterpret_cast<CLayoutValueData*>(*data);
}

static Hyprlang::CParseResult configHandleGradientSet(const char* VALUE, void** data) {
    const std::string V = VALUE;

    if (!*data)
        *data = new CGradientValueData();

    const auto DATA = reinterpret_cast<CGradientValueData*>(*data);

    DATA->m_vColors.clear();
    DATA->m_bIsFallback = false;

    std::string parseError = "";
    std::string rolling    = V;

    while (!rolling.empty()) {
        const auto  SPACEPOS = rolling.find(' ');
        const bool  LAST     = SPACEPOS == std::string::npos;
        std::string var      = rolling.substr(0, SPACEPOS);
        if (var.find("rgb") != std::string::npos) { // rgb(a)
            const auto CLOSEPARENPOS = rolling.find(')');
            if (CLOSEPARENPOS == std::string::npos || CLOSEPARENPOS + 1 >= rolling.length()) {
                var = trim(rolling);
                rolling.clear();
            } else {
                var     = rolling.substr(0, CLOSEPARENPOS + 1);
                rolling = trim(rolling.substr(CLOSEPARENPOS + 2));
            }
        } else if (var.find("deg") != std::string::npos) { // last arg
            try {
                DATA->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (M_PI / 180.0); // radians
            } catch (...) {
                Debug::log(WARN, "Error parsing gradient {}", V);
                parseError = "Error parsing gradient " + V;
            }

            break;
        } else // hex
            rolling = trim(rolling.substr(LAST ? rolling.length() : SPACEPOS + 1));

        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;
        }

        if (var.empty())
            continue;

        try {
            DATA->m_vColors.emplace_back(configStringToInt(var));
        } catch (std::exception& e) {
            Debug::log(WARN, "Error parsing gradient {}", V);
            parseError = "Error parsing gradient " + V + ": " + e.what();
        }
    }

    if (V.empty()) {
        DATA->m_bIsFallback = true;
        DATA->m_vColors.emplace_back(0); // transparent
    }

    if (DATA->m_vColors.size() == 0) {
        Debug::log(WARN, "Error parsing gradient {}", V);
        parseError = "Error parsing gradient " + V + ": No colors?";

        DATA->m_vColors.emplace_back(0); // transparent
    }

    DATA->updateColorsOk();

    Hyprlang::CParseResult result;
    if (!parseError.empty())
        result.setError(parseError.c_str());

    return result;
}

static void configHandleGradientDestroy(void** data) {
    if (*data)
        delete reinterpret_cast<CGradientValueData*>(*data);
}

static std::string getMainConfigPath() {
    static const auto paths = Hyprutils::Path::findConfig("hyprlock");
    if (paths.first.has_value())
        return paths.first.value();
    else
        throw std::runtime_error("Could not find config in HOME, XDG_CONFIG_HOME, XDG_CONFIG_DIRS or /etc/hypr.");
}

CConfigManager::CConfigManager(std::string configPath) :
    m_config(configPath.empty() ? getMainConfigPath().c_str() : configPath.c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = configPath.empty()}) {
    configCurrentPath = configPath.empty() ? getMainConfigPath() : configPath;
}

inline static constexpr auto GRADIENTCONFIG = [](const char* default_value) -> Hyprlang::CUSTOMTYPE {
    return Hyprlang::CUSTOMTYPE{&configHandleGradientSet, configHandleGradientDestroy, default_value};
};

inline static constexpr auto LAYOUTCONFIG = [](const char* default_value) -> Hyprlang::CUSTOMTYPE {
    return Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, default_value};
};

void CConfigManager::init() {

#define SHADOWABLE(name)                                                                                                                                                           \
    m_config.addSpecialConfigValue(name, "shadow_size", Hyprlang::INT{3});                                                                                                         \
    m_config.addSpecialConfigValue(name, "shadow_passes", Hyprlang::INT{0});                                                                                                       \
    m_config.addSpecialConfigValue(name, "shadow_color", Hyprlang::INT{0xFF000000});                                                                                               \
    m_config.addSpecialConfigValue(name, "shadow_boost", Hyprlang::FLOAT{1.2});

#define CLICKABLE(name) m_config.addSpecialConfigValue(name, "onclick", Hyprlang::STRING{""});

    m_config.addConfigValue("general:text_trim", Hyprlang::INT{1});
    m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0});
    m_config.addConfigValue("general:ignore_empty_input", Hyprlang::INT{0});
    m_config.addConfigValue("general:immediate_render", Hyprlang::INT{0});
    m_config.addConfigValue("general:fractional_scaling", Hyprlang::INT{2});
    m_config.addConfigValue("general:screencopy_mode", Hyprlang::INT{0});
    m_config.addConfigValue("general:fail_timeout", Hyprlang::INT{2000});

    m_config.addConfigValue("auth:pam:enabled", Hyprlang::INT{1});
    m_config.addConfigValue("auth:pam:module", Hyprlang::STRING{"hyprlock"});
    m_config.addConfigValue("auth:fingerprint:enabled", Hyprlang::INT{0});
    m_config.addConfigValue("auth:fingerprint:ready_message", Hyprlang::STRING{"(Scan fingerprint to unlock)"});
    m_config.addConfigValue("auth:fingerprint:present_message", Hyprlang::STRING{"Scanning fingerprint"});
    m_config.addConfigValue("auth:fingerprint:retry_delay", Hyprlang::INT{250});

    m_config.addConfigValue("animations:enabled", Hyprlang::INT{1});

    m_config.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
    m_config.addSpecialConfigValue("background", "monitor", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("background", "path", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("background", "color", Hyprlang::INT{0xFF111111});
    m_config.addSpecialConfigValue("background", "blur_size", Hyprlang::INT{8});
    m_config.addSpecialConfigValue("background", "blur_passes", Hyprlang::INT{0});
    m_config.addSpecialConfigValue("background", "noise", Hyprlang::FLOAT{0.0117});
    m_config.addSpecialConfigValue("background", "contrast", Hyprlang::FLOAT{0.8917});
    m_config.addSpecialConfigValue("background", "brightness", Hyprlang::FLOAT{0.8172});
    m_config.addSpecialConfigValue("background", "vibrancy", Hyprlang::FLOAT{0.1686});
    m_config.addSpecialConfigValue("background", "vibrancy_darkness", Hyprlang::FLOAT{0.05});
    m_config.addSpecialConfigValue("background", "zindex", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("background", "reload_time", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("background", "reload_cmd", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("background", "crossfade_time", Hyprlang::FLOAT{-1.0});

    m_config.addSpecialCategory("shape", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
    m_config.addSpecialConfigValue("shape", "monitor", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("shape", "size", LAYOUTCONFIG("100,100"));
    m_config.addSpecialConfigValue("shape", "rounding", Hyprlang::INT{0});
    m_config.addSpecialConfigValue("shape", "border_size", Hyprlang::INT{0});
    m_config.addSpecialConfigValue("shape", "border_color", GRADIENTCONFIG("0xFF00CFE6"));
    m_config.addSpecialConfigValue("shape", "color", Hyprlang::INT{0xFF111111});
    m_config.addSpecialConfigValue("shape", "position", LAYOUTCONFIG("0,0"));
    m_config.addSpecialConfigValue("shape", "halign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("shape", "valign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("shape", "rotate", Hyprlang::FLOAT{0});
    m_config.addSpecialConfigValue("shape", "xray", Hyprlang::INT{0});
    m_config.addSpecialConfigValue("shape", "zindex", Hyprlang::INT{0});
    SHADOWABLE("shape");
    CLICKABLE("shape");

    m_config.addSpecialCategory("image", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
    m_config.addSpecialConfigValue("image", "monitor", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("image", "path", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("image", "size", Hyprlang::INT{150});
    m_config.addSpecialConfigValue("image", "rounding", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("image", "border_size", Hyprlang::INT{4});
    m_config.addSpecialConfigValue("image", "border_color", GRADIENTCONFIG("0xFFDDDDDD"));
    m_config.addSpecialConfigValue("image", "position", LAYOUTCONFIG("0,0"));
    m_config.addSpecialConfigValue("image", "halign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("image", "valign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("image", "rotate", Hyprlang::FLOAT{0});
    m_config.addSpecialConfigValue("image", "reload_time", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("image", "reload_cmd", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("image", "zindex", Hyprlang::INT{0});
    SHADOWABLE("image");
    CLICKABLE("image");

    m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
    m_config.addSpecialConfigValue("input-field", "monitor", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("input-field", "size", LAYOUTCONFIG("400,90"));
    m_config.addSpecialConfigValue("input-field", "inner_color", Hyprlang::INT{0xFFDDDDDD});
    m_config.addSpecialConfigValue("input-field", "outer_color", GRADIENTCONFIG("0xFF111111"));
    m_config.addSpecialConfigValue("input-field", "outline_thickness", Hyprlang::INT{4});
    m_config.addSpecialConfigValue("input-field", "dots_size", Hyprlang::FLOAT{0.25});
    m_config.addSpecialConfigValue("input-field", "dots_center", Hyprlang::INT{1});
    m_config.addSpecialConfigValue("input-field", "dots_spacing", Hyprlang::FLOAT{0.2});
    m_config.addSpecialConfigValue("input-field", "dots_rounding", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("input-field", "dots_text_format", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("input-field", "fade_on_empty", Hyprlang::INT{1});
    m_config.addSpecialConfigValue("input-field", "fade_timeout", Hyprlang::INT{2000});
    m_config.addSpecialConfigValue("input-field", "font_color", Hyprlang::INT{0xFF000000});
    m_config.addSpecialConfigValue("input-field", "font_family", Hyprlang::STRING{"Sans"});
    m_config.addSpecialConfigValue("input-field", "halign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("input-field", "valign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("input-field", "position", LAYOUTCONFIG("0,0"));
    m_config.addSpecialConfigValue("input-field", "placeholder_text", Hyprlang::STRING{"<i>Input Password</i>"});
    m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0});
    m_config.addSpecialConfigValue("input-field", "hide_input_base_color", Hyprlang::INT{0xEE00FF99});
    m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("input-field", "check_color", GRADIENTCONFIG("0xFF22CC88"));
    m_config.addSpecialConfigValue("input-field", "fail_color", GRADIENTCONFIG("0xFFCC2222"));
    m_config.addSpecialConfigValue("input-field", "fail_text", Hyprlang::STRING{"<i>$FAIL</i>"});
    m_config.addSpecialConfigValue("input-field", "capslock_color", GRADIENTCONFIG(""));
    m_config.addSpecialConfigValue("input-field", "numlock_color", GRADIENTCONFIG(""));
    m_config.addSpecialConfigValue("input-field", "bothlock_color", GRADIENTCONFIG(""));
    m_config.addSpecialConfigValue("input-field", "invert_numlock", Hyprlang::INT{0});
    m_config.addSpecialConfigValue("input-field", "swap_font_color", Hyprlang::INT{0});
    m_config.addSpecialConfigValue("input-field", "zindex", Hyprlang::INT{0});
    SHADOWABLE("input-field");

    m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
    m_config.addSpecialConfigValue("label", "monitor", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("label", "position", LAYOUTCONFIG("0,0"));
    m_config.addSpecialConfigValue("label", "color", Hyprlang::INT{0xFFFFFFFF});
    m_config.addSpecialConfigValue("label", "font_size", Hyprlang::INT{16});
    m_config.addSpecialConfigValue("label", "text", Hyprlang::STRING{"Sample Text"});
    m_config.addSpecialConfigValue("label", "font_family", Hyprlang::STRING{"Sans"});
    m_config.addSpecialConfigValue("label", "halign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("label", "rotate", Hyprlang::FLOAT{0});
    m_config.addSpecialConfigValue("label", "text_align", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("label", "zindex", Hyprlang::INT{0});
    SHADOWABLE("label");
    CLICKABLE("label");

    m_config.registerHandler(&::handleSource, "source", {.allowFlags = false});
    m_config.registerHandler(&::handleBezier, "bezier", {.allowFlags = false});
    m_config.registerHandler(&::handleAnimation, "animation", {.allowFlags = false});

    //
    // Init Animations
    //
    m_AnimationTree.createNode("global");

    // toplevel
    m_AnimationTree.createNode("fade", "global");
    m_AnimationTree.createNode("inputField", "global");

    // inputField
    m_AnimationTree.createNode("inputFieldColors", "inputField");
    m_AnimationTree.createNode("inputFieldFade", "inputField");
    m_AnimationTree.createNode("inputFieldWidth", "inputField");
    m_AnimationTree.createNode("inputFieldDots", "inputField");

    // fade
    m_AnimationTree.createNode("fadeIn", "fade");
    m_AnimationTree.createNode("fadeOut", "fade");

    // set config for root node
    m_AnimationTree.setConfigForNode("global", 1, 8.f, "default");
    m_AnimationTree.setConfigForNode("inputFieldColors", 1, 8.f, "linear");

    m_config.commence();

    auto result = m_config.parse();

    if (result.error)
        Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError());

#undef SHADOWABLE
#undef CLICKABLE
}

std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
    std::vector<CConfigManager::SWidgetConfig> result;

#define SHADOWABLE(name)                                                                                                                                                           \
    {"shadow_size", m_config.getSpecialConfigValue(name, "shadow_size", k.c_str())}, {"shadow_passes", m_config.getSpecialConfigValue(name, "shadow_passes", k.c_str())},          \
        {"shadow_color", m_config.getSpecialConfigValue(name, "shadow_color", k.c_str())}, {                                                                                       \
        "shadow_boost", m_config.getSpecialConfigValue(name, "shadow_boost", k.c_str())                                                                                            \
    }

#define CLICKABLE(name) {"onclick", m_config.getSpecialConfigValue(name, "onclick", k.c_str())}

    //
    auto keys = m_config.listKeysForSpecialCategory("background");
    result.reserve(keys.size());
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            .type = "background",
            .monitor = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("background", "monitor", k.c_str())),
            .values = {
                {"path", m_config.getSpecialConfigValue("background", "path", k.c_str())},
                {"color", m_config.getSpecialConfigValue("background", "color", k.c_str())},
                {"blur_size", m_config.getSpecialConfigValue("background", "blur_size", k.c_str())},
                {"blur_passes", m_config.getSpecialConfigValue("background", "blur_passes", k.c_str())},
                {"noise", m_config.getSpecialConfigValue("background", "noise", k.c_str())},
                {"contrast", m_config.getSpecialConfigValue("background", "contrast", k.c_str())},
                {"vibrancy", m_config.getSpecialConfigValue("background", "vibrancy", k.c_str())},
                {"brightness", m_config.getSpecialConfigValue("background", "brightness", k.c_str())},
                {"vibrancy_darkness", m_config.getSpecialConfigValue("background", "vibrancy_darkness", k.c_str())},
                {"zindex", m_config.getSpecialConfigValue("background", "zindex", k.c_str())},
                {"reload_time", m_config.getSpecialConfigValue("background", "reload_time", k.c_str())},
                {"reload_cmd", m_config.getSpecialConfigValue("background", "reload_cmd", k.c_str())},
                {"crossfade_time", m_config.getSpecialConfigValue("background", "crossfade_time", k.c_str())},
            }
        });
        // clang-format on
    }

    //
    keys = m_config.listKeysForSpecialCategory("shape");
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            .type = "shape",
            .monitor = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("shape", "monitor", k.c_str())),
            .values = {
                {"size", m_config.getSpecialConfigValue("shape", "size", k.c_str())},
                {"rounding", m_config.getSpecialConfigValue("shape", "rounding", k.c_str())},
                {"border_size", m_config.getSpecialConfigValue("shape", "border_size", k.c_str())},
                {"border_color", m_config.getSpecialConfigValue("shape", "border_color", k.c_str())},
                {"color", m_config.getSpecialConfigValue("shape", "color", k.c_str())},
                {"position", m_config.getSpecialConfigValue("shape", "position", k.c_str())},
                {"halign", m_config.getSpecialConfigValue("shape", "halign", k.c_str())},
                {"valign", m_config.getSpecialConfigValue("shape", "valign", k.c_str())},
                {"rotate", m_config.getSpecialConfigValue("shape", "rotate", k.c_str())},
                {"xray", m_config.getSpecialConfigValue("shape", "xray", k.c_str())},
                {"zindex", m_config.getSpecialConfigValue("shape", "zindex", k.c_str())},
                SHADOWABLE("shape"),
                CLICKABLE("shape"),
            }
        });
        // clang-format on
    }

    //
    keys = m_config.listKeysForSpecialCategory("image");
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            .type = "image",
            .monitor = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("image", "monitor", k.c_str())),
            .values = {
                {"path", m_config.getSpecialConfigValue("image", "path", k.c_str())},
                {"size", m_config.getSpecialConfigValue("image", "size", k.c_str())},
                {"rounding", m_config.getSpecialConfigValue("image", "rounding", k.c_str())},
                {"border_size", m_config.getSpecialConfigValue("image", "border_size", k.c_str())},
                {"border_color", m_config.getSpecialConfigValue("image", "border_color", k.c_str())},
                {"position", m_config.getSpecialConfigValue("image", "position", k.c_str())},
                {"halign", m_config.getSpecialConfigValue("image", "halign", k.c_str())},
                {"valign", m_config.getSpecialConfigValue("image", "valign", k.c_str())},
                {"rotate", m_config.getSpecialConfigValue("image", "rotate", k.c_str())},
                {"reload_time", m_config.getSpecialConfigValue("image", "reload_time", k.c_str())},
                {"reload_cmd", m_config.getSpecialConfigValue("image", "reload_cmd", k.c_str())},
                {"zindex", m_config.getSpecialConfigValue("image", "zindex", k.c_str())},
                SHADOWABLE("image"),
                CLICKABLE("image"),
            }
        });
        // clang-format on
    }

    keys = m_config.listKeysForSpecialCategory("input-field");
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            .type = "input-field",
            .monitor = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("input-field", "monitor", k.c_str())),
            .values = {
                {"size", m_config.getSpecialConfigValue("input-field", "size", k.c_str())},
                {"inner_color", m_config.getSpecialConfigValue("input-field", "inner_color", k.c_str())},
                {"outer_color", m_config.getSpecialConfigValue("input-field", "outer_color", k.c_str())},
                {"outline_thickness", m_config.getSpecialConfigValue("input-field", "outline_thickness", k.c_str())},
                {"dots_size", m_config.getSpecialConfigValue("input-field", "dots_size", k.c_str())},
                {"dots_spacing", m_config.getSpecialConfigValue("input-field", "dots_spacing", k.c_str())},
                {"dots_center", m_config.getSpecialConfigValue("input-field", "dots_center", k.c_str())},
                {"dots_rounding", m_config.getSpecialConfigValue("input-field", "dots_rounding", k.c_str())},
                {"dots_text_format", m_config.getSpecialConfigValue("input-field", "dots_text_format", k.c_str())},
                {"fade_on_empty", m_config.getSpecialConfigValue("input-field", "fade_on_empty", k.c_str())},
                {"fade_timeout", m_config.getSpecialConfigValue("input-field", "fade_timeout", k.c_str())},
                {"font_color", m_config.getSpecialConfigValue("input-field", "font_color", k.c_str())},
                {"font_family", m_config.getSpecialConfigValue("input-field", "font_family", k.c_str())},
                {"halign", m_config.getSpecialConfigValue("input-field", "halign", k.c_str())},
                {"valign", m_config.getSpecialConfigValue("input-field", "valign", k.c_str())},
                {"position", m_config.getSpecialConfigValue("input-field", "position", k.c_str())},
                {"placeholder_text", m_config.getSpecialConfigValue("input-field", "placeholder_text", k.c_str())},
                {"hide_input", m_config.getSpecialConfigValue("input-field", "hide_input", k.c_str())},
                {"hide_input_base_color", m_config.getSpecialConfigValue("input-field", "hide_input_base_color", k.c_str())},
                {"rounding", m_config.getSpecialConfigValue("input-field", "rounding", k.c_str())},
                {"check_color", m_config.getSpecialConfigValue("input-field", "check_color", k.c_str())},
                {"fail_color", m_config.getSpecialConfigValue("input-field", "fail_color", k.c_str())},
                {"fail_text", m_config.getSpecialConfigValue("input-field", "fail_text", k.c_str())},
                {"capslock_color", m_config.getSpecialConfigValue("input-field", "capslock_color", k.c_str())},
                {"numlock_color", m_config.getSpecialConfigValue("input-field", "numlock_color", k.c_str())},
                {"bothlock_color", m_config.getSpecialConfigValue("input-field", "bothlock_color", k.c_str())},
                {"invert_numlock", m_config.getSpecialConfigValue("input-field", "invert_numlock", k.c_str())},
                {"swap_font_color", m_config.getSpecialConfigValue("input-field", "swap_font_color", k.c_str())},
                {"zindex", m_config.getSpecialConfigValue("input-field", "zindex", k.c_str())},
                SHADOWABLE("input-field"),
            }
        });
        // clang-format on
    }

    keys = m_config.listKeysForSpecialCategory("label");
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            .type = "label",
            .monitor = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("label", "monitor", k.c_str())),
            .values = {
                {"position", m_config.getSpecialConfigValue("label", "position", k.c_str())},
                {"color", m_config.getSpecialConfigValue("label", "color", k.c_str())},
                {"font_size", m_config.getSpecialConfigValue("label", "font_size", k.c_str())},
                {"font_family", m_config.getSpecialConfigValue("label", "font_family", k.c_str())},
                {"text", m_config.getSpecialConfigValue("label", "text", k.c_str())},
                {"halign", m_config.getSpecialConfigValue("label", "halign", k.c_str())},
                {"valign", m_config.getSpecialConfigValue("label", "valign", k.c_str())},
                {"rotate", m_config.getSpecialConfigValue("label", "rotate", k.c_str())},
                {"text_align", m_config.getSpecialConfigValue("label", "text_align", k.c_str())},
                {"zindex", m_config.getSpecialConfigValue("label", "zindex", k.c_str())},
                SHADOWABLE("label"),
                CLICKABLE("label"),
            }
        });
        // clang-format on
    }

    return result;
}

std::optional<std::string> CConfigManager::handleSource(const std::string& command, const std::string& rawpath) {
    if (rawpath.length() < 2) {
        Debug::log(ERR, "source= path garbage");
        return "source path " + rawpath + " bogus!";
    }
    std::unique_ptr<glob_t, void (*)(glob_t*)> glob_buf{new glob_t, [](glob_t* g) { globfree(g); }};
    memset(glob_buf.get(), 0, sizeof(glob_t));

    const auto CURRENTDIR = std::filesystem::path(configCurrentPath).parent_path().string();

    if (auto r = glob(absolutePath(rawpath, CURRENTDIR).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");
        Debug::log(ERR, "{}", err);
        return err;
    }

    for (size_t i = 0; i < glob_buf->gl_pathc; i++) {
        const auto PATH = absolutePath(glob_buf->gl_pathv[i], CURRENTDIR);

        if (PATH.empty() || PATH == configCurrentPath) {
            Debug::log(WARN, "source= skipping invalid path");
            continue;
        }

        if (!std::filesystem::is_regular_file(PATH)) {
            if (std::filesystem::exists(PATH)) {
                Debug::log(WARN, "source= skipping non-file {}", PATH);
                continue;
            }

            Debug::log(ERR, "source= file doesnt exist");
            return "source file " + PATH + " doesn't exist!";
        }

        // allow for nested config parsing
        auto backupConfigPath = configCurrentPath;
        configCurrentPath     = PATH;

        m_config.parseFile(PATH.c_str());

        configCurrentPath = backupConfigPath;
    }

    return {};
}

std::optional<std::string> CConfigManager::handleBezier(const std::string& command, const std::string& args) {
    const auto  ARGS = CVarList(args);

    std::string bezierName = ARGS[0];

    if (ARGS[1] == "")
        return "too few arguments";
    float p1x = std::stof(ARGS[1]);

    if (ARGS[2] == "")
        return "too few arguments";
    float p1y = std::stof(ARGS[2]);

    if (ARGS[3] == "")
        return "too few arguments";
    float p2x = std::stof(ARGS[3]);

    if (ARGS[4] == "")
        return "too few arguments";
    float p2y = std::stof(ARGS[4]);

    if (ARGS[5] != "")
        return "too many arguments";

    g_pAnimationManager->addBezierWithName(bezierName, Vector2D(p1x, p1y), Vector2D(p2x, p2y));

    return {};
}

std::optional<std::string> CConfigManager::handleAnimation(const std::string& command, const std::string& args) {
    const auto ARGS = CVarList(args);

    const auto ANIMNAME = ARGS[0];

    if (!m_AnimationTree.nodeExists(ANIMNAME))
        return "no such animation";

    // This helper casts strings like "1", "true", "off", "yes"... to int.
    int64_t enabledInt = configStringToInt(ARGS[1]);

    // Checking that the int is 1 or 0 because the helper can return integers out of range.
    if (enabledInt > 1 || enabledInt < 0)
        return "invalid animation on/off state";

    if (!enabledInt) {
        m_AnimationTree.setConfigForNode(ANIMNAME, 0, 1, "default");
        return {};
    }

    int64_t speed = -1;

    // speed
    if (isNumber(ARGS[2], true)) {
        speed = std::stof(ARGS[2]);

        if (speed <= 0) {
            speed = 1.f;
            return "invalid speed";
        }
    } else {
        speed = 10.f;
        return "invalid speed";
    }

    std::string bezierName = ARGS[3];
    // ARGS[4] (style) currently usused by hyprlock
    m_AnimationTree.setConfigForNode(ANIMNAME, enabledInt, speed, bezierName, "");

    if (!g_pAnimationManager->bezierExists(bezierName)) {
        const auto PANIMNODE      = m_AnimationTree.getConfig(ANIMNAME);
        PANIMNODE->internalBezier = "default";
        return "no such bezier";
    }

    return {};
}
07070100000023000081A4000000000000000000000001688B5FE9000004ED000000000000000000000000000000000000002C00000000hyprlock-0.9.1/src/config/ConfigManager.hpp#pragma once

#include <hyprutils/animation/AnimationConfig.hpp>

#include <hyprlang.hpp>
#include <optional>
#include <vector>
#include <unordered_map>

#include "../defines.hpp"

class CConfigManager {
  public:
    CConfigManager(std::string configPath);
    void init();

    template <typename T>
    Hyprlang::CSimpleConfigValue<T> getValue(const std::string& name) {
        return Hyprlang::CSimpleConfigValue<T>(&m_config, name.c_str());
    }

    struct SWidgetConfig {
        std::string                               type;
        std::string                               monitor;

        std::unordered_map<std::string, std::any> values;
    };

    std::vector<SWidgetConfig>                 getWidgetConfigs();

    std::optional<std::string>                 handleSource(const std::string&, const std::string&);
    std::optional<std::string>                 handleBezier(const std::string&, const std::string&);
    std::optional<std::string>                 handleAnimation(const std::string&, const std::string&);

    std::string                                configCurrentPath;

    Hyprutils::Animation::CAnimationConfigTree m_AnimationTree;

  private:
    Hyprlang::CConfig m_config;
};

inline UP<CConfigManager> g_pConfigManager;
07070100000024000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001800000000hyprlock-0.9.1/src/core07070100000025000081A4000000000000000000000001688B5FE900001368000000000000000000000000000000000000002D00000000hyprlock-0.9.1/src/core/AnimationManager.cpp#include "AnimationManager.hpp"
#include "../helpers/AnimatedVariable.hpp"
#include "../config/ConfigDataValues.hpp"
#include "../config/ConfigManager.hpp"

CHyprlockAnimationManager::CHyprlockAnimationManager() {
    addBezierWithName("linear", {0, 0}, {1, 1});
}

template <Animable VarType>
void updateVariable(CAnimatedVariable<VarType>& av, const float POINTY, bool warp = false) {
    if (warp || !av.enabled() || av.value() == av.goal()) {
        av.warp(true, false);
        return;
    }

    const auto DELTA = av.goal() - av.begun();
    av.value()       = av.begun() + DELTA * POINTY;
}

void updateColorVariable(CAnimatedVariable<CHyprColor>& av, const float POINTY, bool warp = false) {
    if (warp || !av.enabled() || 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)};
}

void updateGradientVariable(CAnimatedVariable<CGradientValueData>& av, const float POINTY, bool warp = false) {
    if (warp || av.value() == av.goal()) {
        av.warp(true, false);
        return;
    }

    av.value().m_vColors.resize(av.goal().m_vColors.size(), av.goal().m_vColors.back());

    for (size_t i = 0; i < av.value().m_vColors.size(); ++i) {
        const CHyprColor&          sourceCol = (i < av.begun().m_vColors.size()) ? av.begun().m_vColors[i] : av.begun().m_vColors.back();
        const CHyprColor&          targetCol = (i < av.goal().m_vColors.size()) ? av.goal().m_vColors[i] : av.goal().m_vColors.back();

        const auto&                L1 = sourceCol.asOkLab();
        const auto&                L2 = targetCol.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().m_vColors[i] = {lerped, lerp(sourceCol.a, targetCol.a, POINTY)};
    }

    if (av.begun().m_fAngle != av.goal().m_fAngle) {
        const float DELTA   = av.goal().m_fAngle - av.begun().m_fAngle;
        av.value().m_fAngle = av.begun().m_fAngle + DELTA * POINTY;
    }

    av.value().updateColorsOk();
}

void CHyprlockAnimationManager::tick() {
    static const auto ANIMATIONSENABLED = g_pConfigManager->getValue<Hyprlang::INT>("animations:enabled");
    for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) {
        const auto PAV = m_vActiveAnimatedVariables[i].lock();
        if (!PAV || !PAV->ok())
            continue;

        const auto SPENT   = PAV->getPercent();
        const auto PBEZIER = getBezier(PAV->getBezierName());
        const auto POINTY  = PBEZIER->getYForPoint(SPENT);
        const bool WARP    = !*ANIMATIONSENABLED || SPENT >= 1.f;

        switch (PAV->m_Type) {
            case AVARTYPE_FLOAT: {
                auto pTypedAV = dynamic_cast<CAnimatedVariable<float>*>(PAV.get());
                RASSERT(pTypedAV, "Failed to upcast animated float");
                updateVariable(*pTypedAV, POINTY, WARP);
            } break;
            case AVARTYPE_VECTOR: {
                auto pTypedAV = dynamic_cast<CAnimatedVariable<Vector2D>*>(PAV.get());
                RASSERT(pTypedAV, "Failed to upcast animated Vector2D");
                updateVariable(*pTypedAV, POINTY, WARP);
            } break;
            case AVARTYPE_COLOR: {
                auto pTypedAV = dynamic_cast<CAnimatedVariable<CHyprColor>*>(PAV.get());
                RASSERT(pTypedAV, "Failed to upcast animated CHyprColor");
                updateColorVariable(*pTypedAV, POINTY, WARP);
            } break;
            case AVARTYPE_GRADIENT: {
                auto pTypedAV = dynamic_cast<CAnimatedVariable<CGradientValueData>*>(PAV.get());
                RASSERT(pTypedAV, "Failed to upcast animated CGradientValueData");
                updateGradientVariable(*pTypedAV, POINTY, WARP);
            } break;
            default: continue;
        }

        PAV->onUpdate();
    }

    tickDone();
}

void CHyprlockAnimationManager::scheduleTick() {
    m_bTickScheduled = true;
}

void CHyprlockAnimationManager::onTicked() {
    m_bTickScheduled = false;
}
07070100000026000081A4000000000000000000000001688B5FE900000434000000000000000000000000000000000000002D00000000hyprlock-0.9.1/src/core/AnimationManager.hpp#pragma once

#include <hyprutils/animation/AnimationManager.hpp>
#include <hyprutils/animation/AnimatedVariable.hpp>

#include "../helpers/AnimatedVariable.hpp"
#include "../defines.hpp"

class CHyprlockAnimationManager : public Hyprutils::Animation::CAnimationManager {
  public:
    CHyprlockAnimationManager();

    void         tick();
    virtual void scheduleTick();
    virtual void onTicked();

    using SAnimationPropertyConfig = Hyprutils::Animation::SAnimationPropertyConfig;

    template <Animable VarType>
    void createAnimation(const VarType& v, PHLANIMVAR<VarType>& pav, SP<SAnimationPropertyConfig> pConfig) {
        constexpr const eAnimatedVarType EAVTYPE = typeToeAnimatedVarType<VarType>;
        const auto                       PAV     = makeShared<CAnimatedVariable<VarType>>();

        PAV->create(EAVTYPE, static_cast<Hyprutils::Animation::CAnimationManager*>(this), PAV, v);
        PAV->setConfig(pConfig);

        pav = std::move(PAV);
    }

    bool m_bTickScheduled = false;
};

inline UP<CHyprlockAnimationManager> g_pAnimationManager;
07070100000027000081A4000000000000000000000001688B5FE9000002DA000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/core/CursorShape.cpp#include "CursorShape.hpp"
#include "Seat.hpp"

CCursorShape::CCursorShape(SP<CCWpCursorShapeManagerV1> mgr) : mgr(mgr) {
    if (!g_pSeatManager->m_pPointer)
        return;

    dev = makeShared<CCWpCursorShapeDeviceV1>(mgr->sendGetPointer(g_pSeatManager->m_pPointer->resource()));
}

void CCursorShape::setShape(const wpCursorShapeDeviceV1Shape shape) {
    if (!g_pSeatManager->m_pPointer)
        return;

    if (!dev)
        dev = makeShared<CCWpCursorShapeDeviceV1>(mgr->sendGetPointer(g_pSeatManager->m_pPointer->resource()));

    shapeChanged = true;
    dev->sendSetShape(lastCursorSerial, shape);
}

void CCursorShape::hideCursor() {
    g_pSeatManager->m_pPointer->sendSetCursor(lastCursorSerial, nullptr, 0, 0);
}
07070100000028000081A4000000000000000000000001688B5FE9000001B0000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/core/CursorShape.hpp#pragma once

#include "../defines.hpp"
#include "cursor-shape-v1.hpp"

class CCursorShape {
  public:
    CCursorShape(SP<CCWpCursorShapeManagerV1> mgr);

    void     setShape(const wpCursorShapeDeviceV1Shape shape);
    void     hideCursor();

    uint32_t lastCursorSerial = 0;
    bool     shapeChanged     = false;

  private:
    SP<CCWpCursorShapeManagerV1> mgr = nullptr;
    SP<CCWpCursorShapeDeviceV1>  dev = nullptr;
};
07070100000029000081A4000000000000000000000001688B5FE900000B11000000000000000000000000000000000000002000000000hyprlock-0.9.1/src/core/Egl.cpp#include "Egl.hpp"
#include "../helpers/Log.hpp"

PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;

const EGLint                    config_attribs[] = {
    EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE,
};

const EGLint context_attribs[] = {
    EGL_CONTEXT_CLIENT_VERSION,
    2,
    EGL_NONE,
};

CEGL::CEGL(wl_display* display) {
    const char* _EXTS = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
    if (!_EXTS) {
        if (eglGetError() == EGL_BAD_DISPLAY)
            throw std::runtime_error("EGL_EXT_client_extensions not supported");
        else
            throw std::runtime_error("Failed to query EGL client extensions");
    }

    std::string EXTS = _EXTS;

    if (!EXTS.contains("EGL_EXT_platform_base"))
        throw std::runtime_error("EGL_EXT_platform_base not supported");

    if (!EXTS.contains("EGL_EXT_platform_wayland"))
        throw std::runtime_error("EGL_EXT_platform_wayland not supported");

    eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
    if (eglGetPlatformDisplayEXT == nullptr)
        throw std::runtime_error("Failed to get eglGetPlatformDisplayEXT");

    eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
    if (eglCreatePlatformWindowSurfaceEXT == nullptr)
        throw std::runtime_error("Failed to get eglCreatePlatformWindowSurfaceEXT");

    eglDisplay     = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, display, nullptr);
    EGLint matched = 0;
    if (eglDisplay == EGL_NO_DISPLAY) {
        Debug::log(CRIT, "Failed to create EGL display");
        goto error;
    }

    if (eglInitialize(eglDisplay, nullptr, nullptr) == EGL_FALSE) {
        Debug::log(CRIT, "Failed to initialize EGL");
        goto error;
    }

    if (!eglChooseConfig(eglDisplay, config_attribs, &eglConfig, 1, &matched)) {
        Debug::log(CRIT, "eglChooseConfig failed");
        goto error;
    }
    if (matched == 0) {
        Debug::log(CRIT, "Failed to match an EGL config");
        goto error;
    }

    eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, context_attribs);
    if (eglContext == EGL_NO_CONTEXT) {
        Debug::log(CRIT, "Failed to create EGL context");
        goto error;
    }

    return;

error:
    eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}

CEGL::~CEGL() {
    if (eglContext != EGL_NO_CONTEXT)
        eglDestroyContext(eglDisplay, eglContext);

    if (eglDisplay)
        eglTerminate(eglDisplay);

    eglReleaseThread();
}

void CEGL::makeCurrent(EGLSurface surf) {
    eglMakeCurrent(eglDisplay, surf, surf, eglContext);
}
0707010000002A000081A4000000000000000000000001688B5FE9000001F6000000000000000000000000000000000000002000000000hyprlock-0.9.1/src/core/Egl.hpp#pragma once

#include <EGL/egl.h>
#include <EGL/eglext.h>

#include "../defines.hpp"

class CEGL {
  public:
    CEGL(wl_display*);
    ~CEGL();

    EGLDisplay                               eglDisplay;
    EGLConfig                                eglConfig;
    EGLContext                               eglContext;

    PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;

    void                                     makeCurrent(EGLSurface surf);
};

inline UP<CEGL> g_pEGL;
0707010000002B000081A4000000000000000000000001688B5FE900001418000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/core/LockSurface.cpp#include "LockSurface.hpp"
#include "hyprlock.hpp"
#include "Egl.hpp"
#include "../config/ConfigManager.hpp"
#include "../core/AnimationManager.hpp"
#include "../helpers/Log.hpp"
#include "../renderer/Renderer.hpp"

CSessionLockSurface::~CSessionLockSurface() {
    if (eglWindow)
        wl_egl_window_destroy(eglWindow);
}

CSessionLockSurface::CSessionLockSurface(const SP<COutput>& pOutput) : m_outputRef(pOutput), m_outputID(pOutput->m_ID) {
    surface = makeShared<CCWlSurface>(g_pHyprlock->getCompositor()->sendCreateSurface());
    RASSERT(surface, "Couldn't create wl_surface");

    static const auto FRACTIONALSCALING = g_pConfigManager->getValue<Hyprlang::INT>("general:fractional_scaling");
    const auto        ENABLE_FSV1       = *FRACTIONALSCALING == 1 || /* auto enable */ (*FRACTIONALSCALING == 2);
    const auto        PFRACTIONALMGR    = g_pHyprlock->getFractionalMgr();
    const auto        PVIEWPORTER       = g_pHyprlock->getViewporter();

    if (ENABLE_FSV1 && PFRACTIONALMGR && PVIEWPORTER) {
        fractional = makeShared<CCWpFractionalScaleV1>(PFRACTIONALMGR->sendGetFractionalScale(surface->resource()));

        fractional->setPreferredScale([this](CCWpFractionalScaleV1*, uint32_t scale) {
            const bool SAMESCALE = fractionalScale == scale / 120.0;
            fractionalScale      = scale / 120.0;

            Debug::log(LOG, "Got fractional scale: {:.1f}%", fractionalScale * 100.F);

            if (!SAMESCALE && readyForFrame)
                onScaleUpdate();
        });

        viewport = makeShared<CCWpViewport>(PVIEWPORTER->sendGetViewport(surface->resource()));
    }

    if (!PFRACTIONALMGR)
        Debug::log(LOG, "No fractional-scale support! Oops, won't be able to scale!");
    if (!PVIEWPORTER)
        Debug::log(LOG, "No viewporter support! Oops, won't be able to scale!");

    lockSurface = makeShared<CCExtSessionLockSurfaceV1>(g_pHyprlock->getSessionLock()->sendGetLockSurface(surface->resource(), pOutput->m_wlOutput->resource()));
    RASSERT(lockSurface, "Couldn't create ext_session_lock_surface_v1");

    lockSurface->setConfigure([this](CCExtSessionLockSurfaceV1* r, uint32_t serial, uint32_t width, uint32_t height) { configure({(double)width, (double)height}, serial); });
}

void CSessionLockSurface::configure(const Vector2D& size_, uint32_t serial_) {
    Debug::log(LOG, "configure with serial {}", serial_);

    const bool SAMESERIAL = serial == serial_;
    const bool SAMESIZE   = logicalSize == size_;
    const bool SAMESCALE  = appliedScale == fractionalScale;

    const auto POUTPUT = m_outputRef.lock();

    serial       = serial_;
    logicalSize  = size_;
    appliedScale = fractionalScale;

    if (fractional) {
        size = (size_ * fractionalScale).floor();
        viewport->sendSetDestination(logicalSize.x, logicalSize.y);
        surface->sendSetBufferScale(1);
    } else {
        size = size_ * POUTPUT->scale;
        surface->sendSetBufferScale(POUTPUT->scale);
    }

    if (!SAMESERIAL)
        lockSurface->sendAckConfigure(serial);

    Debug::log(LOG, "Configuring surface for logical {} and pixel {}", logicalSize, size);

    surface->sendDamageBuffer(0, 0, 0xFFFF, 0xFFFF);

    if (!eglWindow) {
        eglWindow = wl_egl_window_create((wl_surface*)surface->resource(), size.x, size.y);
        RASSERT(eglWindow, "Couldn't create eglWindow");
    } else
        wl_egl_window_resize(eglWindow, size.x, size.y, 0, 0);

    if (!eglSurface) {
        eglSurface = g_pEGL->eglCreatePlatformWindowSurfaceEXT(g_pEGL->eglDisplay, g_pEGL->eglConfig, eglWindow, nullptr);
        RASSERT(eglSurface, "Couldn't create eglSurface");
    }

    if (readyForFrame && !(SAMESIZE && SAMESCALE)) {
        Debug::log(LOG, "output {} changed, reloading widgets!", POUTPUT->stringPort);
        g_pRenderer->reconfigureWidgetsFor(POUTPUT->m_ID);
    }

    readyForFrame = true;

    render();
}

void CSessionLockSurface::onScaleUpdate() {
    configure(logicalSize, serial);
}

void CSessionLockSurface::render() {
    if (frameCallback || !readyForFrame) {
        needsFrame = true;
        return;
    }

    g_pAnimationManager->tick();
    const auto FEEDBACK = g_pRenderer->renderLock(*this);
    frameCallback       = makeShared<CCWlCallback>(surface->sendFrame());
    frameCallback->setDone([this](CCWlCallback* r, uint32_t frameTime) {
        if (g_pHyprlock->m_bTerminate)
            return;

        if (Debug::verbose) {
            const auto POUTPUT = m_outputRef.lock();
            Debug::log(TRACE, "[{}] frame {}, Current fps: {:.2f}", POUTPUT->stringPort, m_frames, 1000.f / (frameTime - m_lastFrameTime));
        }

        m_lastFrameTime = frameTime;

        m_frames++;

        onCallback();
    });

    eglSwapBuffers(g_pEGL->eglDisplay, eglSurface);

    needsFrame = FEEDBACK.needsFrame || g_pAnimationManager->shouldTickForNext();
}

void CSessionLockSurface::onCallback() {
    frameCallback.reset();

    if (needsFrame && !g_pHyprlock->m_bTerminate && g_pEGL) {
        needsFrame = false;
        render();
    }
}

SP<CCWlSurface> CSessionLockSurface::getWlSurface() {
    return surface;
}
0707010000002C000081A4000000000000000000000001688B5FE90000065E000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/core/LockSurface.hpp#pragma once

#include "../defines.hpp"
#include "wayland.hpp"
#include "ext-session-lock-v1.hpp"
#include "viewporter.hpp"
#include "fractional-scale-v1.hpp"
#include "../helpers/Math.hpp"
#include <wayland-egl.h>
#include <EGL/egl.h>

class COutput;
class CRenderer;

class CSessionLockSurface {
  public:
    CSessionLockSurface(const SP<COutput>& pOutput);
    ~CSessionLockSurface();

    void            configure(const Vector2D& size, uint32_t serial);

    bool            readyForFrame = false;

    float           fractionalScale = 1.0;

    void            render();
    void            onCallback();
    void            onScaleUpdate();
    SP<CCWlSurface> getWlSurface();

  private:
    WP<COutput>                   m_outputRef;
    OUTPUTID                      m_outputID = OUTPUT_INVALID;

    SP<CCWlSurface>               surface     = nullptr;
    SP<CCExtSessionLockSurfaceV1> lockSurface = nullptr;
    uint32_t                      serial      = 0;
    wl_egl_window*                eglWindow   = nullptr;
    Vector2D                      size;
    Vector2D                      logicalSize;
    float                         appliedScale;
    EGLSurface                    eglSurface = nullptr;
    SP<CCWpFractionalScaleV1>     fractional = nullptr;
    SP<CCWpViewport>              viewport   = nullptr;

    bool                          needsFrame = false;

    uint32_t                      m_lastFrameTime = 0;
    uint32_t                      m_frames        = 0;

    // wayland callbacks
    SP<CCWlCallback> frameCallback = nullptr;

    friend class CRenderer;
    friend class COutput;
};
0707010000002D000081A4000000000000000000000001688B5FE90000094A000000000000000000000000000000000000002300000000hyprlock-0.9.1/src/core/Output.cpp#include "Output.hpp"
#include "../helpers/Log.hpp"
#include "hyprlock.hpp"

void COutput::create(WP<COutput> pSelf, SP<CCWlOutput> pWlOutput, uint32_t _name) {
    m_ID       = _name;
    m_wlOutput = pWlOutput;
    m_self     = pSelf;

    m_wlOutput->setDescription([this](CCWlOutput* r, const char* description) {
        stringDesc = description ? std::string{description} : "";
        Debug::log(LOG, "output {} description {}", m_ID, stringDesc);
    });

    m_wlOutput->setName([this](CCWlOutput* r, const char* name) {
        stringName = std::string{name} + stringName;
        stringPort = std::string{name};
        Debug::log(LOG, "output {} name {}", name, name);
    });

    m_wlOutput->setScale([this](CCWlOutput* r, int32_t sc) { scale = sc; });

    m_wlOutput->setDone([this](CCWlOutput* r) {
        done = true;
        Debug::log(LOG, "output {} done", m_ID);
        if (g_pHyprlock->m_lockAquired && !m_sessionLockSurface) {
            Debug::log(LOG, "output {} creating a new lock surface", m_ID);
            createSessionLockSurface();
        }
    });

    m_wlOutput->setMode([this](CCWlOutput* r, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
        // handle portrait mode and flipped cases
        if (transform % 2 == 1)
            size = {height, width};
        else
            size = {width, height};
    });

    m_wlOutput->setGeometry(
        [this](CCWlOutput* r, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char* make, const char* model, int32_t transform_) {
            transform = (wl_output_transform)transform_;

            Debug::log(LOG, "output {} make {} model {}", m_ID, make ? make : "", model ? model : "");
        });
}

void COutput::createSessionLockSurface() {
    if (!m_self.valid()) {
        Debug::log(ERR, "output {} dead??", m_ID);
        return;
    }

    if (m_sessionLockSurface) {
        Debug::log(ERR, "output {} already has a session lock surface", m_ID);
        return;
    }

    if (size == Vector2D{0, 0}) {
        Debug::log(WARN, "output {} refusing to create a lock surface with size 0x0", m_ID);
        return;
    }

    m_sessionLockSurface = makeUnique<CSessionLockSurface>(m_self.lock());
}

Vector2D COutput::getViewport() const {
    return (m_sessionLockSurface) ? m_sessionLockSurface->size : size;
}
0707010000002E000081A4000000000000000000000001688B5FE9000003A6000000000000000000000000000000000000002300000000hyprlock-0.9.1/src/core/Output.hpp#pragma once

#include "../defines.hpp"
#include "wayland.hpp"
#include "LockSurface.hpp"

class COutput {
  public:
    COutput()  = default;
    ~COutput() = default;

    void                    create(WP<COutput> pSelf, SP<CCWlOutput> pWlOutput, uint32_t name);

    OUTPUTID                m_ID      = 0;
    bool                    focused   = false;
    bool                    done      = false;
    wl_output_transform     transform = WL_OUTPUT_TRANSFORM_NORMAL;
    Vector2D                size;
    int                     scale      = 1;
    std::string             stringName = "";
    std::string             stringPort = "";
    std::string             stringDesc = "";

    UP<CSessionLockSurface> m_sessionLockSurface;

    SP<CCWlOutput>          m_wlOutput = nullptr;

    WP<COutput>             m_self;

    void                    createSessionLockSurface();

    Vector2D                getViewport() const;
};
0707010000002F000081A4000000000000000000000001688B5FE900001D3E000000000000000000000000000000000000002100000000hyprlock-0.9.1/src/core/Seat.cpp#include "Seat.hpp"
#include "hyprlock.hpp"
#include "../helpers/Log.hpp"
#include "../config/ConfigManager.hpp"
#include <chrono>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/input-event-codes.h>

CSeatManager::~CSeatManager() {
    if (m_pCursorShape && m_pCursorShape->shapeChanged)
        m_pCursorShape->setShape(wpCursorShapeDeviceV1Shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);

    if (m_pXKBState)
        xkb_state_unref(m_pXKBState);
    if (m_pXKBKeymap)
        xkb_keymap_unref(m_pXKBKeymap);
    if (m_pXKBContext)
        xkb_context_unref(m_pXKBContext);
}

void CSeatManager::registerSeat(SP<CCWlSeat> seat) {
    m_pSeat = seat;

    m_pXKBContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
    if (!m_pXKBContext)
        Debug::log(ERR, "Failed to create xkb context");

    m_pSeat->setCapabilities([this](CCWlSeat* r, wl_seat_capability caps) {
        if (caps & WL_SEAT_CAPABILITY_POINTER) {
            m_pPointer = makeShared<CCWlPointer>(r->sendGetPointer());

            static const auto HIDECURSOR = g_pConfigManager->getValue<Hyprlang::INT>("general:hide_cursor");
            m_pPointer->setMotion([](CCWlPointer* r, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
                g_pHyprlock->m_vMouseLocation = {wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)};

                if (!*HIDECURSOR)
                    g_pHyprlock->onHover(g_pHyprlock->m_vMouseLocation);

                if (std::chrono::system_clock::now() > g_pHyprlock->m_tGraceEnds)
                    return;

                if (!g_pHyprlock->isUnlocked() && g_pHyprlock->m_vLastEnterCoords.distance({wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)}) > 5) {
                    Debug::log(LOG, "In grace and cursor moved more than 5px, unlocking!");
                    g_pHyprlock->unlock();
                }
            });

            m_pPointer->setEnter([this](CCWlPointer* r, uint32_t serial, wl_proxy* surf, wl_fixed_t surface_x, wl_fixed_t surface_y) {
                if (!m_pCursorShape)
                    return;

                m_pCursorShape->lastCursorSerial = serial;

                if (*HIDECURSOR)
                    m_pCursorShape->hideCursor();
                else
                    m_pCursorShape->setShape(wpCursorShapeDeviceV1Shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);

                g_pHyprlock->m_vLastEnterCoords = {wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)};

                if (*HIDECURSOR)
                    return;

                for (const auto& POUTPUT : g_pHyprlock->m_vOutputs) {
                    if (!POUTPUT->m_sessionLockSurface)
                        continue;

                    const auto& PWLSURFACE = POUTPUT->m_sessionLockSurface->getWlSurface();
                    if (PWLSURFACE->resource() == surf)
                        g_pHyprlock->m_focusedOutput = POUTPUT;
                }
            });

            m_pPointer->setLeave([](CCWlPointer* r, uint32_t serial, wl_proxy* surf) { g_pHyprlock->m_focusedOutput.reset(); });

            m_pPointer->setButton([](CCWlPointer* r, uint32_t serial, uint32_t time, uint32_t button, wl_pointer_button_state state) {
                if (*HIDECURSOR)
                    return;
                g_pHyprlock->onClick(button, state == WL_POINTER_BUTTON_STATE_PRESSED, g_pHyprlock->m_vMouseLocation);
            });
        }
        if (caps & WL_SEAT_CAPABILITY_TOUCH) {
            m_pTouch = makeShared<CCWlTouch>(r->sendGetTouch());
            m_pTouch->setDown([](CCWlTouch* r, uint32_t serial, uint32_t time, wl_proxy* surface, int32_t id, wl_fixed_t x, wl_fixed_t y) {
                g_pHyprlock->onClick(BTN_LEFT, true, {wl_fixed_to_double(x), wl_fixed_to_double(y)});
            });
            m_pTouch->setUp([](CCWlTouch* r, uint32_t serial, uint32_t time, int32_t id) { g_pHyprlock->onClick(BTN_LEFT, false, {0, 0}); });
        };
        if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
            m_pKeeb = makeShared<CCWlKeyboard>(r->sendGetKeyboard());

            m_pKeeb->setKeymap([this](CCWlKeyboard*, wl_keyboard_keymap_format format, int32_t fd, uint32_t size) {
                if (!m_pXKBContext)
                    return;

                if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
                    Debug::log(ERR, "Could not recognise keymap format");
                    return;
                }

                const char* buf = (const char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
                if (buf == MAP_FAILED) {
                    Debug::log(ERR, "Failed to mmap xkb keymap: {}", errno);
                    return;
                }

                m_pXKBKeymap = xkb_keymap_new_from_buffer(m_pXKBContext, buf, size - 1, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);

                munmap((void*)buf, size);
                close(fd);

                if (!m_pXKBKeymap) {
                    Debug::log(ERR, "Failed to compile xkb keymap");
                    return;
                }

                m_pXKBState = xkb_state_new(m_pXKBKeymap);
                if (!m_pXKBState) {
                    Debug::log(ERR, "Failed to create xkb state");
                    return;
                }

                const auto PCOMOPOSETABLE = xkb_compose_table_new_from_locale(m_pXKBContext, setlocale(LC_CTYPE, nullptr), XKB_COMPOSE_COMPILE_NO_FLAGS);

                if (!PCOMOPOSETABLE) {
                    Debug::log(ERR, "Failed to create xkb compose table");
                    return;
                }

                m_pXKBComposeState = xkb_compose_state_new(PCOMOPOSETABLE, XKB_COMPOSE_STATE_NO_FLAGS);
            });

            m_pKeeb->setKey([](CCWlKeyboard* r, uint32_t serial, uint32_t time, uint32_t key, wl_keyboard_key_state state) {
                g_pHyprlock->onKey(key, state == WL_KEYBOARD_KEY_STATE_PRESSED);
            });

            m_pKeeb->setModifiers([this](CCWlKeyboard* r, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
                if (!m_pXKBState)
                    return;

                if (group != g_pHyprlock->m_uiActiveLayout) {
                    g_pHyprlock->m_uiActiveLayout = group;
                    for (auto& t : g_pHyprlock->getTimers()) {
                        if (t->canForceUpdate()) {
                            t->call(t);
                            t->cancel();
                        }
                    }
                }

                xkb_state_update_mask(m_pXKBState, mods_depressed, mods_latched, mods_locked, 0, 0, group);
                g_pHyprlock->m_bCapsLock = xkb_state_mod_name_is_active(m_pXKBState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED);
                g_pHyprlock->m_bNumLock  = xkb_state_mod_name_is_active(m_pXKBState, XKB_MOD_NAME_NUM, XKB_STATE_MODS_LOCKED);
            });

            m_pKeeb->setRepeatInfo([](CCWlKeyboard* r, int32_t rate, int32_t delay) {
                g_pHyprlock->m_iKeebRepeatRate  = rate;
                g_pHyprlock->m_iKeebRepeatDelay = delay;
            });
        }
    });

    m_pSeat->setName([](CCWlSeat* r, const char* name) { Debug::log(LOG, "Exposed seat name: {}", name ? name : "nullptr"); });
}

void CSeatManager::registerCursorShape(SP<CCWpCursorShapeManagerV1> shape) {
    m_pCursorShape = makeUnique<CCursorShape>(shape);
}

bool CSeatManager::registered() {
    return m_pSeat;
}
07070100000030000081A4000000000000000000000001688B5FE900000376000000000000000000000000000000000000002100000000hyprlock-0.9.1/src/core/Seat.hpp#pragma once

#include "../defines.hpp"
#include "CursorShape.hpp"
#include "wayland.hpp"
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>

class CSeatManager {
  public:
    CSeatManager() = default;
    ~CSeatManager();

    void               registerSeat(SP<CCWlSeat> seat);
    void               registerCursorShape(SP<CCWpCursorShapeManagerV1> shape);
    bool               registered();

    SP<CCWlKeyboard>   m_pKeeb;
    SP<CCWlPointer>    m_pPointer;
    SP<CCWlTouch>      m_pTouch;

    UP<CCursorShape>   m_pCursorShape;

    xkb_context*       m_pXKBContext      = nullptr;
    xkb_keymap*        m_pXKBKeymap       = nullptr;
    xkb_state*         m_pXKBState        = nullptr;
    xkb_compose_state* m_pXKBComposeState = nullptr;

  private:
    SP<CCWlSeat> m_pSeat;
};

inline UP<CSeatManager> g_pSeatManager = makeUnique<CSeatManager>();
07070100000031000081A4000000000000000000000001688B5FE9000002E0000000000000000000000000000000000000002200000000hyprlock-0.9.1/src/core/Timer.cpp#include "Timer.hpp"

CTimer::CTimer(std::chrono::system_clock::duration timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data_, bool force) :
    cb(cb_), data(data_), allowForceUpdate(force) {
    expires = std::chrono::system_clock::now() + timeout;
}

bool CTimer::passed() {
    return std::chrono::system_clock::now() > expires;
}

void CTimer::cancel() {
    wasCancelled = true;
}

bool CTimer::cancelled() {
    return wasCancelled;
}

void CTimer::call(ASP<CTimer> self) {
    cb(self, data);
}

float CTimer::leftMs() {
    return std::chrono::duration_cast<std::chrono::milliseconds>(expires - std::chrono::system_clock::now()).count();
}

bool CTimer::canForceUpdate() {
    return allowForceUpdate;
}
07070100000032000081A4000000000000000000000001688B5FE9000002F5000000000000000000000000000000000000002200000000hyprlock-0.9.1/src/core/Timer.hpp#pragma once

#include <chrono>
#include <functional>
#include "../defines.hpp"

class CTimer {
  public:
    CTimer(std::chrono::system_clock::duration timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data_, bool force);

    void  cancel();
    bool  passed();
    bool  canForceUpdate();

    float leftMs();

    bool  cancelled();
    void  call(ASP<CTimer> self);

  private:
    std::function<void(ASP<CTimer> self, void* data)> cb;
    void*                                             data = nullptr;
    std::chrono::system_clock::time_point             expires;
    bool                                              wasCancelled     = false;
    bool                                              allowForceUpdate = false;
};
07070100000033000081A4000000000000000000000001688B5FE900007D2C000000000000000000000000000000000000002500000000hyprlock-0.9.1/src/core/hyprlock.cpp#include "hyprlock.hpp"
#include "AnimationManager.hpp"
#include "../helpers/Log.hpp"
#include "../config/ConfigManager.hpp"
#include "../renderer/Renderer.hpp"
#include "../auth/Auth.hpp"
#include "../auth/Fingerprint.hpp"
#include "Egl.hpp"
#include <chrono>
#include <hyprutils/memory/UniquePtr.hpp>
#include <sys/wait.h>
#include <sys/poll.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <csignal>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <xf86drm.h>
#include <filesystem>
#include <fstream>
#include <algorithm>
#include <sdbus-c++/sdbus-c++.h>
#include <hyprutils/os/Process.hpp>
#include <malloc.h>

using namespace Hyprutils::OS;

static void setMallocThreshold() {
#ifdef M_TRIM_THRESHOLD
    // The default is 128 pages,
    // which is very large and can lead to a lot of memory used for no reason
    // because trimming hasn't happened
    static const int PAGESIZE = sysconf(_SC_PAGESIZE);
    mallopt(M_TRIM_THRESHOLD, 6 * PAGESIZE);
#endif
}

CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediateRender, const int graceSeconds) {
    setMallocThreshold();

    m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
    RASSERT(m_sWaylandState.display, "Couldn't connect to a wayland compositor");

    g_pEGL = makeUnique<CEGL>(m_sWaylandState.display);

    if (graceSeconds > 0)
        m_tGraceEnds = std::chrono::system_clock::now() + std::chrono::seconds(graceSeconds);
    else
        m_tGraceEnds = std::chrono::system_clock::from_time_t(0);

    static const auto IMMEDIATERENDER = g_pConfigManager->getValue<Hyprlang::INT>("general:immediate_render");
    m_bImmediateRender                = immediateRender || *IMMEDIATERENDER;

    const auto CURRENTDESKTOP = getenv("XDG_CURRENT_DESKTOP");
    const auto SZCURRENTD     = std::string{CURRENTDESKTOP ? CURRENTDESKTOP : ""};
    m_sCurrentDesktop         = SZCURRENTD;
}

CHyprlock::~CHyprlock() {
    if (dma.gbmDevice)
        gbm_device_destroy(dma.gbmDevice);
}

static void registerSignalAction(int sig, void (*handler)(int), int sa_flags = 0) {
    struct sigaction sa;
    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = sa_flags;
    sigaction(sig, &sa, nullptr);
}

static void handleUnlockSignal(int sig) {
    if (sig == SIGUSR1) {
        Debug::log(LOG, "Unlocking with a SIGUSR1");
        g_pAuth->enqueueUnlock();
    }
}

static void handleForceUpdateSignal(int sig) {
    if (sig == SIGUSR2) {
        for (auto& t : g_pHyprlock->getTimers()) {
            if (t->canForceUpdate()) {
                t->call(t);
                t->cancel();
            }
        }
    }
}

static void handlePollTerminate(int sig) {
    ;
}

static char* gbm_find_render_node(drmDevice* device) {
    drmDevice* devices[64];
    char*      render_node = nullptr;

    int        n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
    for (int i = 0; i < n; ++i) {
        drmDevice* dev = devices[i];
        if (device && !drmDevicesEqual(device, dev)) {
            continue;
        }
        if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)))
            continue;

        render_node = strdup(dev->nodes[DRM_NODE_RENDER]);
        break;
    }

    drmFreeDevices(devices, n);
    return render_node;
}

gbm_device* CHyprlock::createGBMDevice(drmDevice* dev) {
    char* renderNode = gbm_find_render_node(dev);

    if (!renderNode) {
        Debug::log(ERR, "[core] Couldn't find a render node");
        return nullptr;
    }

    Debug::log(TRACE, "[core] createGBMDevice: render node {}", renderNode);

    int fd = open(renderNode, O_RDWR | O_CLOEXEC);
    if (fd < 0) {
        Debug::log(ERR, "[core] couldn't open render node");
        free(renderNode);
        return nullptr;
    }

    free(renderNode);
    return gbm_create_device(fd);
}

void CHyprlock::addDmabufListener() {
    dma.linuxDmabufFeedback->setTrancheDone([this](CCZwpLinuxDmabufFeedbackV1* r) {
        Debug::log(TRACE, "[core] dmabufFeedbackTrancheDone");

        dma.deviceUsed = false;
    });

    dma.linuxDmabufFeedback->setTrancheFormats([this](CCZwpLinuxDmabufFeedbackV1* r, wl_array* indices) {
        Debug::log(TRACE, "[core] dmabufFeedbackTrancheFormats");

        if (!dma.deviceUsed || !dma.formatTable)
            return;

        struct fm_entry {
            uint32_t format;
            uint32_t padding;
            uint64_t modifier;
        };
        // An entry in the table has to be 16 bytes long
        static_assert(sizeof(fm_entry) == 16);

        uint32_t  n_modifiers = dma.formatTableSize / sizeof(fm_entry);
        fm_entry* fm_entry    = (struct fm_entry*)dma.formatTable;
        uint16_t* idx;

        for (idx = (uint16_t*)indices->data; (const char*)idx < (const char*)indices->data + indices->size; idx++) {
            if (*idx >= n_modifiers)
                continue;

            Debug::log(TRACE, "GPU Reports supported format {:x} with modifier {:x}", (fm_entry + *idx)->format, (fm_entry + *idx)->modifier);

            dma.dmabufMods.push_back({(fm_entry + *idx)->format, (fm_entry + *idx)->modifier});
        }
    });

    dma.linuxDmabufFeedback->setTrancheTargetDevice([this](CCZwpLinuxDmabufFeedbackV1* r, wl_array* device_arr) {
        Debug::log(TRACE, "[core] dmabufFeedbackTrancheTargetDevice");

        dev_t device;
        assert(device_arr->size == sizeof(device));
        memcpy(&device, device_arr->data, sizeof(device));

        drmDevice* drmDev;
        if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0)
            return;

        if (dma.gbmDevice) {
            drmDevice* drmDevRenderer = nullptr;
            drmGetDevice2(gbm_device_get_fd(dma.gbmDevice), /* flags */ 0, &drmDevRenderer);
            dma.deviceUsed = drmDevicesEqual(drmDevRenderer, drmDev);
        } else {
            dma.gbmDevice  = createGBMDevice(drmDev);
            dma.deviceUsed = dma.gbm;
        }
    });

    dma.linuxDmabufFeedback->setDone([this](CCZwpLinuxDmabufFeedbackV1* r) {
        Debug::log(TRACE, "[core] dmabufFeedbackDone");

        if (dma.formatTable)
            munmap(dma.formatTable, dma.formatTableSize);

        dma.formatTable     = nullptr;
        dma.formatTableSize = 0;
    });

    dma.linuxDmabufFeedback->setFormatTable([this](CCZwpLinuxDmabufFeedbackV1* r, int fd, uint32_t size) {
        Debug::log(TRACE, "[core] dmabufFeedbackFormatTable");

        dma.dmabufMods.clear();

        dma.formatTable = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);

        if (dma.formatTable == MAP_FAILED) {
            Debug::log(ERR, "[core] format table failed to mmap");
            dma.formatTable     = nullptr;
            dma.formatTableSize = 0;
            return;
        }

        dma.formatTableSize = size;
    });

    dma.linuxDmabufFeedback->setMainDevice([this](CCZwpLinuxDmabufFeedbackV1* r, wl_array* device_arr) {
        Debug::log(LOG, "[core] dmabufFeedbackMainDevice");

        RASSERT(!dma.gbm, "double dmabuf feedback");

        dev_t device;
        assert(device_arr->size == sizeof(device));
        memcpy(&device, device_arr->data, sizeof(device));

        drmDevice* drmDev;
        RASSERT(drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) == 0, "unable to open main device?");

        dma.gbmDevice = createGBMDevice(drmDev);
        drmFreeDevice(&drmDev);
    });

    dma.linuxDmabuf->setModifier([this](CCZwpLinuxDmabufV1* r, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) {
        dma.dmabufMods.push_back({format, (((uint64_t)modifier_hi) << 32) | modifier_lo});
    });
}

void CHyprlock::run() {
    m_sWaylandState.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(m_sWaylandState.display));
    m_sWaylandState.registry->setGlobal([this](CCWlRegistry* r, uint32_t name, const char* interface, uint32_t version) {
        const std::string IFACE = interface;
        Debug::log(LOG, "  | got iface: {} v{}", IFACE, version);

        if (IFACE == zwp_linux_dmabuf_v1_interface.name) {
            if (version < 4) {
                Debug::log(ERR, "cannot use linux_dmabuf with ver < 4");
                return;
            }

            dma.linuxDmabuf         = makeShared<CCZwpLinuxDmabufV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &zwp_linux_dmabuf_v1_interface, 4));
            dma.linuxDmabufFeedback = makeShared<CCZwpLinuxDmabufFeedbackV1>(dma.linuxDmabuf->sendGetDefaultFeedback());

            addDmabufListener();
        } else if (IFACE == wl_seat_interface.name) {
            if (g_pSeatManager->registered()) {
                Debug::log(WARN, "Hyprlock does not support multi-seat configurations. Only binding to the first seat.");
                return;
            }

            g_pSeatManager->registerSeat(makeShared<CCWlSeat>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wl_seat_interface, 8)));
        } else if (IFACE == ext_session_lock_manager_v1_interface.name)
            m_sWaylandState.sessionLock =
                makeShared<CCExtSessionLockManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &ext_session_lock_manager_v1_interface, 1));
        else if (IFACE == wl_output_interface.name) {
            const auto POUTPUT = makeShared<COutput>();
            POUTPUT->create(POUTPUT, makeShared<CCWlOutput>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wl_output_interface, 4)), name);
            m_vOutputs.emplace_back(POUTPUT);
        } else if (IFACE == wp_cursor_shape_manager_v1_interface.name)
            g_pSeatManager->registerCursorShape(
                makeShared<CCWpCursorShapeManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wp_cursor_shape_manager_v1_interface, 1)));
        else if (IFACE == wl_compositor_interface.name)
            m_sWaylandState.compositor = makeShared<CCWlCompositor>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wl_compositor_interface, 4));
        else if (IFACE == wp_fractional_scale_manager_v1_interface.name)
            m_sWaylandState.fractional =
                makeShared<CCWpFractionalScaleManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wp_fractional_scale_manager_v1_interface, 1));
        else if (IFACE == wp_viewporter_interface.name)
            m_sWaylandState.viewporter = makeShared<CCWpViewporter>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wp_viewporter_interface, 1));
        else if (IFACE == zwlr_screencopy_manager_v1_interface.name)
            m_sWaylandState.screencopy =
                makeShared<CCZwlrScreencopyManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &zwlr_screencopy_manager_v1_interface, 3));
        else if (IFACE == wl_shm_interface.name)
            m_sWaylandState.shm = makeShared<CCWlShm>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wl_shm_interface, 1));
        else
            return;

        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    });
    m_sWaylandState.registry->setGlobalRemove([this](CCWlRegistry* r, uint32_t name) {
        Debug::log(LOG, "  | removed iface {}", name);
        auto outputIt = std::ranges::find_if(m_vOutputs, [id = name](const auto& other) { return other->m_ID == id; });
        if (outputIt != m_vOutputs.end()) {
            g_pRenderer->removeWidgetsFor((*outputIt)->m_ID);
            m_vOutputs.erase(outputIt);
        }
    });

    wl_display_roundtrip(m_sWaylandState.display);

    if (!m_sWaylandState.sessionLock) {
        Debug::log(CRIT, "Couldn't bind to ext-session-lock-v1, does your compositor support it?");
        exit(1);
    }

    // gather info about monitors
    wl_display_roundtrip(m_sWaylandState.display);

    g_pRenderer = makeUnique<CRenderer>();
    g_pAuth     = makeUnique<CAuth>();
    g_pAuth->start();

    Debug::log(LOG, "Running on {}", m_sCurrentDesktop);

    if (!g_pHyprlock->m_bImmediateRender) {
        // Gather background resources and screencopy frames before locking the screen.
        // We need to do this because as soon as we lock the screen, workspaces frames can no longer be captured. It either won't work at all, or we will capture hyprlock itself.
        // Bypass with --immediate-render (can cause the background first rendering a solid color and missing or inaccurate screencopy frames)
        const auto MAXDELAYMS    = 2000; // 2 Seconds
        const auto STARTGATHERTP = std::chrono::system_clock::now();

        int        fdcount = 1;
        pollfd     pollfds[2];
        pollfds[0] = {
            .fd     = wl_display_get_fd(m_sWaylandState.display),
            .events = POLLIN,
        };

        if (g_pRenderer->asyncResourceGatherer->gatheredEventfd.isValid()) {
            pollfds[1] = {
                .fd     = g_pRenderer->asyncResourceGatherer->gatheredEventfd.get(),
                .events = POLLIN,
            };

            fdcount++;
        }

        while (!g_pRenderer->asyncResourceGatherer->gathered) {
            wl_display_flush(m_sWaylandState.display);
            if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
                if (poll(pollfds, fdcount, /* 100ms timeout */ 100) < 0) {
                    RASSERT(errno == EINTR, "[core] Polling fds failed with {}", errno);
                    wl_display_cancel_read(m_sWaylandState.display);
                    continue;
                }
                wl_display_read_events(m_sWaylandState.display);
                wl_display_dispatch_pending(m_sWaylandState.display);
            } else {
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
                wl_display_dispatch(m_sWaylandState.display);
            }

            if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - STARTGATHERTP).count() > MAXDELAYMS) {
                Debug::log(WARN, "Gathering resources timed out after {} milliseconds. Backgrounds may be delayed and render `background:color` at first.", MAXDELAYMS);
                break;
            }
        }

        Debug::log(LOG, "Resources gathered after {} milliseconds",
                   std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - STARTGATHERTP).count());
    }

    // Failed to lock the session
    if (!acquireSessionLock()) {
        m_sLoopState.timerEvent = true;
        m_sLoopState.timerCV.notify_all();
        g_pRenderer->asyncResourceGatherer->notify();
        g_pRenderer->asyncResourceGatherer->await();
        g_pAuth->terminate();
        exit(1);
    }

    const auto fingerprintAuth = g_pAuth->getImpl(AUTH_IMPL_FINGERPRINT);
    const auto dbusConn        = (fingerprintAuth) ? ((CFingerprint*)fingerprintAuth.get())->getConnection() : nullptr;

    registerSignalAction(SIGUSR1, handleUnlockSignal, SA_RESTART);
    registerSignalAction(SIGUSR2, handleForceUpdateSignal);
    registerSignalAction(SIGRTMIN, handlePollTerminate);

    pollfd pollfds[2];
    pollfds[0] = {
        .fd     = wl_display_get_fd(m_sWaylandState.display),
        .events = POLLIN,
    };
    if (dbusConn) {
        pollfds[1] = {
            .fd     = dbusConn->getEventLoopPollData().fd,
            .events = POLLIN,
        };
    }
    size_t      fdcount = dbusConn ? 2 : 1;

    std::thread pollThr([this, &pollfds, fdcount]() {
        while (!m_bTerminate) {
            bool preparedToRead = wl_display_prepare_read(m_sWaylandState.display) == 0;

            int  events = 0;
            if (preparedToRead) {
                events = poll(pollfds, fdcount, 5000);

                if (events < 0) {
                    RASSERT(errno == EINTR, "[core] Polling fds failed with {}", errno);
                    wl_display_cancel_read(m_sWaylandState.display);
                    continue;
                }

                for (size_t i = 0; i < fdcount; ++i) {
                    RASSERT(!(pollfds[i].revents & POLLHUP), "[core] Disconnected from pollfd id {}", i);
                }

                wl_display_read_events(m_sWaylandState.display);
                m_sLoopState.wlDispatched = false;
            }

            if (events > 0 || !preparedToRead) {
                Debug::log(TRACE, "[core] got poll event");
                std::unique_lock lk(m_sLoopState.eventLoopMutex);
                m_sLoopState.event = true;
                m_sLoopState.loopCV.notify_all();

                m_sLoopState.wlDispatchCV.wait_for(lk, std::chrono::milliseconds(100), [this] { return m_sLoopState.wlDispatched; });
            }
        }
    });

    std::thread timersThr([this]() {
        while (!m_bTerminate) {
            // calc nearest thing
            m_sLoopState.timersMutex.lock();

            float least = 10000;
            for (auto& t : m_vTimers) {
                const auto TIME = std::clamp(t->leftMs(), 1.f, INFINITY);
                least           = std::min(TIME, least);
            }

            m_sLoopState.timersMutex.unlock();

            std::unique_lock lk(m_sLoopState.timerRequestMutex);
            m_sLoopState.timerCV.wait_for(lk, std::chrono::milliseconds((int)least + 1), [this] { return m_sLoopState.timerEvent; });
            m_sLoopState.timerEvent = false;

            // notify main
            std::lock_guard<std::mutex> lg2(m_sLoopState.eventLoopMutex);
            Debug::log(TRACE, "timer thread firing");
            m_sLoopState.event = true;
            m_sLoopState.loopCV.notify_all();
        }
    });

    m_sLoopState.event = true; // let it process once
    g_pRenderer->startFadeIn();

    while (!m_bTerminate) {
        std::unique_lock lk(m_sLoopState.eventRequestMutex);
        if (!m_sLoopState.event)
            m_sLoopState.loopCV.wait_for(lk, std::chrono::milliseconds(5000), [this] { return m_sLoopState.event; });

        if (m_bTerminate)
            break;

        std::lock_guard<std::mutex> lg(m_sLoopState.eventLoopMutex);

        m_sLoopState.event = false;

        wl_display_dispatch_pending(m_sWaylandState.display);
        wl_display_flush(m_sWaylandState.display);

        m_sLoopState.wlDispatched = true;
        m_sLoopState.wlDispatchCV.notify_all();

        if (pollfds[1].revents & POLLIN /* dbus */) {
            while (dbusConn && dbusConn->processPendingEvent()) {
                ;
            }
        }

        // do timers
        m_sLoopState.timersMutex.lock();
        auto timerscpy = m_vTimers;
        m_sLoopState.timersMutex.unlock();

        std::vector<ASP<CTimer>> passed;

        for (auto& t : timerscpy) {
            if (t->passed() && !t->cancelled()) {
                t->call(t);
                passed.push_back(t);
            }

            if (t->cancelled())
                passed.push_back(t);
        }

        m_sLoopState.timersMutex.lock();
        std::erase_if(m_vTimers, [passed](const auto& timer) { return std::find(passed.begin(), passed.end(), timer) != passed.end(); });
        m_sLoopState.timersMutex.unlock();

        passed.clear();
    }

    const auto DPY = m_sWaylandState.display;

    m_sLoopState.timerEvent = true;
    m_sLoopState.timerCV.notify_all();
    g_pRenderer->asyncResourceGatherer->notify();
    g_pRenderer->asyncResourceGatherer->await();
    m_sWaylandState = {};
    dma             = {};

    m_vOutputs.clear();
    g_pEGL.reset();
    g_pRenderer.reset();
    g_pSeatManager.reset();

    wl_display_disconnect(DPY);

    pthread_kill(pollThr.native_handle(), SIGRTMIN);

    g_pAuth->terminate();

    // wait for threads to exit cleanly to avoid a coredump
    pollThr.join();
    timersThr.join();

    Debug::log(LOG, "Reached the end, exiting");
}

void CHyprlock::unlock() {
    if (!m_bLocked) {
        Debug::log(WARN, "Unlock called, but not locked yet. This can happen when dpms is off during the grace period.");
        return;
    }

    g_pRenderer->startFadeOut(true);

    renderAllOutputs();
}

bool CHyprlock::isUnlocked() {
    return !m_bLocked;
}

void CHyprlock::clearPasswordBuffer() {
    if (m_sPasswordState.passBuffer.empty())
        return;

    m_sPasswordState.passBuffer = "";

    renderAllOutputs();
}

void CHyprlock::renderOutput(const std::string& stringPort) {
    const auto MON = std::ranges::find_if(m_vOutputs, [stringPort](const auto& other) { return other->stringPort == stringPort; });

    if (MON == m_vOutputs.end() || !*MON)
        return;

    const auto& PMONITOR = *MON;

    if (!PMONITOR->m_sessionLockSurface)
        return;

    PMONITOR->m_sessionLockSurface->render();
}

void CHyprlock::renderAllOutputs() {
    for (auto& o : m_vOutputs) {
        if (!o->m_sessionLockSurface)
            continue;

        o->m_sessionLockSurface->render();
    }
}

void CHyprlock::startKeyRepeat(xkb_keysym_t sym) {
    if (m_pKeyRepeatTimer) {
        m_pKeyRepeatTimer->cancel();
        m_pKeyRepeatTimer.reset();
    }

    if (g_pSeatManager->m_pXKBComposeState)
        xkb_compose_state_reset(g_pSeatManager->m_pXKBComposeState);

    if (m_iKeebRepeatDelay <= 0)
        return;

    m_pKeyRepeatTimer = addTimer(std::chrono::milliseconds(m_iKeebRepeatDelay), [sym](ASP<CTimer> self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr);
}

void CHyprlock::repeatKey(xkb_keysym_t sym) {
    if (m_iKeebRepeatRate <= 0)
        return;

    handleKeySym(sym, false);

    // This condition is for backspace and delete keys, but should also be ok for other keysyms since our buffer won't be empty anyways
    if (bool CONTINUE = m_sPasswordState.passBuffer.length() > 0; CONTINUE)
        m_pKeyRepeatTimer = addTimer(std::chrono::milliseconds(m_iKeebRepeatRate), [sym](ASP<CTimer> self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr);

    renderAllOutputs();
}

void CHyprlock::onKey(uint32_t key, bool down) {
    if (isUnlocked())
        return;

    if (down && std::chrono::system_clock::now() < m_tGraceEnds) {
        unlock();
        return;
    }

    if (down && std::ranges::find(m_vPressedKeys, key) != m_vPressedKeys.end()) {
        Debug::log(ERR, "Invalid key down event (key already pressed?)");
        return;
    } else if (!down && std::ranges::find(m_vPressedKeys, key) == m_vPressedKeys.end()) {
        Debug::log(ERR, "Invalid key down event (stray release event?)");
        return;
    }

    if (down)
        m_vPressedKeys.push_back(key);
    else {
        std::erase(m_vPressedKeys, key);
        if (m_pKeyRepeatTimer) {
            m_pKeyRepeatTimer->cancel();
            m_pKeyRepeatTimer.reset();
        }
    }

    if (g_pAuth->checkWaiting()) {
        renderAllOutputs();
        return;
    }

    if (g_pAuth->m_bDisplayFailText)
        g_pAuth->resetDisplayFail();

    if (down) {
        m_bCapsLock = xkb_state_mod_name_is_active(g_pSeatManager->m_pXKBState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED);
        m_bNumLock  = xkb_state_mod_name_is_active(g_pSeatManager->m_pXKBState, XKB_MOD_NAME_NUM, XKB_STATE_MODS_LOCKED);
        m_bCtrl     = xkb_state_mod_name_is_active(g_pSeatManager->m_pXKBState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE);

        const auto              SYM = xkb_state_key_get_one_sym(g_pSeatManager->m_pXKBState, key + 8);

        enum xkb_compose_status composeStatus = XKB_COMPOSE_NOTHING;
        if (g_pSeatManager->m_pXKBComposeState) {
            xkb_compose_state_feed(g_pSeatManager->m_pXKBComposeState, SYM);
            composeStatus = xkb_compose_state_get_status(g_pSeatManager->m_pXKBComposeState);
        }

        handleKeySym(SYM, composeStatus == XKB_COMPOSE_COMPOSED);

        if (SYM == XKB_KEY_BackSpace || SYM == XKB_KEY_Delete) // keys allowed to repeat
            startKeyRepeat(SYM);

    } else if (g_pSeatManager->m_pXKBComposeState && xkb_compose_state_get_status(g_pSeatManager->m_pXKBComposeState) == XKB_COMPOSE_COMPOSED)
        xkb_compose_state_reset(g_pSeatManager->m_pXKBComposeState);

    renderAllOutputs();
}

void CHyprlock::handleKeySym(xkb_keysym_t sym, bool composed) {
    const auto SYM = sym;

    if (SYM == XKB_KEY_Escape || (m_bCtrl && (SYM == XKB_KEY_u || SYM == XKB_KEY_BackSpace || SYM == XKB_KEY_a))) {
        Debug::log(LOG, "Clearing password buffer");

        m_sPasswordState.passBuffer = "";
    } else if (SYM == XKB_KEY_Return || SYM == XKB_KEY_KP_Enter) {
        Debug::log(LOG, "Authenticating");

        static const auto IGNOREEMPTY = g_pConfigManager->getValue<Hyprlang::INT>("general:ignore_empty_input");

        if (m_sPasswordState.passBuffer.empty() && *IGNOREEMPTY) {
            Debug::log(LOG, "Ignoring empty input");
            return;
        }

        g_pAuth->submitInput(m_sPasswordState.passBuffer);
    } else if (SYM == XKB_KEY_BackSpace || SYM == XKB_KEY_Delete) {
        if (m_sPasswordState.passBuffer.length() > 0) {
            // handle utf-8
            while ((m_sPasswordState.passBuffer.back() & 0xc0) == 0x80)
                m_sPasswordState.passBuffer.pop_back();
            m_sPasswordState.passBuffer = m_sPasswordState.passBuffer.substr(0, m_sPasswordState.passBuffer.length() - 1);
        }
    } else if (SYM == XKB_KEY_Caps_Lock) {
        m_bCapsLock = !m_bCapsLock;
    } else if (SYM == XKB_KEY_Num_Lock) {
        m_bNumLock = !m_bNumLock;
    } else {
        char buf[16] = {0};
        int  len     = (composed) ? xkb_compose_state_get_utf8(g_pSeatManager->m_pXKBComposeState, buf, sizeof(buf)) /* nullbyte */ + 1 :
                                    xkb_keysym_to_utf8(SYM, buf, sizeof(buf)) /* already includes a nullbyte */;

        if (len > 1)
            m_sPasswordState.passBuffer += std::string{buf, len - 1};
    }
}

void CHyprlock::onClick(uint32_t button, bool down, const Vector2D& pos) {
    if (!down)
        return;

    if (!m_focusedOutput.lock())
        return;

    // TODO: add the UNLIKELY marco from Hyprland
    if (!m_focusedOutput->m_sessionLockSurface)
        return;

    const auto SCALEDPOS = pos * m_focusedOutput->m_sessionLockSurface->fractionalScale;
    const auto widgets   = g_pRenderer->getOrCreateWidgetsFor(*m_focusedOutput->m_sessionLockSurface);
    for (const auto& widget : widgets) {
        if (widget->containsPoint(SCALEDPOS))
            widget->onClick(button, down, pos);
    }
}

void CHyprlock::onHover(const Vector2D& pos) {
    if (!m_focusedOutput.lock())
        return;

    if (!m_focusedOutput->m_sessionLockSurface)
        return;

    bool       outputNeedsRedraw = false;
    bool       cursorChanged     = false;

    const auto SCALEDPOS = pos * m_focusedOutput->m_sessionLockSurface->fractionalScale;
    const auto widgets   = g_pRenderer->getOrCreateWidgetsFor(*m_focusedOutput->m_sessionLockSurface);
    for (const auto& widget : widgets) {
        const bool CONTAINSPOINT = widget->containsPoint(SCALEDPOS);
        const bool HOVERED       = widget->isHovered();

        if (CONTAINSPOINT) {
            if (!HOVERED) {
                widget->setHover(true);
                widget->onHover(pos);
                outputNeedsRedraw = true;
            }

            if (!cursorChanged)
                cursorChanged = true;

        } else if (HOVERED) {
            widget->setHover(false);
            outputNeedsRedraw = true;
        }
    }

    if (!cursorChanged)
        g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);

    if (outputNeedsRedraw)
        m_focusedOutput->m_sessionLockSurface->render();
}

bool CHyprlock::acquireSessionLock() {
    Debug::log(LOG, "Locking session");
    m_sLockState.lock = makeShared<CCExtSessionLockV1>(m_sWaylandState.sessionLock->sendLock());
    if (!m_sLockState.lock) {
        Debug::log(ERR, "Failed to create a lock object!");
        return false;
    }

    m_sLockState.lock->setLocked([this](CCExtSessionLockV1* r) { onLockLocked(); });

    m_sLockState.lock->setFinished([this](CCExtSessionLockV1* r) { onLockFinished(); });

    // roundtrip in case the compositor sends `finished` right away
    wl_display_roundtrip(m_sWaylandState.display);

    // recieved finished right away (probably already locked)
    if (m_bTerminate)
        return false;

    m_lockAquired = true;

    // create a session lock surface for exiting outputs
    for (auto& o : m_vOutputs) {
        if (!o->done)
            continue;

        o->createSessionLockSurface();
    }

    return true;
}

void CHyprlock::releaseSessionLock() {
    Debug::log(LOG, "Unlocking session");

    if (m_bTerminate) {
        Debug::log(ERR, "Unlock already happend?");
        return;
    }

    if (!m_sLockState.lock) {
        Debug::log(ERR, "Unlock without a lock object!");
        return;
    }

    if (!m_bLocked) {
        // Would be a protocol error to allow this
        Debug::log(ERR, "Trying to unlock the session, but never recieved the locked event!");
        return;
    }

    m_sLockState.lock->sendUnlockAndDestroy();
    m_sLockState.lock = nullptr;

    Debug::log(LOG, "Unlocked, exiting!");

    m_bTerminate = true;
    m_bLocked    = false;

    wl_display_roundtrip(m_sWaylandState.display);
}

void CHyprlock::onLockLocked() {
    Debug::log(LOG, "onLockLocked called");

    m_bLocked = true;
}

void CHyprlock::onLockFinished() {
    Debug::log(LOG, "onLockFinished called. Seems we got yeeten. Is another lockscreen running?");

    if (!m_sLockState.lock) {
        Debug::log(ERR, "onLockFinished without a lock object!");
        return;
    }

    if (m_bLocked)
        // The `finished` event specifies that whenever the `locked` event has been recieved and the compositor sends `finished`,
        // `unlock_and_destroy` should be called by the client.
        // This does not mean the session gets unlocked! That is ultimately the responsiblity of the compositor.
        m_sLockState.lock->sendUnlockAndDestroy();
    else
        m_sLockState.lock.reset();

    m_sLockState.lock = nullptr;
    m_bTerminate      = true;
}

SP<CCExtSessionLockManagerV1> CHyprlock::getSessionLockMgr() {
    return m_sWaylandState.sessionLock;
}

SP<CCExtSessionLockV1> CHyprlock::getSessionLock() {
    return m_sLockState.lock;
}

SP<CCWlCompositor> CHyprlock::getCompositor() {
    return m_sWaylandState.compositor;
}

wl_display* CHyprlock::getDisplay() {
    return m_sWaylandState.display;
}

SP<CCWpFractionalScaleManagerV1> CHyprlock::getFractionalMgr() {
    return m_sWaylandState.fractional;
}

SP<CCWpViewporter> CHyprlock::getViewporter() {
    return m_sWaylandState.viewporter;
}

size_t CHyprlock::getPasswordBufferLen() {
    return m_sPasswordState.passBuffer.length();
}

size_t CHyprlock::getPasswordBufferDisplayLen() {
    // Counts utf-8 codepoints in the buffer. A byte is counted if it does not match 0b10xxxxxx.
    return std::count_if(m_sPasswordState.passBuffer.begin(), m_sPasswordState.passBuffer.end(), [](char c) { return (c & 0xc0) != 0x80; });
}

ASP<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data, bool force) {
    std::lock_guard<std::mutex> lg(m_sLoopState.timersMutex);
    const auto                  T = m_vTimers.emplace_back(makeAtomicShared<CTimer>(timeout, cb_, data, force));
    m_sLoopState.timerEvent       = true;
    m_sLoopState.timerCV.notify_all();
    return T;
}

std::vector<ASP<CTimer>> CHyprlock::getTimers() {
    return m_vTimers;
}

void CHyprlock::enqueueForceUpdateTimers() {
    addTimer(
        std::chrono::milliseconds(1),
        [](ASP<CTimer> self, void* data) {
            for (auto& t : g_pHyprlock->getTimers()) {
                if (t->canForceUpdate()) {
                    t->call(t);
                    t->cancel();
                }
            }
        },
        nullptr, false);
}

SP<CCZwlrScreencopyManagerV1> CHyprlock::getScreencopy() {
    return m_sWaylandState.screencopy;
}

SP<CCWlShm> CHyprlock::getShm() {
    return m_sWaylandState.shm;
}
07070100000034000081A4000000000000000000000001688B5FE90000169D000000000000000000000000000000000000002500000000hyprlock-0.9.1/src/core/hyprlock.hpp#pragma once

#include "../defines.hpp"
#include "wayland.hpp"
#include "ext-session-lock-v1.hpp"
#include "fractional-scale-v1.hpp"
#include "wlr-screencopy-unstable-v1.hpp"
#include "linux-dmabuf-v1.hpp"
#include "viewporter.hpp"
#include "Output.hpp"
#include "Seat.hpp"
#include "CursorShape.hpp"
#include "Timer.hpp"
#include <memory>
#include <vector>
#include <condition_variable>
#include <optional>

#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>

#include <gbm.h>
#include <xf86drm.h>

struct SDMABUFModifier {
    uint32_t fourcc = 0;
    uint64_t mod    = 0;
};

class CHyprlock {
  public:
    CHyprlock(const std::string& wlDisplay, const bool immediateRender, const int gracePeriod);
    ~CHyprlock();

    void                       run();

    void                       unlock();
    bool                       isUnlocked();

    ASP<CTimer>                addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data, bool force = false);

    void                       enqueueForceUpdateTimers();

    void                       onLockLocked();
    void                       onLockFinished();

    bool                       acquireSessionLock();
    void                       releaseSessionLock();

    void                       onKey(uint32_t key, bool down);
    void                       onClick(uint32_t button, bool down, const Vector2D& pos);
    void                       onHover(const Vector2D& pos);
    void                       startKeyRepeat(xkb_keysym_t sym);
    void                       repeatKey(xkb_keysym_t sym);
    void                       handleKeySym(xkb_keysym_t sym, bool compose);
    void                       onPasswordCheckTimer();
    void                       clearPasswordBuffer();
    bool                       passwordCheckWaiting();
    std::optional<std::string> passwordLastFailReason();

    void                       renderOutput(const std::string& stringPort);
    void                       renderAllOutputs();

    size_t                     getPasswordBufferLen();
    size_t                     getPasswordBufferDisplayLen();

    SP<CCExtSessionLockManagerV1>    getSessionLockMgr();
    SP<CCExtSessionLockV1>           getSessionLock();
    SP<CCWlCompositor>               getCompositor();
    wl_display*                      getDisplay();
    SP<CCWpFractionalScaleManagerV1> getFractionalMgr();
    SP<CCWpViewporter>               getViewporter();
    SP<CCZwlrScreencopyManagerV1>    getScreencopy();
    SP<CCWlShm>                      getShm();

    int32_t                          m_iKeebRepeatRate  = 25;
    int32_t                          m_iKeebRepeatDelay = 600;

    xkb_layout_index_t               m_uiActiveLayout = 0;

    bool                             m_bTerminate = false;

    bool                             m_lockAquired = false;
    bool                             m_bLocked     = false;

    bool                             m_bCapsLock = false;
    bool                             m_bNumLock  = false;
    bool                             m_bCtrl     = false;

    bool                             m_bImmediateRender = false;

    std::string                      m_sCurrentDesktop = "";

    //
    std::chrono::system_clock::time_point m_tGraceEnds;
    Vector2D                              m_vLastEnterCoords = {};
    WP<COutput>                           m_focusedOutput;

    Vector2D                              m_vMouseLocation = {};

    ASP<CTimer>                           m_pKeyRepeatTimer = nullptr;

    std::vector<SP<COutput>>              m_vOutputs;
    std::vector<ASP<CTimer>>              getTimers();

    struct {
        SP<CCZwpLinuxDmabufV1>         linuxDmabuf         = nullptr;
        SP<CCZwpLinuxDmabufFeedbackV1> linuxDmabufFeedback = nullptr;

        gbm_bo*                        gbm       = nullptr;
        gbm_device*                    gbmDevice = nullptr;

        void*                          formatTable     = nullptr;
        size_t                         formatTableSize = 0;
        bool                           deviceUsed      = false;

        std::vector<SDMABUFModifier>   dmabufMods;
    } dma;
    gbm_device* createGBMDevice(drmDevice* dev);

  private:
    struct {
        wl_display*                      display     = nullptr;
        SP<CCWlRegistry>                 registry    = nullptr;
        SP<CCExtSessionLockManagerV1>    sessionLock = nullptr;
        SP<CCWlCompositor>               compositor  = nullptr;
        SP<CCWpFractionalScaleManagerV1> fractional  = nullptr;
        SP<CCWpViewporter>               viewporter  = nullptr;
        SP<CCZwlrScreencopyManagerV1>    screencopy  = nullptr;
        SP<CCWlShm>                      shm         = nullptr;
    } m_sWaylandState;

    void addDmabufListener();

    struct {
        SP<CCExtSessionLockV1> lock = nullptr;
    } m_sLockState;

    struct {
        std::string passBuffer      = "";
        size_t      failedAttempts  = 0;
        bool        displayFailText = false;
    } m_sPasswordState;

    struct {
        std::mutex              timersMutex;
        std::mutex              eventRequestMutex;
        std::mutex              eventLoopMutex;
        std::condition_variable loopCV;
        bool                    event = false;

        std::condition_variable wlDispatchCV;
        bool                    wlDispatched = false;

        std::condition_variable timerCV;
        std::mutex              timerRequestMutex;
        bool                    timerEvent = false;
    } m_sLoopState;

    std::vector<ASP<CTimer>> m_vTimers;

    std::vector<uint32_t>    m_vPressedKeys;
};

inline UP<CHyprlock> g_pHyprlock;
07070100000035000081A4000000000000000000000001688B5FE9000001C4000000000000000000000000000000000000001F00000000hyprlock-0.9.1/src/defines.hpp#pragma once

#include <hyprutils/memory/WeakPtr.hpp>
#include <hyprutils/memory/UniquePtr.hpp>
#include <hyprutils/memory/Atomic.hpp>
#include <hyprgraphics/color/Color.hpp>

using namespace Hyprutils::Memory;
using namespace Hyprgraphics;
#define SP CSharedPointer
#define WP CWeakPointer
#define UP CUniquePointer

#define ASP CAtomicSharedPointer
#define AWP CAtomicWeakPointer

typedef int64_t    OUTPUTID;
constexpr OUTPUTID OUTPUT_INVALID = -1;
07070100000036000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001B00000000hyprlock-0.9.1/src/helpers07070100000037000081A4000000000000000000000001688B5FE900000775000000000000000000000000000000000000003000000000hyprlock-0.9.1/src/helpers/AnimatedVariable.hpp#pragma once

#include <hyprutils/animation/AnimatedVariable.hpp>

#include "Color.hpp"
#include "Math.hpp"
#include "../defines.hpp"
#include "../config/ConfigDataValues.hpp"

enum eAnimatedVarType {
    AVARTYPE_INVALID = -1,
    AVARTYPE_FLOAT,
    AVARTYPE_VECTOR,
    AVARTYPE_COLOR,
    AVARTYPE_GRADIENT
};

// Utility to bind a type with its corresponding eAnimatedVarType
template <class T>
// NOLINTNEXTLINE(readability-identifier-naming)
struct STypeToAnimatedVarType_t {
    static constexpr eAnimatedVarType value = AVARTYPE_INVALID;
};

template <>
struct STypeToAnimatedVarType_t<float> {
    static constexpr eAnimatedVarType value = AVARTYPE_FLOAT;
};

template <>
struct STypeToAnimatedVarType_t<Vector2D> {
    static constexpr eAnimatedVarType value = AVARTYPE_VECTOR;
};

template <>
struct STypeToAnimatedVarType_t<CHyprColor> {
    static constexpr eAnimatedVarType value = AVARTYPE_COLOR;
};

template <>
struct STypeToAnimatedVarType_t<CGradientValueData> {
    static constexpr eAnimatedVarType value = AVARTYPE_GRADIENT;
};

template <class T>
inline constexpr eAnimatedVarType typeToeAnimatedVarType = STypeToAnimatedVarType_t<T>::value;

// Utility to define a concept as a list of possible type
template <class T, class... U>
concept OneOf = (... or std::same_as<T, U>);

// Concept to describe which type can be placed into CAnimatedVariable
// This is mainly to get better errors if we put a type that's not supported
// Otherwise template errors are ugly
template <class T>
concept Animable = OneOf<T, Vector2D, float, CHyprColor, CGradientValueData>;

struct SAnimationContext {};

template <Animable VarType>
using CAnimatedVariable = Hyprutils::Animation::CGenericAnimatedVariable<VarType, SAnimationContext>;

template <Animable VarType>
using PHLANIMVAR = SP<CAnimatedVariable<VarType>>;

template <Animable VarType>
using PHLANIMVARREF = WP<CAnimatedVariable<VarType>>;
07070100000038000081A4000000000000000000000001688B5FE9000005A9000000000000000000000000000000000000002500000000hyprlock-0.9.1/src/helpers/Color.cpp#include "Color.hpp"

#define ALPHA(c) ((double)(((c) >> 24) & 0xff) / 255.0)
#define RED(c)   ((double)(((c) >> 16) & 0xff) / 255.0)
#define GREEN(c) ((double)(((c) >> 8) & 0xff) / 255.0)
#define BLUE(c)  ((double)(((c)) & 0xff) / 255.0)

CHyprColor::CHyprColor() {
    ;
}

CHyprColor::CHyprColor(float r_, float g_, float b_, float a_) : r(r_), g(g_), b(b_), a(a_) {
    okLab = Hyprgraphics::CColor(Hyprgraphics::CColor::SSRGB{.r = r, .g = g, .b = b}).asOkLab();
}

CHyprColor::CHyprColor(uint64_t hex) : r(RED(hex)), g(GREEN(hex)), b(BLUE(hex)), a(ALPHA(hex)) {
    okLab = Hyprgraphics::CColor(Hyprgraphics::CColor::SSRGB{.r = r, .g = g, .b = b}).asOkLab();
}

CHyprColor::CHyprColor(const Hyprgraphics::CColor& color, float a_) : a(a_) {
    const auto SRGB = color.asRgb();
    r               = SRGB.r;
    g               = SRGB.g;
    b               = SRGB.b;

    okLab = color.asOkLab();
}

uint32_t CHyprColor::getAsHex() const {
    return ((uint32_t)(a * 255.f) * 0x1000000) + ((uint32_t)(r * 255.f) * 0x10000) + ((uint32_t)(g * 255.f) * 0x100) + ((uint32_t)(b * 255.f) * 0x1);
}

Hyprgraphics::CColor::SSRGB CHyprColor::asRGB() const {
    return {.r = r, .g = g, .b = b};
}

Hyprgraphics::CColor::SOkLab CHyprColor::asOkLab() const {
    return okLab;
}

Hyprgraphics::CColor::SHSL CHyprColor::asHSL() const {
    return Hyprgraphics::CColor(okLab).asHSL();
}

CHyprColor CHyprColor::stripA() const {
    return {r, g, b, 1.F};
}
07070100000039000081A4000000000000000000000001688B5FE9000004D1000000000000000000000000000000000000002500000000hyprlock-0.9.1/src/helpers/Color.hpp#pragma once

#include <cstdint>
#include "../helpers/Log.hpp"
#include <hyprgraphics/color/Color.hpp>

class CHyprColor {
  public:
    CHyprColor();
    CHyprColor(float r, float g, float b, float a);
    CHyprColor(const Hyprgraphics::CColor& col, float a);
    CHyprColor(uint64_t);

    // AR32
    uint32_t                     getAsHex() const;
    Hyprgraphics::CColor::SSRGB  asRGB() const;
    Hyprgraphics::CColor::SOkLab asOkLab() const;
    Hyprgraphics::CColor::SHSL   asHSL() const;
    CHyprColor                   stripA() const;

    //
    bool operator==(const CHyprColor& c2) const {
        return c2.r == r && c2.g == g && c2.b == b && c2.a == a;
    }

    // stubs for the AnimationMgr
    CHyprColor operator-(const CHyprColor& c2) const {
        RASSERT(false, "CHyprColor: - is a STUB");
        return {};
    }

    CHyprColor operator+(const CHyprColor& c2) const {
        RASSERT(false, "CHyprColor: + is a STUB");
        return {};
    }

    CHyprColor operator*(const float& c2) const {
        RASSERT(false, "CHyprColor: * is a STUB");
        return {};
    }

    double r = 0, g = 0, b = 0, a = 0;

  private:
    Hyprgraphics::CColor::SOkLab okLab; // cache for the OkLab representation
};
0707010000003A000081A4000000000000000000000001688B5FE900000866000000000000000000000000000000000000002300000000hyprlock-0.9.1/src/helpers/Log.hpp#pragma once
#include <format>
#include <string>
#include <print>

enum eLogLevel {
    TRACE = 0,
    INFO,
    LOG,
    WARN,
    ERR,
    CRIT,
    NONE
};

#define RASSERT(expr, reason, ...)                                                                                                                                                 \
    if (!(expr)) {                                                                                                                                                                 \
        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); })().c_str());                               \
        std::abort();                                                                                                                                                              \
    }

#define ASSERT(expr) RASSERT(expr, "?")

namespace Debug {
    constexpr const char* logLevelString(eLogLevel level) {
        switch (level) {
            case TRACE: return "TRACE"; break;
            case INFO: return "INFO"; break;
            case LOG: return "LOG"; break;
            case WARN: return "WARN"; break;
            case ERR: return "ERR"; break;
            case CRIT: return "CRITICAL"; break;
            default: return "??";
        }
    }
    inline bool quiet   = false;
    inline bool verbose = false;

    template <typename... Args>
    void log(eLogLevel level, const std::string& fmt, Args&&... args) {

        if (!verbose && level == TRACE)
            return;

        if (quiet)
            return;

        if (level != NONE) {
            std::println("[{}] {}", logLevelString(level), std::vformat(fmt, std::make_format_args(args...)));
        }
    }
};0707010000003B000081A4000000000000000000000001688B5FE90000050E000000000000000000000000000000000000002400000000hyprlock-0.9.1/src/helpers/Math.cpp#include "Math.hpp"

Hyprutils::Math::eTransform wlTransformToHyprutils(wl_output_transform t) {
    switch (t) {
        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 Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_NORMAL;
}

wl_output_transform invertTransform(wl_output_transform tr) {
    if ((tr & WL_OUTPUT_TRANSFORM_90) && !(tr & WL_OUTPUT_TRANSFORM_FLIPPED))
        tr = (wl_output_transform)(tr ^ (int)WL_OUTPUT_TRANSFORM_180);

    return tr;
}
0707010000003C000081A4000000000000000000000001688B5FE90000013C000000000000000000000000000000000000002400000000hyprlock-0.9.1/src/helpers/Math.hpp#pragma once

#include <wayland-client.h>

#include <hyprutils/math/Box.hpp>
#include <hyprutils/math/Vector2D.hpp>
#include <hyprutils/math/Mat3x3.hpp>

using namespace Hyprutils::Math;

eTransform          wlTransformToHyprutils(wl_output_transform t);
wl_output_transform invertTransform(wl_output_transform tr);
0707010000003D000081A4000000000000000000000001688B5FE9000017ED000000000000000000000000000000000000002D00000000hyprlock-0.9.1/src/helpers/MiscFunctions.cpp#include <filesystem>
#include <algorithm>
#include <cmath>
#include <fcntl.h>
#include "MiscFunctions.hpp"
#include "Log.hpp"
#include <hyprutils/string/String.hpp>
#include <hyprutils/os/Process.hpp>
#include <unistd.h>

using namespace Hyprutils::String;
using namespace Hyprutils::OS;

std::string absolutePath(const std::string& rawpath, const std::string& currentDir) {
    std::filesystem::path path(rawpath);

    // Handling where rawpath starts with '~'
    if (!rawpath.empty() && rawpath[0] == '~') {
        static const char* const ENVHOME = getenv("HOME");
        path                             = std::filesystem::path(ENVHOME) / path.relative_path().string().substr(2);
    }

    // Handling e.g. ./, ../
    if (path.is_relative()) {
        return std::filesystem::weakly_canonical(std::filesystem::path(currentDir) / path);
    } else {
        return std::filesystem::weakly_canonical(path);
    }
}

int64_t configStringToInt(const std::string& VALUE) {
    auto parseHex = [](const std::string& value) -> int64_t {
        try {
            size_t position;
            auto   result = stoll(value, &position, 16);
            if (position == value.size())
                return result;
        } catch (const std::exception&) {}
        throw std::invalid_argument("invalid hex " + value);
    };
    if (VALUE.starts_with("0x")) {
        // Values with 0x are hex
        return parseHex(VALUE);
    } else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
        const auto VALUEWITHOUTFUNC = trim(VALUE.substr(5, VALUE.length() - 6));

        // try doing it the comma way first
        if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 3) {
            // cool
            std::string rolling = VALUEWITHOUTFUNC;
            auto        r       = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
            rolling             = rolling.substr(rolling.find(',') + 1);
            auto g              = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
            rolling             = rolling.substr(rolling.find(',') + 1);
            auto b              = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
            rolling             = rolling.substr(rolling.find(',') + 1);
            uint8_t a           = 0;
            try {
                a = std::round(std::stof(trim(rolling.substr(0, rolling.find(',')))) * 255.f);
            } catch (std::exception& e) { throw std::invalid_argument("failed parsing " + VALUEWITHOUTFUNC); }

            return (a * (Hyprlang::INT)0x1000000) + (r * (Hyprlang::INT)0x10000) + (g * (Hyprlang::INT)0x100) + b;
        } else if (VALUEWITHOUTFUNC.length() == 8) {
            const auto RGBA = parseHex(VALUEWITHOUTFUNC);
            // now we need to RGBA -> ARGB. The config holds ARGB only.
            return (RGBA >> 8) + (0x1000000 * (RGBA & 0xFF));
        }

        throw std::invalid_argument("rgba() expects length of 8 characters (4 bytes) or 4 comma separated values");

    } else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
        const auto VALUEWITHOUTFUNC = trim(VALUE.substr(4, VALUE.length() - 5));

        // try doing it the comma way first
        if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 2) {
            // cool
            std::string rolling = VALUEWITHOUTFUNC;
            auto        r       = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
            rolling             = rolling.substr(rolling.find(',') + 1);
            auto g              = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
            rolling             = rolling.substr(rolling.find(',') + 1);
            auto b              = configStringToInt(trim(rolling.substr(0, rolling.find(','))));

            return (Hyprlang::INT)0xFF000000 + (r * (Hyprlang::INT)0x10000) + (g * (Hyprlang::INT)0x100) + b;
        } else if (VALUEWITHOUTFUNC.length() == 6) {
            return parseHex(VALUEWITHOUTFUNC) + 0xFF000000;
        }

        throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes) or 3 comma separated values");
    } else if (VALUE.starts_with("true") || VALUE.starts_with("on") || VALUE.starts_with("yes")) {
        return 1;
    } else if (VALUE.starts_with("false") || VALUE.starts_with("off") || VALUE.starts_with("no")) {
        return 0;
    }

    if (VALUE.empty() || !isNumber(VALUE, false))
        throw std::invalid_argument("cannot parse \"" + VALUE + "\" as an int.");

    try {
        const auto RES = std::stoll(VALUE);
        return RES;
    } catch (std::exception& e) { throw std::invalid_argument(std::string{"stoll threw: "} + e.what()); }

    return 0;
}

int createPoolFile(size_t size, std::string& name) {
    const auto XDGRUNTIMEDIR = getenv("XDG_RUNTIME_DIR");
    if (!XDGRUNTIMEDIR) {
        Debug::log(CRIT, "XDG_RUNTIME_DIR not set!");
        return -1;
    }

    name = std::string(XDGRUNTIMEDIR) + "/.hyprlock_sc_XXXXXX";

    const auto FD = mkstemp((char*)name.c_str());
    if (FD < 0) {
        Debug::log(CRIT, "createPoolFile: fd < 0");
        return -1;
    }
    // set cloexec
    long flags = fcntl(FD, F_GETFD);
    if (flags == -1) {
        close(FD);
        return -1;
    }

    if (fcntl(FD, F_SETFD, flags | FD_CLOEXEC) == -1) {
        close(FD);
        Debug::log(CRIT, "createPoolFile: fcntl < 0");
        return -1;
    }

    if (ftruncate(FD, size) < 0) {
        close(FD);
        Debug::log(CRIT, "createPoolFile: ftruncate < 0");
        return -1;
    }

    return FD;
}

std::string spawnSync(const std::string& cmd) {
    CProcess proc("/bin/sh", {"-c", cmd});
    if (!proc.runSync()) {
        Debug::log(ERR, "Failed to run \"{}\"", cmd);
        return "";
    }

    if (!proc.stdErr().empty())
        Debug::log(ERR, "Shell command \"{}\" STDERR:\n{}", cmd, proc.stdErr());

    return proc.stdOut();
}

void spawnAsync(const std::string& cmd) {
    CProcess proc("/bin/sh", {"-c", cmd});
    if (!proc.runAsync())
        Debug::log(ERR, "Failed to start \"{}\"", cmd);
}
0707010000003E000081A4000000000000000000000001688B5FE900000176000000000000000000000000000000000000002D00000000hyprlock-0.9.1/src/helpers/MiscFunctions.hpp#pragma once

#include <string>
#include <hyprlang.hpp>
#include <hyprutils/math/Vector2D.hpp>

std::string absolutePath(const std::string&, const std::string&);
int64_t     configStringToInt(const std::string& VALUE);
int         createPoolFile(size_t size, std::string& name);
std::string spawnSync(const std::string& cmd);
void        spawnAsync(const std::string& cmd);
0707010000003F000081A4000000000000000000000001688B5FE900001307000000000000000000000000000000000000001C00000000hyprlock-0.9.1/src/main.cpp
#include "config/ConfigManager.hpp"
#include "core/hyprlock.hpp"
#include "helpers/Log.hpp"
#include "core/AnimationManager.hpp"
#include <cstddef>
#include <string_view>

void help() {
    std::println("Usage: hyprlock [options]\n\n"
                 "Options:\n"
                 "  -v, --verbose            - Enable verbose logging\n"
                 "  -q, --quiet              - Disable logging\n"
                 "  -c FILE, --config FILE   - Specify config file to use\n"
                 "  --display NAME           - Specify the Wayland display to connect to\n"
                 "  --grace SECONDS          - Set grace period in seconds before requiring authentication\n"
                 "  --immediate-render       - Do not wait for resources before drawing the background\n"
                 "  --no-fade-in             - Disable the fade-in animation when the lock screen appears\n"
                 "  -V, --version            - Show version information\n"
                 "  -h, --help               - Show this help message");
}

std::optional<std::string> parseArg(const std::vector<std::string>& args, const std::string& flag, std::size_t& i) {
    if (i + 1 < args.size()) {
        return args[++i];
    } else {
        std::println(stderr, "Error: Missing value for {} option.", flag);
        return std::nullopt;
    }
}

static void printVersion() {
    constexpr bool ISTAGGEDRELEASE = std::string_view(HYPRLOCK_COMMIT) == HYPRLOCK_VERSION_COMMIT;
    if (ISTAGGEDRELEASE)
        std::println("Hyprlock version v{}", HYPRLOCK_VERSION);
    else
        std::println("Hyprlock version v{} (commit {})", HYPRLOCK_VERSION, HYPRLOCK_COMMIT);
}

int main(int argc, char** argv, char** envp) {
    std::string              configPath;
    std::string              wlDisplay;
    bool                     immediateRender = false;
    bool                     noFadeIn        = false;
    int                      graceSeconds    = 0;

    std::vector<std::string> args(argv, argv + argc);

    for (std::size_t i = 1; i < args.size(); ++i) {
        const std::string arg = argv[i];

        if (arg == "--help" || arg == "-h") {
            help();
            return 0;
        }

        if (arg == "--version" || arg == "-V") {
            printVersion();
            return 0;
        }

        if (arg == "--verbose" || arg == "-v")
            Debug::verbose = true;

        else if (arg == "--quiet" || arg == "-q")
            Debug::quiet = true;

        else if ((arg == "--config" || arg == "-c") && i + 1 < (std::size_t)argc) {
            if (auto value = parseArg(args, arg, i); value)
                configPath = *value;
            else
                return 1;

        } else if (arg == "--display" && i + 1 < (std::size_t)argc) {
            if (auto value = parseArg(args, arg, i); value)
                wlDisplay = *value;
            else
                return 1;

        } else if (arg == "--grace" && i + 1 < (std::size_t)argc) {
            if (auto value = parseArg(args, arg, i); value) {
                try {
                    graceSeconds = std::stoi(*value);
                    if (graceSeconds < 0) {
                        std::println(stderr, "Error: Grace period must be non-negative.");
                        return 1;
                    }
                } catch (const std::exception&) {
                    std::println(stderr, "Error: Invalid grace period value: {}", *value);
                    return 1;
                }
            } else
                return 1;

        } else if (arg == "--immediate") {
            graceSeconds = 0;
            Debug::log(WARN, R"("--immediate" is deprecated. Use the "--grace" option instead.)");
        }

        else if (arg == "--immediate-render")
            immediateRender = true;

        else if (arg == "--no-fade-in")
            noFadeIn = true;

        else {
            std::println(stderr, "Unknown option: {}", arg);
            help();
            return 1;
        }
    }

    printVersion();
    g_pAnimationManager = makeUnique<CHyprlockAnimationManager>();

    try {
        g_pConfigManager = makeUnique<CConfigManager>(configPath);
        g_pConfigManager->init();
    } catch (const std::exception& ex) {
        Debug::log(CRIT, "ConfigManager threw: {}", ex.what());
        if (std::string(ex.what()).contains("File does not exist"))
            Debug::log(NONE, "           Make sure you have a config.");

        return 1;
    }

    if (noFadeIn)
        g_pConfigManager->m_AnimationTree.setConfigForNode("fadeIn", false, 0.f, "default");

    try {
        g_pHyprlock = makeUnique<CHyprlock>(wlDisplay, immediateRender, graceSeconds);
        g_pHyprlock->run();
    } catch (const std::exception& ex) {
        Debug::log(CRIT, "Hyprlock threw: {}", ex.what());
        return 1;
    }

    return 0;
}
07070100000040000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000001C00000000hyprlock-0.9.1/src/renderer07070100000041000081A4000000000000000000000001688B5FE9000033ED000000000000000000000000000000000000003600000000hyprlock-0.9.1/src/renderer/AsyncResourceGatherer.cpp#include "AsyncResourceGatherer.hpp"
#include "../config/ConfigManager.hpp"
#include "../core/Egl.hpp"
#include "../core/hyprlock.hpp"
#include "../helpers/Color.hpp"
#include "../helpers/Log.hpp"
#include "../helpers/MiscFunctions.hpp"
#include <algorithm>
#include <cairo/cairo.h>
#include <filesystem>
#include <pango/pangocairo.h>
#include <sys/eventfd.h>
#include <hyprgraphics/image/Image.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
using namespace Hyprgraphics;
using namespace Hyprutils::OS;

CAsyncResourceGatherer::CAsyncResourceGatherer() {
    if (g_pHyprlock->getScreencopy())
        enqueueScreencopyFrames();

    initialGatherThread = std::thread([this]() { this->gather(); });
    asyncLoopThread     = std::thread([this]() { this->asyncAssetSpinLock(); });

    gatheredEventfd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
    if (!gatheredEventfd.isValid())
        Debug::log(ERR, "Failed to create eventfd: {}", strerror(errno));
}

void CAsyncResourceGatherer::enqueueScreencopyFrames() {
    static const auto ANIMATIONSENABLED = g_pConfigManager->getValue<Hyprlang::INT>("animations:enabled");

    const auto        FADEINCFG  = g_pConfigManager->m_AnimationTree.getConfig("fadeIn");
    const auto        FADEOUTCFG = g_pConfigManager->m_AnimationTree.getConfig("fadeOut");

    const bool        FADENEEDSSC = *ANIMATIONSENABLED &&
        ((FADEINCFG->pValues && FADEINCFG->pValues->internalEnabled) || // fadeIn or fadeOut enabled
         (FADEOUTCFG->pValues && FADEOUTCFG->pValues->internalEnabled));

    const auto BGSCREENSHOT = std::ranges::any_of(g_pConfigManager->getWidgetConfigs(), [](const auto& w) { //
        return w.type == "background" && std::string{std::any_cast<Hyprlang::STRING>(w.values.at("path"))} == "screenshot";
    });

    if (!BGSCREENSHOT && !FADENEEDSSC) {
        Debug::log(LOG, "Skipping screencopy");
        return;
    }

    for (const auto& MON : g_pHyprlock->m_vOutputs) {
        scframes.emplace_back(makeUnique<CScreencopyFrame>(MON));
    }
}

SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
    if (id.contains(CScreencopyFrame::RESOURCEIDPREFIX)) {
        for (auto& frame : scframes) {
            if (id == frame->m_resourceID)
                return frame->m_asset.ready ? &frame->m_asset : nullptr;
        }

        return nullptr;
    }

    for (auto& a : assets) {
        if (a.first == id)
            return &a.second;
    }

    if (apply()) {
        for (auto& a : assets) {
            if (a.first == id)
                return &a.second;
        }
    };

    return nullptr;
}

static SP<CCairoSurface> getCairoSurfaceFromImageFile(const std::filesystem::path& path) {
    auto image = CImage(path);
    if (!image.success()) {
        Debug::log(ERR, "Image {} could not be loaded: {}", path.string(), image.getError());
        return nullptr;
    }

    return image.cairoSurface();
}

void CAsyncResourceGatherer::gather() {
    const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();

    g_pEGL->makeCurrent(nullptr);

    // gather resources to preload
    // clang-format off
    int preloads = std::count_if(CWIDGETS.begin(), CWIDGETS.end(), [](const auto& w) {
        return w.type == "background" || w.type == "image";
    });
    // clang-format on

    progress = 0;
    for (auto& c : CWIDGETS) {
        if (c.type == "background" || c.type == "image") {
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 180100
            progress = progress + 1.0 / (preloads + 1.0);
#else
            progress += 1.0 / (preloads + 1.0);
#endif

            std::string path = std::any_cast<Hyprlang::STRING>(c.values.at("path"));

            if (path.empty() || path == "screenshot")
                continue;

            std::string id = (c.type == "background" ? std::string{"background:"} : std::string{"image:"}) + path;

            // render the image directly, since we are in a seperate thread
            CAsyncResourceGatherer::SPreloadRequest rq;
            rq.type  = CAsyncResourceGatherer::TARGET_IMAGE;
            rq.asset = path;
            rq.id    = id;

            renderImage(rq);
        }
    }

    while (!g_pHyprlock->m_bTerminate && std::ranges::any_of(scframes, [](const auto& d) { return !d->m_asset.ready; })) {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }

    gathered = true;
    // wake hyprlock from poll
    if (gatheredEventfd.isValid())
        eventfd_write(gatheredEventfd.get(), 1);
}

bool CAsyncResourceGatherer::apply() {
    preloadTargetsMutex.lock();

    if (preloadTargets.empty()) {
        preloadTargetsMutex.unlock();
        return false;
    }

    auto currentPreloadTargets = preloadTargets;
    preloadTargets.clear();
    preloadTargetsMutex.unlock();

    for (auto& t : currentPreloadTargets) {
        if (t.type == TARGET_IMAGE) {
            const auto           ASSET = &assets[t.id];

            const cairo_status_t SURFACESTATUS = (cairo_status_t)t.cairosurface->status();
            const auto           CAIROFORMAT   = cairo_image_surface_get_format(t.cairosurface->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;

            if (SURFACESTATUS != CAIRO_STATUS_SUCCESS) {
                Debug::log(ERR, "Resource {} invalid ({})", t.id, cairo_status_to_string(SURFACESTATUS));
                ASSET->texture.m_iType = TEXTURE_INVALID;
            }

            ASSET->texture.m_vSize = t.size;
            ASSET->texture.allocate();

            glBindTexture(GL_TEXTURE_2D, ASSET->texture.m_iTexID);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            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);
            }
            glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, ASSET->texture.m_vSize.x, ASSET->texture.m_vSize.y, 0, glFormat, glType, t.data);

            cairo_destroy((cairo_t*)t.cairo);
            t.cairosurface.reset();
        } else
            Debug::log(ERR, "Unsupported type in ::apply(): {}", (int)t.type);
    }

    return true;
}

void CAsyncResourceGatherer::renderImage(const SPreloadRequest& rq) {
    SPreloadTarget target;
    target.type = TARGET_IMAGE;
    target.id   = rq.id;

    std::filesystem::path ABSOLUTEPATH(absolutePath(rq.asset, ""));
    const auto            CAIROISURFACE = getCairoSurfaceFromImageFile(ABSOLUTEPATH);

    if (!CAIROISURFACE) {
        Debug::log(ERR, "renderImage: No cairo surface!");
        return;
    }

    const auto CAIRO = cairo_create(CAIROISURFACE->cairo());
    cairo_scale(CAIRO, 1, 1);

    target.cairo        = CAIRO;
    target.cairosurface = CAIROISURFACE;
    target.data         = CAIROISURFACE->data();
    target.size         = CAIROISURFACE->size();

    std::lock_guard lg{preloadTargetsMutex};
    preloadTargets.push_back(target);
}

void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
    SPreloadTarget target;
    target.type = TARGET_IMAGE; /* text is just an image lol */
    target.id   = rq.id;

    const int         FONTSIZE   = rq.props.contains("font_size") ? std::any_cast<int>(rq.props.at("font_size")) : 16;
    const CHyprColor  FONTCOLOR  = rq.props.contains("color") ? std::any_cast<CHyprColor>(rq.props.at("color")) : CHyprColor(1.0, 1.0, 1.0, 1.0);
    const std::string FONTFAMILY = rq.props.contains("font_family") ? std::any_cast<std::string>(rq.props.at("font_family")) : "Sans";
    const bool        ISCMD      = rq.props.contains("cmd") ? std::any_cast<bool>(rq.props.at("cmd")) : false;

    static const auto TRIM = g_pConfigManager->getValue<Hyprlang::INT>("general:text_trim");
    std::string       text = ISCMD ? spawnSync(rq.asset) : rq.asset;

    if (*TRIM) {
        text.erase(0, text.find_first_not_of(" \n\r\t"));
        text.erase(text.find_last_not_of(" \n\r\t") + 1);
    }

    auto CAIROSURFACE = makeShared<CCairoSurface>(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1920, 1080 /* dummy value */));
    auto CAIRO        = cairo_create(CAIROSURFACE->cairo());

    // draw title using Pango
    PangoLayout*          layout = pango_cairo_create_layout(CAIRO);

    PangoFontDescription* fontDesc = pango_font_description_from_string(FONTFAMILY.c_str());
    pango_font_description_set_size(fontDesc, FONTSIZE * PANGO_SCALE);
    pango_layout_set_font_description(layout, fontDesc);
    pango_font_description_free(fontDesc);

    if (rq.props.contains("text_align")) {
        const std::string TEXTALIGN = std::any_cast<std::string>(rq.props.at("text_align"));
        PangoAlignment    align     = PANGO_ALIGN_LEFT;
        if (TEXTALIGN == "center")
            align = PANGO_ALIGN_CENTER;
        else if (TEXTALIGN == "right")
            align = PANGO_ALIGN_RIGHT;

        pango_layout_set_alignment(layout, align);
    }

    PangoAttrList* attrList = nullptr;
    GError*        gError   = nullptr;
    char*          buf      = nullptr;
    if (pango_parse_markup(text.c_str(), -1, 0, &attrList, &buf, nullptr, &gError))
        pango_layout_set_text(layout, buf, -1);
    else {
        Debug::log(ERR, "Pango markup parsing for {} failed: {}", text, gError->message);
        g_error_free(gError);
        pango_layout_set_text(layout, text.c_str(), -1);
    }

    if (!attrList)
        attrList = pango_attr_list_new();

    if (buf)
        free(buf);

    pango_attr_list_insert(attrList, pango_attr_scale_new(1));
    pango_layout_set_attributes(layout, attrList);
    pango_attr_list_unref(attrList);

    int layoutWidth, layoutHeight;
    pango_layout_get_size(layout, &layoutWidth, &layoutHeight);

    // TODO: avoid this?
    cairo_destroy(CAIRO);
    CAIROSURFACE = makeShared<CCairoSurface>(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layoutWidth / PANGO_SCALE, layoutHeight / PANGO_SCALE));
    CAIRO        = cairo_create(CAIROSURFACE->cairo());

    // clear the pixmap
    cairo_save(CAIRO);
    cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
    cairo_paint(CAIRO);
    cairo_restore(CAIRO);

    // render the thing
    cairo_set_source_rgba(CAIRO, FONTCOLOR.r, FONTCOLOR.g, FONTCOLOR.b, FONTCOLOR.a);

    cairo_move_to(CAIRO, 0, 0);
    pango_cairo_show_layout(CAIRO, layout);

    g_object_unref(layout);

    cairo_surface_flush(CAIROSURFACE->cairo());

    target.cairo        = CAIRO;
    target.cairosurface = CAIROSURFACE;
    target.data         = CAIROSURFACE->data();
    target.size         = {layoutWidth / (double)PANGO_SCALE, layoutHeight / (double)PANGO_SCALE};

    std::lock_guard lg{preloadTargetsMutex};
    preloadTargets.push_back(target);
}

void CAsyncResourceGatherer::asyncAssetSpinLock() {
    while (!g_pHyprlock->m_bTerminate) {

        std::unique_lock lk(asyncLoopState.requestsMutex);
        if (!asyncLoopState.pending) // avoid a lock if a thread managed to request something already since we .unlock()ed
            asyncLoopState.requestsCV.wait_for(lk, std::chrono::seconds(5), [this] { return asyncLoopState.pending; }); // wait for events

        asyncLoopState.pending = false;

        if (asyncLoopState.requests.empty()) {
            lk.unlock();
            continue;
        }

        auto requests = asyncLoopState.requests;
        asyncLoopState.requests.clear();

        lk.unlock();

        // process requests
        for (auto& r : requests) {
            Debug::log(TRACE, "Processing requested resourceID {}", r.id);

            if (r.type == TARGET_TEXT) {
                renderText(r);
            } else if (r.type == TARGET_IMAGE) {
                renderImage(r);
            } else {
                Debug::log(ERR, "Unsupported async preload type {}??", (int)r.type);
                continue;
            }

            // plant timer for callback
            if (r.callback)
                g_pHyprlock->addTimer(std::chrono::milliseconds(0), [cb = r.callback](auto, auto) { cb(); }, nullptr);
        }
    }
}

void CAsyncResourceGatherer::requestAsyncAssetPreload(const SPreloadRequest& request) {
    Debug::log(TRACE, "Requesting label resource {}", request.id);

    std::lock_guard<std::mutex> lg(asyncLoopState.requestsMutex);
    asyncLoopState.requests.push_back(request);
    asyncLoopState.pending = true;
    asyncLoopState.requestsCV.notify_all();
}

void CAsyncResourceGatherer::unloadAsset(SPreloadedAsset* asset) {
    std::erase_if(assets, [asset](const auto& a) { return &a.second == asset; });
}

void CAsyncResourceGatherer::notify() {
    std::lock_guard<std::mutex> lg(asyncLoopState.requestsMutex);
    asyncLoopState.requests.clear();
    asyncLoopState.pending = true;
    asyncLoopState.requestsCV.notify_all();
}

void CAsyncResourceGatherer::await() {
    if (initialGatherThread.joinable())
        initialGatherThread.join();
    if (asyncLoopThread.joinable())
        asyncLoopThread.join();
}
07070100000042000081A4000000000000000000000001688B5FE900000A5B000000000000000000000000000000000000003600000000hyprlock-0.9.1/src/renderer/AsyncResourceGatherer.hpp#pragma once

#include "Screencopy.hpp"
#include <thread>
#include <atomic>
#include <vector>
#include <unordered_map>
#include <condition_variable>
#include <any>
#include "Shared.hpp"
#include <hyprgraphics/cairo/CairoSurface.hpp>
#include <hyprutils/os/FileDescriptor.hpp>

class CAsyncResourceGatherer {
  public:
    CAsyncResourceGatherer();
    std::atomic<bool>              gathered = false;
    Hyprutils::OS::CFileDescriptor gatheredEventfd;

    std::atomic<float>             progress = 0;

    /* only call from ogl thread */
    SPreloadedAsset* getAssetByID(const std::string& id);

    bool             apply();

    enum eTargetType {
        TARGET_IMAGE = 0,
        TARGET_TEXT
    };

    struct SPreloadRequest {
        eTargetType                               type;
        std::string                               asset;
        std::string                               id;

        std::unordered_map<std::string, std::any> props;

        // optional. Callbacks will be dispatched from the main thread,
        // so wayland/gl calls are OK.
        // will fire once the resource is fully loaded and ready.
        std::function<void()> callback = nullptr;
    };

    void requestAsyncAssetPreload(const SPreloadRequest& request);
    void unloadAsset(SPreloadedAsset* asset);
    void notify();
    void await();

  private:
    std::thread asyncLoopThread;
    std::thread initialGatherThread;

    void        asyncAssetSpinLock();
    void        renderText(const SPreloadRequest& rq);
    void        renderImage(const SPreloadRequest& rq);

    struct {
        std::condition_variable      requestsCV;
        std::mutex                   requestsMutex;

        std::vector<SPreloadRequest> requests;
        bool                         pending = false;

        bool                         busy = false;
    } asyncLoopState;

    struct SPreloadTarget {
        eTargetType                     type = TARGET_IMAGE;
        std::string                     id   = "";

        void*                           data  = nullptr;
        void*                           cairo = nullptr;
        SP<Hyprgraphics::CCairoSurface> cairosurface;

        Vector2D                        size;
    };

    std::vector<UP<CScreencopyFrame>>                scframes;

    std::vector<SPreloadTarget>                      preloadTargets;
    std::mutex                                       preloadTargetsMutex;

    std::unordered_map<std::string, SPreloadedAsset> assets;

    void                                             gather();
    void                                             enqueueScreencopyFrames();
};
07070100000043000081A4000000000000000000000001688B5FE900001066000000000000000000000000000000000000002C00000000hyprlock-0.9.1/src/renderer/Framebuffer.cpp#include "Framebuffer.hpp"
#include "../helpers/Log.hpp"
#include <hyprutils/os/FileDescriptor.hpp>
#include <libdrm/drm_fourcc.h>
#include <utility>

static uint32_t drmFormatToGL(uint32_t 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: return GL_RGB10_A2;
        default: return GL_RGBA;
    }
    return GL_RGBA;
}

static uint32_t glFormatToType(uint32_t gl) {
    return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE;
}

bool CFramebuffer::alloc(int w, int h, bool highres) {
    bool     firstAlloc = false;

    uint32_t glFormat = highres ? GL_RGBA16F : drmFormatToGL(DRM_FORMAT_XRGB2101010); // TODO: revise only 10b when I find a way to figure out without sc whether display is 10b
    uint32_t glType   = highres ? GL_FLOAT : glFormatToType(glFormat);

    if (m_iFb == (uint32_t)-1) {
        firstAlloc = true;
        glGenFramebuffers(1, &m_iFb);
    }

    if (m_cTex.m_iTexID == 0) {
        firstAlloc = true;
        glGenTextures(1, &m_cTex.m_iTexID);
        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);
        m_cTex.m_vSize = {w, h};
    }

    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);

        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);

            glBindFramebuffer(GL_FRAMEBUFFER, m_iFb);

            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_pStencilTex->m_iTexID, 0);
        }

        auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (status != GL_FRAMEBUFFER_COMPLETE) {
            Debug::log(ERR, "Framebuffer incomplete, couldn't create! (FB status: {})", status);
            abort();
        }

        Debug::log(TRACE, "Framebuffer created, status {}", status);
    }

    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    m_vSize = Vector2D(w, h);

    return true;
}

void CFramebuffer::addStencil() {
    if (!m_pStencilTex) {
        Debug::log(ERR, "No stencil texture allocated.");
        return;
    }

    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! (FB status: {})", status);

    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void CFramebuffer::bind() const {
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iFb);
    glViewport(0, 0, m_vSize.x, m_vSize.y);
}

void CFramebuffer::destroyBuffer() {
    if (m_iFb != (uint32_t)-1 && m_iFb)
        glDeleteFramebuffers(1, &m_iFb);

    if (m_cTex.m_iTexID)
        glDeleteTextures(1, &m_cTex.m_iTexID);

    if (m_pStencilTex && m_pStencilTex->m_iTexID)
        glDeleteTextures(1, &m_pStencilTex->m_iTexID);

    m_cTex.m_iTexID = 0;
    m_iFb           = -1;
    m_vSize         = Vector2D();
    m_pStencilTex   = nullptr;
}

CFramebuffer::~CFramebuffer() {
    destroyBuffer();
}

bool CFramebuffer::isAllocated() const {
    return m_iFb != (GLuint)-1;
}
07070100000044000081A4000000000000000000000001688B5FE900000254000000000000000000000000000000000000002C00000000hyprlock-0.9.1/src/renderer/Framebuffer.hpp#pragma once

#include "../helpers/Math.hpp"
#include <GLES3/gl32.h>
#include "Texture.hpp"

class CFramebuffer {
  public:
    ~CFramebuffer();

    bool          alloc(int w, int h, bool highres = false);
    void          addStencil();
    void          bind() const;
    void          destroyBuffer();
    bool          isAllocated() const;

    Vector2D      m_vSize;

    CTexture      m_cTex;
    GLuint        m_iFb = -1;

    CTexture*     m_pStencilTex = nullptr;

    CFramebuffer& operator=(CFramebuffer&&)      = delete;
    CFramebuffer& operator=(const CFramebuffer&) = delete;
};
07070100000045000081A4000000000000000000000001688B5FE900006641000000000000000000000000000000000000002900000000hyprlock-0.9.1/src/renderer/Renderer.cpp#include "Renderer.hpp"
#include "Shaders.hpp"
#include "Screencopy.hpp"
#include "../config/ConfigManager.hpp"
#include "../core/AnimationManager.hpp"
#include "../core/Egl.hpp"
#include "../core/Output.hpp"
#include "../core/hyprlock.hpp"
#include "../helpers/Color.hpp"
#include "../helpers/Log.hpp"
#include <GLES3/gl32.h>
#include <GLES3/gl3ext.h>
#include <GLES2/gl2ext.h>
#include <algorithm>
#include "widgets/PasswordInputField.hpp"
#include "widgets/Background.hpp"
#include "widgets/Label.hpp"
#include "widgets/Image.hpp"
#include "widgets/Shape.hpp"

inline const float fullVerts[] = {
    1, 0, // top right
    0, 0, // top left
    1, 1, // bottom right
    0, 1, // bottom left
};

GLuint compileShader(const GLuint& type, std::string src) {
    auto shader = glCreateShader(type);

    auto shaderSource = src.c_str();

    glShaderSource(shader, 1, &shaderSource, nullptr);
    glCompileShader(shader);

    GLint ok;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);

    RASSERT(ok != GL_FALSE, "compileShader() failed! GL_COMPILE_STATUS not OK!");

    return shader;
}

GLuint createProgram(const std::string& vert, const std::string& frag) {
    auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert);

    RASSERT(vertCompiled, "Compiling shader failed. VERTEX NULL! Shader source:\n\n{}", vert);

    auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag);

    RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT NULL! 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);

    RASSERT(ok != GL_FALSE, "createProgram() failed! GL_LINK_STATUS not OK!");

    return prog;
}

static void glMessageCallbackA(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
    if (type != GL_DEBUG_TYPE_ERROR)
        return;
    Debug::log(LOG, "[gl] {}", (const char*)message);
}

CRenderer::CRenderer() {
    g_pEGL->makeCurrent(nullptr);

    glEnable(GL_DEBUG_OUTPUT);
    glDebugMessageCallback(glMessageCallbackA, nullptr);

    GLuint prog          = createProgram(QUADVERTSRC, QUADFRAGSRC);
    rectShader.program   = prog;
    rectShader.proj      = glGetUniformLocation(prog, "proj");
    rectShader.color     = glGetUniformLocation(prog, "color");
    rectShader.posAttrib = glGetAttribLocation(prog, "pos");
    rectShader.topLeft   = glGetUniformLocation(prog, "topLeft");
    rectShader.fullSize  = glGetUniformLocation(prog, "fullSize");
    rectShader.radius    = glGetUniformLocation(prog, "radius");

    prog                        = createProgram(TEXVERTSRC, TEXFRAGSRCRGBA);
    texShader.program           = prog;
    texShader.proj              = glGetUniformLocation(prog, "proj");
    texShader.tex               = glGetUniformLocation(prog, "tex");
    texShader.alphaMatte        = glGetUniformLocation(prog, "texMatte");
    texShader.alpha             = glGetUniformLocation(prog, "alpha");
    texShader.texAttrib         = glGetAttribLocation(prog, "texcoord");
    texShader.matteTexAttrib    = glGetAttribLocation(prog, "texcoordMatte");
    texShader.posAttrib         = glGetAttribLocation(prog, "pos");
    texShader.discardOpaque     = glGetUniformLocation(prog, "discardOpaque");
    texShader.discardAlpha      = glGetUniformLocation(prog, "discardAlpha");
    texShader.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
    texShader.topLeft           = glGetUniformLocation(prog, "topLeft");
    texShader.fullSize          = glGetUniformLocation(prog, "fullSize");
    texShader.radius            = glGetUniformLocation(prog, "radius");
    texShader.applyTint         = glGetUniformLocation(prog, "applyTint");
    texShader.tint              = glGetUniformLocation(prog, "tint");
    texShader.useAlphaMatte     = glGetUniformLocation(prog, "useAlphaMatte");

    prog                           = createProgram(TEXVERTSRC, TEXMIXFRAGSRCRGBA);
    texMixShader.program           = prog;
    texMixShader.proj              = glGetUniformLocation(prog, "proj");
    texMixShader.tex               = glGetUniformLocation(prog, "tex1");
    texMixShader.tex2              = glGetUniformLocation(prog, "tex2");
    texMixShader.alphaMatte        = glGetUniformLocation(prog, "texMatte");
    texMixShader.alpha             = glGetUniformLocation(prog, "alpha");
    texMixShader.mixFactor         = glGetUniformLocation(prog, "mixFactor");
    texMixShader.texAttrib         = glGetAttribLocation(prog, "texcoord");
    texMixShader.matteTexAttrib    = glGetAttribLocation(prog, "texcoordMatte");
    texMixShader.posAttrib         = glGetAttribLocation(prog, "pos");
    texMixShader.discardOpaque     = glGetUniformLocation(prog, "discardOpaque");
    texMixShader.discardAlpha      = glGetUniformLocation(prog, "discardAlpha");
    texMixShader.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
    texMixShader.topLeft           = glGetUniformLocation(prog, "topLeft");
    texMixShader.fullSize          = glGetUniformLocation(prog, "fullSize");
    texMixShader.radius            = glGetUniformLocation(prog, "radius");
    texMixShader.applyTint         = glGetUniformLocation(prog, "applyTint");
    texMixShader.tint              = glGetUniformLocation(prog, "tint");
    texMixShader.useAlphaMatte     = glGetUniformLocation(prog, "useAlphaMatte");

    prog                          = createProgram(TEXVERTSRC, FRAGBLUR1);
    blurShader1.program           = prog;
    blurShader1.tex               = glGetUniformLocation(prog, "tex");
    blurShader1.alpha             = glGetUniformLocation(prog, "alpha");
    blurShader1.proj              = glGetUniformLocation(prog, "proj");
    blurShader1.posAttrib         = glGetAttribLocation(prog, "pos");
    blurShader1.texAttrib         = glGetAttribLocation(prog, "texcoord");
    blurShader1.radius            = glGetUniformLocation(prog, "radius");
    blurShader1.halfpixel         = glGetUniformLocation(prog, "halfpixel");
    blurShader1.passes            = glGetUniformLocation(prog, "passes");
    blurShader1.vibrancy          = glGetUniformLocation(prog, "vibrancy");
    blurShader1.vibrancy_darkness = glGetUniformLocation(prog, "vibrancy_darkness");

    prog                  = createProgram(TEXVERTSRC, FRAGBLUR2);
    blurShader2.program   = prog;
    blurShader2.tex       = glGetUniformLocation(prog, "tex");
    blurShader2.alpha     = glGetUniformLocation(prog, "alpha");
    blurShader2.proj      = glGetUniformLocation(prog, "proj");
    blurShader2.posAttrib = glGetAttribLocation(prog, "pos");
    blurShader2.texAttrib = glGetAttribLocation(prog, "texcoord");
    blurShader2.radius    = glGetUniformLocation(prog, "radius");
    blurShader2.halfpixel = glGetUniformLocation(prog, "halfpixel");

    prog                         = createProgram(TEXVERTSRC, FRAGBLURPREPARE);
    blurPrepareShader.program    = prog;
    blurPrepareShader.tex        = glGetUniformLocation(prog, "tex");
    blurPrepareShader.proj       = glGetUniformLocation(prog, "proj");
    blurPrepareShader.posAttrib  = glGetAttribLocation(prog, "pos");
    blurPrepareShader.texAttrib  = glGetAttribLocation(prog, "texcoord");
    blurPrepareShader.contrast   = glGetUniformLocation(prog, "contrast");
    blurPrepareShader.brightness = glGetUniformLocation(prog, "brightness");

    prog                          = createProgram(TEXVERTSRC, FRAGBLURFINISH);
    blurFinishShader.program      = prog;
    blurFinishShader.tex          = glGetUniformLocation(prog, "tex");
    blurFinishShader.proj         = glGetUniformLocation(prog, "proj");
    blurFinishShader.posAttrib    = glGetAttribLocation(prog, "pos");
    blurFinishShader.texAttrib    = glGetAttribLocation(prog, "texcoord");
    blurFinishShader.brightness   = glGetUniformLocation(prog, "brightness");
    blurFinishShader.noise        = glGetUniformLocation(prog, "noise");
    blurFinishShader.colorize     = glGetUniformLocation(prog, "colorize");
    blurFinishShader.colorizeTint = glGetUniformLocation(prog, "colorizeTint");
    blurFinishShader.boostA       = glGetUniformLocation(prog, "boostA");

    prog                               = createProgram(QUADVERTSRC, FRAGBORDER);
    borderShader.program               = prog;
    borderShader.proj                  = glGetUniformLocation(prog, "proj");
    borderShader.thick                 = glGetUniformLocation(prog, "thick");
    borderShader.posAttrib             = glGetAttribLocation(prog, "pos");
    borderShader.texAttrib             = glGetAttribLocation(prog, "texcoord");
    borderShader.topLeft               = glGetUniformLocation(prog, "topLeft");
    borderShader.bottomRight           = glGetUniformLocation(prog, "bottomRight");
    borderShader.fullSize              = glGetUniformLocation(prog, "fullSize");
    borderShader.fullSizeUntransformed = glGetUniformLocation(prog, "fullSizeUntransformed");
    borderShader.radius                = glGetUniformLocation(prog, "radius");
    borderShader.radiusOuter           = glGetUniformLocation(prog, "radiusOuter");
    borderShader.gradient              = glGetUniformLocation(prog, "gradient");
    borderShader.gradientLength        = glGetUniformLocation(prog, "gradientLength");
    borderShader.angle                 = glGetUniformLocation(prog, "angle");
    borderShader.gradient2             = glGetUniformLocation(prog, "gradient2");
    borderShader.gradient2Length       = glGetUniformLocation(prog, "gradient2Length");
    borderShader.angle2                = glGetUniformLocation(prog, "angle2");
    borderShader.gradientLerp          = glGetUniformLocation(prog, "gradientLerp");
    borderShader.alpha                 = glGetUniformLocation(prog, "alpha");

    asyncResourceGatherer = makeUnique<CAsyncResourceGatherer>();

    g_pAnimationManager->createAnimation(0.f, opacity, g_pConfigManager->m_AnimationTree.getConfig("fadeIn"));
}

//
CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf) {
    projection = Mat3x3::outputProjection(surf.size, HYPRUTILS_TRANSFORM_NORMAL);

    g_pEGL->makeCurrent(surf.eglSurface);
    glViewport(0, 0, surf.size.x, surf.size.y);

    GLint fb = 0;
    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fb);
    pushFb(fb);

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

    SRenderFeedback feedback;
    const bool      WAITFORASSETS = !g_pHyprlock->m_bImmediateRender && !asyncResourceGatherer->gathered;

    if (!WAITFORASSETS) {
        // render widgets
        const auto WIDGETS = getOrCreateWidgetsFor(surf);
        for (auto& w : WIDGETS) {
            feedback.needsFrame = w->draw({opacity->value()}) || feedback.needsFrame;
        }
    }

    feedback.needsFrame = feedback.needsFrame || !asyncResourceGatherer->gathered;

    glDisable(GL_BLEND);

    return feedback;
}

void CRenderer::renderRect(const CBox& box, const CHyprColor& col, int rounding) {
    const auto ROUNDEDBOX = box.copy().round();
    Mat3x3     matrix     = projMatrix.projectBox(ROUNDEDBOX, HYPRUTILS_TRANSFORM_NORMAL, box.rot);
    Mat3x3     glMatrix   = projection.copy().multiply(matrix);

    glUseProgram(rectShader.program);

    glUniformMatrix3fv(rectShader.proj, 1, GL_TRUE, glMatrix.getMatrix().data());

    // premultiply the color as well as we don't work with straight alpha
    glUniform4f(rectShader.color, col.r * col.a, col.g * col.a, col.b * col.a, col.a);

    const auto TOPLEFT  = Vector2D(ROUNDEDBOX.x, ROUNDEDBOX.y);
    const auto FULLSIZE = Vector2D(ROUNDEDBOX.width, ROUNDEDBOX.height);

    // Rounded corners
    glUniform2f(rectShader.topLeft, (float)TOPLEFT.x, (float)TOPLEFT.y);
    glUniform2f(rectShader.fullSize, (float)FULLSIZE.x, (float)FULLSIZE.y);
    glUniform1f(rectShader.radius, rounding);

    glVertexAttribPointer(rectShader.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);

    glEnableVertexAttribArray(rectShader.posAttrib);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glDisableVertexAttribArray(rectShader.posAttrib);
}

void CRenderer::renderBorder(const CBox& box, const CGradientValueData& gradient, int thickness, int rounding, float alpha) {
    const auto ROUNDEDBOX = box.copy().round();
    Mat3x3     matrix     = projMatrix.projectBox(ROUNDEDBOX, HYPRUTILS_TRANSFORM_NORMAL, box.rot);
    Mat3x3     glMatrix   = projection.copy().multiply(matrix);

    glUseProgram(borderShader.program);

    glUniformMatrix3fv(borderShader.proj, 1, GL_TRUE, glMatrix.getMatrix().data());

    glUniform4fv(borderShader.gradient, gradient.m_vColorsOkLabA.size() / 4, (float*)gradient.m_vColorsOkLabA.data());
    glUniform1i(borderShader.gradientLength, gradient.m_vColorsOkLabA.size() / 4);
    glUniform1f(borderShader.angle, (int)(gradient.m_fAngle / (M_PI / 180.0)) % 360 * (M_PI / 180.0));
    glUniform1f(borderShader.alpha, alpha);
    glUniform1i(borderShader.gradient2Length, 0);

    const auto TOPLEFT  = Vector2D(ROUNDEDBOX.x, ROUNDEDBOX.y);
    const auto FULLSIZE = Vector2D(ROUNDEDBOX.width, ROUNDEDBOX.height);

    glUniform2f(borderShader.topLeft, (float)TOPLEFT.x, (float)TOPLEFT.y);
    glUniform2f(borderShader.fullSize, (float)FULLSIZE.x, (float)FULLSIZE.y);
    glUniform2f(borderShader.fullSizeUntransformed, (float)box.width, (float)box.height);
    glUniform1f(borderShader.radius, rounding);
    glUniform1f(borderShader.radiusOuter, rounding);
    glUniform1f(borderShader.thick, thickness);

    glVertexAttribPointer(borderShader.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);
    glVertexAttribPointer(borderShader.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);

    glEnableVertexAttribArray(borderShader.posAttrib);
    glEnableVertexAttribArray(borderShader.texAttrib);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glDisableVertexAttribArray(borderShader.posAttrib);
    glDisableVertexAttribArray(borderShader.texAttrib);
}

void CRenderer::renderTexture(const CBox& box, const CTexture& tex, float a, int rounding, std::optional<eTransform> tr) {
    const auto ROUNDEDBOX = box.copy().round();
    Mat3x3     matrix     = projMatrix.projectBox(ROUNDEDBOX, tr.value_or(HYPRUTILS_TRANSFORM_FLIPPED_180), box.rot);
    Mat3x3     glMatrix   = projection.copy().multiply(matrix);

    CShader*   shader = &texShader;

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(tex.m_iTarget, tex.m_iTexID);

    glUseProgram(shader->program);

    glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix.getMatrix().data());
    glUniform1i(shader->tex, 0);
    glUniform1f(shader->alpha, a);
    const auto TOPLEFT  = Vector2D(ROUNDEDBOX.x, ROUNDEDBOX.y);
    const auto FULLSIZE = Vector2D(ROUNDEDBOX.width, ROUNDEDBOX.height);

    // Rounded corners
    glUniform2f(shader->topLeft, TOPLEFT.x, TOPLEFT.y);
    glUniform2f(shader->fullSize, FULLSIZE.x, FULLSIZE.y);
    glUniform1f(shader->radius, rounding);

    glUniform1i(shader->discardOpaque, 0);
    glUniform1i(shader->discardAlpha, 0);
    glUniform1i(shader->applyTint, 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);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glDisableVertexAttribArray(shader->posAttrib);
    glDisableVertexAttribArray(shader->texAttrib);

    glBindTexture(tex.m_iTarget, 0);
}

void CRenderer::renderTextureMix(const CBox& box, const CTexture& tex, const CTexture& tex2, float a, float mixFactor, int rounding, std::optional<eTransform> tr) {
    const auto ROUNDEDBOX = box.copy().round();
    Mat3x3     matrix     = projMatrix.projectBox(ROUNDEDBOX, tr.value_or(HYPRUTILS_TRANSFORM_FLIPPED_180), box.rot);
    Mat3x3     glMatrix   = projection.copy().multiply(matrix);

    CShader*   shader = &texMixShader;

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(tex.m_iTarget, tex.m_iTexID);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(tex2.m_iTarget, tex2.m_iTexID);

    glUseProgram(shader->program);

    glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix.getMatrix().data());
    glUniform1i(shader->tex, 0);
    glUniform1i(shader->tex2, 1);
    glUniform1f(shader->alpha, a);
    glUniform1f(shader->mixFactor, mixFactor);
    const auto TOPLEFT  = Vector2D(ROUNDEDBOX.x, ROUNDEDBOX.y);
    const auto FULLSIZE = Vector2D(ROUNDEDBOX.width, ROUNDEDBOX.height);

    // Rounded corners
    glUniform2f(shader->topLeft, TOPLEFT.x, TOPLEFT.y);
    glUniform2f(shader->fullSize, FULLSIZE.x, FULLSIZE.y);
    glUniform1f(shader->radius, rounding);

    glUniform1i(shader->discardOpaque, 0);
    glUniform1i(shader->discardAlpha, 0);
    glUniform1i(shader->applyTint, 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);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glDisableVertexAttribArray(shader->posAttrib);
    glDisableVertexAttribArray(shader->texAttrib);

    glBindTexture(tex.m_iTarget, 0);
}

template <class Widget>
static void createWidget(std::vector<ASP<IWidget>>& widgets) {
    const auto W = makeAtomicShared<Widget>();
    W->registerSelf(W);
    widgets.emplace_back(W);
}

std::vector<ASP<IWidget>>& CRenderer::getOrCreateWidgetsFor(const CSessionLockSurface& surf) {
    RASSERT(surf.m_outputID != OUTPUT_INVALID, "Invalid output ID!");

    if (!widgets.contains(surf.m_outputID)) {
        auto CWIDGETS = g_pConfigManager->getWidgetConfigs();

        std::ranges::sort(CWIDGETS, [](CConfigManager::SWidgetConfig& a, CConfigManager::SWidgetConfig& b) {
            return std::any_cast<Hyprlang::INT>(a.values.at("zindex")) < std::any_cast<Hyprlang::INT>(b.values.at("zindex"));
        });

        const auto POUTPUT = surf.m_outputRef.lock();
        for (auto& c : CWIDGETS) {
            if (!c.monitor.empty() && c.monitor != POUTPUT->stringPort && !POUTPUT->stringDesc.starts_with(c.monitor) && !("desc:" + POUTPUT->stringDesc).starts_with(c.monitor))
                continue;

            // by type
            if (c.type == "background") {
                createWidget<CBackground>(widgets[surf.m_outputID]);
            } else if (c.type == "input-field") {
                createWidget<CPasswordInputField>(widgets[surf.m_outputID]);
            } else if (c.type == "label") {
                createWidget<CLabel>(widgets[surf.m_outputID]);
            } else if (c.type == "shape") {
                createWidget<CShape>(widgets[surf.m_outputID]);
            } else if (c.type == "image") {
                createWidget<CImage>(widgets[surf.m_outputID]);
            } else {
                Debug::log(ERR, "Unknown widget type: {}", c.type);
                continue;
            }

            widgets[surf.m_outputID].back()->configure(c.values, POUTPUT);
        }
    }

    return widgets[surf.m_outputID];
}

void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
    glDisable(GL_BLEND);
    glDisable(GL_STENCIL_TEST);

    CBox box{0, 0, outfb.m_vSize.x, outfb.m_vSize.y};
    box.round();
    Mat3x3       matrix   = projMatrix.projectBox(box, HYPRUTILS_TRANSFORM_NORMAL, 0);
    Mat3x3       glMatrix = projection.copy().multiply(matrix);

    CFramebuffer mirrors[2];
    mirrors[0].alloc(outfb.m_vSize.x, outfb.m_vSize.y, true);
    mirrors[1].alloc(outfb.m_vSize.x, outfb.m_vSize.y, true);

    CFramebuffer* currentRenderToFB = &mirrors[0];

    // Begin with base color adjustments - global brightness and contrast
    // TODO: make this a part of the first pass maybe to save on a drawcall?
    {
        mirrors[1].bind();

        glActiveTexture(GL_TEXTURE0);

        glBindTexture(outfb.m_cTex.m_iTarget, outfb.m_cTex.m_iTexID);

        glTexParameteri(outfb.m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        glUseProgram(blurPrepareShader.program);

        glUniformMatrix3fv(blurPrepareShader.proj, 1, GL_TRUE, glMatrix.getMatrix().data());
        glUniform1f(blurPrepareShader.contrast, params.contrast);
        glUniform1f(blurPrepareShader.brightness, params.brightness);
        glUniform1i(blurPrepareShader.tex, 0);

        glVertexAttribPointer(blurPrepareShader.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);
        glVertexAttribPointer(blurPrepareShader.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);

        glEnableVertexAttribArray(blurPrepareShader.posAttrib);
        glEnableVertexAttribArray(blurPrepareShader.texAttrib);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        glDisableVertexAttribArray(blurPrepareShader.posAttrib);
        glDisableVertexAttribArray(blurPrepareShader.texAttrib);

        currentRenderToFB = &mirrors[1];
    }

    // declare the draw func
    auto drawPass = [&](CShader* pShader) {
        if (currentRenderToFB == &mirrors[0])
            mirrors[1].bind();
        else
            mirrors[0].bind();

        glActiveTexture(GL_TEXTURE0);

        glBindTexture(currentRenderToFB->m_cTex.m_iTarget, currentRenderToFB->m_cTex.m_iTexID);

        glTexParameteri(currentRenderToFB->m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        glUseProgram(pShader->program);

        // prep two shaders
        glUniformMatrix3fv(pShader->proj, 1, GL_TRUE, glMatrix.getMatrix().data());
        glUniform1f(pShader->radius, params.size);
        if (pShader == &blurShader1) {
            glUniform2f(blurShader1.halfpixel, 0.5f / (outfb.m_vSize.x / 2.f), 0.5f / (outfb.m_vSize.y / 2.f));
            glUniform1i(blurShader1.passes, params.passes);
            glUniform1f(blurShader1.vibrancy, params.vibrancy);
            glUniform1f(blurShader1.vibrancy_darkness, params.vibrancy_darkness);
        } else
            glUniform2f(blurShader2.halfpixel, 0.5f / (outfb.m_vSize.x * 2.f), 0.5f / (outfb.m_vSize.y * 2.f));
        glUniform1i(pShader->tex, 0);

        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);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        glDisableVertexAttribArray(pShader->posAttrib);
        glDisableVertexAttribArray(pShader->texAttrib);

        if (currentRenderToFB != &mirrors[0])
            currentRenderToFB = &mirrors[0];
        else
            currentRenderToFB = &mirrors[1];
    };

    // draw the things.
    // first draw is swap -> mirr
    mirrors[0].bind();
    glBindTexture(mirrors[1].m_cTex.m_iTarget, mirrors[1].m_cTex.m_iTexID);

    for (int i = 1; i <= params.passes; ++i) {
        drawPass(&blurShader1); // down
    }

    for (int i = params.passes - 1; i >= 0; --i) {
        drawPass(&blurShader2); // up
    }

    // finalize the image
    {
        if (currentRenderToFB == &mirrors[0])
            mirrors[1].bind();
        else
            mirrors[0].bind();

        glActiveTexture(GL_TEXTURE0);

        glBindTexture(currentRenderToFB->m_cTex.m_iTarget, currentRenderToFB->m_cTex.m_iTexID);

        glTexParameteri(currentRenderToFB->m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        glUseProgram(blurFinishShader.program);

        glUniformMatrix3fv(blurFinishShader.proj, 1, GL_TRUE, glMatrix.getMatrix().data());
        glUniform1f(blurFinishShader.noise, params.noise);
        glUniform1f(blurFinishShader.brightness, params.brightness);
        glUniform1i(blurFinishShader.colorize, params.colorize.has_value());
        if (params.colorize.has_value())
            glUniform3f(blurFinishShader.colorizeTint, params.colorize->r, params.colorize->g, params.colorize->b);
        glUniform1f(blurFinishShader.boostA, params.boostA);

        glUniform1i(blurFinishShader.tex, 0);

        glVertexAttribPointer(blurFinishShader.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);
        glVertexAttribPointer(blurFinishShader.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts);

        glEnableVertexAttribArray(blurFinishShader.posAttrib);
        glEnableVertexAttribArray(blurFinishShader.texAttrib);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        glDisableVertexAttribArray(blurFinishShader.posAttrib);
        glDisableVertexAttribArray(blurFinishShader.texAttrib);

        if (currentRenderToFB != &mirrors[0])
            currentRenderToFB = &mirrors[0];
        else
            currentRenderToFB = &mirrors[1];
    }

    // finish
    outfb.bind();
    renderTexture(box, currentRenderToFB->m_cTex, 1.0, 0, HYPRUTILS_TRANSFORM_NORMAL);

    glEnable(GL_BLEND);
}

void CRenderer::pushFb(GLint fb) {
    boundFBs.push_back(fb);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
}

void CRenderer::popFb() {
    boundFBs.pop_back();
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundFBs.empty() ? 0 : boundFBs.back());
}

void CRenderer::removeWidgetsFor(OUTPUTID id) {
    widgets.erase(id);
}

void CRenderer::reconfigureWidgetsFor(OUTPUTID id) {
    // TODO: reconfigure widgets by just calling their configure method again.
    // Requires a way to get a widgets config properties.
    // I think the best way would be to store the anonymos key of the widget config.
    removeWidgetsFor(id);
}

void CRenderer::startFadeIn() {
    Debug::log(LOG, "Starting fade in");
    *opacity = 1.f;

    opacity->setCallbackOnEnd([this](auto) { opacity->setConfig(g_pConfigManager->m_AnimationTree.getConfig("fadeOut")); }, true);
}

void CRenderer::startFadeOut(bool unlock) {
    *opacity = 0.f;

    if (unlock)
        opacity->setCallbackOnEnd([](auto) { g_pHyprlock->releaseSessionLock(); }, true);
}
07070100000046000081A4000000000000000000000001688B5FE900000A76000000000000000000000000000000000000002900000000hyprlock-0.9.1/src/renderer/Renderer.hpp#pragma once

#include <chrono>
#include <optional>
#include "Shader.hpp"
#include "../defines.hpp"
#include "../core/LockSurface.hpp"
#include "../helpers/AnimatedVariable.hpp"
#include "../helpers/Color.hpp"
#include "AsyncResourceGatherer.hpp"
#include "../config/ConfigDataValues.hpp"
#include "widgets/IWidget.hpp"
#include "Framebuffer.hpp"

typedef std::unordered_map<OUTPUTID, std::vector<ASP<IWidget>>> widgetMap_t;

class CRenderer {
  public:
    CRenderer();

    struct SRenderFeedback {
        bool needsFrame = false;
    };

    struct SBlurParams {
        int                       size = 0, passes = 0;
        float                     noise = 0, contrast = 0, brightness = 0, vibrancy = 0, vibrancy_darkness = 0;
        std::optional<CHyprColor> colorize;
        float                     boostA = 1.0;
    };

    SRenderFeedback renderLock(const CSessionLockSurface& surf);

    void            renderRect(const CBox& box, const CHyprColor& col, int rounding = 0);
    void            renderBorder(const CBox& box, const CGradientValueData& gradient, int thickness, int rounding = 0, float alpha = 1.0);
    void            renderTexture(const CBox& box, const CTexture& tex, float a = 1.0, int rounding = 0, std::optional<eTransform> tr = {});
    void renderTextureMix(const CBox& box, const CTexture& tex, const CTexture& tex2, float a = 1.0, float mixFactor = 0.0, int rounding = 0, std::optional<eTransform> tr = {});
    void blurFB(const CFramebuffer& outfb, SBlurParams params);

    UP<CAsyncResourceGatherer>            asyncResourceGatherer;
    std::chrono::system_clock::time_point firstFullFrameTime;

    void                                  pushFb(GLint fb);
    void                                  popFb();

    void                                  removeWidgetsFor(OUTPUTID id);
    void                                  reconfigureWidgetsFor(OUTPUTID id);

    void                                  startFadeIn();
    void                                  startFadeOut(bool unlock = false);
    std::vector<ASP<IWidget>>&            getOrCreateWidgetsFor(const CSessionLockSurface& surf);

  private:
    widgetMap_t        widgets;

    CShader            rectShader;
    CShader            texShader;
    CShader            texMixShader;
    CShader            blurShader1;
    CShader            blurShader2;
    CShader            blurPrepareShader;
    CShader            blurFinishShader;
    CShader            borderShader;

    Mat3x3             projMatrix = Mat3x3::identity();
    Mat3x3             projection;

    PHLANIMVAR<float>  opacity;

    std::vector<GLint> boundFBs;
};

inline UP<CRenderer> g_pRenderer;
07070100000047000081A4000000000000000000000001688B5FE9000048F1000000000000000000000000000000000000002B00000000hyprlock-0.9.1/src/renderer/Screencopy.cpp#include "Screencopy.hpp"
#include "../helpers/Log.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../core/hyprlock.hpp"
#include "../core/Egl.hpp"
#include "../config/ConfigManager.hpp"
#include "wlr-screencopy-unstable-v1.hpp"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <cstring>
#include <array>
#include <cstdint>
#include <gbm.h>
#include <hyprutils/memory/UniquePtr.hpp>
#include <unistd.h>
#include <sys/mman.h>
#include <libdrm/drm_fourcc.h>
#include <GLES3/gl32.h>
#include <GLES3/gl3ext.h>
#include <GLES2/gl2ext.h>

static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr;
static PFNEGLQUERYDMABUFMODIFIERSEXTPROC   eglQueryDmaBufModifiersEXT   = nullptr;

//
std::string CScreencopyFrame::getResourceId(SP<COutput> pOutput) {
    return RESOURCEIDPREFIX + std::format(":{}-{}x{}", pOutput->stringPort, pOutput->size.x, pOutput->size.y);
}

CScreencopyFrame::CScreencopyFrame(SP<COutput> pOutput) : m_outputRef(pOutput) {
    captureOutput();

    static const auto SCMODE = g_pConfigManager->getValue<Hyprlang::INT>("general:screencopy_mode");
    if (*SCMODE == 1)
        m_frame = makeUnique<CSCSHMFrame>(m_sc);
    else
        m_frame = makeUnique<CSCDMAFrame>(m_sc);
}

void CScreencopyFrame::captureOutput() {
    const auto POUTPUT = m_outputRef.lock();
    RASSERT(POUTPUT, "Screencopy, but no valid output");

    m_resourceID = getResourceId(POUTPUT);

    m_sc = makeShared<CCZwlrScreencopyFrameV1>(g_pHyprlock->getScreencopy()->sendCaptureOutput(false, POUTPUT->m_wlOutput->resource()));

    m_sc->setBufferDone([this](CCZwlrScreencopyFrameV1* r) {
        Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)this);

        if (!m_frame || !m_frame->onBufferDone() || !m_frame->m_wlBuffer) {
            Debug::log(ERR, "[sc] Failed to create a wayland buffer for the screencopy frame");
            return;
        }

        m_sc->sendCopy(m_frame->m_wlBuffer->resource());

        Debug::log(TRACE, "[sc] wlr frame copied");
    });

    m_sc->setFailed([this](CCZwlrScreencopyFrameV1* r) {
        Debug::log(ERR, "[sc] wlrOnFailed for {}", (void*)r);

        m_frame.reset();
    });

    m_sc->setReady([this](CCZwlrScreencopyFrameV1* r, uint32_t, uint32_t, uint32_t) {
        Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)this);

        if (!m_frame || !m_frame->onBufferReady(m_asset)) {
            Debug::log(ERR, "[sc] Failed to bind the screencopy buffer to a texture");
            return;
        }

        m_sc.reset();
    });
}

CSCDMAFrame::CSCDMAFrame(SP<CCZwlrScreencopyFrameV1> sc) : m_sc(sc) {
    if (!glEGLImageTargetTexture2DOES) {
        glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
        if (!glEGLImageTargetTexture2DOES) {
            Debug::log(ERR, "No glEGLImageTargetTexture2DOES??");
            return;
        }
    }

    if (!eglQueryDmaBufModifiersEXT)
        eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");

    m_sc->setLinuxDmabuf([this](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height) {
        Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)this);

        m_w   = width;
        m_h   = height;
        m_fmt = format;

        Debug::log(TRACE, "[sc] DMABUF format reported: {:x}", format);
    });

    m_sc->setBuffer([](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
        ; // unused by dma
    });
}

CSCDMAFrame::~CSCDMAFrame() {
    if (g_pEGL)
        eglDestroyImage(g_pEGL->eglDisplay, m_image);

    // leaks bo and stuff but lives throughout so for now who cares
}

bool CSCDMAFrame::onBufferDone() {
    uint32_t flags = GBM_BO_USE_RENDERING;

    if (!eglQueryDmaBufModifiersEXT) {
        Debug::log(WARN, "Querying modifiers without eglQueryDmaBufModifiersEXT support");
        m_bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, m_w, m_h, m_fmt, flags);
    } else {
        std::array<uint64_t, 64>   mods;
        std::array<EGLBoolean, 64> externalOnly;
        int                        num = 0;
        if (!eglQueryDmaBufModifiersEXT(g_pEGL->eglDisplay, m_fmt, 64, mods.data(), externalOnly.data(), &num) || num == 0) {
            Debug::log(WARN, "eglQueryDmaBufModifiersEXT failed, falling back to regular bo");
            m_bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, m_w, m_h, m_fmt, flags);
        } else {
            Debug::log(LOG, "eglQueryDmaBufModifiersEXT found {} mods", num);
            std::vector<uint64_t> goodMods;
            for (int i = 0; i < num; ++i) {
                if (externalOnly[i]) {
                    Debug::log(TRACE, "Modifier {:x} failed test", mods[i]);
                    continue;
                }

                Debug::log(TRACE, "Modifier {:x} passed test", mods[i]);
                goodMods.emplace_back(mods[i]);
            }

            m_bo = gbm_bo_create_with_modifiers2(g_pHyprlock->dma.gbmDevice, m_w, m_h, m_fmt, goodMods.data(), goodMods.size(), flags);
        }
    }

    if (!m_bo) {
        Debug::log(ERR, "[bo] Couldn't create a drm buffer");
        return false;
    }

    m_planes = gbm_bo_get_plane_count(m_bo);
    Debug::log(LOG, "[bo] has {} plane(s)", m_planes);

    m_mod = gbm_bo_get_modifier(m_bo);
    Debug::log(LOG, "[bo] chose modifier {:x}", m_mod);

    auto params = makeShared<CCZwpLinuxBufferParamsV1>(g_pHyprlock->dma.linuxDmabuf->sendCreateParams());
    if (!params) {
        Debug::log(ERR, "zwp_linux_dmabuf_v1_create_params failed");
        gbm_bo_destroy(m_bo);
        return false;
    }

    for (size_t plane = 0; plane < (size_t)m_planes; plane++) {
        m_stride[plane] = gbm_bo_get_stride_for_plane(m_bo, plane);
        m_offset[plane] = gbm_bo_get_offset(m_bo, plane);
        m_fd[plane]     = gbm_bo_get_fd_for_plane(m_bo, plane);

        if (m_fd[plane] < 0) {
            Debug::log(ERR, "gbm_m_bo_get_fd_for_plane failed");
            params.reset();
            gbm_bo_destroy(m_bo);
            for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) {
                close(m_fd[plane_tmp]);
            }
            return false;
        }

        params->sendAdd(m_fd[plane], plane, m_offset[plane], m_stride[plane], m_mod >> 32, m_mod & 0xffffffff);
    }

    m_wlBuffer = makeShared<CCWlBuffer>(params->sendCreateImmed(m_w, m_h, m_fmt, (zwpLinuxBufferParamsV1Flags)0));
    params.reset();

    if (!m_wlBuffer) {
        Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed");
        gbm_bo_destroy(m_bo);
        for (size_t plane = 0; plane < (size_t)m_planes; plane++)
            close(m_fd[plane]);

        return false;
    }

    return true;
}

bool CSCDMAFrame::onBufferReady(SPreloadedAsset& asset) {
    static constexpr struct {
        EGLAttrib fd;
        EGLAttrib offset;
        EGLAttrib pitch;
        EGLAttrib modlo;
        EGLAttrib modhi;
    } attrNames[4] = {{.fd     = EGL_DMA_BUF_PLANE0_FD_EXT,
                       .offset = EGL_DMA_BUF_PLANE0_OFFSET_EXT,
                       .pitch  = EGL_DMA_BUF_PLANE0_PITCH_EXT,
                       .modlo  = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
                       .modhi  = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT},
                      {.fd     = EGL_DMA_BUF_PLANE1_FD_EXT,
                       .offset = EGL_DMA_BUF_PLANE1_OFFSET_EXT,
                       .pitch  = EGL_DMA_BUF_PLANE1_PITCH_EXT,
                       .modlo  = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
                       .modhi  = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT},
                      {.fd     = EGL_DMA_BUF_PLANE2_FD_EXT,
                       .offset = EGL_DMA_BUF_PLANE2_OFFSET_EXT,
                       .pitch  = EGL_DMA_BUF_PLANE2_PITCH_EXT,
                       .modlo  = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
                       .modhi  = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT},
                      {.fd     = EGL_DMA_BUF_PLANE3_FD_EXT,
                       .offset = EGL_DMA_BUF_PLANE3_OFFSET_EXT,
                       .pitch  = EGL_DMA_BUF_PLANE3_PITCH_EXT,
                       .modlo  = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT,
                       .modhi  = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT}};

    std::vector<EGLAttrib> attribs = {
        EGL_WIDTH, m_w, EGL_HEIGHT, m_h, EGL_LINUX_DRM_FOURCC_EXT, m_fmt,
    };
    for (int i = 0; i < m_planes; i++) {
        attribs.emplace_back(attrNames[i].fd);
        attribs.emplace_back(m_fd[i]);
        attribs.emplace_back(attrNames[i].offset);
        attribs.emplace_back(m_offset[i]);
        attribs.emplace_back(attrNames[i].pitch);
        attribs.emplace_back(m_stride[i]);
        if (m_mod != DRM_FORMAT_MOD_INVALID) {
            attribs.emplace_back(attrNames[i].modlo);
            attribs.emplace_back(m_mod & 0xFFFFFFFF);
            attribs.emplace_back(attrNames[i].modhi);
            attribs.emplace_back(m_mod >> 32);
        }
    }
    attribs.emplace_back(EGL_NONE);

    m_image = eglCreateImage(g_pEGL->eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data());

    if (m_image == EGL_NO_IMAGE) {
        Debug::log(ERR, "Failed creating an egl image");
        return false;
    }

    asset.texture.allocate();
    asset.texture.m_vSize = {m_w, m_h};
    glBindTexture(GL_TEXTURE_2D, asset.texture.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_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image);
    glBindTexture(GL_TEXTURE_2D, 0);

    Debug::log(LOG, "Got dma frame with size {}", asset.texture.m_vSize);

    asset.ready = true;

    return true;
}

CSCSHMFrame::CSCSHMFrame(SP<CCZwlrScreencopyFrameV1> sc) : m_sc(sc) {
    Debug::log(TRACE, "[sc] [shm] Creating a SHM frame");

    m_sc->setBuffer([this](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
        Debug::log(TRACE, "[sc] [shm] wlrOnBuffer for {}", (void*)this);

        const auto SIZE = stride * height;
        m_shmFmt        = format;
        m_w             = width;
        m_h             = height;
        m_stride        = stride;

        // Create a shm pool with format and size
        std::string shmPoolFile;
        const auto  FD = createPoolFile(SIZE, shmPoolFile);

        if (FD < 0) {
            Debug::log(ERR, "[sc] [shm] failed to create a pool file");
            return;
        }

        m_shmData = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0);
        if (m_shmData == MAP_FAILED) {
            Debug::log(ERR, "[sc] [shm] failed to (errno {})", strerror(errno));
            close(FD);
            m_ok = false;
            return;
        }

        if (!g_pHyprlock->getShm()) {
            Debug::log(ERR, "[sc] [shm] Failed to get WLShm global");
            close(FD);
            m_ok = false;
            return;
        }

        auto pShmPool = makeShared<CCWlShmPool>(g_pHyprlock->getShm()->sendCreatePool(FD, SIZE));
        m_wlBuffer    = makeShared<CCWlBuffer>(pShmPool->sendCreateBuffer(0, width, height, stride, m_shmFmt));

        pShmPool.reset();

        close(FD);
    });

    m_sc->setLinuxDmabuf([](CCZwlrScreencopyFrameV1* r, uint32_t, uint32_t, uint32_t) {
        ; // unused by scshm
    });
}

CSCSHMFrame::~CSCSHMFrame() {
    if (m_convBuffer)
        free(m_convBuffer);
    if (m_shmData)
        munmap(m_shmData, m_stride * m_h);
}

void CSCSHMFrame::convertBuffer() {
    const auto BYTESPERPX = m_stride / m_w;
    if (BYTESPERPX == 4) {
        switch (m_shmFmt) {
            case WL_SHM_FORMAT_ARGB8888:
            case WL_SHM_FORMAT_XRGB8888: {
                Debug::log(LOG, "[sc] [shm] Converting ARGB to RGBA");
                uint8_t* data = (uint8_t*)m_shmData;

                for (uint32_t y = 0; y < m_h; ++y) {
                    for (uint32_t x = 0; x < m_w; ++x) {
                        struct pixel {
                            // little-endian ARGB
                            unsigned char blue;
                            unsigned char green;
                            unsigned char red;
                            unsigned char alpha;
                        }* px = (struct pixel*)(data + (y * m_w * 4) + (x * 4));

                        // RGBA
                        *px = {.blue = px->red, .green = px->green, .red = px->blue, .alpha = px->alpha};
                    }
                }
            } break;
            case WL_SHM_FORMAT_ABGR8888:
            case WL_SHM_FORMAT_XBGR8888: {
                Debug::log(LOG, "[sc] [shm] Converting ABGR to RGBA");
                uint8_t* data = (uint8_t*)m_shmData;

                for (uint32_t y = 0; y < m_h; ++y) {
                    for (uint32_t x = 0; x < m_w; ++x) {
                        struct pixel {
                            // little-endian ARGB
                            unsigned char blue;
                            unsigned char green;
                            unsigned char red;
                            unsigned char alpha;
                        }* px = (struct pixel*)(data + (y * m_w * 4) + (x * 4));

                        // BGRA
                        *px = {.blue = px->blue, .green = px->green, .red = px->red, .alpha = px->alpha};
                    }
                }
            } break;
            case WL_SHM_FORMAT_ABGR2101010:
            case WL_SHM_FORMAT_ARGB2101010:
            case WL_SHM_FORMAT_XRGB2101010:
            case WL_SHM_FORMAT_XBGR2101010: {
                Debug::log(LOG, "[sc] [shm] Converting 10-bit channels to 8-bit");
                uint8_t*   data = (uint8_t*)m_shmData;

                const bool FLIP = m_shmFmt != WL_SHM_FORMAT_XBGR2101010;

                for (uint32_t y = 0; y < m_h; ++y) {
                    for (uint32_t x = 0; x < m_w; ++x) {
                        uint32_t* px = (uint32_t*)(data + (y * m_w * 4) + (x * 4));

                        // conv to 8 bit
                        uint8_t R = (uint8_t)std::round((255.0 * (((*px) & 0b00000000000000000000001111111111) >> 0) / 1023.0));
                        uint8_t G = (uint8_t)std::round((255.0 * (((*px) & 0b00000000000011111111110000000000) >> 10) / 1023.0));
                        uint8_t B = (uint8_t)std::round((255.0 * (((*px) & 0b00111111111100000000000000000000) >> 20) / 1023.0));
                        uint8_t A = (uint8_t)std::round((255.0 * (((*px) & 0b11000000000000000000000000000000) >> 30) / 3.0));

                        // write 8-bit values
                        *px = ((FLIP ? B : R) << 0) + (G << 8) + ((FLIP ? R : B) << 16) + (A << 24);
                    }
                }
            } break;
            default: {
                Debug::log(WARN, "[sc] [shm] Unsupported format {}", m_shmFmt);
            }
        }
    } else if (BYTESPERPX == 3) {
        Debug::log(LOG, "[sc] [shm] Converting 24 bit to 32 bit");
        m_convBuffer        = malloc(m_w * m_h * 4);
        const int NEWSTRIDE = m_w * 4;
        RASSERT(m_convBuffer, "malloc failed");

        switch (m_shmFmt) {
            case WL_SHM_FORMAT_BGR888: {
                Debug::log(LOG, "[sc] [shm] Converting BGR to RGBA");
                for (uint32_t y = 0; y < m_h; ++y) {
                    for (uint32_t x = 0; x < m_w; ++x) {
                        struct pixel3 {
                            // little-endian RGB
                            unsigned char blue;
                            unsigned char green;
                            unsigned char red;
                        }* srcPx = (struct pixel3*)((char*)m_shmData + (y * m_stride) + (x * 3));
                        struct pixel4 {
                            // little-endian ARGB
                            unsigned char blue;
                            unsigned char green;
                            unsigned char red;
                            unsigned char alpha;
                        }* dstPx = (struct pixel4*)((char*)m_convBuffer + (y * NEWSTRIDE) + (x * 4));
                        *dstPx   = {.blue = srcPx->blue, .green = srcPx->green, .red = srcPx->red, .alpha = 0xFF};
                    }
                }
            } break;
            case WL_SHM_FORMAT_RGB888: {
                Debug::log(LOG, "[sc] [shm] Converting RGB to RGBA");
                for (uint32_t y = 0; y < m_h; ++y) {
                    for (uint32_t x = 0; x < m_w; ++x) {
                        struct pixel3 {
                            // big-endian RGB
                            unsigned char red;
                            unsigned char green;
                            unsigned char blue;
                        }* srcPx = (struct pixel3*)((char*)m_shmData + (y * m_stride) + (x * 3));
                        struct pixel4 {
                            // big-endian ARGB
                            unsigned char alpha;
                            unsigned char red;
                            unsigned char green;
                            unsigned char blue;
                        }* dstPx = (struct pixel4*)((char*)m_convBuffer + (y * NEWSTRIDE) + (x * 4));
                        *dstPx   = {.alpha = srcPx->red, .red = srcPx->green, .green = srcPx->blue, .blue = 0xFF};
                    }
                }
            } break;
            default: {
                Debug::log(ERR, "[sc] [shm] Unsupported format for 24bit buffer {}", m_shmFmt);
            }
        }

    } else {
        Debug::log(ERR, "[sc] [shm] Unsupported bytes per pixel {}", BYTESPERPX);
    }
}

bool CSCSHMFrame::onBufferReady(SPreloadedAsset& asset) {
    convertBuffer();

    asset.texture.allocate();
    asset.texture.m_vSize.x = m_w;
    asset.texture.m_vSize.y = m_h;

    glBindTexture(GL_TEXTURE_2D, asset.texture.m_iTexID);

    void* buffer = m_convBuffer ? m_convBuffer : m_shmData;

    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_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_w, m_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    glBindTexture(GL_TEXTURE_2D, 0);

    Debug::log(LOG, "[sc] [shm] Got screenshot with size {}", asset.texture.m_vSize);

    asset.ready = true;

    return true;
}
07070100000048000081A4000000000000000000000001688B5FE9000009CE000000000000000000000000000000000000002B00000000hyprlock-0.9.1/src/renderer/Screencopy.hpp#pragma once

#include "../defines.hpp"
#include "../core/Output.hpp"
#include <cstdint>
#include <gbm.h>
#include <memory>
#include "Shared.hpp"
#include "linux-dmabuf-v1.hpp"
#include "wlr-screencopy-unstable-v1.hpp"

class ISCFrame {
  public:
    ISCFrame()          = default;
    virtual ~ISCFrame() = default;

    virtual bool   onBufferDone()                        = 0;
    virtual bool   onBufferReady(SPreloadedAsset& asset) = 0;

    SP<CCWlBuffer> m_wlBuffer = nullptr;
};

class CScreencopyFrame {
  public:
    static std::string                 getResourceId(SP<COutput> pOutput);
    static constexpr const std::string RESOURCEIDPREFIX = "screencopy";

    CScreencopyFrame(SP<COutput> pOutput);
    ~CScreencopyFrame() = default;

    void                        captureOutput();

    SP<CCZwlrScreencopyFrameV1> m_sc = nullptr;

    std::string                 m_resourceID;
    SPreloadedAsset             m_asset;

  private:
    WP<COutput>  m_outputRef;
    UP<ISCFrame> m_frame = nullptr;

    bool         m_dmaFailed = false;
};

// Uses a gpu buffer created via gbm_bo
class CSCDMAFrame : public ISCFrame {
  public:
    CSCDMAFrame(SP<CCZwlrScreencopyFrameV1> sc);
    virtual ~CSCDMAFrame();

    virtual bool onBufferReady(SPreloadedAsset& asset);
    virtual bool onBufferDone();

  private:
    gbm_bo*                     m_bo = nullptr;

    int                         m_planes = 0;
    uint64_t                    m_mod    = 0;

    int                         m_fd[4];
    uint32_t                    m_stride[4], m_offset[4];

    int                         m_w = 0, m_h = 0;
    uint32_t                    m_fmt = 0;

    SP<CCZwlrScreencopyFrameV1> m_sc = nullptr;

    EGLImage                    m_image = nullptr;
};

// Uses a shm buffer - is slow and needs ugly format conversion
// Used as a fallback just in case.
class CSCSHMFrame : public ISCFrame {
  public:
    CSCSHMFrame(SP<CCZwlrScreencopyFrameV1> sc);
    virtual ~CSCSHMFrame();

    virtual bool onBufferDone() {
        return m_ok;
    }
    virtual bool onBufferReady(SPreloadedAsset& asset);
    void         convertBuffer();

  private:
    bool                        m_ok = true;

    uint32_t                    m_w = 0, m_h = 0;
    uint32_t                    m_stride = 0;

    SP<CCZwlrScreencopyFrameV1> m_sc = nullptr;

    uint32_t                    m_shmFmt     = 0;
    void*                       m_shmData    = nullptr;
    void*                       m_convBuffer = nullptr;
};
07070100000049000081A4000000000000000000000001688B5FE9000001CB000000000000000000000000000000000000002700000000hyprlock-0.9.1/src/renderer/Shader.cpp#include "Shader.hpp"

GLint CShader::getUniformLocation(const std::string& unif) {
    const auto itpos = m_muUniforms.find(unif);

    if (itpos == m_muUniforms.end()) {
        const auto unifLoc = glGetUniformLocation(program, unif.c_str());
        m_muUniforms[unif] = unifLoc;
        return unifLoc;
    }

    return itpos->second;
}

CShader::~CShader() {
    destroy();
}

void CShader::destroy() {
    glDeleteProgram(program);

    program = 0;
}0707010000004A000081A4000000000000000000000001688B5FE900000785000000000000000000000000000000000000002700000000hyprlock-0.9.1/src/renderer/Shader.hpp#pragma once

#include <unordered_map>
#include <GLES3/gl32.h>
#include <string>

class CShader {
  public:
    ~CShader();

    GLuint  program           = 0;
    GLint   proj              = -1;
    GLint   color             = -1;
    GLint   alphaMatte        = -1;
    GLint   tex               = -1;
    GLint   tex2              = -1;
    GLint   alpha             = -1;
    GLfloat mixFactor         = -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;

    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   gradient2       = -1;
    GLint   gradient2Length = -1;
    GLint   gradientLerp    = -1;
    GLint   angle           = -1;
    GLint   angle2          = -1;

    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;

    // colorize
    GLint colorize     = -1;
    GLint colorizeTint = -1;
    GLint boostA       = -1;

    GLint getUniformLocation(const std::string&);

    void  destroy();

  private:
    std::unordered_map<std::string, GLint> m_muUniforms;
};0707010000004B000081A4000000000000000000000001688B5FE900004172000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/renderer/Shaders.hpp#pragma once

#include <string>
#include <format>
#include <cmath>

constexpr float              SHADER_ROUNDED_SMOOTHING_FACTOR = M_PI / 5.34665792551;

inline static constexpr auto ROUNDED_SHADER_FUNC = [](const std::string colorVarName) -> std::string {
    return R"#(

    // branchless baby!
    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 = length(pixCoord);

	    if (dist > radius + SMOOTHING_CONSTANT * 2.0)
	        discard;

	    if (dist > radius - SMOOTHING_CONSTANT * 2.0) {
	        float dist = length(pixCoord);

            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;

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 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 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 TEXMIXFRAGSRCRGBA = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float mixFactor;
uniform float alpha;

uniform vec2 topLeft;
uniform vec2 fullSize;
uniform float radius;

uniform int discardOpaque;
uniform int discardAlpha;
uniform float discardAlphaValue;

uniform int applyTint;
uniform vec3 tint;

void main() {

    vec4 pixColor = mix(texture2D(tex1, v_texcoord), texture2D(tex2, v_texcoord), smoothstep(0.0, 1.0, mixFactor));

    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 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;

uniform int       colorize;
uniform vec3      colorizeTint;
uniform float     boostA;

float hash(vec2 p) {
    return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}

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;
    }

    pixColor.a *= boostA;

    if (colorize == 1) {
        gl_FragColor = vec4(colorizeTint.r * pixColor.a, colorizeTint.g * pixColor.a, colorizeTint.b * pixColor.a, pixColor.a);
        return;
    }

    gl_FragColor = pixColor;
}
)#";

// makes a stencil without corners
inline const std::string FRAGBORDER = 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 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 = length(pixCoord);
	    float distOuter = length(pixCoordOuter);
        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;
}
)#";0707010000004C000081A4000000000000000000000001688B5FE90000008C000000000000000000000000000000000000002700000000hyprlock-0.9.1/src/renderer/Shared.hpp#pragma once
#include "Texture.hpp"
#include "../defines.hpp"

struct SPreloadedAsset {
    CTexture texture;
    bool     ready = false;
};0707010000004D000081A4000000000000000000000001688B5FE900000182000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/renderer/Texture.cpp#include "Texture.hpp"

CTexture::CTexture() {
    ; // naffin'
}

CTexture::~CTexture() {
    destroyTexture();
}

void CTexture::destroyTexture() {
    if (m_bAllocated) {
        glDeleteTextures(1, &m_iTexID);
        m_iTexID = 0;
    }
    m_bAllocated = false;
}

void CTexture::allocate() {
    if (!m_bAllocated)
        glGenTextures(1, &m_iTexID);
    m_bAllocated = true;
}
0707010000004E000081A4000000000000000000000001688B5FE900000221000000000000000000000000000000000000002800000000hyprlock-0.9.1/src/renderer/Texture.hpp#pragma once

#include <GLES3/gl32.h>
#include "../helpers/Math.hpp"

enum TEXTURETYPE {
    TEXTURE_INVALID,  // Invalid
    TEXTURE_RGBA,     // 4 channels
    TEXTURE_RGBX,     // discard A
    TEXTURE_EXTERNAL, // EGLImage
};

class CTexture {
  public:
    CTexture();
    ~CTexture();

    void        destroyTexture();
    void        allocate();

    TEXTURETYPE m_iType      = TEXTURE_RGBA;
    GLenum      m_iTarget    = GL_TEXTURE_2D;
    bool        m_bAllocated = false;
    GLuint      m_iTexID     = 0;
    Vector2D    m_vSize;
};0707010000004F000081A4000000000000000000000001688B5FE9000016E8000000000000000000000000000000000000002400000000hyprlock-0.9.1/src/renderer/mtx.hpp
#pragma once
#include <cstring>
#include <wayland-client.h>
#include "../helpers/Box.hpp"

static enum wl_output_transform wlr_output_transform_invert(enum wl_output_transform tr) {
    if ((tr & WL_OUTPUT_TRANSFORM_90) && !(tr & WL_OUTPUT_TRANSFORM_FLIPPED)) {
        tr = (wl_output_transform)((int)tr ^ (int)WL_OUTPUT_TRANSFORM_180);
    }
    return tr;
}

static void wlr_matrix_identity(float mat[9]) {
    const float identity[9] = {
        1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    };
    memcpy(mat, identity, sizeof(identity));
}

static void wlr_matrix_multiply(float mat[9], const float a[9], const float b[9]) {
    float product[9];

    product[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
    product[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
    product[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];

    product[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
    product[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
    product[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];

    product[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
    product[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
    product[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];

    memcpy(mat, product, sizeof(product));
}

static void wlr_matrix_transpose(float mat[9], const float a[9]) {
    float transposition[9] = {
        a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8],
    };
    memcpy(mat, transposition, sizeof(transposition));
}

static void wlr_matrix_translate(float mat[9], float x, float y) {
    float translate[9] = {
        1.0f, 0.0f, x, 0.0f, 1.0f, y, 0.0f, 0.0f, 1.0f,
    };
    wlr_matrix_multiply(mat, mat, translate);
}

static void wlr_matrix_scale(float mat[9], float x, float y) {
    float scale[9] = {
        x, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 1.0f,
    };
    wlr_matrix_multiply(mat, mat, scale);
}

static void wlr_matrix_rotate(float mat[9], float rad) {
    float rotate[9] = {
        cos(rad), -sin(rad), 0.0f, sin(rad), cos(rad), 0.0f, 0.0f, 0.0f, 1.0f,
    };
    wlr_matrix_multiply(mat, mat, rotate);
}

static const float transforms[][9] = {
    [WL_OUTPUT_TRANSFORM_NORMAL] =
        {
            1.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
        },
    [WL_OUTPUT_TRANSFORM_90] =
        {
            0.0f,
            1.0f,
            0.0f,
            -1.0f,
            0.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
        },
    [WL_OUTPUT_TRANSFORM_180] =
        {
            -1.0f,
            0.0f,
            0.0f,
            0.0f,
            -1.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
        },
    [WL_OUTPUT_TRANSFORM_270] =
        {
            0.0f,
            -1.0f,
            0.0f,
            1.0f,
            0.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
        },
    [WL_OUTPUT_TRANSFORM_FLIPPED] =
        {
            -1.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
        },
    [WL_OUTPUT_TRANSFORM_FLIPPED_90] =
        {
            0.0f,
            1.0f,
            0.0f,
            1.0f,
            0.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
        },
    [WL_OUTPUT_TRANSFORM_FLIPPED_180] =
        {
            1.0f,
            0.0f,
            0.0f,
            0.0f,
            -1.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
        },
    [WL_OUTPUT_TRANSFORM_FLIPPED_270] =
        {
            0.0f,
            -1.0f,
            0.0f,
            -1.0f,
            0.0f,
            0.0f,
            0.0f,
            0.0f,
            1.0f,
        },
};

static void wlr_matrix_transform(float mat[9], enum wl_output_transform transform) {
    wlr_matrix_multiply(mat, mat, transforms[transform]);
}

static void matrix_projection(float mat[9], int width, int height, enum wl_output_transform transform) {
    std::memset(mat, 0, sizeof(*mat) * 9);

    const float* t = transforms[transform];
    float        x = 2.0f / width;
    float        y = 2.0f / height;

    // Rotation + reflection
    mat[0] = x * t[0];
    mat[1] = x * t[1];
    mat[3] = y * -t[3];
    mat[4] = y * -t[4];

    // Translation
    mat[2] = -copysign(1.0f, mat[0] + mat[1]);
    mat[5] = -copysign(1.0f, mat[3] + mat[4]);

    // Identity
    mat[8] = 1.0f;
}

static void wlr_matrix_project_box(float mat[9], const CBox* box, enum wl_output_transform transform, float rotation, const float projection[9]) {
    int x      = box->x;
    int y      = box->y;
    int width  = box->width;
    int height = box->height;

    wlr_matrix_identity(mat);
    wlr_matrix_translate(mat, x, y);

    if (rotation != 0) {
        wlr_matrix_translate(mat, width / 2, height / 2);
        wlr_matrix_rotate(mat, rotation);
        wlr_matrix_translate(mat, -width / 2, -height / 2);
    }

    wlr_matrix_scale(mat, width, height);

    if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
        wlr_matrix_translate(mat, 0.5, 0.5);
        wlr_matrix_transform(mat, transform);
        wlr_matrix_translate(mat, -0.5, -0.5);
    }

    wlr_matrix_multiply(mat, projection, mat);
}

static void matrixProjection(float mat[9], int w, int h, wl_output_transform tr) {
    memset(mat, 0, sizeof(*mat) * 9);

    const float* t = transforms[tr];
    float        x = 2.0f / w;
    float        y = 2.0f / h;

    // Rotation + reflection
    mat[0] = x * t[0];
    mat[1] = x * t[1];
    mat[3] = y * t[3];
    mat[4] = y * t[4];

    // Translation
    mat[2] = -copysign(1.0f, mat[0] + mat[1]);
    mat[5] = -copysign(1.0f, mat[3] + mat[4]);

    // Identity
    mat[8] = 1.0f;
}
07070100000050000041ED000000000000000000000002688B5FE900000000000000000000000000000000000000000000002400000000hyprlock-0.9.1/src/renderer/widgets07070100000051000081A4000000000000000000000001688B5FE90000308E000000000000000000000000000000000000003300000000hyprlock-0.9.1/src/renderer/widgets/Background.cpp#include "Background.hpp"
#include "../Renderer.hpp"
#include "../Framebuffer.hpp"
#include "../Shared.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/Log.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../core/AnimationManager.hpp"
#include "../../config/ConfigManager.hpp"
#include <chrono>
#include <hyprlang.hpp>
#include <filesystem>
#include <memory>
#include <GLES3/gl32.h>

CBackground::CBackground() {
    blurredFB        = makeUnique<CFramebuffer>();
    pendingBlurredFB = makeUnique<CFramebuffer>();
    transformedScFB  = makeUnique<CFramebuffer>();
}

CBackground::~CBackground() {
    reset();
}

void CBackground::registerSelf(const ASP<CBackground>& self) {
    m_self = self;
}

void CBackground::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
    reset();

    try {
        color             = std::any_cast<Hyprlang::INT>(props.at("color"));
        blurPasses        = std::any_cast<Hyprlang::INT>(props.at("blur_passes"));
        blurSize          = std::any_cast<Hyprlang::INT>(props.at("blur_size"));
        vibrancy          = std::any_cast<Hyprlang::FLOAT>(props.at("vibrancy"));
        vibrancy_darkness = std::any_cast<Hyprlang::FLOAT>(props.at("vibrancy_darkness"));
        noise             = std::any_cast<Hyprlang::FLOAT>(props.at("noise"));
        brightness        = std::any_cast<Hyprlang::FLOAT>(props.at("brightness"));
        contrast          = std::any_cast<Hyprlang::FLOAT>(props.at("contrast"));
        path              = std::any_cast<Hyprlang::STRING>(props.at("path"));
        reloadCommand     = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
        reloadTime        = std::any_cast<Hyprlang::INT>(props.at("reload_time"));

    } catch (const std::bad_any_cast& e) {
        RASSERT(false, "Failed to construct CBackground: {}", e.what()); //
    } catch (const std::out_of_range& e) {
        RASSERT(false, "Missing propperty for CBackground: {}", e.what()); //
    }

    isScreenshot = path == "screenshot";

    viewport     = pOutput->getViewport();
    outputPort   = pOutput->stringPort;
    transform    = wlTransformToHyprutils(invertTransform(pOutput->transform));
    scResourceID = CScreencopyFrame::getResourceId(pOutput);

    g_pAnimationManager->createAnimation(0.f, crossFadeProgress, g_pConfigManager->m_AnimationTree.getConfig("fadeIn"));

    // When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available.
    // Dynamic ones are tricky, because a screencopy would copy hyprlock itself.
    if (g_pRenderer->asyncResourceGatherer->gathered && !g_pRenderer->asyncResourceGatherer->getAssetByID(scResourceID)) {
        Debug::log(LOG, "Missing screenshot for output {}", outputPort);
        scResourceID = "";
    }

    if (isScreenshot) {
        resourceID = scResourceID; // Fallback to solid background:color when scResourceID==""

        if (!g_pHyprlock->getScreencopy()) {
            Debug::log(ERR, "No screencopy support! path=screenshot won't work. Falling back to background color.");
            resourceID = "";
        }
    } else if (!path.empty())
        resourceID = "background:" + path;

    if (!isScreenshot && reloadTime > -1) {
        try {
            modificationTime = std::filesystem::last_write_time(absolutePath(path, ""));
        } catch (std::exception& e) { Debug::log(ERR, "{}", e.what()); }

        plantReloadTimer(); // No reloads for screenshots.
    }
}

void CBackground::reset() {
    if (reloadTimer) {
        reloadTimer->cancel();
        reloadTimer.reset();
    }

    blurredFB->destroyBuffer();
    pendingBlurredFB->destroyBuffer();
}

void CBackground::updatePrimaryAsset() {
    if (asset || resourceID.empty())
        return;

    asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
    if (!asset)
        return;

    const bool NEEDFB = (isScreenshot || blurPasses > 0 || asset->texture.m_vSize != viewport) && (!blurredFB->isAllocated() || firstRender);
    if (NEEDFB)
        renderToFB(asset->texture, *blurredFB, blurPasses);
}

void CBackground::updatePendingAsset() {
    // For crossfading a new asset
    if (!pendingAsset || blurPasses == 0 || pendingBlurredFB->isAllocated())
        return;

    renderToFB(pendingAsset->texture, *pendingBlurredFB, blurPasses);
}

void CBackground::updateScAsset() {
    if (scAsset || scResourceID.empty())
        return;

    // path=screenshot -> scAsset = asset
    scAsset = (asset && isScreenshot) ? asset : g_pRenderer->asyncResourceGatherer->getAssetByID(scResourceID);
    if (!scAsset)
        return;

    const bool NEEDSCTRANSFORM = transform != HYPRUTILS_TRANSFORM_NORMAL;
    if (NEEDSCTRANSFORM)
        renderToFB(scAsset->texture, *transformedScFB, 0, true);
}

const CTexture& CBackground::getPrimaryAssetTex() const {
    // This case is only for background:path=screenshot with blurPasses=0
    if (isScreenshot && blurPasses == 0 && transformedScFB->isAllocated())
        return transformedScFB->m_cTex;

    return (blurredFB->isAllocated()) ? blurredFB->m_cTex : asset->texture;
}

const CTexture& CBackground::getPendingAssetTex() const {
    return (pendingBlurredFB->isAllocated()) ? pendingBlurredFB->m_cTex : pendingAsset->texture;
}

const CTexture& CBackground::getScAssetTex() const {
    return (transformedScFB->isAllocated()) ? transformedScFB->m_cTex : scAsset->texture;
}

void CBackground::renderRect(CHyprColor color) {
    CBox monbox = {0, 0, viewport.x, viewport.y};
    g_pRenderer->renderRect(monbox, color, 0);
}

static void onReloadTimer(AWP<CBackground> ref) {
    if (auto PBG = ref.lock(); PBG) {
        PBG->onReloadTimerUpdate();
        PBG->plantReloadTimer();
    }
}

static void onAssetCallback(AWP<CBackground> ref) {
    if (auto PBG = ref.lock(); PBG)
        PBG->startCrossFade();
}

static CBox getScaledBoxForTextureSize(const Vector2D& size, const Vector2D& viewport) {
    CBox  texbox = {{}, size};

    float scaleX = viewport.x / size.x;
    float scaleY = viewport.y / size.y;

    texbox.w *= std::max(scaleX, scaleY);
    texbox.h *= std::max(scaleX, scaleY);

    if (scaleX > scaleY)
        texbox.y = -(texbox.h - viewport.y) / 2.f;
    else
        texbox.x = -(texbox.w - viewport.x) / 2.f;
    texbox.round();

    return texbox;
}

void CBackground::renderToFB(const CTexture& tex, CFramebuffer& fb, int passes, bool applyTransform) {
    if (firstRender)
        firstRender = false;

    // make it brah
    Vector2D size = tex.m_vSize;
    if (applyTransform && transform % 2 == 1) {
        size.x = tex.m_vSize.y;
        size.y = tex.m_vSize.x;
    }

    const auto TEXBOX = getScaledBoxForTextureSize(size, viewport);

    if (!fb.isAllocated())
        fb.alloc(viewport.x, viewport.y); // TODO 10 bit

    fb.bind();

    g_pRenderer->renderTexture(TEXBOX, tex, 1.0, 0, applyTransform ? transform : HYPRUTILS_TRANSFORM_NORMAL);

    if (blurPasses > 0)
        g_pRenderer->blurFB(fb,
                            CRenderer::SBlurParams{
                                .size              = blurSize,
                                .passes            = passes,
                                .noise             = noise,
                                .contrast          = contrast,
                                .brightness        = brightness,
                                .vibrancy          = vibrancy,
                                .vibrancy_darkness = vibrancy_darkness,
                            });
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}

bool CBackground::draw(const SRenderData& data) {
    updatePrimaryAsset();
    updatePendingAsset();
    updateScAsset();

    if (asset && asset->texture.m_iType == TEXTURE_INVALID) {
        g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
        resourceID = "";
        renderRect(color);
        return false;
    }

    if (!asset || resourceID.empty()) {
        // fade in/out with a solid color
        if (data.opacity < 1.0 && scAsset) {
            const auto& SCTEX    = getScAssetTex();
            const auto  SCTEXBOX = getScaledBoxForTextureSize(SCTEX.m_vSize, viewport);
            g_pRenderer->renderTexture(SCTEXBOX, SCTEX, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
            CHyprColor col = color;
            col.a *= data.opacity;
            renderRect(col);
            return true;
        }

        renderRect(color);
        return !asset && !resourceID.empty(); // resource not ready
    }

    const auto& TEX    = getPrimaryAssetTex();
    const auto  TEXBOX = getScaledBoxForTextureSize(TEX.m_vSize, viewport);
    if (data.opacity < 1.0 && scAsset) {
        const auto& SCTEX = getScAssetTex();
        g_pRenderer->renderTextureMix(TEXBOX, SCTEX, TEX, 1.0, data.opacity, 0);
    } else if (crossFadeProgress->isBeingAnimated()) {
        const auto& PENDINGTEX = getPendingAssetTex();
        g_pRenderer->renderTextureMix(TEXBOX, TEX, PENDINGTEX, 1.0, crossFadeProgress->value(), 0);
    } else
        g_pRenderer->renderTexture(TEXBOX, TEX, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);

    return crossFadeProgress->isBeingAnimated() || data.opacity < 1.0;
}

void CBackground::plantReloadTimer() {

    if (reloadTime == 0)
        reloadTimer = g_pHyprlock->addTimer(std::chrono::hours(1), [REF = m_self](auto, auto) { onReloadTimer(REF); }, nullptr, true);
    else if (reloadTime > 0)
        reloadTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), [REF = m_self](auto, auto) { onReloadTimer(REF); }, nullptr, true);
}

void CBackground::onReloadTimerUpdate() {
    const std::string OLDPATH = path;

    // Path parsing and early returns

    if (!reloadCommand.empty()) {
        path = spawnSync(reloadCommand);

        if (path.ends_with('\n'))
            path.pop_back();

        if (path.starts_with("file://"))
            path = path.substr(7);

        if (path.empty())
            return;
    }

    try {
        const auto MTIME = std::filesystem::last_write_time(absolutePath(path, ""));
        if (OLDPATH == path && MTIME == modificationTime)
            return;

        modificationTime = MTIME;
    } catch (std::exception& e) {
        path = OLDPATH;
        Debug::log(ERR, "{}", e.what());
        return;
    }

    if (!pendingResourceID.empty())
        return;

    // Issue the next request

    request.id        = std::string{"background:"} + path + ",time:" + std::to_string((uint64_t)modificationTime.time_since_epoch().count());
    pendingResourceID = request.id;
    request.asset     = path;
    request.type      = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE;

    request.callback = [REF = m_self]() { onAssetCallback(REF); };

    g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}

void CBackground::startCrossFade() {
    auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
    if (newAsset) {
        if (newAsset->texture.m_iType == TEXTURE_INVALID) {
            g_pRenderer->asyncResourceGatherer->unloadAsset(newAsset);
            Debug::log(ERR, "New asset had an invalid texture!");
            pendingResourceID = "";
        } else if (resourceID != pendingResourceID) {
            pendingAsset = newAsset;
            crossFadeProgress->setValueAndWarp(0);
            *crossFadeProgress = 1.0;

            crossFadeProgress->setCallbackOnEnd(
                [REF = m_self](auto) {
                    if (const auto PSELF = REF.lock()) {
                        PSELF->asset        = PSELF->pendingAsset;
                        PSELF->pendingAsset = nullptr;
                        g_pRenderer->asyncResourceGatherer->unloadAsset(PSELF->pendingAsset);
                        PSELF->resourceID        = PSELF->pendingResourceID;
                        PSELF->pendingResourceID = "";

                        PSELF->blurredFB->destroyBuffer();
                        PSELF->blurredFB = std::move(PSELF->pendingBlurredFB);
                    }
                },
                true);

            g_pHyprlock->renderOutput(outputPort);
        }
    } else if (!pendingResourceID.empty()) {
        Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
        g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
    }
}
07070100000052000081A4000000000000000000000001688B5FE900000CB9000000000000000000000000000000000000003300000000hyprlock-0.9.1/src/renderer/widgets/Background.hpp#pragma once

#include "IWidget.hpp"
#include "../../helpers/AnimatedVariable.hpp"
#include "../../helpers/Color.hpp"
#include "../../helpers/Math.hpp"
#include "../../core/Timer.hpp"
#include "../Framebuffer.hpp"
#include "../AsyncResourceGatherer.hpp"
#include <cstdint>
#include <hyprutils/math/Misc.hpp>
#include <string>
#include <unordered_map>
#include <any>
#include <chrono>
#include <filesystem>

struct SPreloadedAsset;
class COutput;

class CBackground : public IWidget {
  public:
    CBackground();
    ~CBackground();

    void            registerSelf(const ASP<CBackground>& self);

    virtual void    configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput);
    virtual bool    draw(const SRenderData& data);

    void            reset(); // Unload assets, remove timers, etc.

    void            updatePrimaryAsset();
    void            updatePendingAsset();
    void            updateScAsset();

    const CTexture& getPrimaryAssetTex() const;
    const CTexture& getPendingAssetTex() const;
    const CTexture& getScAssetTex() const;

    void            renderRect(CHyprColor color);
    void            renderToFB(const CTexture& text, CFramebuffer& fb, int passes, bool applyTransform = false);

    void            onReloadTimerUpdate();
    void            plantReloadTimer();
    void            startCrossFade();

  private:
    AWP<CBackground> m_self;

    // if needed
    UP<CFramebuffer>                        blurredFB;
    UP<CFramebuffer>                        pendingBlurredFB;
    UP<CFramebuffer>                        transformedScFB;

    int                                     blurSize          = 10;
    int                                     blurPasses        = 3;
    float                                   noise             = 0.0117;
    float                                   contrast          = 0.8916;
    float                                   brightness        = 0.8172;
    float                                   vibrancy          = 0.1696;
    float                                   vibrancy_darkness = 0.0;
    Vector2D                                viewport;
    std::string                             path = "";

    std::string                             outputPort;
    Hyprutils::Math::eTransform             transform;

    std::string                             resourceID;
    std::string                             scResourceID;
    std::string                             pendingResourceID;

    PHLANIMVAR<float>                       crossFadeProgress;

    CHyprColor                              color;
    SPreloadedAsset*                        asset        = nullptr;
    SPreloadedAsset*                        scAsset      = nullptr;
    SPreloadedAsset*                        pendingAsset = nullptr;
    bool                                    isScreenshot = false;
    bool                                    firstRender  = true;

    int                                     reloadTime = -1;
    std::string                             reloadCommand;
    CAsyncResourceGatherer::SPreloadRequest request;
    ASP<CTimer>                             reloadTimer;
    std::filesystem::file_time_type         modificationTime;
};
07070100000053000081A4000000000000000000000001688B5FE9000025F6000000000000000000000000000000000000003000000000hyprlock-0.9.1/src/renderer/widgets/IWidget.cpp#include "IWidget.hpp"
#include "../../helpers/Log.hpp"
#include "../../core/hyprlock.hpp"
#include "../../auth/Auth.hpp"
#include <chrono>
#include <unistd.h>
#include <pwd.h>
#include <hyprutils/string/String.hpp>
#include <hyprutils/string/VarList.hpp>

using namespace Hyprutils::String;

#if defined(_LIBCPP_VERSION)
#pragma comment(lib, "date-tz")
#include <date/tz.h>
namespace std {
    namespace chrono {
        using date::current_zone;
        using date::locate_zone;
        using date::time_zone;
    }
}
#endif

static Vector2D rotateVector(const Vector2D& vec, const double& ang) {
    const double COS = std::abs(std::cos(ang));
    const double SIN = std::abs(std::sin(ang));
    return Vector2D((vec.x * COS) + (vec.y * SIN), (vec.x * SIN) + (vec.y * COS));
}

Vector2D IWidget::posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign, const double& ang) {

    // offset after rotation for alignment
    Vector2D rot;
    if (ang != 0)
        rot = (size - rotateVector(size, ang)) / 2.0;

    Vector2D pos = offset;
    if (halign == "center")
        pos.x += viewport.x / 2.0 - size.x / 2.0;
    else if (halign == "left")
        pos.x += 0 - rot.x;
    else if (halign == "right")
        pos.x += viewport.x - size.x + rot.x;
    else if (halign != "none")
        Debug::log(ERR, "IWidget: invalid halign {}", halign);

    if (valign == "center")
        pos.y += viewport.y / 2.0 - size.y / 2.0;
    else if (valign == "top")
        pos.y += viewport.y - size.y + rot.y;
    else if (valign == "bottom")
        pos.y += 0 - rot.y;
    else if (valign != "none")
        Debug::log(ERR, "IWidget: invalid valign {}", valign);

    return pos;
}

int IWidget::roundingForBox(const CBox& box, int roundingConfig) {
    const int MINHALFBOX = std::min(box.w, box.h) / 2.0;
    if (roundingConfig == -1)
        return MINHALFBOX;

    return std::clamp(roundingConfig, 0, MINHALFBOX);
}

int IWidget::roundingForBorderBox(const CBox& borderBox, int roundingConfig, int thickness) {
    const int MINHALFBORDER = std::min(borderBox.w, borderBox.h) / 2.0;
    if (roundingConfig == -1)
        return MINHALFBORDER;

    else if (roundingConfig == 0)
        return 0;

    return std::clamp(roundingConfig + thickness, 0, MINHALFBORDER);
}

static void replaceAllAttempts(std::string& str) {

    const size_t      ATTEMPTS = g_pAuth->getFailedAttempts();
    const std::string STR      = std::to_string(ATTEMPTS);
    size_t            pos      = 0;

    while ((pos = str.find("$ATTEMPTS", pos)) != std::string::npos) {
        if (str.substr(pos, 10).ends_with('[') && str.substr(pos).contains(']')) {
            const std::string REPL = str.substr(pos + 10, str.find_first_of(']', pos) - 10 - pos);
            if (ATTEMPTS == 0) {
                str.replace(pos, 11 + REPL.length(), REPL);
                pos += REPL.length();
            } else {
                str.replace(pos, 11 + REPL.length(), STR);
                pos += STR.length();
            }
        } else {
            str.replace(pos, 9, STR);
            pos += STR.length();
        }
    }
}

static void replaceAllLayout(std::string& str) {
    std::string layoutName = "error";
    const auto  LAYOUTIDX  = g_pHyprlock->m_uiActiveLayout;

    if (g_pSeatManager->m_pXKBKeymap) {
        const auto PNAME = xkb_keymap_layout_get_name(g_pSeatManager->m_pXKBKeymap, LAYOUTIDX);
        if (PNAME)
            layoutName = PNAME;
    }

    size_t pos = 0;
    while ((pos = str.find("$LAYOUT", pos)) != std::string::npos) {
        if (str.substr(pos, 8).ends_with('[') && str.substr(pos).contains(']')) {
            const std::string REPL = str.substr(pos + 8, str.find_first_of(']', pos) - 8 - pos);
            const CVarList    LANGS(REPL);
            if (LAYOUTIDX >= LANGS.size()) {
                Debug::log(ERR, "Layout index {} out of bounds. Max is {}.", LAYOUTIDX, LANGS.size() - 1);
                return;
            }

            const std::string LANG = LANGS[LAYOUTIDX].empty() ? layoutName : LANGS[LAYOUTIDX] == "!" ? "" : LANGS[LAYOUTIDX];
            str.replace(pos, 9 + REPL.length(), LANG);
            pos += LANG.length();
        } else {
            str.replace(pos, 7, layoutName);
            pos += layoutName.length();
        }
    }
}

static bool                                                       logMissingTzOnce = true;
static std::chrono::hh_mm_ss<std::chrono::system_clock::duration> getTime() {
    const std::chrono::time_zone* pCurrentTz = nullptr;
    try {
        auto name = std::getenv("TZ");
        if (name)
            pCurrentTz = std::chrono::locate_zone(name);
    } catch (std::runtime_error&) { Debug::log(WARN, "Invalid TZ value. Falling back to current timezone!"); }

    if (!pCurrentTz)
        pCurrentTz = std::chrono::current_zone();

    const auto TPNOW = std::chrono::system_clock::now();

    //
    std::chrono::hh_mm_ss<std::chrono::system_clock::duration> hhmmss;
    if (!pCurrentTz) {
        if (logMissingTzOnce) {
            Debug::log(WARN, "Current timezone unknown. Falling back to UTC!");
            logMissingTzOnce = false;
        }
        hhmmss = std::chrono::hh_mm_ss{TPNOW - std::chrono::floor<std::chrono::days>(TPNOW)};
    } else
        hhmmss = std::chrono::hh_mm_ss{pCurrentTz->to_local(TPNOW) - std::chrono::floor<std::chrono::days>(pCurrentTz->to_local(TPNOW))};

    return hhmmss;
}

static std::string getTime24h() {
    const auto HHMMSS = getTime();
    const auto HRS    = HHMMSS.hours().count();
    const auto MINS   = HHMMSS.minutes().count();
    return (HRS < 10 ? "0" : "") + std::to_string(HRS) + ":" + (MINS < 10 ? "0" : "") + std::to_string(MINS);
}

static std::string getTime12h() {
    const auto HHMMSS = getTime();
    const auto HRS    = HHMMSS.hours().count();
    const auto MINS   = HHMMSS.minutes().count();
    return (HRS == 12 || HRS == 0 ? "12" : (HRS % 12 < 10 ? "0" : "") + std::to_string(HRS % 12)) + ":" + (MINS < 10 ? "0" : "") + std::to_string(MINS) +
        (HRS < 12 ? " AM" : " PM");
}

IWidget::SFormatResult IWidget::formatString(std::string in) {

    auto  uidPassword = getpwuid(getuid());
    char* username    = uidPassword->pw_name;
    char* user_gecos  = uidPassword->pw_gecos;

    if (!username)
        Debug::log(ERR, "Error in formatString, username null. Errno: ", errno);

    if (!user_gecos)
        Debug::log(WARN, "Error in formatString, user_gecos null. Errno: ", errno);

    IWidget::SFormatResult result;
    replaceInString(in, "$DESC", std::string{user_gecos ? user_gecos : ""});
    replaceInString(in, "$USER", std::string{username ? username : ""});
    replaceInString(in, "<br/>", std::string{"\n"});

    if (in.contains("$TIME12")) {
        replaceInString(in, "$TIME12", getTime12h());
        result.updateEveryMs = result.updateEveryMs != 0 && result.updateEveryMs < 1000 ? result.updateEveryMs : 1000;
    }

    if (in.contains("$TIME")) {
        replaceInString(in, "$TIME", getTime24h());
        result.updateEveryMs = result.updateEveryMs != 0 && result.updateEveryMs < 1000 ? result.updateEveryMs : 1000;
    }

    if (in.contains("$ATTEMPTS")) {
        replaceAllAttempts(in);
        result.allowForceUpdate = true;
    }

    if (in.contains("$LAYOUT")) {
        replaceAllLayout(in);
        result.allowForceUpdate = true;
    }

    if (in.contains("$FAIL")) {
        const auto FAIL = g_pAuth->getCurrentFailText();
        replaceInString(in, "$FAIL", FAIL);
        result.allowForceUpdate = true;
    }

    if (in.contains("$PAMFAIL")) {
        const auto FAIL = g_pAuth->getFailText(AUTH_IMPL_PAM);
        replaceInString(in, "$PAMFAIL", FAIL.value_or(""));
        result.allowForceUpdate = true;
    }

    if (in.contains("$PAMPROMPT")) {
        const auto PROMPT = g_pAuth->getPrompt(AUTH_IMPL_PAM);
        replaceInString(in, "$PAMPROMPT", PROMPT.value_or(""));
        result.allowForceUpdate = true;
    }

    if (in.contains("$FPRINTFAIL")) {
        const auto FPRINTFAIL = g_pAuth->getFailText(AUTH_IMPL_FINGERPRINT);
        replaceInString(in, "$FPRINTFAIL", FPRINTFAIL.value_or(""));
        result.allowForceUpdate = true;
    }

    if (in.contains("$FPRINTPROMPT")) {
        const auto FPRINTPROMPT = g_pAuth->getPrompt(AUTH_IMPL_FINGERPRINT);
        replaceInString(in, "$FPRINTPROMPT", FPRINTPROMPT.value_or(""));
        result.allowForceUpdate = true;
    }

    if (in.starts_with("cmd[") && in.contains("]")) {
        // this is a command
        CVarList vars(in.substr(4, in.find_first_of(']') - 4), 0, ',', true);

        for (const auto& v : vars) {
            if (v.starts_with("update:")) {
                try {
                    if (v.substr(7).contains(':')) {
                        auto str                = v.substr(v.substr(7).find_first_of(':') + 8);
                        result.allowForceUpdate = str == "true" || std::stoull(str) == 1;
                    }

                    result.updateEveryMs = std::stoull(v.substr(7));
                } catch (std::exception& e) { Debug::log(ERR, "Error parsing {} in cmd[]", v); }
            } else {
                Debug::log(ERR, "Unknown prop in string format {}", v);
            }
        }

        result.alwaysUpdate = true;
        in                  = in.substr(in.find_first_of(']') + 1);
        result.cmd          = true;
    }

    result.formatted = in;
    return result;
}

void IWidget::setHover(bool hover) {
    hovered = hover;
}

bool IWidget::isHovered() const {
    return hovered;
}

bool IWidget::containsPoint(const Vector2D& pos) const {
    return getBoundingBoxWl().containsPoint(pos);
}
07070100000054000081A4000000000000000000000001688B5FE90000065B000000000000000000000000000000000000003000000000hyprlock-0.9.1/src/renderer/widgets/IWidget.hpp#pragma once

#include "../../defines.hpp"
#include "../../helpers/Math.hpp"
#include <string>
#include <unordered_map>
#include <any>

class COutput;

class IWidget {
  public:
    struct SRenderData {
        float opacity = 1;
    };

    virtual ~IWidget() = default;

    virtual void    configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput) = 0;
    virtual bool    draw(const SRenderData& data)                                                                = 0;

    static Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign,
                                   const double& ang = 0);
    static int      roundingForBox(const CBox& box, int roundingConfig);
    static int      roundingForBorderBox(const CBox& borderBox, int roundingConfig, int thickness);

    virtual CBox    getBoundingBoxWl() const {
        return CBox();
    };
    virtual void onClick(uint32_t button, bool down, const Vector2D& pos) {}
    virtual void onHover(const Vector2D& pos) {}
    bool         containsPoint(const Vector2D& pos) const;

    struct SFormatResult {
        std::string formatted;
        float       updateEveryMs    = 0; // 0 means don't (static)
        bool        alwaysUpdate     = false;
        bool        cmd              = false;
        bool        allowForceUpdate = false;
    };

    static SFormatResult formatString(std::string in);

    void                 setHover(bool hover);
    bool                 isHovered() const;

  private:
    bool hovered = false;
};
07070100000055000081A4000000000000000000000001688B5FE9000020AC000000000000000000000000000000000000002E00000000hyprlock-0.9.1/src/renderer/widgets/Image.cpp#include "Image.hpp"
#include "../Renderer.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/Log.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../config/ConfigDataValues.hpp"
#include <cmath>
#include <hyprlang.hpp>
#include <hyprutils/math/Vector2D.hpp>

CImage::~CImage() {
    reset();
}

void CImage::registerSelf(const ASP<CImage>& self) {
    m_self = self;
}

static void onTimer(AWP<CImage> ref) {
    if (auto PIMAGE = ref.lock(); PIMAGE) {
        PIMAGE->onTimerUpdate();
        PIMAGE->plantTimer();
    }
}

static void onAssetCallback(AWP<CImage> ref) {
    if (auto PIMAGE = ref.lock(); PIMAGE)
        PIMAGE->renderUpdate();
}

void CImage::onTimerUpdate() {
    const std::string OLDPATH = path;

    if (!reloadCommand.empty()) {
        path = spawnSync(reloadCommand);

        if (path.ends_with('\n'))
            path.pop_back();

        if (path.starts_with("file://"))
            path = path.substr(7);

        if (path.empty())
            return;
    }

    try {
        const auto MTIME = std::filesystem::last_write_time(absolutePath(path, ""));
        if (OLDPATH == path && MTIME == modificationTime)
            return;

        modificationTime = MTIME;
    } catch (std::exception& e) {
        path = OLDPATH;
        Debug::log(ERR, "{}", e.what());
        return;
    }

    if (!pendingResourceID.empty())
        return;

    request.id        = std::string{"image:"} + path + ",time:" + std::to_string((uint64_t)modificationTime.time_since_epoch().count());
    pendingResourceID = request.id;
    request.asset     = path;
    request.type      = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE;
    request.callback  = [REF = m_self]() { onAssetCallback(REF); };

    g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}

void CImage::plantTimer() {

    if (reloadTime == 0) {
        imageTimer = g_pHyprlock->addTimer(std::chrono::hours(1), [REF = m_self](auto, auto) { onTimer(REF); }, nullptr, true);
    } else if (reloadTime > 0)
        imageTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), [REF = m_self](auto, auto) { onTimer(REF); }, nullptr, false);
}

void CImage::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
    reset();

    viewport   = pOutput->getViewport();
    stringPort = pOutput->stringPort;

    shadow.configure(m_self, props, viewport);

    try {
        size      = std::any_cast<Hyprlang::INT>(props.at("size"));
        rounding  = std::any_cast<Hyprlang::INT>(props.at("rounding"));
        border    = std::any_cast<Hyprlang::INT>(props.at("border_size"));
        color     = *CGradientValueData::fromAnyPv(props.at("border_color"));
        configPos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
        halign    = std::any_cast<Hyprlang::STRING>(props.at("halign"));
        valign    = std::any_cast<Hyprlang::STRING>(props.at("valign"));
        angle     = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));

        path           = std::any_cast<Hyprlang::STRING>(props.at("path"));
        reloadTime     = std::any_cast<Hyprlang::INT>(props.at("reload_time"));
        reloadCommand  = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
        onclickCommand = std::any_cast<Hyprlang::STRING>(props.at("onclick"));
    } catch (const std::bad_any_cast& e) {
        RASSERT(false, "Failed to construct CImage: {}", e.what()); //
    } catch (const std::out_of_range& e) {
        RASSERT(false, "Missing propperty for CImage: {}", e.what()); //
    }

    resourceID = "image:" + path;
    angle      = angle * M_PI / 180.0;

    if (reloadTime > -1) {
        try {
            modificationTime = std::filesystem::last_write_time(absolutePath(path, ""));
        } catch (std::exception& e) { Debug::log(ERR, "{}", e.what()); }

        plantTimer();
    }
}

void CImage::reset() {
    if (imageTimer) {
        imageTimer->cancel();
        imageTimer.reset();
    }

    if (g_pHyprlock->m_bTerminate)
        return;

    imageFB.destroyBuffer();

    if (asset && reloadTime > -1) // Don't unload asset if it's a static image
        g_pRenderer->asyncResourceGatherer->unloadAsset(asset);

    asset             = nullptr;
    pendingResourceID = "";
    resourceID        = "";
}

bool CImage::draw(const SRenderData& data) {

    if (resourceID.empty())
        return false;

    if (!asset)
        asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);

    if (!asset)
        return true;

    if (asset->texture.m_iType == TEXTURE_INVALID) {
        g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
        resourceID = "";
        return false;
    }

    if (!imageFB.isAllocated()) {

        const Vector2D IMAGEPOS  = {border, border};
        const Vector2D BORDERPOS = {0.0, 0.0};
        const Vector2D TEXSIZE   = asset->texture.m_vSize;
        const float    SCALEX    = size / TEXSIZE.x;
        const float    SCALEY    = size / TEXSIZE.y;

        // image with borders offset, with extra pixel for anti-aliasing when rotated
        CBox texbox = {angle == 0 ? IMAGEPOS : IMAGEPOS + Vector2D{1.0, 1.0}, TEXSIZE};

        texbox.w *= std::max(SCALEX, SCALEY);
        texbox.h *= std::max(SCALEX, SCALEY);

        // plus borders if any
        CBox borderBox = {angle == 0 ? BORDERPOS : BORDERPOS + Vector2D{1.0, 1.0}, texbox.size() + IMAGEPOS * 2.0};

        borderBox.round();

        const Vector2D FBSIZE      = angle == 0 ? borderBox.size() : borderBox.size() + Vector2D{2.0, 2.0};
        const int      ROUND       = roundingForBox(texbox, rounding);
        const int      BORDERROUND = roundingForBorderBox(borderBox, rounding, border);

        imageFB.alloc(FBSIZE.x, FBSIZE.y, true);
        g_pRenderer->pushFb(imageFB.m_iFb);
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);

        if (border > 0)
            g_pRenderer->renderBorder(borderBox, color, border, BORDERROUND, 1.0);

        texbox.round();
        g_pRenderer->renderTexture(texbox, asset->texture, 1.0, ROUND, HYPRUTILS_TRANSFORM_NORMAL);
        g_pRenderer->popFb();
    }

    CTexture* tex    = &imageFB.m_cTex;
    CBox      texbox = {{}, tex->m_vSize};

    if (firstRender) {
        firstRender = false;
        shadow.markShadowDirty();
    }

    shadow.draw(data);

    pos = posFromHVAlign(viewport, tex->m_vSize, configPos, halign, valign, angle);

    texbox.x = pos.x;
    texbox.y = pos.y;

    texbox.round();
    texbox.rot = angle;
    g_pRenderer->renderTexture(texbox, *tex, data.opacity, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);

    return data.opacity < 1.0;
}

void CImage::renderUpdate() {
    auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
    if (newAsset) {
        if (newAsset->texture.m_iType == TEXTURE_INVALID) {
            g_pRenderer->asyncResourceGatherer->unloadAsset(newAsset);
        } else if (resourceID != pendingResourceID) {
            g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
            imageFB.destroyBuffer();

            asset       = newAsset;
            resourceID  = pendingResourceID;
            firstRender = true;
        }
        pendingResourceID = "";
    } else if (!pendingResourceID.empty()) {
        Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
        pendingResourceID = "";
    } else if (!pendingResourceID.empty()) {
        Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);

        g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
    }

    g_pHyprlock->renderOutput(stringPort);
}

CBox CImage::getBoundingBoxWl() const {
    if (!imageFB.isAllocated())
        return CBox{};

    return {
        Vector2D{pos.x, viewport.y - pos.y - imageFB.m_cTex.m_vSize.y},
        imageFB.m_cTex.m_vSize,
    };
}

void CImage::onClick(uint32_t button, bool down, const Vector2D& pos) {
    if (down && !onclickCommand.empty())
        spawnAsync(onclickCommand);
}

void CImage::onHover(const Vector2D& pos) {
    if (!onclickCommand.empty())
        g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);
}
07070100000056000081A4000000000000000000000001688B5FE9000008F2000000000000000000000000000000000000002E00000000hyprlock-0.9.1/src/renderer/widgets/Image.hpp#pragma once

#include "IWidget.hpp"
#include "../../helpers/Color.hpp"
#include "../../helpers/Math.hpp"
#include "../../config/ConfigDataValues.hpp"
#include "../../core/Timer.hpp"
#include "../AsyncResourceGatherer.hpp"
#include "Shadowable.hpp"
#include <string>
#include <filesystem>
#include <unordered_map>
#include <any>

struct SPreloadedAsset;
class COutput;

class CImage : public IWidget {
  public:
    CImage() = default;
    ~CImage();

    void         registerSelf(const ASP<CImage>& self);

    virtual void configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput);
    virtual bool draw(const SRenderData& data);
    virtual CBox getBoundingBoxWl() const;
    virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
    virtual void onHover(const Vector2D& pos);

    void         reset();

    void         renderUpdate();
    void         onTimerUpdate();
    void         plantTimer();

  private:
    AWP<CImage>                             m_self;

    CFramebuffer                            imageFB;

    int                                     size;
    int                                     rounding;
    double                                  border;
    double                                  angle;
    CGradientValueData                      color;
    Vector2D                                pos;
    Vector2D                                configPos;

    std::string                             halign, valign, path;

    bool                                    firstRender = true;

    int                                     reloadTime;
    std::string                             reloadCommand;
    std::string                             onclickCommand;

    std::filesystem::file_time_type         modificationTime;
    ASP<CTimer>                             imageTimer;
    CAsyncResourceGatherer::SPreloadRequest request;

    Vector2D                                viewport;
    std::string                             stringPort;

    std::string                             resourceID;
    std::string                             pendingResourceID; // if reloading image
    SPreloadedAsset*                        asset = nullptr;
    CShadowable                             shadow;
};
07070100000057000081A4000000000000000000000001688B5FE9000018D4000000000000000000000000000000000000002E00000000hyprlock-0.9.1/src/renderer/widgets/Label.cpp#include "Label.hpp"
#include "../Renderer.hpp"
#include "../../helpers/Log.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/Color.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../config/ConfigDataValues.hpp"
#include <hyprlang.hpp>
#include <stdexcept>

CLabel::~CLabel() {
    reset();
}

void CLabel::registerSelf(const ASP<CLabel>& self) {
    m_self = self;
}

static void onTimer(AWP<CLabel> ref) {
    if (auto PLABEL = ref.lock(); PLABEL) {
        // update label
        PLABEL->onTimerUpdate();
        // plant new timer
        PLABEL->plantTimer();
    }
}

static void onAssetCallback(AWP<CLabel> ref) {
    if (auto PLABEL = ref.lock(); PLABEL)
        PLABEL->renderUpdate();
}

std::string CLabel::getUniqueResourceId() {
    return std::string{"label:"} + std::to_string((uintptr_t)this) + ",time:" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count());
}

void CLabel::onTimerUpdate() {
    std::string oldFormatted = label.formatted;

    label = formatString(labelPreFormat);

    if (label.formatted == oldFormatted && !label.alwaysUpdate)
        return;

    if (!pendingResourceID.empty()) {
        Debug::log(WARN, "Trying to update label, but resource {} is still pending! Skipping update.", pendingResourceID);
        return;
    }

    // request new
    request.id        = getUniqueResourceId();
    pendingResourceID = request.id;
    request.asset     = label.formatted;

    request.callback = [REF = m_self]() { onAssetCallback(REF); };

    g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}

void CLabel::plantTimer() {

    if (label.updateEveryMs != 0)
        labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), [REF = m_self](auto, auto) { onTimer(REF); }, this, label.allowForceUpdate);
    else if (label.updateEveryMs == 0 && label.allowForceUpdate)
        labelTimer = g_pHyprlock->addTimer(std::chrono::hours(1), [REF = m_self](auto, auto) { onTimer(REF); }, this, true);
}

void CLabel::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
    reset();

    outputStringPort = pOutput->stringPort;
    viewport         = pOutput->getViewport();

    shadow.configure(m_self, props, viewport);

    try {
        configPos      = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
        labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
        halign         = std::any_cast<Hyprlang::STRING>(props.at("halign"));
        valign         = std::any_cast<Hyprlang::STRING>(props.at("valign"));
        angle          = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
        angle          = angle * M_PI / 180.0;
        onclickCommand = std::any_cast<Hyprlang::STRING>(props.at("onclick"));

        std::string textAlign  = std::any_cast<Hyprlang::STRING>(props.at("text_align"));
        std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
        CHyprColor  labelColor = std::any_cast<Hyprlang::INT>(props.at("color"));
        int         fontSize   = std::any_cast<Hyprlang::INT>(props.at("font_size"));

        label = formatString(labelPreFormat);

        request.id                   = getUniqueResourceId();
        resourceID                   = request.id;
        request.asset                = label.formatted;
        request.type                 = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
        request.props["font_family"] = fontFamily;
        request.props["color"]       = labelColor;
        request.props["font_size"]   = fontSize;
        request.props["cmd"]         = label.cmd;

        if (!textAlign.empty())
            request.props["text_align"] = textAlign;

    } catch (const std::bad_any_cast& e) {
        RASSERT(false, "Failed to construct CLabel: {}", e.what()); //
    } catch (const std::out_of_range& e) {
        RASSERT(false, "Missing property for CLabel: {}", e.what()); //
    }

    pos = configPos; // Label size not known yet

    g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);

    plantTimer();
}

void CLabel::reset() {
    if (labelTimer) {
        labelTimer->cancel();
        labelTimer.reset();
    }

    if (g_pHyprlock->m_bTerminate)
        return;

    if (asset)
        g_pRenderer->asyncResourceGatherer->unloadAsset(asset);

    asset = nullptr;
    pendingResourceID.clear();
    resourceID.clear();
}

bool CLabel::draw(const SRenderData& data) {
    if (!asset) {
        asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);

        if (!asset)
            return true;
    }

    if (updateShadow) {
        updateShadow = false;
        shadow.markShadowDirty();
    }

    shadow.draw(data);

    // calc pos
    pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign, angle);

    CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y};
    box.rot  = angle;
    g_pRenderer->renderTexture(box, asset->texture, data.opacity);

    return false;
}

void CLabel::renderUpdate() {
    auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
    if (newAsset) {
        // new asset is ready :D
        g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
        asset             = newAsset;
        resourceID        = pendingResourceID;
        pendingResourceID = "";
        updateShadow      = true;
    } else {
        Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);

        g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
        return;
    }

    g_pHyprlock->renderOutput(outputStringPort);
}

CBox CLabel::getBoundingBoxWl() const {
    if (!asset)
        return CBox{};

    return {
        Vector2D{pos.x, viewport.y - pos.y - asset->texture.m_vSize.y},
        asset->texture.m_vSize,
    };
}

void CLabel::onClick(uint32_t button, bool down, const Vector2D& pos) {
    if (down && !onclickCommand.empty())
        spawnAsync(onclickCommand);
}

void CLabel::onHover(const Vector2D& pos) {
    if (!onclickCommand.empty())
        g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);
}
07070100000058000081A4000000000000000000000001688B5FE90000079C000000000000000000000000000000000000002E00000000hyprlock-0.9.1/src/renderer/widgets/Label.hpp#pragma once

#include "IWidget.hpp"
#include "Shadowable.hpp"
#include "../../helpers/Math.hpp"
#include "../../core/Timer.hpp"
#include "../AsyncResourceGatherer.hpp"
#include <string>
#include <unordered_map>
#include <any>

struct SPreloadedAsset;
class CSessionLockSurface;

class CLabel : public IWidget {
  public:
    CLabel() = default;
    ~CLabel();

    void         registerSelf(const ASP<CLabel>& self);

    virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
    virtual bool draw(const SRenderData& data);
    virtual CBox getBoundingBoxWl() const;
    virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
    virtual void onHover(const Vector2D& pos);

    void         reset();

    void         renderUpdate();
    void         onTimerUpdate();
    void         plantTimer();

  private:
    AWP<CLabel>                             m_self;

    std::string                             getUniqueResourceId();

    std::string                             labelPreFormat;
    IWidget::SFormatResult                  label;

    Vector2D                                viewport;
    Vector2D                                pos;
    Vector2D                                configPos;
    double                                  angle;
    std::string                             resourceID;
    std::string                             pendingResourceID; // if dynamic label
    std::string                             halign, valign;
    std::string                             onclickCommand;
    SPreloadedAsset*                        asset = nullptr;

    std::string                             outputStringPort;

    CAsyncResourceGatherer::SPreloadRequest request;

    ASP<CTimer>                             labelTimer = nullptr;

    CShadowable                             shadow;
    bool                                    updateShadow = true;
};
07070100000059000081A4000000000000000000000001688B5FE900004E50000000000000000000000000000000000000003B00000000hyprlock-0.9.1/src/renderer/widgets/PasswordInputField.cpp#include "PasswordInputField.hpp"
#include "../Renderer.hpp"
#include "../../core/hyprlock.hpp"
#include "../../auth/Auth.hpp"
#include "../../config/ConfigDataValues.hpp"
#include "../../config/ConfigManager.hpp"
#include "../../helpers/Log.hpp"
#include "../../core/AnimationManager.hpp"
#include "../../helpers/Color.hpp"
#include <cmath>
#include <hyprutils/math/Vector2D.hpp>
#include <hyprutils/string/String.hpp>
#include <algorithm>
#include <hyprlang.hpp>

using namespace Hyprutils::String;

CPasswordInputField::~CPasswordInputField() {
    reset();
}

void CPasswordInputField::registerSelf(const ASP<CPasswordInputField>& self) {
    m_self = self;
}

void CPasswordInputField::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
    reset();

    outputStringPort = pOutput->stringPort;
    viewport         = pOutput->getViewport();

    shadow.configure(m_self, props, viewport);

    try {
        pos                      = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
        configSize               = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport);
        halign                   = std::any_cast<Hyprlang::STRING>(props.at("halign"));
        valign                   = std::any_cast<Hyprlang::STRING>(props.at("valign"));
        outThick                 = std::any_cast<Hyprlang::INT>(props.at("outline_thickness"));
        dots.size                = std::any_cast<Hyprlang::FLOAT>(props.at("dots_size"));
        dots.spacing             = std::any_cast<Hyprlang::FLOAT>(props.at("dots_spacing"));
        dots.center              = std::any_cast<Hyprlang::INT>(props.at("dots_center"));
        dots.rounding            = std::any_cast<Hyprlang::INT>(props.at("dots_rounding"));
        dots.textFormat          = std::any_cast<Hyprlang::STRING>(props.at("dots_text_format"));
        fadeOnEmpty              = std::any_cast<Hyprlang::INT>(props.at("fade_on_empty"));
        fadeTimeoutMs            = std::any_cast<Hyprlang::INT>(props.at("fade_timeout"));
        hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input"));
        rounding                 = std::any_cast<Hyprlang::INT>(props.at("rounding"));
        configPlaceholderText    = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text"));
        configFailText           = std::any_cast<Hyprlang::STRING>(props.at("fail_text"));
        fontFamily               = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
        colorConfig.outer        = CGradientValueData::fromAnyPv(props.at("outer_color"));
        colorConfig.inner        = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
        colorConfig.font         = std::any_cast<Hyprlang::INT>(props.at("font_color"));
        colorConfig.fail         = CGradientValueData::fromAnyPv(props.at("fail_color"));
        colorConfig.check        = CGradientValueData::fromAnyPv(props.at("check_color"));
        colorConfig.both         = CGradientValueData::fromAnyPv(props.at("bothlock_color"));
        colorConfig.caps         = CGradientValueData::fromAnyPv(props.at("capslock_color"));
        colorConfig.num          = CGradientValueData::fromAnyPv(props.at("numlock_color"));
        colorConfig.invertNum    = std::any_cast<Hyprlang::INT>(props.at("invert_numlock"));
        colorConfig.swapFont     = std::any_cast<Hyprlang::INT>(props.at("swap_font_color"));
        colorConfig.hiddenBase   = std::any_cast<Hyprlang::INT>(props.at("hide_input_base_color"));
    } catch (const std::bad_any_cast& e) {
        RASSERT(false, "Failed to construct CPasswordInputField: {}", e.what()); //
    } catch (const std::out_of_range& e) {
        RASSERT(false, "Missing property for CPasswordInputField: {}", e.what()); //
    }

    configPos       = pos;
    colorState.font = colorConfig.font;

    pos          = posFromHVAlign(viewport, configSize, pos, halign, valign);
    dots.size    = std::clamp(dots.size, 0.2f, 0.8f);
    dots.spacing = std::clamp(dots.spacing, -1.f, 1.f);

    colorConfig.caps = colorConfig.caps->m_bIsFallback ? colorConfig.fail : colorConfig.caps;

    g_pAnimationManager->createAnimation(0.f, fade.a, g_pConfigManager->m_AnimationTree.getConfig("inputFieldFade"));
    g_pAnimationManager->createAnimation(0.f, dots.currentAmount, g_pConfigManager->m_AnimationTree.getConfig("inputFieldDots"));
    g_pAnimationManager->createAnimation(configSize, size, g_pConfigManager->m_AnimationTree.getConfig("inputFieldWidth"));
    g_pAnimationManager->createAnimation(colorConfig.inner, colorState.inner, g_pConfigManager->m_AnimationTree.getConfig("inputFieldColors"));
    g_pAnimationManager->createAnimation(*colorConfig.outer, colorState.outer, g_pConfigManager->m_AnimationTree.getConfig("inputFieldColors"));

    srand(std::chrono::system_clock::now().time_since_epoch().count());

    pos = posFromHVAlign(viewport, size->goal(), configPos, halign, valign);

    if (!dots.textFormat.empty()) {
        dots.textResourceID = std::format("input:{}-{}", (uintptr_t)this, dots.textFormat);
        CAsyncResourceGatherer::SPreloadRequest request;
        request.id                   = dots.textResourceID;
        request.asset                = dots.textFormat;
        request.type                 = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
        request.props["font_family"] = fontFamily;
        request.props["color"]       = colorConfig.font;
        request.props["font_size"]   = (int)(std::nearbyint(configSize.y * dots.size * 0.5f) * 2.f);

        g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
    }

    // request the inital placeholder asset
    updatePlaceholder();
}

void CPasswordInputField::reset() {
    if (fade.fadeOutTimer.get()) {
        fade.fadeOutTimer->cancel();
        fade.fadeOutTimer.reset();
    }

    if (g_pHyprlock->m_bTerminate)
        return;

    if (placeholder.asset)
        g_pRenderer->asyncResourceGatherer->unloadAsset(placeholder.asset);

    placeholder.asset = nullptr;
    placeholder.resourceID.clear();
    placeholder.currentText.clear();
}

static void fadeOutCallback(AWP<CPasswordInputField> ref) {
    if (const auto PP = ref.lock(); PP)
        PP->onFadeOutTimer();
}

void CPasswordInputField::onFadeOutTimer() {
    fade.allowFadeOut = true;
    fade.fadeOutTimer.reset();

    g_pHyprlock->renderOutput(outputStringPort);
}

void CPasswordInputField::updateFade() {
    if (!fadeOnEmpty) {
        fade.a->setValueAndWarp(1.0);
        return;
    }

    const bool INPUTUSED = passwordLength > 0 || checkWaiting;

    if (INPUTUSED && fade.allowFadeOut)
        fade.allowFadeOut = false;

    if (INPUTUSED && fade.fadeOutTimer.get()) {
        fade.fadeOutTimer->cancel();
        fade.fadeOutTimer.reset();
    }

    if (!INPUTUSED && fade.a->goal() != 0.0) {
        if (fade.allowFadeOut || fadeTimeoutMs == 0) {
            *fade.a           = 0.0;
            fade.allowFadeOut = false;
        } else if (!fade.fadeOutTimer.get())
            fade.fadeOutTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(fadeTimeoutMs), [REF = m_self](auto, auto) { fadeOutCallback(REF); }, nullptr);

    } else if (INPUTUSED && fade.a->goal() != 1.0)
        *fade.a = 1.0;

    if (fade.a->isBeingAnimated())
        redrawShadow = true;
}

void CPasswordInputField::updateDots() {
    if (dots.currentAmount->goal() == passwordLength)
        return;

    if (checkWaiting)
        return;

    if (passwordLength == 0)
        dots.currentAmount->setValueAndWarp(passwordLength);
    else
        *dots.currentAmount = passwordLength;
}

bool CPasswordInputField::draw(const SRenderData& data) {
    if (firstRender || redrawShadow) {
        firstRender  = false;
        redrawShadow = false;
        shadow.markShadowDirty();
    }

    bool forceReload = false;

    passwordLength = g_pHyprlock->getPasswordBufferDisplayLen();
    checkWaiting   = g_pAuth->checkWaiting();
    displayFail    = g_pAuth->m_bDisplayFailText;

    updateFade();
    updateDots();
    updateColors();
    updatePlaceholder();
    updateWidth();
    updateHiddenInputState();

    CBox        inputFieldBox = {pos, size->value()};
    CBox        outerBox      = {pos - Vector2D{outThick, outThick}, size->value() + Vector2D{outThick * 2, outThick * 2}};

    SRenderData shadowData = data;
    shadowData.opacity *= fade.a->value();

    if (!size->isBeingAnimated())
        shadow.draw(shadowData);

    //CGradientValueData outerGrad = colorState.outer->value();
    //for (auto& c : outerGrad.m_vColors)
    //    c.a *= fade.a->value() * data.opacity;

    CHyprColor innerCol = colorState.inner->value();
    innerCol.a *= fade.a->value() * data.opacity;
    CHyprColor fontCol = colorState.font;
    fontCol.a *= fade.a->value() * data.opacity;

    if (outThick > 0) {
        const auto OUTERROUND = roundingForBorderBox(outerBox, rounding, outThick);
        g_pRenderer->renderBorder(outerBox, colorState.outer->value(), outThick, OUTERROUND, fade.a->value() * data.opacity);

        if (passwordLength != 0 && !checkWaiting && hiddenInputState.enabled) {
            CBox     outerBoxScaled = outerBox;
            Vector2D p              = outerBox.pos();
            outerBoxScaled.translate(-p).scale(0.5).translate(p);
            if (hiddenInputState.lastQuadrant > 1)
                outerBoxScaled.y += outerBoxScaled.h;
            if (hiddenInputState.lastQuadrant % 2 == 1)
                outerBoxScaled.x += outerBoxScaled.w;
            glEnable(GL_SCISSOR_TEST);
            glScissor(outerBoxScaled.x, outerBoxScaled.y, outerBoxScaled.w, outerBoxScaled.h);
            g_pRenderer->renderBorder(outerBox, hiddenInputState.lastColor, outThick, OUTERROUND, fade.a->value() * data.opacity);
            glScissor(0, 0, viewport.x, viewport.y);
            glDisable(GL_SCISSOR_TEST);
        }
    }

    const int ROUND = roundingForBox(inputFieldBox, rounding);
    g_pRenderer->renderRect(inputFieldBox, innerCol, ROUND);

    if (!hiddenInputState.enabled) {
        const int RECTPASSSIZE = std::nearbyint(inputFieldBox.h * dots.size * 0.5f) * 2.f;
        Vector2D  passSize{RECTPASSSIZE, RECTPASSSIZE};
        int       passSpacing = std::floor(passSize.x * dots.spacing);

        if (!dots.textFormat.empty()) {
            if (!dots.textAsset)
                dots.textAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(dots.textResourceID);

            if (!dots.textAsset)
                forceReload = true;
            else {
                passSize    = dots.textAsset->texture.m_vSize;
                passSpacing = std::floor(passSize.x * dots.spacing);
            }
        }

        const auto   CURRDOTS     = dots.currentAmount->value();
        const double DOTPAD       = (inputFieldBox.h - passSize.y) / 2.0;
        const double DOTAREAWIDTH = inputFieldBox.w - (DOTPAD * 2);
        const int    MAXDOTS      = std::round(DOTAREAWIDTH * 1.0 / (passSize.x + passSpacing));
        const int    DOTFLOORED   = std::floor(CURRDOTS);
        const auto   DOTALPHA     = fontCol.a;

        // Calculate the total width required for all dots including spaces between them
        const double CURRWIDTH = ((passSize.x + passSpacing) * CURRDOTS) - passSpacing;

        // Calculate starting x-position to ensure dots stay centered within the input field
        double xstart = dots.center ? ((DOTAREAWIDTH - CURRWIDTH) / 2.0) + DOTPAD : DOTPAD;

        if (CURRDOTS > MAXDOTS)
            xstart = (inputFieldBox.w + MAXDOTS * (passSize.x + passSpacing) - passSpacing - 2 * CURRWIDTH) / 2.0;

        if (dots.rounding == -1)
            dots.rounding = passSize.x / 2.0;
        else if (dots.rounding == -2)
            dots.rounding = rounding == -1 ? passSize.x / 2.0 : rounding * dots.size;

        for (int i = 0; i < CURRDOTS; ++i) {
            if (i < DOTFLOORED - MAXDOTS)
                continue;

            if (CURRDOTS != DOTFLOORED) {
                if (i == DOTFLOORED)
                    fontCol.a *= (CURRDOTS - DOTFLOORED) * data.opacity;
                else if (i == DOTFLOORED - MAXDOTS)
                    fontCol.a *= (1 - CURRDOTS + DOTFLOORED) * data.opacity;
            }

            Vector2D dotPosition = inputFieldBox.pos() + Vector2D{xstart + (i * (passSize.x + passSpacing)), (inputFieldBox.h / 2.0) - (passSize.y / 2.0)};
            CBox     box{dotPosition, passSize};
            if (!dots.textFormat.empty()) {
                if (!dots.textAsset) {
                    forceReload = true;
                    fontCol.a   = DOTALPHA;
                    break;
                }

                g_pRenderer->renderTexture(box, dots.textAsset->texture, fontCol.a, dots.rounding);
            } else
                g_pRenderer->renderRect(box, fontCol, dots.rounding);

            fontCol.a = DOTALPHA;
        }
    }

    if (passwordLength == 0 && !checkWaiting && !placeholder.resourceID.empty()) {
        SPreloadedAsset* currAsset = nullptr;

        if (!placeholder.asset)
            placeholder.asset = g_pRenderer->asyncResourceGatherer->getAssetByID(placeholder.resourceID);

        currAsset = placeholder.asset;

        if (currAsset) {
            const Vector2D ASSETPOS = inputFieldBox.pos() + inputFieldBox.size() / 2.0 - currAsset->texture.m_vSize / 2.0;
            const CBox     ASSETBOX{ASSETPOS, currAsset->texture.m_vSize};

            // Cut the texture to the width of the input field
            glEnable(GL_SCISSOR_TEST);
            glScissor(inputFieldBox.x, inputFieldBox.y, inputFieldBox.w, inputFieldBox.h);
            g_pRenderer->renderTexture(ASSETBOX, currAsset->texture, data.opacity * fade.a->value(), 0);
            glScissor(0, 0, viewport.x, viewport.y);
            glDisable(GL_SCISSOR_TEST);
        } else
            forceReload = true;
    }

    return redrawShadow || forceReload;
}

void CPasswordInputField::updatePlaceholder() {
    if (passwordLength != 0) {
        if (placeholder.asset && /* keep prompt asset cause it is likely to be used again */ displayFail) {
            std::erase(placeholder.registeredResourceIDs, placeholder.resourceID);
            g_pRenderer->asyncResourceGatherer->unloadAsset(placeholder.asset);
            placeholder.asset      = nullptr;
            placeholder.resourceID = "";
            redrawShadow           = true;
        }
        return;
    }

    // already requested a placeholder for the current fail
    if (displayFail && placeholder.failedAttempts == g_pAuth->getFailedAttempts())
        return;

    placeholder.failedAttempts = g_pAuth->getFailedAttempts();

    std::string newText = (displayFail) ? formatString(configFailText).formatted : formatString(configPlaceholderText).formatted;

    // if the text is unchanged we don't need to do anything, unless we are swapping font color
    const auto ALLOWCOLORSWAP = outThick == 0 && colorConfig.swapFont;
    if (!ALLOWCOLORSWAP && newText == placeholder.currentText)
        return;

    const auto NEWRESOURCEID = std::format("placeholder:{}{}{}{}{}{}", newText, (uintptr_t)this, colorState.font.r, colorState.font.g, colorState.font.b, colorState.font.a);

    if (placeholder.resourceID == NEWRESOURCEID)
        return;

    Debug::log(TRACE, "Updating placeholder text: {}", newText);
    placeholder.currentText = newText;
    placeholder.asset       = nullptr;
    placeholder.resourceID  = NEWRESOURCEID;

    if (std::ranges::find(placeholder.registeredResourceIDs, placeholder.resourceID) != placeholder.registeredResourceIDs.end())
        return;

    Debug::log(TRACE, "Requesting new placeholder asset: {}", placeholder.resourceID);
    placeholder.registeredResourceIDs.push_back(placeholder.resourceID);

    // query
    CAsyncResourceGatherer::SPreloadRequest request;
    request.id                   = placeholder.resourceID;
    request.asset                = placeholder.currentText;
    request.type                 = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
    request.props["font_family"] = fontFamily;
    request.props["color"]       = colorState.font;
    request.props["font_size"]   = (int)size->value().y / 4;
    request.callback             = [REF = m_self] {
        if (const auto SELF = REF.lock(); SELF)
            g_pHyprlock->renderOutput(SELF->outputStringPort);
    };
    g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}

void CPasswordInputField::updateWidth() {
    double targetSizeX = configSize.x;

    if (passwordLength == 0 && placeholder.asset)
        targetSizeX = placeholder.asset->texture.m_vSize.x + size->goal().y;

    targetSizeX = std::max(targetSizeX, configSize.x);

    if (size->goal().x != targetSizeX) {
        *size = Vector2D{targetSizeX, configSize.y};
        size->setCallbackOnEnd([this](auto) {
            redrawShadow = true;
            pos          = posFromHVAlign(viewport, size->value(), configPos, halign, valign);
        });
    }

    if (size->isBeingAnimated()) {
        redrawShadow = true;
        pos          = posFromHVAlign(viewport, size->value(), configPos, halign, valign);
    }
}

void CPasswordInputField::updateHiddenInputState() {
    if (!hiddenInputState.enabled || (size_t)hiddenInputState.lastPasswordLength == passwordLength)
        return;

    // randomize new thang
    hiddenInputState.lastPasswordLength = passwordLength;

    const auto BASEOK = colorConfig.hiddenBase.asOkLab();

    // convert to polar coordinates
    const auto OKICHCHROMA = std::sqrt(std::pow(BASEOK.a, 2) + std::pow(BASEOK.b, 2));

    // now randomly rotate the hue
    const double OKICHHUE = (rand() % 10000000 / 10000000.0) * M_PI * 4;

    // convert back to OkLab
    const Hyprgraphics::CColor newColor = Hyprgraphics::CColor::SOkLab{
        .l = BASEOK.l,
        .a = OKICHCHROMA * std::cos(OKICHHUE),
        .b = OKICHCHROMA * std::sin(OKICHHUE),
    };

    hiddenInputState.lastColor    = {newColor, 1.0};
    hiddenInputState.lastQuadrant = (hiddenInputState.lastQuadrant + rand() % 3 + 1) % 4;
}

void CPasswordInputField::updateColors() {
    const bool          BORDERLESS = outThick == 0;
    const bool          NUMLOCK    = (colorConfig.invertNum) ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock;

    CGradientValueData* targetGrad = nullptr;

    if (g_pHyprlock->m_bCapsLock && NUMLOCK && !colorConfig.both->m_bIsFallback)
        targetGrad = colorConfig.both;
    else if (g_pHyprlock->m_bCapsLock)
        targetGrad = colorConfig.caps;
    else if (NUMLOCK && !colorConfig.num->m_bIsFallback)
        targetGrad = colorConfig.num;

    if (checkWaiting)
        targetGrad = colorConfig.check;
    else if (displayFail && passwordLength == 0)
        targetGrad = colorConfig.fail;

    CGradientValueData* outerTarget = colorConfig.outer;
    CHyprColor          innerTarget = colorConfig.inner;
    CHyprColor          fontTarget  = (displayFail) ? colorConfig.fail->m_vColors.front() : colorConfig.font;

    if (targetGrad) {
        if (BORDERLESS && colorConfig.swapFont) {
            fontTarget = targetGrad->m_vColors.front();
        } else if (BORDERLESS && !colorConfig.swapFont) {
            innerTarget = targetGrad->m_vColors.front();
            // When changing the inner color, the font cannot be fail_color
            fontTarget = colorConfig.font;
        } else if (targetGrad) {
            outerTarget = targetGrad;
        }
    }

    if (!BORDERLESS && *outerTarget != colorState.outer->goal())
        *colorState.outer = *outerTarget;

    if (innerTarget != colorState.inner->goal())
        *colorState.inner = innerTarget;

    colorState.font = fontTarget;
}

CBox CPasswordInputField::getBoundingBoxWl() const {
    return {
        Vector2D{pos.x, viewport.y - pos.y - size->value().y},
        size->value(),
    };
}

void CPasswordInputField::onHover(const Vector2D& pos) {
    g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT);
}
0707010000005A000081A4000000000000000000000001688B5FE900000FBA000000000000000000000000000000000000003B00000000hyprlock-0.9.1/src/renderer/widgets/PasswordInputField.hpp#pragma once

#include "IWidget.hpp"
#include "../../helpers/Color.hpp"
#include "../../helpers/Math.hpp"
#include "../../core/Timer.hpp"
#include "Shadowable.hpp"
#include "../../config/ConfigDataValues.hpp"
#include "../../helpers/AnimatedVariable.hpp"
#include <hyprutils/math/Vector2D.hpp>
#include <vector>
#include <any>
#include <unordered_map>

struct SPreloadedAsset;

class CPasswordInputField : public IWidget {
  public:
    CPasswordInputField() = default;
    virtual ~CPasswordInputField();

    void         registerSelf(const ASP<CPasswordInputField>& self);

    virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
    virtual bool draw(const SRenderData& data);
    virtual void onHover(const Vector2D& pos);
    virtual CBox getBoundingBoxWl() const;

    void         reset();
    void         onFadeOutTimer();

  private:
    AWP<CPasswordInputField> m_self;

    void                     updateDots();
    void                     updateFade();
    void                     updatePlaceholder();
    void                     updateWidth();
    void                     updateHiddenInputState();
    void                     updateInputState();
    void                     updateColors();

    bool                     firstRender  = true;
    bool                     redrawShadow = false;
    bool                     checkWaiting = false;
    bool                     displayFail  = false;

    size_t                   passwordLength = 0;

    PHLANIMVAR<Vector2D>     size;
    Vector2D                 pos;
    Vector2D                 viewport;
    Vector2D                 configPos;
    Vector2D                 configSize;

    std::string              halign, valign, configFailText, outputStringPort, configPlaceholderText, fontFamily;
    uint64_t                 configFailTimeoutMs = 2000;

    int                      outThick, rounding;

    struct {
        PHLANIMVAR<float> currentAmount;
        bool              center     = false;
        float             size       = 0;
        float             spacing    = 0;
        int               rounding   = 0;
        std::string       textFormat = "";
        std::string       textResourceID;
        SPreloadedAsset*  textAsset = nullptr;
    } dots;

    struct {
        PHLANIMVAR<float> a;
        bool              appearing    = true;
        ASP<CTimer>       fadeOutTimer = nullptr;
        bool              allowFadeOut = false;
    } fade;

    struct {
        std::string              resourceID = "";
        SPreloadedAsset*         asset      = nullptr;

        std::string              currentText    = "";
        size_t                   failedAttempts = 0;

        std::vector<std::string> registeredResourceIDs;
    } placeholder;

    struct {
        CHyprColor lastColor;
        int        lastQuadrant       = 0;
        int        lastPasswordLength = 0;
        bool       enabled            = false;
    } hiddenInputState;

    struct {
        CGradientValueData* outer = nullptr;
        CHyprColor          inner;
        CHyprColor          font;
        CGradientValueData* fail  = nullptr;
        CGradientValueData* check = nullptr;
        CGradientValueData* caps  = nullptr;
        CGradientValueData* num   = nullptr;
        CGradientValueData* both  = nullptr;

        CHyprColor          hiddenBase;

        int                 transitionMs = 0;
        bool                invertNum    = false;
        bool                swapFont     = false;
    } colorConfig;

    struct {
        PHLANIMVAR<CGradientValueData> outer;
        PHLANIMVAR<CHyprColor>         inner;
        // Font color is only chaned, when `swap_font_color` is set to true and no border is present.
        // It is not animated, because that does not look good and we would need to rerender the text for each frame.
        CHyprColor font;
    } colorState;

    bool        fadeOnEmpty;
    uint64_t    fadeTimeoutMs;

    CShadowable shadow;
};
0707010000005B000081A4000000000000000000000001688B5FE9000005E8000000000000000000000000000000000000003300000000hyprlock-0.9.1/src/renderer/widgets/Shadowable.cpp#include "Shadowable.hpp"
#include "../Renderer.hpp"
#include <hyprlang.hpp>

void CShadowable::configure(AWP<IWidget> widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_) {
    m_widget = widget_;
    viewport = viewport_;

    size   = std::any_cast<Hyprlang::INT>(props.at("shadow_size"));
    passes = std::any_cast<Hyprlang::INT>(props.at("shadow_passes"));
    color  = std::any_cast<Hyprlang::INT>(props.at("shadow_color"));
    boostA = std::any_cast<Hyprlang::FLOAT>(props.at("shadow_boost"));
}

void CShadowable::markShadowDirty() {
    const auto WIDGET = m_widget.lock();

    if (!m_widget)
        return;

    if (passes == 0)
        return;

    if (!shadowFB.isAllocated())
        shadowFB.alloc(viewport.x, viewport.y, true);

    g_pRenderer->pushFb(shadowFB.m_iFb);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    ignoreDraw = true;
    WIDGET->draw(IWidget::SRenderData{.opacity = 1.0});
    ignoreDraw = false;

    g_pRenderer->blurFB(shadowFB, CRenderer::SBlurParams{.size = size, .passes = passes, .colorize = color, .boostA = boostA});

    g_pRenderer->popFb();
}

bool CShadowable::draw(const IWidget::SRenderData& data) {
    if (!m_widget || passes == 0)
        return true;

    if (!shadowFB.isAllocated() || ignoreDraw)
        return true;

    CBox box = {0, 0, viewport.x, viewport.y};
    g_pRenderer->renderTexture(box, shadowFB.m_cTex, data.opacity, 0, HYPRUTILS_TRANSFORM_NORMAL);
    return true;
}
0707010000005C000081A4000000000000000000000001688B5FE9000003A8000000000000000000000000000000000000003300000000hyprlock-0.9.1/src/renderer/widgets/Shadowable.hpp#pragma once

#include "../Framebuffer.hpp"
#include "../../helpers/Color.hpp"
#include "../../helpers/Math.hpp"
#include "IWidget.hpp"

#include <string>
#include <unordered_map>
#include <any>

class CShadowable {
  public:
    virtual ~CShadowable() = default;
    CShadowable()          = default;
    void configure(AWP<IWidget> widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_ /* TODO: make this not the entire viewport */);

    // instantly re-renders the shadow using the widget's draw() method
    void         markShadowDirty();
    virtual bool draw(const IWidget::SRenderData& data);

  private:
    AWP<IWidget> m_widget;
    int          size   = 10;
    int          passes = 4;
    float        boostA = 1.0;
    CHyprColor   color{0, 0, 0, 1.0};
    Vector2D     viewport;

    // to avoid recursive shadows
    bool         ignoreDraw = false;

    CFramebuffer shadowFB;
};
0707010000005D000081A4000000000000000000000001688B5FE900001125000000000000000000000000000000000000002E00000000hyprlock-0.9.1/src/renderer/widgets/Shape.cpp#include "Shape.hpp"
#include "../Renderer.hpp"
#include "../../config/ConfigDataValues.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include <cmath>
#include <hyprlang.hpp>
#include <sys/types.h>

void CShape::registerSelf(const ASP<CShape>& self) {
    m_self = self;
}

void CShape::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
    viewport = pOutput->getViewport();

    shadow.configure(m_self, props, viewport);

    try {
        size           = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport);
        rounding       = std::any_cast<Hyprlang::INT>(props.at("rounding"));
        border         = std::any_cast<Hyprlang::INT>(props.at("border_size"));
        color          = std::any_cast<Hyprlang::INT>(props.at("color"));
        borderGrad     = *CGradientValueData::fromAnyPv(props.at("border_color"));
        pos            = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
        halign         = std::any_cast<Hyprlang::STRING>(props.at("halign"));
        valign         = std::any_cast<Hyprlang::STRING>(props.at("valign"));
        angle          = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
        xray           = std::any_cast<Hyprlang::INT>(props.at("xray"));
        onclickCommand = std::any_cast<Hyprlang::STRING>(props.at("onclick"));
    } catch (const std::bad_any_cast& e) {
        RASSERT(false, "Failed to construct CShape: {}", e.what()); //
    } catch (const std::out_of_range& e) {
        RASSERT(false, "Missing property for CShape: {}", e.what()); //
    }

    angle = angle * M_PI / 180.0;

    const Vector2D VBORDER  = {border, border};
    const Vector2D REALSIZE = size + VBORDER * 2.0;
    const Vector2D OFFSET   = angle == 0 ? Vector2D{0.0, 0.0} : Vector2D{1.0, 1.0};

    pos = posFromHVAlign(viewport, xray ? size : REALSIZE + OFFSET * 2.0, pos, halign, valign, xray ? 0 : angle);

    if (xray) {
        shapeBox  = {pos, size};
        borderBox = {pos - VBORDER, REALSIZE};
    } else {
        shapeBox  = {OFFSET + VBORDER, size};
        borderBox = {OFFSET, REALSIZE};
    }
}

bool CShape::draw(const SRenderData& data) {

    if (firstRender) {
        firstRender = false;
        shadow.markShadowDirty();
    }

    shadow.draw(data);

    const auto MINHALFBORDER = std::min(borderBox.w, borderBox.h) / 2.0;

    if (xray) {
        if (border > 0) {
            const int PIROUND = std::min(MINHALFBORDER, std::round(border * M_PI));
            g_pRenderer->renderBorder(borderBox, borderGrad, border, rounding == -1 ? PIROUND : std::clamp(rounding, 0, PIROUND), data.opacity);
        }

        glEnable(GL_SCISSOR_TEST);
        glScissor(shapeBox.x, shapeBox.y, shapeBox.width, shapeBox.height);
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glDisable(GL_SCISSOR_TEST);

        return data.opacity < 1.0;
    }

    if (!shapeFB.isAllocated()) {
        const int ROUND       = roundingForBox(shapeBox, rounding);
        const int BORDERROUND = roundingForBorderBox(borderBox, rounding, border);
        Debug::log(LOG, "round: {}, borderround: {}", ROUND, BORDERROUND);

        shapeFB.alloc(borderBox.width + (borderBox.x * 2.0), borderBox.height + (borderBox.y * 2.0), true);
        g_pRenderer->pushFb(shapeFB.m_iFb);
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT);

        if (border > 0)
            g_pRenderer->renderBorder(borderBox, borderGrad, border, BORDERROUND, 1.0);

        g_pRenderer->renderRect(shapeBox, color, ROUND);
        g_pRenderer->popFb();
    }

    CTexture* tex    = &shapeFB.m_cTex;
    CBox      texbox = {pos, tex->m_vSize};

    texbox.round();
    texbox.rot = angle;

    g_pRenderer->renderTexture(texbox, *tex, data.opacity, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);

    return data.opacity < 1.0;
}
CBox CShape::getBoundingBoxWl() const {
    return {
        Vector2D{pos.x, viewport.y - pos.y - size.y},
        size,
    };
}

void CShape::onClick(uint32_t button, bool down, const Vector2D& pos) {
    if (down && !onclickCommand.empty())
        spawnAsync(onclickCommand);
}

void CShape::onHover(const Vector2D& pos) {
    if (!onclickCommand.empty())
        g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);
}
0707010000005E000081A4000000000000000000000001688B5FE90000051A000000000000000000000000000000000000002E00000000hyprlock-0.9.1/src/renderer/widgets/Shape.hpp#pragma once

#include "IWidget.hpp"
#include "../../helpers/Color.hpp"
#include "../../config/ConfigDataValues.hpp"
#include "Shadowable.hpp"
#include <hyprutils/math/Box.hpp>
#include <string>
#include <unordered_map>
#include <any>

class CShape : public IWidget {
  public:
    CShape()          = default;
    virtual ~CShape() = default;

    void         registerSelf(const ASP<CShape>& self);

    virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
    virtual bool draw(const SRenderData& data);
    virtual CBox getBoundingBoxWl() const;
    virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
    virtual void onHover(const Vector2D& pos);

  private:
    AWP<CShape>        m_self;

    CFramebuffer       shapeFB;

    int                rounding;
    double             border;
    double             angle;
    CHyprColor         color;
    CGradientValueData borderGrad;
    Vector2D           size;
    Vector2D           pos;
    CBox               shapeBox;
    CBox               borderBox;
    bool               xray;

    std::string        halign, valign;

    bool               firstRender = true;
    std::string        onclickCommand;

    Vector2D           viewport;
    CShadowable        shadow;
};
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!741 blocks
openSUSE Build Service is sponsored by