summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/goma.cc5
-rw-r--r--src/icecc.cc333
-rw-r--r--src/monmon.cc47
-rw-r--r--src/monmon.hh15
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_;
};