From 7efff8c560aa0c4a834201da48f53e709519c14b Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Fri, 29 May 2026 11:23:54 +0200 Subject: Improve redraw --- src/icecc.cc | 335 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 220 insertions(+), 115 deletions(-) (limited to 'src/icecc.cc') diff --git a/src/icecc.cc b/src/icecc.cc index 2b16267..967488e 100644 --- a/src/icecc.cc +++ b/src/icecc.cc @@ -2,6 +2,7 @@ #include #include +#include #include "animation.hh" #include "args.hh" @@ -19,7 +20,11 @@ class IceccMonMon : public MonMon, virtual Monitor::Observer { public: IceccMonMon(std::shared_ptr const& looper, unsigned columns) : MonMon(looper), connected_(false), max_jobs_(0), jobs_(0), requests_(0), - force_columns_(columns) { + force_columns_(columns), + layout_w_(0), layout_h_(0), + pad_x_(0.0), pad_y_(0.0), margin_x_(0.0), margin_y_(0.0), + columns_(0), box_width_(0.0), box_height_c_(0.0), + all_dirty_(true) { } bool connect(Args const* args) override { @@ -41,8 +46,15 @@ protected: return "MonMon"; } + void mark_all_dirty() override { + all_dirty_ = true; + } + void width_changed() override { job_pattern_.reset(); + layout_w_ = 0; + layout_h_ = 0; + all_dirty_ = true; } #if FAKE_MONITOR @@ -61,9 +73,14 @@ private: unsigned requests; double x; std::shared_ptr animation; + pango::unique_layout name_layout; + int name_layout_width; // pango units; -1 means not yet set + bool name_dirty; + bool dirty; // needs redraw this frame explicit Machine(uint32_t id) - : id(id), jobs(0), requests(0), x(0.0) { + : id(id), jobs(0), requests(0), x(0.0), name_layout_width(-1), + name_dirty(true), dirty(true) { } Machine(Machine const&) = delete; @@ -81,6 +98,7 @@ private: void ticked(Animator*, Animation*, double value) override { x = value; + dirty = true; } void stopped(Animator*, Animation*) override { @@ -129,15 +147,18 @@ private: connected_ = false; stop_all_animations(); machines_.clear(); + by_id_.clear(); max_jobs_ = 0; jobs_ = 0; requests_ = 0; + all_dirty_ = true; if (!wnd_) return; - draw(); + request_draw(); break; case Monitor::CONNECTED: connected_ = true; - draw(); + all_dirty_ = true; + request_draw(); break; } } @@ -154,27 +175,37 @@ private: } void update(Machine* machine) { - auto old = machine->data.max_jobs; + auto const old_name = machine->data.name; + auto const old_max_jobs = machine->data.max_jobs; machine->data = monitor_->machine(machine->id); - if (old < machine->data.max_jobs) { - max_jobs_ += machine->data.max_jobs - old; + if (machine->data.name != old_name) + machine->name_dirty = true; + if (old_max_jobs < machine->data.max_jobs) { + max_jobs_ += machine->data.max_jobs - old_max_jobs; if (machine->jobs) animate(machine); - } else if (old > machine->data.max_jobs) { - max_jobs_ -= old - machine->data.max_jobs; + } else if (old_max_jobs > machine->data.max_jobs) { + max_jobs_ -= old_max_jobs - machine->data.max_jobs; if (machine->jobs) animate(machine); } } - void draw_content(cairo_t* cairo, PangoLayout* layout, uint16_t w, uint16_t h) - override { - auto const pad_x = std::min(9.0, w / 20.0), pad_y = pad_x; - auto const margin_x = std::min(7.5, w / 20.0), margin_y = margin_x; - auto y = pad_y; - auto const columns = force_columns_ ? force_columns_ : std::max(1, w / h); - auto const box_width = (w - pad_x) / columns - pad_x; - auto const box_height = box_height_ + margin_y * 2; + void draw_content(cairo_t* cairo, PangoLayout* /*layout*/, + uint16_t w, uint16_t h, + std::vector& dirty) override { + if (w != layout_w_ || h != layout_h_) { + pad_x_ = std::min(9.0, w / 20.0); + pad_y_ = pad_x_; + margin_x_ = std::min(7.5, w / 20.0); + margin_y_ = margin_x_; + columns_ = force_columns_ ? force_columns_ : std::max(1, w / h); + box_width_ = (w - pad_x_) / columns_ - pad_x_; + box_height_c_ = box_height_ + margin_y_ * 2; + job_pattern_.reset(); + layout_w_ = w; + layout_h_ = h; + } if (!job_pattern_) { - job_pattern_.reset(cairo_pattern_create_linear(0.0, 0.0, box_width, 0.0)); + job_pattern_.reset(cairo_pattern_create_linear(0.0, 0.0, box_width_, 0.0)); cairo_pattern_add_color_stop_rgb(job_pattern_.get(), 0.000000, 0.000000, 0.000000, 0.000000); cairo_pattern_add_color_stop_rgb(job_pattern_.get(), 0.594324, @@ -186,69 +217,113 @@ private: cairo_pattern_add_color_stop_rgb(job_pattern_.get(), 1.000000, 0.976471, 0.968627, 0.831373); } - pango_layout_set_width(layout, - (box_width - margin_x * 2) * PANGO_SCALE); + + bool const full = all_dirty_; + if (full) { + fill_background(cairo, {0, 0, w, h}); + all_dirty_ = false; + for (auto const& machine : machines_) machine->dirty = true; + } + + int const text_width_pu = + static_cast((box_width_ - margin_x_ * 2) * PANGO_SCALE); unsigned col = 0; + auto y = pad_y_; for (auto const& machine : machines_) { - pango_layout_set_text(layout, machine->data.name.data(), - machine->data.name.size()); - auto x = pad_x + col * (box_width + pad_x); - rounded_path(cairo, x, y, box_width, box_height); - cairo_set_source_rgba(cairo, 0.1, 0.1, 0.1, 0.7); - - if (machine->x > 0.0) { - cairo_fill_preserve(cairo); - auto old = cairo::unique_path(cairo_copy_path(cairo)); - cairo_new_path(cairo); - cairo_rectangle(cairo, x, y, machine->x * box_width, box_height); - cairo_clip(cairo); - cairo_append_path(cairo, old.get()); - cairo_matrix_t matrix; - cairo_matrix_init_translate(&matrix, -x, 0); - cairo_pattern_set_matrix(job_pattern_.get(), &matrix); - cairo_set_source(cairo, job_pattern_.get()); - cairo_fill(cairo); - cairo_reset_clip(cairo); - } else { - cairo_fill(cairo); - } - if (machine->requests > 0) { - auto radius = box_height / 10.0; - cairo_set_line_width(cairo, 1.5); - cairo_new_path(cairo); - cairo_move_to(cairo, x + box_width - radius, y); - cairo_rel_line_to(cairo, - -(machine->requests * (box_width - radius * 2)) - / requests_, 0); - cairo_set_source_rgb(cairo, 0, 0.6, 0); + auto const x = pad_x_ + col * (box_width_ + pad_x_); + auto const bx = static_cast(x); + auto const by = static_cast(y); + auto const bw = static_cast(box_width_ + 1); + auto const bh = static_cast(box_height_c_ + 1); + + if (machine->dirty) { + machine->dirty = false; + + if (!machine->name_layout) { + machine->name_layout = make_layout(); + machine->name_layout_width = -1; + machine->name_dirty = true; + } + if (machine->name_layout_width != text_width_pu) { + pango_layout_set_width(machine->name_layout.get(), text_width_pu); + machine->name_layout_width = text_width_pu; + machine->name_dirty = true; + } + if (machine->name_dirty) { + pango_layout_set_text(machine->name_layout.get(), + machine->data.name.data(), + machine->data.name.size()); + machine->name_dirty = false; + } + auto* ml = machine->name_layout.get(); + + if (!full) { + fill_background(cairo, {bx, by, bw, bh}); + dirty.push_back({bx, by, bw, bh}); + } + + rounded_path(cairo, x, y, box_width_, box_height_c_); + cairo_set_source_rgba(cairo, 0.1, 0.1, 0.1, 0.7); + + if (machine->x > 0.0) { + cairo_fill_preserve(cairo); + auto old = cairo::unique_path(cairo_copy_path(cairo)); + cairo_new_path(cairo); + cairo_rectangle(cairo, x, y, machine->x * box_width_, box_height_c_); + cairo_clip(cairo); + cairo_append_path(cairo, old.get()); + cairo_matrix_t matrix; + cairo_matrix_init_translate(&matrix, -x, 0); + cairo_pattern_set_matrix(job_pattern_.get(), &matrix); + cairo_set_source(cairo, job_pattern_.get()); + cairo_fill(cairo); + cairo_reset_clip(cairo); + } else { + cairo_fill(cairo); + } + if (machine->requests > 0) { + auto radius = box_height_c_ / 10.0; + cairo_set_line_width(cairo, 1.5); + cairo_new_path(cairo); + cairo_move_to(cairo, x + box_width_ - radius, y); + cairo_rel_line_to(cairo, + -(machine->requests * (box_width_ - radius * 2)) + / requests_, 0); + cairo_set_source_rgb(cairo, 0, 0.6, 0); + cairo_stroke(cairo); + } + + cairo_move_to(cairo, x + margin_x_, y + margin_y_); + pango_cairo_layout_path(cairo, ml); + cairo_set_source_rgb(cairo, 0.0, 0.0, 0.0); + cairo_set_line_cap(cairo, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width(cairo, 2.0); cairo_stroke(cairo); + cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0); + cairo_move_to(cairo, x + margin_x_, y + margin_y_); + pango_cairo_show_layout(cairo, ml); } - cairo_move_to(cairo, x + margin_x, y + margin_y); - pango_cairo_layout_path(cairo, layout); - cairo_set_source_rgb(cairo, 0.0, 0.0, 0.0); - cairo_set_line_cap(cairo, CAIRO_LINE_CAP_ROUND); - cairo_set_line_width(cairo, 2.0); - cairo_stroke(cairo); - cairo_set_source_rgb(cairo, 1.0, 1.0, 1.0); - cairo_move_to(cairo, x + margin_x, y + margin_y); - pango_cairo_show_layout(cairo, layout); - if (++col == columns) { + if (++col == columns_) { col = 0; - y += box_height + pad_y * 2; + y += box_height_c_ + pad_y_ * 2; if (y >= h) break; } } + + if (full) dirty.push_back({0, 0, w, h}); } void added_machine(Monitor*, uint32_t id) override { auto machine = std::make_unique(id); update(machine.get()); - machines_.emplace( + auto it = machines_.emplace( std::lower_bound(machines_.begin(), machines_.end(), machine, compare_machine), std::move(machine)); - draw(); + by_id_[id] = it->get(); + all_dirty_ = true; + request_draw(); } static bool compare_machine(std::unique_ptr const& m1, @@ -257,82 +332,98 @@ private: } void updated_machine(Monitor*, uint32_t id) override { - for (auto& machine : machines_) { - if (machine->id == id) { - auto old = machine->data.name; - update(machine.get()); - if (machine->data.name != old) { - // TODO: Perhaps remove and insert instead? - std::sort(machines_.begin(), machines_.end(), compare_machine); - } - draw(); - return; - } + auto bit = by_id_.find(id); + if (bit == by_id_.end()) { assert(false); return; } + auto* machine = bit->second; + auto const old_name = machine->data.name; + update(machine); + if (machine->data.name != old_name) { + // TODO: Perhaps remove and insert instead? + std::sort(machines_.begin(), machines_.end(), compare_machine); + all_dirty_ = true; + } else { + machine->dirty = true; } - assert(false); + request_draw(); } void removed_machine(Monitor*, uint32_t id) override { - for (auto it = machines_.begin(); it != machines_.end(); ++it) { - if ((*it)->id == id) { - max_jobs_ -= (*it)->data.max_jobs; - requests_ -= (*it)->requests; - jobs_ -= (*it)->jobs; - if (animator_) animator_->stop((*it)->animation.get()); - machines_.erase(it); - draw(); - return; - } + auto bit = by_id_.find(id); + if (bit == by_id_.end()) { assert(false); return; } + auto* machine = bit->second; + max_jobs_ -= machine->data.max_jobs; + requests_ -= machine->requests; + jobs_ -= machine->jobs; + if (animator_ && machine->animation) + animator_->stop(machine->animation.get()); + by_id_.erase(bit); + auto it = std::find_if(machines_.begin(), machines_.end(), + [id](std::unique_ptr const& m) { + return m->id == id; + }); + machines_.erase(it); + all_dirty_ = true; + request_draw(); + } + + void mark_request_machines_dirty() { + for (auto const& machine : machines_) { + if (machine->requests > 0) machine->dirty = true; } - assert(false); } void added_job(Monitor*, uint32_t source, uint32_t target) override { if (source == target) { - for (auto& machine : machines_) { - if (machine->id == source) { - ++machine->jobs; - animate(machine.get()); - } + auto it = by_id_.find(source); + if (it != by_id_.end()) { + ++it->second->jobs; + it->second->dirty = true; + animate(it->second); } } else { - for (auto& machine : machines_) { - if (machine->id == source) { - ++machine->requests; - } - if (machine->id == target) { - ++machine->jobs; - animate(machine.get()); - } + auto src = by_id_.find(source); + if (src != by_id_.end()) { + ++src->second->requests; + src->second->dirty = true; + } + auto tgt = by_id_.find(target); + if (tgt != by_id_.end()) { + ++tgt->second->jobs; + tgt->second->dirty = true; + animate(tgt->second); } ++requests_; + mark_request_machines_dirty(); } ++jobs_; - draw(); + request_draw(); } void removed_job(Monitor*, uint32_t source, uint32_t target) override { if (source == target) { - for (auto& machine : machines_) { - if (machine->id == source) { - --machine->jobs; - animate(machine.get()); - } + auto it = by_id_.find(source); + if (it != by_id_.end()) { + --it->second->jobs; + it->second->dirty = true; + animate(it->second); } } else { - for (auto& machine : machines_) { - if (machine->id == source) { - --machine->requests; - } - if (machine->id == target) { - --machine->jobs; - animate(machine.get()); - } + auto src = by_id_.find(source); + if (src != by_id_.end()) { + --src->second->requests; + src->second->dirty = true; + } + auto tgt = by_id_.find(target); + if (tgt != by_id_.end()) { + --tgt->second->jobs; + tgt->second->dirty = true; + animate(tgt->second); } --requests_; + mark_request_machines_dirty(); } --jobs_; - draw(); + request_draw(); } void animate(Machine* machine) { @@ -345,11 +436,25 @@ private: std::unique_ptr monitor_; bool connected_; std::vector> machines_; + std::unordered_map by_id_; unsigned max_jobs_; unsigned jobs_; unsigned requests_; cairo::unique_pattern job_pattern_; unsigned force_columns_; + + // Cached layout geometry (valid when layout_w_ / layout_h_ != 0) + uint16_t layout_w_; + uint16_t layout_h_; + double pad_x_; + double pad_y_; + double margin_x_; + double margin_y_; + unsigned columns_; + double box_width_; + double box_height_c_; + + bool all_dirty_; }; } // namespace -- cgit v1.3