diff options
| -rw-r--r-- | src/goma.cc | 5 | ||||
| -rw-r--r-- | src/icecc.cc | 333 | ||||
| -rw-r--r-- | src/monmon.cc | 47 | ||||
| -rw-r--r-- | src/monmon.hh | 15 |
4 files changed, 271 insertions, 129 deletions
diff --git a/src/goma.cc b/src/goma.cc index 47f0a43..bc7cdd4 100644 --- a/src/goma.cc +++ b/src/goma.cc @@ -211,9 +211,12 @@ private: } void draw_content(cairo_t* cairo, PangoLayout* layout, - uint16_t w, uint16_t h) override { + uint16_t w, uint16_t h, + std::vector<xcb_rectangle_t>& dirty) override { if (!poll_) return; auto state = poll_->state(); + fill_background(cairo, {0, 0, w, h}); + dirty.push_back({0, 0, w, h}); 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; 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 <algorithm> #include <math.h> +#include <unordered_map> #include "animation.hh" #include "args.hh" @@ -19,7 +20,11 @@ class IceccMonMon : public MonMon, virtual Monitor::Observer { public: IceccMonMon(std::shared_ptr<PollLooper> 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<MachineAnimation> 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<xcb_rectangle_t>& 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<int>((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); + auto const x = pad_x_ + col * (box_width_ + pad_x_); + auto const bx = static_cast<int16_t>(x); + auto const by = static_cast<int16_t>(y); + auto const bw = static_cast<uint16_t>(box_width_ + 1); + auto const bh = static_cast<uint16_t>(box_height_c_ + 1); - 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); + 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<Machine>(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<Machine> 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<Machine> 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> monitor_; bool connected_; std::vector<std::unique_ptr<Machine>> machines_; + std::unordered_map<uint32_t, Machine*> 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 diff --git a/src/monmon.cc b/src/monmon.cc index ef341df..2c359e4 100644 --- a/src/monmon.cc +++ b/src/monmon.cc @@ -22,7 +22,7 @@ MonMon::MonMon(std::shared_ptr<PollLooper> const& looper) : looper_(looper), animator_(Animator::create(looper)), depth_(0), black_pixel_(0), screen_(nullptr), x_(0), y_(0), w_(0), h_(0), rootpmap_(XCB_ATOM_NONE), desktop_window_(XCB_NONE), - desktop_pixmap_(XCB_NONE) { + desktop_pixmap_(XCB_NONE), draw_requested_(false) { pipe_.open(); looper_->add(pipe_.read(), Looper::EV_READ, std::bind(&MonMon::pipe, this, std::placeholders::_1, @@ -185,6 +185,7 @@ void MonMon::configure(int16_t x, int16_t y, uint16_t w, uint16_t h) { y_ = y; if (desktop_surface_) { draw = true; + mark_all_dirty(); io::write(pipe_, "d", 1); } } @@ -366,6 +367,7 @@ void MonMon::update_desktop_pixmap() { desktop_pixmap_ = pixmap; if (desktop_pixmap_ == XCB_NONE) desktop_surface_.reset(); + mark_all_dirty(); io::write(pipe_, "d", 1); } @@ -375,11 +377,35 @@ void MonMon::close_pipe() { pipe_.reset(); } +pango::unique_layout MonMon::make_layout() const { + return pango::unique_layout(pango_layout_copy(layout_.get())); +} + +void MonMon::fill_background(cairo_t* cairo, xcb_rectangle_t const& r) { + cairo_save(cairo); + cairo_rectangle(cairo, r.x, r.y, r.width, r.height); + cairo_clip(cairo); + if (!desktop_surface_) { + cairo_set_source_rgb(cairo, 0, 0, 0); + } else { + cairo_set_source_surface(cairo, desktop_surface_.get(), -x_, -y_); + } + cairo_paint(cairo); + cairo_restore(cairo); +} + +void MonMon::request_draw() { + if (draw_requested_ || !pipe_) return; + draw_requested_ = true; + io::write(pipe_, "d", 1); +} + void MonMon::pipe(Looper*, int, uint8_t event) { if (event & Looper::EV_READ) { char tmp; if (io::read(pipe_, &tmp, 1)) { if (tmp == 'd') { + draw_requested_ = false; force_draw(); return; } @@ -424,19 +450,14 @@ void MonMon::rounded_path(cairo_t* cr, double x, double y, void MonMon::internal_draw() { std::lock_guard<std::mutex> lock(mutex_); - if (!desktop_surface_) { - cairo_set_source_rgb(cairo_.get(), 0, 0, 0); - } else { - cairo_set_source_surface(cairo_.get(), desktop_surface_.get(), -x_, -y_); - } - cairo_rectangle(cairo_.get(), 0, 0, w_, h_); - cairo_fill(cairo_.get()); - - draw_content(cairo_.get(), layout_.get(), w_, h_); - + std::vector<xcb_rectangle_t> dirty; + draw_content(cairo_.get(), layout_.get(), w_, h_, dirty); + if (dirty.empty()) return; cairo_surface_flush(surface_.get()); - xcb_copy_area(wnd_.conn(), pixmap_.get(), wnd_.get(), gcontext_.get(), - 0, 0, 0, 0, w_, h_); + for (auto const& r : dirty) { + xcb_copy_area(wnd_.conn(), pixmap_.get(), wnd_.get(), gcontext_.get(), + r.x, r.y, r.x, r.y, r.width, r.height); + } } void MonMon::force_draw() { diff --git a/src/monmon.hh b/src/monmon.hh index 0699f57..c02cae6 100644 --- a/src/monmon.hh +++ b/src/monmon.hh @@ -3,6 +3,7 @@ #include <memory> #include <mutex> +#include <vector> #include "animator.hh" #include "cairo.hh" @@ -63,13 +64,24 @@ protected: virtual void internal_quit() { } + virtual void mark_all_dirty() { + } + virtual void draw_content(cairo_t* cairo, PangoLayout* layout, - uint16_t w, uint16_t h) = 0; + uint16_t w, uint16_t h, + std::vector<xcb_rectangle_t>& dirty) = 0; static void rounded_path(cairo_t* cr, double x, double y, double width, double height); void draw(); + void request_draw(); + + // Clone the base layout (font, alignment, ellipsize) for per-item use + pango::unique_layout make_layout() const; + + // Fill the background (desktop or black) for a given rectangle on the cairo surface + void fill_background(cairo_t* cairo, xcb_rectangle_t const& r); std::shared_ptr<PollLooper> looper_; std::unique_ptr<Animator> animator_; @@ -109,6 +121,7 @@ private: xcb_window_t desktop_window_; xcb_pixmap_t desktop_pixmap_; cairo::unique_surface desktop_surface_; + bool draw_requested_; }; |
