New upstream version 0.1.11
This commit is contained in:
parent
7f4eaa029d
commit
7bf3e9672f
|
|
@ -18,6 +18,10 @@ jobs:
|
||||||
pacman --noconfirm --noprogressbar -Syyu
|
pacman --noconfirm --noprogressbar -Syyu
|
||||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang cairo librsvg git libzip tomlplusplus
|
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang cairo librsvg git libzip tomlplusplus
|
||||||
|
|
||||||
|
- name: Get hyprutils-git
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
|
||||||
|
|
||||||
- name: Install hyprlang
|
- name: Install hyprlang
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/hyprwm/hyprlang --recursive
|
git clone https://github.com/hyprwm/hyprlang --recursive
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on: [push, pull_request, workflow_dispatch]
|
||||||
|
jobs:
|
||||||
|
nix:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: DeterminateSystems/nix-installer-action@main
|
||||||
|
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||||
|
|
||||||
|
# not needed (yet)
|
||||||
|
# - uses: cachix/cachix-action@v12
|
||||||
|
# with:
|
||||||
|
# name: hyprland
|
||||||
|
# authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: nix build .#hyprcursor-with-tests --print-build-logs --keep-going
|
||||||
|
|
||||||
|
# keep a fixed rev in case anything changes
|
||||||
|
- name: Install hyprcursor theme
|
||||||
|
run: nix build github:fufexan/dotfiles/4e05e373c1c70a2ae259b2c15eec2ad6e11ce581#bibata-hyprcursor --print-build-logs --keep-going
|
||||||
|
|
||||||
|
- name: Set up env
|
||||||
|
run: |
|
||||||
|
export HYPRCURSOR_THEME=Bibata-Modern-Classic-Hyprcursor
|
||||||
|
export HYPRCURSOR_SIZE=16
|
||||||
|
mkdir -p $HOME/.local/share/icons
|
||||||
|
ln -s $(realpath result/share/icons/Bibata-Modern-Classic-Hyprcursor) $HOME/.local/share/icons/
|
||||||
|
|
||||||
|
- name: Run test1
|
||||||
|
run: nix shell .#hyprcursor-with-tests -c hyprcursor_test1
|
||||||
|
- name: Run test2
|
||||||
|
run: nix shell .#hyprcursor-with-tests -c hyprcursor_test2
|
||||||
|
- name: Run test_c
|
||||||
|
run: nix shell .#hyprcursor-with-tests -c hyprcursor_test_c
|
||||||
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
.vscode/
|
.vscode/
|
||||||
build/
|
build/
|
||||||
|
.cache/
|
||||||
|
|
|
||||||
110
CMakeLists.txt
110
CMakeLists.txt
|
|
@ -1,12 +1,14 @@
|
||||||
cmake_minimum_required(VERSION 3.19)
|
cmake_minimum_required(VERSION 3.19)
|
||||||
|
|
||||||
set(HYPRCURSOR_VERSION "0.1.9")
|
file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
|
||||||
|
string(STRIP ${VER_RAW} HYPRCURSOR_VERSION)
|
||||||
|
|
||||||
add_compile_definitions(HYPRCURSOR_VERSION="${HYPRCURSOR_VERSION}")
|
add_compile_definitions(HYPRCURSOR_VERSION="${HYPRCURSOR_VERSION}")
|
||||||
|
|
||||||
project(hyprcursor
|
project(
|
||||||
VERSION ${HYPRCURSOR_VERSION}
|
hyprcursor
|
||||||
DESCRIPTION "A library and toolkit for the Hyprland cursor format"
|
VERSION ${HYPRCURSOR_VERSION}
|
||||||
)
|
DESCRIPTION "A library and toolkit for the Hyprland cursor format")
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
@ -20,46 +22,68 @@ configure_file(hyprcursor.pc.in hyprcursor.pc @ONLY)
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET hyprlang>=0.4.2 libzip cairo librsvg-2.0 tomlplusplus)
|
pkg_check_modules(
|
||||||
|
deps
|
||||||
|
REQUIRED
|
||||||
|
IMPORTED_TARGET
|
||||||
|
hyprlang>=0.4.2
|
||||||
|
libzip
|
||||||
|
cairo
|
||||||
|
librsvg-2.0
|
||||||
|
tomlplusplus)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||||
message(STATUS "Configuring hyprcursor in Debug")
|
message(STATUS "Configuring hyprcursor in Debug")
|
||||||
add_compile_definitions(HYPRLAND_DEBUG)
|
add_compile_definitions(HYPRLAND_DEBUG)
|
||||||
else()
|
else()
|
||||||
add_compile_options(-O3)
|
add_compile_options(-O3)
|
||||||
message(STATUS "Configuring hyprcursor in Release")
|
message(STATUS "Configuring hyprcursor in Release")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "libhyprcursor/*.cpp" "include/hyprcursor/hyprcursor.hpp" "include/hyprcursor/hyprcursor.h" "include/hyprcursor/shared.h")
|
file(
|
||||||
|
GLOB_RECURSE
|
||||||
|
SRCFILES
|
||||||
|
CONFIGURE_DEPENDS
|
||||||
|
"libhyprcursor/*.cpp"
|
||||||
|
"include/hyprcursor/hyprcursor.hpp"
|
||||||
|
"include/hyprcursor/hyprcursor.h"
|
||||||
|
"include/hyprcursor/shared.h")
|
||||||
|
|
||||||
add_library(hyprcursor SHARED ${SRCFILES})
|
add_library(hyprcursor SHARED ${SRCFILES})
|
||||||
target_include_directories( hyprcursor
|
target_include_directories(
|
||||||
PUBLIC "./include"
|
hyprcursor
|
||||||
PRIVATE "./libhyprcursor"
|
PUBLIC "./include"
|
||||||
)
|
PRIVATE "./libhyprcursor")
|
||||||
set_target_properties(hyprcursor PROPERTIES
|
set_target_properties(
|
||||||
VERSION ${hyprcursor_VERSION}
|
hyprcursor
|
||||||
SOVERSION 0
|
PROPERTIES VERSION ${hyprcursor_VERSION}
|
||||||
PUBLIC_HEADER include/hyprcursor/hyprcursor.hpp include/hyprcursor/hyprcursor.h include/hyprcursor/shared.h
|
SOVERSION 0
|
||||||
)
|
PUBLIC_HEADER include/hyprcursor/hyprcursor.hpp
|
||||||
|
include/hyprcursor/hyprcursor.h include/hyprcursor/shared.h)
|
||||||
|
|
||||||
target_link_libraries(hyprcursor PkgConfig::deps)
|
target_link_libraries(hyprcursor PkgConfig::deps)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
# for std::expected.
|
# for std::expected. probably evil. Arch's clang is very outdated tho...
|
||||||
# probably evil. Arch's clang is very outdated tho...
|
target_compile_options(
|
||||||
target_compile_options(hyprcursor PUBLIC
|
hyprcursor PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-std=gnu++2b
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:-std=gnu++2b -D__cpp_concepts=202002L>
|
-D__cpp_concepts=202002L> -Wno-builtin-macro-redefined)
|
||||||
-Wno-builtin-macro-redefined)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# hyprcursor-util
|
# hyprcursor-util
|
||||||
file(GLOB_RECURSE UTILSRCFILES CONFIGURE_DEPENDS "hyprcursor-util/src/*.cpp" "include/hyprcursor/hyprcursor.hpp" "include/hyprcursor/hyprcursor.h" "include/hyprcursor/shared.h")
|
file(
|
||||||
|
GLOB_RECURSE
|
||||||
|
UTILSRCFILES
|
||||||
|
CONFIGURE_DEPENDS
|
||||||
|
"hyprcursor-util/src/*.cpp"
|
||||||
|
"include/hyprcursor/hyprcursor.hpp"
|
||||||
|
"include/hyprcursor/hyprcursor.h"
|
||||||
|
"include/hyprcursor/shared.h")
|
||||||
add_executable(hyprcursor-util ${UTILSRCFILES})
|
add_executable(hyprcursor-util ${UTILSRCFILES})
|
||||||
target_include_directories(hyprcursor-util
|
target_include_directories(
|
||||||
PUBLIC "./include"
|
hyprcursor-util
|
||||||
PRIVATE "./libhyprcursor" "./hyprcursor-util/src"
|
PUBLIC "./include"
|
||||||
)
|
PRIVATE "./libhyprcursor" "./hyprcursor-util/src")
|
||||||
target_link_libraries(hyprcursor-util PkgConfig::deps hyprcursor)
|
target_link_libraries(hyprcursor-util PkgConfig::deps hyprcursor)
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
|
|
@ -67,21 +91,37 @@ add_custom_target(tests)
|
||||||
|
|
||||||
add_executable(hyprcursor_test1 "tests/full_rendering.cpp")
|
add_executable(hyprcursor_test1 "tests/full_rendering.cpp")
|
||||||
target_link_libraries(hyprcursor_test1 PRIVATE hyprcursor)
|
target_link_libraries(hyprcursor_test1 PRIVATE hyprcursor)
|
||||||
add_test(NAME "Test libhyprcursor in C++ (full rendering)" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprcursor_test1)
|
add_test(
|
||||||
|
NAME "Test libhyprcursor in C++ (full rendering)"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||||
|
COMMAND hyprcursor_test1)
|
||||||
add_dependencies(tests hyprcursor_test1)
|
add_dependencies(tests hyprcursor_test1)
|
||||||
|
|
||||||
add_executable(hyprcursor_test2 "tests/only_metadata.cpp")
|
add_executable(hyprcursor_test2 "tests/only_metadata.cpp")
|
||||||
target_link_libraries(hyprcursor_test2 PRIVATE hyprcursor)
|
target_link_libraries(hyprcursor_test2 PRIVATE hyprcursor)
|
||||||
add_test(NAME "Test libhyprcursor in C++ (only metadata)" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprcursor_test2)
|
add_test(
|
||||||
|
NAME "Test libhyprcursor in C++ (only metadata)"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||||
|
COMMAND hyprcursor_test2)
|
||||||
add_dependencies(tests hyprcursor_test2)
|
add_dependencies(tests hyprcursor_test2)
|
||||||
|
|
||||||
add_executable(hyprcursor_test_c "tests/c_test.c")
|
add_executable(hyprcursor_test_c "tests/c_test.c")
|
||||||
target_link_libraries(hyprcursor_test_c PRIVATE hyprcursor)
|
target_link_libraries(hyprcursor_test_c PRIVATE hyprcursor)
|
||||||
add_test(NAME "Test libhyprcursor in C" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprcursor_test_c)
|
add_test(
|
||||||
|
NAME "Test libhyprcursor in C"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||||
|
COMMAND hyprcursor_test_c)
|
||||||
add_dependencies(tests hyprcursor_test_c)
|
add_dependencies(tests hyprcursor_test_c)
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
install(TARGETS hyprcursor)
|
install(TARGETS hyprcursor)
|
||||||
install(TARGETS hyprcursor-util)
|
install(TARGETS hyprcursor-util)
|
||||||
install(DIRECTORY "include/hyprcursor" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
install(DIRECTORY "include/hyprcursor" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
install(FILES ${CMAKE_BINARY_DIR}/hyprcursor.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
install(FILES ${CMAKE_BINARY_DIR}/hyprcursor.pc
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||||
|
|
||||||
|
if(INSTALL_TESTS)
|
||||||
|
install(TARGETS hyprcursor_test1)
|
||||||
|
install(TARGETS hyprcursor_test2)
|
||||||
|
install(TARGETS hyprcursor_test_c)
|
||||||
|
endif()
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -16,6 +16,11 @@ doesn't suck as much.
|
||||||
- Support for SVG cursors
|
- Support for SVG cursors
|
||||||
- Way more space-efficient. As an example, Bibata-XCursor is 44.1MB, while it's 6.6MB in hyprcursor.
|
- Way more space-efficient. As an example, Bibata-XCursor is 44.1MB, while it's 6.6MB in hyprcursor.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
See the [wiki here](https://wiki.hyprland.org/Hypr-Ecosystem/hyprcursor/)
|
||||||
|
check out [docs/](./docs)
|
||||||
|
and [standards](https://standards.hyprland.org/hyprcursor)
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
### hyprcursor-util
|
### hyprcursor-util
|
||||||
|
|
@ -32,10 +37,6 @@ It provides C and C++ bindings.
|
||||||
|
|
||||||
For both C and C++, see `tests/`.
|
For both C and C++, see `tests/`.
|
||||||
|
|
||||||
## Docs
|
|
||||||
|
|
||||||
See `docs/`.
|
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
Library:
|
Library:
|
||||||
|
|
@ -53,6 +54,7 @@ Util:
|
||||||
- cairo
|
- cairo
|
||||||
- libzip
|
- libzip
|
||||||
- librsvg
|
- librsvg
|
||||||
|
- tomlplusplus
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ Each cursor image is a separate directory. In it, multiple size variations can b
|
||||||
resize_algorithm = bilinear
|
resize_algorithm = bilinear
|
||||||
|
|
||||||
# "hotspot" is where in your cursor the actual "click point" should be.
|
# "hotspot" is where in your cursor the actual "click point" should be.
|
||||||
# this is in absolute coordinates. x+ is east, y+ is north.
|
# this is in absolute coordinates. x+ is east, y+ is south.
|
||||||
# the pixel coordinates of the hotspot at size are rounded to the nearest:
|
# the pixel coordinates of the hotspot at size are rounded to the nearest:
|
||||||
# (round(size * hotspot_x), round(size * hotspot_y))
|
# (round(size * hotspot_x), round(size * hotspot_y))
|
||||||
hotspot_x = 0.0 # this goes 0 - 1
|
hotspot_x = 0.0 # this goes 0 - 1
|
||||||
|
|
@ -71,6 +71,7 @@ define_size = 32, image32.png
|
||||||
# define_size = 64, anim2.png, 500
|
# define_size = 64, anim2.png, 500
|
||||||
# define_size = 64, anim3.png, 500
|
# define_size = 64, anim3.png, 500
|
||||||
# define_size = 64, anim4.png, 500
|
# define_size = 64, anim4.png, 500
|
||||||
|
# Make sure the timeout is > 0, as otherwise the consumer might ignore your timeouts for being invalid.
|
||||||
```
|
```
|
||||||
|
|
||||||
Supported cursor image types are png and svg.
|
Supported cursor image types are png and svg.
|
||||||
|
|
|
||||||
38
flake.lock
38
flake.lock
|
|
@ -2,6 +2,7 @@
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"hyprlang": {
|
"hyprlang": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"hyprutils": "hyprutils",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
|
|
@ -10,11 +11,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1713121246,
|
"lastModified": 1734364628,
|
||||||
"narHash": "sha256-502X0Q0fhN6tJK7iEUA8CghONKSatW/Mqj4Wappd++0=",
|
"narHash": "sha256-ii8fzJfI953n/EmIxVvq64ZAwhvwuuPHWfGd61/mJG8=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprlang",
|
"repo": "hyprlang",
|
||||||
"rev": "78fcaa27ae9e1d782faa3ff06c8ea55ddce63706",
|
"rev": "16e59c1eb13d9fb6de066f54e7555eb5e8a4aba5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -23,13 +24,38 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hyprutils": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hyprlang",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"hyprlang",
|
||||||
|
"systems"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1733502241,
|
||||||
|
"narHash": "sha256-KAUNC4Dgq8WQjYov5auBw/usaHixhacvb7cRDd0AG/k=",
|
||||||
|
"owner": "hyprwm",
|
||||||
|
"repo": "hyprutils",
|
||||||
|
"rev": "104117aed6dd68561be38b50f218190aa47f2cd8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hyprwm",
|
||||||
|
"repo": "hyprutils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1712963716,
|
"lastModified": 1734119587,
|
||||||
"narHash": "sha256-WKm9CvgCldeIVvRz87iOMi8CFVB1apJlkUT4GGvA0iM=",
|
"narHash": "sha256-AKU6qqskl0yf2+JdRdD0cfxX4b9x3KKV5RqA6wijmPM=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "cfd6b5fc90b15709b780a5a1619695a88505a176",
|
"rev": "3566ab7246670a43abd2ffa913cc62dad9cdf7d5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
packages = eachSystem (system: {
|
packages = eachSystem (system: {
|
||||||
default = self.packages.${system}.hyprcursor;
|
default = self.packages.${system}.hyprcursor;
|
||||||
inherit (pkgsFor.${system}) hyprcursor;
|
inherit (pkgsFor.${system}) hyprcursor hyprcursor-with-tests;
|
||||||
});
|
});
|
||||||
|
|
||||||
checks = eachSystem (system: self.packages.${system});
|
checks = eachSystem (system: self.packages.${system});
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <zip.h>
|
#include <zip.h>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ struct SCursorRawShapeDataC {
|
||||||
char* overridenBy;
|
char* overridenBy;
|
||||||
enum eHyprcursorResizeAlgo resizeAlgo;
|
enum eHyprcursorResizeAlgo resizeAlgo;
|
||||||
enum eHyprcursorDataType type;
|
enum eHyprcursorDataType type;
|
||||||
|
float nominalSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct SCursorRawShapeDataC hyprcursor_cursor_raw_shape_data;
|
typedef struct SCursorRawShapeDataC hyprcursor_cursor_raw_shape_data;
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,7 @@ CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char del
|
||||||
std::string args{in};
|
std::string args{in};
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
std::ranges::replace_if(
|
std::ranges::replace_if(args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
|
||||||
args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
|
|
||||||
|
|
||||||
for (const auto& s : args | std::views::split(0)) {
|
for (const auto& s : args | std::views::split(0)) {
|
||||||
if (removeEmpty && s.empty())
|
if (removeEmpty && s.empty())
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
#include "internalSharedTypes.hpp"
|
#include "internalSharedTypes.hpp"
|
||||||
#include "internalDefines.hpp"
|
#include "internalDefines.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <sstream>
|
||||||
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <zip.h>
|
#include <zip.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
@ -15,11 +17,25 @@
|
||||||
|
|
||||||
using namespace Hyprcursor;
|
using namespace Hyprcursor;
|
||||||
|
|
||||||
// directories for lookup
|
static std::vector<std::string> getSystemThemeDirs() {
|
||||||
constexpr const std::array<const char*, 1> systemThemeDirs = {"/usr/share/icons"};
|
const auto envXdgData = std::getenv("XDG_DATA_DIRS");
|
||||||
|
std::vector<std::string> result;
|
||||||
|
if (envXdgData) {
|
||||||
|
std::stringstream envXdgStream(envXdgData);
|
||||||
|
std::string tmpStr;
|
||||||
|
while (getline(envXdgStream, tmpStr, ':'))
|
||||||
|
result.push_back((tmpStr + "/icons"));
|
||||||
|
} else
|
||||||
|
result = {"/usr/share/icons"};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> systemThemeDirs = getSystemThemeDirs();
|
||||||
constexpr const std::array<const char*, 2> userThemeDirs = {"/.local/share/icons", "/.icons"};
|
constexpr const std::array<const char*, 2> userThemeDirs = {"/.local/share/icons", "/.icons"};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
static std::string themeNameFromEnv(PHYPRCURSORLOGFUNC logfn) {
|
static std::string themeNameFromEnv(PHYPRCURSORLOGFUNC logfn) {
|
||||||
const auto ENV = getenv("HYPRCURSOR_THEME");
|
const auto ENV = getenv("HYPRCURSOR_THEME");
|
||||||
if (!ENV) {
|
if (!ENV) {
|
||||||
|
|
@ -291,13 +307,15 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap
|
||||||
if (REQUESTEDSHAPE != shape->directory && std::find(shape->overrides.begin(), shape->overrides.end(), REQUESTEDSHAPE) == shape->overrides.end())
|
if (REQUESTEDSHAPE != shape->directory && std::find(shape->overrides.begin(), shape->overrides.end(), REQUESTEDSHAPE) == shape->overrides.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
const int PIXELSIDE = std::round(info.size / shape->nominalSize);
|
||||||
|
|
||||||
hotX = shape->hotspotX;
|
hotX = shape->hotspotX;
|
||||||
hotY = shape->hotspotY;
|
hotY = shape->hotspotY;
|
||||||
|
|
||||||
// matched :)
|
// matched :)
|
||||||
bool foundAny = false;
|
bool foundAny = false;
|
||||||
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
||||||
if (image->side != info.size)
|
if (image->side != PIXELSIDE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// found size
|
// found size
|
||||||
|
|
@ -317,7 +335,7 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap
|
||||||
// find nearest
|
// find nearest
|
||||||
int leader = 13371337;
|
int leader = 13371337;
|
||||||
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
||||||
if (std::abs((int)(image->side - info.size)) > std::abs((int)(leader - info.size)))
|
if (std::abs((int)(image->side - PIXELSIDE)) > std::abs((int)(leader - PIXELSIDE)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
leader = image->side;
|
leader = image->side;
|
||||||
|
|
@ -373,14 +391,25 @@ SCursorRawShapeDataC* CHyprcursorManager::getRawShapeDataC(const char* shape_) {
|
||||||
|
|
||||||
SCursorRawShapeDataC* data = new SCursorRawShapeDataC;
|
SCursorRawShapeDataC* data = new SCursorRawShapeDataC;
|
||||||
std::vector<SLoadedCursorImage*> resultingImages;
|
std::vector<SLoadedCursorImage*> resultingImages;
|
||||||
|
data->overridenBy = nullptr;
|
||||||
|
data->images = nullptr;
|
||||||
|
data->len = 0;
|
||||||
|
data->hotspotX = 0.f;
|
||||||
|
data->hotspotY = 0.F;
|
||||||
|
data->nominalSize = 1.F;
|
||||||
|
data->resizeAlgo = eHyprcursorResizeAlgo::HC_RESIZE_NONE;
|
||||||
|
data->type = eHyprcursorDataType::HC_DATA_PNG;
|
||||||
|
|
||||||
for (auto& shape : impl->theme.shapes) {
|
for (auto& shape : impl->theme.shapes) {
|
||||||
// if it's overridden just return the override
|
// if it's overridden just return the override
|
||||||
if (const auto IT = std::find(shape->overrides.begin(), shape->overrides.end(), SHAPE); IT != shape->overrides.end()) {
|
if (const auto IT = std::find_if(shape->overrides.begin(), shape->overrides.end(), [&](const auto& e) { return e == SHAPE && SHAPE != shape->directory; });
|
||||||
data->overridenBy = strdup(IT->c_str());
|
IT != shape->overrides.end()) {
|
||||||
|
data->overridenBy = strdup(shape->directory.c_str());
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& shape : impl->theme.shapes) {
|
||||||
if (shape->directory != SHAPE)
|
if (shape->directory != SHAPE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -392,9 +421,10 @@ SCursorRawShapeDataC* CHyprcursorManager::getRawShapeDataC(const char* shape_) {
|
||||||
resultingImages.push_back(i.get());
|
resultingImages.push_back(i.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
data->hotspotX = shape->hotspotX;
|
data->hotspotX = shape->hotspotX;
|
||||||
data->hotspotY = shape->hotspotY;
|
data->hotspotY = shape->hotspotY;
|
||||||
data->type = shape->shapeType == SHAPE_PNG ? HC_DATA_PNG : HC_DATA_SVG;
|
data->nominalSize = shape->nominalSize;
|
||||||
|
data->type = shape->shapeType == SHAPE_PNG ? HC_DATA_PNG : HC_DATA_SVG;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,8 +454,10 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||||
bool sizeFound = false;
|
bool sizeFound = false;
|
||||||
|
|
||||||
if (shape->shapeType == SHAPE_PNG) {
|
if (shape->shapeType == SHAPE_PNG) {
|
||||||
|
const int IDEALSIDE = std::round(info.size / shape->nominalSize);
|
||||||
|
|
||||||
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
||||||
if (image->side != info.size)
|
if (image->side != IDEALSIDE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sizeFound = true;
|
sizeFound = true;
|
||||||
|
|
@ -439,7 +471,7 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||||
SLoadedCursorImage* leader = nullptr;
|
SLoadedCursorImage* leader = nullptr;
|
||||||
int leaderVal = 1000000;
|
int leaderVal = 1000000;
|
||||||
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
||||||
if (image->side < info.size)
|
if (image->side < IDEALSIDE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (image->side > leaderVal)
|
if (image->side > leaderVal)
|
||||||
|
|
@ -451,7 +483,7 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||||
|
|
||||||
if (!leader) {
|
if (!leader) {
|
||||||
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
for (auto& image : impl->loadedShapes[shape.get()].images) {
|
||||||
if (std::abs((int)(image->side - info.size)) > leaderVal)
|
if (std::abs((int)(image->side - IDEALSIDE)) > leaderVal)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
leaderVal = image->side;
|
leaderVal = image->side;
|
||||||
|
|
@ -468,12 +500,16 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||||
|
|
||||||
Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: png shape {} has {} frames", shape->directory, FRAMES.size());
|
Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: png shape {} has {} frames", shape->directory, FRAMES.size());
|
||||||
|
|
||||||
|
const int PIXELSIDE = std::round(leader->side / shape->nominalSize);
|
||||||
|
|
||||||
|
Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: png shape has nominal {:.2f}, pixel size will be {}x", shape->nominalSize, PIXELSIDE);
|
||||||
|
|
||||||
for (auto& f : FRAMES) {
|
for (auto& f : FRAMES) {
|
||||||
auto& newImage = impl->loadedShapes[shape.get()].images.emplace_back(std::make_unique<SLoadedCursorImage>());
|
auto& newImage = impl->loadedShapes[shape.get()].images.emplace_back(std::make_unique<SLoadedCursorImage>());
|
||||||
newImage->artificial = true;
|
newImage->artificial = true;
|
||||||
newImage->side = info.size;
|
newImage->side = PIXELSIDE;
|
||||||
newImage->artificialData = new char[info.size * info.size * 4];
|
newImage->artificialData = new char[PIXELSIDE * PIXELSIDE * 4];
|
||||||
newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, info.size, info.size, info.size * 4);
|
newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, PIXELSIDE, PIXELSIDE, PIXELSIDE * 4);
|
||||||
newImage->delay = f->delay;
|
newImage->delay = f->delay;
|
||||||
|
|
||||||
const auto PCAIRO = cairo_create(newImage->cairoSurface);
|
const auto PCAIRO = cairo_create(newImage->cairoSurface);
|
||||||
|
|
@ -487,12 +523,12 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||||
|
|
||||||
const auto PTN = cairo_pattern_create_for_surface(f->cairoSurface);
|
const auto PTN = cairo_pattern_create_for_surface(f->cairoSurface);
|
||||||
cairo_pattern_set_extend(PTN, CAIRO_EXTEND_NONE);
|
cairo_pattern_set_extend(PTN, CAIRO_EXTEND_NONE);
|
||||||
const float scale = info.size / (float)f->side;
|
const float scale = PIXELSIDE / (float)f->side;
|
||||||
cairo_scale(PCAIRO, scale, scale);
|
cairo_scale(PCAIRO, scale, scale);
|
||||||
cairo_pattern_set_filter(PTN, shape->resizeAlgo == HC_RESIZE_BILINEAR ? CAIRO_FILTER_GOOD : CAIRO_FILTER_NEAREST);
|
cairo_pattern_set_filter(PTN, shape->resizeAlgo == HC_RESIZE_BILINEAR ? CAIRO_FILTER_GOOD : CAIRO_FILTER_NEAREST);
|
||||||
cairo_set_source(PCAIRO, PTN);
|
cairo_set_source(PCAIRO, PTN);
|
||||||
|
|
||||||
cairo_rectangle(PCAIRO, 0, 0, info.size, info.size);
|
cairo_rectangle(PCAIRO, 0, 0, PIXELSIDE, PIXELSIDE);
|
||||||
|
|
||||||
cairo_fill(PCAIRO);
|
cairo_fill(PCAIRO);
|
||||||
cairo_surface_flush(newImage->cairoSurface);
|
cairo_surface_flush(newImage->cairoSurface);
|
||||||
|
|
@ -505,12 +541,16 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||||
|
|
||||||
Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: svg shape {} has {} frames", shape->directory, FRAMES.size());
|
Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: svg shape {} has {} frames", shape->directory, FRAMES.size());
|
||||||
|
|
||||||
|
const int PIXELSIDE = std::round(info.size / shape->nominalSize);
|
||||||
|
|
||||||
|
Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: svg shape has nominal {:.2f}, pixel size will be {}x", shape->nominalSize, PIXELSIDE);
|
||||||
|
|
||||||
for (auto& f : FRAMES) {
|
for (auto& f : FRAMES) {
|
||||||
auto& newImage = impl->loadedShapes[shape.get()].images.emplace_back(std::make_unique<SLoadedCursorImage>());
|
auto& newImage = impl->loadedShapes[shape.get()].images.emplace_back(std::make_unique<SLoadedCursorImage>());
|
||||||
newImage->artificial = true;
|
newImage->artificial = true;
|
||||||
newImage->side = info.size;
|
newImage->side = PIXELSIDE;
|
||||||
newImage->artificialData = new char[info.size * info.size * 4];
|
newImage->artificialData = new char[PIXELSIDE * PIXELSIDE * 4];
|
||||||
newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, info.size, info.size, info.size * 4);
|
newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, PIXELSIDE, PIXELSIDE, PIXELSIDE * 4);
|
||||||
newImage->delay = f->delay;
|
newImage->delay = f->delay;
|
||||||
|
|
||||||
const auto PCAIRO = cairo_create(newImage->cairoSurface);
|
const auto PCAIRO = cairo_create(newImage->cairoSurface);
|
||||||
|
|
@ -528,7 +568,7 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RsvgRectangle rect = {0, 0, (double)info.size, (double)info.size};
|
RsvgRectangle rect = {0, 0, (double)PIXELSIDE, (double)PIXELSIDE};
|
||||||
|
|
||||||
if (!rsvg_handle_render_document(handle, PCAIRO, &rect, &error)) {
|
if (!rsvg_handle_render_document(handle, PCAIRO, &rect, &error)) {
|
||||||
Debug::log(HC_LOG_ERR, logFn, "Failed rendering svg: {}", error->message);
|
Debug::log(HC_LOG_ERR, logFn, "Failed rendering svg: {}", error->message);
|
||||||
|
|
@ -558,7 +598,7 @@ void CHyprcursorManager::cursorSurfaceStyleDone(const SCursorStyleInfo& info) {
|
||||||
const bool isArtificial = e->artificial;
|
const bool isArtificial = e->artificial;
|
||||||
|
|
||||||
// clean artificial rasters made for this
|
// clean artificial rasters made for this
|
||||||
if (isArtificial && e->side == info.size)
|
if (isArtificial && e->side == std::round(info.size / shape->nominalSize))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// clean invalid non-svg rasters
|
// clean invalid non-svg rasters
|
||||||
|
|
@ -634,18 +674,22 @@ std::optional<std::string> CHyprcursorImplementation::loadTheme() {
|
||||||
int errp = 0;
|
int errp = 0;
|
||||||
zip_t* zip = zip_open(cursor.path().string().c_str(), ZIP_RDONLY, &errp);
|
zip_t* zip = zip_open(cursor.path().string().c_str(), ZIP_RDONLY, &errp);
|
||||||
|
|
||||||
zip_file_t* meta_file = zip_fopen(zip, "meta.hl", ZIP_FL_UNCHANGED);
|
zip_int64_t index = zip_name_locate(zip, "meta.hl", ZIP_FL_ENC_GUESS);
|
||||||
bool metaIsHL = true;
|
bool metaIsHL = true;
|
||||||
if (!meta_file) {
|
|
||||||
meta_file = zip_fopen(zip, "meta.toml", ZIP_FL_UNCHANGED);
|
if (index == -1) {
|
||||||
metaIsHL = false;
|
index = zip_name_locate(zip, "meta.toml", ZIP_FL_ENC_GUESS);
|
||||||
if (!meta_file)
|
metaIsHL = false;
|
||||||
return "cursor" + cursor.path().string() + "failed to load meta";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* buffer = new char[1024 * 1024]; /* 1MB should be more than enough */
|
if (index == -1)
|
||||||
|
return "cursor" + cursor.path().string() + "failed to load meta";
|
||||||
|
|
||||||
int readBytes = zip_fread(meta_file, buffer, 1024 * 1024 - 1);
|
zip_file_t* meta_file = zip_fopen_index(zip, index, ZIP_FL_UNCHANGED);
|
||||||
|
|
||||||
|
char* buffer = new char[1024 * 1024]; /* 1MB should be more than enough */
|
||||||
|
|
||||||
|
int readBytes = zip_fread(meta_file, buffer, 1024 * 1024 - 1);
|
||||||
|
|
||||||
zip_fclose(meta_file);
|
zip_fclose(meta_file);
|
||||||
|
|
||||||
|
|
@ -670,6 +714,9 @@ std::optional<std::string> CHyprcursorImplementation::loadTheme() {
|
||||||
|
|
||||||
SHAPE->overrides = meta.parsedData.overrides;
|
SHAPE->overrides = meta.parsedData.overrides;
|
||||||
|
|
||||||
|
zip_stat_t sb;
|
||||||
|
zip_stat_init(&sb);
|
||||||
|
|
||||||
for (auto& i : SHAPE->images) {
|
for (auto& i : SHAPE->images) {
|
||||||
if (SHAPE->shapeType == SHAPE_INVALID) {
|
if (SHAPE->shapeType == SHAPE_INVALID) {
|
||||||
if (i.filename.ends_with(".svg"))
|
if (i.filename.ends_with(".svg"))
|
||||||
|
|
@ -694,14 +741,23 @@ std::optional<std::string> CHyprcursorImplementation::loadTheme() {
|
||||||
IMAGE->delay = i.delay;
|
IMAGE->delay = i.delay;
|
||||||
IMAGE->isSVG = SHAPE->shapeType == SHAPE_SVG;
|
IMAGE->isSVG = SHAPE->shapeType == SHAPE_SVG;
|
||||||
|
|
||||||
// read from zip
|
index = zip_name_locate(zip, i.filename.c_str(), ZIP_FL_ENC_GUESS);
|
||||||
zip_file_t* image_file = zip_fopen(zip, i.filename.c_str(), ZIP_FL_UNCHANGED);
|
if (index == -1)
|
||||||
if (!image_file)
|
|
||||||
return "cursor" + cursor.path().string() + "failed to load image_file";
|
return "cursor" + cursor.path().string() + "failed to load image_file";
|
||||||
|
|
||||||
IMAGE->data = new char[1024 * 1024]; /* 1MB should be more than enough, again. This probably should be in the spec. */
|
// read from zip
|
||||||
|
zip_file_t* image_file = zip_fopen_index(zip, index, ZIP_FL_UNCHANGED);
|
||||||
|
zip_stat_index(zip, index, ZIP_FL_UNCHANGED, &sb);
|
||||||
|
|
||||||
IMAGE->dataLen = zip_fread(image_file, IMAGE->data, 1024 * 1024 - 1);
|
if (sb.valid & ZIP_STAT_SIZE) {
|
||||||
|
IMAGE->data = new char[sb.size + 1];
|
||||||
|
IMAGE->dataLen = sb.size + 1;
|
||||||
|
} else {
|
||||||
|
IMAGE->data = new char[1024 * 1024]; /* 1MB should be more than enough, again. This probably should be in the spec. */
|
||||||
|
IMAGE->dataLen = 1024 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE->dataLen = zip_fread(image_file, IMAGE->data, IMAGE->dataLen - 1);
|
||||||
|
|
||||||
zip_fclose(image_file);
|
zip_fclose(image_file);
|
||||||
|
|
||||||
|
|
@ -712,7 +768,7 @@ std::optional<std::string> CHyprcursorImplementation::loadTheme() {
|
||||||
IMAGE->cairoSurface = cairo_image_surface_create_from_png_stream(::readPNG, IMAGE);
|
IMAGE->cairoSurface = cairo_image_surface_create_from_png_stream(::readPNG, IMAGE);
|
||||||
|
|
||||||
if (const auto STATUS = cairo_surface_status(IMAGE->cairoSurface); STATUS != CAIRO_STATUS_SUCCESS) {
|
if (const auto STATUS = cairo_surface_status(IMAGE->cairoSurface); STATUS != CAIRO_STATUS_SUCCESS) {
|
||||||
delete[](char*) IMAGE->data;
|
delete[] (char*)IMAGE->data;
|
||||||
IMAGE->data = nullptr;
|
IMAGE->data = nullptr;
|
||||||
return "Failed reading cairoSurface, status " + std::to_string((int)STATUS);
|
return "Failed reading cairoSurface, status " + std::to_string((int)STATUS);
|
||||||
}
|
}
|
||||||
|
|
@ -724,10 +780,11 @@ std::optional<std::string> CHyprcursorImplementation::loadTheme() {
|
||||||
if (SHAPE->images.empty())
|
if (SHAPE->images.empty())
|
||||||
return "meta invalid: no images for shape " + cursor.path().stem().string();
|
return "meta invalid: no images for shape " + cursor.path().stem().string();
|
||||||
|
|
||||||
SHAPE->directory = cursor.path().stem().string();
|
SHAPE->directory = cursor.path().stem().string();
|
||||||
SHAPE->hotspotX = meta.parsedData.hotspotX;
|
SHAPE->hotspotX = meta.parsedData.hotspotX;
|
||||||
SHAPE->hotspotY = meta.parsedData.hotspotY;
|
SHAPE->hotspotY = meta.parsedData.hotspotY;
|
||||||
SHAPE->resizeAlgo = stringToAlgo(meta.parsedData.resizeAlgo);
|
SHAPE->nominalSize = meta.parsedData.nominalSize;
|
||||||
|
SHAPE->resizeAlgo = stringToAlgo(meta.parsedData.resizeAlgo);
|
||||||
|
|
||||||
zip_discard(zip);
|
zip_discard(zip);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ struct SCursorImage {
|
||||||
|
|
||||||
struct SCursorShape {
|
struct SCursorShape {
|
||||||
std::string directory;
|
std::string directory;
|
||||||
float hotspotX = 0, hotspotY = 0;
|
float hotspotX = 0, hotspotY = 0, nominalSize = 1.F;
|
||||||
eHyprcursorResizeAlgo resizeAlgo = HC_RESIZE_NEAREST;
|
eHyprcursorResizeAlgo resizeAlgo = HC_RESIZE_NEAREST;
|
||||||
std::vector<SCursorImage> images;
|
std::vector<SCursorImage> images;
|
||||||
std::vector<std::string> overrides;
|
std::vector<std::string> overrides;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <toml++/toml.hpp>
|
#include <toml++/toml.hpp>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "VarList.hpp"
|
#include "VarList.hpp"
|
||||||
|
|
||||||
|
|
@ -71,49 +72,53 @@ static Hyprlang::CParseResult parseDefineSize(const char* C, const char* V) {
|
||||||
Hyprlang::CParseResult result;
|
Hyprlang::CParseResult result;
|
||||||
const std::string VALUE = V;
|
const std::string VALUE = V;
|
||||||
|
|
||||||
if (!VALUE.contains(",")) {
|
CVarList sizes(VALUE, 0, ';');
|
||||||
result.setError("Invalid define_size");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto LHS = removeBeginEndSpacesTabs(VALUE.substr(0, VALUE.find_first_of(",")));
|
for (const auto& sizeStr : sizes) {
|
||||||
auto RHS = removeBeginEndSpacesTabs(VALUE.substr(VALUE.find_first_of(",") + 1));
|
if (!sizeStr.contains(",")) {
|
||||||
auto DELAY = 0;
|
result.setError("Invalid define_size");
|
||||||
|
|
||||||
CMeta::SDefinedSize size;
|
|
||||||
|
|
||||||
if (RHS.contains(",")) {
|
|
||||||
const auto LL = removeBeginEndSpacesTabs(RHS.substr(0, RHS.find(",")));
|
|
||||||
const auto RR = removeBeginEndSpacesTabs(RHS.substr(RHS.find(",") + 1));
|
|
||||||
|
|
||||||
try {
|
|
||||||
size.delayMs = std::stoull(RR);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
result.setError(e.what());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RHS = LL;
|
auto LHS = removeBeginEndSpacesTabs(sizeStr.substr(0, sizeStr.find_first_of(",")));
|
||||||
}
|
auto RHS = removeBeginEndSpacesTabs(sizeStr.substr(sizeStr.find_first_of(",") + 1));
|
||||||
|
auto DELAY = 0;
|
||||||
|
|
||||||
if (!std::regex_match(RHS, std::regex("^[A-Za-z0-9_\\-\\.]+$"))) {
|
CMeta::SDefinedSize size;
|
||||||
result.setError("Invalid cursor file name, characters must be within [A-Za-z0-9_\\-\\.] (if this seems like a mistake, check for invisible characters)");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
size.file = RHS;
|
if (RHS.contains(",")) {
|
||||||
|
const auto LL = removeBeginEndSpacesTabs(RHS.substr(0, RHS.find(",")));
|
||||||
|
const auto RR = removeBeginEndSpacesTabs(RHS.substr(RHS.find(",") + 1));
|
||||||
|
|
||||||
if (!size.file.ends_with(".svg")) {
|
try {
|
||||||
try {
|
size.delayMs = std::stoull(RR);
|
||||||
size.size = std::stoull(LHS);
|
} catch (std::exception& e) {
|
||||||
} catch (std::exception& e) {
|
result.setError(e.what());
|
||||||
result.setError(e.what());
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHS = LL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::regex_match(RHS, std::regex("^[A-Za-z0-9_\\-\\.]+$"))) {
|
||||||
|
result.setError("Invalid cursor file name, characters must be within [A-Za-z0-9_\\-\\.] (if this seems like a mistake, check for invisible characters)");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
size.size = 0;
|
|
||||||
|
|
||||||
currentMeta->parsedData.definedSizes.push_back(size);
|
size.file = RHS;
|
||||||
|
|
||||||
|
if (!size.file.ends_with(".svg")) {
|
||||||
|
try {
|
||||||
|
size.size = std::stoull(LHS);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
result.setError(e.what());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
size.size = 0;
|
||||||
|
|
||||||
|
currentMeta->parsedData.definedSizes.push_back(size);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +127,11 @@ static Hyprlang::CParseResult parseOverride(const char* C, const char* V) {
|
||||||
Hyprlang::CParseResult result;
|
Hyprlang::CParseResult result;
|
||||||
const std::string VALUE = V;
|
const std::string VALUE = V;
|
||||||
|
|
||||||
currentMeta->parsedData.overrides.push_back(VALUE);
|
CVarList overrides(VALUE, 0, ';');
|
||||||
|
|
||||||
|
for (const auto& o : overrides) {
|
||||||
|
currentMeta->parsedData.overrides.push_back(VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -134,6 +143,7 @@ std::optional<std::string> CMeta::parseHL() {
|
||||||
meta = std::make_unique<Hyprlang::CConfig>(rawdata.c_str(), Hyprlang::SConfigOptions{.pathIsStream = !dataPath});
|
meta = std::make_unique<Hyprlang::CConfig>(rawdata.c_str(), Hyprlang::SConfigOptions{.pathIsStream = !dataPath});
|
||||||
meta->addConfigValue("hotspot_x", Hyprlang::FLOAT{0.F});
|
meta->addConfigValue("hotspot_x", Hyprlang::FLOAT{0.F});
|
||||||
meta->addConfigValue("hotspot_y", Hyprlang::FLOAT{0.F});
|
meta->addConfigValue("hotspot_y", Hyprlang::FLOAT{0.F});
|
||||||
|
meta->addConfigValue("nominal_size", Hyprlang::FLOAT{1.F});
|
||||||
meta->addConfigValue("resize_algorithm", Hyprlang::STRING{"nearest"});
|
meta->addConfigValue("resize_algorithm", Hyprlang::STRING{"nearest"});
|
||||||
meta->registerHandler(::parseDefineSize, "define_size", {.allowFlags = false});
|
meta->registerHandler(::parseDefineSize, "define_size", {.allowFlags = false});
|
||||||
meta->registerHandler(::parseOverride, "define_override", {.allowFlags = false});
|
meta->registerHandler(::parseOverride, "define_override", {.allowFlags = false});
|
||||||
|
|
@ -143,9 +153,10 @@ std::optional<std::string> CMeta::parseHL() {
|
||||||
return RESULT.getError();
|
return RESULT.getError();
|
||||||
} catch (const char* err) { return "failed parsing meta: " + std::string{err}; }
|
} catch (const char* err) { return "failed parsing meta: " + std::string{err}; }
|
||||||
|
|
||||||
parsedData.hotspotX = std::any_cast<Hyprlang::FLOAT>(meta->getConfigValue("hotspot_x"));
|
parsedData.hotspotX = std::any_cast<Hyprlang::FLOAT>(meta->getConfigValue("hotspot_x"));
|
||||||
parsedData.hotspotY = std::any_cast<Hyprlang::FLOAT>(meta->getConfigValue("hotspot_y"));
|
parsedData.hotspotY = std::any_cast<Hyprlang::FLOAT>(meta->getConfigValue("hotspot_y"));
|
||||||
parsedData.resizeAlgo = std::any_cast<Hyprlang::STRING>(meta->getConfigValue("resize_algorithm"));
|
parsedData.nominalSize = std::clamp(std::any_cast<Hyprlang::FLOAT>(meta->getConfigValue("nominal_size")), 0.1F, 2.F);
|
||||||
|
parsedData.resizeAlgo = std::any_cast<Hyprlang::STRING>(meta->getConfigValue("resize_algorithm"));
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -154,8 +165,9 @@ std::optional<std::string> CMeta::parseTOML() {
|
||||||
try {
|
try {
|
||||||
auto MANIFEST = dataPath ? toml::parse_file(rawdata) : toml::parse(rawdata);
|
auto MANIFEST = dataPath ? toml::parse_file(rawdata) : toml::parse(rawdata);
|
||||||
|
|
||||||
parsedData.hotspotX = MANIFEST["General"]["hotspot_x"].value_or(0.f);
|
parsedData.hotspotX = MANIFEST["General"]["hotspot_x"].value_or(0.f);
|
||||||
parsedData.hotspotY = MANIFEST["General"]["hotspot_y"].value_or(0.f);
|
parsedData.hotspotY = MANIFEST["General"]["hotspot_y"].value_or(0.f);
|
||||||
|
parsedData.nominalSize = std::clamp(MANIFEST["General"]["nominal_size"].value_or(1.F), 0.1F, 2.F);
|
||||||
|
|
||||||
const std::string OVERRIDES = MANIFEST["General"]["define_override"].value_or("");
|
const std::string OVERRIDES = MANIFEST["General"]["define_override"].value_or("");
|
||||||
const std::string SIZES = MANIFEST["General"]["define_size"].value_or("");
|
const std::string SIZES = MANIFEST["General"]["define_size"].value_or("");
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class CMeta {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::string resizeAlgo;
|
std::string resizeAlgo;
|
||||||
float hotspotX = 0, hotspotY = 0;
|
float hotspotX = 0, hotspotY = 0, nominalSize = 1.F;
|
||||||
std::vector<std::string> overrides;
|
std::vector<std::string> overrides;
|
||||||
std::vector<SDefinedSize> definedSizes;
|
std::vector<SDefinedSize> definedSizes;
|
||||||
} parsedData;
|
} parsedData;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
hyprlang,
|
hyprlang,
|
||||||
librsvg,
|
librsvg,
|
||||||
libzip,
|
libzip,
|
||||||
|
xcur2png,
|
||||||
tomlplusplus,
|
tomlplusplus,
|
||||||
version ? "git",
|
version ? "git",
|
||||||
}:
|
}:
|
||||||
|
|
@ -15,11 +16,6 @@ stdenv.mkDerivation {
|
||||||
inherit version;
|
inherit version;
|
||||||
src = ../.;
|
src = ../.;
|
||||||
|
|
||||||
patches = [
|
|
||||||
# adds /run/current-system/sw/share/icons to the icon lookup directories
|
|
||||||
./dirs.patch
|
|
||||||
];
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
cmake
|
cmake
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
@ -30,6 +26,7 @@ stdenv.mkDerivation {
|
||||||
hyprlang
|
hyprlang
|
||||||
librsvg
|
librsvg
|
||||||
libzip
|
libzip
|
||||||
|
xcur2png
|
||||||
tomlplusplus
|
tomlplusplus
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
diff --git a/libhyprcursor/hyprcursor.cpp b/libhyprcursor/hyprcursor.cpp
|
|
||||||
index 304ab9f..1f7e95d 100644
|
|
||||||
--- a/libhyprcursor/hyprcursor.cpp
|
|
||||||
+++ b/libhyprcursor/hyprcursor.cpp
|
|
||||||
@@ -14,7 +14,7 @@
|
|
||||||
using namespace Hyprcursor;
|
|
||||||
|
|
||||||
// directories for lookup
|
|
||||||
-constexpr const std::array<const char*, 1> systemThemeDirs = {"/usr/share/icons"};
|
|
||||||
+constexpr const std::array<const char*, 2> systemThemeDirs = {"/usr/share/icons", "/run/current-system/sw/share/icons"};
|
|
||||||
constexpr const std::array<const char*, 2> userThemeDirs = {"/.local/share/icons", "/.icons"};
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
(builtins.substring 4 2 longDate)
|
(builtins.substring 4 2 longDate)
|
||||||
(builtins.substring 6 2 longDate)
|
(builtins.substring 6 2 longDate)
|
||||||
]);
|
]);
|
||||||
|
version = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
||||||
in {
|
in {
|
||||||
default = inputs.self.overlays.hyprcursor;
|
default = inputs.self.overlays.hyprcursor;
|
||||||
|
|
||||||
|
|
@ -14,10 +15,14 @@ in {
|
||||||
inputs.hyprlang.overlays.default
|
inputs.hyprlang.overlays.default
|
||||||
(final: prev: {
|
(final: prev: {
|
||||||
hyprcursor = prev.callPackage ./default.nix {
|
hyprcursor = prev.callPackage ./default.nix {
|
||||||
stdenv = prev.gcc13Stdenv;
|
stdenv = prev.gcc14Stdenv;
|
||||||
version = "0.pre" + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty");
|
version = version + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty");
|
||||||
inherit (final) hyprlang;
|
inherit (final) hyprlang;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hyprcursor-with-tests = final.hyprcursor.overrideAttrs (_: _: {
|
||||||
|
cmakeFlags = [(lib.cmakeBool "INSTALL_TESTS" true)];
|
||||||
|
});
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,22 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hyprcursor_cursor_raw_shape_data* shapeData = hyprcursor_get_raw_shape_data(mgr, "left_ptr");
|
hyprcursor_cursor_raw_shape_data* shapeData = hyprcursor_get_raw_shape_data(mgr, "left_ptr");
|
||||||
if (!shapeData || shapeData->len <= 0) {
|
if (!shapeData) {
|
||||||
printf("failed querying left_ptr\n");
|
printf("failed querying left_ptr\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shapeData->overridenBy) {
|
||||||
|
hyprcursor_cursor_raw_shape_data* ov = hyprcursor_get_raw_shape_data(mgr, shapeData->overridenBy);
|
||||||
|
hyprcursor_raw_shape_data_free(shapeData);
|
||||||
|
shapeData = ov;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shapeData || shapeData->len <= 0) {
|
||||||
|
printf("left_ptr has no images\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
printf("left_ptr images: %d\n", shapeData->len);
|
printf("left_ptr images: %d\n", shapeData->len);
|
||||||
|
|
||||||
for (size_t i = 0; i < shapeData->len; ++i) {
|
for (size_t i = 0; i < shapeData->len; ++i) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue