From 3f2f73aed54cb4737edb5b5a0b899b8b1cedfe8f Mon Sep 17 00:00:00 2001 From: Artem Senichev Date: Thu, 23 Jun 2022 14:16:06 +0300 Subject: [PATCH] Render text using Pango framework Adds options to configure font face, size, and color for text output. Signed-off-by: Artem Senichev --- .github/workflows/ci.yml | 5 +- extra/swayimgrc | 6 +++ extra/swayimgrc.5 | 3 ++ meson.build | 27 ++++++---- src/canvas.c | 40 -------------- src/canvas.h | 18 ------- src/config.c | 92 +++++++++++++++++++++++---------- src/config.h | 24 ++++++--- src/exif.c | 4 +- src/image.c | 83 ++++++++--------------------- src/image.h | 17 +++--- src/text.c | 109 +++++++++++++++++++++++++++++++++++++++ src/text.h | 42 +++++++++++++++ src/viewer.c | 40 ++++++++------ 14 files changed, 315 insertions(+), 195 deletions(-) create mode 100644 src/text.c create mode 100644 src/text.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 715d52f7..9a95e1c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: [push] jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Install dependencies @@ -12,7 +12,8 @@ jobs: sudo apt install --no-install-recommends --yes build-essential meson pkg-config wayland-protocols libwayland-dev libcairo-dev libjson-c-dev libxkbcommon-dev - libgif-dev libjpeg-dev librsvg2-dev libwebp-dev + libpango1.0-dev libpangocairo-1.0-0 + libavif-dev libgif-dev libjpeg-dev librsvg2-dev libwebp-dev - name: Check out source code uses: actions/checkout@v2.2.0 diff --git a/extra/swayimgrc b/extra/swayimgrc index 892f7311..7d9c3f88 100644 --- a/extra/swayimgrc +++ b/extra/swayimgrc @@ -20,6 +20,12 @@ background = grid # Show image info: format, size, EXIF, and current scale (yes/no) info = no +# Font used to show image info +font = monospace 10 + +# Text color +color = b4b4b4 + # Window class/app_id (default is autogenerated swayimg_XXXX) #app_id = swayimg diff --git a/extra/swayimgrc.5 b/extra/swayimgrc.5 index e8fb83a7..4bd8b542 100644 --- a/extra/swayimgrc.5 +++ b/extra/swayimgrc.5 @@ -32,6 +32,8 @@ Empty lines and comments are ignored. .IP "\fBfullscreen\fR: start in full screen mode, possible values are \fIyes\fR or \fIno\fR;" .IP "\fBbackground\fR: background mode or color, possible values are \fIgrid\fR or RGB hex color (e.g. \fI1a2b3c\fR);" .IP "\fBinfo\fR: show image meta information: format, size, EXIF, and current scale, possible values are \fIyes\fR or \fIno\fR;" +.IP "\fBfont\fR: font name and size used to render image info text in pango format;" +.IP "\fBcolor\fR: color used to render text, RGB hex format (e.g. \fI1a2b3c\fR);" .IP "\fBapp_id\fR: set a constant window class/app_id, setting this may break the window layout;" .IP "\fBrules\fR: enable or disable adding Sway rules (window position management), possible values are \fIyes\fR or \fIno\fR." .\" example file @@ -42,6 +44,7 @@ scale = fit fullscreen = no background = 1a2b3c info = yes +font = monospace 10 .EE .\" related man pages .SH SEE ALSO diff --git a/meson.build b/meson.build index 6ed62d6e..cfe49734 100644 --- a/meson.build +++ b/meson.build @@ -25,19 +25,21 @@ version = get_option('version') # mandatory dependencies wlcln = dependency('wayland-client') cairo = dependency('cairo') -json = dependency('json-c') -xkb = dependency('xkbcommon') -rt = cc.find_library('rt') +json = dependency('json-c') +xkb = dependency('xkbcommon') +pango = dependency('pango') +pangocairo = dependency('pangocairo') +rt = cc.find_library('rt') # optional dependencies: file formats support -avif = dependency('libavif', required: get_option('avif')) -gif = cc.find_library('gif', required: get_option('gif')) -jpeg = dependency('libjpeg', required: get_option('jpeg')) -png = dependency('libpng', required: get_option('png')) -rsvg = dependency('librsvg-2.0', version: '>=2.46', required: get_option('svg')) -webp = dependency('libwebp', required: get_option('webp')) +avif = dependency('libavif', required: get_option('avif')) +gif = cc.find_library('gif', required: get_option('gif')) +jpeg = dependency('libjpeg', required: get_option('jpeg')) +png = dependency('libpng', required: get_option('png')) +rsvg = dependency('librsvg-2.0', version: '>=2.46', required: get_option('svg')) +webp = dependency('libwebp', required: get_option('webp')) # optional dependencies: other features -exif = dependency('libexif', required: get_option('exif')) -bash = dependency('bash-completion', required: get_option('bash')) +exif = dependency('libexif', required: get_option('exif')) +bash = dependency('bash-completion', required: get_option('bash')) # Arch specific: https://bugs.archlinux.org/task/73931 jxl_feature = get_option('jxl') jxl = dependency('libjxl', required: false) @@ -117,6 +119,7 @@ sources = [ 'src/image.c', 'src/main.c', 'src/sway.c', + 'src/text.c', 'src/viewer.c', 'src/window.c', 'src/formats/bmp.c', @@ -160,6 +163,8 @@ executable( jpeg, json, jxl, + pango, + pangocairo, png, rsvg, rt, diff --git a/src/canvas.c b/src/canvas.c index 936077fe..9ef2e9b4 100644 --- a/src/canvas.c +++ b/src/canvas.c @@ -9,13 +9,6 @@ #include #include -// Text render parameters -#define FONT_FAMILY "monospace" -#define FONT_SIZE 16 -#define LINE_SPACING 2 -#define TEXT_COLOR 0xb2b2b2 -#define TEXT_SHADOW 0x101010 - // Background grid parameters #define GRID_STEP 10 #define GRID_COLOR1 0x333333 @@ -146,39 +139,6 @@ void draw_grid(const canvas_t* canvas, cairo_surface_t* image, cairo_t* cairo) cairo_surface_mark_dirty(wnd_surface); } -void draw_text(cairo_t* cairo, int x, int y, const char* text) -{ - // set font - cairo_font_face_t* font = cairo_toy_font_face_create( - FONT_FAMILY, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); - cairo_set_font_face(cairo, font); - cairo_set_font_size(cairo, FONT_SIZE); - - // shadow - cairo_set_source_rgb(cairo, RGB_RED(TEXT_SHADOW), RGB_GREEN(TEXT_SHADOW), - RGB_BLUE(TEXT_SHADOW)); - cairo_move_to(cairo, x + 1, y + 1 + FONT_SIZE); - cairo_show_text(cairo, text); - - // normal text - cairo_set_source_rgb(cairo, RGB_RED(TEXT_COLOR), RGB_GREEN(TEXT_COLOR), - RGB_BLUE(TEXT_COLOR)); - cairo_move_to(cairo, x, y + FONT_SIZE); - cairo_show_text(cairo, text); - - cairo_set_font_face(cairo, NULL); - cairo_font_face_destroy(font); -} - -void draw_lines(cairo_t* cairo, int x, int y, const char** lines) -{ - while (*lines) { - draw_text(cairo, x, y, *lines); - ++lines; - y += FONT_SIZE + LINE_SPACING; - } -} - bool move_viewpoint(canvas_t* canvas, cairo_surface_t* image, move_t direction) { const int img_w = canvas->scale * cairo_image_surface_get_width(image); diff --git a/src/canvas.h b/src/canvas.h index 91db3e9f..9fb4eed5 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -88,24 +88,6 @@ void draw_image(const canvas_t* canvas, cairo_surface_t* image, cairo_t* cairo); */ void draw_grid(const canvas_t* canvas, cairo_surface_t* image, cairo_t* cairo); -/** - * Draw text line. - * @param[in] cairo paint context - * @param[in] x left offset - * @param[in] y top offset - * @param[in] text text to draw - */ -void draw_text(cairo_t* cairo, int x, int y, const char* text); - -/** - * Draw multiline text. - * @param[in] cairo paint context - * @param[in] x left offset - * @param[in] y top offset - * @param[in] lines array of strings, last line must be NULL - */ -void draw_lines(cairo_t* cairo, int x, int y, const char** lines); - /** * Move view point. * @param[in] canvas canvas context diff --git a/src/config.c b/src/config.c index 032328c4..56365f19 100644 --- a/src/config.c +++ b/src/config.c @@ -16,6 +16,11 @@ /** Max length of window class/app_id name */ #define APP_ID_MAX 32 +/** Default font name/size */ +#define FONT_FACE "monospace 10" +/** Default text color. */ +#define TEXT_COLOR 0xb4b4b4 + /** Config file location. */ typedef struct { const char* prefix; ///< Environment variable name @@ -86,6 +91,44 @@ static bool set_boolean(const char* text, bool* value) return rc; } +/** + * Convert text value to RGV color. + * @param[in] text text to convert + * @param[out] value target variable + * @return false if value has invalid format + */ +static bool set_color(const char* text, uint32_t* value) +{ + const unsigned long rgb = strtoul(text, NULL, 16); + if (rgb > 0x00ffffff || errno == ERANGE || (rgb == 0 && errno == EINVAL)) { + return false; + } + *value = rgb; + return true; +} + +/** + * Set (replace) string in config parameter. + * @param[in] src source string + * @param[out] dst destination buffer + * @return false if not enough memory + */ +static bool set_string(const char* src, char** dst) +{ + const size_t len = strlen(src) + 1 /*last null*/; + char* ptr = malloc(len); + if (!ptr) { + fprintf(stderr, "Not enough memory\n"); + return false; + } + memcpy(ptr, src, len); + + free(*dst); + *dst = ptr; + + return true; +} + /** * Apply property to configuration. * @param[out] cfg target configuration instance @@ -104,6 +147,10 @@ static bool apply_conf(config_t* cfg, const char* key, const char* value) return set_background_config(cfg, value); } else if (strcmp(key, "info") == 0) { return set_boolean(value, &cfg->show_info); + } else if (strcmp(key, "font") == 0) { + return set_font_config(cfg, value); + } else if (strcmp(key, "color") == 0) { + return set_color(value, &cfg->font_color); } else if (strcmp(key, "app_id") == 0) { return set_appid_config(cfg, value); } else if (strcmp(key, "rules") == 0) { @@ -130,6 +177,8 @@ static config_t* default_config(void) cfg->scale = scale_fit_or100; cfg->background = BACKGROUND_GRID; cfg->sway_rules = true; + set_font_config(cfg, FONT_FACE); + cfg->font_color = TEXT_COLOR; // create unique application id if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { @@ -228,6 +277,7 @@ config_t* init_config(void) void free_config(config_t* cfg) { if (cfg) { + free((void*)cfg->font_face); free((void*)cfg->app_id); free(cfg); } @@ -273,21 +323,13 @@ bool set_scale_config(config_t* cfg, const char* scale) bool set_background_config(config_t* cfg, const char* background) { - uint32_t bkg; - if (strcmp(background, "grid") == 0) { - bkg = BACKGROUND_GRID; - } else { - bkg = strtoul(background, NULL, 16); - if (bkg > 0x00ffffff || errno == ERANGE || - (bkg == 0 && errno == EINVAL)) { - fprintf(stderr, "Invalid background: %s\n", background); - fprintf(stderr, "Expected 'grid' or RGB hex value.\n"); - return false; - } + cfg->background = BACKGROUND_GRID; + } else if (!set_color(background, &cfg->background)) { + fprintf(stderr, "Invalid background: %s\n", background); + fprintf(stderr, "Expected 'grid' or RGB hex value.\n"); + return false; } - - cfg->background = bkg; return true; } @@ -325,28 +367,22 @@ bool set_geometry_config(config_t* cfg, const char* geometry) bool set_appid_config(config_t* cfg, const char* app_id) { - char* ptr; - size_t len; - - len = app_id ? strlen(app_id) : 0; + const size_t len = app_id ? strlen(app_id) : 0; if (len == 0 || len > APP_ID_MAX) { fprintf(stderr, "Invalid class/app_id: %s\n", app_id); fprintf(stderr, "Expected non-empty string up to %d chars.\n", APP_ID_MAX); return false; } + return set_string(app_id, (char**)&cfg->app_id); +} - ++len; // add last null - - ptr = malloc(len); - if (!ptr) { - fprintf(stderr, "Not enough memory\n"); +bool set_font_config(config_t* cfg, const char* font) +{ + const size_t len = font ? strlen(font) : 0; + if (len == 0) { + fprintf(stderr, "Invalid font description: %s\n", font); return false; } - memcpy(ptr, app_id, len); - - free((void*)cfg->app_id); - cfg->app_id = ptr; - - return true; + return set_string(font, (char**)&cfg->font_face); } diff --git a/src/config.h b/src/config.h index 969237e2..450fd484 100644 --- a/src/config.h +++ b/src/config.h @@ -7,13 +7,15 @@ /** App configuration. */ typedef struct { - scale_t scale; ///< Initial scale - rect_t window; ///< Window geometry - uint32_t background; ///< Background mode/color - bool fullscreen; ///< Full screen mode - bool show_info; ///< Show image info - const char* app_id; ///< Window class/app_id name - bool sway_rules; ///< Enable/disable Sway rules + scale_t scale; ///< Initial scale + rect_t window; ///< Window geometry + uint32_t background; ///< Background mode/color + bool fullscreen; ///< Full screen mode + bool show_info; ///< Show image info + const char* font_face; ///< Font name and size (pango format) + uint32_t font_color; ///< Font color + const char* app_id; ///< Window class/app_id name + bool sway_rules; ///< Enable/disable Sway rules } config_t; /** @@ -66,3 +68,11 @@ bool set_geometry_config(config_t* cfg, const char* geometry); * @return false if value format is invalid */ bool set_appid_config(config_t* cfg, const char* app_id); + +/** + * Set font name and size. + * @param[out] cfg target configuration instance + * @param[in] font font name and size in pango format + * @return false if value format is invalid + */ +bool set_font_config(config_t* cfg, const char* font); diff --git a/src/exif.c b/src/exif.c index 56933d56..77d365db 100644 --- a/src/exif.c +++ b/src/exif.c @@ -38,7 +38,7 @@ static void add_meta(image_t* img, ExifData* exif, ExifTag tag, if (entry) { exif_entry_get_value(entry, value, sizeof(value)); if (*value) { - add_image_meta(img, name, value); + add_image_info(img, name, value); } } } @@ -133,7 +133,7 @@ static void read_location(image_t* img, ExifData* exif) if (read_coordinate(exif, EXIF_TAG_GPS_LONGITUDE, EXIF_TAG_GPS_LONGITUDE_REF, location + pos, sizeof(location) - pos)) { - add_image_meta(img, "Location", location); + add_image_info(img, "Location", location); } } } diff --git a/src/image.c b/src/image.c index 7c62bdec..933ccda8 100644 --- a/src/image.c +++ b/src/image.c @@ -15,9 +15,6 @@ #include #include -// Number of characters in the name field in meta info -#define META_NAME_LEN 12 - /** * Image loader function. * @param[in] data raw image data @@ -188,14 +185,14 @@ static image_t* image_create(const char* path, const uint8_t* data, size_t size) } // add general meta info - add_image_meta(img, "File", path); - add_image_meta(img, "Format", meta); + add_image_info(img, "File", path); + add_image_info(img, "Format", meta); human_size(size, meta, sizeof(meta)); - add_image_meta(img, "File size", meta); + add_image_info(img, "File size", meta); snprintf(meta, sizeof(meta), "%ix%i", cairo_image_surface_get_width(img->surface), cairo_image_surface_get_height(img->surface)); - add_image_meta(img, "Image size", meta); + add_image_info(img, "Image size", meta); #ifdef HAVE_LIBEXIF // handle EXIF data @@ -294,69 +291,31 @@ image_t* image_from_stdin(void) void image_free(image_t* img) { if (img) { - for (size_t i = 0; i < sizeof(img->meta) / sizeof(img->meta[0]); ++i) { - free(img->meta[i]); - } if (img->surface) { cairo_surface_destroy(img->surface); } + free((void*)img->info); free(img); } } -/** - * Create meta information line. - * @param[in] name property name - * @param[in] value property value - * @return pointer to allocated string, caller must free it - */ -static char* create_meta(const char* name, const char* value) -{ - char* meta; - size_t meta_len; - size_t consumed = 0; - const size_t name_len = strlen(name); - const size_t value_len = strlen(value); - - // calculate entry length - if (name_len < META_NAME_LEN) { - meta_len = META_NAME_LEN; - } else { - meta_len = name_len; - } - meta_len += 3; // delimiter (": ") and termination null - meta_len += value_len; - - // compose entry line "name: value" - meta = malloc(meta_len); - if (!meta) { - fprintf(stderr, "Not enough memory\n"); - return NULL; - } - memcpy(meta, name, name_len); - consumed = name_len; - meta[consumed++] = ':'; - meta[consumed++] = ' '; - while (consumed < META_NAME_LEN) { - meta[consumed++] = ' '; - } - memcpy(meta + consumed, value, value_len); - consumed += value_len; - meta[consumed++] = 0; - - return meta; -} - -void add_image_meta(image_t* img, const char* name, const char* value) +void add_image_info(image_t* img, const char* key, const char* value) { - size_t index; - const size_t max_index = sizeof(img->meta) / sizeof(img->meta[0]) - 1; - - // search for free meta entry - for (index = 0; index < max_index; ++index) { - if (!img->meta[index]) { - img->meta[index] = create_meta(name, value); - break; + char* buffer = (char*)img->info; + const char* delim = ":\t"; + const size_t cur_len = img->info ? strlen(img->info) + 1 : 0; + const size_t add_len = strlen(key) + strlen(value) + strlen(delim) + 1; + + buffer = realloc(buffer, cur_len + add_len); + if (buffer) { + if (cur_len == 0) { + buffer[0] = 0; + } else { + strcat(buffer, "\n"); } + strcat(buffer, key); + strcat(buffer, delim); + strcat(buffer, value); + img->info = buffer; } } diff --git a/src/image.h b/src/image.h index 2d8484aa..03bac9a2 100644 --- a/src/image.h +++ b/src/image.h @@ -5,9 +5,6 @@ #include -// Max number of meta entries -#define META_MAX_ENTRY 16 - // Orientation (top left corner projection) typedef enum { ori_undefined, @@ -23,10 +20,10 @@ typedef enum { /** Image description. */ typedef struct { - const char* path; ///< Path to the file - char* meta[META_MAX_ENTRY]; ///< Image meta info - orientation_t orientation; ///< Image orientation - cairo_surface_t* surface; ///< Image surface + const char* path; ///< Path to the file + const char* info; ///< Image meta info + orientation_t orientation; ///< Image orientation + cairo_surface_t* surface; ///< Image surface } image_t; /** @@ -49,12 +46,12 @@ image_t* image_from_stdin(void); void image_free(image_t* img); /** - * Add meta property. + * Add meta info property. * @param[in] img image instance - * @param[in] name property name + * @param[in] key property name * @param[in] value property value */ -void add_image_meta(image_t* img, const char* name, const char* value); +void add_image_info(image_t* img, const char* key, const char* value); /** * Get string with the names of the supported image formats. diff --git a/src/text.c b/src/text.c new file mode 100644 index 00000000..5abd61dd --- /dev/null +++ b/src/text.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +// Text renderer based on the Pango framework. +// Copyright (C) 2022 Artem Senichev + +#include "text.h" + +#include +#include + +/** Text padding: space between text layout and window edge. */ +#define TEXT_PADDING 10 + +/** Text renderer context. */ +struct text_render { + PangoFontDescription* font; ///< Font description + uint32_t color; ///< Font color +}; + +text_render_t* init_text(config_t* cfg) +{ + text_render_t* ctx; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) { + return NULL; + } + + ctx->font = pango_font_description_from_string(cfg->font_face); + if (!ctx->font) { + free(ctx); + return NULL; + } + + ctx->color = cfg->font_color; + + return ctx; +} + +void free_text(text_render_t* ctx) +{ + if (ctx) { + if (ctx->font) { + pango_font_description_free(ctx->font); + } + free(ctx); + } +} + +void print_text(text_render_t* ctx, cairo_t* cairo, text_position_t pos, + const char* text) +{ + PangoLayout* layout; + PangoTabArray* tab; + cairo_surface_t* surface; + int x = 0, y = 0; + int wnd_w = 0, wnd_h = 0; + int txt_w = 0, txt_h = 0; + + layout = pango_cairo_create_layout(cairo); + if (!layout) { + return; + } + + // get window size + surface = cairo_get_target(cairo); + wnd_w = cairo_image_surface_get_width(surface); + wnd_h = cairo_image_surface_get_height(surface); + + // setup layout + pango_layout_set_font_description(layout, ctx->font); + pango_layout_set_text(layout, text, -1); + + // magic, need to handle tabs in a right way + tab = pango_tab_array_new_with_positions(1, true, PANGO_TAB_LEFT, 200); + pango_layout_set_tabs(layout, tab); + pango_tab_array_free(tab); + + // calculate layout position + pango_layout_get_size(layout, &txt_w, &txt_h); + txt_w /= PANGO_SCALE; + txt_h /= PANGO_SCALE; + switch (pos) { + case text_top_left: + x = TEXT_PADDING; + y = TEXT_PADDING; + break; + case text_top_right: + x = wnd_w - txt_w - TEXT_PADDING; + y = TEXT_PADDING; + break; + case text_bottom_left: + x = TEXT_PADDING; + y = wnd_h - txt_h - TEXT_PADDING; + break; + case text_bottom_right: + x = wnd_w - txt_w - TEXT_PADDING; + y = wnd_h - txt_h - TEXT_PADDING; + break; + } + + // put layout on cairo surface + cairo_set_source_rgb(cairo, RGB_RED(ctx->color), RGB_GREEN(ctx->color), + RGB_BLUE(ctx->color)); + cairo_move_to(cairo, x, y); + pango_cairo_show_layout(cairo, layout); + cairo_identity_matrix(cairo); + + g_object_unref(layout); +} diff --git a/src/text.h b/src/text.h new file mode 100644 index 00000000..e2d56515 --- /dev/null +++ b/src/text.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// Text renderer based on the Pango framework. +// Copyright (C) 2022 Artem Senichev + +#pragma once + +#include "config.h" + +/** Text renderer context. */ +struct text_render; +typedef struct text_render text_render_t; + +/** Position on the text block relative to the output window. */ +typedef enum { + text_top_left, + text_top_right, + text_bottom_left, + text_bottom_right +} text_position_t; + +/** + * Initialize text renderer context. + * @param[in] cfg configuration instance + * @return created text renderer context + */ +text_render_t* init_text(config_t* cfg); + +/** + * Free text renderer. + * @param[in] ctx renderer context + */ +void free_text(text_render_t* ctx); + +/** + * Print text. + * @param[in] ctx renderer context + * @param[in] cairo output cairo context + * @param[in] pos text block position + * @param[in] text text to output + */ +void print_text(text_render_t* ctx, cairo_t* cairo, text_position_t pos, + const char* text); diff --git a/src/viewer.c b/src/viewer.c index 0f7c7b3a..7183b281 100644 --- a/src/viewer.c +++ b/src/viewer.c @@ -8,6 +8,7 @@ #include "config.h" #include "image.h" #include "sway.h" +#include "text.h" #include "window.h" #include @@ -20,9 +21,10 @@ typedef struct { size_t total; ///< Total number of files in the list size_t current; ///< Index of currently displayed image in the list } file_list; - image_t* image; ///< Currently displayed image - canvas_t canvas; ///< Canvas context - config_t* config; ///< Configuration + image_t* image; ///< Currently displayed image + canvas_t canvas; ///< Canvas context + text_render_t* text; ///< Text renderer + config_t* config; ///< Configuration } viewer_t; static viewer_t viewer; @@ -168,18 +170,20 @@ static void on_redraw(cairo_surface_t* window) draw_image(&viewer.canvas, viewer.image->surface, cairo); // image meta information: file name, format, exif, etc - if (viewer.config->show_info) { + if (viewer.config->show_info && viewer.text) { char text[32]; + // print meta info + print_text(viewer.text, cairo, text_top_left, viewer.image->info); + // print current scale snprintf(text, sizeof(text), "%i%%", (int)(viewer.canvas.scale * 100.0)); - draw_text(cairo, 10, get_window_height() - 30, text); + print_text(viewer.text, cairo, text_bottom_left, text); + // print file number in list if (viewer.file_list.total > 1) { - const int len = - snprintf(text, sizeof(text), "%lu of %lu", - viewer.file_list.current + 1, viewer.file_list.total); - draw_text(cairo, get_window_width() - 10 - len * 10, 10, text); + snprintf(text, sizeof(text), "%lu of %lu", + viewer.file_list.current + 1, viewer.file_list.total); + print_text(viewer.text, cairo, text_top_right, text); } - draw_lines(cairo, 10, 10, (const char**)viewer.image->meta); } cairo_destroy(cairo); @@ -267,7 +271,11 @@ bool run_viewer(config_t* cfg, const char** files, size_t total) static const struct handlers handlers = { .on_redraw = on_redraw, .on_resize = on_resize, .on_keyboard = on_keyboard }; + bool rc = false; + + // initialize viewer context viewer.config = cfg; + viewer.text = init_text(cfg); if (cfg->sway_rules) { // setup window position via Sway IPC @@ -289,15 +297,14 @@ bool run_viewer(config_t* cfg, const char** files, size_t total) // GUI prepare if (!create_window(&handlers, cfg->window.width, cfg->window.height, cfg->app_id)) { - return false; + goto done; } // load first file viewer.file_list.files = files; viewer.file_list.total = total; if (!next_file(true)) { - destroy_window(); - return false; + goto done; } if (cfg->fullscreen) { @@ -307,9 +314,12 @@ bool run_viewer(config_t* cfg, const char** files, size_t total) // GUI loop show_window(); - destroy_window(); + rc = true; +done: + destroy_window(); image_free(viewer.image); + free_text(viewer.text); - return true; + return rc; }