#include "bar/renderer.h" #include "bar/gfx.h" #include "bar/config.h" #include "bar/modules.h" #include #include #include #include struct renderer { struct fcft_font *font_small; struct fcft_font *font_large; short max_segments; float *anim_w; float *anim_v; struct timespec last_render_ts; }; static int compute_baseline(int my, int mh, struct fcft_font *font) { return my + (mh + font->ascent - font->descent) / 2; } static int measure_segment(const segment_t *s, struct fcft_font *small, struct fcft_font *large) { struct fcft_font *f = s->font ? s->font : large; if (!s->is_multi_line) return measure_text(f, s->text); int a = measure_text(small, s->top); int b = measure_text(f, s->bottom); return b > a ? b : a; } renderer_t *renderer_init(short max_segments) { renderer_t *r = calloc(1, sizeof *r); if (!r) return nullptr; r->max_segments = max_segments; r->anim_w = calloc(max_segments, sizeof(float)); r->anim_v = calloc(max_segments, sizeof(float)); if (!r->anim_w || !r->anim_v) { if (r->anim_w) free(r->anim_w); if (r->anim_v) free(r->anim_v); return free(r), nullptr; } for (short i = 0; i < max_segments; i++) { r->anim_w[i] = -1.0f; r->anim_v[i] = 0.0f; } clock_gettime(CLOCK_MONOTONIC, &r->last_render_ts); if (!fcft_init(FCFT_LOG_COLORIZE_NEVER, false, FCFT_LOG_CLASS_NONE)) { return renderer_destroy(r), nullptr; } const char *small[] = {g_config.font_small, "monospace:size=11", nullptr}; const char *large[] = {g_config.font_large, "monospace:size=13.5", nullptr}; r->font_small = fcft_from_name(1, small, nullptr); r->font_large = fcft_from_name(1, large, nullptr); if (!r->font_small || !r->font_large) { renderer_destroy(r); return nullptr; } return r; } bool renderer_draw(renderer_t *r, platform_t *pf, const segment_t *segments, short count) { if (!platform_ready(pf)) return true; pixman_image_t *dest = nullptr; int bw = 0, bh = 0; if (!platform_begin_frame(pf, &dest, &bw, &bh)) return false; if (count > r->max_segments) count = r->max_segments; struct timespec now_ts; clock_gettime(CLOCK_MONOTONIC, &now_ts); float dt = (float)(now_ts.tv_sec - r->last_render_ts.tv_sec) + (float)(now_ts.tv_nsec - r->last_render_ts.tv_nsec) * 1e-9f; r->last_render_ts = now_ts; if (dt > 0.1f) dt = 0.1f; bool animating = false; const float tension = 300.0f; const float omega = sqrtf(tension); float *targets = malloc(r->max_segments * sizeof(float)); bool *active = calloc(r->max_segments, sizeof(bool)); if (!targets || !active) { if (targets) free(targets); if (active) free(active); return false; } for (short i = 0; i < r->max_segments; i++) { targets[i] = 0.0f; } const segment_t **lookup = calloc(r->max_segments, sizeof(segment_t *)); if (!lookup) { free(targets); free(active); return false; } for (short i = 0; i < count; i++) { const segment_t *seg = &segments[i]; int anim_idx = (seg->module_id * MAX_SEGMENTS_PER_MODULE) + seg->seg_id; if (anim_idx >= r->max_segments) continue; active[anim_idx] = true; lookup[anim_idx] = seg; targets[anim_idx] = (float)measure_segment(seg, r->font_small, r->font_large); if (r->anim_w[anim_idx] < 0.0f) { r->anim_w[anim_idx] = 0.0f; r->anim_v[anim_idx] = 0.0f; } } for (short i = 0; i < r->max_segments; i++) { if (r->anim_w[i] < 0.0f) continue; float target = targets[i]; float x0 = r->anim_w[i]; float v0 = r->anim_v[i]; float A = x0 - target; float B = v0 + omega * A; float exp_term = expf(-omega * dt); float next_w = target + (A + B * dt) * exp_term; float next_v = (B - omega * (A + B * dt)) * exp_term; if (fabsf(next_w - target) < 0.05f && fabsf(next_v) < 0.05f) { r->anim_w[i] = target; r->anim_v[i] = 0.0f; } else { r->anim_w[i] = next_w; r->anim_v[i] = next_v; animating = true; } if (!active[i] && r->anim_w[i] <= 0.0f) { r->anim_w[i] = -1.0f; r->anim_v[i] = 0.0f; } } int total = 0; for (short i = 0; i < r->max_segments; i++) { if (r->anim_w[i] < 0.0f) continue; int pad_l = g_config.module_padding_x; int pad_r = g_config.module_padding_x; bool connect_prev = false; if (lookup[i]) { pad_l = lookup[i]->connect_prev ? g_config.inner_padding : g_config.module_padding_x; pad_r = lookup[i]->connect_next ? g_config.inner_padding : g_config.module_padding_x; connect_prev = lookup[i]->connect_prev; } if (!connect_prev) total += g_config.module_gap; total += (int)r->anim_w[i] + pad_l + pad_r; } memset(pixman_image_get_data(dest), 0, (size_t)bw * (size_t)bh * 4); int x = (bw - total) / 2; for (short i = 0; i < r->max_segments; i++) { if (r->anim_w[i] < 0.0f) continue; int w = (int)r->anim_w[i]; int pad_l = g_config.module_padding_x; int pad_r = g_config.module_padding_x; bool connect_prev = false; const segment_t *seg = lookup[i]; if (!seg) { if (!connect_prev) x += g_config.module_gap; x += w + pad_l + pad_r; continue; } struct fcft_font *f = seg->font ? seg->font : r->font_large; int rl = seg->connect_prev ? 0 : g_config.module_radius; int rr = seg->connect_next ? 0 : g_config.module_radius; pad_l = seg->connect_prev ? g_config.inner_padding : g_config.module_padding_x; pad_r = seg->connect_next ? g_config.inner_padding : g_config.module_padding_x; connect_prev = seg->connect_prev; if (!connect_prev) x += g_config.module_gap; fill_pill(dest, x, 0, w + pad_l + pad_r, bh, rl, rr, seg->bg, seg->bg_image); if (seg->is_multi_line) { int top_x = x + pad_l + (seg->align_right ? (w - measure_text(r->font_small, seg->top)) : 0); if (seg->top) render_text(dest, r->font_small, seg->top, top_x, (bh / 2) - 4, seg->top_fg); if (seg->bottom) render_text(dest, f, seg->bottom, x + pad_l, (bh / 2) + 14, seg->bottom_fg); } else { render_text(dest, f, seg->text, x + pad_l, compute_baseline(0, bh, f), seg->fg); } x += w + pad_l + pad_r; } free(targets); free(active); free(lookup); platform_end_frame(pf); return animating; } void renderer_destroy(renderer_t *r) { if (!r) return; if (r->font_small) fcft_destroy(r->font_small); if (r->font_large) fcft_destroy(r->font_large); if (r->anim_w) free(r->anim_w); if (r->anim_v) free(r->anim_v); fcft_fini(); free(r); }