From cab1352801b62d1b8a12ca1c995cb24445ce4bc9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 28 Mar 2018 23:04:20 -0400 Subject: Start port of swaybar to layer shell This starts up the event loop and wayland display and shims out the basic top level rendering concepts. Also includes some changes to incorporate pango into the 1.x codebase properly. --- swaybar/render.c | 388 ++++++------------------------------------------------- 1 file changed, 42 insertions(+), 346 deletions(-) (limited to 'swaybar/render.c') diff --git a/swaybar/render.c b/swaybar/render.c index 6fc09078..2eaa0195 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -1,367 +1,63 @@ #include #include #include - -#include "client/cairo.h" -#include "client/pango.h" -#include "client/window.h" +#include +#include "cairo.h" +#include "pango.h" +#include "pool-buffer.h" +#include "swaybar/bar.h" #include "swaybar/config.h" -#include "swaybar/status_line.h" #include "swaybar/render.h" -#ifdef ENABLE_TRAY -#include "swaybar/tray/tray.h" -#include "swaybar/tray/sni.h" -#endif -#include "log.h" - - -/* internal spacing */ -static int margin = 3; -static int ws_horizontal_padding = 5; -static double ws_vertical_padding = 1.5; -static int ws_spacing = 1; - -/** - * Renders a sharp line of any width and height. - * - * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 - * if the line has a width/height of one pixel, respectively. - */ -static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { - cairo_set_source_u32(cairo, color); - - if (width > 1 && height > 1) { - cairo_rectangle(cairo, x, y, width, height); - cairo_fill(cairo); - } else { - if (width == 1) { - x += 0.5; - height += y; - width = x; - } - - if (height == 1) { - y += 0.5; - width += x; - height = y; - } - - cairo_move_to(cairo, x, y); - cairo_set_line_width(cairo, 1.0); - cairo_line_to(cairo, width, height); - cairo_stroke(cairo); - } -} - -static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { - int width, height, sep_width; - get_text_size(window->cairo, window->font, &width, &height, - window->scale, block->markup, "%s", block->full_text); - - int textwidth = width; - double block_width = width; - - if (width < block->min_width) { - width = block->min_width; - } - - *x -= width; - - if (block->border != 0 && block->border_left > 0) { - *x -= (block->border_left + margin); - block_width += block->border_left + margin; - } - - if (block->border != 0 && block->border_right > 0) { - *x -= (block->border_right + margin); - block_width += block->border_right + margin; - } - - // Add separator - if (!edge) { - if (config->sep_symbol) { - get_text_size(window->cairo, window->font, &sep_width, &height, - window->scale, false, "%s", config->sep_symbol); - if (sep_width > block->separator_block_width) { - block->separator_block_width = sep_width + margin * 2; - } - } - - *x -= block->separator_block_width; - } else { - *x -= margin; - } - - double pos = *x; - - block->x = (int)pos; - block->width = (int)block_width; - - // render background - if (block->background != 0x0) { - cairo_set_source_u32(window->cairo, block->background); - cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2); - cairo_fill(window->cairo); - } - - // render top border - if (block->border != 0 && block->border_top > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block_width, - block->border_top); - } - - // render bottom border - if (block->border != 0 && block->border_bottom > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - (window->height * window->scale) - 1 - block->border_bottom, - block_width, - block->border_bottom); - } - - // render left border - if (block->border != 0 && block->border_left > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block->border_left, - (window->height * window->scale) - 2); - - pos += block->border_left + margin; - } - - // render text - double offset = 0; - - if (strncmp(block->align, "left", 5) == 0) { - offset = pos; - } else if (strncmp(block->align, "right", 5) == 0) { - offset = pos + width - textwidth; - } else if (strncmp(block->align, "center", 6) == 0) { - offset = pos + (width - textwidth) / 2; - } - - cairo_move_to(window->cairo, offset, margin); - cairo_set_source_u32(window->cairo, block->color); - pango_printf(window->cairo, window->font, window->scale, - block->markup, "%s", block->full_text); - - pos += width; +#include "wlr-layer-shell-unstable-v1-client-protocol.h" - // render right border - if (block->border != 0 && block->border_right > 0) { - pos += margin; - - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block->border_right, - (window->height * window->scale) - 2); - - pos += block->border_right; - } - - // render separator - if (!edge && block->separator) { - if (is_focused) { - cairo_set_source_u32(window->cairo, config->colors.focused_separator); - } else { - cairo_set_source_u32(window->cairo, config->colors.separator); - } - if (config->sep_symbol) { - offset = pos + (block->separator_block_width - sep_width) / 2; - cairo_move_to(window->cairo, offset, margin); - pango_printf(window->cairo, window->font, window->scale, - false, "%s", config->sep_symbol); - } else { - cairo_set_line_width(window->cairo, 1); - cairo_move_to(window->cairo, pos + block->separator_block_width/2, - margin); - cairo_line_to(window->cairo, pos + block->separator_block_width/2, - (window->height * window->scale) - margin); - cairo_stroke(window->cairo); - } - } +static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, + struct swaybar_output *output) { + struct swaybar_config *config = bar->config; -} - -static const char *strip_workspace_name(bool strip_num, const char *ws_name) { - bool strip = false; - int i; - - if (strip_num) { - int len = strlen(ws_name); - for (i = 0; i < len; ++i) { - if (!('0' <= ws_name[i] && ws_name[i] <= '9')) { - if (':' == ws_name[i] && i < len-1 && i > 0) { - strip = true; - ++i; - } - break; - } - } - } - - if (strip) { - return ws_name + i; - } - - return ws_name; -} - -void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { - const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name); - - get_text_size(window->cairo, window->font, width, height, - window->scale, true, "%s", stripped_name); - *width += 2 * ws_horizontal_padding; - *height += 2 * ws_vertical_padding; -} - -static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) { - const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name); - - struct box_colors box_colors; - if (ws->urgent) { - box_colors = config->colors.urgent_workspace; - } else if (ws->focused) { - box_colors = config->colors.focused_workspace; - } else if (ws->visible) { - box_colors = config->colors.active_workspace; - } else { - box_colors = config->colors.inactive_workspace; - } - - int width, height; - workspace_button_size(window, stripped_name, &width, &height); - - // background - cairo_set_source_u32(window->cairo, box_colors.background); - cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); - cairo_fill(window->cairo); - - // border - cairo_set_source_u32(window->cairo, box_colors.border); - cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); - cairo_stroke(window->cairo); - - // text - cairo_set_source_u32(window->cairo, box_colors.text); - cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); - pango_printf(window->cairo, window->font, window->scale, - true, "%s", stripped_name); - - *x += width + ws_spacing; -} - -static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { - int width, height; - get_text_size(window->cairo, window->font, &width, &height, - window->scale, false, "%s", config->mode); - - // background - cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); - cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); - cairo_fill(window->cairo); - - // border - cairo_set_source_u32(window->cairo, config->colors.binding_mode.border); - cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); - cairo_stroke(window->cairo); - - // text - cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); - cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin); - pango_printf(window->cairo, window->font, window->scale, - false, "%s", config->mode); -} - -void render(struct output *output, struct config *config, struct status_line *line) { - int i; - - struct window *window = output->window; - cairo_t *cairo = window->cairo; - bool is_focused = output->focused; - - // Clear cairo_save(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); cairo_paint(cairo); cairo_restore(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - - // Background - if (is_focused) { + if (output->focused) { cairo_set_source_u32(cairo, config->colors.focused_background); } else { cairo_set_source_u32(cairo, config->colors.background); } cairo_paint(cairo); -#ifdef ENABLE_TRAY - uint32_t tray_width = tray_render(output, config); -#else - const uint32_t tray_width = window->width * window->scale; -#endif - - // Command output - if (is_focused) { - cairo_set_source_u32(cairo, config->colors.focused_statusline); + // TODO: use actual height + return 20; +} + +void render_frame(struct swaybar *bar, + struct swaybar_output *output) { + cairo_surface_t *recorder = cairo_recording_surface_create( + CAIRO_CONTENT_COLOR_ALPHA, NULL); + cairo_t *cairo = cairo_create(recorder); + uint32_t height = render_to_cairo(cairo, bar, output); + if (height != output->height) { + // Reconfigure surface + zwlr_layer_surface_v1_set_size( + output->layer_surface, 0, height); + // TODO: this could infinite loop if the compositor assigns us a + // different height than what we asked for + wl_surface_commit(output->surface); + wl_display_roundtrip(bar->display); } else { - cairo_set_source_u32(cairo, config->colors.statusline); - } - - int width, height; - - if (line->protocol == TEXT) { - get_text_size(window->cairo, window->font, &width, &height, - window->scale, config->pango_markup, "%s", line->text_line); - cairo_move_to(cairo, tray_width - margin - width, margin); - pango_printf(window->cairo, window->font, window->scale, - config->pango_markup, "%s", line->text_line); - } else if (line->protocol == I3BAR && line->block_line) { - double pos = tray_width - 0.5; - bool edge = true; - for (i = line->block_line->length - 1; i >= 0; --i) { - struct status_block *block = line->block_line->items[i]; - if (block->full_text && block->full_text[0]) { - render_block(window, config, block, &pos, edge, is_focused); - edge = false; - } - } - } - - cairo_set_line_width(cairo, 1.0); - double x = 0.5; - - // Workspaces - if (config->workspace_buttons) { - for (i = 0; i < output->workspaces->length; ++i) { - struct workspace *ws = output->workspaces->items[i]; - render_workspace_button(window, config, ws, &x); - } - } - - // binding mode indicator - if (config->mode && config->binding_mode_indicator) { - render_binding_mode_indicator(window, config, x); - } -} - -void set_window_height(struct window *window, int height) { - int text_width, text_height; - get_text_size(window->cairo, window->font, - &text_width, &text_height, window->scale, false, - "Test string for measuring purposes"); - if (height > 0) { - margin = (height - text_height) / 2; - ws_vertical_padding = margin - 1.5; - } - window->height = (text_height + margin * 2) / window->scale; + // Replay recording into shm and send it off + output->current_buffer = get_next_buffer(bar->shm, + output->buffers, output->width, output->height); + cairo_t *shm = output->current_buffer->cairo; + cairo_set_source_surface(shm, recorder, 0.0, 0.0); + cairo_paint(shm); + wl_surface_attach(output->surface, + output->current_buffer->buffer, 0, 0); + wl_surface_damage(output->surface, 0, 0, output->width, output->height); + wl_surface_commit(output->surface); + wl_display_roundtrip(bar->display); + } + cairo_surface_destroy(recorder); + cairo_destroy(cairo); } -- cgit v1.2.3-70-g09d2 From 3a458cd7b55bdfad1b04a01064f4fe8fa86ed0de Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 00:49:59 -0400 Subject: Implement workspace button rendering --- swaybar/render.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 4 deletions(-) (limited to 'swaybar/render.c') diff --git a/swaybar/render.c b/swaybar/render.c index 2eaa0195..226e4ce3 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -10,8 +10,79 @@ #include "swaybar/render.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, - struct swaybar_output *output) { +static const char *strip_workspace_number(const char *ws_name) { + size_t len = strlen(ws_name); + for (size_t i = 0; i < len; ++i) { + if (ws_name[i] < '0' || ws_name[i] > '9') { + if (':' == ws_name[i] && i < len - 1 && i > 0) { + return ws_name + i + 1; + } + return ws_name; + } + } + return ws_name; +} + +static uint32_t render_workspace_button(cairo_t *cairo, + struct swaybar_config *config, struct swaybar_workspace *ws, + double *x, uint32_t height) { + static const int ws_horizontal_padding = 5; + static const double ws_vertical_padding = 1.5; + static const int ws_spacing = 1; + + const char *name = ws->name; + if (config->strip_workspace_numbers) { + name = strip_workspace_number(ws->name); + } + + struct box_colors box_colors; + if (ws->urgent) { + box_colors = config->colors.urgent_workspace; + } else if (ws->focused) { + box_colors = config->colors.focused_workspace; + } else if (ws->visible) { + box_colors = config->colors.active_workspace; + } else { + box_colors = config->colors.inactive_workspace; + } + + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, + 1, true, "%s", name); + uint32_t ideal_height = ws_vertical_padding * 2 + text_height; + if (height < ideal_height) { + height = ideal_height; + } + uint32_t width = ws_horizontal_padding * 2 + text_width; + + cairo_set_source_u32(cairo, box_colors.background); + cairo_rectangle(cairo, *x, 0, width - 1, height); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, box_colors.border); + cairo_rectangle(cairo, *x, 0, width - 1, height); + cairo_stroke(cairo); + + double text_y = height / 2.0 - text_height / 2.0; + cairo_set_source_u32(cairo, box_colors.text); + cairo_move_to(cairo, (int)*x + ws_horizontal_padding, (int)floor(text_y)); + pango_printf(cairo, config->font, 1, true, "%s", name); + + *x += width + ws_spacing; + return ideal_height; +} + +static void update_heights(uint32_t height, uint32_t *min, uint32_t *max) { + if (*min < height) { + *min = height; + } + if (height > *max) { + *max = height; + } +} + +static uint32_t render_to_cairo(cairo_t *cairo, + struct swaybar *bar, struct swaybar_output *output) { struct swaybar_config *config = bar->config; cairo_save(cairo); @@ -27,8 +98,20 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, } cairo_paint(cairo); - // TODO: use actual height - return 20; + uint32_t min_height = output->height, max_height = output->height; + + double x = 0; + if (config->workspace_buttons) { + struct swaybar_workspace *ws; + wl_list_for_each(ws, &output->workspaces, link) { + uint32_t h = render_workspace_button( + cairo, config, ws, &x, output->height); + update_heights(h, &min_height, &max_height); + } + } + + // TODO: Shrink via min_height if sane + return max_height; } void render_frame(struct swaybar *bar, @@ -41,6 +124,7 @@ void render_frame(struct swaybar *bar, // Reconfigure surface zwlr_layer_surface_v1_set_size( output->layer_surface, 0, height); + zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); // TODO: this could infinite loop if the compositor assigns us a // different height than what we asked for wl_surface_commit(output->surface); -- cgit v1.2.3-70-g09d2 From 37b61eff2df3c8b47b1304650d1fb204a62658db Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 10:59:33 -0400 Subject: Add binding mode indicator --- swaybar/ipc.c | 1 + swaybar/render.c | 77 +++++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 55 insertions(+), 23 deletions(-) (limited to 'swaybar/render.c') diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 75f17953..3c2d6fbc 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -306,6 +306,7 @@ bool handle_ipc_event(struct swaybar *bar) { ipc_get_workspaces(bar); break; case IPC_EVENT_MODE: { + // TODO: interpret "pango_markup" field json_object *result = json_tokener_parse(resp->payload); if (!result) { free_ipc_response(resp); diff --git a/swaybar/render.c b/swaybar/render.c index 226e4ce3..beb4de40 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,6 +11,38 @@ #include "swaybar/render.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +static const int ws_horizontal_padding = 5; +static const double ws_vertical_padding = 1.5; +static const int ws_spacing = 1; + +static uint32_t render_binding_mode_indicator(cairo_t *cairo, + struct swaybar_config *config, const char *mode, double x, + uint32_t height) { + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, + 1, true, "⚡ %s", mode); + uint32_t ideal_height = text_height + ws_vertical_padding * 2; + if (height < ideal_height) { + height = ideal_height; + } + + cairo_set_source_u32(cairo, config->colors.binding_mode.background); + cairo_rectangle(cairo, x, 0, text_width + ws_horizontal_padding * 2 - 1, + height + ws_vertical_padding * 2); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, config->colors.binding_mode.border); + cairo_rectangle(cairo, x, 0, text_width + ws_horizontal_padding * 2 - 1, + height + ws_vertical_padding * 2); + cairo_stroke(cairo); + + double text_y = height / 2.0 - text_height / 2.0; + cairo_set_source_u32(cairo, config->colors.binding_mode.text); + cairo_move_to(cairo, (int)x + ws_horizontal_padding, (int)floor(text_y)); + pango_printf(cairo, config->font, 1, true, "⚡ %s", mode); + return ideal_height; +} + static const char *strip_workspace_number(const char *ws_name) { size_t len = strlen(ws_name); for (size_t i = 0; i < len; ++i) { @@ -26,10 +59,6 @@ static const char *strip_workspace_number(const char *ws_name) { static uint32_t render_workspace_button(cairo_t *cairo, struct swaybar_config *config, struct swaybar_workspace *ws, double *x, uint32_t height) { - static const int ws_horizontal_padding = 5; - static const double ws_vertical_padding = 1.5; - static const int ws_spacing = 1; - const char *name = ws->name; if (config->strip_workspace_numbers) { name = strip_workspace_number(ws->name); @@ -72,24 +101,10 @@ static uint32_t render_workspace_button(cairo_t *cairo, return ideal_height; } -static void update_heights(uint32_t height, uint32_t *min, uint32_t *max) { - if (*min < height) { - *min = height; - } - if (height > *max) { - *max = height; - } -} - static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, struct swaybar_output *output) { struct swaybar_config *config = bar->config; - cairo_save(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); - cairo_paint(cairo); - cairo_restore(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); if (output->focused) { cairo_set_source_u32(cairo, config->colors.focused_background); @@ -98,20 +113,30 @@ static uint32_t render_to_cairo(cairo_t *cairo, } cairo_paint(cairo); - uint32_t min_height = output->height, max_height = output->height; - + uint32_t max_height = 0; + /* + * Each render_* function takes the actual height of the bar, and returns + * the ideal height. If the actual height is too short, the render function + * can do whatever it wants - the buffer won't be committed. If the actual + * height is too tall, the render function should adapt its drawing to + * utilize the available space. + */ double x = 0; if (config->workspace_buttons) { struct swaybar_workspace *ws; wl_list_for_each(ws, &output->workspaces, link) { uint32_t h = render_workspace_button( cairo, config, ws, &x, output->height); - update_heights(h, &min_height, &max_height); + max_height = h > max_height ? h : max_height; } } + if (config->binding_mode_indicator && config->mode) { + uint32_t h = render_binding_mode_indicator( + cairo, config, config->mode, x, output->height); + max_height = h > max_height ? h : max_height; + } - // TODO: Shrink via min_height if sane - return max_height; + return max_height > output->height ? max_height : output->height; } void render_frame(struct swaybar *bar, @@ -134,6 +159,12 @@ void render_frame(struct swaybar *bar, output->current_buffer = get_next_buffer(bar->shm, output->buffers, output->width, output->height); cairo_t *shm = output->current_buffer->cairo; + + cairo_save(shm); + cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); + cairo_paint(shm); + cairo_restore(shm); + cairo_set_source_surface(shm, recorder, 0.0, 0.0); cairo_paint(shm); wl_surface_attach(output->surface, -- cgit v1.2.3-70-g09d2 From 1e8faeec0263a7da311a13c56a0de34e47e66fa6 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 11:58:54 -0400 Subject: Pixel-perfect rendering --- include/swaybar/config.h | 1 + swaybar/config.c | 1 + swaybar/ipc.c | 62 ++++++++++++++++++++++++++++++++---------------- swaybar/render.c | 48 ++++++++++++++++++++++++------------- 4 files changed, 75 insertions(+), 37 deletions(-) (limited to 'swaybar/render.c') diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 6bcefe64..7634cb16 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -24,6 +24,7 @@ struct swaybar_config { char *font; char *sep_symbol; char *mode; + bool mode_pango_markup; bool strip_workspace_numbers; bool binding_mode_indicator; bool wrap_scroll; diff --git a/swaybar/config.c b/swaybar/config.c index 83cf2309..802d0779 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -41,6 +41,7 @@ struct swaybar_config *init_config() { /* colors */ config->colors.background = 0x000000FF; + config->colors.focused_background = 0x000000FF; config->colors.statusline = 0xFFFFFFFF; config->colors.separator = 0x666666FF; diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 3c2d6fbc..a260b798 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -48,58 +48,76 @@ static void ipc_parse_colors( json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg); json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text); if (background) { - config->colors.background = parse_color(json_object_get_string(background)); + config->colors.background = parse_color( + json_object_get_string(background)); } if (statusline) { - config->colors.statusline = parse_color(json_object_get_string(statusline)); + config->colors.statusline = parse_color( + json_object_get_string(statusline)); } if (separator) { - config->colors.separator = parse_color(json_object_get_string(separator)); + config->colors.separator = parse_color( + json_object_get_string(separator)); } if (focused_background) { - config->colors.focused_background = parse_color(json_object_get_string(focused_background)); + config->colors.focused_background = parse_color( + json_object_get_string(focused_background)); } if (focused_statusline) { - config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline)); + config->colors.focused_statusline = parse_color( + json_object_get_string(focused_statusline)); } if (focused_separator) { - config->colors.focused_separator = parse_color(json_object_get_string(focused_separator)); + config->colors.focused_separator = parse_color( + json_object_get_string(focused_separator)); } if (focused_workspace_border) { - config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border)); + config->colors.focused_workspace.border = parse_color( + json_object_get_string(focused_workspace_border)); } if (focused_workspace_bg) { - config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg)); + config->colors.focused_workspace.background = parse_color( + json_object_get_string(focused_workspace_bg)); } if (focused_workspace_text) { - config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text)); + config->colors.focused_workspace.text = parse_color( + json_object_get_string(focused_workspace_text)); } if (active_workspace_border) { - config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border)); + config->colors.active_workspace.border = parse_color( + json_object_get_string(active_workspace_border)); } if (active_workspace_bg) { - config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg)); + config->colors.active_workspace.background = parse_color( + json_object_get_string(active_workspace_bg)); } if (active_workspace_text) { - config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text)); + config->colors.active_workspace.text = parse_color( + json_object_get_string(active_workspace_text)); } if (inactive_workspace_border) { - config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border)); + config->colors.inactive_workspace.border = parse_color( + json_object_get_string(inactive_workspace_border)); } if (inactive_workspace_bg) { - config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg)); + config->colors.inactive_workspace.background = parse_color( + json_object_get_string(inactive_workspace_bg)); } if (inactive_workspace_text) { - config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text)); + config->colors.inactive_workspace.text = parse_color( + json_object_get_string(inactive_workspace_text)); } if (binding_mode_border) { - config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border)); + config->colors.binding_mode.border = parse_color( + json_object_get_string(binding_mode_border)); } if (binding_mode_bg) { - config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg)); + config->colors.binding_mode.background = parse_color( + json_object_get_string(binding_mode_bg)); } if (binding_mode_text) { - config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text)); + config->colors.binding_mode.text = parse_color( + json_object_get_string(binding_mode_text)); } } @@ -306,14 +324,13 @@ bool handle_ipc_event(struct swaybar *bar) { ipc_get_workspaces(bar); break; case IPC_EVENT_MODE: { - // TODO: interpret "pango_markup" field json_object *result = json_tokener_parse(resp->payload); if (!result) { free_ipc_response(resp); wlr_log(L_ERROR, "failed to parse payload as json"); return false; } - json_object *json_change; + json_object *json_change, *json_pango_markup; if (json_object_object_get_ex(result, "change", &json_change)) { const char *change = json_object_get_string(json_change); free(bar->config->mode); @@ -328,6 +345,11 @@ bool handle_ipc_event(struct swaybar *bar) { free_ipc_response(resp); return false; } + if (json_object_object_get_ex(result, + "pango_markup", &json_pango_markup)) { + bar->config->mode_pango_markup = json_object_get_boolean( + json_pango_markup); + } json_object_put(result); break; } diff --git a/swaybar/render.c b/swaybar/render.c index beb4de40..ba22e9d4 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -13,33 +13,39 @@ static const int ws_horizontal_padding = 5; static const double ws_vertical_padding = 1.5; -static const int ws_spacing = 1; +static const double border_width = 1; static uint32_t render_binding_mode_indicator(cairo_t *cairo, struct swaybar_config *config, const char *mode, double x, uint32_t height) { int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, - 1, true, "⚡ %s", mode); - uint32_t ideal_height = text_height + ws_vertical_padding * 2; + 1, true, "%s", mode); + uint32_t ideal_height = text_height + ws_vertical_padding * 2 + + border_width * 2; if (height < ideal_height) { height = ideal_height; } + uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; cairo_set_source_u32(cairo, config->colors.binding_mode.background); - cairo_rectangle(cairo, x, 0, text_width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); + cairo_rectangle(cairo, x, 0, width, height); cairo_fill(cairo); cairo_set_source_u32(cairo, config->colors.binding_mode.border); - cairo_rectangle(cairo, x, 0, text_width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); - cairo_stroke(cairo); + cairo_rectangle(cairo, x, 0, width, border_width); + cairo_fill(cairo); + cairo_rectangle(cairo, x, 0, border_width, height); + cairo_fill(cairo); + cairo_rectangle(cairo, x + width - border_width, 0, border_width, height); + cairo_fill(cairo); + cairo_rectangle(cairo, x, height - border_width, width, border_width); + cairo_fill(cairo); double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, config->colors.binding_mode.text); - cairo_move_to(cairo, (int)x + ws_horizontal_padding, (int)floor(text_y)); - pango_printf(cairo, config->font, 1, true, "⚡ %s", mode); + cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); + pango_printf(cairo, config->font, 1, true, "%s", mode); return ideal_height; } @@ -78,26 +84,33 @@ static uint32_t render_workspace_button(cairo_t *cairo, int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, 1, true, "%s", name); - uint32_t ideal_height = ws_vertical_padding * 2 + text_height; + uint32_t ideal_height = ws_vertical_padding * 2 + text_height + + border_width * 2; if (height < ideal_height) { height = ideal_height; } - uint32_t width = ws_horizontal_padding * 2 + text_width; + uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2; cairo_set_source_u32(cairo, box_colors.background); - cairo_rectangle(cairo, *x, 0, width - 1, height); + cairo_rectangle(cairo, *x, 0, width, height); cairo_fill(cairo); cairo_set_source_u32(cairo, box_colors.border); - cairo_rectangle(cairo, *x, 0, width - 1, height); - cairo_stroke(cairo); + cairo_rectangle(cairo, *x, 0, width, border_width); + cairo_fill(cairo); + cairo_rectangle(cairo, *x, 0, border_width, height); + cairo_fill(cairo); + cairo_rectangle(cairo, *x + width - border_width, 0, border_width, height); + cairo_fill(cairo); + cairo_rectangle(cairo, *x, height - border_width, width, border_width); + cairo_fill(cairo); double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, box_colors.text); - cairo_move_to(cairo, (int)*x + ws_horizontal_padding, (int)floor(text_y)); + cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); pango_printf(cairo, config->font, 1, true, "%s", name); - *x += width + ws_spacing; + *x += width; return ideal_height; } @@ -167,6 +180,7 @@ void render_frame(struct swaybar *bar, cairo_set_source_surface(shm, recorder, 0.0, 0.0); cairo_paint(shm); + wl_surface_attach(output->surface, output->current_buffer->buffer, 0, 0); wl_surface_damage(output->surface, 0, 0, output->width, output->height); -- cgit v1.2.3-70-g09d2 From 531c175d3e60bf9dbfd538af1fd5eebb906c6f91 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 12:03:08 -0400 Subject: Respect user bar height preference This is an i3-gaps feature we support --- swaybar/render.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'swaybar/render.c') diff --git a/swaybar/render.c b/swaybar/render.c index ba22e9d4..317b0f65 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -158,6 +158,9 @@ void render_frame(struct swaybar *bar, CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *cairo = cairo_create(recorder); uint32_t height = render_to_cairo(cairo, bar, output); + if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { + height = bar->config->height; + } if (height != output->height) { // Reconfigure surface zwlr_layer_surface_v1_set_size( -- cgit v1.2.3-70-g09d2 From 718502c815d0af81b4ab71fb6b86976b5403adf9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 13:49:46 -0400 Subject: Iterate over workspaces backwards --- swaybar/render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'swaybar/render.c') diff --git a/swaybar/render.c b/swaybar/render.c index 317b0f65..f797873c 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -137,7 +137,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, double x = 0; if (config->workspace_buttons) { struct swaybar_workspace *ws; - wl_list_for_each(ws, &output->workspaces, link) { + wl_list_for_each_reverse(ws, &output->workspaces, link) { uint32_t h = render_workspace_button( cairo, config, ws, &x, output->height); max_height = h > max_height ? h : max_height; -- cgit v1.2.3-70-g09d2 From 0d0ab7c5ce148bce841fa0682d04bc7b6c21b902 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 15:16:12 -0400 Subject: Implement status line Does not yet support i3bar json protocol --- include/swaybar/bar.h | 1 + include/swaybar/status_line.h | 65 ++++++++++-------------------------- swaybar/bar.c | 26 ++++++++++++--- swaybar/config.c | 1 + swaybar/meson.build | 1 + swaybar/render.c | 56 +++++++++++++++++++++++++++++++ swaybar/status_line.c | 78 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 176 insertions(+), 52 deletions(-) create mode 100644 swaybar/status_line.c (limited to 'swaybar/render.c') diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index c89aa61c..1bf2ea2d 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -16,6 +16,7 @@ struct swaybar { struct swaybar_config *config; struct swaybar_output *focused_output; + struct status_line *status; int ipc_event_socketfd; int ipc_socketfd; diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index 0664ddee..6c595df0 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -1,61 +1,30 @@ #ifndef _SWAYBAR_STATUS_LINE_H #define _SWAYBAR_STATUS_LINE_H - #include +#include #include - -#include "list.h" #include "bar.h" -typedef enum {UNDEF, TEXT, I3BAR} command_protocol; +enum status_protocol { + PROTOCOL_UNDEF, + PROTOCOL_TEXT, + PROTOCOL_I3BAR, +}; struct status_line { - list_t *block_line; - const char *text_line; - command_protocol protocol; - bool click_events; -}; + pid_t pid; + int read_fd, write_fd; + FILE *read, *write; -struct status_block { - char *full_text, *short_text, *align; - bool urgent; - uint32_t color; - int min_width; - char *name, *instance; - bool separator; - int separator_block_width; - bool markup; - // Airblader features - uint32_t background; - uint32_t border; - int border_top; - int border_bottom; - int border_left; - int border_right; + enum status_protocol protocol; + const char *text; - // Set during rendering - int x; - int width; + char *buffer; + size_t buffer_size; }; -/** - * Initialize status line struct. - */ -struct status_line *init_status_line(); - -/** - * handle status line activity. - */ -bool handle_status_line(struct bar *bar); - -/** - * Handle mouse clicks. - */ -bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button); - -/** - * Free status line struct. - */ -void free_status_line(struct status_line *line); +struct status_line *status_line_init(char *cmd); +void status_line_free(struct status_line *status); +bool handle_status_readable(struct status_line *status); -#endif /* _SWAYBAR_STATUS_LINE_H */ +#endif diff --git a/swaybar/bar.c b/swaybar/bar.c index 90fd5ad4..72c4be8f 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -13,6 +13,7 @@ #include "swaybar/render.h" #include "swaybar/config.h" #include "swaybar/event_loop.h" +#include "swaybar/status_line.h" #include "swaybar/bar.h" #include "swaybar/ipc.h" #include "ipc-client.h" @@ -98,6 +99,9 @@ void bar_setup(struct swaybar *bar, bar->ipc_socketfd = ipc_open_socket(socket_path); bar->ipc_event_socketfd = ipc_open_socket(socket_path); ipc_initialize(bar, bar_id); + if (bar->config->status_command) { + bar->status = status_line_init(bar->config->status_command); + } assert(bar->display = wl_display_connect(NULL)); @@ -134,6 +138,13 @@ void bar_setup(struct swaybar *bar, } } +static void render_all_frames(struct swaybar *bar) { + struct swaybar_output *output; + wl_list_for_each(output, &bar->outputs, link) { + render_frame(bar, output); + } +} + static void display_in(int fd, short mask, void *_bar) { struct swaybar *bar = (struct swaybar *)_bar; if (wl_display_dispatch(bar->display) == -1) { @@ -144,16 +155,23 @@ static void display_in(int fd, short mask, void *_bar) { static void ipc_in(int fd, short mask, void *_bar) { struct swaybar *bar = (struct swaybar *)_bar; if (handle_ipc_event(bar)) { - struct swaybar_output *output; - wl_list_for_each(output, &bar->outputs, link) { - render_frame(bar, output); - } + render_all_frames(bar); + } +} + +static void status_in(int fd, short mask, void *_bar) { + struct swaybar *bar = (struct swaybar *)_bar; + if (handle_status_readable(bar->status)) { + render_all_frames(bar); } } void bar_run(struct swaybar *bar) { add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar); + if (bar->status) { + add_event(bar->status->read_fd, POLLIN, status_in, bar); + } while (1) { event_loop_poll(); } diff --git a/swaybar/config.c b/swaybar/config.c index 802d0779..9169ad27 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -43,6 +43,7 @@ struct swaybar_config *init_config() { config->colors.background = 0x000000FF; config->colors.focused_background = 0x000000FF; config->colors.statusline = 0xFFFFFFFF; + config->colors.focused_statusline = 0xFFFFFFFF; config->colors.separator = 0x666666FF; config->colors.focused_workspace.border = 0x4C7899FF; diff --git a/swaybar/meson.build b/swaybar/meson.build index 6dc7c564..d15e8b5c 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -7,6 +7,7 @@ executable( 'ipc.c', 'main.c', 'render.c', + 'status_line.c', ], include_directories: [sway_inc], dependencies: [ diff --git a/swaybar/render.c b/swaybar/render.c index f797873c..ec1239a1 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -9,12 +9,59 @@ #include "swaybar/bar.h" #include "swaybar/config.h" #include "swaybar/render.h" +#include "swaybar/status_line.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static const int ws_horizontal_padding = 5; static const double ws_vertical_padding = 1.5; static const double border_width = 1; +static uint32_t render_status_line_text(cairo_t *cairo, + struct swaybar_config *config, struct status_line *status, + bool focused, uint32_t width, uint32_t height) { + if (!status->text) { + return 0; + } + //wlr_log(L_DEBUG, "focused %d", focused); + cairo_set_source_u32(cairo, focused ? + config->colors.focused_statusline : config->colors.statusline); + static const int margin = 3; + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, + 1, config->pango_markup, "%s", status->text); + uint32_t ideal_height = text_height + ws_vertical_padding * 2; + if (height < ideal_height) { + height = ideal_height; + } + double text_y = height / 2.0 - text_height / 2.0; + cairo_move_to(cairo, width - text_width - margin, (int)floor(text_y)); + pango_printf(cairo, config->font, 1, config->pango_markup, + "%s", status->text); + return ideal_height; +} + +static uint32_t render_status_line_i3bar(cairo_t *cairo, + struct swaybar_config *config, struct status_line *status, + bool focused, uint32_t width, uint32_t height) { + // TODO + return 0; +} + +static uint32_t render_status_line(cairo_t *cairo, + struct swaybar_config *config, struct status_line *status, + bool focused, uint32_t width, uint32_t height) { + switch (status->protocol) { + case PROTOCOL_TEXT: + return render_status_line_text(cairo, + config, status, focused, width, height); + case PROTOCOL_I3BAR: + return render_status_line_i3bar(cairo, + config, status, focused, width, height); + default: + return 0; + } +} + static uint32_t render_binding_mode_indicator(cairo_t *cairo, struct swaybar_config *config, const char *mode, double x, uint32_t height) { @@ -148,6 +195,11 @@ static uint32_t render_to_cairo(cairo_t *cairo, cairo, config, config->mode, x, output->height); max_height = h > max_height ? h : max_height; } + if (bar->status) { + uint32_t h = render_status_line(cairo, config, bar->status, + output->focused, output->width, output->height); + max_height = h > max_height ? h : max_height; + } return max_height > output->height ? max_height : output->height; } @@ -157,6 +209,10 @@ void render_frame(struct swaybar *bar, cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *cairo = cairo_create(recorder); + cairo_save(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); + cairo_paint(cairo); + cairo_restore(cairo); uint32_t height = render_to_cairo(cairo, bar, output); if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { height = bar->config->height; diff --git a/swaybar/status_line.c b/swaybar/status_line.c new file mode 100644 index 00000000..ff668c9c --- /dev/null +++ b/swaybar/status_line.c @@ -0,0 +1,78 @@ +#define _POSIX_C_SOURCE +#include +#include +#include +#include +#include +#include +#include "swaybar/config.h" +#include "swaybar/status_line.h" +#include "readline.h" + +bool handle_status_readable(struct status_line *status) { + char *line = read_line_buffer(status->read, + status->buffer, status->buffer_size); + switch (status->protocol) { + case PROTOCOL_I3BAR: + // TODO + break; + case PROTOCOL_TEXT: + status->text = line; + return true; + case PROTOCOL_UNDEF: + if (!line) { + return false; + } + if (line[0] == '{') { + // TODO: JSON + } else { + status->text = line; + status->protocol = PROTOCOL_TEXT; + } + return false; + } + return false; +} + +struct status_line *status_line_init(char *cmd) { + struct status_line *status = calloc(1, sizeof(struct status_line)); + status->buffer_size = 4096; + status->buffer = malloc(status->buffer_size); + + int pipe_read_fd[2]; + int pipe_write_fd[2]; + if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) { + wlr_log(L_ERROR, "Unable to create pipes for status_command fork"); + exit(1); + } + + status->pid = fork(); + if (status->pid == 0) { + dup2(pipe_read_fd[1], STDOUT_FILENO); + close(pipe_read_fd[0]); + close(pipe_read_fd[1]); + + dup2(pipe_write_fd[0], STDIN_FILENO); + close(pipe_write_fd[0]); + close(pipe_write_fd[1]); + + char *const _cmd[] = { "sh", "-c", cmd, NULL, }; + execvp(_cmd[0], _cmd); + exit(1); + } + + close(pipe_read_fd[1]); + status->read_fd = pipe_read_fd[0]; + fcntl(status->read_fd, F_SETFL, O_NONBLOCK); + close(pipe_write_fd[0]); + status->write_fd = pipe_write_fd[1]; + fcntl(status->write_fd, F_SETFL, O_NONBLOCK); + + status->read = fdopen(status->read_fd, "r"); + status->write = fdopen(status->write_fd, "w"); + return status; +} + +void status_line_free(struct status_line *line) { + free(line); +} -- cgit v1.2.3-70-g09d2 From da6e48520bad9718a7c4ddf0591474d54736c1c2 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 15:36:52 -0400 Subject: Tear down bar when display exits --- swaybar/bar.c | 3 ++- swaybar/render.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'swaybar/render.c') diff --git a/swaybar/bar.c b/swaybar/bar.c index 44f4ee31..afbce7cc 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -148,7 +148,8 @@ static void render_all_frames(struct swaybar *bar) { static void display_in(int fd, short mask, void *_bar) { struct swaybar *bar = (struct swaybar *)_bar; if (wl_display_dispatch(bar->display) == -1) { - wlr_log(L_ERROR, "failed to dispatch wl: %d", errno); + bar_teardown(bar); + exit(0); } } diff --git a/swaybar/render.c b/swaybar/render.c index ec1239a1..a5834f4b 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -22,7 +22,6 @@ static uint32_t render_status_line_text(cairo_t *cairo, if (!status->text) { return 0; } - //wlr_log(L_DEBUG, "focused %d", focused); cairo_set_source_u32(cairo, focused ? config->colors.focused_statusline : config->colors.statusline); static const int margin = 3; -- cgit v1.2.3-70-g09d2 From d8104db8f1820bd3d4db8bf4f1ee51ae334ee6e7 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 23:35:49 -0400 Subject: Early return from render functions if necessary --- swaybar/render.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'swaybar/render.c') diff --git a/swaybar/render.c b/swaybar/render.c index a5834f4b..3d9ef66b 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -30,7 +30,7 @@ static uint32_t render_status_line_text(cairo_t *cairo, 1, config->pango_markup, "%s", status->text); uint32_t ideal_height = text_height + ws_vertical_padding * 2; if (height < ideal_height) { - height = ideal_height; + return ideal_height; } double text_y = height / 2.0 - text_height / 2.0; cairo_move_to(cairo, width - text_width - margin, (int)floor(text_y)); @@ -70,7 +70,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, uint32_t ideal_height = text_height + ws_vertical_padding * 2 + border_width * 2; if (height < ideal_height) { - height = ideal_height; + return ideal_height; } uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; @@ -133,7 +133,7 @@ static uint32_t render_workspace_button(cairo_t *cairo, uint32_t ideal_height = ws_vertical_padding * 2 + text_height + border_width * 2; if (height < ideal_height) { - height = ideal_height; + return ideal_height; } uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2; -- cgit v1.2.3-70-g09d2