diff options
Diffstat (limited to 'src/icecc.cc')
| -rw-r--r-- | src/icecc.cc | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/src/icecc.cc b/src/icecc.cc new file mode 100644 index 0000000..5e99981 --- /dev/null +++ b/src/icecc.cc @@ -0,0 +1,357 @@ +#include "common.hh" + +#include <algorithm> +#include <math.h> + +#include "animation.hh" +#include "args.hh" +#include "blissful_monitor.hh" +#include "cairo.hh" +#include "fake_monitor.hh" +#include "icecc.hh" +#include "monitor.hh" +#include "pango.hh" + +namespace { + +class IceccMonMonImpl : public IceccMonMon, virtual Monitor::Observer { +public: + IceccMonMonImpl(std::shared_ptr<PollLooper> const& looper, unsigned columns) + : IceccMonMon(looper), connected_(false), max_jobs_(0), jobs_(0), + requests_(0), force_columns_(columns) { + } + + void connect(Args const* args) override { + std::unique_ptr<Monitor> monitor; +#if FAKE_MONITOR + monitor = FakeMonitor::create(looper_); +#else + monitor = Monitor::create(looper_); +#endif + monitor_ = BlissfulMonitor::create(looper_, std::move(monitor)); + monitor_->add_observer(this); + monitor_->connect(args->arg("network", ""), + args->arg("scheduler", "")); + } + +protected: + void width_changed() override { + job_pattern_.reset(); + } + +#if FAKE_MONITOR + void do_toggle_fakes() override { + monitor_->toggle_fakes(); + } +#endif + +private: + class MachineAnimation; + + struct Machine : virtual Animator::AnimationObserver { + uint32_t id; + Monitor::Machine data; + unsigned jobs; + unsigned requests; + double x; + std::shared_ptr<MachineAnimation> animation; + + explicit Machine(uint32_t id) + : id(id), jobs(0), requests(0), x(0.0) { + } + + Machine(Machine const&) = delete; + Machine& operator=(Machine const&) = delete; + + ~Machine() override { + assert(!animation); + } + + bool operator<(Machine const& machine) const { + if (data.name < machine.data.name) return true; + if (data.name > machine.data.name) return false; + return id < machine.id; + } + + void ticked(Animator*, Animation*, double value) override { + x = value; + } + + void stopped(Animator*, Animation*) override { + x = static_cast<double>(jobs) / data.max_jobs; + animation.reset(); + } + }; + + class MachineAnimation : public Animation { + public: + explicit MachineAnimation(Machine* machine) + : machine_(machine), last_(0.0) { + } + + bool tick(double duration, double* value) override { + auto target = static_cast<double>(machine_->jobs) + / machine_->data.max_jobs; + if (duration == 0.0) { + last_ = duration; + *value = machine_->x; + return machine_->x != target; + } else { + double diff = target - machine_->x; + double max = (duration - last_) * .5; + last_ = duration; + if (fabs(diff) > max) { + diff = diff < 0.0 ? -max : max; + *value = machine_->x + diff; + return true; + } else { + *value = target; + return false; + } + } + } + + private: + Machine* machine_; + double last_; + }; + + + void state(Monitor*, Monitor::State state) override { + switch (state) { + case Monitor::SEARCHING: + connected_ = false; + stop_all_animations(); + machines_.clear(); + max_jobs_ = 0; + jobs_ = 0; + requests_ = 0; + if (!wnd_) return; + draw(); + break; + case Monitor::CONNECTED: + connected_ = true; + draw(); + break; + } + } + + void stop_all_animations() override { + if (!animator_) return; + for (auto& machine : machines_) { + if (machine->animation) animator_->stop(machine->animation.get()); + } + } + + void internal_quit() override { + monitor_->disconnect(); + } + + void update(Machine* machine) { + auto old = 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->jobs) animate(machine); + } else if (old > machine->data.max_jobs) { + max_jobs_ -= old - 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; + if (!job_pattern_) { + 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, + 0.729412, 0.000000, 0.000000); + cairo_pattern_add_color_stop_rgb(job_pattern_.get(), 0.809683, + 1.000000, 0.545098, 0.196078); + cairo_pattern_add_color_stop_rgb(job_pattern_.get(), 0.899833, + 0.972549, 0.937255, 0.074510); + 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); + unsigned col = 0; + 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); + cairo_stroke(cairo); + } + + 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) { + col = 0; + y += box_height + pad_y * 2; + if (y >= h) break; + } + } + } + + void added_machine(Monitor*, uint32_t id) override { + auto machine = std::make_unique<Machine>(id); + update(machine.get()); + machines_.emplace( + std::lower_bound(machines_.begin(), machines_.end(), machine, + compare_machine), + std::move(machine)); + draw(); + } + + static bool compare_machine(std::unique_ptr<Machine> const& m1, + std::unique_ptr<Machine> const& m2) { + return *m1 < *m2; + } + + 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; + } + } + assert(false); + } + + 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; + } + } + 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()); + } + } + } else { + for (auto& machine : machines_) { + if (machine->id == source) { + ++machine->requests; + } + if (machine->id == target) { + ++machine->jobs; + animate(machine.get()); + } + } + ++requests_; + } + ++jobs_; + 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()); + } + } + } else { + for (auto& machine : machines_) { + if (machine->id == source) { + --machine->requests; + } + if (machine->id == target) { + --machine->jobs; + animate(machine.get()); + } + } + --requests_; + } + --jobs_; + draw(); + } + + void animate(Machine* machine) { + if (!animator_) return; + if (machine->animation) return; + machine->animation = std::make_unique<MachineAnimation>(machine); + animator_->start(machine->animation, machine); + } + + std::unique_ptr<Monitor> monitor_; + bool connected_; + std::vector<std::unique_ptr<Machine>> machines_; + unsigned max_jobs_; + unsigned jobs_; + unsigned requests_; + cairo::unique_pattern job_pattern_; + unsigned force_columns_; +}; + +} // namespace + +IceccMonMon::IceccMonMon(std::shared_ptr<PollLooper> const& looper) + : MonMon(looper) {} + +std::unique_ptr<IceccMonMon> create_icecc_monmon( + std::shared_ptr<PollLooper> const& looper, unsigned columns) { + return std::make_unique<IceccMonMonImpl>(looper, columns); +} |
