core: add nominal size support (#73)

Adds nominal size support
This commit is contained in:
Vaxry 2024-12-21 20:31:12 +01:00 committed by GitHub
parent f388aacd22
commit 84203d8126
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 48 additions and 29 deletions

View file

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

View file

@ -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()) 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
@ -333,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;
@ -392,8 +394,9 @@ SCursorRawShapeDataC* CHyprcursorManager::getRawShapeDataC(const char* shape_) {
data->overridenBy = nullptr; data->overridenBy = nullptr;
data->images = nullptr; data->images = nullptr;
data->len = 0; data->len = 0;
data->hotspotX = 0; data->hotspotX = 0.f;
data->hotspotY = 0; data->hotspotY = 0.F;
data->nominalSize = 1.F;
data->resizeAlgo = eHyprcursorResizeAlgo::HC_RESIZE_NONE; data->resizeAlgo = eHyprcursorResizeAlgo::HC_RESIZE_NONE;
data->type = eHyprcursorDataType::HC_DATA_PNG; data->type = eHyprcursorDataType::HC_DATA_PNG;
@ -418,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;
} }
@ -450,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;
@ -465,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)
@ -477,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;
@ -494,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);
@ -513,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);
@ -531,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);
@ -554,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);
@ -584,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
@ -766,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);
} }

View file

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

View file

@ -142,6 +142,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});
@ -151,9 +152,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::any_cast<Hyprlang::FLOAT>(meta->getConfigValue("nominal_size"));
parsedData.resizeAlgo = std::any_cast<Hyprlang::STRING>(meta->getConfigValue("resize_algorithm"));
return {}; return {};
} }
@ -164,6 +166,7 @@ std::optional<std::string> CMeta::parseTOML() {
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.hotspotY = MANIFEST["General"]["nominal_size"].value_or(1.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("");

View file

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