#include "bar/bar.h" #include #include #include #include #include #include #include #include "bar/binding.h" #include "bar/cell.h" #include "bar/text.h" #include "bar/types.h" #include "bar/windows.h" #include "bar/workspaces.h" #include "base/array.h" #include "base/color.h" #include "base/macros.h" #include "base/memory.h" #include "base/time.h" #include "core/listeners.h" static zdwm_bar_item_t *bar_add_item( zdwm_output_id_t output_id, bar_side_t *side, zdwm_bar_item_type_t item_type, void *config ) { auto item = array_push(side->items, side->count, side->capacity); item->api = item_type; auto create_state = item_type.create_state; if (create_state) item->state = create_state(output_id, config); return item; } static void bar_item_cleanup(zdwm_bar_item_t *item) { auto destroy_state = item->api.destroy_state; if (destroy_state) destroy_state(item->state); item->state = nullptr; for (size_t i = 0; i < item->count; ++i) { auto cell = &item->cells[i]; p_delete(&cell->text); p_delete(&cell->fg_text); p_delete(&cell->bg_text); } p_delete(&item->cells); p_clear(item, 1); } #define VALUE(value, fallback) ((value) ? (value) : (fallback)) void bar_output_add_workspace( bar_output_t *bar_output, zdwm_bar_config_t *config, listeners_t *listeners ) { bar_workspace_config_t workspace_config = { .cell_padding = VALUE(config->tag_cell_padding_x, config->padding_x), .indicator_width = VALUE(config->tag_indicator_width, 4), .bg = VALUE(config->tag_bg, config->bg), .fg = VALUE(config->tag_fg, config->fg), .active_bg = config->tag_active_bg, .active_fg = config->tag_active_fg, .urgent_bg = config->tag_urgent_bg, .urgent_fg = config->tag_urgent_fg, .layout_bg = VALUE(config->layout_bg, config->bg), .layout_fg = VALUE(config->layout_fg, config->fg), }; auto item = bar_add_item( bar_output->output_id, &bar_output->left, bar_workspace, &workspace_config ); item->cell_padding = workspace_config.cell_padding; item->indicator_width = workspace_config.indicator_width; bar_workspace_add_listeners(listeners, item->state); } static void bar_output_add_binding( bar_output_t *bar_output, zdwm_bar_config_t *config, listeners_t *listeners ) { bar_binding_config_t binding_config = { .show_default = config->binding_show_default, .cell_padding = VALUE(config->binding_padding_x, config->padding_x), .bg = VALUE(config->binding_mode_bg, config->bg), .fg = VALUE(config->binding_mode_fg, config->fg), }; auto item = bar_add_item( bar_output->output_id, &bar_output->left, bar_binding, &binding_config ); item->cell_padding = binding_config.cell_padding; bar_binding_add_listeners(listeners, item->state); } static void bar_output_add_windows( bar_output_t *bar_output, zdwm_bar_config_t *config, listeners_t *listeners ) { auto item = &bar_output->center; bar_windows_config_t window_config = { .cell_padding = VALUE(config->window_padding_x, config->padding_x), .bg = VALUE(config->window_bg, config->bg), .fg = VALUE(config->window_fg, config->fg), .focused_bg = VALUE(config->window_focused_bg, config->bg), .focused_fg = VALUE(config->window_focused_fg, config->fg), }; item->cell_padding = window_config.cell_padding; item->api = bar_windows; auto create_state = item->api.create_state; auto output_id = bar_output->output_id; if (create_state) item->state = create_state(output_id, &window_config); bar_windows_add_listeners(listeners, item->state); } void bar_init(bar_t *bar, listeners_t *listeners) { auto c = &bar->config; bar->ctx = text_context_create(c->font_family, c->font_size, c->dpi); for (size_t i = 0; i < bar->count; ++i) { auto bar_output = &bar->bars[i]; bar_output->height = bar->config.height; bar_output_add_workspace(bar_output, &bar->config, listeners); bar_output_add_binding(bar_output, &bar->config, listeners); bar_output_add_windows(bar_output, &bar->config, listeners); } bar->timerfd = time_create_monotonic_timerfd_by_fps(bar->config.fps); } static void bar_side_cleanup(bar_side_t *side) { for (size_t i = 0; i < side->count; ++i) bar_item_cleanup(&side->items[i]); p_delete(&side->items); p_clear(side, 1); } void bar_cleanup(bar_t *bar) { close(bar->timerfd); bar->timerfd = -1; text_context_destory(bar->ctx); bar->ctx = nullptr; for (size_t i = 0; i < bar->count; ++i) { auto bar_output = &bar->bars[i]; bar_side_cleanup(&bar_output->left); bar_side_cleanup(&bar_output->right); bar_item_cleanup(&bar_output->center); cairo_destroy(bar_output->cr); bar_output->cr = nullptr; } p_delete(&bar->bars); bar->count = 0; } static inline void bar_item_update(zdwm_bar_item_t *item) { auto update = item->api.update; if (!update) return; auto update_interval_ms = item->api.update_interval_ms; auto last_updated_time = item->last_updated_time; auto now = time_monotonic_ms(); if (now < last_updated_time + update_interval_ms) return; item->last_updated_time = now; update(item, &bar_cell_api, item->state); } static inline void bar_side_update(bar_side_t *side) { for (size_t i = 0; i < side->count; ++i) { bar_item_update(&side->items[i]); } } void bar_update(bar_t *bar) { for (size_t i = 0; i < bar->count; ++i) { auto bar_output = &bar->bars[i]; bar_side_update(&bar_output->left); bar_side_update(&bar_output->right); bar_item_update(&bar_output->center); } } static void bar_output_layout(bar_output_t *bar_output, text_context_t *ctx) { int32_t start = 0; auto end = bar_output->width; auto left = &bar_output->left; for (size_t i = 0; i < left->count; ++i) { auto item = &left->items[i]; item->region.start = start; for (size_t j = 0; j < item->count; ++j) { auto cell = &item->cells[j]; int32_t width = 0; text_context_get_text_size(ctx, cell->text, &width, nullptr); bar_x_region_t region = { .start = start, .end = start + width + item->cell_padding * 2, }; bar_cell_set_region(item, j, region); start = region.end; } item->region.end = start; } auto right = &bar_output->right; for (size_t i = right->count; i > 0; --i) { auto item_index = i - 1; auto item = &right->items[item_index]; item->region.end = end; for (size_t j = item->count; j > 0; --j) { auto cell_index = j - 1; auto cell = &item->cells[cell_index]; int32_t width = 0; text_context_get_text_size(ctx, cell->text, &width, nullptr); bar_x_region_t region = { .start = end - width - item->cell_padding * 2, .end = end, }; bar_cell_set_region(item, cell_index, region); end = region.start; } item->region.start = end; } auto center = &bar_output->center; if (center->count == 0) return; center->region = (bar_x_region_t){.start = start, .end = end}; auto width = (end - start) / (int32_t)center->count; for (size_t i = 0; i < center->count; ++i) { bar_x_region_t region = { .start = start, .end = start + width + center->cell_padding * 2, }; start = region.end; bar_cell_set_region(center, i, region); } } static void bar_item_draw( cairo_t *cr, zdwm_bar_item_t *item, text_context_t *ctx, int32_t height ) { for (size_t i = 0; i < item->count; ++i) { auto cell = &item->cells[i]; auto region = &cell->region; zdwm_rect_t cell_area = { .x = region->start, .y = 0, .width = region->end - region->start, .height = height, }; if (cell_area.width <= 0) { cell->dirty = false; continue; } draw_background(cr, &cell->bg, cell_area); if (cell->show_indicator) { auto size = MIN(item->indicator_width, cell_area.width); size = MIN(size, height); if (size > 0) { zdwm_rect_t indicator_area = { .x = region->start, .y = 0, .width = size, .height = size, }; draw_background(cr, &cell->fg, indicator_area); } } zdwm_rect_t text_area = { .x = cell_area.x + item->cell_padding, .y = 0, .width = cell_area.width - item->cell_padding * 2, .height = height, }; if (text_area.width > 0 && cell->text && cell->text[0] != '\0') { draw_text(cr, ctx, cell->text, &cell->fg, text_area); } cell->dirty = false; } item->dirty = false; } static bool bar_item_is_dirty(zdwm_bar_item_t *item) { if (item->dirty) return true; for (size_t i = 0; i < item->count; ++i) { auto cell = &item->cells[i]; if (cell->dirty) return true; } return false; } static bool bar_side_is_dirty(bar_side_t *side) { for (size_t j = 0; j < side->count; ++j) { auto item = &side->items[j]; if (bar_item_is_dirty(item)) return true; } return false; } static inline bool bar_output_is_dirty(bar_output_t *bar_output) { if (bar_side_is_dirty(&bar_output->left)) return true; if (bar_side_is_dirty(&bar_output->right)) return true; if (bar_item_is_dirty(&bar_output->center)) return true; return false; } static inline void clean_cairo_context(cairo_t *cr) { cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_restore(cr); } static void bar_output_draw(bar_output_t *bar_output, text_context_t *ctx, color_t *bg) { auto cr = bar_output->cr; clean_cairo_context(cr); zdwm_rect_t output_area = { .x = 0, .y = 0, .width = bar_output->width, .height = bar_output->height, }; draw_background(cr, bg, output_area); auto left = &bar_output->left; for (size_t i = 0; i < left->count; ++i) { bar_item_draw(cr, &left->items[i], ctx, bar_output->height); } auto right = &bar_output->right; for (size_t i = 0; i < right->count; ++i) { bar_item_draw(cr, &right->items[i], ctx, bar_output->height); } bar_item_draw(cr, &bar_output->center, ctx, bar_output->height); } bool bar_draw(bar_t *bar) { auto ctx = bar->ctx; bool changed = false; for (size_t i = 0; i < bar->count; ++i) { auto bar_output = &bar->bars[i]; if (!bar_output_is_dirty(bar_output)) continue; bar_output_layout(bar_output, ctx); bar_output_draw(bar_output, ctx, &bar->palette.bg); changed = true; } return changed; }