From 22a419555739bf9d75159f4123bebb969d55233d Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sun, 24 Mar 2024 20:37:31 +0000 Subject: [PATCH] lib: add user-defined logging --- include/hyprcursor/hyprcursor.h | 19 ++++- include/hyprcursor/hyprcursor.hpp | 18 +++++ include/hyprcursor/shared.h | 14 ++++ libhyprcursor/Log.hpp | 43 ++--------- libhyprcursor/hyprcursor.cpp | 117 +++++++++++++++++++++--------- libhyprcursor/hyprcursor_c.cpp | 11 ++- libhyprcursor/internalDefines.hpp | 13 +++- tests/test.c | 7 +- tests/test.cpp | 6 +- 9 files changed, 170 insertions(+), 78 deletions(-) diff --git a/include/hyprcursor/hyprcursor.h b/include/hyprcursor/hyprcursor.h index 7110375..6fb443e 100644 --- a/include/hyprcursor/hyprcursor.h +++ b/include/hyprcursor/hyprcursor.h @@ -43,6 +43,11 @@ struct hyprcursor_cursor_style_info { */ CAPI struct hyprcursor_manager_t* hyprcursor_manager_create(const char* theme_name); +/*! + Same as hyprcursor_manager_create, but with a logger. +*/ +CAPI struct hyprcursor_manager_t* hyprcursor_manager_create_with_logger(const char* theme_name, PHYPRCURSORLOGFUNC fn); + /*! Free a hyprcursor_manager_t* */ @@ -71,7 +76,8 @@ CAPI int hyprcursor_load_theme_style(struct hyprcursor_manager_t* manager, struc Once done with a size, call hyprcursor_style_done() */ -CAPI hyprcursor_cursor_image_data** hyprcursor_get_cursor_image_data(struct hyprcursor_manager_t* manager, const char* shape, struct hyprcursor_cursor_style_info info, int* out_size); +CAPI hyprcursor_cursor_image_data** hyprcursor_get_cursor_image_data(struct hyprcursor_manager_t* manager, const char* shape, struct hyprcursor_cursor_style_info info, + int* out_size); /*! Free a returned hyprcursor_cursor_image_data. @@ -83,4 +89,15 @@ CAPI void hyprcursor_cursor_image_data_free(hyprcursor_cursor_image_data** data, */ CAPI void hyprcursor_style_done(struct hyprcursor_manager_t* manager, struct hyprcursor_cursor_style_info info); +/*! + \since 0.1.5 + + Registers a logging function to a hyprcursor_manager_t* + + PHYPRCURSORLOGFUNC's msg is owned by the caller and will be freed afterwards. + + fn can be null to remove a logger. +*/ +CAPI void hyprcursor_register_logging_function(struct hyprcursor_manager_t* manager, PHYPRCURSORLOGFUNC fn); + #endif \ No newline at end of file diff --git a/include/hyprcursor/hyprcursor.hpp b/include/hyprcursor/hyprcursor.hpp index 7e8fc99..a1b027a 100644 --- a/include/hyprcursor/hyprcursor.hpp +++ b/include/hyprcursor/hyprcursor.hpp @@ -43,6 +43,10 @@ namespace Hyprcursor { class CHyprcursorManager { public: CHyprcursorManager(const char* themeName); + /*! + \since 0.1.5 + */ + CHyprcursorManager(const char* themeName, PHYPRCURSORLOGFUNC fn); ~CHyprcursorManager(); /*! @@ -99,9 +103,23 @@ namespace Hyprcursor { */ void cursorSurfaceStyleDone(const SCursorStyleInfo&); + /*! + \since 0.1.5 + + Registers a logging function to this manager. + PHYPRCURSORLOGFUNC's msg is owned by the caller and will be freed afterwards. + fn can be null to unregister a logger. + */ + void registerLoggingFunction(PHYPRCURSORLOGFUNC fn); + private: + void init(const char* themeName_); + CHyprcursorImplementation* impl = nullptr; bool finalizedAndValid = false; + PHYPRCURSORLOGFUNC logFn = nullptr; + + friend class CHyprcursorImplementation; }; } \ No newline at end of file diff --git a/include/hyprcursor/shared.h b/include/hyprcursor/shared.h index d4416ad..6e07a05 100644 --- a/include/hyprcursor/shared.h +++ b/include/hyprcursor/shared.h @@ -16,4 +16,18 @@ struct SCursorImageData { typedef struct SCursorImageData hyprcursor_cursor_image_data; +enum eHyprcursorLogLevel { + HC_LOG_NONE = 0, + HC_LOG_TRACE, + HC_LOG_INFO, + HC_LOG_WARN, + HC_LOG_ERR, + HC_LOG_CRITICAL, +}; + +/* + msg is owned by the caller and will be freed afterwards. +*/ +typedef void (*PHYPRCURSORLOGFUNC)(enum eHyprcursorLogLevel level, char* msg); + #endif diff --git a/libhyprcursor/Log.hpp b/libhyprcursor/Log.hpp index 75ad948..361c82c 100644 --- a/libhyprcursor/Log.hpp +++ b/libhyprcursor/Log.hpp @@ -1,53 +1,22 @@ #pragma once -enum eLogLevel { - TRACE = 0, - INFO, - LOG, - WARN, - ERR, - CRIT, - NONE -}; - #include #include #include +#include + namespace Debug { inline bool quiet = false; inline bool verbose = false; template - void log(eLogLevel level, const std::string& fmt, Args&&... args) { - -#ifndef HYPRLAND_DEBUG - // don't log in release - return; -#endif - - if (!verbose && level == TRACE) + void log(eHyprcursorLogLevel level, PHYPRCURSORLOGFUNC fn, const std::string& fmt, Args&&... args) { + if (!fn) return; - if (quiet) - return; + const std::string LOG = std::vformat(fmt, std::make_format_args(args...)); - 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"; + fn(level, (char*)LOG.c_str()); } }; \ No newline at end of file diff --git a/libhyprcursor/hyprcursor.cpp b/libhyprcursor/hyprcursor.cpp index d0f8a22..d1b992f 100644 --- a/libhyprcursor/hyprcursor.cpp +++ b/libhyprcursor/hyprcursor.cpp @@ -18,10 +18,12 @@ constexpr const std::array systemThemeDirs = {"/usr/share/icons" constexpr const std::array userThemeDirs = {"/.local/share/icons", "/.icons"}; // -static std::string themeNameFromEnv() { +static std::string themeNameFromEnv(PHYPRCURSORLOGFUNC logfn) { const auto ENV = getenv("HYPRCURSOR_THEME"); - if (!ENV) + if (!ENV) { + Debug::log(HC_LOG_INFO, logfn, "themeNameFromEnv: env unset"); return ""; + } return std::string{ENV}; } @@ -36,7 +38,7 @@ static bool themeAccessible(const std::string& path) { return true; } -static std::string getFirstTheme() { +static std::string getFirstTheme(PHYPRCURSORLOGFUNC logfn) { // try user directories first const auto HOMEENV = getenv("HOME"); @@ -57,8 +59,10 @@ static std::string getFirstTheme() { const auto MANIFESTPATH = themeDir.path().string() + "/manifest.hl"; - if (std::filesystem::exists(MANIFESTPATH)) + if (std::filesystem::exists(MANIFESTPATH)) { + Debug::log(HC_LOG_INFO, logfn, "getFirstTheme: found {}", themeDir.path().string()); return themeDir.path().stem().string(); + } } } @@ -74,15 +78,17 @@ static std::string getFirstTheme() { const auto MANIFESTPATH = themeDir.path().string() + "/manifest.hl"; - if (std::filesystem::exists(MANIFESTPATH)) + if (std::filesystem::exists(MANIFESTPATH)) { + Debug::log(HC_LOG_INFO, logfn, "getFirstTheme: found {}", themeDir.path().string()); return themeDir.path().stem().string(); + } } } return ""; } -static std::string getFullPathForThemeName(const std::string& name) { +static std::string getFullPathForThemeName(const std::string& name, PHYPRCURSORLOGFUNC logfn) { const auto HOMEENV = getenv("HOME"); if (!HOMEENV) return ""; @@ -102,8 +108,10 @@ static std::string getFullPathForThemeName(const std::string& name) { const auto MANIFESTPATH = themeDir.path().string() + "/manifest.hl"; if (name.empty()) { - if (std::filesystem::exists(MANIFESTPATH)) + if (std::filesystem::exists(MANIFESTPATH)) { + Debug::log(HC_LOG_INFO, logfn, "getFullPathForThemeName: found {}", themeDir.path().string()); return std::filesystem::canonical(themeDir.path()).string(); + } continue; } @@ -123,6 +131,7 @@ static std::string getFullPathForThemeName(const std::string& name) { if (NAME != name) continue; + Debug::log(HC_LOG_INFO, logfn, "getFullPathForThemeName: found {}", themeDir.path().string()); return std::filesystem::canonical(themeDir.path()).string(); } } @@ -145,49 +154,65 @@ static std::string getFullPathForThemeName(const std::string& name) { const auto MANIFESTPATH = themeDir.path().string() + "/manifest.hl"; - if (std::filesystem::exists(MANIFESTPATH)) + if (std::filesystem::exists(MANIFESTPATH)) { + Debug::log(HC_LOG_INFO, logfn, "getFullPathForThemeName: found {}", themeDir.path().string()); return std::filesystem::canonical(themeDir.path()).string(); + } } } - if (!name.empty()) // try without name - return getFullPathForThemeName(""); + if (!name.empty()) { // try without name + Debug::log(HC_LOG_INFO, logfn, "getFullPathForThemeName: failed, trying without name of {}", name); + return getFullPathForThemeName("", logfn); + } return ""; } CHyprcursorManager::CHyprcursorManager(const char* themeName_) { + init(themeName_); +} + +CHyprcursorManager::CHyprcursorManager(const char* themeName_, PHYPRCURSORLOGFUNC fn) { + logFn = fn; + init(themeName_); +} + +void CHyprcursorManager::init(const char* themeName_) { std::string themeName = themeName_ ? themeName_ : ""; if (themeName.empty()) { // try reading from env - themeName = themeNameFromEnv(); + Debug::log(HC_LOG_INFO, logFn, "CHyprcursorManager: attempting to find theme from env"); + themeName = themeNameFromEnv(logFn); } if (themeName.empty()) { // try finding first, in the hierarchy - themeName = getFirstTheme(); + Debug::log(HC_LOG_INFO, logFn, "CHyprcursorManager: attempting to find any theme"); + themeName = getFirstTheme(logFn); } if (themeName.empty()) { // holy shit we're done + Debug::log(HC_LOG_INFO, logFn, "CHyprcursorManager: no themes matched"); return; } // initialize theme - impl = new CHyprcursorImplementation; + impl = new CHyprcursorImplementation(this, logFn); impl->themeName = themeName; - impl->themeFullDir = getFullPathForThemeName(themeName); + impl->themeFullDir = getFullPathForThemeName(themeName, logFn); if (impl->themeFullDir.empty()) return; - Debug::log(LOG, "Found theme {} at {}\n", impl->themeName, impl->themeFullDir); + Debug::log(HC_LOG_INFO, logFn, "Found theme {} at {}\n", impl->themeName, impl->themeFullDir); const auto LOADSTATUS = impl->loadTheme(); if (LOADSTATUS.has_value()) { - Debug::log(ERR, "Theme failed to load with {}\n", LOADSTATUS.value()); + Debug::log(HC_LOG_ERR, logFn, "Theme failed to load with {}\n", LOADSTATUS.value()); return; } @@ -204,6 +229,11 @@ bool CHyprcursorManager::valid() { } SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shape_, const SCursorStyleInfo& info) { + if (!shape_) { + Debug::log(HC_LOG_ERR, logFn, "getShapesC: shape of nullptr is invalid"); + return nullptr; + } + std::string REQUESTEDSHAPE = shape_; std::vector resultingImages; @@ -232,7 +262,7 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap // if we get here, means loadThemeStyle wasn't called most likely. If resize algo is specified, this is an error. if (shape->resizeAlgo != RESIZE_NONE) { - Debug::log(ERR, "getSurfaceFor didn't match a size?"); + Debug::log(HC_LOG_ERR, logFn, "getSurfaceFor didn't match a size?"); return nullptr; } @@ -246,7 +276,7 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap } if (leader == 13371337) { // ??? - Debug::log(ERR, "getSurfaceFor didn't match any nearest size?"); + Debug::log(HC_LOG_ERR, logFn, "getSurfaceFor didn't match any nearest size?"); return nullptr; } @@ -263,7 +293,7 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap if (foundAny) break; - Debug::log(ERR, "getSurfaceFor didn't match any nearest size (2)?"); + Debug::log(HC_LOG_ERR, logFn, "getSurfaceFor didn't match any nearest size (2)?"); return nullptr; } @@ -280,13 +310,20 @@ SCursorImageData** CHyprcursorManager::getShapesC(int& outSize, const char* shap outSize = resultingImages.size(); + Debug::log(HC_LOG_INFO, logFn, "getShapesC: found {} images for {}", outSize, shape_); + return data; } bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { + Debug::log(HC_LOG_INFO, logFn, "loadThemeStyle: loading for size {}", info.size); + for (auto& shape : impl->theme.shapes) { - if (shape->resizeAlgo == RESIZE_NONE && shape->shapeType != SHAPE_SVG) - continue; // don't resample NONE style cursors + if (shape->resizeAlgo == RESIZE_NONE && shape->shapeType != SHAPE_SVG) { + // don't resample NONE style cursors + Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: ignoring {}", shape->directory); + continue; + } bool sizeFound = false; @@ -327,12 +364,14 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { } if (!leader) { - Debug::log(ERR, "Resampling failed to find a candidate???"); + Debug::log(HC_LOG_ERR, logFn, "Resampling failed to find a candidate???"); return false; } const auto FRAMES = impl->getFramesFor(shape.get(), leader->side); + Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: png shape {} has {} frames", shape->directory, FRAMES.size()); + for (auto& f : FRAMES) { auto& newImage = impl->loadedShapes[shape.get()].images.emplace_back(std::make_unique()); newImage->artificial = true; @@ -368,6 +407,8 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { } else if (shape->shapeType == SHAPE_SVG) { const auto FRAMES = impl->getFramesFor(shape.get(), 0); + Debug::log(HC_LOG_TRACE, logFn, "loadThemeStyle: svg shape {} has {} frames", shape->directory, FRAMES.size()); + for (auto& f : FRAMES) { auto& newImage = impl->loadedShapes[shape.get()].images.emplace_back(std::make_unique()); newImage->artificial = true; @@ -387,14 +428,14 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { RsvgHandle* handle = rsvg_handle_new_from_data((unsigned char*)f->data, f->dataLen, &error); if (!handle) { - Debug::log(ERR, "Failed reading svg: {}", error->message); + Debug::log(HC_LOG_ERR, logFn, "Failed reading svg: {}", error->message); return false; } RsvgRectangle rect = {0, 0, (double)info.size, (double)info.size}; if (!rsvg_handle_render_document(handle, PCAIRO, &rect, &error)) { - Debug::log(ERR, "Failed rendering svg: {}", error->message); + Debug::log(HC_LOG_ERR, logFn, "Failed rendering svg: {}", error->message); return false; } @@ -403,7 +444,7 @@ bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) { cairo_destroy(PCAIRO); } } else { - Debug::log(ERR, "Invalid shapetype in loadThemeStyle"); + Debug::log(HC_LOG_ERR, logFn, "Invalid shapetype in loadThemeStyle"); return false; } } @@ -433,6 +474,10 @@ void CHyprcursorManager::cursorSurfaceStyleDone(const SCursorStyleInfo& info) { } } +void CHyprcursorManager::registerLoggingFunction(PHYPRCURSORLOGFUNC fn) { + logFn = fn; +} + /* Implementation @@ -535,7 +580,6 @@ static cairo_status_t readPNG(void* data, unsigned char* output, unsigned int le if (DATA->readNeedle >= DATA->dataLen) { delete[] (char*)DATA->data; DATA->data = nullptr; - Debug::log(TRACE, "cairo: png read, freeing mem"); } return CAIRO_STATUS_SUCCESS; @@ -563,7 +607,7 @@ std::optional CHyprcursorImplementation::loadTheme() { manifest->commence(); manifest->parse(); } catch (const char* err) { - Debug::log(ERR, "Failed parsing manifest due to {}", err); + Debug::log(HC_LOG_ERR, logFn, "Failed parsing manifest due to {}", err); return std::string{"failed: "} + err; } @@ -574,8 +618,10 @@ std::optional CHyprcursorImplementation::loadTheme() { return "loadTheme: cursors_directory missing or empty"; for (auto& cursor : std::filesystem::directory_iterator(CURSORDIR)) { - if (!cursor.is_regular_file()) + if (!cursor.is_regular_file()) { + Debug::log(HC_LOG_TRACE, logFn, "loadTheme: skipping {}", cursor.path().string()); continue; + } auto& SHAPE = theme.shapes.emplace_back(std::make_unique()); auto& LOADEDSHAPE = loadedShapes[SHAPE.get()]; @@ -612,7 +658,10 @@ std::optional CHyprcursorImplementation::loadTheme() { meta->registerHandler(::parseOverride, "define_override", {.allowFlags = false}); meta->commence(); meta->parse(); - } catch (const char* err) { return "failed parsing meta: " + std::string{err}; } + } catch (const char* err) { + delete[] buffer; + return "failed parsing meta: " + std::string{err}; + } delete[] buffer; @@ -623,7 +672,7 @@ std::optional CHyprcursorImplementation::loadTheme() { else if (i.filename.ends_with(".png")) SHAPE->shapeType = SHAPE_PNG; else { - std::cout << "WARNING: image " << i.filename << " has no known extension, assuming png.\n"; + Debug::log(HC_LOG_WARN, logFn, "WARNING: image {} has no known extension, assuming png.", i.filename); SHAPE->shapeType = SHAPE_PNG; } } else { @@ -634,7 +683,7 @@ std::optional CHyprcursorImplementation::loadTheme() { } // load image - Debug::log(TRACE, "Loading {} for shape {}", i.filename, cursor.path().stem().string()); + Debug::log(HC_LOG_TRACE, logFn, "Loading {} for shape {}", i.filename, cursor.path().stem().string()); auto* IMAGE = LOADEDSHAPE.images.emplace_back(std::make_unique()).get(); IMAGE->side = SHAPE->shapeType == SHAPE_SVG ? 0 : i.size; IMAGE->delay = i.delay; @@ -651,7 +700,7 @@ std::optional CHyprcursorImplementation::loadTheme() { zip_fclose(image_file); - Debug::log(TRACE, "Cairo: set up surface read"); + Debug::log(HC_LOG_TRACE, logFn, "Cairo: set up surface read"); if (SHAPE->shapeType == SHAPE_PNG) { @@ -663,7 +712,7 @@ std::optional CHyprcursorImplementation::loadTheme() { return "Failed reading cairoSurface, status " + std::to_string((int)STATUS); } } else { - Debug::log(LOG, "Skipping cairo load for a svg surface"); + Debug::log(HC_LOG_TRACE, logFn, "Skipping cairo load for a svg surface"); } } @@ -695,4 +744,4 @@ std::vector CHyprcursorImplementation::getFramesFor(SCursor } return frames; -} \ No newline at end of file +} diff --git a/libhyprcursor/hyprcursor_c.cpp b/libhyprcursor/hyprcursor_c.cpp index 2277bb0..b0c5499 100644 --- a/libhyprcursor/hyprcursor_c.cpp +++ b/libhyprcursor/hyprcursor_c.cpp @@ -7,6 +7,10 @@ hyprcursor_manager_t* hyprcursor_manager_create(const char* theme_name) { return (hyprcursor_manager_t*)new CHyprcursorManager(theme_name); } +hyprcursor_manager_t* hyprcursor_manager_create_with_logger(const char* theme_name, PHYPRCURSORLOGFUNC fn) { + return (hyprcursor_manager_t*)new CHyprcursorManager(theme_name, fn); +} + void hyprcursor_manager_free(hyprcursor_manager_t* manager) { delete (CHyprcursorManager*)manager; } @@ -46,4 +50,9 @@ void hyprcursor_style_done(hyprcursor_manager_t* manager, hyprcursor_cursor_styl SCursorStyleInfo info; info.size = info_.size; return MGR->cursorSurfaceStyleDone(info); -} \ No newline at end of file +} + +void hyprcursor_register_logging_function(struct hyprcursor_manager_t* manager, PHYPRCURSORLOGFUNC fn) { + const auto MGR = (CHyprcursorManager*)manager; + MGR->registerLoggingFunction(fn); +} diff --git a/libhyprcursor/internalDefines.hpp b/libhyprcursor/internalDefines.hpp index f6c1c14..18fe5de 100644 --- a/libhyprcursor/internalDefines.hpp +++ b/libhyprcursor/internalDefines.hpp @@ -37,10 +37,17 @@ struct SLoadedCursorShape { class CHyprcursorImplementation { public: - std::string themeName; - std::string themeFullDir; + CHyprcursorImplementation(Hyprcursor::CHyprcursorManager* mgr, PHYPRCURSORLOGFUNC fn) : owner(mgr), logFn(fn) { + ; + } - SCursorTheme theme; + Hyprcursor::CHyprcursorManager* owner = nullptr; + PHYPRCURSORLOGFUNC logFn = nullptr; + + std::string themeName; + std::string themeFullDir; + + SCursorTheme theme; // std::unordered_map loadedShapes; diff --git a/tests/test.c b/tests/test.c index d5fa80a..b6be478 100644 --- a/tests/test.c +++ b/tests/test.c @@ -2,13 +2,18 @@ #include #include +void logFunction(enum eHyprcursorLogLevel level, char* message) { + printf("[hc] %s\n", message); +} + int main(int argc, char** argv) { - struct hyprcursor_manager_t* mgr = hyprcursor_manager_create(NULL); + struct hyprcursor_manager_t* mgr = hyprcursor_manager_create_with_logger(NULL, logFunction); if (!mgr) { printf("mgr null\n"); return 1; } + if (!hyprcursor_manager_valid(mgr)) { printf("mgr is invalid\n"); return 1; diff --git a/tests/test.cpp b/tests/test.cpp index 95377d7..b404674 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1,8 +1,12 @@ #include #include +void logFunction(enum eHyprcursorLogLevel level, char* message) { + std::cout << "[hc] " << message << "\n"; +} + int main(int argc, char** argv) { - Hyprcursor::CHyprcursorManager mgr(nullptr); + Hyprcursor::CHyprcursorManager mgr(nullptr, logFunction); if (!mgr.valid()) { std::cout << "mgr is invalid\n";