diff --git a/include/hyprcursor/shared.h b/include/hyprcursor/shared.h index c3fb9c6..c36456c 100644 --- a/include/hyprcursor/shared.h +++ b/include/hyprcursor/shared.h @@ -54,6 +54,7 @@ struct SCursorRawShapeDataC { char* overridenBy; enum eHyprcursorResizeAlgo resizeAlgo; enum eHyprcursorDataType type; + float nominalSize; }; typedef struct SCursorRawShapeDataC hyprcursor_cursor_raw_shape_data; diff --git a/libhyprcursor/hyprcursor.cpp b/libhyprcursor/hyprcursor.cpp index 544a506..e0926f6 100644 --- a/libhyprcursor/hyprcursor.cpp +++ b/libhyprcursor/hyprcursor.cpp @@ -307,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()) continue; + const int PIXELSIDE = std::round(info.size / shape->nominalSize); + hotX = shape->hotspotX; hotY = shape->hotspotY; // matched :) bool foundAny = false; for (auto& image : impl->loadedShapes[shape.get()].images) { - if (image->side != info.size) + if (image->side != PIXELSIDE) continue; // found size @@ -333,7 +335,7 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap // find nearest int leader = 13371337; 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; leader = image->side; @@ -392,8 +394,9 @@ SCursorRawShapeDataC* CHyprcursorManager::getRawShapeDataC(const char* shape_) { data->overridenBy = nullptr; data->images = nullptr; data->len = 0; - data->hotspotX = 0; - data->hotspotY = 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; @@ -418,9 +421,10 @@ SCursorRawShapeDataC* CHyprcursorManager::getRawShapeDataC(const char* shape_) { resultingImages.push_back(i.get()); } - data->hotspotX = shape->hotspotX; - data->hotspotY = shape->hotspotY; - data->type = shape->shapeType == SHAPE_PNG ? HC_DATA_PNG : HC_DATA_SVG; + data->hotspotX = shape->hotspotX; + data->hotspotY = shape->hotspotY; + data->nominalSize = shape->nominalSize; + data->type = shape->shapeType == SHAPE_PNG ? HC_DATA_PNG : HC_DATA_SVG; break; } @@ -494,12 +498,16 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: png shape {} has {} frames", shape->directory, FRAMES.size()); + const int PIXELSIDE = std::round(info.size / 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) { auto& newImage = impl->loadedShapes[shape.get()].images.emplace_back(std::make_unique()); newImage->artificial = true; - newImage->side = info.size; - newImage->artificialData = new char[info.size * info.size * 4]; - newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, info.size, info.size, info.size * 4); + newImage->side = PIXELSIDE; + newImage->artificialData = new char[PIXELSIDE * PIXELSIDE * 4]; + newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, PIXELSIDE, PIXELSIDE, PIXELSIDE * 4); newImage->delay = f->delay; const auto PCAIRO = cairo_create(newImage->cairoSurface); @@ -513,12 +521,12 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { const auto PTN = cairo_pattern_create_for_surface(f->cairoSurface); 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_pattern_set_filter(PTN, shape->resizeAlgo == HC_RESIZE_BILINEAR ? CAIRO_FILTER_GOOD : CAIRO_FILTER_NEAREST); 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_surface_flush(newImage->cairoSurface); @@ -531,12 +539,16 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { 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) { auto& newImage = impl->loadedShapes[shape.get()].images.emplace_back(std::make_unique()); newImage->artificial = true; - newImage->side = info.size; - newImage->artificialData = new char[info.size * info.size * 4]; - newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, info.size, info.size, info.size * 4); + newImage->side = PIXELSIDE; + newImage->artificialData = new char[PIXELSIDE * PIXELSIDE * 4]; + newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, PIXELSIDE, PIXELSIDE, PIXELSIDE * 4); newImage->delay = f->delay; const auto PCAIRO = cairo_create(newImage->cairoSurface); @@ -584,7 +596,7 @@ void CHyprcursorManager::cursorSurfaceStyleDone(const SCursorStyleInfo& info) { const bool isArtificial = e->artificial; // clean artificial rasters made for this - if (isArtificial && e->side == info.size) + if (isArtificial && e->side == std::round(info.size / shape->nominalSize)) return true; // clean invalid non-svg rasters diff --git a/libhyprcursor/internalSharedTypes.hpp b/libhyprcursor/internalSharedTypes.hpp index 3a032aa..3775d53 100644 --- a/libhyprcursor/internalSharedTypes.hpp +++ b/libhyprcursor/internalSharedTypes.hpp @@ -37,7 +37,7 @@ struct SCursorImage { struct SCursorShape { std::string directory; - float hotspotX = 0, hotspotY = 0; + float hotspotX = 0, hotspotY = 0, nominalSize = 1.F; eHyprcursorResizeAlgo resizeAlgo = HC_RESIZE_NEAREST; std::vector images; std::vector overrides; diff --git a/libhyprcursor/meta.cpp b/libhyprcursor/meta.cpp index 23b2c5b..38ea262 100644 --- a/libhyprcursor/meta.cpp +++ b/libhyprcursor/meta.cpp @@ -142,6 +142,7 @@ std::optional CMeta::parseHL() { meta = std::make_unique(rawdata.c_str(), Hyprlang::SConfigOptions{.pathIsStream = !dataPath}); meta->addConfigValue("hotspot_x", 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->registerHandler(::parseDefineSize, "define_size", {.allowFlags = false}); meta->registerHandler(::parseOverride, "define_override", {.allowFlags = false}); @@ -164,6 +165,7 @@ std::optional CMeta::parseTOML() { parsedData.hotspotX = MANIFEST["General"]["hotspot_x"].value_or(0.f); parsedData.hotspotY = MANIFEST["General"]["hotspot_y"].value_or(0.f); + parsedData.hotspotY = MANIFEST["General"]["nominal_size"].value_or(1.f); const std::string OVERRIDES = MANIFEST["General"]["define_override"].value_or(""); const std::string SIZES = MANIFEST["General"]["define_size"].value_or(""); diff --git a/libhyprcursor/meta.hpp b/libhyprcursor/meta.hpp index 837d5ee..9f0cb18 100644 --- a/libhyprcursor/meta.hpp +++ b/libhyprcursor/meta.hpp @@ -20,7 +20,7 @@ class CMeta { struct { std::string resizeAlgo; - float hotspotX = 0, hotspotY = 0; + float hotspotX = 0, hotspotY = 0, nominalSize = 1.F; std::vector overrides; std::vector definedSizes; } parsedData;