diff --git a/include/cairo/font.hpp b/include/cairo/font.hpp index 488a0875..5b396480 100644 --- a/include/cairo/font.hpp +++ b/include/cairo/font.hpp @@ -15,327 +15,332 @@ POLYBAR_NS namespace cairo { - /** - * @brief Global pointer to the Freetype library handler - */ - static FT_Library g_ftlib; +/** + * @brief Global pointer to the Freetype library handler + */ +static FT_Library g_ftlib; + +/** + * @brief Abstract font face + */ +class font { + public: + explicit font(cairo_t* cairo, double offset) : m_cairo(cairo), m_offset(offset) {} + virtual ~font(){}; + + virtual string name() const = 0; + virtual string file() const = 0; + virtual double offset() const = 0; + virtual double size(double dpi) const = 0; + + virtual cairo_font_extents_t extents() = 0; + + virtual void use() { + cairo_set_font_face(m_cairo, cairo_font_face_reference(m_font_face)); + } + + virtual size_t match(utils::unicode_character& character) = 0; + virtual size_t match(utils::unicode_charlist& charlist) = 0; + virtual size_t render(const string& text, double x = 0.0, double y = 0.0) = 0; + virtual void textwidth(const string& text, cairo_text_extents_t* extents) = 0; + + protected: + cairo_t* m_cairo; + cairo_font_face_t* m_font_face{nullptr}; + cairo_font_extents_t m_extents{}; + double m_offset{0.0}; +}; + +/** + * @brief Font based on fontconfig/freetype + */ +class font_fc : public font { + public: + explicit font_fc(cairo_t* cairo, FcPattern* pattern, double offset, double dpi_x, double dpi_y) + : font(cairo, offset), m_pattern(pattern) { + cairo_matrix_t fm; + cairo_matrix_t ctm; + cairo_matrix_init_scale(&fm, size(dpi_x), size(dpi_y)); + cairo_get_matrix(m_cairo, &ctm); + + auto fontface = cairo_ft_font_face_create_for_pattern(m_pattern); + auto opts = cairo_font_options_create(); + m_scaled = cairo_scaled_font_create(fontface, &fm, &ctm, opts); + cairo_font_options_destroy(opts); + cairo_font_face_destroy(fontface); + + auto status = cairo_scaled_font_status(m_scaled); + if (status != CAIRO_STATUS_SUCCESS) { + throw application_error(sstream() << "cairo_scaled_font_create(): " << cairo_status_to_string(status)); + } + + auto lock = make_unique(m_scaled); + auto face = static_cast(*lock); + + if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) == FT_Err_Ok) { + return; + } else if (FT_Select_Charmap(face, FT_ENCODING_BIG5) == FT_Err_Ok) { + return; + } else if (FT_Select_Charmap(face, FT_ENCODING_SJIS) == FT_Err_Ok) { + return; + } + + lock.reset(); + } + + ~font_fc() override { + if (m_scaled != nullptr) { + cairo_scaled_font_destroy(m_scaled); + } + if (m_pattern != nullptr) { + FcPatternDestroy(m_pattern); + } + } + + cairo_font_extents_t extents() override { + cairo_scaled_font_extents(m_scaled, &m_extents); + return m_extents; + } + + string name() const override { + return property("family"); + } + + string file() const override { + return property("file"); + } + + double offset() const override { + return m_offset; + } /** - * @brief Abstract font face + * Calculates the font size in pixels for the given dpi + * + * We use the two font properties size and pixelsize. size is in points and + * needs to be scaled with the given dpi. pixelsize is not scaled. + * + * If both size properties are 0, we fall back to a default value of 10 + * points for scalable fonts or 10 pixel for non-scalable ones. This should + * only happen if both properties are purposefully set to 0 + * + * For scalable fonts we try to use the size property scaled according to + * the dpi. + * For non-scalable fonts we try to use the pixelsize property as-is */ - class font { - public: - explicit font(cairo_t* cairo, double offset) : m_cairo(cairo), m_offset(offset) {} - virtual ~font(){}; + double size(double dpi) const override { + bool scalable; + double fc_pixelsize = 0, fc_size = 0; - virtual string name() const = 0; - virtual string file() const = 0; - virtual double offset() const = 0; - virtual double size(double dpi) const = 0; + property(FC_SCALABLE, &scalable); - virtual cairo_font_extents_t extents() = 0; + // Size in points + property(FC_SIZE, &fc_size); - virtual void use() { - cairo_set_font_face(m_cairo, cairo_font_face_reference(m_font_face)); - } + // Size in pixels + property(FC_PIXEL_SIZE, &fc_pixelsize); - virtual size_t match(utils::unicode_character& character) = 0; - virtual size_t match(utils::unicode_charlist& charlist) = 0; - virtual size_t render(const string& text, double x = 0.0, double y = 0.0) = 0; - virtual void textwidth(const string& text, cairo_text_extents_t* extents) = 0; + // Fall back to a default value if the size is 0 + double pixelsize = fc_pixelsize == 0 ? 10 : fc_pixelsize; + double size = fc_size == 0 ? 10 : fc_size; - protected: - cairo_t* m_cairo; - cairo_font_face_t* m_font_face{nullptr}; - cairo_font_extents_t m_extents{}; - double m_offset{0.0}; - }; + // Font size in pixels if we use the pixelsize property + int px_pixelsize = pixelsize + 0.5; - /** - * @brief Font based on fontconfig/freetype - */ - class font_fc : public font { - public: - explicit font_fc(cairo_t* cairo, FcPattern* pattern, double offset, double dpi_x, double dpi_y) - : font(cairo, offset), m_pattern(pattern) { - cairo_matrix_t fm; - cairo_matrix_t ctm; - cairo_matrix_init_scale(&fm, size(dpi_x), size(dpi_y)); - cairo_get_matrix(m_cairo, &ctm); - - auto fontface = cairo_ft_font_face_create_for_pattern(m_pattern); - auto opts = cairo_font_options_create(); - m_scaled = cairo_scaled_font_create(fontface, &fm, &ctm, opts); - cairo_font_options_destroy(opts); - cairo_font_face_destroy(fontface); - - auto status = cairo_scaled_font_status(m_scaled); - if (status != CAIRO_STATUS_SUCCESS) { - throw application_error(sstream() << "cairo_scaled_font_create(): " << cairo_status_to_string(status)); - } - - auto lock = make_unique(m_scaled); - auto face = static_cast(*lock); - - if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) == FT_Err_Ok) { - return; - } else if (FT_Select_Charmap(face, FT_ENCODING_BIG5) == FT_Err_Ok) { - return; - } else if (FT_Select_Charmap(face, FT_ENCODING_SJIS) == FT_Err_Ok) { - return; - } - - lock.reset(); - } - - ~font_fc() override { - if (m_scaled != nullptr) { - cairo_scaled_font_destroy(m_scaled); - } - if (m_pattern != nullptr) { - FcPatternDestroy(m_pattern); - } - } - - cairo_font_extents_t extents() override { - cairo_scaled_font_extents(m_scaled, &m_extents); - return m_extents; - } - - string name() const override { - return property("family"); - } - - string file() const override { - return property("file"); - } - - double offset() const override { - return m_offset; - } - - /** - * Calculates the font size in pixels for the given dpi - * - * We use the two font properties size and pixelsize. size is in points and - * needs to be scaled with the given dpi. pixelsize is not scaled. - * - * If both size properties are 0, we fall back to a default value of 10 - * points for scalable fonts or 10 pixel for non-scalable ones. This should - * only happen if both properties are purposefully set to 0 - * - * For scalable fonts we try to use the size property scaled according to - * the dpi. - * For non-scalable fonts we try to use the pixelsize property as-is + /* + * Font size in pixels if we use the size property. Since the size + * specifies the font size in points, this is converted to pixels + * according to the dpi given. + * One point is 1/72 inches, thus this gives us the number of 'dots' + * (or pixels) for this font */ - double size(double dpi) const override { - bool scalable; - double fc_pixelsize = 0, fc_size = 0; + int px_size = size / 72.0 * dpi + 0.5; - property(FC_SCALABLE, &scalable); - - // Size in points - property(FC_SIZE, &fc_size); - - // Size in pixels - property(FC_PIXEL_SIZE, &fc_pixelsize); - - // Fall back to a default value if the size is 0 - double pixelsize = fc_pixelsize == 0 ? 10 : fc_pixelsize; - double size = fc_size == 0 ? 10 : fc_size; - - // Font size in pixels if we use the pixelsize property - int px_pixelsize = pixelsize + 0.5; + if (fc_size == 0 && fc_pixelsize == 0) { + return scalable ? px_size : px_pixelsize; + } + if (scalable) { /* - * Font size in pixels if we use the size property. Since the size - * specifies the font size in points, this is converted to pixels - * according to the dpi given. - * One point is 1/72 inches, thus this gives us the number of 'dots' - * (or pixels) for this font + * Use the point size if it's not 0. The pixelsize is only used if the + * size property is 0 and pixelsize is not */ - int px_size = size / 72.0 * dpi + 0.5; - - if (fc_size == 0 && fc_pixelsize == 0) { - return scalable ? px_size : px_pixelsize; - } - - if (scalable) { - /* - * Use the point size if it's not 0. The pixelsize is only used if the - * size property is 0 and pixelsize is not - */ - if (fc_size != 0) { - return px_size; - } else { - return px_pixelsize; - } + if (fc_size != 0) { + return px_size; } else { - /* - * Non-scalable fonts do it the other way around, here the size - * property is only used if pixelsize is 0 and size is not - */ - if (fc_pixelsize != 0) { - return px_pixelsize; - } else { - return px_size; - } + return px_pixelsize; + } + } else { + /* + * Non-scalable fonts do it the other way around, here the size + * property is only used if pixelsize is 0 and size is not + */ + if (fc_pixelsize != 0) { + return px_pixelsize; + } else { + return px_size; + } + } + } + + void use() override { + cairo_set_scaled_font(m_cairo, m_scaled); + } + + size_t match(utils::unicode_character& character) override { + auto lock = make_unique(m_scaled); + auto face = static_cast(*lock); + return FT_Get_Char_Index(face, character.codepoint) ? 1 : 0; + } + + size_t match(utils::unicode_charlist& charlist) override { + auto lock = make_unique(m_scaled); + auto face = static_cast(*lock); + size_t available_chars = 0; + for (auto&& c : charlist) { + if (FT_Get_Char_Index(face, c.codepoint)) { + available_chars++; + } else { + break; } } - void use() override { - cairo_set_scaled_font(m_cairo, m_scaled); - } + return available_chars; + } - size_t match(utils::unicode_character& character) override { - auto lock = make_unique(m_scaled); - auto face = static_cast(*lock); - return FT_Get_Char_Index(face, character.codepoint) ? 1 : 0; - } + size_t render(const string& text, double x = 0.0, double y = 0.0) override { + cairo_glyph_t* glyphs{nullptr}; + cairo_text_cluster_t* clusters{nullptr}; + cairo_text_cluster_flags_t cf{}; + int nglyphs = 0; + int nclusters = 0; - size_t match(utils::unicode_charlist& charlist) override { - auto lock = make_unique(m_scaled); - auto face = static_cast(*lock); - size_t available_chars = 0; - for (auto&& c : charlist) { - if (FT_Get_Char_Index(face, c.codepoint)) { - available_chars++; - } else { - break; - } + string utf8 = string(text); + auto status = cairo_scaled_font_text_to_glyphs( + m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf); + + if (status != CAIRO_STATUS_SUCCESS) { + logger::make().notice("ERROR %d", status); + for (char& c : utf8) { + logger::make().notice("0x%02x", c); } - - return available_chars; + throw application_error(sstream() << "cairo_scaled_font_text_to_glyphs() " << cairo_status_to_string(status)); } - size_t render(const string& text, double x = 0.0, double y = 0.0) override { - cairo_glyph_t* glyphs{nullptr}; - cairo_text_cluster_t* clusters{nullptr}; - cairo_text_cluster_flags_t cf{}; - int nglyphs = 0, nclusters = 0; + size_t bytes = 0; + for (int g = 0; g < nglyphs; g++) { + if (glyphs[g].index) { + bytes += clusters[g].num_bytes; + } else { + break; + } + } - string utf8 = string(text); + if (bytes && bytes < text.size()) { + cairo_glyph_free(glyphs); + cairo_text_cluster_free(clusters); + + utf8 = text.substr(0, bytes); auto status = cairo_scaled_font_text_to_glyphs( m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf); if (status != CAIRO_STATUS_SUCCESS) { - throw application_error(sstream() << "cairo_scaled_font_text_to_glyphs()" << cairo_status_to_string(status)); - } - - size_t bytes = 0; - for (int g = 0; g < nglyphs; g++) { - if (glyphs[g].index) { - bytes += clusters[g].num_bytes; - } else { - break; - } - } - - if (bytes && bytes < text.size()) { - cairo_glyph_free(glyphs); - cairo_text_cluster_free(clusters); - - utf8 = text.substr(0, bytes); - auto status = cairo_scaled_font_text_to_glyphs( - m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf); - - if (status != CAIRO_STATUS_SUCCESS) { - throw application_error(sstream() << "cairo_scaled_font_text_to_glyphs()" << cairo_status_to_string(status)); - } - } - - if (bytes) { - // auto lock = make_unique(cairo_surface_get_device(cairo_get_target(m_cairo))); - // if (lock.get()) { - // cairo_glyph_path(m_cairo, glyphs, nglyphs); - // } - - cairo_text_extents_t extents{}; - cairo_scaled_font_glyph_extents(m_scaled, glyphs, nglyphs, &extents); - cairo_show_text_glyphs(m_cairo, utf8.c_str(), utf8.size(), glyphs, nglyphs, clusters, nclusters, cf); - cairo_fill(m_cairo); - cairo_move_to(m_cairo, x + extents.x_advance, 0.0); - } - - cairo_glyph_free(glyphs); - cairo_text_cluster_free(clusters); - - return bytes; - } - - void textwidth(const string& text, cairo_text_extents_t* extents) override { - cairo_scaled_font_text_extents(m_scaled, text.c_str(), extents); - } - - protected: - string property(string&& property) const { - FcChar8* file; - if (FcPatternGetString(m_pattern, property.c_str(), 0, &file) == FcResultMatch) { - return string(reinterpret_cast(file)); - } else { - return ""; + throw application_error(sstream() << "cairo_scaled_font_text_to_glyphs() " << cairo_status_to_string(status)); } } - void property(string&& property, bool* dst) const { - FcBool b; - FcPatternGetBool(m_pattern, property.c_str(), 0, &b); - *dst = b; + if (bytes) { + // auto lock = make_unique(cairo_surface_get_device(cairo_get_target(m_cairo))); + // if (lock.get()) { + // cairo_glyph_path(m_cairo, glyphs, nglyphs); + // } + + cairo_text_extents_t extents{}; + cairo_scaled_font_glyph_extents(m_scaled, glyphs, nglyphs, &extents); + cairo_show_text_glyphs(m_cairo, utf8.c_str(), utf8.size(), glyphs, nglyphs, clusters, nclusters, cf); + cairo_fill(m_cairo); + cairo_move_to(m_cairo, x + extents.x_advance, 0.0); } - void property(string&& property, double* dst) const { - FcPatternGetDouble(m_pattern, property.c_str(), 0, dst); + cairo_glyph_free(glyphs); + cairo_text_cluster_free(clusters); + + return bytes; + } + + void textwidth(const string& text, cairo_text_extents_t* extents) override { + cairo_scaled_font_text_extents(m_scaled, text.c_str(), extents); + } + + protected: + string property(string&& property) const { + FcChar8* file; + if (FcPatternGetString(m_pattern, property.c_str(), 0, &file) == FcResultMatch) { + return string(reinterpret_cast(file)); + } else { + return ""; } + } - void property(string&& property, int* dst) const { - FcPatternGetInteger(m_pattern, property.c_str(), 0, dst); - } + void property(string&& property, bool* dst) const { + FcBool b; + FcPatternGetBool(m_pattern, property.c_str(), 0, &b); + *dst = b; + } - private: - cairo_scaled_font_t* m_scaled{nullptr}; - FcPattern* m_pattern{nullptr}; - }; + void property(string&& property, double* dst) const { + FcPatternGetDouble(m_pattern, property.c_str(), 0, dst); + } - /** - * Match and create font from given fontconfig pattern - */ - inline decltype(auto) make_font(cairo_t* cairo, string&& fontname, double offset, double dpi_x, double dpi_y) { - static bool fc_init{false}; - if (!fc_init && !(fc_init = FcInit())) { - throw application_error("Could not load fontconfig"); - } else if (FT_Init_FreeType(&g_ftlib) != FT_Err_Ok) { - throw application_error("Could not load FreeType"); - } + void property(string&& property, int* dst) const { + FcPatternGetInteger(m_pattern, property.c_str(), 0, dst); + } - static scope_util::on_exit fc_cleanup([] { - FT_Done_FreeType(g_ftlib); - FcFini(); - }); + private: + cairo_scaled_font_t* m_scaled{nullptr}; + FcPattern* m_pattern{nullptr}; +}; - auto pattern = FcNameParse((FcChar8*)fontname.c_str()); +/** + * Match and create font from given fontconfig pattern + */ +inline decltype(auto) make_font(cairo_t* cairo, string&& fontname, double offset, double dpi_x, double dpi_y) { + static bool fc_init{false}; + if (!fc_init && !(fc_init = FcInit())) { + throw application_error("Could not load fontconfig"); + } else if (FT_Init_FreeType(&g_ftlib) != FT_Err_Ok) { + throw application_error("Could not load FreeType"); + } - if (!pattern) { - logger::make().err("Could not parse font \"%s\"", fontname); - throw application_error("Could not parse font \"" + fontname + "\""); - } + static scope_util::on_exit fc_cleanup([] { + FT_Done_FreeType(g_ftlib); + FcFini(); + }); - FcDefaultSubstitute(pattern); - FcConfigSubstitute(nullptr, pattern, FcMatchPattern); + auto pattern = FcNameParse((FcChar8*)fontname.c_str()); - FcResult result; - FcPattern* match = FcFontMatch(nullptr, pattern, &result); - FcPatternDestroy(pattern); + if (!pattern) { + logger::make().err("Could not parse font \"%s\"", fontname); + throw application_error("Could not parse font \"" + fontname + "\""); + } - if (match == nullptr) { - throw application_error("Could not load font \"" + fontname + "\""); - } + FcDefaultSubstitute(pattern); + FcConfigSubstitute(nullptr, pattern, FcMatchPattern); + + FcResult result; + FcPattern* match = FcFontMatch(nullptr, pattern, &result); + FcPatternDestroy(pattern); + + if (match == nullptr) { + throw application_error("Could not load font \"" + fontname + "\""); + } #ifdef DEBUG_FONTCONFIG - FcPatternPrint(match); + FcPatternPrint(match); #endif - return make_shared(cairo, match, offset, dpi_x, dpi_y); - } + return make_shared(cairo, match, offset, dpi_x, dpi_y); +} } // namespace cairo POLYBAR_NS_END diff --git a/include/cairo/utils.hpp b/include/cairo/utils.hpp index 5db3a145..bd0497c1 100644 --- a/include/cairo/utils.hpp +++ b/include/cairo/utils.hpp @@ -1,6 +1,7 @@ #pragma once #include + #include #include "common.hpp" @@ -8,63 +9,63 @@ POLYBAR_NS namespace cairo { - namespace utils { - /** - * @brief RAII wrapper used acquire cairo_device_t - */ - class device_lock { - public: - explicit device_lock(cairo_device_t* device); - ~device_lock(); - operator bool() const; - operator cairo_device_t*() const; +namespace utils { + /** + * @brief RAII wrapper used acquire cairo_device_t + */ + class device_lock { + public: + explicit device_lock(cairo_device_t* device); + ~device_lock(); + operator bool() const; + operator cairo_device_t*() const; - private: - cairo_device_t* m_device{nullptr}; - }; + private: + cairo_device_t* m_device{nullptr}; + }; - /** - * @brief RAII wrapper used to access the underlying - * FT_Face of a scaled font face - */ - class ft_face_lock { - public: - explicit ft_face_lock(cairo_scaled_font_t* font); - ~ft_face_lock(); - operator FT_Face() const; + /** + * @brief RAII wrapper used to access the underlying + * FT_Face of a scaled font face + */ + class ft_face_lock { + public: + explicit ft_face_lock(cairo_scaled_font_t* font); + ~ft_face_lock(); + operator FT_Face() const; - private: - cairo_scaled_font_t* m_font; - FT_Face m_face; - }; + private: + cairo_scaled_font_t* m_font; + FT_Face m_face; + }; - /** - * @brief Unicode character containing converted codepoint - * and details on where its position in the source string - */ - struct unicode_character { - explicit unicode_character(); - unsigned long codepoint; - int offset; - int length; - }; - using unicode_charlist = std::list; + /** + * @brief Unicode character containing converted codepoint + * and details on where its position in the source string + */ + struct unicode_character { + explicit unicode_character(); + unsigned long codepoint; + int offset; + int length; + }; + using unicode_charlist = std::list; - /** - * @see - */ - cairo_operator_t str2operator(const string& mode, cairo_operator_t fallback); + /** + * @see + */ + cairo_operator_t str2operator(const string& mode, cairo_operator_t fallback); - /** - * @brief Create a UCS-4 codepoint from a utf-8 encoded string - */ - bool utf8_to_ucs4(const unsigned char* src, unicode_charlist& result_list); + /** + * @brief Create a UCS-4 codepoint from a utf-8 encoded string + */ + bool utf8_to_ucs4(const unsigned char* src, unicode_charlist& result_list); - /** - * @brief Convert a UCS-4 codepoint to a utf-8 encoded string - */ - size_t ucs4_to_utf8(char* utf8, unsigned int ucs); - } -} + /** + * @brief Convert a UCS-4 codepoint to a utf-8 encoded string + */ + size_t ucs4_to_utf8(char* utf8, unsigned int ucs); +} // namespace utils +} // namespace cairo POLYBAR_NS_END diff --git a/include/utils/string.hpp b/include/utils/string.hpp index c2ee4ead..68399933 100644 --- a/include/utils/string.hpp +++ b/include/utils/string.hpp @@ -34,53 +34,53 @@ class sstream { }; namespace string_util { - /** - * Hash type - */ - using hash_type = unsigned long; +/** + * Hash type + */ +using hash_type = unsigned long; - bool contains(const string& haystack, const string& needle); - bool contains_ignore_case(const string& haystack, const string& needle); - bool ends_with(const string& haystack, const string& suffix); - string upper(const string& s); - string lower(const string& s); - bool compare(const string& s1, const string& s2); +bool contains(const string& haystack, const string& needle); +bool contains_ignore_case(const string& haystack, const string& needle); +bool ends_with(const string& haystack, const string& suffix); +string upper(const string& s); +string lower(const string& s); +bool compare(const string& s1, const string& s2); - string replace(const string& haystack, const string& needle, const string& replacement, size_t start = 0, - size_t end = string::npos); - string replace_all(const string& haystack, const string& needle, const string& replacement, size_t start = 0, - size_t end = string::npos); +string replace(const string& haystack, const string& needle, const string& replacement, size_t start = 0, + size_t end = string::npos); +string replace_all(const string& haystack, const string& needle, const string& replacement, size_t start = 0, + size_t end = string::npos); - string squeeze(const string& haystack, char needle); +string squeeze(const string& haystack, char needle); - string strip(const string& haystack, char needle); - string strip_trailing_newline(const string& haystack); +string strip(const string& haystack, char needle); +string strip_trailing_newline(const string& haystack); - string ltrim(string value, function pred); - string rtrim(string value, function pred); - string trim(string value, function pred); +string ltrim(string value, function pred); +string rtrim(string value, function pred); +string trim(string value, function pred); - string ltrim(string&& value, const char& needle = ' '); - string rtrim(string&& value, const char& needle = ' '); - string trim(string&& value, const char& needle = ' '); +string ltrim(string&& value, const char& needle = ' '); +string rtrim(string&& value, const char& needle = ' '); +string trim(string&& value, const char& needle = ' '); - size_t char_len(const string& value); - string utf8_truncate(string&& value, size_t len); +size_t char_len(const string& value); +string utf8_truncate(string&& value, size_t len); - string join(const vector& strs, const string& delim); - vector split(const string& s, char delim); - std::vector tokenize(const string& str, char delimiters); +string join(const vector& strs, const string& delim); +vector split(const string& s, char delim); +std::vector tokenize(const string& str, char delimiters); - size_t find_nth(const string& haystack, size_t pos, const string& needle, size_t nth); +size_t find_nth(const string& haystack, size_t pos, const string& needle, size_t nth); - string floating_point(double value, size_t precision, bool fixed = false, const string& locale = ""); - string filesize_mib(unsigned long long kibibytes, size_t precision = 0, const string& locale = ""); - string filesize_gib(unsigned long long kibibytes, size_t precision = 0, const string& locale = ""); - string filesize_gib_mib( - unsigned long long kibibytes, size_t precision_mib = 0, size_t precision_gib = 0, const string& locale = ""); - string filesize(unsigned long long kbytes, size_t precision = 0, bool fixed = false, const string& locale = ""); +string floating_point(double value, size_t precision, bool fixed = false, const string& locale = ""); +string filesize_mib(unsigned long long kibibytes, size_t precision = 0, const string& locale = ""); +string filesize_gib(unsigned long long kibibytes, size_t precision = 0, const string& locale = ""); +string filesize_gib_mib( + unsigned long long kibibytes, size_t precision_mib = 0, size_t precision_gib = 0, const string& locale = ""); +string filesize(unsigned long long kbytes, size_t precision = 0, bool fixed = false, const string& locale = ""); - hash_type hash(const string& src); +hash_type hash(const string& src); } // namespace string_util POLYBAR_NS_END diff --git a/src/cairo/utils.cpp b/src/cairo/utils.cpp index b58e4309..f5649130 100644 --- a/src/cairo/utils.cpp +++ b/src/cairo/utils.cpp @@ -1,176 +1,176 @@ -#include - #include "cairo/utils.hpp" +#include + POLYBAR_NS namespace cairo { - namespace utils { +namespace utils { - // implementation : device_lock {{{ + // implementation : device_lock {{{ - device_lock::device_lock(cairo_device_t* device) { - auto status = cairo_device_acquire(device); - if (status == CAIRO_STATUS_SUCCESS) { - m_device = device; - } - } - device_lock::~device_lock() { - cairo_device_release(m_device); - } - device_lock::operator bool() const { - return m_device != nullptr; - } - device_lock::operator cairo_device_t*() const { - return m_device; - } - - // }}} - // implementation : ft_face_lock {{{ - - ft_face_lock::ft_face_lock(cairo_scaled_font_t* font) : m_font(font) { - m_face = cairo_ft_scaled_font_lock_face(m_font); - } - ft_face_lock::~ft_face_lock() { - cairo_ft_scaled_font_unlock_face(m_font); - } - ft_face_lock::operator FT_Face() const { - return m_face; - } - - // }}} - // implementation : unicode_character {{{ - - unicode_character::unicode_character() : codepoint(0), offset(0), length(0) {} - - // }}} - - /** - * @see - */ - cairo_operator_t str2operator(const string& mode, cairo_operator_t fallback) { - if (mode.empty()) { - return fallback; - } - static std::map modes; - if (modes.empty()) { - modes["clear"s] = CAIRO_OPERATOR_CLEAR; - modes["source"s] = CAIRO_OPERATOR_SOURCE; - modes["over"s] = CAIRO_OPERATOR_OVER; - modes["in"s] = CAIRO_OPERATOR_IN; - modes["out"s] = CAIRO_OPERATOR_OUT; - modes["atop"s] = CAIRO_OPERATOR_ATOP; - modes["dest"s] = CAIRO_OPERATOR_DEST; - modes["dest-over"s] = CAIRO_OPERATOR_DEST_OVER; - modes["dest-in"s] = CAIRO_OPERATOR_DEST_IN; - modes["dest-out"s] = CAIRO_OPERATOR_DEST_OUT; - modes["dest-atop"s] = CAIRO_OPERATOR_DEST_ATOP; - modes["xor"s] = CAIRO_OPERATOR_XOR; - modes["add"s] = CAIRO_OPERATOR_ADD; - modes["saturate"s] = CAIRO_OPERATOR_SATURATE; - modes["multiply"s] = CAIRO_OPERATOR_MULTIPLY; - modes["screen"s] = CAIRO_OPERATOR_SCREEN; - modes["overlay"s] = CAIRO_OPERATOR_OVERLAY; - modes["darken"s] = CAIRO_OPERATOR_DARKEN; - modes["lighten"s] = CAIRO_OPERATOR_LIGHTEN; - modes["color-dodge"s] = CAIRO_OPERATOR_COLOR_DODGE; - modes["color-burn"s] = CAIRO_OPERATOR_COLOR_BURN; - modes["hard-light"s] = CAIRO_OPERATOR_HARD_LIGHT; - modes["soft-light"s] = CAIRO_OPERATOR_SOFT_LIGHT; - modes["difference"s] = CAIRO_OPERATOR_DIFFERENCE; - modes["exclusion"s] = CAIRO_OPERATOR_EXCLUSION; - modes["hsl-hue"s] = CAIRO_OPERATOR_HSL_HUE; - modes["hsl-saturation"s] = CAIRO_OPERATOR_HSL_SATURATION; - modes["hsl-color"s] = CAIRO_OPERATOR_HSL_COLOR; - modes["hsl-luminosity"s] = CAIRO_OPERATOR_HSL_LUMINOSITY; - } - auto it = modes.find(mode); - return it != modes.end() ? it->second : fallback; - } - - /** - * @brief Create a UCS-4 codepoint from a utf-8 encoded string - */ - bool utf8_to_ucs4(const unsigned char* src, unicode_charlist& result_list) { - if (!src) { - return false; - } - const unsigned char* first = src; - while (*first) { - int len = 0; - unsigned long result = 0; - if ((*first >> 7) == 0) { - len = 1; - result = *first; - } else if ((*first >> 5) == 6) { - len = 2; - result = *first & 31; - } else if ((*first >> 4) == 14) { - len = 3; - result = *first & 15; - } else if ((*first >> 3) == 30) { - len = 4; - result = *first & 7; - } else { - return false; - } - const unsigned char* next; - for (next = first + 1; *next && ((*next >> 6) == 2) && (next - first < len); next++) { - result = result << 6; - result |= *next & 63; - } - unicode_character uc_char; - uc_char.codepoint = result; - uc_char.offset = first - src; - uc_char.length = next - first; - result_list.push_back(uc_char); - first = next; - } - return true; - } - - /** - * @brief Convert a UCS-4 codepoint to a utf-8 encoded string - */ - size_t ucs4_to_utf8(char* utf8, unsigned int ucs) { - if (ucs <= 0x7f) { - *utf8 = ucs; - return 1; - } else if (ucs <= 0x07ff) { - *(utf8++) = ((ucs >> 6) & 0xff) | 0xc0; - *utf8 = (ucs & 0x3f) | 0x80; - return 2; - } else if (ucs <= 0xffff) { - *(utf8++) = ((ucs >> 12) & 0x0f) | 0xe0; - *(utf8++) = ((ucs >> 6) & 0x3f) | 0x80; - *utf8 = (ucs & 0x3f) | 0x80; - return 3; - } else if (ucs <= 0x1fffff) { - *(utf8++) = ((ucs >> 18) & 0x07) | 0xf0; - *(utf8++) = ((ucs >> 12) & 0x3f) | 0x80; - *(utf8++) = ((ucs >> 6) & 0x3f) | 0x80; - *utf8 = (ucs & 0x3f) | 0x80; - return 4; - } else if (ucs <= 0x03ffffff) { - *(utf8++) = ((ucs >> 24) & 0x03) | 0xf8; - *(utf8++) = ((ucs >> 18) & 0x3f) | 0x80; - *(utf8++) = ((ucs >> 12) & 0x3f) | 0x80; - *(utf8++) = ((ucs >> 6) & 0x3f) | 0x80; - *utf8 = (ucs & 0x3f) | 0x80; - return 5; - } else if (ucs <= 0x7fffffff) { - *(utf8++) = ((ucs >> 30) & 0x01) | 0xfc; - *(utf8++) = ((ucs >> 24) & 0x3f) | 0x80; - *(utf8++) = ((ucs >> 18) & 0x3f) | 0x80; - *(utf8++) = ((ucs >> 12) & 0x3f) | 0x80; - *(utf8++) = ((ucs >> 6) & 0x3f) | 0x80; - *utf8 = (ucs & 0x3f) | 0x80; - return 6; - } else { - return 0; - } + device_lock::device_lock(cairo_device_t* device) { + auto status = cairo_device_acquire(device); + if (status == CAIRO_STATUS_SUCCESS) { + m_device = device; } } -} + device_lock::~device_lock() { + cairo_device_release(m_device); + } + device_lock::operator bool() const { + return m_device != nullptr; + } + device_lock::operator cairo_device_t*() const { + return m_device; + } + + // }}} + // implementation : ft_face_lock {{{ + + ft_face_lock::ft_face_lock(cairo_scaled_font_t* font) : m_font(font) { + m_face = cairo_ft_scaled_font_lock_face(m_font); + } + ft_face_lock::~ft_face_lock() { + cairo_ft_scaled_font_unlock_face(m_font); + } + ft_face_lock::operator FT_Face() const { + return m_face; + } + + // }}} + // implementation : unicode_character {{{ + + unicode_character::unicode_character() : codepoint(0), offset(0), length(0) {} + + // }}} + + /** + * @see + */ + cairo_operator_t str2operator(const string& mode, cairo_operator_t fallback) { + if (mode.empty()) { + return fallback; + } + static std::map modes; + if (modes.empty()) { + modes["clear"s] = CAIRO_OPERATOR_CLEAR; + modes["source"s] = CAIRO_OPERATOR_SOURCE; + modes["over"s] = CAIRO_OPERATOR_OVER; + modes["in"s] = CAIRO_OPERATOR_IN; + modes["out"s] = CAIRO_OPERATOR_OUT; + modes["atop"s] = CAIRO_OPERATOR_ATOP; + modes["dest"s] = CAIRO_OPERATOR_DEST; + modes["dest-over"s] = CAIRO_OPERATOR_DEST_OVER; + modes["dest-in"s] = CAIRO_OPERATOR_DEST_IN; + modes["dest-out"s] = CAIRO_OPERATOR_DEST_OUT; + modes["dest-atop"s] = CAIRO_OPERATOR_DEST_ATOP; + modes["xor"s] = CAIRO_OPERATOR_XOR; + modes["add"s] = CAIRO_OPERATOR_ADD; + modes["saturate"s] = CAIRO_OPERATOR_SATURATE; + modes["multiply"s] = CAIRO_OPERATOR_MULTIPLY; + modes["screen"s] = CAIRO_OPERATOR_SCREEN; + modes["overlay"s] = CAIRO_OPERATOR_OVERLAY; + modes["darken"s] = CAIRO_OPERATOR_DARKEN; + modes["lighten"s] = CAIRO_OPERATOR_LIGHTEN; + modes["color-dodge"s] = CAIRO_OPERATOR_COLOR_DODGE; + modes["color-burn"s] = CAIRO_OPERATOR_COLOR_BURN; + modes["hard-light"s] = CAIRO_OPERATOR_HARD_LIGHT; + modes["soft-light"s] = CAIRO_OPERATOR_SOFT_LIGHT; + modes["difference"s] = CAIRO_OPERATOR_DIFFERENCE; + modes["exclusion"s] = CAIRO_OPERATOR_EXCLUSION; + modes["hsl-hue"s] = CAIRO_OPERATOR_HSL_HUE; + modes["hsl-saturation"s] = CAIRO_OPERATOR_HSL_SATURATION; + modes["hsl-color"s] = CAIRO_OPERATOR_HSL_COLOR; + modes["hsl-luminosity"s] = CAIRO_OPERATOR_HSL_LUMINOSITY; + } + auto it = modes.find(mode); + return it != modes.end() ? it->second : fallback; + } + + /** + * @brief Create a UCS-4 codepoint from a utf-8 encoded string + */ + bool utf8_to_ucs4(const unsigned char* src, unicode_charlist& result_list) { + if (!src) { + return false; + } + const unsigned char* first = src; + while (*first) { + int len = 0; + unsigned long result = 0; + if ((*first >> 7) == 0) { + len = 1; + result = *first; + } else if ((*first >> 5) == 6) { + len = 2; + result = *first & 31; + } else if ((*first >> 4) == 14) { + len = 3; + result = *first & 15; + } else if ((*first >> 3) == 30) { + len = 4; + result = *first & 7; + } else { + return false; + } + const unsigned char* next; + for (next = first + 1; *next && ((*next >> 6) == 2) && (next - first < len); next++) { + result = result << 6; + result |= *next & 63; + } + unicode_character uc_char; + uc_char.codepoint = result; + uc_char.offset = first - src; + uc_char.length = next - first; + result_list.push_back(uc_char); + first = next; + } + return true; + } + + /** + * @brief Convert a UCS-4 codepoint to a utf-8 encoded string + */ + size_t ucs4_to_utf8(char* utf8, unsigned int ucs) { + if (ucs <= 0x7f) { + *utf8 = ucs; + return 1; + } else if (ucs <= 0x07ff) { + *(utf8++) = ((ucs >> 6) & 0xff) | 0xc0; + *utf8 = (ucs & 0x3f) | 0x80; + return 2; + } else if (ucs <= 0xffff) { + *(utf8++) = ((ucs >> 12) & 0x0f) | 0xe0; + *(utf8++) = ((ucs >> 6) & 0x3f) | 0x80; + *utf8 = (ucs & 0x3f) | 0x80; + return 3; + } else if (ucs <= 0x1fffff) { + *(utf8++) = ((ucs >> 18) & 0x07) | 0xf0; + *(utf8++) = ((ucs >> 12) & 0x3f) | 0x80; + *(utf8++) = ((ucs >> 6) & 0x3f) | 0x80; + *utf8 = (ucs & 0x3f) | 0x80; + return 4; + } else if (ucs <= 0x03ffffff) { + *(utf8++) = ((ucs >> 24) & 0x03) | 0xf8; + *(utf8++) = ((ucs >> 18) & 0x3f) | 0x80; + *(utf8++) = ((ucs >> 12) & 0x3f) | 0x80; + *(utf8++) = ((ucs >> 6) & 0x3f) | 0x80; + *utf8 = (ucs & 0x3f) | 0x80; + return 5; + } else if (ucs <= 0x7fffffff) { + *(utf8++) = ((ucs >> 30) & 0x01) | 0xfc; + *(utf8++) = ((ucs >> 24) & 0x3f) | 0x80; + *(utf8++) = ((ucs >> 18) & 0x3f) | 0x80; + *(utf8++) = ((ucs >> 12) & 0x3f) | 0x80; + *(utf8++) = ((ucs >> 6) & 0x3f) | 0x80; + *utf8 = (ucs & 0x3f) | 0x80; + return 6; + } else { + return 0; + } + } +} // namespace utils +} // namespace cairo POLYBAR_NS_END diff --git a/src/utils/string.cpp b/src/utils/string.cpp index aea57634..86f7f95d 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -8,337 +8,336 @@ POLYBAR_NS namespace string_util { - /** - * Check if haystack contains needle - */ - bool contains(const string& haystack, const string& needle) { - return haystack.find(needle) != string::npos; +/** + * Check if haystack contains needle + */ +bool contains(const string& haystack, const string& needle) { + return haystack.find(needle) != string::npos; +} + +bool ends_with(const string& haystack, const string& suffix) { + if (haystack.length() < suffix.length()) { + return false; } - bool ends_with(const string& haystack, const string& suffix) { - if (haystack.length() < suffix.length()) { - return false; + return haystack.compare(haystack.length() - suffix.length(), suffix.length(), suffix) == 0; +} + +/** + * Check if haystack contains needle ignoring case + */ +bool contains_ignore_case(const string& haystack, const string& needle) { + return lower(haystack).find(lower(needle)) != string::npos; +} + +/** + * Convert string to uppercase + */ +string upper(const string& s) { + string str(s); + for (auto& c : str) { + c = toupper(c); + } + return str; +} + +/** + * Convert string to lowercase + */ +string lower(const string& s) { + string str(s); + for (auto& c : str) { + c = tolower(c); + } + return str; +} + +/** + * Test lower case equality + */ +bool compare(const string& s1, const string& s2) { + return lower(s1) == lower(s2); +} + +/** + * Replace first occurrence of needle in haystack + */ +string replace(const string& haystack, const string& needle, const string& replacement, size_t start, size_t end) { + string str(haystack); + string::size_type pos; + + if (needle != replacement && (pos = str.find(needle, start)) != string::npos) { + if (end == string::npos || pos < end) { + str = str.replace(pos, needle.length(), replacement); } - - return haystack.compare(haystack.length() - suffix.length(), suffix.length(), suffix) == 0; } - /** - * Check if haystack contains needle ignoring case - */ - bool contains_ignore_case(const string& haystack, const string& needle) { - return lower(haystack).find(lower(needle)) != string::npos; + return str; +} + +/** + * Replace all occurrences of needle in haystack + */ +string replace_all(const string& haystack, const string& needle, const string& replacement, size_t start, size_t end) { + string result{haystack}; + string::size_type pos; + while ((pos = result.find(needle, start)) != string::npos && pos < result.length() && + (end == string::npos || pos + needle.length() <= end)) { + result.replace(pos, needle.length(), replacement); + start = pos + replacement.length(); + } + return result; +} + +/** + * Replace all consecutive occurrences of needle in haystack + */ +string squeeze(const string& haystack, char needle) { + string result = haystack; + while (result.find({needle, needle}) != string::npos) { + result = replace_all(result, {needle, needle}, {needle}); + } + return result; +} + +/** + * Remove all occurrences of needle in haystack + */ +string strip(const string& haystack, char needle) { + string str(haystack); + string::size_type pos; + while ((pos = str.find(needle)) != string::npos) { + str.erase(pos, 1); + } + return str; +} + +/** + * Remove trailing newline + */ +string strip_trailing_newline(const string& haystack) { + string str(haystack); + if (str[str.length() - 1] == '\n') { + str.erase(str.length() - 1, 1); + } + return str; +} + +/** + * Trims all characters that match pred from the left + */ +string ltrim(string value, function pred) { + value.erase(value.begin(), find_if(value.begin(), value.end(), std::not_fn(pred))); + return value; +} + +/** + * Trims all characters that match pred from the right + */ +string rtrim(string value, function pred) { + value.erase(find_if(value.rbegin(), value.rend(), std::not_fn(pred)).base(), value.end()); + return value; +} + +/** + * Trims all characters that match pred from both sides + */ +string trim(string value, function pred) { + return ltrim(rtrim(move(value), pred), pred); +} + +/** + * Remove needle from the start of the string + */ +string ltrim(string&& value, const char& needle) { + if (value.empty()) { + return ""; + } + while (*value.begin() == needle) { + value.erase(0, 1); + } + return forward(value); +} + +/** + * Remove needle from the end of the string + */ +string rtrim(string&& value, const char& needle) { + if (value.empty()) { + return ""; + } + while (*(value.end() - 1) == needle) { + value.erase(value.length() - 1, 1); + } + return forward(value); +} + +/** + * Remove needle from the start and end of the string + */ +string trim(string&& value, const char& needle) { + if (value.empty()) { + return ""; + } + return rtrim(ltrim(forward(value), needle), needle); +} + +/** + * Counts the number of codepoints in a utf8 encoded string. + */ +size_t char_len(const string& value) { + // utf-8 bytes of the form 10xxxxxx are continuation bytes, so we + // simply count the number of bytes not of this form. + // + // 0xc0 = 11000000 + // 0x80 = 10000000 + return std::count_if(value.begin(), value.end(), [](char c) { return (c & 0xc0) != 0x80; }); +} + +/** + * Truncates a utf8 string at len number of codepoints. This isn't 100% + * matching the user-perceived character count, but it should be close + * enough and avoids having to pull in something like ICU to count actual + * grapheme clusters. + */ +string utf8_truncate(string&& value, size_t len) { + if (value.empty()) { + return ""; } - /** - * Convert string to uppercase - */ - string upper(const string& s) { - string str(s); - for (auto& c : str) { - c = toupper(c); - } - return str; + // utf-8 bytes of the form 10xxxxxx are continuation bytes, so we + // simply jump forward to bytes not of that form and truncate starting + // at that byte if we've counted too many codepoints + // + // 0xc0 = 11000000 + // 0x80 = 10000000 + auto it = value.begin(); + auto end = value.end(); + for (size_t i = 0; i < len; ++i) { + if (it == end) + break; + ++it; + it = std::find_if(it, end, [](char c) { return (c & 0xc0) != 0x80; }); + } + value.erase(it, end); + + return forward(value); +} + +/** + * Join all strings in vector into a single string separated by delim + */ +string join(const vector& strs, const string& delim) { + string str; + for (auto& s : strs) { + str += (str.empty() ? "" : delim) + s; + } + return str; +} + +/** + * Explode string by delim, ignore empty tokens + */ +vector split(const string& s, char delim) { + std::string::size_type pos = 0; + std::vector result; + + while ((pos = s.find_first_not_of(delim, pos)) != std::string::npos) { + auto nextpos = s.find_first_of(delim, pos); + result.emplace_back(s.substr(pos, nextpos - pos)); + pos = nextpos; } - /** - * Convert string to lowercase - */ - string lower(const string& s) { - string str(s); - for (auto& c : str) { - c = tolower(c); - } - return str; - } + return result; +} - /** - * Test lower case equality - */ - bool compare(const string& s1, const string& s2) { - return lower(s1) == lower(s2); - } - - /** - * Replace first occurrence of needle in haystack - */ - string replace(const string& haystack, const string& needle, const string& replacement, size_t start, size_t end) { - string str(haystack); - string::size_type pos; - - if (needle != replacement && (pos = str.find(needle, start)) != string::npos) { - if (end == string::npos || pos < end) { - str = str.replace(pos, needle.length(), replacement); - } - } - - return str; - } - - /** - * Replace all occurrences of needle in haystack - */ - string replace_all( - const string& haystack, const string& needle, const string& replacement, size_t start, size_t end) { - string result{haystack}; - string::size_type pos; - while ((pos = result.find(needle, start)) != string::npos && pos < result.length() && - (end == string::npos || pos + needle.length() <= end)) { - result.replace(pos, needle.length(), replacement); - start = pos + replacement.length(); - } - return result; - } - - /** - * Replace all consecutive occurrences of needle in haystack - */ - string squeeze(const string& haystack, char needle) { - string result = haystack; - while (result.find({needle, needle}) != string::npos) { - result = replace_all(result, {needle, needle}, {needle}); - } - return result; - } - - /** - * Remove all occurrences of needle in haystack - */ - string strip(const string& haystack, char needle) { - string str(haystack); - string::size_type pos; - while ((pos = str.find(needle)) != string::npos) { - str.erase(pos, 1); - } - return str; - } - - /** - * Remove trailing newline - */ - string strip_trailing_newline(const string& haystack) { - string str(haystack); - if (str[str.length() - 1] == '\n') { - str.erase(str.length() - 1, 1); - } - return str; - } - - /** - * Trims all characters that match pred from the left - */ - string ltrim(string value, function pred) { - value.erase(value.begin(), find_if(value.begin(), value.end(), std::not_fn(pred))); - return value; - } - - /** - * Trims all characters that match pred from the right - */ - string rtrim(string value, function pred) { - value.erase(find_if(value.rbegin(), value.rend(), std::not_fn(pred)).base(), value.end()); - return value; - } - - /** - * Trims all characters that match pred from both sides - */ - string trim(string value, function pred) { - return ltrim(rtrim(move(value), pred), pred); - } - - /** - * Remove needle from the start of the string - */ - string ltrim(string&& value, const char& needle) { - if (value.empty()) { - return ""; - } - while (*value.begin() == needle) { - value.erase(0, 1); - } - return forward(value); - } - - /** - * Remove needle from the end of the string - */ - string rtrim(string&& value, const char& needle) { - if (value.empty()) { - return ""; - } - while (*(value.end() - 1) == needle) { - value.erase(value.length() - 1, 1); - } - return forward(value); - } - - /** - * Remove needle from the start and end of the string - */ - string trim(string&& value, const char& needle) { - if (value.empty()) { - return ""; - } - return rtrim(ltrim(forward(value), needle), needle); - } - - /** - * Counts the number of codepoints in a utf8 encoded string. - */ - size_t char_len(const string& value) { - // utf-8 bytes of the form 10xxxxxx are continuation bytes, so we - // simply count the number of bytes not of this form. - // - // 0xc0 = 11000000 - // 0x80 = 10000000 - return std::count_if(value.begin(), value.end(), [](char c) { return (c & 0xc0) != 0x80; }); - } - - /** - * Truncates a utf8 string at len number of codepoints. This isn't 100% - * matching the user-perceived character count, but it should be close - * enough and avoids having to pull in something like ICU to count actual - * grapheme clusters. - */ - string utf8_truncate(string&& value, size_t len) { - if (value.empty()) { - return ""; - } - - // utf-8 bytes of the form 10xxxxxx are continuation bytes, so we - // simply jump forward to bytes not of that form and truncate starting - // at that byte if we've counted too many codepoints - // - // 0xc0 = 11000000 - // 0x80 = 10000000 - auto it = value.begin(); - auto end = value.end(); - for (size_t i = 0; i < len; ++i) { - if (it == end) - break; - ++it; - it = std::find_if(it, end, [](char c) { return (c & 0xc0) != 0x80; }); - } - value.erase(it, end); - - return forward(value); - } - - /** - * Join all strings in vector into a single string separated by delim - */ - string join(const vector& strs, const string& delim) { - string str; - for (auto& s : strs) { - str += (str.empty() ? "" : delim) + s; - } - return str; - } - - /** - * Explode string by delim, ignore empty tokens - */ - vector split(const string& s, char delim) { - std::string::size_type pos = 0; - std::vector result; - - while ((pos = s.find_first_not_of(delim, pos)) != std::string::npos) { - auto nextpos = s.find_first_of(delim, pos); - result.emplace_back(s.substr(pos, nextpos - pos)); - pos = nextpos; - } - - return result; - } - - /** - * Explode string by delim, include empty tokens - */ - std::vector tokenize(const string& str, char delimiters) { - std::vector tokens; - std::string::size_type lastPos = 0; - auto pos = str.find_first_of(delimiters, lastPos); - - while (pos != std::string::npos && lastPos != std::string::npos) { - tokens.emplace_back(str.substr(lastPos, pos - lastPos)); - - lastPos = pos + 1; - pos = str.find_first_of(delimiters, lastPos); - } +/** + * Explode string by delim, include empty tokens + */ +std::vector tokenize(const string& str, char delimiters) { + std::vector tokens; + std::string::size_type lastPos = 0; + auto pos = str.find_first_of(delimiters, lastPos); + while (pos != std::string::npos && lastPos != std::string::npos) { tokens.emplace_back(str.substr(lastPos, pos - lastPos)); - return tokens; + + lastPos = pos + 1; + pos = str.find_first_of(delimiters, lastPos); } - /** - * Find the nth occurrence of needle in haystack starting from pos - */ - size_t find_nth(const string& haystack, size_t pos, const string& needle, size_t nth) { - size_t found_pos = haystack.find(needle, pos); - if (1 == nth || string::npos == found_pos) { - return found_pos; - } - return find_nth(haystack, found_pos + 1, needle, nth - 1); - } + tokens.emplace_back(str.substr(lastPos, pos - lastPos)); + return tokens; +} - /** - * Create a floating point string - */ - string floating_point(double value, size_t precision, bool fixed, const string& locale) { - std::stringstream ss; - ss.imbue(!locale.empty() ? std::locale(locale.c_str()) : std::locale::classic()); - ss << std::fixed << std::setprecision(precision) << value; - return fixed ? ss.str() : replace(ss.str(), ".00", ""); +/** + * Find the nth occurrence of needle in haystack starting from pos + */ +size_t find_nth(const string& haystack, size_t pos, const string& needle, size_t nth) { + size_t found_pos = haystack.find(needle, pos); + if (1 == nth || string::npos == found_pos) { + return found_pos; } + return find_nth(haystack, found_pos + 1, needle, nth - 1); +} - /** - * Create a MiB filesize string - */ - string filesize_mib(unsigned long long kibibytes, size_t precision, const string& locale) { - return floating_point(kibibytes / 1024.0, precision, true, locale) + " MiB"; - } +/** + * Create a floating point string + */ +string floating_point(double value, size_t precision, bool fixed, const string& locale) { + std::stringstream ss; + ss.imbue(!locale.empty() ? std::locale(locale.c_str()) : std::locale::classic()); + ss << std::fixed << std::setprecision(precision) << value; + return fixed ? ss.str() : replace(ss.str(), ".00", ""); +} - /** - * Create a GiB filesize string - */ - string filesize_gib(unsigned long long kibibytes, size_t precision, const string& locale) { - return floating_point(kibibytes / 1024.0 / 1024.0, precision, true, locale) + " GiB"; - } +/** + * Create a MiB filesize string + */ +string filesize_mib(unsigned long long kibibytes, size_t precision, const string& locale) { + return floating_point(kibibytes / 1024.0, precision, true, locale) + " MiB"; +} - /** - * Create a GiB string, if the value in GiB is >= 1.0. Otherwise, create a MiB string. - */ - string filesize_gib_mib( - unsigned long long kibibytes, size_t precision_mib, size_t precision_gib, const string& locale) { - if (kibibytes < 1024 * 1024) { - return filesize_mib(kibibytes, precision_mib, locale); - } else { - return filesize_gib(kibibytes, precision_gib, locale); - } - } +/** + * Create a GiB filesize string + */ +string filesize_gib(unsigned long long kibibytes, size_t precision, const string& locale) { + return floating_point(kibibytes / 1024.0 / 1024.0, precision, true, locale) + " GiB"; +} - /** - * Create a filesize string by converting given bytes to highest unit possible - */ - string filesize(unsigned long long bytes, size_t precision, bool fixed, const string& locale) { - vector suffixes{"TB", "GB", "MB", "KB"}; - string suffix{"B"}; - double value = bytes; - while (!suffixes.empty() && value >= 1024.0) { - suffix = suffixes.back(); - suffixes.pop_back(); - value /= 1024.0; - } - return floating_point(value, precision, fixed, locale) + " " + suffix; +/** + * Create a GiB string, if the value in GiB is >= 1.0. Otherwise, create a MiB string. + */ +string filesize_gib_mib( + unsigned long long kibibytes, size_t precision_mib, size_t precision_gib, const string& locale) { + if (kibibytes < 1024 * 1024) { + return filesize_mib(kibibytes, precision_mib, locale); + } else { + return filesize_gib(kibibytes, precision_gib, locale); } +} - /** - * Compute string hash - */ - hash_type hash(const string& src) { - return std::hash()(src); +/** + * Create a filesize string by converting given bytes to highest unit possible + */ +string filesize(unsigned long long bytes, size_t precision, bool fixed, const string& locale) { + vector suffixes{"TB", "GB", "MB", "KB"}; + string suffix{"B"}; + double value = bytes; + while (!suffixes.empty() && value >= 1024.0) { + suffix = suffixes.back(); + suffixes.pop_back(); + value /= 1024.0; } -} // namespace string_util + return floating_point(value, precision, fixed, locale) + " " + suffix; +} + +/** + * Compute string hash + */ +hash_type hash(const string& src) { + return std::hash()(src); +} +} // namespace string_util POLYBAR_NS_END