mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-11-16 23:05:58 +01:00
core: initial commit
This commit is contained in:
parent
a2f0160ac6
commit
a6ac79641a
41 changed files with 2834 additions and 1 deletions
65
.clang-format
Normal file
65
.clang-format
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
---
|
||||||
|
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
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.vscode/
|
||||||
|
build/
|
||||||
|
protocols/
|
80
CMakeLists.txt
Normal file
80
CMakeLists.txt
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
cmake_minimum_required(VERSION 3.19)
|
||||||
|
|
||||||
|
set(VERSION 0.1.0)
|
||||||
|
|
||||||
|
project(hyprlock
|
||||||
|
DESCRIPTION "A gpu-accelerated screen lock for Hyprland"
|
||||||
|
VERSION ${VERSION}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
|
||||||
|
|
||||||
|
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/"
|
||||||
|
)
|
||||||
|
|
||||||
|
# configure
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value
|
||||||
|
-Wno-missing-field-initializers -Wno-narrowing)
|
||||||
|
|
||||||
|
# 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 cairo pango pam)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||||
|
add_executable(hyprlock ${SRCFILES})
|
||||||
|
target_link_libraries(hyprlock PRIVATE 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)
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
install(TARGETS hyprlock)
|
63
README.md
63
README.md
|
@ -1,2 +1,63 @@
|
||||||
# hyprlock
|
# hyprlock
|
||||||
Hyprland's screen locking utility
|
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
|
||||||
|
|
||||||
|
## Example config
|
||||||
|
|
||||||
|
```ini
|
||||||
|
general {
|
||||||
|
disable_loading_bar = false
|
||||||
|
}
|
||||||
|
|
||||||
|
background {
|
||||||
|
monitor = DP-2
|
||||||
|
path = /home/me/someImage.png
|
||||||
|
}
|
||||||
|
|
||||||
|
background {
|
||||||
|
monitor = WL-2
|
||||||
|
path = /home/me/someImage2.png
|
||||||
|
}
|
||||||
|
|
||||||
|
input-field {
|
||||||
|
monitor =
|
||||||
|
size = 200, 50
|
||||||
|
outline_thickness = 3
|
||||||
|
outer_color = rgb(151515)
|
||||||
|
inner_color = rgb(200, 200, 200)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
soon:tm:
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
### Deps
|
||||||
|
- wayland-client
|
||||||
|
- wayland-protocols
|
||||||
|
- cairo
|
||||||
|
- gles3.2
|
||||||
|
- pango
|
||||||
|
- hyprlang>=0.4.0
|
||||||
|
- xkbcommon
|
||||||
|
- pam
|
||||||
|
|
||||||
|
### 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
|
||||||
|
```
|
||||||
|
|
84
src/config/ConfigManager.cpp
Normal file
84
src/config/ConfigManager.cpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#include "ConfigManager.hpp"
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
static std::string getConfigDir() {
|
||||||
|
static const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
|
||||||
|
|
||||||
|
if (xdgConfigHome && std::filesystem::path(xdgConfigHome).is_absolute())
|
||||||
|
return xdgConfigHome;
|
||||||
|
|
||||||
|
return getenv("HOME") + std::string("/.config");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getMainConfigPath() {
|
||||||
|
return getConfigDir() + "/hypr/hyprlock.conf";
|
||||||
|
}
|
||||||
|
|
||||||
|
CConfigManager::CConfigManager() : m_config(getMainConfigPath().c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true}) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CConfigManager::init() {
|
||||||
|
m_config.addConfigValue("general:disable_loading_bar", Hyprlang::INT{0});
|
||||||
|
|
||||||
|
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.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.commence();
|
||||||
|
|
||||||
|
auto result = m_config.parse();
|
||||||
|
|
||||||
|
if (result.error)
|
||||||
|
Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
//
|
||||||
|
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())},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 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())},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
30
src/config/ConfigManager.hpp
Normal file
30
src/config/ConfigManager.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../helpers/Log.hpp"
|
||||||
|
|
||||||
|
#include <hyprlang.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class CConfigManager {
|
||||||
|
public:
|
||||||
|
CConfigManager();
|
||||||
|
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();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Hyprlang::CConfig m_config;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CConfigManager> g_pConfigManager;
|
16
src/core/CursorShape.cpp
Normal file
16
src/core/CursorShape.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#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);
|
||||||
|
}
|
15
src/core/CursorShape.hpp
Normal file
15
src/core/CursorShape.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
wp_cursor_shape_manager_v1* mgr = nullptr;
|
||||||
|
wp_cursor_shape_device_v1* dev = nullptr;
|
||||||
|
};
|
80
src/core/Egl.cpp
Normal file
80
src/core/Egl.cpp
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#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);
|
||||||
|
if (eglDisplay)
|
||||||
|
eglTerminate(eglDisplay);
|
||||||
|
|
||||||
|
eglReleaseThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEGL::makeCurrent(EGLSurface surf) {
|
||||||
|
eglMakeCurrent(eglDisplay, surf, surf, eglContext);
|
||||||
|
}
|
22
src/core/Egl.hpp
Normal file
22
src/core/Egl.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
|
||||||
|
class CEGL {
|
||||||
|
public:
|
||||||
|
CEGL(wl_display*);
|
||||||
|
|
||||||
|
EGLDisplay eglDisplay;
|
||||||
|
EGLConfig eglConfig;
|
||||||
|
EGLContext eglContext;
|
||||||
|
|
||||||
|
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
||||||
|
|
||||||
|
void makeCurrent(EGLSurface surf);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CEGL> g_pEGL;
|
150
src/core/LockSurface.cpp
Normal file
150
src/core/LockSurface.cpp
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
#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 {}", PSURF->fractionalScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
wl_egl_window_destroy(eglWindow);
|
||||||
|
ext_session_lock_surface_v1_destroy(lockSurface);
|
||||||
|
wl_surface_destroy(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
fractional = wp_fractional_scale_manager_v1_get_fractional_scale(g_pHyprlock->getFractionalMgr(), surface);
|
||||||
|
if (fractional) {
|
||||||
|
wp_fractional_scale_v1_add_listener(fractional, &fsListener, this);
|
||||||
|
viewport = wp_viewporter_get_viewport(g_pHyprlock->getViewporter(), surface);
|
||||||
|
wl_display_roundtrip(g_pHyprlock->getDisplay());
|
||||||
|
} else {
|
||||||
|
Debug::log(LOG, "No fractional-scale support! Oops, won't be able to scale!");
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(output->size, 0);
|
||||||
|
g_pRenderer->renderLock(*this);
|
||||||
|
|
||||||
|
lockSurface = ext_session_lock_v1_get_lock_surface(g_pHyprlock->getSessionLock(), surface, output->output);
|
||||||
|
|
||||||
|
if (!surface) {
|
||||||
|
Debug::log(CRIT, "Couldn't create ext_session_lock_surface_v1");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ext_session_lock_surface_v1_add_listener(lockSurface, &lockListener, this);
|
||||||
|
wl_display_roundtrip(g_pHyprlock->getDisplay());
|
||||||
|
wl_display_flush(g_pHyprlock->getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSessionLockSurface::configure(const Vector2D& size_, uint32_t serial_) {
|
||||||
|
Debug::log(LOG, "configure with serial {}", serial);
|
||||||
|
|
||||||
|
serial = serial_;
|
||||||
|
size = (size_ * fractionalScale).floor();
|
||||||
|
logicalSize = size_;
|
||||||
|
if (serial != 0)
|
||||||
|
ext_session_lock_surface_v1_ack_configure(lockSurface, serial);
|
||||||
|
|
||||||
|
if (fractional)
|
||||||
|
wp_viewport_set_destination(viewport, logicalSize.x, logicalSize.y);
|
||||||
|
|
||||||
|
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);
|
||||||
|
else
|
||||||
|
wl_egl_window_resize(eglWindow, size.x, size.y, 0, 0);
|
||||||
|
|
||||||
|
if (!eglWindow) {
|
||||||
|
Debug::log(CRIT, "Couldn't create eglWindow");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial == 0)
|
||||||
|
eglSurface = g_pEGL->eglCreatePlatformWindowSurfaceEXT(g_pEGL->eglDisplay, g_pEGL->eglConfig, eglWindow, nullptr);
|
||||||
|
|
||||||
|
if (!eglSurface) {
|
||||||
|
Debug::log(CRIT, "Couldn't create eglSurface");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
readyForFrame = true;
|
||||||
|
|
||||||
|
if (serial != 0)
|
||||||
|
render();
|
||||||
|
|
||||||
|
if (fractional)
|
||||||
|
wp_viewport_set_destination(viewport, logicalSize.x, logicalSize.y);
|
||||||
|
|
||||||
|
wl_surface_set_buffer_scale(surface, 1);
|
||||||
|
wl_surface_damage_buffer(surface, 0, 0, 0xFFFF, 0xFFFF);
|
||||||
|
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
wl_display_roundtrip(g_pHyprlock->getDisplay());
|
||||||
|
wl_display_flush(g_pHyprlock->getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleDone(void* data, wl_callback* wl_callback, uint32_t callback_data) {
|
||||||
|
const auto PSURF = (CSessionLockSurface*)data;
|
||||||
|
PSURF->onCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const wl_callback_listener callbackListener = {
|
||||||
|
.done = handleDone,
|
||||||
|
};
|
||||||
|
|
||||||
|
void CSessionLockSurface::render() {
|
||||||
|
Debug::log(LOG, "render lock");
|
||||||
|
|
||||||
|
const auto FEEDBACK = g_pRenderer->renderLock(*this);
|
||||||
|
frameCallback = wl_surface_frame(surface);
|
||||||
|
wl_callback_add_listener(frameCallback, &callbackListener, this);
|
||||||
|
eglSwapBuffers(g_pEGL->eglDisplay, eglSurface);
|
||||||
|
|
||||||
|
if (fractional)
|
||||||
|
wp_viewport_set_destination(viewport, logicalSize.x, logicalSize.y);
|
||||||
|
|
||||||
|
wl_surface_damage_buffer(surface, 0, 0, 0xFFFF, 0xFFFF);
|
||||||
|
wl_surface_set_buffer_scale(surface, 1);
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
|
needsFrame = FEEDBACK.needsFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSessionLockSurface::onCallback() {
|
||||||
|
readyForFrame = true;
|
||||||
|
frameCallback = nullptr;
|
||||||
|
|
||||||
|
if (needsFrame && !g_pHyprlock->m_bTerminate && g_pEGL)
|
||||||
|
render();
|
||||||
|
}
|
46
src/core/LockSurface.hpp
Normal file
46
src/core/LockSurface.hpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#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();
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
49
src/core/Output.cpp
Normal file
49
src/core/Output.cpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#include "Output.hpp"
|
||||||
|
#include "../helpers/Log.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;
|
||||||
|
POUTPUT->size = {width, height};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleDone(void* data, wl_output* output) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
Debug::log(LOG, "output {} description {}", POUTPUT->name, description ? description : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
25
src/core/Output.hpp
Normal file
25
src/core/Output.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#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::unique_ptr<CSessionLockSurface> sessionLockSurface;
|
||||||
|
|
||||||
|
wl_output* output = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
36
src/core/Password.cpp
Normal file
36
src/core/Password.cpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#include "Password.hpp"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <security/pam_misc.h>
|
||||||
|
|
||||||
|
struct pam_response* reply;
|
||||||
|
|
||||||
|
//
|
||||||
|
int conv(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr) {
|
||||||
|
*resp = reply;
|
||||||
|
return PAM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPassword::SVerificationResult CPassword::verify(const std::string& pass) {
|
||||||
|
const pam_conv localConv = {conv, NULL};
|
||||||
|
pam_handle_t* handle = NULL;
|
||||||
|
|
||||||
|
int ret = pam_start("su", getlogin(), &localConv, &handle);
|
||||||
|
|
||||||
|
if (ret != PAM_SUCCESS)
|
||||||
|
return {false, "pam_start failed"};
|
||||||
|
|
||||||
|
reply = (struct pam_response*)malloc(sizeof(struct pam_response));
|
||||||
|
|
||||||
|
reply->resp = strdup(pass.c_str());
|
||||||
|
reply->resp_retcode = 0;
|
||||||
|
ret = pam_authenticate(handle, 0);
|
||||||
|
|
||||||
|
if (ret != PAM_SUCCESS)
|
||||||
|
return {false, ret == PAM_AUTH_ERR ? "Authentication failed" : "pam_authenticate failed"};
|
||||||
|
|
||||||
|
ret = pam_end(handle, ret);
|
||||||
|
|
||||||
|
return {true, "Successfully authenticated"};
|
||||||
|
}
|
16
src/core/Password.hpp
Normal file
16
src/core/Password.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class CPassword {
|
||||||
|
public:
|
||||||
|
struct SVerificationResult {
|
||||||
|
bool success = false;
|
||||||
|
std::string failReason = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
SVerificationResult verify(const std::string& pass);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CPassword> g_pPassword = std::make_unique<CPassword>();
|
375
src/core/hyprlock.cpp
Normal file
375
src/core/hyprlock.cpp
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
#include "hyprlock.hpp"
|
||||||
|
#include "../helpers/Log.hpp"
|
||||||
|
#include "../config/ConfigManager.hpp"
|
||||||
|
#include "../renderer/Renderer.hpp"
|
||||||
|
#include "Password.hpp"
|
||||||
|
#include "Egl.hpp"
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <cuchar>
|
||||||
|
|
||||||
|
CHyprlock::CHyprlock() {
|
||||||
|
m_sWaylandState.display = wl_display_connect("wayland-2");
|
||||||
|
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");
|
||||||
|
|
||||||
|
g_pRenderer = std::make_unique<CRenderer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprlock::onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name) {
|
||||||
|
Debug::log(LOG, " | removed iface {}", name);
|
||||||
|
std::erase_if(m_vOutputs, [name](const auto& other) { return other->name == name; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// end wl_registry
|
||||||
|
|
||||||
|
void CHyprlock::run() {
|
||||||
|
m_sWaylandState.registry = wl_display_get_registry(m_sWaylandState.display);
|
||||||
|
|
||||||
|
wl_registry_add_listener(m_sWaylandState.registry, ®istryListener, 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);
|
||||||
|
|
||||||
|
lockSession();
|
||||||
|
|
||||||
|
while (wl_display_dispatch(m_sWaylandState.display) != -1) {
|
||||||
|
if (m_bTerminate)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::log(LOG, "Reached the end, exiting");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
g_pHyprlock->m_pCursorShape->setShape(serial, wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handlePointerButton(void* data, struct wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state) {
|
||||||
|
;
|
||||||
|
// TODO: remove xD
|
||||||
|
g_pHyprlock->unlockSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
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_SHARED, fd, 0);
|
||||||
|
if (buf == MAP_FAILED) {
|
||||||
|
Debug::log(ERR, "Failed to mmap xkb keymap: %d", 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) {
|
||||||
|
if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_pHyprlock->onKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::onKey(uint32_t key) {
|
||||||
|
const auto SYM = xkb_state_key_get_one_sym(m_pXKBState, key + 8);
|
||||||
|
|
||||||
|
if (SYM == XKB_KEY_BackSpace) {
|
||||||
|
if (m_sPasswordState.passBuffer.length() > 0)
|
||||||
|
m_sPasswordState.passBuffer = m_sPasswordState.passBuffer.substr(0, m_sPasswordState.passBuffer.length() - 1);
|
||||||
|
} else if (SYM == XKB_KEY_Return) {
|
||||||
|
Debug::log(LOG, "Authenticating");
|
||||||
|
|
||||||
|
const auto RESULT = g_pPassword->verify(m_sPasswordState.passBuffer);
|
||||||
|
|
||||||
|
Debug::log(LOG, "Password auth result: {}", RESULT.failReason);
|
||||||
|
|
||||||
|
if (RESULT.success)
|
||||||
|
unlockSession();
|
||||||
|
} 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};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& o : m_vOutputs) {
|
||||||
|
o->sessionLockSurface->render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprlock::lockSession() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprlock::unlockSession() {
|
||||||
|
Debug::log(LOG, "Unlocking session");
|
||||||
|
ext_session_lock_v1_unlock_and_destroy(m_sLockState.lock);
|
||||||
|
m_sLockState.lock = nullptr;
|
||||||
|
|
||||||
|
m_vOutputs.clear();
|
||||||
|
g_pEGL.reset();
|
||||||
|
Debug::log(LOG, "Unlocked, exiting!");
|
||||||
|
|
||||||
|
m_bTerminate = true;
|
||||||
|
|
||||||
|
wl_display_roundtrip(m_sWaylandState.display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprlock::onLockLocked() {
|
||||||
|
Debug::log(LOG, "onLockLocked called");
|
||||||
|
|
||||||
|
for (auto& o : m_vOutputs) {
|
||||||
|
o->sessionLockSurface = std::make_unique<CSessionLockSurface>(o.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHyprlock::onLockFinished() {
|
||||||
|
Debug::log(LOG, "onLockFinished called. Seems we got yeeten. Is another lockscreen running?");
|
||||||
|
ext_session_lock_v1_unlock_and_destroy(m_sLockState.lock);
|
||||||
|
m_sLockState.lock = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
74
src/core/hyprlock.hpp
Normal file
74
src/core/hyprlock.hpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include "ext-session-lock-v1-protocol.h"
|
||||||
|
#include "fractional-scale-v1-protocol.h"
|
||||||
|
#include "viewporter-protocol.h"
|
||||||
|
#include "Output.hpp"
|
||||||
|
#include "CursorShape.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
|
class CHyprlock {
|
||||||
|
public:
|
||||||
|
CHyprlock();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
void onLockLocked();
|
||||||
|
void onLockFinished();
|
||||||
|
|
||||||
|
void lockSession();
|
||||||
|
void unlockSession();
|
||||||
|
|
||||||
|
void onKey(uint32_t key);
|
||||||
|
|
||||||
|
size_t getPasswordBufferLen();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
bool m_bTerminate = false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
} m_sWaylandState;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
ext_session_lock_v1* lock = nullptr;
|
||||||
|
} m_sLockState;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::string passBuffer = "";
|
||||||
|
} m_sPasswordState;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<COutput>> m_vOutputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CHyprlock> g_pHyprlock;
|
101
src/helpers/Box.cpp
Normal file
101
src/helpers/Box.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#include "Box.hpp"
|
||||||
|
|
||||||
|
#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};
|
||||||
|
}
|
69
src/helpers/Box.hpp
Normal file
69
src/helpers/Box.hpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#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();
|
||||||
|
};
|
26
src/helpers/Color.cpp
Normal file
26
src/helpers/Color.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#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;
|
||||||
|
}
|
34
src/helpers/Color.hpp
Normal file
34
src/helpers/Color.hpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#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};
|
||||||
|
}
|
||||||
|
};
|
58
src/helpers/Log.hpp
Normal file
58
src/helpers/Log.hpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#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."); \
|
||||||
|
*((int*)nullptr) = 1; /* so that we crash and get a coredump */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#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";
|
||||||
|
}
|
||||||
|
};
|
51
src/helpers/Vector2D.cpp
Normal file
51
src/helpers/Vector2D.cpp
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#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));
|
||||||
|
}
|
154
src/helpers/Vector2D.hpp
Normal file
154
src/helpers/Vector2D.hpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
class Vector2D {
|
||||||
|
public:
|
||||||
|
Vector2D(double, double);
|
||||||
|
Vector2D();
|
||||||
|
~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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
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); }
|
||||||
|
}
|
||||||
|
};
|
31
src/main.cpp
Normal file
31
src/main.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
#include "config/ConfigManager.hpp"
|
||||||
|
#include "core/hyprlock.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char **argv, char **envp) {
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
std::string arg = argv[i];
|
||||||
|
|
||||||
|
if (arg == "--verbose" || arg == "-v")
|
||||||
|
Debug::verbose = true;
|
||||||
|
|
||||||
|
else if (arg == "--quiet" || arg == "-q")
|
||||||
|
Debug::quiet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
g_pConfigManager = std::make_unique<CConfigManager>();
|
||||||
|
g_pConfigManager->init();
|
||||||
|
} catch (const char *err) {
|
||||||
|
Debug::log(CRIT, "ConfigManager threw: {}", err);
|
||||||
|
std::string strerr = err;
|
||||||
|
if (strerr.contains("File does not exist"))
|
||||||
|
Debug::log(NONE, " Make sure you have a config.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pHyprlock = std::make_unique<CHyprlock>();
|
||||||
|
g_pHyprlock->run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
92
src/renderer/AsyncResourceGatherer.cpp
Normal file
92
src/renderer/AsyncResourceGatherer.cpp
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#include "AsyncResourceGatherer.hpp"
|
||||||
|
#include "../config/ConfigManager.hpp"
|
||||||
|
#include "../core/Egl.hpp"
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
CAsyncResourceGatherer::CAsyncResourceGatherer() {
|
||||||
|
thread = std::thread([this]() { this->gather(); });
|
||||||
|
thread.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
|
||||||
|
for (auto& a : assets) {
|
||||||
|
if (a.first == id)
|
||||||
|
return &a.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
});
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
progress = 0;
|
||||||
|
for (auto& c : CWIDGETS) {
|
||||||
|
if (c.type == "background") {
|
||||||
|
progress += 1.0 / (preloads + 1.0);
|
||||||
|
|
||||||
|
std::string path = std::any_cast<Hyprlang::STRING>(c.values.at("path"));
|
||||||
|
std::string id = std::string{"background:"} + path;
|
||||||
|
|
||||||
|
// preload bg img
|
||||||
|
const auto CAIROISURFACE = cairo_image_surface_create_from_png(path.c_str());
|
||||||
|
|
||||||
|
const auto CAIRO = cairo_create(CAIROISURFACE);
|
||||||
|
cairo_scale(CAIRO, 1, 1);
|
||||||
|
|
||||||
|
const auto TARGET = &preloadTargets.emplace_back(CAsyncResourceGatherer::SPreloadTarget{});
|
||||||
|
|
||||||
|
TARGET->size = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)};
|
||||||
|
TARGET->type = TARGET_IMAGE;
|
||||||
|
TARGET->id = id;
|
||||||
|
|
||||||
|
const auto DATA = cairo_image_surface_get_data(CAIROISURFACE);
|
||||||
|
TARGET->cairo = CAIRO;
|
||||||
|
TARGET->cairosurface = CAIROISURFACE;
|
||||||
|
TARGET->data = DATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceGatherer::apply() {
|
||||||
|
|
||||||
|
for (auto& t : preloadTargets) {
|
||||||
|
if (t.type == TARGET_IMAGE) {
|
||||||
|
const auto ASSET = &assets[t.id];
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applied = true;
|
||||||
|
}
|
50
src/renderer/AsyncResourceGatherer.hpp
Normal file
50
src/renderer/AsyncResourceGatherer.hpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Shader.hpp"
|
||||||
|
#include "../helpers/Box.hpp"
|
||||||
|
#include "../helpers/Color.hpp"
|
||||||
|
#include "Texture.hpp"
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
struct SPreloadedAsset {
|
||||||
|
CTexture texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CAsyncResourceGatherer {
|
||||||
|
public:
|
||||||
|
CAsyncResourceGatherer();
|
||||||
|
std::atomic<bool> ready = false;
|
||||||
|
std::atomic<bool> applied = false;
|
||||||
|
|
||||||
|
std::atomic<float> progress = 0;
|
||||||
|
|
||||||
|
SPreloadedAsset* getAssetByID(const std::string& id);
|
||||||
|
|
||||||
|
void apply();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::thread thread;
|
||||||
|
|
||||||
|
enum eTargetType {
|
||||||
|
TARGET_IMAGE = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SPreloadTarget {
|
||||||
|
eTargetType type = TARGET_IMAGE;
|
||||||
|
std::string id = "";
|
||||||
|
|
||||||
|
void* data;
|
||||||
|
void* cairo;
|
||||||
|
void* cairosurface;
|
||||||
|
|
||||||
|
Vector2D size;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<SPreloadTarget> preloadTargets;
|
||||||
|
std::unordered_map<std::string, SPreloadedAsset> assets;
|
||||||
|
|
||||||
|
void gather();
|
||||||
|
};
|
262
src/renderer/Renderer.cpp
Normal file
262
src/renderer/Renderer.cpp
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
#include "Renderer.hpp"
|
||||||
|
#include "../core/Egl.hpp"
|
||||||
|
#include "../config/ConfigManager.hpp"
|
||||||
|
#include "../helpers/Color.hpp"
|
||||||
|
#include "../core/Output.hpp"
|
||||||
|
#include "mtx.hpp"
|
||||||
|
|
||||||
|
#include <GLES3/gl32.h>
|
||||||
|
#include <GLES3/gl3ext.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "Shaders.hpp"
|
||||||
|
|
||||||
|
#include "widgets/PasswordInputField.hpp"
|
||||||
|
#include "widgets/Background.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");
|
||||||
|
|
||||||
|
wlr_matrix_identity(projMatrix.data());
|
||||||
|
|
||||||
|
asyncResourceGatherer = std::make_unique<CAsyncResourceGatherer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int frames = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf) {
|
||||||
|
static auto* const PDISABLEBAR = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:disable_loading_bar");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
glScissor(frames, 0, surf.size.x, surf.size.y);
|
||||||
|
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
float bga = 0;
|
||||||
|
|
||||||
|
SRenderFeedback feedback;
|
||||||
|
|
||||||
|
if (!asyncResourceGatherer->ready) {
|
||||||
|
// 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 (!asyncResourceGatherer->applied) {
|
||||||
|
asyncResourceGatherer->apply();
|
||||||
|
gatheredAt = std::chrono::system_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// render widgets
|
||||||
|
const auto WIDGETS = getOrCreateWidgetsFor(&surf);
|
||||||
|
for (auto& w : *WIDGETS) {
|
||||||
|
feedback.needsFrame = w->draw() || feedback.needsFrame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frames++;
|
||||||
|
|
||||||
|
Debug::log(LOG, "frame {}", frames);
|
||||||
|
|
||||||
|
feedback.needsFrame = feedback.needsFrame || bga < 1.0;
|
||||||
|
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, 0,
|
||||||
|
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) {
|
||||||
|
float matrix[9];
|
||||||
|
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_FLIPPED_180 /* ugh coordinate spaces */, 0,
|
||||||
|
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)) {
|
||||||
|
|
||||||
|
const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
|
||||||
|
|
||||||
|
for (auto& c : CWIDGETS) {
|
||||||
|
if (!c.monitor.empty() && c.monitor != surf->output->stringPort)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// by type
|
||||||
|
if (c.type == "background")
|
||||||
|
widgets[surf].emplace_back(std::make_unique<CBackground>(surf->size, std::string{"background:"} + std::any_cast<Hyprlang::STRING>(c.values.at("path"))));
|
||||||
|
if (c.type == "input-field") {
|
||||||
|
const auto SIZE = std::any_cast<Hyprlang::VEC2>(c.values.at("size"));
|
||||||
|
widgets[surf].emplace_back(std::make_unique<CPasswordInputField>(surf->size, Vector2D{SIZE.x, SIZE.y}, std::any_cast<Hyprlang::INT>(c.values.at("outer_color")),
|
||||||
|
std::any_cast<Hyprlang::INT>(c.values.at("inner_color")),
|
||||||
|
std::any_cast<Hyprlang::INT>(c.values.at("outline_thickness"))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &widgets[surf];
|
||||||
|
}
|
43
src/renderer/Renderer.hpp
Normal file
43
src/renderer/Renderer.hpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "../core/LockSurface.hpp"
|
||||||
|
#include "Shader.hpp"
|
||||||
|
#include "../helpers/Box.hpp"
|
||||||
|
#include "../helpers/Color.hpp"
|
||||||
|
#include "AsyncResourceGatherer.hpp"
|
||||||
|
#include "widgets/IWidget.hpp"
|
||||||
|
|
||||||
|
typedef std::unordered_map<const CSessionLockSurface*, std::vector<std::unique_ptr<IWidget>>> widgetMap_t;
|
||||||
|
|
||||||
|
class CRenderer {
|
||||||
|
public:
|
||||||
|
CRenderer();
|
||||||
|
|
||||||
|
struct SRenderFeedback {
|
||||||
|
bool needsFrame = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
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::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer;
|
||||||
|
std::chrono::system_clock::time_point gatheredAt;
|
||||||
|
|
||||||
|
private:
|
||||||
|
widgetMap_t widgets;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<IWidget>>* getOrCreateWidgetsFor(const CSessionLockSurface* surf);
|
||||||
|
|
||||||
|
CShader rectShader;
|
||||||
|
CShader texShader;
|
||||||
|
|
||||||
|
std::array<float, 9> projMatrix;
|
||||||
|
std::array<float, 9> projection;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::unique_ptr<CRenderer> g_pRenderer;
|
23
src/renderer/Shader.cpp
Normal file
23
src/renderer/Shader.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#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;
|
||||||
|
}
|
68
src/renderer/Shader.hpp
Normal file
68
src/renderer/Shader.hpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#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;
|
||||||
|
|
||||||
|
GLint getUniformLocation(const std::string&);
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, GLint> m_muUniforms;
|
||||||
|
};
|
122
src/renderer/Shaders.hpp
Normal file
122
src/renderer/Shaders.hpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#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;
|
||||||
|
})#";
|
19
src/renderer/Texture.cpp
Normal file
19
src/renderer/Texture.cpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include "Texture.hpp"
|
||||||
|
|
||||||
|
CTexture::CTexture() {
|
||||||
|
// naffin'
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
25
src/renderer/Texture.hpp
Normal file
25
src/renderer/Texture.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#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();
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
236
src/renderer/mtx.hpp
Normal file
236
src/renderer/mtx.hpp
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstring>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include "../helpers/Box.hpp"
|
||||||
|
|
||||||
|
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_180;
|
||||||
|
// }
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
void wlr_matrix_transform(float mat[9], enum wl_output_transform transform) {
|
||||||
|
wlr_matrix_multiply(mat, mat, transforms[transform]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
21
src/renderer/widgets/Background.cpp
Normal file
21
src/renderer/widgets/Background.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include "Background.hpp"
|
||||||
|
#include "../Renderer.hpp"
|
||||||
|
|
||||||
|
CBackground::CBackground(const Vector2D& viewport_, const std::string& resourceID_) : viewport(viewport_), resourceID(resourceID_) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBackground::draw() {
|
||||||
|
if (!asset)
|
||||||
|
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
||||||
|
|
||||||
|
if (!asset)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float bga = std::clamp(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - g_pRenderer->gatheredAt).count() / 500000.0, 0.0, 1.0);
|
||||||
|
CBox monbox = {0, 0, viewport.x, viewport.y};
|
||||||
|
|
||||||
|
g_pRenderer->renderTexture(monbox, asset->texture, bga);
|
||||||
|
|
||||||
|
return bga < 1.0;
|
||||||
|
}
|
19
src/renderer/widgets/Background.hpp
Normal file
19
src/renderer/widgets/Background.hpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IWidget.hpp"
|
||||||
|
#include "../../helpers/Vector2D.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct SPreloadedAsset;
|
||||||
|
|
||||||
|
class CBackground : public IWidget {
|
||||||
|
public:
|
||||||
|
CBackground(const Vector2D& viewport, const std::string& resourceID);
|
||||||
|
|
||||||
|
virtual bool draw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector2D viewport;
|
||||||
|
std::string resourceID;
|
||||||
|
SPreloadedAsset* asset = nullptr;
|
||||||
|
};
|
6
src/renderer/widgets/IWidget.hpp
Normal file
6
src/renderer/widgets/IWidget.hpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class IWidget {
|
||||||
|
public:
|
||||||
|
virtual bool draw() = 0;
|
||||||
|
};
|
62
src/renderer/widgets/PasswordInputField.cpp
Normal file
62
src/renderer/widgets/PasswordInputField.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include "PasswordInputField.hpp"
|
||||||
|
#include "../Renderer.hpp"
|
||||||
|
#include "../../core/hyprlock.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
CPasswordInputField::CPasswordInputField(const Vector2D& viewport, const Vector2D& size_, const CColor& outer_, const CColor& inner_, int out_thick_) {
|
||||||
|
size = size_;
|
||||||
|
pos = viewport / 2.f - size_ / 2.f;
|
||||||
|
inner = inner_;
|
||||||
|
outer = outer_;
|
||||||
|
out_thick = out_thick_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPasswordInputField::updateDots() {
|
||||||
|
const auto PASSLEN = g_pHyprlock->getPasswordBufferLen();
|
||||||
|
|
||||||
|
size_t dotsAppearingOrPresent = std::count_if(dots.begin(), dots.end(), [](const auto& dot) { return dot.appearing || !dot.animated; });
|
||||||
|
|
||||||
|
if (dotsAppearingOrPresent < PASSLEN) {
|
||||||
|
dots.push_back(dot{.idx = dotsAppearingOrPresent + 1, .appearing = true, .animated = true, .a = 0, .start = std::chrono::system_clock::now()});
|
||||||
|
} else if (dotsAppearingOrPresent > PASSLEN) {
|
||||||
|
dots[dots.size() - 1].animated = true;
|
||||||
|
dots[dots.size() - 1].appearing = false;
|
||||||
|
dots[dots.size() - 1].start = std::chrono::system_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& dot : dots) {
|
||||||
|
if (dot.appearing) {
|
||||||
|
if (dot.a < 1.0)
|
||||||
|
dot.a = std::clamp(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - dot.start).count() / 100000.0, 0.0, 1.0);
|
||||||
|
} else {
|
||||||
|
if (dot.a > 0.0)
|
||||||
|
dot.a = std::clamp(1.0 - std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - dot.start).count() / 100000.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dot.appearing && dot.a == 1.0)
|
||||||
|
dot.animated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::erase_if(dots, [](const auto& dot) { return !dot.appearing && dot.a == 0.0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPasswordInputField::draw() {
|
||||||
|
CBox inputFieldBox = {pos, size};
|
||||||
|
CBox outerBox = {pos - Vector2D{out_thick, out_thick}, size + Vector2D{out_thick * 2, out_thick * 2}};
|
||||||
|
|
||||||
|
updateDots();
|
||||||
|
|
||||||
|
g_pRenderer->renderRect(outerBox, outer, outerBox.h / 2.0);
|
||||||
|
g_pRenderer->renderRect(inputFieldBox, inner, inputFieldBox.h / 2.0);
|
||||||
|
|
||||||
|
constexpr int PASS_SPACING = 3;
|
||||||
|
constexpr int PASS_SIZE = 8;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < dots.size(); ++i) {
|
||||||
|
Vector2D currentPos = inputFieldBox.pos() + Vector2D{PASS_SPACING, inputFieldBox.h / 2.f - PASS_SIZE / 2.f} + Vector2D{(PASS_SIZE + PASS_SPACING) * dots[i].idx, 0};
|
||||||
|
CBox box{currentPos, Vector2D{PASS_SIZE, PASS_SIZE}};
|
||||||
|
g_pRenderer->renderRect(box, CColor{0, 0, 0, dots[i].a}, PASS_SIZE / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::ranges::any_of(dots.begin(), dots.end(), [](const auto& dot) { return dot.animated; });
|
||||||
|
}
|
34
src/renderer/widgets/PasswordInputField.hpp
Normal file
34
src/renderer/widgets/PasswordInputField.hpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IWidget.hpp"
|
||||||
|
#include "../../helpers/Vector2D.hpp"
|
||||||
|
#include "../../helpers/Color.hpp"
|
||||||
|
#include <chrono>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class CPasswordInputField : public IWidget {
|
||||||
|
public:
|
||||||
|
CPasswordInputField(const Vector2D& viewport, const Vector2D& size, const CColor& outer, const CColor& inner, int out_thick);
|
||||||
|
|
||||||
|
virtual bool draw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateDots();
|
||||||
|
|
||||||
|
Vector2D size;
|
||||||
|
Vector2D pos;
|
||||||
|
|
||||||
|
int out_thick;
|
||||||
|
|
||||||
|
CColor inner, outer;
|
||||||
|
|
||||||
|
struct dot {
|
||||||
|
size_t idx = 0;
|
||||||
|
bool appearing = false;
|
||||||
|
bool animated = false;
|
||||||
|
float a = 0;
|
||||||
|
std::chrono::system_clock::time_point start;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<dot> dots;
|
||||||
|
};
|
Loading…
Reference in a new issue