#include #include "state.h" #include "log.h" State *state_create(const char *label, Window *win, Drawer *draw) { State *state = g_malloc0(sizeof(State)); g_assert_nonnull(state); state->label = label ? label : "comet"; state->win = win; state->draw = draw; return state; } void state_add_button(State *state, Button *btn) { state->btns = g_list_append(state->btns, btn); } void state_remove_button(State *state, Button *btn) { state->btns = g_list_remove(state->btns, btn); } static gint align_compare(gconstpointer a, gconstpointer b) { PangoAlignment a_align = CAST(a, Button)->align; PangoAlignment b_align = CAST(b, Button)->align; if (a_align < b_align) return -1; if (a_align > b_align) return 1; return 0; } void state_order_button(State *state) { state->btns = g_list_sort(state->btns, align_compare); } static gboolean redraw_handler(State *state) { log_debug("Redrawing once [relayout=%d, state=\"%s\"]", state->relayout, state->label); if (state->relayout) { draw_compute_layout(state->draw, state->win, state->btns); state->relayout = false; } draw_paint(state->draw, state->win); state->idle_id = 0; return G_SOURCE_REMOVE; } void state_request_redraw(State *state, bool relayout) { // Override old redraw if (state->idle_id != 0) g_source_remove(state->idle_id); state->relayout = relayout; if (state->anim_id != 0) return; state->idle_id = g_idle_add(G_SOURCE_FUNC(redraw_handler), state); } static bool anim_check_more(State *state, GList *btns) { bool more = false; for (GList *it = btns; it; it = it->next) { Button *btn = it->data; if (btn->anim != NULL) { bool layout = btn->anim->layout_func != NULL; bool before = btn->anim->before_func != NULL; bool after = btn->anim->after_func != NULL; // Remove animation if all functions finished if (!layout && !before && !after) { log_debug("Removing animation [type=%d, end=%ld, button=%p, state=\"%s\"]", btn->anim->type, btn->anim->start + btn->anim->duration, btn, state->label); g_clear_pointer(&btn->anim, animation_destroy); } else { more = true; state->relayout |= layout; } } if (!btn->simple && anim_check_more(state, CAST(btn, ButtonGroup)->children)) more = true; } return more; } static gboolean anim_handler(State *state) { bool more = anim_check_more(state, state->btns); log_debug("Redrawing animation [relayout=%d, more=%d, state=\"%s\"]", state->relayout, more, state->label); if (state->relayout) { draw_compute_layout(state->draw, state->win, state->btns); state->relayout = false; } draw_paint(state->draw, state->win); if (!more) state->anim_id = 0; return more; } // FIXME: When spamming animations there seem to be occasional // race condition that block animations from playing // (the animation timeout eagerly quits) void state_request_animation(State *state) { if (state->idle_id != 0) { g_source_remove(state->idle_id); state->idle_id = 0; } if (state->anim_id != 0) return; if (!anim_handler(state)) return; const int fps = 60; state->anim_id = g_timeout_add(1000 / fps, G_SOURCE_FUNC(anim_handler), state); } void state_destroy(State *state) { g_free(state); } // vim: ts=4 sw=4 et