File hyprlock-0.4.1.obscpio of Package hyprlock

07070100000000000081A4000000000000000000000001669CF85C0000070E000000000000000000000000000000000000001D00000000hyprlock-0.4.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
07070100000001000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001700000000hyprlock-0.4.1/.github07070100000002000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000002100000000hyprlock-0.4.1/.github/workflows07070100000003000081A4000000000000000000000001669CF85C000001EE000000000000000000000000000000000000002900000000hyprlock-0.4.1/.github/workflows/nix.ymlname: Build

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

    - uses: DeterminateSystems/nix-installer-action@main
    - uses: DeterminateSystems/magic-nix-cache-action@main

    # 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

07070100000004000081A4000000000000000000000001669CF85C0000009B000000000000000000000000000000000000001A00000000hyprlock-0.4.1/.gitignore**/.cache
.direnv
.envrc
.vscode/
CMakeCache.txt
CMakeFiles/
Makefile
cmake_install.cmake
build/
compile_commands.json
protocols/*.c
protocols/*.h
*.kdev4
07070100000005000081A4000000000000000000000001669CF85C00000EFD000000000000000000000000000000000000001E00000000hyprlock-0.4.1/CMakeLists.txtcmake_minimum_required(VERSION 3.19)

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)

# 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)
pkg_check_modules(
  deps
  REQUIRED
  IMPORTED_TARGET
  wayland-client
  wayland-protocols
  wayland-egl
  hyprlang>=0.4.0
  egl
  opengl
  xkbcommon
  libjpeg
  libwebp
  libmagic
  cairo
  pangocairo
  libdrm
  gbm
  hyprutils>=0.2.0)

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::GL)

# protocols
find_program(WaylandScanner NAMES wayland-scanner)
message(STATUS "Found WaylandScanner at ${WaylandScanner}")
execute_process(
  COMMAND pkg-config --variable=pkgdatadir wayland-protocols
  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
  OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR
  OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")

function(protocol protoPath protoName external)
  if(external)
    execute_process(
      COMMAND ${WaylandScanner} client-header ${protoPath}
              protocols/${protoName}-protocol.h
      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
    execute_process(
      COMMAND ${WaylandScanner} private-code ${protoPath}
              protocols/${protoName}-protocol.c
      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
    target_sources(hyprlock PRIVATE protocols/${protoName}-protocol.c)
  else()
    execute_process(
      COMMAND
        ${WaylandScanner} client-header ${WAYLAND_PROTOCOLS_DIR}/${protoPath}
        protocols/${protoName}-protocol.h
      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
    execute_process(
      COMMAND
        ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/${protoPath}
        protocols/${protoName}-protocol.c
      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
    target_sources(hyprlock PRIVATE protocols/${protoName}-protocol.c)
  endif()
endfunction()

make_directory(${CMAKE_SOURCE_DIR}/protocols) # we don't ship any custom ones so
                                              # the dir won't be there
protocol("staging/ext-session-lock/ext-session-lock-v1.xml"
         "ext-session-lock-v1" false)
protocol("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false)
protocol("unstable/tablet/tablet-unstable-v2.xml" "tablet-unstable-v2" false)
protocol("staging/fractional-scale/fractional-scale-v1.xml"
         "fractional-scale-v1" false)
protocol("stable/viewporter/viewporter.xml" "viewporter" false)
protocol("protocols/wlr-screencopy-unstable-v1.xml"
         "wlr-screencopy-unstable-v1" true)
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml"
         "linux-dmabuf-unstable-v1" false)

# Installation
install(TARGETS hyprlock)

install(FILES ${CMAKE_SOURCE_DIR}/pam/hyprlock
        DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/pam.d)
07070100000006000081A4000000000000000000000001669CF85C000005DF000000000000000000000000000000000000001700000000hyprlock-0.4.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.
07070100000007000081A4000000000000000000000001669CF85C00000514000000000000000000000000000000000000001900000000hyprlock-0.4.1/README.md# hyprlock
Hyprland's simple, yet multi-threaded and GPU-accelerated screen locking utility.

## Features
 - uses the secure ext-session-lock protocol
 - full support for fractional-scale
 - fully GPU accelerated
 - multi-threaded resource acquisition for no hitches

## 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 also need the following dependencies
- wayland-client
- wayland-protocols
- mesa

And the development libraries for the following
- cairo
- libdrm
- pango
- xkbcommon
- pam
- hyprlang >= 0.4
- libmagic (file-devel on Fedora)

Development libraries are usually suffixed with `-devel` or `-dev` in most distro repos.

You also need to install `mesa-libgbm-devel` on some distros like RPM based ones where its not
bundled with the mesa package.

### 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
```
07070100000008000081A4000000000000000000000001669CF85C00000006000000000000000000000000000000000000001700000000hyprlock-0.4.1/VERSION0.4.1
07070100000009000081A4000000000000000000000001669CF85C000008B4000000000000000000000000000000000000001A00000000hyprlock-0.4.1/flake.lock{
  "nodes": {
    "hyprlang": {
      "inputs": {
        "hyprutils": [
          "hyprutils"
        ],
        "nixpkgs": [
          "nixpkgs"
        ],
        "systems": [
          "systems"
        ]
      },
      "locked": {
        "lastModified": 1721324361,
        "narHash": "sha256-BiJKO0IIdnSwHQBSrEJlKlFr753urkLE48wtt0UhNG4=",
        "owner": "hyprwm",
        "repo": "hyprlang",
        "rev": "adbefbf49664a6c2c8bf36b6487fd31e3eb68086",
        "type": "github"
      },
      "original": {
        "owner": "hyprwm",
        "repo": "hyprlang",
        "type": "github"
      }
    },
    "hyprutils": {
      "inputs": {
        "nixpkgs": [
          "nixpkgs"
        ],
        "systems": [
          "systems"
        ]
      },
      "locked": {
        "lastModified": 1721324102,
        "narHash": "sha256-WAZ0X6yJW1hFG6otkHBfyJDKRpNP5stsRqdEuHrFRpk=",
        "owner": "hyprwm",
        "repo": "hyprutils",
        "rev": "962582a090bc233c4de9d9897f46794280288989",
        "type": "github"
      },
      "original": {
        "owner": "hyprwm",
        "repo": "hyprutils",
        "type": "github"
      }
    },
    "nixpkgs": {
      "locked": {
        "lastModified": 1721138476,
        "narHash": "sha256-+W5eZOhhemLQxelojLxETfbFbc19NWawsXBlapYpqIA=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "ad0b5eed1b6031efaed382844806550c3dcb4206",
        "type": "github"
      },
      "original": {
        "owner": "NixOS",
        "ref": "nixos-unstable",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "root": {
      "inputs": {
        "hyprlang": "hyprlang",
        "hyprutils": "hyprutils",
        "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
}
0707010000000A000081A4000000000000000000000001669CF85C0000057D000000000000000000000000000000000000001900000000hyprlock-0.4.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";

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

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

    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);
  };
}
0707010000000B000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001300000000hyprlock-0.4.1/nix0707010000000C000081A4000000000000000000000001669CF85C0000030D000000000000000000000000000000000000001F00000000hyprlock-0.4.1/nix/default.nix{
  lib,
  stdenv,
  cmake,
  pkg-config,
  cairo,
  file,
  libdrm,
  libGL,
  libjpeg,
  libwebp,
  libxkbcommon,
  mesa,
  hyprlang,
  hyprutils,
  pam,
  pango,
  wayland,
  wayland-protocols,
  version ? "git",
}:
stdenv.mkDerivation {
  pname = "hyprlock";
  inherit version;

  src = ../.;

  nativeBuildInputs = [
    cmake
    pkg-config
  ];

  buildInputs = [
    cairo
    file
    libdrm
    libGL
    libjpeg
    libwebp
    libxkbcommon
    mesa
    hyprlang
    hyprutils
    pam
    pango
    wayland
    wayland-protocols
  ];

  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";
  };
}
0707010000000D000081A4000000000000000000000001669CF85C000002D5000000000000000000000000000000000000002000000000hyprlock-0.4.1/nix/overlays.nix{
  lib,
  inputs,
}: 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.hyprlang.overlays.default
    inputs.hyprutils.overlays.default
    (final: prev: {
      hyprlock = prev.callPackage ./default.nix {
        stdenv = prev.gcc13Stdenv;
        version = version + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty");
        inherit (final) hyprlang;
      };
    })
  ];
}
0707010000000E000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001300000000hyprlock-0.4.1/pam0707010000000F000081A4000000000000000000000001669CF85C0000007E000000000000000000000000000000000000001C00000000hyprlock-0.4.1/pam/hyprlock# PAM configuration file for hyprlock
# the 'login' configuration file (see /etc/pam.d/login)

auth        include     login

07070100000010000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001900000000hyprlock-0.4.1/protocols07070100000011000081A4000000000000000000000001669CF85C00002585000000000000000000000000000000000000003800000000hyprlock-0.4.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>
07070100000012000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001300000000hyprlock-0.4.1/src07070100000013000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001A00000000hyprlock-0.4.1/src/config07070100000014000081A4000000000000000000000001669CF85C000055C9000000000000000000000000000000000000002C00000000hyprlock-0.4.1/src/config/ConfigManager.cpp#include "ConfigManager.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "src/helpers/Log.hpp"
#include <hyprutils/path/Path.hpp>
#include <filesystem>
#include <glob.h>
#include <cstring>
#include <mutex>

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

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

    m_config.addConfigValue("general:disable_loading_bar", Hyprlang::INT{0});
    m_config.addConfigValue("general:text_trim", Hyprlang::INT{1});
    m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0});
    m_config.addConfigValue("general:grace", Hyprlang::INT{0});
    m_config.addConfigValue("general:no_fade_in", Hyprlang::INT{0});
    m_config.addConfigValue("general:no_fade_out", 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:pam_module", Hyprlang::STRING{"hyprlock"});

    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.addSpecialCategory("shape", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
    m_config.addSpecialConfigValue("shape", "monitor", Hyprlang::STRING{""});
    m_config.addSpecialConfigValue("shape", "size", Hyprlang::VEC2{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", Hyprlang::INT{0xFF00CFE6});
    m_config.addSpecialConfigValue("shape", "color", Hyprlang::INT{0xFF111111});
    m_config.addSpecialConfigValue("shape", "position", Hyprlang::VEC2{0, 80});
    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");

    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", Hyprlang::INT{0xFFDDDDDD});
    m_config.addSpecialConfigValue("image", "position", Hyprlang::VEC2{0, 200});
    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");

    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", Hyprlang::VEC2{400, 90});
    m_config.addSpecialConfigValue("input-field", "inner_color", Hyprlang::INT{0xFFDDDDDD});
    m_config.addSpecialConfigValue("input-field", "outer_color", Hyprlang::INT{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", "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", "halign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("input-field", "valign", Hyprlang::STRING{"center"});
    m_config.addSpecialConfigValue("input-field", "position", Hyprlang::VEC2{0, -20});
    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", "rounding", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("input-field", "check_color", Hyprlang::INT{0xFFCC8822});
    m_config.addSpecialConfigValue("input-field", "fail_color", Hyprlang::INT{0xFFCC2222});
    m_config.addSpecialConfigValue("input-field", "fail_text", Hyprlang::STRING{"<i>$FAIL</i>"});
    m_config.addSpecialConfigValue("input-field", "fail_timeout", Hyprlang::INT{2000});
    m_config.addSpecialConfigValue("input-field", "fail_transition", Hyprlang::INT{300});
    m_config.addSpecialConfigValue("input-field", "capslock_color", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("input-field", "numlock_color", Hyprlang::INT{-1});
    m_config.addSpecialConfigValue("input-field", "bothlock_color", Hyprlang::INT{-1});
    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", Hyprlang::VEC2{400, 90});
    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{"none"});
    m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"none"});
    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");

    m_config.registerHandler(&::handleSource, "source", {false});

    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
}

std::mutex   configMtx;

void* const* CConfigManager::getValuePtr(const std::string& name) {
    std::lock_guard<std::mutex> lg(configMtx);
    return m_config.getConfigValuePtr(name.c_str())->getDataStaticPtr();
}

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())                                                                                            \
    }

    //
    auto keys = m_config.listKeysForSpecialCategory("background");
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            "background",
            std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("background", "monitor", k.c_str())),
            {
                {"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())},
            }
        });
        // clang-format on
    }

    //
    keys = m_config.listKeysForSpecialCategory("shape");
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            "shape",
            std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("shape", "monitor", k.c_str())),
            {
                {"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"),
            }
        });
        // clang-format on
    }

    //
    keys = m_config.listKeysForSpecialCategory("image");
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            "image",
            std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("image", "monitor", k.c_str())),
            {
                {"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"),
            }
        });
        // clang-format on
    }

    keys = m_config.listKeysForSpecialCategory("input-field");
    for (auto& k : keys) {
        // clang-format off
        result.push_back(CConfigManager::SWidgetConfig{
            "input-field",
            std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("input-field", "monitor", k.c_str())),
            {
                {"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())},
                {"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())},
                {"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())},
                {"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())},
                {"fail_timeout", m_config.getSpecialConfigValue("input-field", "fail_timeout", k.c_str())},
                {"fail_transition", m_config.getSpecialConfigValue("input-field", "fail_transition", 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{
            "label",
            std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("label", "monitor", k.c_str())),
            {
                {"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"),
            }
        });
        // 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 {};
}
07070100000015000081A4000000000000000000000001669CF85C0000030B000000000000000000000000000000000000002C00000000hyprlock-0.4.1/src/config/ConfigManager.hpp#pragma once

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

class CConfigManager {
  public:
    CConfigManager(std::string configPath);
    void         init();
    void* const* getValuePtr(const std::string& name);

    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::string                configCurrentPath;

  private:
    Hyprlang::CConfig m_config;
};

inline std::unique_ptr<CConfigManager> g_pConfigManager;
07070100000016000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001800000000hyprlock-0.4.1/src/core07070100000017000081A4000000000000000000000001669CF85C0000180E000000000000000000000000000000000000002100000000hyprlock-0.4.1/src/core/Auth.cpp#include "Auth.hpp"
#include "hyprlock.hpp"
#include "../helpers/Log.hpp"
#include "src/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 = (CAuth::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;
                    g_pAuth->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); break;
        }
    }

    *resp = pamReply;
    return PAM_SUCCESS;
}

CAuth::CAuth() {
    static auto* const PPAMMODULE = (Hyprlang::STRING*)(g_pConfigManager->getValuePtr("general:pam_module"));
    m_sPamModule                  = *PPAMMODULE;

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

static void passwordCheckTimerCallback(std::shared_ptr<CTimer> self, void* data) {
    g_pHyprlock->onPasswordCheckTimer();
}

void CAuth::start() {
    std::thread([this]() {
        resetConversation();

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

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

        const auto AUTHENTICATED = auth();
        m_bAuthenticated         = AUTHENTICATED;

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

        g_pHyprlock->addTimer(std::chrono::milliseconds(1), passwordCheckTimerCallback, nullptr);
    }).detach();
}

bool CAuth::auth() {
    const pam_conv localConv   = {conv, (void*)&m_sConversationState};
    pam_handle_t*  handle      = NULL;
    auto           uidPassword = getpwuid(getuid());

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

bool CAuth::isAuthenticated() {
    return m_bAuthenticated;
}

// clearing the input must be done from the main thread
static void clearInputTimerCallback(std::shared_ptr<CTimer> self, void* data) {
    g_pHyprlock->clearPasswordBuffer();
}

void CAuth::waitForInput() {
    g_pHyprlock->addTimer(std::chrono::milliseconds(1), clearInputTimerCallback, nullptr);

    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 CAuth::submitInput(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> CAuth::getLastFailText() {
    return m_sConversationState.failText.empty() ? std::nullopt : std::optional(m_sConversationState.failText);
}

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

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

void CAuth::terminate() {
    m_sConversationState.inputSubmittedCondition.notify_all();
}

void CAuth::resetConversation() {
    m_sConversationState.input             = "";
    m_sConversationState.waitingForPamAuth = false;
    m_sConversationState.inputRequested    = false;
}
07070100000018000081A4000000000000000000000001669CF85C0000055D000000000000000000000000000000000000002100000000hyprlock-0.4.1/src/core/Auth.hpp#pragma once

#include <memory>
#include <optional>
#include <string>
#include <mutex>
#include <condition_variable>

class CAuth {
  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;
    };

    CAuth();

    void                       start();
    bool                       auth();
    bool                       isAuthenticated();

    void                       waitForInput();
    void                       submitInput(std::string input);

    std::optional<std::string> getLastFailText();
    std::optional<std::string> getLastPrompt();

    bool                       checkWaiting();

    void                       terminate();

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

  private:
    SPamConversationState m_sConversationState;

    bool                  m_bBlockInput    = true;
    bool                  m_bAuthenticated = false;

    std::string           m_sPamModule;

    void                  resetConversation();
};

inline std::unique_ptr<CAuth> g_pAuth;
07070100000019000081A4000000000000000000000001669CF85C00000247000000000000000000000000000000000000002800000000hyprlock-0.4.1/src/core/CursorShape.cpp#include "CursorShape.hpp"
#include "hyprlock.hpp"

CCursorShape::CCursorShape(wp_cursor_shape_manager_v1* mgr) : mgr(mgr) {
    if (!g_pHyprlock->m_pPointer)
        return;

    dev = wp_cursor_shape_manager_v1_get_pointer(mgr, g_pHyprlock->m_pPointer);
}

void CCursorShape::setShape(const uint32_t serial, const wp_cursor_shape_device_v1_shape shape) {
    if (!dev)
        return;

    wp_cursor_shape_device_v1_set_shape(dev, serial, shape);
}

void CCursorShape::hideCursor(const uint32_t serial) {
    wl_pointer_set_cursor(g_pHyprlock->m_pPointer, serial, nullptr, 0, 0);
}0707010000001A000081A4000000000000000000000001669CF85C00000193000000000000000000000000000000000000002800000000hyprlock-0.4.1/src/core/CursorShape.hpp#pragma once

#include <wayland-client.h>
#include "cursor-shape-v1-protocol.h"

class CCursorShape {
  public:
    CCursorShape(wp_cursor_shape_manager_v1* mgr);

    void setShape(const uint32_t serial, const wp_cursor_shape_device_v1_shape shape);
    void hideCursor(const uint32_t serial);

  private:
    wp_cursor_shape_manager_v1* mgr = nullptr;
    wp_cursor_shape_device_v1*  dev = nullptr;
};0707010000001B000081A4000000000000000000000001669CF85C00000B01000000000000000000000000000000000000002000000000hyprlock-0.4.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 == NULL)
        throw std::runtime_error("Failed to get eglGetPlatformDisplayEXT");

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

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

    if (eglInitialize(eglDisplay, NULL, NULL) == 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);
}0707010000001C000081A4000000000000000000000001669CF85C00000216000000000000000000000000000000000000002000000000hyprlock-0.4.1/src/core/Egl.hpp#pragma once

#include <wayland-client.h>
#include <memory>

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

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

    EGLDisplay                               eglDisplay;
    EGLConfig                                eglConfig;
    EGLContext                               eglContext;

    PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;

    void                                     makeCurrent(EGLSurface surf);
};

inline std::unique_ptr<CEGL> g_pEGL;0707010000001D000081A4000000000000000000000001669CF85C00001448000000000000000000000000000000000000002800000000hyprlock-0.4.1/src/core/LockSurface.cpp#include "LockSurface.hpp"
#include "hyprlock.hpp"
#include "../helpers/Log.hpp"
#include "Egl.hpp"
#include "../renderer/Renderer.hpp"

static void handleConfigure(void* data, ext_session_lock_surface_v1* surf, uint32_t serial, uint32_t width, uint32_t height) {
    const auto PSURF = (CSessionLockSurface*)data;
    PSURF->configure({width, height}, serial);
}

static const ext_session_lock_surface_v1_listener lockListener = {
    .configure = handleConfigure,
};

static void handlePreferredScale(void* data, wp_fractional_scale_v1* wp_fractional_scale_v1, uint32_t scale) {
    const auto PSURF       = (CSessionLockSurface*)data;
    PSURF->fractionalScale = scale / 120.0;
    Debug::log(LOG, "Got fractional scale: {}", PSURF->fractionalScale);

    if (PSURF->readyForFrame)
        PSURF->onScaleUpdate();
}

static const wp_fractional_scale_v1_listener fsListener = {
    .preferred_scale = handlePreferredScale,
};

CSessionLockSurface::~CSessionLockSurface() {
    if (fractional) {
        wp_viewport_destroy(viewport);
        wp_fractional_scale_v1_destroy(fractional);
    }

    if (eglWindow)
        wl_egl_window_destroy(eglWindow);

    if (lockSurface)
        ext_session_lock_surface_v1_destroy(lockSurface);

    if (surface)
        wl_surface_destroy(surface);

    if (frameCallback)
        wl_callback_destroy(frameCallback);
}

CSessionLockSurface::CSessionLockSurface(COutput* output) : output(output) {
    surface = wl_compositor_create_surface(g_pHyprlock->getCompositor());

    if (!surface) {
        Debug::log(CRIT, "Couldn't create wl_surface");
        exit(1);
    }

    const auto PFRACTIONALMGR = g_pHyprlock->getFractionalMgr();
    const auto PVIEWPORTER    = g_pHyprlock->getViewporter();
    if (PFRACTIONALMGR && PVIEWPORTER) {
        fractional = wp_fractional_scale_manager_v1_get_fractional_scale(PFRACTIONALMGR, surface);
        if (fractional) {
            wp_fractional_scale_v1_add_listener(fractional, &fsListener, this);
            viewport = wp_viewporter_get_viewport(PVIEWPORTER, surface);
        }
    }

    if (!PFRACTIONALMGR || !fractional)
        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 = ext_session_lock_v1_get_lock_surface(g_pHyprlock->getSessionLock(), surface, output->output);

    if (!lockSurface) {
        Debug::log(CRIT, "Couldn't create ext_session_lock_surface_v1");
        exit(1);
    }

    ext_session_lock_surface_v1_add_listener(lockSurface, &lockListener, this);
}

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

    const bool sameSerial = serial == serial_;

    serial      = serial_;
    logicalSize = size_;

    if (fractional) {
        size = (size_ * fractionalScale).floor();
        wp_viewport_set_destination(viewport, logicalSize.x, logicalSize.y);
    } else {
        size = size_;
    }

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

    if (!sameSerial)
        ext_session_lock_surface_v1_ack_configure(lockSurface, serial);

    wl_surface_set_buffer_scale(surface, 1);
    wl_surface_damage_buffer(surface, 0, 0, 0xFFFF, 0xFFFF);

    if (!eglWindow) {
        eglWindow = wl_egl_window_create(surface, size.x, size.y);
        if (!eglWindow) {
            Debug::log(CRIT, "Couldn't create eglWindow");
            exit(1);
        }
    } 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);
        if (!eglSurface) {
            Debug::log(CRIT, "Couldn't create eglSurface: {}", (int)eglGetError());
            // Clean up resources to prevent leaks
            wl_egl_window_destroy(eglWindow);
            eglWindow = nullptr;
            exit(1); // Consider graceful exit or fallback
        }
    }

    readyForFrame = true;

    render();
}

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

static void handleDone(void* data, wl_callback* wl_callback, uint32_t callback_data) {
    const auto PSURF = (CSessionLockSurface*)data;

    if (g_pHyprlock->m_bTerminate)
        return;

    PSURF->onCallback();
}

static const wl_callback_listener callbackListener = {
    .done = handleDone,
};

void CSessionLockSurface::render() {
    Debug::log(TRACE, "render lock");

    if (frameCallback || !readyForFrame) {
        needsFrame = true;
        return;
    }

    const auto FEEDBACK = g_pRenderer->renderLock(*this);
    frameCallback       = wl_surface_frame(surface);
    wl_callback_add_listener(frameCallback, &callbackListener, this);

    eglSwapBuffers(g_pEGL->eglDisplay, eglSurface);

    needsFrame = FEEDBACK.needsFrame;
}

void CSessionLockSurface::onCallback() {
    wl_callback_destroy(frameCallback);
    frameCallback = nullptr;

    if (needsFrame && !g_pHyprlock->m_bTerminate && g_pEGL) {
        needsFrame = false;
        render();
    }
}0707010000001E000081A4000000000000000000000001669CF85C000004F8000000000000000000000000000000000000002800000000hyprlock-0.4.1/src/core/LockSurface.hpp#pragma once

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

class COutput;
class CRenderer;

class CSessionLockSurface {
  public:
    CSessionLockSurface(COutput* output);
    ~CSessionLockSurface();

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

    bool  readyForFrame = false;

    float fractionalScale = 1.0;

    void  render();
    void  onCallback();
    void  onScaleUpdate();

  private:
    COutput*                     output      = nullptr;
    wl_surface*                  surface     = nullptr;
    ext_session_lock_surface_v1* lockSurface = nullptr;
    uint32_t                     serial      = 0;
    wl_egl_window*               eglWindow   = nullptr;
    Vector2D                     size;
    Vector2D                     logicalSize;
    EGLSurface                   eglSurface = nullptr;
    wp_fractional_scale_v1*      fractional = nullptr;
    wp_viewport*                 viewport   = nullptr;

    bool                         needsFrame = false;

    // wayland callbacks
    wl_callback* frameCallback = nullptr;

    friend class CRenderer;
};0707010000001F000081A4000000000000000000000001669CF85C000009BC000000000000000000000000000000000000002300000000hyprlock-0.4.1/src/core/Output.cpp#include "Output.hpp"
#include "../helpers/Log.hpp"
#include "hyprlock.hpp"
#include "../renderer/Renderer.hpp"

static void handleGeometry(void* data, wl_output* output, 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) {
    const auto POUTPUT = (COutput*)data;
    POUTPUT->transform = (wl_output_transform)transform;

    Debug::log(LOG, "output {} make {} model {}", POUTPUT->name, make ? make : "", model ? model : "");
}

static void handleMode(void* data, wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
    const auto POUTPUT = (COutput*)data;

    // handle portrait mode and flipped cases
    if (POUTPUT->transform % 2 == 1)
        POUTPUT->size = {height, width};
    else
        POUTPUT->size = {width, height};
}

static void handleDone(void* data, wl_output* output) {
    const auto POUTPUT = (COutput*)data;
    Debug::log(LOG, "output {} done", POUTPUT->name);
    if (g_pHyprlock->m_bLocked && !POUTPUT->sessionLockSurface) {
        // if we are already locked, create a surface dynamically
        Debug::log(LOG, "Creating a surface dynamically for output as we are already locked");
        POUTPUT->sessionLockSurface = std::make_unique<CSessionLockSurface>(POUTPUT);
    }
}

static void handleScale(void* data, wl_output* output, int32_t factor) {
    const auto POUTPUT = (COutput*)data;
    POUTPUT->scale     = factor;
}

static void handleName(void* data, wl_output* output, const char* name) {
    const auto POUTPUT  = (COutput*)data;
    POUTPUT->stringName = std::string{name} + POUTPUT->stringName;
    POUTPUT->stringPort = std::string{name};
    Debug::log(LOG, "output {} name {}", POUTPUT->name, name);
}

static void handleDescription(void* data, wl_output* output, const char* description) {
    const auto POUTPUT  = (COutput*)data;
    POUTPUT->stringDesc = description ? std::string{description} : "";
    Debug::log(LOG, "output {} description {}", POUTPUT->name, POUTPUT->stringDesc);
}

static const wl_output_listener outputListener = {
    .geometry    = handleGeometry,
    .mode        = handleMode,
    .done        = handleDone,
    .scale       = handleScale,
    .name        = handleName,
    .description = handleDescription,
};

COutput::COutput(wl_output* output, uint32_t name) : name(name), output(output) {
    wl_output_add_listener(output, &outputListener, this);
}
07070100000020000081A4000000000000000000000001669CF85C00000329000000000000000000000000000000000000002300000000hyprlock-0.4.1/src/core/Output.hpp#pragma once

#include <wayland-client.h>
#include "../helpers/Vector2D.hpp"
#include "LockSurface.hpp"
#include <memory>

class COutput {
  public:
    COutput(wl_output* output, uint32_t name);

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

    std::unique_ptr<CSessionLockSurface> sessionLockSurface;

    wl_output*                           output = nullptr;

  private:
};
07070100000021000081A4000000000000000000000001669CF85C00000302000000000000000000000000000000000000002200000000hyprlock-0.4.1/src/core/Timer.cpp#include "Timer.hpp"

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

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

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

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

void CTimer::call(std::shared_ptr<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;
}
07070100000022000081A4000000000000000000000001669CF85C0000032F000000000000000000000000000000000000002200000000hyprlock-0.4.1/src/core/Timer.hpp#pragma once

#include <chrono>
#include <functional>

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

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

    float leftMs();

    bool  cancelled();
    void  call(std::shared_ptr<CTimer> self);

  private:
    std::function<void(std::shared_ptr<CTimer> self, void* data)> cb;
    void*                                                         data = nullptr;
    std::chrono::system_clock::time_point                         expires;
    bool                                                          wasCancelled     = false;
    bool                                                          allowForceUpdate = false;
};
07070100000023000081A4000000000000000000000001669CF85C00009B80000000000000000000000000000000000000002500000000hyprlock-0.4.1/src/core/hyprlock.cpp#include "hyprlock.hpp"
#include "../helpers/Log.hpp"
#include "../config/ConfigManager.hpp"
#include "../renderer/Renderer.hpp"
#include "Auth.hpp"
#include "Egl.hpp"
#include "linux-dmabuf-unstable-v1-protocol.h"
#include <sys/wait.h>
#include <sys/poll.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <xf86drm.h>
#include <filesystem>
#include <fstream>
#include <algorithm>

CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender) {
    m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
    if (!m_sWaylandState.display) {
        Debug::log(CRIT, "Couldn't connect to a wayland compositor");
        exit(1);
    }

    g_pEGL = std::make_unique<CEGL>(m_sWaylandState.display);

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

    if (!immediate) {
        const auto PGRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace");
        m_tGraceEnds      = **PGRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**PGRACE) : std::chrono::system_clock::from_time_t(0);
    } else {
        m_tGraceEnds = std::chrono::system_clock::from_time_t(0);
    }

    const auto PIMMEDIATERENDER = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:immediate_render");
    m_bImmediateRender          = immediateRender || **PIMMEDIATERENDER;
}

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

    if (m_pXKBState)
        xkb_state_unref(m_pXKBState);

    if (m_pXKBKeymap)
        xkb_keymap_unref(m_pXKBKeymap);
}

// wl_seat

static void                   handleCapabilities(void* data, wl_seat* wl_seat, uint32_t capabilities);
static void                   handleName(void* data, struct wl_seat* wl_seat, const char* name);

inline const wl_seat_listener seatListener = {
    .capabilities = handleCapabilities,
    .name         = handleName,
};

// end wl_seat

// dmabuf

static void handleDMABUFFormat(void* data, struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1, uint32_t format) {
    ;
}

static void handleDMABUFModifier(void* data, struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) {
    g_pHyprlock->dma.dmabufMods.push_back({format, (((uint64_t)modifier_hi) << 32) | modifier_lo});
}

inline const zwp_linux_dmabuf_v1_listener dmabufListener = {
    .format   = handleDMABUFFormat,
    .modifier = handleDMABUFModifier,
};

static void dmabufFeedbackMainDevice(void* data, zwp_linux_dmabuf_feedback_v1* feedback, wl_array* device_arr) {
    Debug::log(LOG, "[core] dmabufFeedbackMainDevice");

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

    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) {
        Debug::log(WARN, "[dmabuf] unable to open main device?");
        exit(1);
    }

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

static void dmabufFeedbackFormatTable(void* data, zwp_linux_dmabuf_feedback_v1* feedback, int fd, uint32_t size) {
    Debug::log(TRACE, "[core] dmabufFeedbackFormatTable");

    g_pHyprlock->dma.dmabufMods.clear();

    g_pHyprlock->dma.formatTable = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);

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

    g_pHyprlock->dma.formatTableSize = size;
}

static void dmabufFeedbackDone(void* data, zwp_linux_dmabuf_feedback_v1* feedback) {
    Debug::log(TRACE, "[core] dmabufFeedbackDone");

    if (g_pHyprlock->dma.formatTable)
        munmap(g_pHyprlock->dma.formatTable, g_pHyprlock->dma.formatTableSize);

    g_pHyprlock->dma.formatTable     = nullptr;
    g_pHyprlock->dma.formatTableSize = 0;
}

static void dmabufFeedbackTrancheTargetDevice(void* data, zwp_linux_dmabuf_feedback_v1* feedback, 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 (g_pHyprlock->dma.gbmDevice) {
        drmDevice* drmDevRenderer = NULL;
        drmGetDevice2(gbm_device_get_fd(g_pHyprlock->dma.gbmDevice), /* flags */ 0, &drmDevRenderer);
        g_pHyprlock->dma.deviceUsed = drmDevicesEqual(drmDevRenderer, drmDev);
    } else {
        g_pHyprlock->dma.gbmDevice  = g_pHyprlock->createGBMDevice(drmDev);
        g_pHyprlock->dma.deviceUsed = g_pHyprlock->dma.gbm;
    }
}

static void dmabufFeedbackTrancheFlags(void* data, zwp_linux_dmabuf_feedback_v1* feedback, uint32_t flags) {
    ;
}

static void dmabufFeedbackTrancheFormats(void* data, zwp_linux_dmabuf_feedback_v1* feedback, wl_array* indices) {
    Debug::log(TRACE, "[core] dmabufFeedbackTrancheFormats");

    if (!g_pHyprlock->dma.deviceUsed || !g_pHyprlock->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
    assert(sizeof(fm_entry) == 16);

    uint32_t  n_modifiers = g_pHyprlock->dma.formatTableSize / sizeof(fm_entry);
    fm_entry* fm_entry    = (struct fm_entry*)g_pHyprlock->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);

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

static void dmabufFeedbackTrancheDone(void* data, struct zwp_linux_dmabuf_feedback_v1* zwp_linux_dmabuf_feedback_v1) {
    Debug::log(TRACE, "[core] dmabufFeedbackTrancheDone");

    g_pHyprlock->dma.deviceUsed = false;
}

inline const zwp_linux_dmabuf_feedback_v1_listener dmabufFeedbackListener = {
    .done                  = dmabufFeedbackDone,
    .format_table          = dmabufFeedbackFormatTable,
    .main_device           = dmabufFeedbackMainDevice,
    .tranche_done          = dmabufFeedbackTrancheDone,
    .tranche_target_device = dmabufFeedbackTrancheTargetDevice,
    .tranche_formats       = dmabufFeedbackTrancheFormats,
    .tranche_flags         = dmabufFeedbackTrancheFlags,
};

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

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

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

// end dmabuf

// wl_registry

static void handleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
    g_pHyprlock->onGlobal(data, registry, name, interface, version);
}

static void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name) {
    g_pHyprlock->onGlobalRemoved(data, registry, name);
}

inline const wl_registry_listener registryListener = {
    .global        = handleGlobal,
    .global_remove = handleGlobalRemove,
};

void CHyprlock::onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
    const std::string IFACE = interface;
    Debug::log(LOG, "  | got iface: {} v{}", IFACE, version);

    if (IFACE == ext_session_lock_manager_v1_interface.name) {
        m_sWaylandState.sessionLock = (ext_session_lock_manager_v1*)wl_registry_bind(registry, name, &ext_session_lock_manager_v1_interface, version);
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    } else if (IFACE == wl_seat_interface.name) {
        if (m_sWaylandState.seat) {
            Debug::log(WARN, "Hyprlock does not support multi-seat configurations. Only binding to the first seat.");
            return;
        }

        m_sWaylandState.seat = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, version);
        wl_seat_add_listener(m_sWaylandState.seat, &seatListener, nullptr);
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    } else if (IFACE == wl_output_interface.name) {
        m_vOutputs.emplace_back(std::make_unique<COutput>((wl_output*)wl_registry_bind(registry, name, &wl_output_interface, version), name));
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    } else if (IFACE == wp_cursor_shape_manager_v1_interface.name) {
        m_pCursorShape = std::make_unique<CCursorShape>((wp_cursor_shape_manager_v1*)wl_registry_bind(registry, name, &wp_cursor_shape_manager_v1_interface, version));
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    } else if (IFACE == wl_compositor_interface.name) {
        m_sWaylandState.compositor = (wl_compositor*)wl_registry_bind(registry, name, &wl_compositor_interface, version);
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    } else if (IFACE == wp_fractional_scale_manager_v1_interface.name) {
        m_sWaylandState.fractional = (wp_fractional_scale_manager_v1*)wl_registry_bind(registry, name, &wp_fractional_scale_manager_v1_interface, version);
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    } else if (IFACE == wp_viewporter_interface.name) {
        m_sWaylandState.viewporter = (wp_viewporter*)wl_registry_bind(registry, name, &wp_viewporter_interface, version);
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    } else if (IFACE == zwp_linux_dmabuf_v1_interface.name) {
        if (version < 4) {
            Debug::log(ERR, "cannot use linux_dmabuf with ver < 4");
            return;
        }

        dma.linuxDmabuf         = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version);
        dma.linuxDmabufFeedback = zwp_linux_dmabuf_v1_get_default_feedback((zwp_linux_dmabuf_v1*)dma.linuxDmabuf);
        zwp_linux_dmabuf_feedback_v1_add_listener((zwp_linux_dmabuf_feedback_v1*)dma.linuxDmabufFeedback, &dmabufFeedbackListener, nullptr);
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    } else if (IFACE == zwlr_screencopy_manager_v1_interface.name) {
        m_sWaylandState.screencopy = (zwlr_screencopy_manager_v1*)wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, version);
        Debug::log(LOG, "   > Bound to {} v{}", IFACE, version);
    }
}

void CHyprlock::onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name) {
    Debug::log(LOG, "  | removed iface {}", name);
    auto outputIt = std::find_if(m_vOutputs.begin(), m_vOutputs.end(), [name](const auto& other) { return other->name == name; });
    if (outputIt != m_vOutputs.end()) {
        g_pRenderer->removeWidgetsFor(outputIt->get()->sessionLockSurface.get());
        m_vOutputs.erase(outputIt);
    }
}

// end wl_registry

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, NULL);
}

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

static void forceUpdateTimers() {
    for (auto& t : g_pHyprlock->getTimers()) {
        if (t->canForceUpdate()) {
            t->call(t);
            t->cancel();
        }
    }
}

static void handleForceUpdateSignal(int sig) {
    if (sig == SIGUSR2) {
        forceUpdateTimers();
    }
}

static void handlePollTerminate(int sig) {
    ;
}

static void handleCriticalSignal(int sig) {
    g_pHyprlock->attemptRestoreOnDeath();

    // remove our handlers
    struct sigaction sa;
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGABRT, &sa, NULL);
    sigaction(SIGSEGV, &sa, NULL);

    abort();
}

void CHyprlock::run() {
    m_sWaylandState.registry = wl_display_get_registry(m_sWaylandState.display);

    wl_registry_add_listener(m_sWaylandState.registry, &registryListener, nullptr);

    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 = std::make_unique<CRenderer>();

    const auto         CURRENTDESKTOP = getenv("XDG_CURRENT_DESKTOP");
    const auto         SZCURRENTD     = std::string{CURRENTDESKTOP ? CURRENTDESKTOP : ""};
    static auto* const PNOFADEOUT     = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:no_fade_out");
    const bool         NOFADEOUT      = **PNOFADEOUT;

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

    // Hyprland violates the protocol a bit to allow for this.
    if (SZCURRENTD != "Hyprland") {
        while (!g_pRenderer->asyncResourceGatherer->gathered) {
            wl_display_flush(m_sWaylandState.display);
            if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
                wl_display_read_events(m_sWaylandState.display);
                wl_display_dispatch_pending(m_sWaylandState.display);
            } else {
                wl_display_dispatch(m_sWaylandState.display);
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
    }

    acquireSessionLock();

    if (m_bTerminate) // Recieved finished
        exit(1);

    g_pAuth = std::make_unique<CAuth>();
    g_pAuth->start();

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

    createSessionLockSurfaces();

    pollfd pollfds[] = {
        {
            .fd     = wl_display_get_fd(m_sWaylandState.display),
            .events = POLLIN,
        },
    };

    std::thread pollThr([this, &pollfds]() {
        while (!m_bTerminate) {
            int ret = poll(pollfds, 1, 5000 /* 5 seconds, reasonable. Just in case we need to terminate and the signal fails */);

            if (ret < 0) {
                if (errno == EINTR)
                    continue;

                Debug::log(CRIT, "[core] Polling fds failed with {}", errno);
                attemptRestoreOnDeath();
                m_bTerminate = true;
                exit(1);
            }

            for (size_t i = 0; i < 1; ++i) {
                if (pollfds[i].revents & POLLHUP) {
                    Debug::log(CRIT, "[core] Disconnected from pollfd id {}", i);
                    attemptRestoreOnDeath();
                    m_bTerminate = true;
                    exit(1);
                }
            }

            if (ret != 0) {
                Debug::log(TRACE, "[core] got poll event");
                std::lock_guard<std::mutex> lg2(m_sLoopState.eventLoopMutex);
                m_sLoopState.event = true;
                m_sLoopState.loopCV.notify_all();
            }
        }
    });

    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);
                if (TIME < least)
                    least = TIME;
            }

            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

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

        if (!NOFADEOUT && m_bFadeStarted && std::chrono::system_clock::now() > m_tFadeEnds) {
            releaseSessionLock();
            break;
        }

        if (m_bTerminate)
            break;

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

        m_sLoopState.event = false;

        if (pollfds[0].revents & POLLIN /* wl */) {
            Debug::log(TRACE, "got wl event");
            wl_display_flush(m_sWaylandState.display);
            if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
                wl_display_read_events(m_sWaylandState.display);
                wl_display_dispatch_pending(m_sWaylandState.display);
            } else {
                wl_display_dispatch(m_sWaylandState.display);
            }
        }

        // finalize wayland dispatching. Dispatch pending on the queue
        int ret = 0;
        do {
            ret = wl_display_dispatch_pending(m_sWaylandState.display);
            wl_display_flush(m_sWaylandState.display);
        } while (ret > 0 && !m_bTerminate);

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

        std::vector<std::shared_ptr<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();

        if (!NOFADEOUT && m_bFadeStarted && std::chrono::system_clock::now() > m_tFadeEnds) {
            releaseSessionLock();
            break;
        }
    }

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

    m_vOutputs.clear();
    g_pEGL.reset();
    g_pRenderer = nullptr;

    xkb_context_unref(m_pXKBContext);

    wl_display_disconnect(m_sWaylandState.display);

    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() {
    static auto* const PNOFADEOUT     = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:no_fade_out");
    const auto         CURRENTDESKTOP = getenv("XDG_CURRENT_DESKTOP");
    const auto         SZCURRENTD     = std::string{CURRENTDESKTOP ? CURRENTDESKTOP : ""};

    if (**PNOFADEOUT || SZCURRENTD != "Hyprland") {
        releaseSessionLock();
        return;
    }

    m_tFadeEnds    = std::chrono::system_clock::now() + std::chrono::milliseconds(500);
    m_bFadeStarted = true;

    renderAllOutputs();
}

bool CHyprlock::isUnlocked() {
    return m_bFadeStarted || m_bTerminate;
}

// wl_seat

static void handlePointerEnter(void* data, struct wl_pointer* wl_pointer, uint32_t serial, struct wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
    if (!g_pHyprlock->m_pCursorShape)
        return;

    static auto* const PHIDE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:hide_cursor");

    if (**PHIDE)
        g_pHyprlock->m_pCursorShape->hideCursor(serial);
    else
        g_pHyprlock->m_pCursorShape->setShape(serial, wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);

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

static void handlePointerLeave(void* data, struct wl_pointer* wl_pointer, uint32_t serial, struct wl_surface* surface) {
    ;
}

static void handlePointerAxis(void* data, wl_pointer* wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
    // ignored
}

static void handlePointerMotion(void* data, struct wl_pointer* wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
    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();
    }
}

static void handlePointerButton(void* data, struct wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state) {
    ;
}

static void handleFrame(void* data, struct wl_pointer* wl_pointer) {
    ;
}

static void handleAxisSource(void* data, struct wl_pointer* wl_pointer, uint32_t axis_source) {
    ;
}

static void handleAxisStop(void* data, struct wl_pointer* wl_pointer, uint32_t time, uint32_t axis) {
    ;
}

static void handleAxisDiscrete(void* data, struct wl_pointer* wl_pointer, uint32_t axis, int32_t discrete) {
    ;
}

static void handleAxisValue120(void* data, struct wl_pointer* wl_pointer, uint32_t axis, int32_t value120) {
    ;
}

static void handleAxisRelativeDirection(void* data, struct wl_pointer* wl_pointer, uint32_t axis, uint32_t direction) {
    ;
}

inline const wl_pointer_listener pointerListener = {
    .enter                   = handlePointerEnter,
    .leave                   = handlePointerLeave,
    .motion                  = handlePointerMotion,
    .button                  = handlePointerButton,
    .axis                    = handlePointerAxis,
    .frame                   = handleFrame,
    .axis_source             = handleAxisSource,
    .axis_stop               = handleAxisStop,
    .axis_discrete           = handleAxisDiscrete,
    .axis_value120           = handleAxisValue120,
    .axis_relative_direction = handleAxisRelativeDirection,
};

static void handleKeyboardKeymap(void* data, wl_keyboard* wl_keyboard, uint format, int fd, uint size) {
    if (!g_pHyprlock->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(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (buf == MAP_FAILED) {
        Debug::log(ERR, "Failed to mmap xkb keymap: {}", errno);
        return;
    }

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

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

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

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

static void handleKeyboardKey(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
    g_pHyprlock->onKey(key, state == WL_KEYBOARD_KEY_STATE_PRESSED);
}

static void handleKeyboardEnter(void* data, wl_keyboard* wl_keyboard, uint serial, wl_surface* surface, wl_array* keys) {
    ;
}

static void handleKeyboardLeave(void* data, wl_keyboard* wl_keyboard, uint serial, wl_surface* surface) {
    ;
}

static void handleKeyboardModifiers(void* data, wl_keyboard* wl_keyboard, uint serial, uint mods_depressed, uint mods_latched, uint mods_locked, uint group) {
    if (!g_pHyprlock->m_pXKBState)
        return;

    if (group != g_pHyprlock->m_uiActiveLayout) {
        g_pHyprlock->m_uiActiveLayout = group;
        forceUpdateTimers();
    }

    xkb_state_update_mask(g_pHyprlock->m_pXKBState, mods_depressed, mods_latched, mods_locked, 0, 0, group);
}

static void handleRepeatInfo(void* data, struct wl_keyboard* wl_keyboard, int32_t rate, int32_t delay) {
    g_pHyprlock->m_iKeebRepeatRate  = rate;
    g_pHyprlock->m_iKeebRepeatDelay = delay;
}

inline const wl_keyboard_listener keyboardListener = {
    .keymap      = handleKeyboardKeymap,
    .enter       = handleKeyboardEnter,
    .leave       = handleKeyboardLeave,
    .key         = handleKeyboardKey,
    .modifiers   = handleKeyboardModifiers,
    .repeat_info = handleRepeatInfo,
};

static void handleCapabilities(void* data, wl_seat* wl_seat, uint32_t capabilities) {
    if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
        g_pHyprlock->m_pPointer = wl_seat_get_pointer(wl_seat);
        wl_pointer_add_listener(g_pHyprlock->m_pPointer, &pointerListener, wl_seat);
    }

    if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
        g_pHyprlock->m_pKeeb = wl_seat_get_keyboard(wl_seat);
        wl_keyboard_add_listener(g_pHyprlock->m_pKeeb, &keyboardListener, wl_seat);
    }
}

static void handleName(void* data, struct wl_seat* wl_seat, const char* name) {
    ;
}

// end wl_seat

// session_lock

static void handleLocked(void* data, ext_session_lock_v1* ext_session_lock_v1) {
    g_pHyprlock->onLockLocked();
}

static void handleFinished(void* data, ext_session_lock_v1* ext_session_lock_v1) {
    g_pHyprlock->onLockFinished();
}

static const ext_session_lock_v1_listener sessionLockListener = {
    .locked   = handleLocked,
    .finished = handleFinished,
};

// end session_lock

void CHyprlock::onPasswordCheckTimer() {
    // check result
    if (g_pAuth->isAuthenticated()) {
        unlock();
    } else {
        m_sPasswordState.passBuffer = "";
        m_sPasswordState.failedAttempts += 1;
        Debug::log(LOG, "Failed attempts: {}", m_sPasswordState.failedAttempts);

        g_pAuth->m_bDisplayFailText = true;
        forceUpdateTimers();

        g_pAuth->start();

        renderAllOutputs();
    }
}

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

    m_sPasswordState.passBuffer = "";

    renderAllOutputs();
}

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

    if (MON == m_vOutputs.end() || !MON->get())
        return;

    const auto PMONITOR = MON->get();

    if (!PMONITOR->sessionLockSurface)
        return;

    PMONITOR->sessionLockSurface->render();
}

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

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

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

    if (m_iKeebRepeatDelay <= 0)
        return;

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

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

    handleKeySym(sym);

    // 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](std::shared_ptr<CTimer> self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr);

    renderAllOutputs();
}

void CHyprlock::onKey(uint32_t key, bool down) {
    if (m_bFadeStarted || m_bTerminate)
        return;

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

    if (down && std::find(m_vPressedKeys.begin(), m_vPressedKeys.end(), key) != m_vPressedKeys.end()) {
        Debug::log(ERR, "Invalid key down event (key already pressed?)");
        return;
    } else if (!down && std::find(m_vPressedKeys.begin(), m_vPressedKeys.end(), 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 (down) {
        const auto SYM = xkb_state_key_get_one_sym(m_pXKBState, key + 8);

        m_bCapsLock = xkb_state_mod_name_is_active(g_pHyprlock->m_pXKBState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED);
        m_bNumLock  = xkb_state_mod_name_is_active(g_pHyprlock->m_pXKBState, XKB_MOD_NAME_NUM, XKB_STATE_MODS_LOCKED);
        m_bCtrl     = xkb_state_mod_name_is_active(m_pXKBState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE);

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

    renderAllOutputs();
}

void CHyprlock::handleKeySym(xkb_keysym_t sym) {
    const auto SYM = sym;
    if (SYM == XKB_KEY_Escape || (m_bCtrl && (SYM == XKB_KEY_u || SYM == XKB_KEY_BackSpace))) {
        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 auto* const PIGNOREEMPTY = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:ignore_empty_input");

        if (m_sPasswordState.passBuffer.empty() && **PIGNOREEMPTY) {
            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     = xkb_keysym_to_utf8(SYM, buf, 16);
        if (len > 1)
            m_sPasswordState.passBuffer += std::string{buf, len - 1};
    }
}

void CHyprlock::acquireSessionLock() {
    Debug::log(LOG, "Locking session");
    m_sLockState.lock = ext_session_lock_manager_v1_lock(m_sWaylandState.sessionLock);
    ext_session_lock_v1_add_listener(m_sLockState.lock, &sessionLockListener, nullptr);

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

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

    ext_session_lock_v1_unlock_and_destroy(m_sLockState.lock);
    m_sLockState.lock = nullptr;

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

    m_bTerminate = true;
    m_bLocked    = false;

    wl_display_roundtrip(m_sWaylandState.display);
}

void CHyprlock::createSessionLockSurfaces() {
    for (auto& o : m_vOutputs) {
        o->sessionLockSurface = std::make_unique<CSessionLockSurface>(o.get());
    }
}

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 ultimatly the responsiblity of the compositor.
        ext_session_lock_v1_unlock_and_destroy(m_sLockState.lock);
    else
        ext_session_lock_v1_destroy(m_sLockState.lock);

    m_sLockState.lock = nullptr;
    m_bTerminate      = true;
}

ext_session_lock_manager_v1* CHyprlock::getSessionLockMgr() {
    return m_sWaylandState.sessionLock;
}

ext_session_lock_v1* CHyprlock::getSessionLock() {
    return m_sLockState.lock;
}

wl_compositor* CHyprlock::getCompositor() {
    return m_sWaylandState.compositor;
}

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

wp_fractional_scale_manager_v1* CHyprlock::getFractionalMgr() {
    return m_sWaylandState.fractional;
}

wp_viewporter* 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; });
}

size_t CHyprlock::getPasswordFailedAttempts() {
    return m_sPasswordState.failedAttempts;
}

std::shared_ptr<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(std::shared_ptr<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(std::make_shared<CTimer>(timeout, cb_, data, force));
    m_sLoopState.timerEvent       = true;
    m_sLoopState.timerCV.notify_all();
    return T;
}

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

void CHyprlock::enqueueForceUpdateTimers() {
    addTimer(
        std::chrono::milliseconds(1), [](std::shared_ptr<CTimer> self, void* data) { forceUpdateTimers(); }, nullptr, false);
}

void CHyprlock::spawnAsync(const std::string& args) {
    Debug::log(LOG, "Executing (async) {}", args);

    int socket[2];
    if (pipe(socket) != 0)
        Debug::log(LOG, "Unable to create pipe for fork");

    pid_t child, grandchild;
    child = fork();

    if (child < 0) {
        close(socket[0]);
        close(socket[1]);
        Debug::log(LOG, "Fail to create the first fork");
        return;
    }

    if (child == 0) {
        // run in child

        sigset_t set;
        sigemptyset(&set);
        sigprocmask(SIG_SETMASK, &set, NULL);

        grandchild = fork();

        if (grandchild == 0) {
            // run in grandchild
            close(socket[0]);
            close(socket[1]);
            execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr);
            // exit grandchild
            _exit(0);
        }

        close(socket[0]);
        write(socket[1], &grandchild, sizeof(grandchild));
        close(socket[1]);
        // exit child
        _exit(0);
    }

    // run in parent
    close(socket[1]);
    read(socket[0], &grandchild, sizeof(grandchild));
    close(socket[0]);
    // clear child and leave child to init
    waitpid(child, NULL, 0);

    if (child < 0) {
        Debug::log(LOG, "Failed to create the second fork");
        return;
    }

    Debug::log(LOG, "Process Created with pid {}", grandchild);
}

std::string CHyprlock::spawnSync(const std::string& cmd) {
    std::array<char, 128>                          buffer;
    std::string                                    result;
    const std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe)
        return "";

    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    return result;
}

zwlr_screencopy_manager_v1* CHyprlock::getScreencopy() {
    return m_sWaylandState.screencopy;
}

void CHyprlock::attemptRestoreOnDeath() {
    if (m_bTerminate)
        return;

    const auto XDG_RUNTIME_DIR = getenv("XDG_RUNTIME_DIR");
    const auto HIS             = getenv("HYPRLAND_INSTANCE_SIGNATURE");

    if (!XDG_RUNTIME_DIR || !HIS)
        return;

    // dirty hack
    uint64_t   timeNowMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(0)).count();

    const auto LASTRESTARTPATH = std::string{XDG_RUNTIME_DIR} + "/.hyprlockrestart";

    if (std::filesystem::exists(LASTRESTARTPATH)) {
        std::ifstream ifs(LASTRESTARTPATH);
        std::string   content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
        uint64_t      timeEncoded = 0;
        try {
            timeEncoded = std::stoull(content);
        } catch (std::exception& e) {
            // oops?
            ifs.close();
            std::filesystem::remove(LASTRESTARTPATH);
            return;
        }
        ifs.close();

        if (timeNowMs - timeEncoded < 4000 /* 4s, seems reasonable */) {
            Debug::log(LOG, "Not restoring on death; less than 4s since last death");
            return;
        }
    }

    std::ofstream ofs(LASTRESTARTPATH, std::ios::trunc);
    ofs << timeNowMs;
    ofs.close();

    spawnSync("hyprctl keyword misc:allow_session_lock_restore true");
    spawnAsync("sleep 2 && hyprlock --immediate --immediate-render & disown");
}
07070100000024000081A4000000000000000000000001669CF85C000018BF000000000000000000000000000000000000002500000000hyprlock-0.4.1/src/core/hyprlock.hpp#pragma once

#include <wayland-client.h>
#include "ext-session-lock-v1-protocol.h"
#include "fractional-scale-v1-protocol.h"
#include "wlr-screencopy-unstable-v1-protocol.h"
#include "viewporter-protocol.h"
#include "Output.hpp"
#include "CursorShape.hpp"
#include "Timer.hpp"
#include <memory>
#include <vector>
#include <condition_variable>
#include <optional>

#include <xkbcommon/xkbcommon.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 immediate, const bool immediateRender);
    ~CHyprlock();

    void                            run();

    void                            unlock();
    bool                            isUnlocked();

    void                            onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
    void                            onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name);

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

    void                            enqueueForceUpdateTimers();

    void                            onLockLocked();
    void                            onLockFinished();

    void                            acquireSessionLock();
    void                            releaseSessionLock();

    void                            createSessionLockSurfaces();

    void                            attemptRestoreOnDeath();

    void                            spawnAsync(const std::string& cmd);
    std::string                     spawnSync(const std::string& cmd);

    void                            onKey(uint32_t key, bool down);
    void                            handleKeySym(xkb_keysym_t sym);
    void                            startKeyRepeat(xkb_keysym_t sym);
    void                            repeatKey(xkb_keysym_t sym);
    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();
    size_t                          getPasswordFailedAttempts();

    ext_session_lock_manager_v1*    getSessionLockMgr();
    ext_session_lock_v1*            getSessionLock();
    wl_compositor*                  getCompositor();
    wl_display*                     getDisplay();
    wp_fractional_scale_manager_v1* getFractionalMgr();
    wp_viewporter*                  getViewporter();
    zwlr_screencopy_manager_v1*     getScreencopy();

    wl_pointer*                     m_pPointer = nullptr;
    wl_keyboard*                    m_pKeeb    = nullptr;

    std::unique_ptr<CCursorShape>   m_pCursorShape;

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

    int32_t                         m_iKeebRepeatRate  = 25;
    int32_t                         m_iKeebRepeatDelay = 600;

    xkb_layout_index_t              m_uiActiveLayout = 0;

    bool                            m_bTerminate = false;

    bool                            m_bLocked = false;

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

    bool                            m_bImmediateRender = false;
    //
    std::chrono::system_clock::time_point m_tGraceEnds;
    std::chrono::system_clock::time_point m_tFadeEnds;
    Vector2D                              m_vLastEnterCoords = {};

    std::shared_ptr<CTimer>               m_pKeyRepeatTimer = nullptr;

    std::vector<std::unique_ptr<COutput>> m_vOutputs;
    std::vector<std::shared_ptr<CTimer>>  getTimers();

    struct {
        void*                        linuxDmabuf         = nullptr;
        void*                        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;
        wl_registry*                    registry    = nullptr;
        wl_seat*                        seat        = nullptr;
        ext_session_lock_manager_v1*    sessionLock = nullptr;
        wl_compositor*                  compositor  = nullptr;
        wp_fractional_scale_manager_v1* fractional  = nullptr;
        wp_viewporter*                  viewporter  = nullptr;
        zwlr_screencopy_manager_v1*     screencopy  = nullptr;
    } m_sWaylandState;

    struct {
        ext_session_lock_v1* 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 timerCV;
        std::mutex              timerRequestMutex;
        bool                    timerEvent = false;
    } m_sLoopState;

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

    std::vector<uint32_t>                m_vPressedKeys;
};

inline std::unique_ptr<CHyprlock> g_pHyprlock;
07070100000025000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001B00000000hyprlock-0.4.1/src/helpers07070100000026000081A4000000000000000000000001669CF85C00000774000000000000000000000000000000000000002300000000hyprlock-0.4.1/src/helpers/Box.cpp#include "Box.hpp"
#include <cmath>
#include <limits>
#include <algorithm>

#define VECINRECT(vec, x1, y1, x2, y2) ((vec).x >= (x1) && (vec).x <= (x2) && (vec).y >= (y1) && (vec).y <= (y2))

CBox& CBox::scale(double scale) {
    x *= scale;
    y *= scale;
    w *= scale;
    h *= scale;

    return *this;
}

CBox& CBox::scale(const Vector2D& scale) {
    x *= scale.x;
    y *= scale.y;
    w *= scale.x;
    h *= scale.y;

    return *this;
}

CBox& CBox::translate(const Vector2D& vec) {
    x += vec.x;
    y += vec.y;

    return *this;
}

Vector2D CBox::middle() const {
    return Vector2D{x + w / 2.0, y + h / 2.0};
}

bool CBox::containsPoint(const Vector2D& vec) const {
    return VECINRECT(vec, x, y, x + w, y + h);
}

bool CBox::empty() const {
    return w == 0 || h == 0;
}

CBox& CBox::round() {
    float newW = x + w - std::round(x);
    float newH = y + h - std::round(y);
    x          = std::round(x);
    y          = std::round(y);
    w          = std::round(newW);
    h          = std::round(newH);

    return *this;
}

CBox& CBox::scaleFromCenter(double scale) {
    double oldW = w, oldH = h;

    w *= scale;
    h *= scale;

    x -= (w - oldW) / 2.0;
    y -= (h - oldH) / 2.0;

    return *this;
}

CBox& CBox::expand(const double& value) {
    x -= value;
    y -= value;
    w += value * 2.0;
    h += value * 2.0;

    return *this;
}

CBox& CBox::noNegativeSize() {
    std::clamp(w, 0.0, std::numeric_limits<double>::infinity());
    std::clamp(h, 0.0, std::numeric_limits<double>::infinity());

    return *this;
}

CBox CBox::roundInternal() {
    float newW = x + w - std::floor(x);
    float newH = y + h - std::floor(y);

    return CBox{std::floor(x), std::floor(y), std::floor(newW), std::floor(newH)};
}

CBox CBox::copy() const {
    return CBox{*this};
}

Vector2D CBox::pos() const {
    return {x, y};
}

Vector2D CBox::size() const {
    return {w, h};
}07070100000027000081A4000000000000000000000001669CF85C000004FE000000000000000000000000000000000000002300000000hyprlock-0.4.1/src/helpers/Box.hpp#pragma once
#include "Vector2D.hpp"

class CBox {
  public:
    CBox(double x_, double y_, double w_, double h_) {
        x = x_;
        y = y_;
        w = w_;
        h = h_;
    }

    CBox() {
        w = 0;
        h = 0;
    }

    CBox(const double d) {
        x = d;
        y = d;
        w = d;
        h = d;
    }

    CBox(const Vector2D& pos, const Vector2D& size) {
        x = pos.x;
        y = pos.y;
        w = size.x;
        h = size.y;
    }

    CBox&    scale(double scale);
    CBox&    scaleFromCenter(double scale);
    CBox&    scale(const Vector2D& scale);
    CBox&    translate(const Vector2D& vec);
    CBox&    round();
    CBox&    expand(const double& value);
    CBox&    noNegativeSize();

    CBox     copy() const;

    Vector2D middle() const;
    Vector2D pos() const;
    Vector2D size() const;

    bool     containsPoint(const Vector2D& vec) const;
    bool     empty() const;

    double   x = 0, y = 0;
    union {
        double w;
        double width;
    };
    union {
        double h;
        double height;
    };

    double rot = 0; /* rad, ccw */

    //
    bool operator==(const CBox& rhs) const {
        return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
    }

  private:
    CBox roundInternal();
};
07070100000028000081A4000000000000000000000001669CF85C00000281000000000000000000000000000000000000002500000000hyprlock-0.4.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)

CColor::CColor() {}

CColor::CColor(float r, float g, float b, float a) {
    this->r = r;
    this->g = g;
    this->b = b;
    this->a = a;
}

CColor::CColor(uint64_t hex) {
    this->r = RED(hex);
    this->g = GREEN(hex);
    this->b = BLUE(hex);
    this->a = ALPHA(hex);
}

uint64_t CColor::getAsHex() {
    return ((int)a) * 0x1000000 + ((int)r) * 0x10000 + ((int)g) * 0x100 + ((int)b) * 0x1;
}07070100000029000081A4000000000000000000000001669CF85C000002E1000000000000000000000000000000000000002500000000hyprlock-0.4.1/src/helpers/Color.hpp#pragma once

#include <cstdint>

class CColor {
  public:
    CColor();
    CColor(float r, float g, float b, float a);
    CColor(uint64_t);

    float    r = 0, g = 0, b = 0, a = 1.f;

    uint64_t getAsHex();

    CColor   operator-(const CColor& c2) const {
        return CColor(r - c2.r, g - c2.g, b - c2.b, a - c2.a);
    }

    CColor operator+(const CColor& c2) const {
        return CColor(r + c2.r, g + c2.g, b + c2.b, a + c2.a);
    }

    CColor operator*(const float& v) const {
        return CColor(r * v, g * v, b * v, a * v);
    }

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

    CColor stripA() const {
        return {r, g, b, 1};
    }
};
0707010000002A000081A4000000000000000000000001669CF85C000009D6000000000000000000000000000000000000002400000000hyprlock-0.4.1/src/helpers/Jpeg.cpp#include "Jpeg.hpp"
#include "Log.hpp"
#include <jpeglib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

cairo_surface_t* JPEG::createSurfaceFromJPEG(const std::filesystem::path& path) {

    if (!std::filesystem::exists(path)) {
        Debug::log(ERR, "createSurfaceFromJPEG: file doesn't exist??");
        return nullptr;
    }

    if (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) {
        Debug::log(CRIT, "tried to load a jpeg on a big endian system! ping vaxry he is lazy.");
        return nullptr;
    }

    void*       imageRawData;
    struct stat fileInfo = {};
    const auto  FD       = open(path.c_str(), O_RDONLY);

    fstat(FD, &fileInfo);

    imageRawData = malloc(fileInfo.st_size);

    read(FD, imageRawData, fileInfo.st_size);
    close(FD);

    // now the JPEG is in the memory

    jpeg_decompress_struct decompressStruct = {};
    jpeg_error_mgr         errorManager     = {};

    decompressStruct.err = jpeg_std_error(&errorManager);
    jpeg_create_decompress(&decompressStruct);
    jpeg_mem_src(&decompressStruct, (const unsigned char*)imageRawData, fileInfo.st_size);
    jpeg_read_header(&decompressStruct, true);

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    decompressStruct.out_color_space = JCS_EXT_BGRA;
#else
    decompressStruct.out_color_space = JCS_EXT_ARGB;
#endif

    // decompress
    jpeg_start_decompress(&decompressStruct);

    auto cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, decompressStruct.output_width, decompressStruct.output_height);

    if (cairo_surface_status(cairoSurface) != CAIRO_STATUS_SUCCESS) {
        Debug::log(ERR, "createSurfaceFromJPEG: Cairo Failed (?)");
        return nullptr;
    }

    const auto CAIRODATA   = cairo_image_surface_get_data(cairoSurface);
    const auto CAIROSTRIDE = cairo_image_surface_get_stride(cairoSurface);
    JSAMPROW   rowRead;

    while (decompressStruct.output_scanline < decompressStruct.output_height) {
        const auto PROW = CAIRODATA + (decompressStruct.output_scanline * CAIROSTRIDE);
        rowRead         = PROW;
        jpeg_read_scanlines(&decompressStruct, &rowRead, 1);
    }

    cairo_surface_flush(cairoSurface);
    cairo_surface_mark_dirty(cairoSurface);
    cairo_surface_set_mime_data(cairoSurface, CAIRO_MIME_TYPE_JPEG, (const unsigned char*)imageRawData, fileInfo.st_size, free, imageRawData);
    jpeg_finish_decompress(&decompressStruct);
    jpeg_destroy_decompress(&decompressStruct);

    return cairoSurface;
}
0707010000002B000081A4000000000000000000000001669CF85C0000009C000000000000000000000000000000000000002400000000hyprlock-0.4.1/src/helpers/Jpeg.hpp#pragma once

#include <filesystem>
#include <cairo/cairo.h>

namespace JPEG {
    cairo_surface_t* createSurfaceFromJPEG(const std::filesystem::path&);
};
0707010000002C000081A4000000000000000000000001669CF85C0000090A000000000000000000000000000000000000002300000000hyprlock-0.4.1/src/helpers/Log.hpp#pragma once
#include <format>
#include <iostream>
#include <string>

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());                               \
        printf("Assertion failed! See the log in /tmp/hypr/hyprland.log for more info.");                                                                                          \
        std::abort();                                                                                                         \
    }

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

namespace Debug {
    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::cout << '[';

            switch (level) {
                case TRACE: std::cout << "TRACE"; break;
                case INFO: std::cout << "INFO"; break;
                case LOG: std::cout << "LOG"; break;
                case WARN: std::cout << "WARN"; break;
                case ERR: std::cout << "ERR"; break;
                case CRIT: std::cout << "CRITICAL"; break;
                default: break;
            }

            std::cout << "] ";
        }

        std::cout << std::vformat(fmt, std::make_format_args(args...)) << "\n";
    }
};0707010000002D000081A4000000000000000000000001669CF85C00000295000000000000000000000000000000000000002D00000000hyprlock-0.4.1/src/helpers/MiscFunctions.cpp#include <filesystem>
#include "MiscFunctions.hpp"

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");
        return std::filesystem::path(ENVHOME) / path.relative_path().string().substr(2);
    }
    // Handling e.g. ./, ../
    else if (path.is_relative()) {
        return std::filesystem::weakly_canonical(std::filesystem::path(currentDir) / path);
    } else {
        return std::filesystem::weakly_canonical(path);
    }
}0707010000002E000081A4000000000000000000000001669CF85C00000064000000000000000000000000000000000000002D00000000hyprlock-0.4.1/src/helpers/MiscFunctions.hpp#pragma once

#include <string>


std::string absolutePath(const std::string&, const std::string&);
0707010000002F000081A4000000000000000000000001669CF85C0000055B000000000000000000000000000000000000002800000000hyprlock-0.4.1/src/helpers/Vector2D.cpp#include "Vector2D.hpp"
#include <algorithm>
#include <cmath>

Vector2D::Vector2D(double xx, double yy) {
    x = xx;
    y = yy;
}

Vector2D::Vector2D() {
    x = 0;
    y = 0;
}

Vector2D::~Vector2D() {}

double Vector2D::normalize() {
    // get max abs
    const auto max = std::abs(x) > std::abs(y) ? std::abs(x) : std::abs(y);

    x /= max;
    y /= max;

    return max;
}

Vector2D Vector2D::floor() const {
    return Vector2D(std::floor(x), std::floor(y));
}

Vector2D Vector2D::round() const {
    return Vector2D(std::round(x), std::round(y));
}

Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) const {
    return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y));
}

double Vector2D::distance(const Vector2D& other) const {
    double dx = x - other.x;
    double dy = y - other.y;
    return std::sqrt(dx * dx + dy * dy);
}

double Vector2D::size() const {
    return std::sqrt(x * x + y * y);
}

Vector2D Vector2D::getComponentMax(const Vector2D& other) const {
    return Vector2D(std::max(this->x, other.x), std::max(this->y, other.y));
}

Vector2D Vector2D::rotated(const double& ang) const {
    const double COS = std::abs(std::cos(ang));
    const double SIN = std::abs(std::sin(ang));
    return Vector2D(x * COS + y * SIN, x * SIN + y * COS);
}
07070100000030000081A4000000000000000000000001669CF85C00001CD0000000000000000000000000000000000000002800000000hyprlock-0.4.1/src/helpers/Vector2D.hpp#pragma once

#include <format>
#include <hyprlang.hpp>

class Vector2D {
  public:
    Vector2D(double, double);
    Vector2D();
    Vector2D(const Hyprlang::VEC2& v) {
        x = v.x;
        y = v.y;
    }
    ~Vector2D();

    double x = 0;
    double y = 0;

    // returns the scale
    double   normalize();

    Vector2D operator+(const Vector2D& a) const {
        return Vector2D(this->x + a.x, this->y + a.y);
    }
    Vector2D operator-(const Vector2D& a) const {
        return Vector2D(this->x - a.x, this->y - a.y);
    }
    Vector2D operator-() const {
        return Vector2D(-this->x, -this->y);
    }
    Vector2D operator*(const double& a) const {
        return Vector2D(this->x * a, this->y * a);
    }
    Vector2D operator/(const double& a) const {
        return Vector2D(this->x / a, this->y / a);
    }

    bool operator==(const Vector2D& a) const {
        return a.x == x && a.y == y;
    }

    bool operator!=(const Vector2D& a) const {
        return a.x != x || a.y != y;
    }

    Vector2D operator*(const Vector2D& a) const {
        return Vector2D(this->x * a.x, this->y * a.y);
    }

    Vector2D operator/(const Vector2D& a) const {
        return Vector2D(this->x / a.x, this->y / a.y);
    }

    bool operator>(const Vector2D& a) const {
        return this->x > a.x && this->y > a.y;
    }

    bool operator<(const Vector2D& a) const {
        return this->x < a.x && this->y < a.y;
    }
    Vector2D& operator+=(const Vector2D& a) {
        this->x += a.x;
        this->y += a.y;
        return *this;
    }
    Vector2D& operator-=(const Vector2D& a) {
        this->x -= a.x;
        this->y -= a.y;
        return *this;
    }
    Vector2D& operator*=(const Vector2D& a) {
        this->x *= a.x;
        this->y *= a.y;
        return *this;
    }
    Vector2D& operator/=(const Vector2D& a) {
        this->x /= a.x;
        this->y /= a.y;
        return *this;
    }
    Vector2D& operator*=(const double& a) {
        this->x *= a;
        this->y *= a;
        return *this;
    }
    Vector2D& operator/=(const double& a) {
        this->x /= a;
        this->y /= a;
        return *this;
    }

    double   distance(const Vector2D& other) const;
    double   size() const;
    Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D{-1, -1}) const;

    Vector2D floor() const;
    Vector2D round() const;

    Vector2D getComponentMax(const Vector2D& other) const;
    Vector2D rotated(const double& ang) const;
};

/**
    format specification
    - 'j', as json array
    - 'X', same as std::format("{}x{}", vec.x, vec.y)
    - number, floating point precision, use `0` to format as integer
*/
// absolutely ridiculous formatter spec parsing
#define FORMAT_PARSE(specs__, type__)                                                                                                                                              \
    template <typename FormatContext>                                                                                                                                              \
    constexpr auto parse(FormatContext& ctx) {                                                                                                                                     \
        auto it = ctx.begin();                                                                                                                                                     \
        for (; it != ctx.end() && *it != '}'; it++) {                                                                                                                              \
            switch (*it) { specs__ default : throw std::format_error("invalid format specification"); }                                                                            \
        }                                                                                                                                                                          \
        return it;                                                                                                                                                                 \
    }

#define FORMAT_FLAG(spec__, flag__)                                                                                                                                                \
    case spec__: (flag__) = true; break;

#define FORMAT_NUMBER(buf__)                                                                                                                                                       \
    case '0':                                                                                                                                                                      \
    case '1':                                                                                                                                                                      \
    case '2':                                                                                                                                                                      \
    case '3':                                                                                                                                                                      \
    case '4':                                                                                                                                                                      \
    case '5':                                                                                                                                                                      \
    case '6':                                                                                                                                                                      \
    case '7':                                                                                                                                                                      \
    case '8':                                                                                                                                                                      \
    case '9': (buf__).push_back(*it); break;
template <typename CharT>
struct std::formatter<Vector2D, CharT> : std::formatter<CharT> {
    bool        formatJson = false;
    bool        formatX    = false;
    std::string precision  = "";
    FORMAT_PARSE(FORMAT_FLAG('j', formatJson) //
                 FORMAT_FLAG('X', formatX)    //
                 FORMAT_NUMBER(precision),
                 Vector2D)

    template <typename FormatContext>
    auto format(const Vector2D& vec, FormatContext& ctx) const {
        std::string formatString = precision.empty() ? "{}" : std::format("{{:.{}f}}", precision);

        if (formatJson)
            formatString = std::format("[{0}, {0}]", formatString);
        else if (formatX)
            formatString = std::format("{0}x{0}", formatString);
        else
            formatString = std::format("[Vector2D: x: {0}, y: {0}]", formatString);
        try {
            string buf = std::vformat(formatString, std::make_format_args(vec.x, vec.y));
            return std::format_to(ctx.out(), "{}", buf);
        } catch (std::format_error& e) { return std::format_to(ctx.out(), "[{}, {}]", vec.x, vec.y); }
    }
};
07070100000031000081A4000000000000000000000001669CF85C00000A40000000000000000000000000000000000000002400000000hyprlock-0.4.1/src/helpers/Webp.cpp#include "Webp.hpp"
#include "Log.hpp"

#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

#include <webp/decode.h>

cairo_surface_t* WEBP::createSurfaceFromWEBP(const std::filesystem::path& path) {

    if (!std::filesystem::exists(path)) {
        Debug::log(ERR, "createSurfaceFromWEBP: file doesn't exist??");
        return nullptr;
    }

    void*       imageRawData;

    struct stat fileInfo = {};

    const auto  FD = open(path.c_str(), O_RDONLY);

    fstat(FD, &fileInfo);

    imageRawData = malloc(fileInfo.st_size);

    read(FD, imageRawData, fileInfo.st_size);
    close(FD);

    // now the WebP is in the memory

    WebPDecoderConfig config;
    if (!WebPInitDecoderConfig(&config)) {
        Debug::log(CRIT, "WebPInitDecoderConfig Failed");
        return nullptr;
    }

    if (WebPGetFeatures((const unsigned char*)imageRawData, fileInfo.st_size, &config.input) != VP8_STATUS_OK) {
        Debug::log(ERR, "createSurfaceFromWEBP: file is not webp format");
        free(imageRawData);
        return nullptr;
    }

    const auto HEIGHT = config.input.height;
    const auto WIDTH  = config.input.width;

    auto       cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT);
    if (cairo_surface_status(cairoSurface) != CAIRO_STATUS_SUCCESS) {
        Debug::log(CRIT, "createSurfaceFromWEBP: Cairo Failed (?)");
        cairo_surface_destroy(cairoSurface);
        return nullptr;
    }

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    config.output.colorspace = MODE_bgrA;
#else
    config.output.colorspace = MODE_Argb;
#endif

    const auto CAIRODATA   = cairo_image_surface_get_data(cairoSurface);
    const auto CAIROSTRIDE = cairo_image_surface_get_stride(cairoSurface);

    config.options.no_fancy_upsampling = 1;
    config.output.u.RGBA.rgba          = CAIRODATA;
    config.output.u.RGBA.stride        = CAIROSTRIDE;
    config.output.u.RGBA.size          = CAIROSTRIDE * HEIGHT;
    config.output.is_external_memory   = 1;
    config.output.width                = WIDTH;
    config.output.height               = HEIGHT;

    if (WebPDecode((const unsigned char*)imageRawData, fileInfo.st_size, &config) != VP8_STATUS_OK) {
        Debug::log(CRIT, "createSurfaceFromWEBP: WebP Decode Failed (?)");
        return nullptr;
    }

    cairo_surface_flush(cairoSurface);
    cairo_surface_mark_dirty(cairoSurface);
    cairo_surface_set_mime_data(cairoSurface, CAIRO_MIME_TYPE_PNG, (const unsigned char*)imageRawData, fileInfo.st_size, free, imageRawData);

    WebPFreeDecBuffer(&config.output);

    return cairoSurface;
}
07070100000032000081A4000000000000000000000001669CF85C0000009C000000000000000000000000000000000000002400000000hyprlock-0.4.1/src/helpers/Webp.hpp#pragma once

#include <filesystem>
#include <cairo/cairo.h>

namespace WEBP {
    cairo_surface_t* createSurfaceFromWEBP(const std::filesystem::path&);
};
07070100000033000081A4000000000000000000000001669CF85C00000CCD000000000000000000000000000000000000001C00000000hyprlock-0.4.1/src/main.cpp
#include "config/ConfigManager.hpp"
#include "core/hyprlock.hpp"
#include "src/helpers/Log.hpp"
#include <cstddef>
#include <iostream>

void help() {
    std::cout << "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 (display)      - Specify the Wayland display to connect to\n"
                 "  --immediate              - Lock immediately, ignoring any configured grace period\n"
                 "  --immediate-render       - Do not wait for resources before drawing the background\n"
                 "  -h, --help               - Show this help message\n";
}

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::cerr << "Error: Missing value for " << flag << " option.\n";
        return std::nullopt;
    }
}

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

    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 == "--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 == "--immediate")
            immediate = true;

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

        else if (arg == "--help" || arg == "-h") {
            showHelp = true;
            break;

        } else {
            std::cerr << "Unknown option: " << arg << "\n";
            help();
            return 1;
        }
    }

    if (showHelp) {
        help();
        return 0;
    }

    try {
        g_pConfigManager = std::make_unique<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;
    }

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

    return 0;
}
07070100000034000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000001C00000000hyprlock-0.4.1/src/renderer07070100000035000081A4000000000000000000000001669CF85C00003A0A000000000000000000000000000000000000003600000000hyprlock-0.4.1/src/renderer/AsyncResourceGatherer.cpp#include "AsyncResourceGatherer.hpp"
#include "../config/ConfigManager.hpp"
#include "../core/Egl.hpp"
#include <cairo/cairo.h>
#include <magic.h>
#include <pango/pangocairo.h>
#include <algorithm>
#include <filesystem>
#include "../core/hyprlock.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../helpers/Jpeg.hpp"
#include "../helpers/Webp.hpp"
#include "src/helpers/Color.hpp"
#include "src/helpers/Log.hpp"

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

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

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

void CAsyncResourceGatherer::enqueueDMAFrames() {
    // some things can't be done async :(
    // gather background textures when needed

    const auto               CWIDGETS = g_pConfigManager->getWidgetConfigs();

    std::vector<std::string> mons;

    for (auto& c : CWIDGETS) {
        if (c.type != "background")
            continue;

        if (std::string{std::any_cast<Hyprlang::STRING>(c.values.at("path"))} != "screenshot")
            continue;

        // mamma mia
        if (c.monitor.empty()) {
            mons.clear();
            for (auto& m : g_pHyprlock->m_vOutputs) {
                mons.push_back(m->stringPort);
            }
            break;
        } else
            mons.push_back(c.monitor);
    }

    for (auto& mon : mons) {
        const auto MON = std::find_if(g_pHyprlock->m_vOutputs.begin(), g_pHyprlock->m_vOutputs.end(),
                                      [mon](const auto& other) { return other->stringPort == mon || other->stringDesc.starts_with(mon); });

        if (MON == g_pHyprlock->m_vOutputs.end())
            continue;

        const auto PMONITOR = MON->get();

        dmas.emplace_back(std::make_unique<CDMAFrame>(PMONITOR));
    }
}

SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
    for (auto& a : assets) {
        if (a.first == id)
            return &a.second;
    }

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

    for (auto& dma : dmas) {
        if (id == dma->resourceID)
            return dma->asset.ready ? &dma->asset : nullptr;
    }

    return nullptr;
}

enum class FileType {
    PNG,
    JPEG,
    WEBP,
    UNKNOWN,
};

FileType getFileType(const std::filesystem::path& path) {
    std::string ext = path.extension().string();
    // convert the extension to lower case
    std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) { return c <= 'Z' && c >= 'A' ? c - ('Z' - 'z') : c; });

    FileType ft = FileType::UNKNOWN;
    Debug::log(TRACE, "Extension: {}", ext);
    if (ext == ".png")
        ft = FileType::PNG;
    else if (ext == ".jpg" || ext == ".jpeg")
        ft = FileType::JPEG;
    else if (ext == ".webp")
        ft = FileType::WEBP;
    else {
        // magic is slow, so only use it when no recognized extension is found
        auto handle = magic_open(MAGIC_NONE | MAGIC_COMPRESS);
        magic_load(handle, nullptr);

        const auto type_str   = std::string(magic_file(handle, path.c_str()));
        const auto first_word = type_str.substr(0, type_str.find(" "));
        magic_close(handle);

        if (first_word == "PNG")
            ft = FileType::PNG;
        else if (first_word == "JPEG")
            ft = FileType::JPEG;
        else if (first_word == "RIFF" && type_str.find("Web/P image") != std::string::npos)
            ft = FileType::WEBP;
    }

    return ft;
}

cairo_surface_t* getCairoSurfaceFromImageFile(const std::filesystem::path& path) {
    cairo_surface_t* cairoSurface = nullptr;
    switch (getFileType(path)) {
        case FileType::PNG: cairoSurface = cairo_image_surface_create_from_png(path.c_str()); break;
        case FileType::JPEG: cairoSurface = JPEG::createSurfaceFromJPEG(path); break;
        case FileType::WEBP: cairoSurface = WEBP::createSurfaceFromWEBP(path); break;
        default: Debug::log(ERR, "unrecognized image format of {}", path.c_str());
    }

    return 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 (std::any_of(dmas.begin(), dmas.end(), [](const auto& d) { return !d->asset.ready; })) {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }

    gathered = true;
}

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 auto  SURFACESTATUS = cairo_surface_status((cairo_surface_t*)t.cairosurface);
            const auto  CAIROFORMAT   = cairo_image_surface_get_format((cairo_surface_t*)t.cairosurface);
            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);
            cairo_surface_destroy((cairo_surface_t*)t.cairosurface);

        } 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, "No cairo surface!");
        return;
    }

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

    target.cairo        = CAIRO;
    target.cairosurface = CAIROISURFACE;
    target.data         = cairo_image_surface_get_data(CAIROISURFACE);
    target.size         = {(double)cairo_image_surface_get_width(CAIROISURFACE), (double)cairo_image_surface_get_height(CAIROISURFACE)};

    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 CColor       FONTCOLOR  = rq.props.contains("color") ? std::any_cast<CColor>(rq.props.at("color")) : CColor(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 auto* const TRIM = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:text_trim");
    std::string        TEXT = ISCMD ? g_pHyprlock->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 = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1920, 1080 /* dummy value */);
    auto CAIRO        = cairo_create(CAIROSURFACE);

    // 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);
    cairo_surface_destroy(CAIROSURFACE);
    CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layoutWidth / PANGO_SCALE, layoutHeight / PANGO_SCALE);
    CAIRO        = cairo_create(CAIROSURFACE);

    // 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);

    target.cairo        = CAIRO;
    target.cairosurface = CAIROSURFACE;
    target.data         = cairo_image_surface_get_data(CAIROSURFACE);
    target.size         = {layoutWidth / (double)PANGO_SCALE, layoutHeight / (double)PANGO_SCALE};

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

struct STimerCallbackData {
    void (*cb)(void*) = nullptr;
    void* data        = nullptr;
};

static void timerCallback(std::shared_ptr<CTimer> self, void* data_) {
    auto data = (STimerCallbackData*)data_;
    data->cb(data->data);
    delete data;
}

void CAsyncResourceGatherer::asyncAssetSpinLock() {
    while (!g_pHyprlock->m_bTerminate) {

        std::unique_lock lk(asyncLoopState.requestsMutex);
        if (asyncLoopState.pending == false) // 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), timerCallback, new STimerCallbackData{r.callback, r.callbackData});
        }
    }
}

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();
}
07070100000036000081A4000000000000000000000001669CF85C0000093F000000000000000000000000000000000000003600000000hyprlock-0.4.1/src/renderer/AsyncResourceGatherer.hpp#pragma once

#include "DMAFrame.hpp"
#include <thread>
#include <atomic>
#include <vector>
#include <unordered_map>
#include <condition_variable>
#include <any>
#include "Shared.hpp"

class CAsyncResourceGatherer {
  public:
    CAsyncResourceGatherer();
    std::atomic<bool>  gathered = false;

    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.
        void (*callback)(void*) = nullptr;
        void* callbackData      = 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;
        void*       cairo;
        void*       cairosurface;

        Vector2D    size;
    };

    std::vector<std::unique_ptr<CDMAFrame>>          dmas;

    std::vector<SPreloadTarget>                      preloadTargets;
    std::mutex                                       preloadTargetsMutex;

    std::unordered_map<std::string, SPreloadedAsset> assets;

    void                                             gather();
    void                                             enqueueDMAFrames();
};
07070100000037000081A4000000000000000000000001669CF85C00002271000000000000000000000000000000000000002900000000hyprlock-0.4.1/src/renderer/DMAFrame.cpp#include "DMAFrame.hpp"
#include "linux-dmabuf-unstable-v1-protocol.h"
#include "wlr-screencopy-unstable-v1-protocol.h"
#include "../helpers/Log.hpp"
#include "../core/hyprlock.hpp"
#include "../core/Egl.hpp"
#include <EGL/eglext.h>
#include <libdrm/drm_fourcc.h>
#include <GLES3/gl32.h>
#include <GLES3/gl3ext.h>
#include <GLES2/gl2ext.h>
#include <unistd.h>

static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr;
static PFNEGLQUERYDMABUFMODIFIERSEXTPROC   eglQueryDmaBufModifiersEXT   = nullptr;

//
static void wlrOnBuffer(void* data, zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
    const auto PDATA = (SScreencopyData*)data;

    Debug::log(TRACE, "[sc] wlrOnBuffer for {}", (void*)PDATA);

    PDATA->size   = stride * height;
    PDATA->stride = stride;
}

static void wlrOnFlags(void* data, zwlr_screencopy_frame_v1* frame, uint32_t flags) {
    ;
}

static void wlrOnReady(void* data, zwlr_screencopy_frame_v1* frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
    const auto PDATA = (SScreencopyData*)data;

    Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)PDATA);

    if (!PDATA->frame->onBufferReady()) {
        Debug::log(ERR, "onBufferReady failed");
        return;
    }

    zwlr_screencopy_frame_v1_destroy(frame);
}

static void wlrOnFailed(void* data, zwlr_screencopy_frame_v1* frame) {
    ;
}

static void wlrOnDamage(void* data, zwlr_screencopy_frame_v1* frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
    ;
}

static void wlrOnDmabuf(void* data, zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height) {
    const auto PDATA = (SScreencopyData*)data;

    Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)PDATA);

    PDATA->w   = width;
    PDATA->h   = height;
    PDATA->fmt = format;

    Debug::log(TRACE, "[sc] DMABUF format reported: {:x}", format);
}

static void wlrOnBufferDone(void* data, zwlr_screencopy_frame_v1* frame) {
    const auto PDATA = (SScreencopyData*)data;

    Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)PDATA);

    if (!PDATA->frame->onBufferDone()) {
        Debug::log(ERR, "onBufferDone failed");
        return;
    }

    zwlr_screencopy_frame_v1_copy(frame, PDATA->frame->wlBuffer);

    Debug::log(TRACE, "[sc] wlr frame copied");
}

static const zwlr_screencopy_frame_v1_listener wlrFrameListener = {
    .buffer       = wlrOnBuffer,
    .flags        = wlrOnFlags,
    .ready        = wlrOnReady,
    .failed       = wlrOnFailed,
    .damage       = wlrOnDamage,
    .linux_dmabuf = wlrOnDmabuf,
    .buffer_done  = wlrOnBufferDone,
};

std::string CDMAFrame::getResourceId(COutput* output) {
    return std::format("dma:{}-{}x{}", output->stringPort, output->size.x, output->size.y);
}

CDMAFrame::CDMAFrame(COutput* output_) {
    resourceID = getResourceId(output_);

    if (!glEGLImageTargetTexture2DOES) {
        glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
        if (!glEGLImageTargetTexture2DOES) {
            Debug::log(ERR, "No glEGLImageTargetTexture2DOES??");
            return;
        }
    }

    if (!eglQueryDmaBufModifiersEXT)
        eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");

    // firstly, plant a listener for the frame
    frameCb = zwlr_screencopy_manager_v1_capture_output(g_pHyprlock->getScreencopy(), false, output_->output);

    scdata.frame = this;

    zwlr_screencopy_frame_v1_add_listener(frameCb, &wlrFrameListener, &scdata);
}

CDMAFrame::~CDMAFrame() {
    if (g_pEGL)
        eglDestroyImage(g_pEGL->eglDisplay, image);

    // leaks bo and stuff but lives throughout so for now who cares
}

bool CDMAFrame::onBufferDone() {
    uint32_t flags = GBM_BO_USE_RENDERING;

    if (!eglQueryDmaBufModifiersEXT) {
        Debug::log(WARN, "Querying modifiers without eglQueryDmaBufModifiersEXT support");
        bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, scdata.w, scdata.h, scdata.fmt, flags);
    } else {
        std::vector<uint64_t> mods;
        mods.resize(64);
        std::vector<EGLBoolean> externalOnly;
        externalOnly.resize(64);
        int num = 0;
        if (!eglQueryDmaBufModifiersEXT(g_pEGL->eglDisplay, scdata.fmt, 64, mods.data(), externalOnly.data(), &num) || num == 0) {
            Debug::log(WARN, "eglQueryDmaBufModifiersEXT failed, falling back to regular bo");
            bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, scdata.w, scdata.h, scdata.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.push_back(mods[i]);
            }

            uint64_t zero      = 0;
            bool     hasLinear = std::find(goodMods.begin(), goodMods.end(), 0) != goodMods.end();

            bo = gbm_bo_create_with_modifiers2(g_pHyprlock->dma.gbmDevice, scdata.w, scdata.h, scdata.fmt, hasLinear ? &zero : goodMods.data(), hasLinear ? 1 : goodMods.size(),
                                               flags);
        }
    }

    if (!bo) {
        Debug::log(ERR, "Couldn't create a drm buffer");
        return false;
    }

    planes = gbm_bo_get_plane_count(bo);

    uint64_t mod = gbm_bo_get_modifier(bo);
    Debug::log(LOG, "bo chose modifier {:x}", mod);

    zwp_linux_buffer_params_v1* params = zwp_linux_dmabuf_v1_create_params((zwp_linux_dmabuf_v1*)g_pHyprlock->dma.linuxDmabuf);
    if (!params) {
        Debug::log(ERR, "zwp_linux_dmabuf_v1_create_params failed");
        gbm_bo_destroy(bo);
        return false;
    }

    for (size_t plane = 0; plane < (size_t)planes; plane++) {
        size[plane]   = 0;
        stride[plane] = gbm_bo_get_stride_for_plane(bo, plane);
        offset[plane] = gbm_bo_get_offset(bo, plane);
        fd[plane]     = gbm_bo_get_fd_for_plane(bo, plane);

        if (fd[plane] < 0) {
            Debug::log(ERR, "gbm_bo_get_fd_for_plane failed");
            zwp_linux_buffer_params_v1_destroy(params);
            gbm_bo_destroy(bo);
            for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) {
                close(fd[plane_tmp]);
            }
            return false;
        }

        zwp_linux_buffer_params_v1_add(params, fd[plane], plane, offset[plane], stride[plane], mod >> 32, mod & 0xffffffff);
    }

    wlBuffer = zwp_linux_buffer_params_v1_create_immed(params, scdata.w, scdata.h, scdata.fmt, 0);
    zwp_linux_buffer_params_v1_destroy(params);

    if (!wlBuffer) {
        Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed");
        gbm_bo_destroy(bo);
        for (size_t plane = 0; plane < (size_t)planes; plane++)
            close(fd[plane]);

        return false;
    }

    return true;
}

bool CDMAFrame::onBufferReady() {
    static const int general_attribs    = 3;
    static const int plane_attribs      = 5;
    static const int entries_per_attrib = 2;
    EGLAttrib        attribs[(general_attribs + plane_attribs * 4) * entries_per_attrib + 1];
    int              attr = 0;
    Vector2D         size{scdata.w, scdata.h};

    attribs[attr++] = EGL_WIDTH;
    attribs[attr++] = size.x;
    attribs[attr++] = EGL_HEIGHT;
    attribs[attr++] = size.y;
    attribs[attr++] = EGL_LINUX_DRM_FOURCC_EXT;
    attribs[attr++] = scdata.fmt;
    attribs[attr++] = EGL_DMA_BUF_PLANE0_FD_EXT;
    attribs[attr++] = fd[0];
    attribs[attr++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
    attribs[attr++] = offset[0];
    attribs[attr++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
    attribs[attr++] = stride[0];
    attribs[attr]   = EGL_NONE;

    image = eglCreateImage(g_pEGL->eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);

    if (image == EGL_NO_IMAGE) {
        Debug::log(ERR, "failed creating an egl image");
        return false;
    }

    asset.texture.allocate();
    asset.texture.m_vSize = size;
    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, image);
    glBindTexture(GL_TEXTURE_2D, 0);

    Debug::log(LOG, "Got dma frame with size {}", size);

    asset.ready = true;

    return true;
}07070100000038000081A4000000000000000000000001669CF85C000003A5000000000000000000000000000000000000002900000000hyprlock-0.4.1/src/renderer/DMAFrame.hpp#pragma once

#include "../core/Output.hpp"
#include <gbm.h>
#include "Shared.hpp"

struct zwlr_screencopy_frame_v1;

class CDMAFrame;

struct SScreencopyData {
    int        w = 0, h = 0;
    uint32_t   fmt;
    size_t     size;
    size_t     stride;
    CDMAFrame* frame = nullptr;
};

class CDMAFrame {
  public:
    static std::string getResourceId(COutput* output);

    CDMAFrame(COutput* mon);
    ~CDMAFrame();

    bool            onBufferDone();
    bool            onBufferReady();

    wl_buffer*      wlBuffer = nullptr;

    std::string     resourceID;

    SPreloadedAsset asset;

  private:
    gbm_bo*                   bo = nullptr;

    int                       planes = 0;

    int                       fd[4];
    uint32_t                  size[4], stride[4], offset[4];

    zwlr_screencopy_frame_v1* frameCb = nullptr;
    SScreencopyData           scdata;

    EGLImage                  image = nullptr;
};07070100000039000081A4000000000000000000000001669CF85C00001013000000000000000000000000000000000000002C00000000hyprlock-0.4.1/src/renderer/Framebuffer.cpp#include "Framebuffer.hpp"
#include "../helpers/Log.hpp"
#include <libdrm/drm_fourcc.h>

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(LOG, "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::release() {
    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() {
    release();
}

bool CFramebuffer::isAllocated() {
    return m_iFb != (GLuint)-1;
}0707010000003A000081A4000000000000000000000001669CF85C000001C7000000000000000000000000000000000000002C00000000hyprlock-0.4.1/src/renderer/Framebuffer.hpp#pragma once

#include "../helpers/Vector2D.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      release();
    void      reset();
    bool      isAllocated();

    Vector2D  m_vSize;

    CTexture  m_cTex;
    GLuint    m_iFb = -1;

    CTexture* m_pStencilTex = nullptr;
};0707010000003B000081A4000000000000000000000001669CF85C00005343000000000000000000000000000000000000002900000000hyprlock-0.4.1/src/renderer/Renderer.cpp#include "Renderer.hpp"
#include "../core/Egl.hpp"
#include "../config/ConfigManager.hpp"
#include "../helpers/Color.hpp"
#include "../core/Output.hpp"
#include "../core/hyprlock.hpp"
#include "../renderer/DMAFrame.hpp"
#include "mtx.hpp"
#include <GLES3/gl32.h>
#include <GLES3/gl3ext.h>
#include <algorithm>
#include "Shaders.hpp"
#include "src/helpers/Log.hpp"
#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, (const GLchar**)&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.c_str());

    auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag);

    RASSERT(fragCompiled, "Compiling shader failed. FRAGMENT NULL! Shader source:\n\n{}", frag.c_str());

    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, 0);

    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, 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");

    wlr_matrix_identity(projMatrix.data());

    asyncResourceGatherer = std::make_unique<CAsyncResourceGatherer>();
}

static int  frames         = 0;
static bool firstFullFrame = false;

//
CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf) {
    static auto* const PDISABLEBAR = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:disable_loading_bar");
    static auto* const PNOFADEIN   = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:no_fade_in");
    static auto* const PNOFADEOUT  = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:no_fade_out");

    matrixProjection(projection.data(), surf.size.x, surf.size.y, WL_OUTPUT_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;
    float           bga           = 0.0;
    const bool      WAITFORASSETS = !g_pHyprlock->m_bImmediateRender && !asyncResourceGatherer->gathered;

    if (WAITFORASSETS) {

        // render status
        if (!**PDISABLEBAR) {
            CBox progress = {0, 0, asyncResourceGatherer->progress * surf.size.x, 2};
            renderRect(progress, CColor{0.2f, 0.1f, 0.1f, 1.f}, 0);
        }
    } else {

        if (!firstFullFrame) {
            firstFullFrameTime = std::chrono::system_clock::now();
            firstFullFrame     = true;
        }

        bga = std::clamp(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - firstFullFrameTime).count() / 500000.0, 0.0, 1.0);

        if (**PNOFADEIN)
            bga = 1.0;

        if (g_pHyprlock->m_bFadeStarted && !**PNOFADEOUT) {
            bga =
                std::clamp(std::chrono::duration_cast<std::chrono::microseconds>(g_pHyprlock->m_tFadeEnds - std::chrono::system_clock::now()).count() / 500000.0 - 0.02, 0.0, 1.0);
            // - 0.02 so that the fade ends a little earlier than the final second
        }
        // render widgets
        const auto WIDGETS = getOrCreateWidgetsFor(&surf);
        for (auto& w : *WIDGETS) {
            feedback.needsFrame = w->draw({bga}) || feedback.needsFrame;
        }
    }

    frames++;

    Debug::log(TRACE, "frame {}", frames);

    feedback.needsFrame = feedback.needsFrame || !asyncResourceGatherer->gathered || bga < 1.0;

    glDisable(GL_BLEND);

    return feedback;
}

void CRenderer::renderRect(const CBox& box, const CColor& col, int rounding) {
    float matrix[9];
    wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, box.rot,
                           projMatrix.data()); // TODO: write own, don't use WLR here

    float glMatrix[9];
    wlr_matrix_multiply(glMatrix, projection.data(), matrix);

    glUseProgram(rectShader.program);

    glUniformMatrix3fv(rectShader.proj, 1, GL_TRUE, glMatrix);

    // 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(box.x, box.y);
    const auto FULLSIZE = Vector2D(box.width, box.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::renderTexture(const CBox& box, const CTexture& tex, float a, int rounding, std::optional<wl_output_transform> tr) {
    float matrix[9];
    wlr_matrix_project_box(matrix, &box, tr.value_or(WL_OUTPUT_TRANSFORM_FLIPPED_180) /* ugh coordinate spaces */, box.rot,
                           projMatrix.data()); // TODO: write own, don't use WLR here

    float glMatrix[9];
    wlr_matrix_multiply(glMatrix, projection.data(), matrix);

    CShader* shader = &texShader;

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(tex.m_iTarget, tex.m_iTexID);

    glUseProgram(shader->program);

    glUniformMatrix3fv(shader->proj, 1, GL_TRUE, glMatrix);
    glUniform1i(shader->tex, 0);
    glUniform1f(shader->alpha, a);
    const auto TOPLEFT  = Vector2D(box.x, box.y);
    const auto FULLSIZE = Vector2D(box.width, box.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);
}

std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CSessionLockSurface* surf) {
    if (!widgets.contains(surf)) {

        auto CWIDGETS = g_pConfigManager->getWidgetConfigs();

        std::sort(CWIDGETS.begin(), CWIDGETS.end(), [](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"));
        });

        for (auto& c : CWIDGETS) {
            if (!c.monitor.empty() && c.monitor != surf->output->stringPort && !surf->output->stringDesc.starts_with(c.monitor))
                continue;

            // by type
            if (c.type == "background") {
                const std::string PATH = std::any_cast<Hyprlang::STRING>(c.values.at("path"));

                std::string       resourceID = "";
                if (PATH == "screenshot") {
                    resourceID = CDMAFrame::getResourceId(surf->output);
                    // 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 (asyncResourceGatherer->gathered) {
                        if (!asyncResourceGatherer->getAssetByID(resourceID))
                            resourceID = ""; // Fallback to solid color (background:color)
                    }

                    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;

                widgets[surf].emplace_back(std::make_unique<CBackground>(surf->size, surf->output, resourceID, c.values, PATH == "screenshot"));
            } else if (c.type == "input-field") {
                widgets[surf].emplace_back(std::make_unique<CPasswordInputField>(surf->size, c.values, surf->output->stringPort));
            } else if (c.type == "label") {
                widgets[surf].emplace_back(std::make_unique<CLabel>(surf->size, c.values, surf->output->stringPort));
            } else if (c.type == "shape") {
                widgets[surf].emplace_back(std::make_unique<CShape>(surf->size, c.values));
            } else if (c.type == "image") {
                const std::string PATH = std::any_cast<Hyprlang::STRING>(c.values.at("path"));

                std::string       resourceID = "";
                if (!PATH.empty())
                    resourceID = "image:" + PATH;

                widgets[surf].emplace_back(std::make_unique<CImage>(surf->size, surf->output, resourceID, c.values));
            }
        }
    }

    return &widgets[surf];
}

void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
    glDisable(GL_BLEND);
    glDisable(GL_STENCIL_TEST);

    float matrix[9];
    CBox  box{0, 0, outfb.m_vSize.x, outfb.m_vSize.y};
    wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
                           projMatrix.data()); // TODO: write own, don't use WLR here

    float glMatrix[9];
    wlr_matrix_multiply(glMatrix, projection.data(), 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);
        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);
        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);
        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, WL_OUTPUT_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(const CSessionLockSurface* surf) {
    widgets.erase(surf);
}
0707010000003C000081A4000000000000000000000001669CF85C0000091A000000000000000000000000000000000000002900000000hyprlock-0.4.1/src/renderer/Renderer.hpp#pragma once

#include <memory>
#include <chrono>
#include <optional>
#include "../core/LockSurface.hpp"
#include "Shader.hpp"
#include "../helpers/Box.hpp"
#include "../helpers/Color.hpp"
#include "AsyncResourceGatherer.hpp"
#include "widgets/IWidget.hpp"
#include "Framebuffer.hpp"

typedef std::unordered_map<const CSessionLockSurface*, std::vector<std::unique_ptr<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<CColor> colorize;
        float                 boostA = 1.0;
    };

    SRenderFeedback                         renderLock(const CSessionLockSurface& surface);

    void                                    renderRect(const CBox& box, const CColor& col, int rounding = 0);
    void                                    renderTexture(const CBox& box, const CTexture& tex, float a = 1.0, int rounding = 0, std::optional<wl_output_transform> tr = {});
    void                                    blurFB(const CFramebuffer& outfb, SBlurParams params);

    std::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer;
    std::chrono::system_clock::time_point   firstFullFrameTime;

    void                                    pushFb(GLint fb);
    void                                    popFb();

    void                                    removeWidgetsFor(const CSessionLockSurface* surf);

  private:
    widgetMap_t                            widgets;

    std::vector<std::unique_ptr<IWidget>>* getOrCreateWidgetsFor(const CSessionLockSurface* surf);

    CShader                                rectShader;
    CShader                                texShader;
    CShader                                blurShader1;
    CShader                                blurShader2;
    CShader                                blurPrepareShader;
    CShader                                blurFinishShader;

    std::array<float, 9>                   projMatrix;
    std::array<float, 9>                   projection;

    std::vector<GLint>                     boundFBs;
};

inline std::unique_ptr<CRenderer> g_pRenderer;0707010000003D000081A4000000000000000000000001669CF85C000001CB000000000000000000000000000000000000002700000000hyprlock-0.4.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;
}0707010000003E000081A4000000000000000000000001669CF85C000006B2000000000000000000000000000000000000002700000000hyprlock-0.4.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   alpha             = -1;
    GLint   posAttrib         = -1;
    GLint   texAttrib         = -1;
    GLint   matteTexAttrib    = -1;
    GLint   discardOpaque     = -1;
    GLint   discardAlpha      = -1;
    GLfloat discardAlphaValue = -1;

    GLint   topLeft               = -1;
    GLint   bottomRight           = -1;
    GLint   fullSize              = -1;
    GLint   fullSizeUntransformed = -1;
    GLint   radius                = -1;
    GLint   radiusOuter           = -1;

    GLint   thick = -1;

    GLint   halfpixel = -1;

    GLint   range         = -1;
    GLint   shadowPower   = -1;
    GLint   useAlphaMatte = -1; // always inverted

    GLint   applyTint = -1;
    GLint   tint      = -1;

    GLint   gradient       = -1;
    GLint   gradientLength = -1;
    GLint   angle          = -1;

    GLint   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;
};0707010000003F000081A4000000000000000000000001669CF85C00002521000000000000000000000000000000000000002800000000hyprlock-0.4.1/src/renderer/Shaders.hpp#pragma once

#include <string>

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

    if (pixCoord.x + pixCoord.y > radius) {

	    float dist = length(pixCoord);

	    if (dist > radius + 1.0)
	        discard;

	    if (dist > radius - 1.0) {
	        float dist = length(pixCoord);

            float normalized = 1.0 - smoothstep(0.0, 1.0, dist - radius + 0.5);

	        )#" +
        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 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;
}
)#";07070100000040000081A4000000000000000000000001669CF85C00000072000000000000000000000000000000000000002700000000hyprlock-0.4.1/src/renderer/Shared.hpp#pragma once
#include "Texture.hpp"

struct SPreloadedAsset {
    CTexture texture;
    bool     ready = false;
};07070100000041000081A4000000000000000000000001669CF85C0000017F000000000000000000000000000000000000002800000000hyprlock-0.4.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;
}07070100000042000081A4000000000000000000000001669CF85C00000225000000000000000000000000000000000000002800000000hyprlock-0.4.1/src/renderer/Texture.hpp#pragma once

#include <GLES3/gl32.h>
#include "../helpers/Vector2D.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;
};07070100000043000081A4000000000000000000000001669CF85C000016E8000000000000000000000000000000000000002400000000hyprlock-0.4.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;
}
07070100000044000041ED000000000000000000000002669CF85C00000000000000000000000000000000000000000000002400000000hyprlock-0.4.1/src/renderer/widgets07070100000045000081A4000000000000000000000001669CF85C00000EAE000000000000000000000000000000000000003300000000hyprlock-0.4.1/src/renderer/widgets/Background.cpp#include "Background.hpp"
#include "../Renderer.hpp"
#include "../mtx.hpp"

CBackground::CBackground(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map<std::string, std::any>& props, bool ss) :
    viewport(viewport_), resourceID(resourceID_), output(output_), isScreenshot(ss) {

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

void CBackground::renderRect(CColor color) {
    CBox monbox = {0, 0, viewport.x, viewport.y};
    g_pRenderer->renderRect(monbox, color, 0);
}

bool CBackground::draw(const SRenderData& data) {

    if (resourceID.empty()) {
        CColor col = color;
        col.a *= data.opacity;
        renderRect(col);
        return data.opacity < 1.0;
    }

    if (!asset)
        asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);

    if (!asset) {
        CColor col = color;
        col.a *= data.opacity;
        renderRect(col);
        return true;
    }

    if (asset->texture.m_iType == TEXTURE_INVALID) {
        g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
        resourceID = "";
        return true;
    }

    if ((blurPasses > 0 || isScreenshot) && !blurredFB.isAllocated()) {
        // make it brah
        Vector2D size = asset->texture.m_vSize;

        if (output->transform % 2 == 1 && isScreenshot) {
            size.x = asset->texture.m_vSize.y;
            size.y = asset->texture.m_vSize.x;
        }

        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();
        blurredFB.alloc(viewport.x, viewport.y); // TODO 10 bit
        blurredFB.bind();

        g_pRenderer->renderTexture(texbox, asset->texture, 1.0, 0,
                                   isScreenshot ?
                                       wlr_output_transform_invert(output->transform) :
                                       WL_OUTPUT_TRANSFORM_NORMAL); // this could be omitted but whatever it's only once and makes code cleaner plus less blurring on large texs
        if (blurPasses > 0)
            g_pRenderer->blurFB(blurredFB, CRenderer::SBlurParams{blurSize, blurPasses, noise, contrast, brightness, vibrancy, vibrancy_darkness});
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    }

    CTexture* tex = blurredFB.isAllocated() ? &blurredFB.m_cTex : &asset->texture;

    CBox      texbox = {{}, tex->m_vSize};

    Vector2D  size   = tex->m_vSize;
    float     scaleX = viewport.x / tex->m_vSize.x;
    float     scaleY = viewport.y / tex->m_vSize.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();
    g_pRenderer->renderTexture(texbox, *tex, data.opacity, 0, WL_OUTPUT_TRANSFORM_FLIPPED_180);

    return data.opacity < 1.0;
}07070100000046000081A4000000000000000000000001669CF85C00000484000000000000000000000000000000000000003300000000hyprlock-0.4.1/src/renderer/widgets/Background.hpp#pragma once

#include "IWidget.hpp"
#include "../../helpers/Vector2D.hpp"
#include "../../helpers/Color.hpp"
#include "../Framebuffer.hpp"
#include <string>
#include <unordered_map>
#include <any>

struct SPreloadedAsset;
class COutput;

class CBackground : public IWidget {
  public:
    CBackground(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props, bool ss_);

    virtual bool draw(const SRenderData& data);
    void         renderRect(CColor color);

  private:
    // if needed
    CFramebuffer     blurredFB;

    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      resourceID;
    CColor           color;
    SPreloadedAsset* asset        = nullptr;
    COutput*         output       = nullptr;
    bool             isScreenshot = false;
};07070100000047000081A4000000000000000000000001669CF85C00001992000000000000000000000000000000000000003000000000hyprlock-0.4.1/src/renderer/widgets/IWidget.cpp#include "IWidget.hpp"
#include "../../helpers/Log.hpp"
#include "../../core/hyprlock.hpp"
#include "src/core/Auth.hpp"
#include <chrono>
#include <unistd.h>
#include <pwd.h>
#include <hyprutils/string/VarList.hpp>

using namespace Hyprutils::String;

#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 190100
#pragma comment(lib, "date-tz")
#include <date/tz.h>
namespace std {
    namespace chrono {
        using date::current_zone;
    }
}
#endif

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 - size.rotated(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;
}

static void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if (from.empty())
        return;
    size_t pos = 0;
    while ((pos = str.find(from, pos)) != std::string::npos) {
        str.replace(pos, from.length(), to);
        pos += to.length();
    }
}

static void replaceAllAttempts(std::string& str) {

    const size_t      ATTEMPTS = g_pHyprlock->getPasswordFailedAttempts();
    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) {

    const auto        LAYOUTIDX  = g_pHyprlock->m_uiActiveLayout;
    const auto        LAYOUTNAME = xkb_keymap_layout_get_name(g_pHyprlock->m_pXKBKeymap, LAYOUTIDX);
    const std::string STR        = LAYOUTNAME ? LAYOUTNAME : "error";
    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);
            const std::string LANG = LANGS[LAYOUTIDX].empty() ? STR : LANGS[LAYOUTIDX] == "!" ? "" : LANGS[LAYOUTIDX];
            str.replace(pos, 9 + REPL.length(), LANG);
            pos += LANG.length();
        } else {
            str.replace(pos, 7, STR);
            pos += STR.length();
        }
    }
}

static std::string getTime() {
    const auto current_zone = std::chrono::current_zone();
    const auto HHMMSS       = std::chrono::hh_mm_ss{current_zone->to_local(std::chrono::system_clock::now()) -
                                              std::chrono::floor<std::chrono::days>(current_zone->to_local(std::chrono::system_clock::now()))};
    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);
}

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;
    replaceAll(in, "$DESC", std::string{user_gecos ? user_gecos : ""});
    replaceAll(in, "$USER", std::string{username ? username : ""});
    replaceAll(in, "<br/>", std::string{"\n"});

    if (in.contains("$TIME")) {
        replaceAll(in, "$TIME", getTime());
        result.updateEveryMs = result.updateEveryMs != 0 && result.updateEveryMs < 1000 ? result.updateEveryMs : 1000;
    }

    if (in.contains("$FAIL")) {
        const auto FAIL = g_pAuth->getLastFailText();
        replaceAll(in, "$FAIL", FAIL.has_value() ? FAIL.value() : "");
        result.allowForceUpdate = true;
    }

    if (in.contains("$PROMPT")) {
        const auto PROMPT = g_pAuth->getLastPrompt();
        replaceAll(in, "$PROMPT", PROMPT.has_value() ? PROMPT.value() : "");
        result.allowForceUpdate = true;
    }

    if (in.contains("$ATTEMPTS")) {
        replaceAllAttempts(in);
        result.allowForceUpdate = true;
    }

    if (in.contains("$LAYOUT")) {
        replaceAllLayout(in);
        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;
}
07070100000048000081A4000000000000000000000001669CF85C00000322000000000000000000000000000000000000003000000000hyprlock-0.4.1/src/renderer/widgets/IWidget.hpp#pragma once

#include "../../helpers/Vector2D.hpp"
#include <string>

class IWidget {
  public:
    struct SRenderData {
        float opacity = 1;
    };
    virtual ~IWidget() = default;

    virtual bool     draw(const SRenderData& data) = 0;

    virtual Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign,
                                    const double& ang = 0);

    struct SFormatResult {
        std::string formatted;
        float       updateEveryMs    = 0; // 0 means don't (static)
        bool        alwaysUpdate     = false;
        bool        cmd              = false;
        bool        allowForceUpdate = false;
    };

    virtual SFormatResult formatString(std::string in);
};
07070100000049000081A4000000000000000000000001669CF85C00001A0E000000000000000000000000000000000000002E00000000hyprlock-0.4.1/src/renderer/widgets/Image.cpp#include "Image.hpp"
#include "../Renderer.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/Log.hpp"
#include <cmath>

CImage::~CImage() {
    if (imageTimer) {
        imageTimer->cancel();
        imageTimer.reset();
    }
}

static void onTimer(std::shared_ptr<CTimer> self, void* data) {
    const auto PIMAGE = (CImage*)data;

    PIMAGE->onTimerUpdate();
    PIMAGE->plantTimer();
}

static void onAssetCallback(void* data) {
    const auto PIMAGE = (CImage*)data;
    PIMAGE->renderSuper();
}

void CImage::onTimerUpdate() {
    const std::string OLDPATH = path;

    if (!reloadCommand.empty()) {
        path = g_pHyprlock->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(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     = onAssetCallback;
    request.callbackData = this;

    g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}

void CImage::plantTimer() {

    if (reloadTime == 0) {
        imageTimer = g_pHyprlock->addTimer(std::chrono::hours(1), onTimer, this, true);
    } else if (reloadTime > 0)
        imageTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), onTimer, this, false);
}

CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map<std::string, std::any>& props) :
    viewport(viewport_), resourceID(resourceID_), output(output_), shadow(this, props, viewport_) {

    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    = std::any_cast<Hyprlang::INT>(props.at("border_color"));
    pos      = std::any_cast<Hyprlang::VEC2>(props.at("position"));
    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"));

    try {
        modificationTime = std::filesystem::last_write_time(path);
    } catch (std::exception& e) { Debug::log(ERR, "{}", e.what()); }

    angle = angle * M_PI / 180.0;

    plantTimer();
}

bool CImage::draw(const SRenderData& data) {

    if (!pendingResourceID.empty()) {
        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.release();

                asset       = newAsset;
                resourceID  = pendingResourceID;
                firstRender = true;
            }
            pendingResourceID = "";
        }
    }

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

        const bool ALLOWROUND = rounding > -1 && rounding < std::min(texbox.w, texbox.h) / 2.0;

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

        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->renderRect(borderBox, color, ALLOWROUND ? (rounding == 0 ? 0 : rounding + std::round(border / M_PI)) : std::min(borderBox.w, borderBox.h) / 2.0);

        texbox.round();
        g_pRenderer->renderTexture(texbox, asset->texture, 1.0, ALLOWROUND ? rounding : std::min(texbox.w, texbox.h) / 2.0, WL_OUTPUT_TRANSFORM_NORMAL);
        g_pRenderer->popFb();
    }

    CTexture* tex    = &imageFB.m_cTex;
    CBox      texbox = {{}, tex->m_vSize};

    if (firstRender) {
        firstRender = false;
        shadow.markShadowDirty();
    }

    shadow.draw(data);

    const auto TEXPOS = posFromHVAlign(viewport, tex->m_vSize, pos, halign, valign, angle);

    texbox.x = TEXPOS.x;
    texbox.y = TEXPOS.y;

    texbox.round();
    texbox.rot = angle;
    g_pRenderer->renderTexture(texbox, *tex, data.opacity, 0, WL_OUTPUT_TRANSFORM_FLIPPED_180);

    return data.opacity < 1.0;
}

static void onAssetCallbackTimer(std::shared_ptr<CTimer> self, void* data) {
    const auto PIMAGE = (CImage*)data;
    PIMAGE->renderSuper();
}

void CImage::renderSuper() {
    g_pHyprlock->renderOutput(output->stringPort);

    if (!pendingResourceID.empty()) /* did not consume the pending resource */
        g_pHyprlock->addTimer(std::chrono::milliseconds(100), onAssetCallbackTimer, this);
}
0707010000004A000081A4000000000000000000000001669CF85C00000733000000000000000000000000000000000000002E00000000hyprlock-0.4.1/src/renderer/widgets/Image.hpp#pragma once

#include "IWidget.hpp"
#include "../../helpers/Vector2D.hpp"
#include "../../helpers/Color.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(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props);
    ~CImage();

    virtual bool draw(const SRenderData& data);

    void         renderSuper();
    void         onTimerUpdate();
    void         plantTimer();

  private:
    CFramebuffer                            imageFB;

    int                                     size;
    int                                     rounding;
    double                                  border;
    double                                  angle;
    CColor                                  color;
    Vector2D                                pos;

    std::string                             halign, valign, path;

    bool                                    firstRender = true;

    int                                     reloadTime;
    std::string                             reloadCommand;
    std::filesystem::file_time_type         modificationTime;
    std::shared_ptr<CTimer>                 imageTimer;
    CAsyncResourceGatherer::SPreloadRequest request;

    Vector2D                                viewport;
    std::string                             resourceID;
    std::string                             pendingResourceID; // if reloading image
    SPreloadedAsset*                        asset  = nullptr;
    COutput*                                output = nullptr;
    CShadowable                             shadow;
};
0707010000004B000081A4000000000000000000000001669CF85C000013AE000000000000000000000000000000000000002E00000000hyprlock-0.4.1/src/renderer/widgets/Label.cpp#include "Label.hpp"
#include "../../helpers/Color.hpp"
#include <hyprlang.hpp>
#include <stdexcept>
#include "../Renderer.hpp"
#include "../../helpers/Log.hpp"
#include "../../core/hyprlock.hpp"

CLabel::~CLabel() {
    if (labelTimer) {
        labelTimer->cancel();
        labelTimer.reset();
    }
}

static void onTimer(std::shared_ptr<CTimer> self, void* data) {
    if (data == nullptr)
        return;
    const auto PLABEL = (CLabel*)data;

    // update label
    PLABEL->onTimerUpdate();

    // plant new timer
    PLABEL->plantTimer();
}

static void onAssetCallback(void* data) {
    const auto PLABEL = (CLabel*)data;
    PLABEL->renderSuper();
}

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     = onAssetCallback;
    request.callbackData = this;

    g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}

void CLabel::plantTimer() {
    if (label.updateEveryMs != 0)
        labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), onTimer, this, label.allowForceUpdate);
    else if (label.updateEveryMs == 0 && label.allowForceUpdate)
        labelTimer = g_pHyprlock->addTimer(std::chrono::hours(1), onTimer, this, true);
}

CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, const std::string& output) :
    outputStringPort(output), shadow(this, props, viewport_) {
    try {
        labelPreFormat         = std::any_cast<Hyprlang::STRING>(props.at("text"));
        std::string textAlign  = std::any_cast<Hyprlang::STRING>(props.at("text_align"));
        std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
        CColor      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;

        g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);

        auto POS__ = std::any_cast<Hyprlang::VEC2>(props.at("position"));
        pos        = {POS__.x, POS__.y};
        configPos  = pos;

        viewport = 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"));
        angle = angle * M_PI / 180.0;

        plantTimer();
    } catch (const std::bad_any_cast& e) {
        Debug::log(ERR, "Failed to construct CLabel: {}", e.what());
        throw;
    } catch (const std::out_of_range& e) {
        Debug::log(ERR, "Missing propperty for CLabel:{}", e.what());
        throw;
    }
}

bool CLabel::draw(const SRenderData& data) {
    if (!asset) {
        asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);

        if (!asset)
            return true;

        shadow.markShadowDirty();
    }

    if (!pendingResourceID.empty()) {
        // new asset is pending
        auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
        if (newAsset) {
            // new asset is ready :D
            g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
            asset             = newAsset;
            resourceID        = pendingResourceID;
            pendingResourceID = "";
            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::renderSuper() {
    g_pHyprlock->renderOutput(outputStringPort);
}
0707010000004C000081A4000000000000000000000001669CF85C000005E6000000000000000000000000000000000000002E00000000hyprlock-0.4.1/src/renderer/widgets/Label.hpp#pragma once

#include "IWidget.hpp"
#include "Shadowable.hpp"
#include "../../helpers/Vector2D.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(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props, const std::string& output);
    ~CLabel();

    virtual bool draw(const SRenderData& data);

    void         renderSuper();
    void         onTimerUpdate();
    void         plantTimer();

  private:
    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;
    SPreloadedAsset*                        asset = nullptr;

    std::string                             outputStringPort;

    CAsyncResourceGatherer::SPreloadRequest request;

    std::shared_ptr<CTimer>                 labelTimer = nullptr;

    CShadowable                             shadow;
};
0707010000004D000081A4000000000000000000000001669CF85C000056B6000000000000000000000000000000000000003B00000000hyprlock-0.4.1/src/renderer/widgets/PasswordInputField.cpp#include "PasswordInputField.hpp"
#include "../Renderer.hpp"
#include "../../core/hyprlock.hpp"
#include "src/core/Auth.hpp"
#include <algorithm>

static void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if (from.empty())
        return;
    size_t pos = 0;
    while ((pos = str.find(from, pos)) != std::string::npos) {
        str.replace(pos, from.length(), to);
        pos += to.length();
    }
}

CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, const std::string& output) :
    outputStringPort(output), shadow(this, props, viewport_) {
    size                     = std::any_cast<Hyprlang::VEC2>(props.at("size"));
    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"));
    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"));
    configFailTimeoutMs      = std::any_cast<Hyprlang::INT>(props.at("fail_timeout"));
    col.transitionMs         = std::any_cast<Hyprlang::INT>(props.at("fail_transition"));
    col.outer                = std::any_cast<Hyprlang::INT>(props.at("outer_color"));
    col.inner                = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
    col.font                 = std::any_cast<Hyprlang::INT>(props.at("font_color"));
    col.fail                 = std::any_cast<Hyprlang::INT>(props.at("fail_color"));
    col.check                = std::any_cast<Hyprlang::INT>(props.at("check_color"));
    col.both                 = std::any_cast<Hyprlang::INT>(props.at("bothlock_color"));
    col.caps                 = std::any_cast<Hyprlang::INT>(props.at("capslock_color"));
    col.num                  = std::any_cast<Hyprlang::INT>(props.at("numlock_color"));
    col.invertNum            = std::any_cast<Hyprlang::INT>(props.at("invert_numlock"));
    col.swapFont             = std::any_cast<Hyprlang::INT>(props.at("swap_font_color"));
    viewport                 = viewport_;

    auto POS__ = std::any_cast<Hyprlang::VEC2>(props.at("position"));
    pos        = {POS__.x, POS__.y};
    configPos  = pos;
    configSize = size;

    halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
    valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));

    pos              = posFromHVAlign(viewport, size, pos, halign, valign);
    dots.size        = std::clamp(dots.size, 0.2f, 0.8f);
    dots.spacing     = std::clamp(dots.spacing, 0.f, 1.f);
    col.transitionMs = std::clamp(col.transitionMs, 0, 1000);

    col.both = col.both == -1 ? col.outer : col.both;
    col.caps = col.caps == -1 ? col.outer : col.caps;
    col.num  = col.num == -1 ? col.outer : col.num;

    g_pHyprlock->m_bNumLock = col.invertNum;

    // Render placeholder if either placeholder_text or fail_text are non-empty
    // as placeholder must be rendered to show fail_text
    if (!configPlaceholderText.empty() || !configFailText.empty()) {
        placeholder.currentText = configPlaceholderText;

        replaceAll(placeholder.currentText, "$PROMPT", "");

        placeholder.resourceID = "placeholder:" + placeholder.currentText + std::to_string((uintptr_t)this);
        CAsyncResourceGatherer::SPreloadRequest request;
        request.id                   = placeholder.resourceID;
        request.asset                = placeholder.currentText;
        request.type                 = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
        request.props["font_family"] = std::string{"Sans"};
        request.props["color"]       = CColor{1.0 - col.font.r, 1.0 - col.font.g, 1.0 - col.font.b, 0.5};
        request.props["font_size"]   = (int)size.y / 4;
        g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
    }
}

static void fadeOutCallback(std::shared_ptr<CTimer> self, void* data) {
    CPasswordInputField* p = (CPasswordInputField*)data;

    p->onFadeOutTimer();
}

void CPasswordInputField::onFadeOutTimer() {
    fade.allowFadeOut = true;
    fade.fadeOutTimer.reset();

    g_pHyprlock->renderOutput(outputStringPort);
}

void CPasswordInputField::updateFade() {
    if (!fadeOnEmpty) {
        fade.a = 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 != 0.0 && (!fade.animated || fade.appearing)) {
        if (fade.allowFadeOut || fadeTimeoutMs == 0) {
            fade.a            = 1.0;
            fade.animated     = true;
            fade.appearing    = false;
            fade.start        = std::chrono::system_clock::now();
            fade.allowFadeOut = false;
        } else if (!fade.fadeOutTimer.get())
            fade.fadeOutTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(fadeTimeoutMs), fadeOutCallback, this);
    }

    if (INPUTUSED && fade.a != 1.0 && (!fade.animated || !fade.appearing)) {
        fade.a         = 0.0;
        fade.animated  = true;
        fade.appearing = true;
        fade.start     = std::chrono::system_clock::now();
    }

    if (fade.animated) {
        if (fade.appearing)
            fade.a = std::clamp(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - fade.start).count() / 100000.0, 0.0, 1.0);
        else
            fade.a = std::clamp(1.0 - std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - fade.start).count() / 100000.0, 0.0, 1.0);

        if ((fade.appearing && fade.a == 1.0) || (!fade.appearing && fade.a == 0.0))
            fade.animated = false;

        redrawShadow = true;
    }
}

void CPasswordInputField::updateDots() {
    if (passwordLength == dots.currentAmount)
        return;

    if (std::abs(passwordLength - dots.currentAmount) > 1) {
        dots.currentAmount = std::clamp(dots.currentAmount, passwordLength - 1.f, passwordLength + 1.f);
        dots.lastFrame     = std::chrono::system_clock::now();
    }

    const auto  DELTA = std::clamp((int)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - dots.lastFrame).count(), 0, 20000);

    const float TOADD = DELTA / 1000000.0 * dots.speedPerSecond;

    if (passwordLength > dots.currentAmount) {
        dots.currentAmount += TOADD;
        if (dots.currentAmount > passwordLength)
            dots.currentAmount = passwordLength;
    } else if (passwordLength < dots.currentAmount) {
        dots.currentAmount -= TOADD;
        if (dots.currentAmount < passwordLength)
            dots.currentAmount = passwordLength;
    }

    dots.lastFrame = std::chrono::system_clock::now();
}

bool CPasswordInputField::draw(const SRenderData& data) {
    CBox inputFieldBox = {pos, size};
    CBox outerBox      = {pos - Vector2D{outThick, outThick}, size + Vector2D{outThick * 2, outThick * 2}};

    if (firstRender || redrawShadow) {
        firstRender  = false;
        redrawShadow = false;
        shadow.markShadowDirty();
    }

    bool forceReload = false;

    passwordLength = g_pHyprlock->getPasswordBufferDisplayLen();
    checkWaiting   = g_pAuth->checkWaiting();

    updateFade();
    updateDots();
    updatePlaceholder();
    updateColors();
    updateHiddenInputState();

    static auto TIMER = std::chrono::system_clock::now();

    if (placeholder.asset) {
        const auto TARGETSIZEX = placeholder.asset->texture.m_vSize.x + inputFieldBox.h;

        if (size.x < TARGETSIZEX) {
            const auto DELTA = std::clamp((int)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - TIMER).count(), 8000, 20000);
            TIMER            = std::chrono::system_clock::now();
            forceReload      = true;

            size.x += std::clamp((TARGETSIZEX - size.x) * DELTA / 100000.0, 1.0, 1000.0);

            if (size.x > TARGETSIZEX) {
                size.x       = TARGETSIZEX;
                redrawShadow = true;
            }
        }

        pos = posFromHVAlign(viewport, size, configPos, halign, valign);
    } else if (size.x != configSize.x) {
        size.x = configSize.x;
        pos    = posFromHVAlign(viewport, size, configPos, halign, valign);
    }

    SRenderData shadowData = data;
    shadowData.opacity *= fade.a;
    shadow.draw(shadowData);

    CColor outerCol = col.outer;
    outerCol.a *= fade.a * data.opacity;
    CColor innerCol = col.inner;
    innerCol.a *= fade.a * data.opacity;
    CColor fontCol = col.font;
    fontCol.a *= fade.a * data.opacity;

    if (outThick > 0) {
        g_pRenderer->renderRect(outerBox, outerCol, rounding == -1 ? outerBox.h / 2.0 : rounding);

        if (passwordLength != 0 && hiddenInputState.enabled && !fade.animated && data.opacity == 1.0) {
            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->renderRect(outerBox, hiddenInputState.lastColor, rounding == -1 ? outerBox.h / 2.0 : rounding);
            glScissor(0, 0, viewport.x, viewport.y);
            glDisable(GL_SCISSOR_TEST);
        }
    }

    g_pRenderer->renderRect(inputFieldBox, innerCol, rounding == -1 ? inputFieldBox.h / 2.0 : rounding - outThick);

    if (!hiddenInputState.enabled && !g_pHyprlock->m_bFadeStarted) {
        const int   PASS_SIZE      = std::nearbyint(inputFieldBox.h * dots.size * 0.5f) * 2.f;
        const int   PASS_SPACING   = std::floor(PASS_SIZE * dots.spacing);
        const int   DOT_PAD        = (inputFieldBox.h - PASS_SIZE) / 2;
        const int   DOT_AREA_WIDTH = inputFieldBox.w - DOT_PAD * 2;                                 // avail width for dots
        const int   MAX_DOTS       = std::round(DOT_AREA_WIDTH * 1.0 / (PASS_SIZE + PASS_SPACING)); // max amount of dots that can fit in the area
        const int   DOT_FLOORED    = std::floor(dots.currentAmount);
        const float DOT_ALPHA      = fontCol.a;

        // Calculate the total width required for all dots including spaces between them
        const int TOTAL_DOTS_WIDTH = (PASS_SIZE + PASS_SPACING) * dots.currentAmount - PASS_SPACING;

        // Calculate starting x-position to ensure dots stay centered within the input field
        int xstart = dots.center ? (DOT_AREA_WIDTH - TOTAL_DOTS_WIDTH) / 2 + DOT_PAD : DOT_PAD;

        if (dots.currentAmount > MAX_DOTS)
            xstart = (inputFieldBox.w + MAX_DOTS * (PASS_SIZE + PASS_SPACING) - PASS_SPACING - 2 * TOTAL_DOTS_WIDTH) / 2;

        if (dots.rounding == -1)
            dots.rounding = PASS_SIZE / 2.0;
        else if (dots.rounding == -2)
            dots.rounding = rounding == -1 ? PASS_SIZE / 2.0 : rounding * dots.size;

        for (int i = 0; i < dots.currentAmount; ++i) {
            if (i < DOT_FLOORED - MAX_DOTS)
                continue;

            if (dots.currentAmount != DOT_FLOORED) {
                if (i == DOT_FLOORED)
                    fontCol.a *= (dots.currentAmount - DOT_FLOORED) * data.opacity;
                else if (i == DOT_FLOORED - MAX_DOTS)
                    fontCol.a *= (1 - dots.currentAmount + DOT_FLOORED) * data.opacity;
            }

            Vector2D dotPosition =
                inputFieldBox.pos() + Vector2D{xstart + (int)inputFieldBox.w % 2 / 2.f + i * (PASS_SIZE + PASS_SPACING), inputFieldBox.h / 2.f - PASS_SIZE / 2.f};
            CBox box{dotPosition, Vector2D{PASS_SIZE, PASS_SIZE}};
            g_pRenderer->renderRect(box, fontCol, dots.rounding);
            fontCol.a = DOT_ALPHA;
        }
    }

    if (passwordLength == 0 && !placeholder.resourceID.empty()) {
        SPreloadedAsset* currAsset = nullptr;

        if (!placeholder.asset)
            placeholder.asset = g_pRenderer->asyncResourceGatherer->getAssetByID(placeholder.resourceID);

        currAsset = placeholder.asset;

        if (currAsset) {
            Vector2D pos = outerBox.pos() + outerBox.size() / 2.f;
            pos          = pos - currAsset->texture.m_vSize / 2.f;
            CBox textbox{pos, currAsset->texture.m_vSize};
            g_pRenderer->renderTexture(textbox, currAsset->texture, data.opacity * fade.a, 0);
        } else
            forceReload = true;
    }

    return dots.currentAmount != passwordLength || fade.animated || col.animated || redrawShadow || data.opacity < 1.0 || forceReload;
}

static void failTimeoutCallback(std::shared_ptr<CTimer> self, void* data) {
    g_pAuth->m_bDisplayFailText = false;
    g_pHyprlock->renderAllOutputs();
}

void CPasswordInputField::updatePlaceholder() {
    if (passwordLength != 0) {
        if (placeholder.asset && /* keep prompt asset cause it is likely to be used again */ placeholder.isFailText) {
            std::erase(placeholder.registeredResourceIDs, placeholder.resourceID);
            g_pRenderer->asyncResourceGatherer->unloadAsset(placeholder.asset);
            placeholder.asset      = nullptr;
            placeholder.resourceID = "";
            redrawShadow           = true;
        }
        return;
    }

    const auto AUTHFEEDBACK = g_pAuth->m_bDisplayFailText ? g_pAuth->getLastFailText().value_or("Ups, no fail text?") : g_pAuth->getLastPrompt().value_or("Ups, no prompt?");

    if (placeholder.lastAuthFeedback == AUTHFEEDBACK && g_pHyprlock->getPasswordFailedAttempts() == placeholder.failedAttempts)
        return;

    placeholder.failedAttempts   = g_pHyprlock->getPasswordFailedAttempts();
    placeholder.isFailText       = g_pAuth->m_bDisplayFailText;
    placeholder.lastAuthFeedback = AUTHFEEDBACK;

    placeholder.asset = nullptr;

    if (placeholder.isFailText) {
        g_pHyprlock->addTimer(std::chrono::milliseconds(configFailTimeoutMs), failTimeoutCallback, nullptr);
        placeholder.currentText = configFailText;
        replaceAll(placeholder.currentText, "$FAIL", AUTHFEEDBACK);
        replaceAll(placeholder.currentText, "$ATTEMPTS", std::to_string(placeholder.failedAttempts));
    } else {
        placeholder.currentText = configPlaceholderText;
        replaceAll(placeholder.currentText, "$PROMPT", AUTHFEEDBACK);
    }

    placeholder.resourceID = "placeholder:" + placeholder.currentText + std::to_string((uintptr_t)this);
    if (std::find(placeholder.registeredResourceIDs.begin(), placeholder.registeredResourceIDs.end(), placeholder.resourceID) != placeholder.registeredResourceIDs.end())
        return;

    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"] = std::string{"Sans"};
    request.props["color"]       = (placeholder.isFailText) ? col.fail : col.font;
    request.props["font_size"]   = (int)size.y / 4;
    g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}

void CPasswordInputField::updateHiddenInputState() {
    if (!hiddenInputState.enabled || (size_t)hiddenInputState.lastPasswordLength == passwordLength)
        return;

    // randomize new thang
    hiddenInputState.lastPasswordLength = passwordLength;

    srand(std::chrono::system_clock::now().time_since_epoch().count());
    float r1 = (rand() % 100) / 255.0;
    float r2 = (rand() % 100) / 255.0;
    int   r3 = rand() % 3;
    int   r4 = rand() % 2;
    int   r5 = rand() % 2;

    ((float*)&hiddenInputState.lastColor.r)[r3]            = r1 + 155 / 255.0;
    ((float*)&hiddenInputState.lastColor.r)[(r3 + r4) % 3] = r2 + 155 / 255.0;

    for (int i = 0; i < 3; ++i) {
        if (i != r3 && i != ((r3 + r4) % 3)) {
            ((float*)&hiddenInputState.lastColor.r)[i] = 1.0 - ((float*)&hiddenInputState.lastColor.r)[r5 ? r3 : ((r3 + r4) % 3)];
        }
    }

    hiddenInputState.lastColor.a  = 1.0;
    hiddenInputState.lastQuadrant = (hiddenInputState.lastQuadrant + rand() % 3 + 1) % 4;
}

static void changeChannel(const float& source, const float& target, float& subject, const double& multi, bool& animated) {

    const float DELTA = target - source;

    if (subject != target) {
        subject += DELTA * multi;
        animated = true;

        if ((source < target && subject > target) || (source > target && subject < target))
            subject = target;
    }
}

static void changeColor(const CColor& source, const CColor& target, CColor& subject, const double& multi, bool& animated) {

    changeChannel(source.r, target.r, subject.r, multi, animated);
    changeChannel(source.g, target.g, subject.g, multi, animated);
    changeChannel(source.b, target.b, subject.b, multi, animated);
    changeChannel(source.a, target.a, subject.a, multi, animated);
}

void CPasswordInputField::updateColors() {
    static auto OUTER = col.outer, TARGET = OUTER, SOURCE = OUTER;
    static auto INNER = col.inner, ITARGET = INNER, ISOURCE = INNER;
    static auto FONT = col.font, FTARGET = FONT, FSOURCE = FONT;

    const bool  BORDERLESS = outThick == 0;

    if (col.animated) {
        // some cases when events happen too quick (within transitionMs)
        // TODO: find more?
        const bool LOCKCHANGED = col.stateNum != (col.invertNum ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock) || col.stateCaps != g_pHyprlock->m_bCapsLock;
        const bool ANIMONCHECK = checkWaiting && (TARGET == (BORDERLESS ? INNER : OUTER) || TARGET == col.fail);

        if (LOCKCHANGED || ANIMONCHECK) {
            const bool EQUALCOLORS = ANIMONCHECK && OUTER == col.check;
            // to avoid throttle when check_color set to the same as outer.
            SOURCE  = BORDERLESS ? (EQUALCOLORS ? INNER : col.inner) : col.outer;
            FSOURCE = EQUALCOLORS ? FONT : col.font;
            ISOURCE = EQUALCOLORS ? INNER : col.inner;
        }
    } else {
        SOURCE  = BORDERLESS ? col.inner : col.outer;
        FSOURCE = col.font;
        ISOURCE = col.inner;
    }

    col.stateNum  = col.invertNum ? !g_pHyprlock->m_bNumLock : g_pHyprlock->m_bNumLock;
    col.stateCaps = g_pHyprlock->m_bCapsLock;

    if (!placeholder.isFailText || passwordLength > 0 || (passwordLength == 0 && checkWaiting)) {
        if (g_pHyprlock->m_bFadeStarted) {
            if (TARGET == col.check)
                SOURCE = BORDERLESS ? col.inner : col.outer;
            col.transitionMs = 100;
            TARGET           = BORDERLESS ? INNER : OUTER;
        } else if (checkWaiting) {
            FTARGET               = col.swapFont ? INNER : FONT;
            const float PASSALPHA = FTARGET.a * 0.5;
            FTARGET.a             = PASSALPHA;

            TARGET  = col.check;
            ITARGET = col.swapFont ? FONT : INNER;
        } else if (col.stateCaps && col.stateNum && col.both != OUTER) {
            TARGET  = col.both;
            FTARGET = col.swapFont && BORDERLESS ? INNER : FONT;
        } else if (col.stateCaps && col.caps != OUTER) {
            TARGET  = col.caps;
            FTARGET = col.swapFont && BORDERLESS ? INNER : FONT;
        } else if (col.stateNum && col.num != OUTER) {
            TARGET  = col.num;
            FTARGET = col.swapFont && BORDERLESS ? INNER : FONT;
        } else {
            // if quickly pressed after failure
            if (col.animated && TARGET == col.fail)
                SOURCE = BORDERLESS ? col.inner : col.outer;

            TARGET  = BORDERLESS ? INNER : OUTER;
            FTARGET = FONT;
            ITARGET = INNER;
        }
    } else {
        FSOURCE               = col.swapFont ? INNER : FONT;
        const float PASSALPHA = FSOURCE.a * 0.5;
        FSOURCE.a             = PASSALPHA;
        FTARGET               = FONT;

        SOURCE  = col.check;
        TARGET  = col.fail;
        ISOURCE = FONT;
        ITARGET = FONT;

        if (fade.animated || fade.a < 1.0) {
            TARGET = BORDERLESS ? INNER : OUTER;
            SOURCE = col.fail;
        }
    }

    col.animated = false;

    const bool SWAPDONE = !BORDERLESS && col.swapFont ? col.inner == ITARGET : true;

    if ((BORDERLESS ? col.inner : col.outer) == TARGET && col.font == FTARGET && SWAPDONE) {
        col.shouldStart = true;
        return;
    }

    if (col.shouldStart) {
        col.lastFrame   = std::chrono::system_clock::now();
        col.shouldStart = false;
    }

    const auto MULTI = col.transitionMs == 0 ?
        1.0 :
        std::clamp(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - col.lastFrame).count() / (double)col.transitionMs, 0.016, 0.5);

    changeColor(SOURCE, TARGET, (BORDERLESS ? col.inner : col.outer), MULTI, col.animated);
    changeColor(FSOURCE, FTARGET, col.font, MULTI, col.animated);
    if (col.swapFont && !BORDERLESS)
        changeColor(ISOURCE, ITARGET, col.inner, MULTI, col.animated);

    col.lastFrame = std::chrono::system_clock::now();
}
0707010000004E000081A4000000000000000000000001669CF85C00000D13000000000000000000000000000000000000003B00000000hyprlock-0.4.1/src/renderer/widgets/PasswordInputField.hpp#pragma once

#include "IWidget.hpp"
#include "../../helpers/Vector2D.hpp"
#include "../../helpers/Color.hpp"
#include "../../core/Timer.hpp"
#include "Shadowable.hpp"
#include <chrono>
#include <vector>
#include <any>
#include <unordered_map>

struct SPreloadedAsset;

class CPasswordInputField : public IWidget {
  public:
    CPasswordInputField(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props, const std::string& output);

    virtual bool draw(const SRenderData& data);
    void         onFadeOutTimer();

  private:
    void        updateDots();
    void        updateFade();
    void        updatePlaceholder();
    void        updateHiddenInputState();
    void        updateColors();

    bool        firstRender  = true;
    bool        redrawShadow = false;
    bool        checkWaiting = false;

    size_t      passwordLength = 0;

    Vector2D    size;
    Vector2D    pos;
    Vector2D    viewport;
    Vector2D    configPos;
    Vector2D    configSize;

    std::string halign, valign, configFailText, outputStringPort, configPlaceholderText;
    uint64_t    configFailTimeoutMs = 2000;

    int         outThick, rounding;

    struct {
        float                                 currentAmount  = 0;
        float                                 speedPerSecond = 5; // actually per... something. I am unsure xD
        std::chrono::system_clock::time_point lastFrame;
        bool                                  center   = false;
        float                                 size     = 0;
        float                                 spacing  = 0;
        int                                   rounding = 0;
    } dots;

    struct {
        std::chrono::system_clock::time_point start;
        float                                 a            = 0;
        bool                                  appearing    = true;
        bool                                  animated     = false;
        std::shared_ptr<CTimer>               fadeOutTimer = nullptr;
        bool                                  allowFadeOut = false;
    } fade;

    struct {
        std::string              resourceID = "";
        SPreloadedAsset*         asset      = nullptr;

        std::string              currentText    = "";
        size_t                   failedAttempts = 0;
        bool                     canGetNewText  = true;
        bool                     isFailText     = false;

        std::string              lastAuthFeedback;

        std::vector<std::string> registeredResourceIDs;

    } placeholder;

    struct {
        CColor lastColor;
        int    lastQuadrant       = 0;
        int    lastPasswordLength = 0;
        bool   enabled            = false;
    } hiddenInputState;

    struct {
        CColor outer;
        CColor inner;
        CColor font;
        CColor fail;
        CColor check;
        CColor caps;
        CColor num;
        CColor both;

        int    transitionMs = 0;
        bool   invertNum    = false;
        bool   animated     = false;
        bool   stateNum     = false;
        bool   stateCaps    = false;
        bool   swapFont     = false;
        bool   shouldStart;

        //
        std::chrono::system_clock::time_point lastFrame;
    } col;

    bool        fadeOnEmpty;
    uint64_t    fadeTimeoutMs;

    CShadowable shadow;
};
0707010000004F000081A4000000000000000000000001669CF85C00000562000000000000000000000000000000000000003300000000hyprlock-0.4.1/src/renderer/widgets/Shadowable.cpp#include "Shadowable.hpp"
#include "../Renderer.hpp"

CShadowable::CShadowable(IWidget* widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_) : 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() {

    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 (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, WL_OUTPUT_TRANSFORM_NORMAL);
    return true;
}07070100000050000081A4000000000000000000000001669CF85C00000322000000000000000000000000000000000000003300000000hyprlock-0.4.1/src/renderer/widgets/Shadowable.hpp#pragma once

#include "../Framebuffer.hpp"
#include "../../helpers/Color.hpp"
#include "IWidget.hpp"

#include <string>
#include <unordered_map>
#include <any>

class CShadowable {
  public:
    CShadowable(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:
    IWidget* widget = nullptr;
    int      size   = 10;
    int      passes = 4;
    float    boostA = 1.0;
    CColor   color{0, 0, 0, 1.0};
    Vector2D viewport;

    // to avoid recursive shadows
    bool         ignoreDraw = false;

    CFramebuffer shadowFB;
};07070100000051000081A4000000000000000000000001669CF85C00000CE1000000000000000000000000000000000000002E00000000hyprlock-0.4.1/src/renderer/widgets/Shape.cpp#include "Shape.hpp"
#include "../Renderer.hpp"
#include <cmath>

CShape::CShape(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) : shadow(this, props, viewport_) {

    size        = std::any_cast<Hyprlang::VEC2>(props.at("size"));
    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"));
    borderColor = std::any_cast<Hyprlang::INT>(props.at("border_color"));
    pos         = std::any_cast<Hyprlang::VEC2>(props.at("position"));
    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"));

    viewport = viewport_;
    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));
            CColor    borderCol = borderColor;
            borderCol.a *= data.opacity;
            g_pRenderer->renderRect(borderBox, borderCol, rounding == -1 ? PIROUND : std::clamp(rounding, 0, PIROUND));
        }

        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 auto MINHALFSHAPE = std::min(shapeBox.w, shapeBox.h) / 2.0;
        const bool ALLOWROUND   = rounding > -1 && rounding < MINHALFSHAPE;

        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->renderRect(borderBox, borderColor, ALLOWROUND ? (rounding == 0 ? 0 : rounding + std::round(border / M_PI)) : MINHALFBORDER);

        g_pRenderer->renderRect(shapeBox, color, ALLOWROUND ? rounding : MINHALFSHAPE);
        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, WL_OUTPUT_TRANSFORM_FLIPPED_180);

    return data.opacity < 1.0;
}
07070100000052000081A4000000000000000000000001669CF85C00000341000000000000000000000000000000000000002E00000000hyprlock-0.4.1/src/renderer/widgets/Shape.hpp#pragma once

#include "IWidget.hpp"
#include "../../helpers/Vector2D.hpp"
#include "../../helpers/Color.hpp"
#include "../../helpers/Box.hpp"
#include "Shadowable.hpp"
#include <string>
#include <unordered_map>
#include <any>

class CShape : public IWidget {
  public:
    CShape(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props);

    virtual bool draw(const SRenderData& data);

  private:
    CFramebuffer shapeFB;

    int          rounding;
    double       border;
    double       angle;
    CColor       color;
    CColor       borderColor;
    Vector2D     size;
    Vector2D     pos;
    CBox         shapeBox;
    CBox         borderBox;
    bool         xray;

    std::string  halign, valign;

    bool         firstRender = true;

    Vector2D     viewport;
    CShadowable  shadow;
};
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!561 blocks
openSUSE Build Service is sponsored by