summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cairo.hh50
-rw-r--r--src/main.cc614
-rw-r--r--src/monmon.cc455
-rw-r--r--src/monmon.hh109
-rw-r--r--src/pango.hh41
5 files changed, 711 insertions, 558 deletions
diff --git a/src/cairo.hh b/src/cairo.hh
new file mode 100644
index 0000000..a53cee9
--- /dev/null
+++ b/src/cairo.hh
@@ -0,0 +1,50 @@
+#ifndef CAIRO_HH
+#define CAIRO_HH
+
+#include <cairo-xcb.h>
+#include <memory>
+
+namespace cairo {
+
+namespace priv {
+
+struct CairoSurfaceDelete {
+ void operator()(cairo_surface_t* surface) const {
+ cairo_surface_destroy(surface);
+ }
+};
+
+struct CairoDelete {
+ void operator()(cairo_t* cairo) const {
+ cairo_destroy(cairo);
+ }
+};
+
+struct CairoPatternDelete {
+ void operator()(cairo_pattern_t* pattern) const {
+ cairo_pattern_destroy(pattern);
+ }
+};
+
+struct CairoPathDelete {
+ void operator()(cairo_path_t* path) const {
+ cairo_path_destroy(path);
+ }
+};
+
+} // namespace priv
+
+typedef std::unique_ptr<cairo_surface_t, priv::CairoSurfaceDelete>
+ unique_surface;
+
+typedef std::unique_ptr<cairo_path_t, priv::CairoPathDelete>
+ unique_path;
+
+typedef std::unique_ptr<cairo_pattern_t, priv::CairoPatternDelete>
+ unique_pattern;
+
+typedef std::unique_ptr<cairo_t, priv::CairoDelete> unique;
+
+} // namespace cairo
+
+#endif // CAIRO_HH
diff --git a/src/main.cc b/src/main.cc
index f7b3e58..c8be11c 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -1,11 +1,8 @@
#include "common.hh"
#include <algorithm>
-#include <cairo-xcb.h>
#include <iostream>
#include <math.h>
-#include <mutex>
-#include <pango/pangocairo.h>
#include <string.h>
#include <thread>
#include <xcb/xcb_event.h>
@@ -16,210 +13,22 @@
#include "animator.hh"
#include "args.hh"
#include "blissful_monitor.hh"
+#include "cairo.hh"
#include "fake_monitor.hh"
#include "io.hh"
#include "monitor.hh"
+#include "monmon.hh"
+#include "pango.hh"
#include "poll_looper.hh"
#include "x.hh"
namespace {
-struct CairoSurfaceDelete {
- void operator()(cairo_surface_t* surface) const {
- cairo_surface_destroy(surface);
- }
-};
-
-struct CairoDelete {
- void operator()(cairo_t* cairo) const {
- cairo_destroy(cairo);
- }
-};
-
-struct GObjectDelete {
- void operator()(gpointer ptr) const {
- g_object_unref(ptr);
- }
-};
-
-struct PangoFontMetricsDelete {
- void operator()(PangoFontMetrics* ptr) const {
- pango_font_metrics_unref(ptr);
- }
-};
-
-struct PangoFontDescriptionDelete {
- void operator()(PangoFontDescription* ptr) const {
- pango_font_description_free(ptr);
- }
-};
-
-struct CairoPatternDelete {
- void operator()(cairo_pattern_t* pattern) const {
- cairo_pattern_destroy(pattern);
- }
-};
-
-struct CairoPathDelete {
- void operator()(cairo_path_t* path) const {
- cairo_path_destroy(path);
- }
-};
-
-struct MwmHints {
- uint32_t flags;
- uint32_t functions;
- uint32_t decorations;
- int32_t input_mode;
- uint32_t status;
-};
-
-class MonMon : virtual Monitor::Observer, virtual Animator::Observer {
+class IceccMonMon : public MonMon, virtual Monitor::Observer {
public:
- MonMon(std::shared_ptr<PollLooper> const& looper, unsigned columns)
- : looper_(looper), animator_(Animator::create(looper)),
- connected_(false), depth_(0), black_pixel_(0),
- screen_(nullptr), max_jobs_(0), jobs_(0), requests_(0),
- x_(0), y_(0), w_(0), h_(0), force_columns_(columns),
- rootpmap_(XCB_ATOM_NONE), desktop_window_(XCB_NONE),
- desktop_pixmap_(XCB_NONE) {
- pipe_.open();
- looper_->add(pipe_.read(), Looper::EV_READ,
- std::bind(&MonMon::pipe, this, std::placeholders::_1,
- std::placeholders::_2, std::placeholders::_3));
- animator_->add_observer(this);
- }
-
- ~MonMon() {
- stop_all_animations();
- unset_desktop_window();
- close_pipe();
- wnd_.reset();
- }
-
- void init(x::shared_connection const& conn, xcb_screen_t const* screen,
- x::Format format, std::shared_ptr<x::Atoms> const& atoms,
- std::shared_ptr<x::Ewmh> const& ewmh,
- uint16_t width, uint16_t height, bool normal, bool black) {
- screen_ = screen;
- wnd_.reset(conn);
- pixmap_.reset(conn);
- gcontext_.reset(conn);
- cmap_.reset();
-
- uint32_t values[4];
- size_t i = 0;
- uint32_t mask = XCB_CW_BACK_PIXEL;
- values[i++] = screen->black_pixel;
- if (format.need_border()) {
- mask |= XCB_CW_BORDER_PIXEL;
- values[i++] = screen->black_pixel;
- }
- mask |= XCB_CW_EVENT_MASK;
- values[i++] = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_EXPOSURE
- | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
- if (format.need_colormap()) {
- mask |= XCB_CW_COLORMAP;
- cmap_.reset(conn);
- xcb_create_colormap(cmap_.conn(), XCB_COLORMAP_ALLOC_NONE, cmap_.get(),
- screen->root, format.visual->visual_id);
- values[i++] = cmap_.get();
- }
- xcb_create_window(wnd_.conn(), format.depth,
- wnd_.get(), screen->root,
- 0, 0, width, height, 0,
- XCB_WINDOW_CLASS_INPUT_OUTPUT,
- format.visual->visual_id,
- mask, values);
- if (!normal) {
- if (atoms->get("_MOTIF_WM_INFO", false) != XCB_ATOM_NONE) {
- auto atom = atoms->get("_MOTIF_WM_HINTS");
- MwmHints hints;
- hints.flags = 1 << 1; // MWM_HINTS_DECORATIONS
- hints.decorations = 0;
- xcb_change_property(wnd_.conn(), XCB_PROP_MODE_REPLACE, wnd_.get(),
- atom, atom, 32, sizeof(hints) / 4, &hints);
- } else {
-#ifndef NDEBUG
- // There is no way to know how the window manager will implement
- // utility window type, some might show them without titlebar ...
- std::cerr << "Warning: No motif support detected, falling back to "
- << "NETWM utility window type." << std::endl;
-#endif
- auto wm = ewmh->conn();
- if (wm && ewmh->supported(wm->_NET_WM_WINDOW_TYPE_UTILITY)) {
- xcb_atom_t values[1];
- values[0] = wm->_NET_WM_WINDOW_TYPE_UTILITY;
- xcb_ewmh_set_wm_window_type(wm, wnd_.get(), 1, values);
- } else {
- // TODO: Fallback to override-redirect?
- }
- }
- }
- if (!black) {
- rootpmap_ = atoms->get("_XROOTPMAP_ID", false);
- if (rootpmap_ == XCB_ATOM_NONE) {
- std::cerr << "Warning: No support for Eterm style background pixmap"
- << std::endl;
- }
- }
- auto wm = ewmh->conn();
- if (wm) {
- xcb_ewmh_set_wm_name(wm, wnd_.get(), 6, "MonMon");
- } else {
- xcb_icccm_set_wm_name(wnd_.conn(), wnd_.get(), XCB_ATOM_STRING, 8, 6, "MonMon");
- }
- xcb_create_gc(gcontext_.conn(), gcontext_.get(), wnd_.get(), 0, nullptr);
- auto atom = atoms->get("WM_DELETE_WINDOW");
- xcb_icccm_set_wm_protocols(wnd_.conn(), wnd_.get(),
- atoms->get("WM_PROTOCOLS"),
- 1, &atom);
- xcb_create_pixmap(pixmap_.conn(), format.depth, pixmap_.get(), wnd_.get(),
- width, height);
- if (format.render) {
- surface_.reset(
- cairo_xcb_surface_create_with_xrender_format(
- pixmap_.conn(),
- const_cast<xcb_screen_t*>(screen),
- pixmap_.get(),
- format.render,
- width, height));
- if (cairo_surface_status(surface_.get()) != CAIRO_STATUS_SUCCESS) {
- surface_.reset();
- }
- }
- if (!surface_) {
- surface_.reset(
- cairo_xcb_surface_create(pixmap_.conn(), pixmap_.get(),
- format.visual, width, height));
- }
- cairo_.reset(cairo_create(surface_.get()));
- layout_.reset(pango_cairo_create_layout(cairo_.get()));
- pango_layout_set_ellipsize(layout_.get(), PANGO_ELLIPSIZE_MIDDLE);
- pango_layout_set_alignment(layout_.get(), PANGO_ALIGN_CENTER);
- pango_layout_set_height(layout_.get(), 0);
- auto font =
- std::unique_ptr<PangoFontDescription, PangoFontDescriptionDelete>(
- pango_font_description_new());
- pango_font_description_set_size(font.get(), 10 * PANGO_SCALE);
- pango_layout_set_font_description(layout_.get(), font.get());
- auto context = pango_layout_get_context(layout_.get());
- auto metrics = std::unique_ptr<PangoFontMetrics, PangoFontMetricsDelete>(
- pango_context_get_metrics(context, nullptr, nullptr));
- box_height_ = static_cast<double>(
- pango_font_metrics_get_ascent(metrics.get()) +
- pango_font_metrics_get_descent(metrics.get())) / PANGO_SCALE;
- x_ = 0;
- y_ = 0;
- w_ = width;
- h_ = height;
- depth_ = format.depth;
- black_pixel_ = screen->black_pixel;
- update_desktop_window();
-
- internal_draw();
- xcb_map_window(wnd_.conn(), wnd_.get());
- xcb_flush(wnd_.conn());
+ IceccMonMon(std::shared_ptr<PollLooper> const& looper, unsigned columns)
+ : MonMon(looper), connected_(false), max_jobs_(0), jobs_(0), requests_(0),
+ force_columns_(columns) {
}
void connect(Args const* args) {
@@ -235,155 +44,17 @@ public:
args->arg("scheduler", ""));
}
- void quit_from_xcb() {
- io::write(pipe_, "q", 1);
+protected:
+ void width_changed() override {
+ job_pattern_.reset();
}
#if FAKE_MONITOR
- void toggle_fakes() {
- io::write(pipe_, " ", 1);
+ void do_toggle_fakes() override {
+ monitor_->toggle_fakes();
}
#endif
- /*
- void quit_from_main(std::shared_ptr<x::Atoms> const& atoms) {
- // Wake up the event thread by sending an event to our window
- xcb_client_message_event_t event;
- memset(&event, 0, sizeof(event));
- event.response_type = XCB_CLIENT_MESSAGE;
- event.format = 32;
- event.sequence = 0;
- event.window = wnd_.get();
- event.type = atoms->get("__MONMON_QUIT");
-
- xcb_send_event(wnd_.conn(), false, wnd_.get(), XCB_EVENT_MASK_NO_EVENT,
- reinterpret_cast<char*>(&event));
- xcb_flush(wnd_.conn());
- }
- */
-
- void expose(int16_t x, int16_t y, uint16_t w, uint16_t h) {
- std::lock_guard<std::mutex> lock(mutex_);
- xcb_copy_area(wnd_.conn(), pixmap_.get(), wnd_.get(), gcontext_.get(),
- x, y, x, y, w, h);
- }
-
- bool match(xcb_window_t wnd) const {
- return wnd == wnd_.get();
- }
-
- void configure(int16_t x, int16_t y, uint16_t w, uint16_t h) {
- bool draw = false;
- if (rootpmap_ && (x != x_ || y != y_)) {
- x_ = x;
- y_ = y;
- if (desktop_surface_) {
- draw = true;
- io::write(pipe_, "d", 1);
- }
- }
- if (w == w_ && h == h_) return;
- {
- std::lock_guard<std::mutex> lock(mutex_);
- x::unique_pixmap tmp(pixmap_.shared_conn());
- x::unique_gcontext gc(tmp.shared_conn());
- cairo_surface_flush(surface_.get());
- xcb_create_pixmap(tmp.conn(), depth_, tmp.get(), wnd_.get(), w, h);
- uint32_t value[1];
- value[0] = black_pixel_;
- xcb_create_gc(gc.conn(), gc.get(), tmp.get(), XCB_GC_FOREGROUND, value);
- uint32_t count = 0;
- xcb_rectangle_t rect[2];
- uint16_t copy_width, copy_height;
- if (w > w_) {
- copy_width = w_;
- rect[count].x = w_;
- rect[count].width = w - w_;
- rect[count].y = 0;
- rect[count].height = h_;
- ++count;
- } else {
- copy_width = w;
- }
- if (h > h_) {
- copy_height = h_;
- rect[count].x = 0;
- rect[count].width = w;
- rect[count].y = h_;
- rect[count].height = h - h_;
- ++count;
- } else {
- copy_height = h;
- }
- if (count) {
- xcb_poly_fill_rectangle(tmp.conn(), tmp.get(), gc.get(),
- count, rect);
- }
- xcb_copy_area(tmp.conn(), pixmap_.get(), tmp.get(), gc.get(), 0, 0, 0, 0,
- copy_width, copy_height);
- cairo_xcb_surface_set_drawable(surface_.get(), tmp.get(), w, h);
- if (w_ != w) {
- job_pattern_.reset();
- w_ = w;
- }
- h_ = h;
- pixmap_ = std::move(tmp);
- xcb_copy_area(wnd_.conn(), pixmap_.get(), wnd_.get(), gcontext_.get(),
- 0, 0, 0, 0, w, h);
- }
- if (!draw) io::write(pipe_, "d", 1);
- }
-
- void update_desktop_window() {
- if (rootpmap_ == XCB_ATOM_NONE) return;
-
- xcb_window_t desktop_window = XCB_NONE;
-
- auto last = wnd_.get();
- while (true) {
- auto cookie = xcb_query_tree(wnd_.conn(), last);
- auto reply = xcb_query_tree_reply(wnd_.conn(), cookie, nullptr);
- if (!reply) break;
- auto cookie2 = xcb_get_property(wnd_.conn(), 0, reply->parent, rootpmap_,
- XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
- auto reply2 = xcb_get_property_reply(wnd_.conn(), cookie2, nullptr);
- if (reply2) {
- if (reply2->type != 0 && reply2->format != 0 && reply2->length != 0) {
- desktop_window = reply->parent;
- free(reply2);
- free(reply);
- break;
- }
- free(reply2);
- }
- if (reply->parent == reply->root) {
- free(reply);
- break;
- }
- last = reply->parent;
- free(reply);
- }
-
- if (desktop_window == desktop_window_) return;
- unset_desktop_window();
- if (desktop_window == XCB_NONE) return;
-
- uint32_t values[1];
- values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
- xcb_change_window_attributes(wnd_.conn(), desktop_window,
- XCB_CW_EVENT_MASK,
- values);
- desktop_window_ = desktop_window;
- update_desktop_pixmap();
- }
-
- void update_desktop_window(xcb_window_t window, xcb_atom_t property) {
- if (window != desktop_window_) return;
- if (property != rootpmap_) return;
-
- update_desktop_pixmap();
- }
-
private:
class MachineAnimation;
@@ -455,115 +126,6 @@ private:
double last_;
};
- void unset_desktop_window() {
- if (desktop_window_ == XCB_NONE) return;
- uint32_t values[1];
- values[0] = 0;
- xcb_change_window_attributes(wnd_.conn(), desktop_window_,
- XCB_CW_EVENT_MASK,
- values);
- desktop_window_ = XCB_NONE;
- update_desktop_pixmap();
- }
-
- void update_desktop_pixmap() {
- xcb_pixmap_t pixmap = XCB_NONE;
-
- if (desktop_window_ != XCB_NONE) {
- auto cookie = xcb_get_property(wnd_.conn(), 0, desktop_window_, rootpmap_,
- XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
- auto reply = xcb_get_property_reply(wnd_.conn(), cookie, nullptr);
- if (reply) {
- if (reply->type == XCB_ATOM_PIXMAP) {
- pixmap = *reinterpret_cast<xcb_pixmap_t*>(
- xcb_get_property_value(reply));
- }
- free(reply);
- }
- }
-
- if (pixmap == desktop_pixmap_) return;
-
- auto geometry_cookie = xcb_get_geometry(wnd_.conn(), pixmap);
- auto attr_cookie = xcb_get_window_attributes(wnd_.conn(), desktop_window_);
-
- auto geometry_reply = xcb_get_geometry_reply(wnd_.conn(), geometry_cookie,
- nullptr);
- uint16_t w, h;
- uint8_t depth;
- if (geometry_reply) {
- depth = geometry_reply->depth;
- w = geometry_reply->width;
- h = geometry_reply->height;
- free(geometry_reply);
- } else {
- pixmap = XCB_NONE;
- depth = 0;
- }
-
- auto attr_reply = xcb_get_window_attributes_reply(wnd_.conn(), attr_cookie,
- nullptr);
- if (attr_reply) {
- auto iter = xcb_screen_allowed_depths_iterator(screen_);
- auto count = xcb_screen_allowed_depths_length(screen_);
- while (count--) {
- if (iter.data->depth == depth) {
- auto visuals = xcb_depth_visuals(iter.data);
- auto visual_count = xcb_depth_visuals_length(iter.data);
- while (visual_count--) {
- if (visuals->visual_id == attr_reply->visual) {
- if (pixmap != XCB_NONE) {
- desktop_surface_.reset(cairo_xcb_surface_create(
- wnd_.conn(), pixmap,
- visuals, w, h));
- }
- break;
- }
- ++visuals;
- }
- }
- xcb_depth_next(&iter);
- }
- free(attr_reply);
- } else {
- pixmap = XCB_NONE;
- }
-
- desktop_pixmap_ = pixmap;
- if (desktop_pixmap_ == XCB_NONE) desktop_surface_.reset();
-
- io::write(pipe_, "d", 1);
- }
-
- void close_pipe() {
- if (!pipe_) return;
- looper_->remove(pipe_.read());
- pipe_.reset();
- }
-
- void pipe(Looper*, int, uint8_t event) {
- if (event & Looper::EV_READ) {
- char tmp;
- if (io::read(pipe_, &tmp, 1)) {
- if (tmp == 'd') {
- force_draw();
- return;
- }
-#if FAKE_MONITOR
- if (tmp == ' ') {
- monitor_->toggle_fakes();
- return;
- }
-#endif
- }
- }
-
- stop_all_animations();
- animator_.reset();
- monitor_->disconnect();
- looper_->exit_when_empty();
- close_pipe();
- }
void state(Monitor*, Monitor::State state) override {
switch (state) {
@@ -584,13 +146,17 @@ private:
}
}
- void stop_all_animations() {
+ 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);
@@ -603,43 +169,13 @@ private:
}
}
- void draw() {
- // Animator will draw soon anyway, so let it
- if (animator_ && animator_->active()) return;
- force_draw();
- }
-
- static void rounded_path(cairo_t* cr, double x, double y,
- double width, double height) {
- auto radius = height / 10.0;
- auto degrees = M_PI / 180.0;
-
- cairo_new_sub_path(cr);
- cairo_arc(cr, x + width - radius, y + radius, radius,
- -90 * degrees, 0 * degrees);
- cairo_arc(cr, x + width - radius, y + height - radius, radius,
- 0 * degrees, 90 * degrees);
- cairo_arc(cr, x + radius, y + height - radius, radius,
- 90 * degrees, 180 * degrees);
- cairo_arc(cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
- cairo_close_path(cr);
- }
-
- void 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());
- 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;
+ 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 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));
@@ -654,64 +190,59 @@ private:
cairo_pattern_add_color_stop_rgb(job_pattern_.get(), 1.000000,
0.976471, 0.968627, 0.831373);
}
- pango_layout_set_width(layout_.get(),
+ 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_.get(), machine->data.name.data(),
+ 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_.get(), x, y, box_width, box_height);
- cairo_set_source_rgba(cairo_.get(), 0.1, 0.1, 0.1, 0.7);
+ 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_.get());
- auto old = std::unique_ptr<cairo_path_t, CairoPathDelete>(
- cairo_copy_path(cairo_.get()));
- cairo_new_path(cairo_.get());
- cairo_rectangle(cairo_.get(), x, y,
- machine->x * box_width, box_height);
- cairo_clip(cairo_.get());
- cairo_append_path(cairo_.get(), old.get());
+ 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_.get(), job_pattern_.get());
- cairo_fill(cairo_.get());
- cairo_reset_clip(cairo_.get());
+ cairo_set_source(cairo, job_pattern_.get());
+ cairo_fill(cairo);
+ cairo_reset_clip(cairo);
} else {
- cairo_fill(cairo_.get());
+ cairo_fill(cairo);
}
if (machine->requests > 0) {
auto radius = box_height / 10.0;
- cairo_set_line_width(cairo_.get(), 1.5);
- cairo_new_path(cairo_.get());
- cairo_move_to(cairo_.get(), x + box_width - radius, y);
- cairo_rel_line_to(cairo_.get(),
+ 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_.get(), 0, 0.6, 0);
- cairo_stroke(cairo_.get());
+ cairo_set_source_rgb(cairo, 0, 0.6, 0);
+ cairo_stroke(cairo);
}
- cairo_move_to(cairo_.get(), x + margin_x, y + margin_y);
- pango_cairo_layout_path(cairo_.get(), layout_.get());
- cairo_set_source_rgb(cairo_.get(), 0.0, 0.0, 0.0);
- cairo_set_line_cap(cairo_.get(), CAIRO_LINE_CAP_ROUND);
- cairo_set_line_width(cairo_.get(), 2.0);
- cairo_stroke(cairo_.get());
- cairo_set_source_rgb(cairo_.get(), 1.0, 1.0, 1.0);
- cairo_move_to(cairo_.get(), x + margin_x, y + margin_y);
- pango_cairo_show_layout(cairo_.get(), layout_.get());
+ 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;
+ if (y >= h) break;
}
}
- cairo_surface_flush(surface_.get());
- xcb_copy_area(wnd_.conn(), pixmap_.get(), wnd_.get(), gcontext_.get(),
- 0, 0, 0, 0, w_, h_);
}
void added_machine(Monitor*, uint32_t id) override {
@@ -808,15 +339,6 @@ private:
draw();
}
- void force_draw() {
- internal_draw();
- xcb_flush(wnd_.conn());
- }
-
- void tick(Animator*) override {
- force_draw();
- }
-
void animate(Machine* machine) {
if (!animator_) return;
if (machine->animation) return;
@@ -824,36 +346,14 @@ private:
animator_->start(machine->animation, machine);
}
- std::shared_ptr<PollLooper> looper_;
std::unique_ptr<Monitor> monitor_;
- std::unique_ptr<Animator> animator_;
bool connected_;
- io::pipe pipe_;
- std::shared_ptr<x::Atoms> atoms_;
- x::unique_window wnd_;
- x::unique_gcontext gcontext_;
- x::unique_colormap cmap_;
- uint8_t depth_;
- uint32_t black_pixel_;
- xcb_screen_t const* screen_;
- double box_height_;
std::vector<std::unique_ptr<Machine>> machines_;
unsigned max_jobs_;
unsigned jobs_;
unsigned requests_;
- std::mutex mutex_;
- std::unique_ptr<cairo_surface_t, CairoSurfaceDelete> surface_;
- std::unique_ptr<cairo_t, CairoDelete> cairo_;
- std::unique_ptr<cairo_pattern_t, CairoPatternDelete> job_pattern_;
- std::unique_ptr<PangoLayout, GObjectDelete> layout_;
- x::unique_pixmap pixmap_;
- int16_t x_, y_;
- uint16_t w_, h_;
+ cairo::unique_pattern job_pattern_;
unsigned force_columns_;
- xcb_atom_t rootpmap_;
- xcb_window_t desktop_window_;
- xcb_pixmap_t desktop_pixmap_;
- std::unique_ptr<cairo_surface_t, CairoSurfaceDelete> desktop_surface_;
};
struct XcbKeySymbolsDelete {
@@ -1038,11 +538,9 @@ int main(int argc, char** argv) {
atoms->preload("WM_DELETE_WINDOW");
atoms->preload("WM_PROTOCOLS");
atoms->preload("__MONMON_QUIT");
- atoms->preload("_MOTIF_WM_INFO", false);
- atoms->preload("_MOTIF_WM_HINTS");
- atoms->preload("_XROOTPMAP_ID", false);
+ MonMon::preload(atoms.get());
std::shared_ptr<x::Ewmh> ewmh(x::Ewmh::create(conn, screen_index));
- auto monmon = std::make_shared<MonMon>(looper, columns);
+ auto monmon = std::make_shared<IceccMonMon>(looper, columns);
monmon->init(conn, screen, format, atoms, ewmh, 400, 400,
args->is_set("titlebar"), args->is_set("black"));
monmon->connect(args.get());
diff --git a/src/monmon.cc b/src/monmon.cc
new file mode 100644
index 0000000..4335acb
--- /dev/null
+++ b/src/monmon.cc
@@ -0,0 +1,455 @@
+#include "common.hh"
+
+#include <iostream>
+#include <math.h>
+#include <xcb/xcb_icccm.h>
+
+#include "monmon.hh"
+
+namespace {
+
+struct MwmHints {
+ uint32_t flags;
+ uint32_t functions;
+ uint32_t decorations;
+ int32_t input_mode;
+ uint32_t status;
+};
+
+} // namespace
+
+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) {
+ pipe_.open();
+ looper_->add(pipe_.read(), Looper::EV_READ,
+ std::bind(&MonMon::pipe, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3));
+ animator_->add_observer(this);
+}
+
+MonMon::~MonMon() {
+ stop_all_animations();
+ unset_desktop_window();
+ close_pipe();
+ wnd_.reset();
+}
+
+void MonMon::init(x::shared_connection const& conn, xcb_screen_t const* screen,
+ x::Format format, std::shared_ptr<x::Atoms> const& atoms,
+ std::shared_ptr<x::Ewmh> const& ewmh,
+ uint16_t width, uint16_t height, bool normal, bool black) {
+ screen_ = screen;
+ wnd_.reset(conn);
+ pixmap_.reset(conn);
+ gcontext_.reset(conn);
+ cmap_.reset();
+
+ uint32_t values[4];
+ size_t i = 0;
+ uint32_t mask = XCB_CW_BACK_PIXEL;
+ values[i++] = screen->black_pixel;
+ if (format.need_border()) {
+ mask |= XCB_CW_BORDER_PIXEL;
+ values[i++] = screen->black_pixel;
+ }
+ mask |= XCB_CW_EVENT_MASK;
+ values[i++] = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_EXPOSURE
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
+ if (format.need_colormap()) {
+ mask |= XCB_CW_COLORMAP;
+ cmap_.reset(conn);
+ xcb_create_colormap(cmap_.conn(), XCB_COLORMAP_ALLOC_NONE, cmap_.get(),
+ screen->root, format.visual->visual_id);
+ values[i++] = cmap_.get();
+ }
+ xcb_create_window(wnd_.conn(), format.depth,
+ wnd_.get(), screen->root,
+ 0, 0, width, height, 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ format.visual->visual_id,
+ mask, values);
+ if (!normal) {
+ if (atoms->get("_MOTIF_WM_INFO", false) != XCB_ATOM_NONE) {
+ auto atom = atoms->get("_MOTIF_WM_HINTS");
+ MwmHints hints;
+ hints.flags = 1 << 1; // MWM_HINTS_DECORATIONS
+ hints.decorations = 0;
+ xcb_change_property(wnd_.conn(), XCB_PROP_MODE_REPLACE, wnd_.get(),
+ atom, atom, 32, sizeof(hints) / 4, &hints);
+ } else {
+#ifndef NDEBUG
+ // There is no way to know how the window manager will implement
+ // utility window type, some might show them without titlebar ...
+ std::cerr << "Warning: No motif support detected, falling back to "
+ << "NETWM utility window type." << std::endl;
+#endif
+ auto wm = ewmh->conn();
+ if (wm && ewmh->supported(wm->_NET_WM_WINDOW_TYPE_UTILITY)) {
+ xcb_atom_t values[1];
+ values[0] = wm->_NET_WM_WINDOW_TYPE_UTILITY;
+ xcb_ewmh_set_wm_window_type(wm, wnd_.get(), 1, values);
+ } else {
+ // TODO: Fallback to override-redirect?
+ }
+ }
+ }
+ if (!black) {
+ rootpmap_ = atoms->get("_XROOTPMAP_ID", false);
+ if (rootpmap_ == XCB_ATOM_NONE) {
+ std::cerr << "Warning: No support for Eterm style background pixmap"
+ << std::endl;
+ }
+ }
+ auto wm = ewmh->conn();
+ if (wm) {
+ xcb_ewmh_set_wm_name(wm, wnd_.get(), 6, "MonMon");
+ } else {
+ xcb_icccm_set_wm_name(wnd_.conn(), wnd_.get(), XCB_ATOM_STRING, 8,
+ 6, "MonMon");
+ }
+ xcb_create_gc(gcontext_.conn(), gcontext_.get(), wnd_.get(), 0, nullptr);
+ auto atom = atoms->get("WM_DELETE_WINDOW");
+ xcb_icccm_set_wm_protocols(wnd_.conn(), wnd_.get(),
+ atoms->get("WM_PROTOCOLS"),
+ 1, &atom);
+ xcb_create_pixmap(pixmap_.conn(), format.depth, pixmap_.get(), wnd_.get(),
+ width, height);
+ if (format.render) {
+ surface_.reset(
+ cairo_xcb_surface_create_with_xrender_format(
+ pixmap_.conn(),
+ const_cast<xcb_screen_t*>(screen),
+ pixmap_.get(),
+ format.render,
+ width, height));
+ if (cairo_surface_status(surface_.get()) != CAIRO_STATUS_SUCCESS) {
+ surface_.reset();
+ }
+ }
+ if (!surface_) {
+ surface_.reset(
+ cairo_xcb_surface_create(pixmap_.conn(), pixmap_.get(),
+ format.visual, width, height));
+ }
+ cairo_.reset(cairo_create(surface_.get()));
+ layout_.reset(pango_cairo_create_layout(cairo_.get()));
+ pango_layout_set_ellipsize(layout_.get(), PANGO_ELLIPSIZE_MIDDLE);
+ pango_layout_set_alignment(layout_.get(), PANGO_ALIGN_CENTER);
+ pango_layout_set_height(layout_.get(), 0);
+ auto font = pango::unique_font_description(pango_font_description_new());
+ pango_font_description_set_size(font.get(), 10 * PANGO_SCALE);
+ pango_layout_set_font_description(layout_.get(), font.get());
+ auto context = pango_layout_get_context(layout_.get());
+ auto metrics = pango::unique_font_metrics(
+ pango_context_get_metrics(context, nullptr, nullptr));
+ box_height_ = static_cast<double>(
+ pango_font_metrics_get_ascent(metrics.get()) +
+ pango_font_metrics_get_descent(metrics.get())) / PANGO_SCALE;
+ x_ = 0;
+ y_ = 0;
+ w_ = width;
+ h_ = height;
+ depth_ = format.depth;
+ black_pixel_ = screen->black_pixel;
+ update_desktop_window();
+
+ internal_draw();
+ xcb_map_window(wnd_.conn(), wnd_.get());
+ xcb_flush(wnd_.conn());
+}
+
+void MonMon::quit_from_xcb() {
+ io::write(pipe_, "q", 1);
+}
+
+#if FAKE_MONITOR
+void MonMon::toggle_fakes() {
+ io::write(pipe_, " ", 1);
+}
+#endif
+
+void MonMon::expose(int16_t x, int16_t y, uint16_t w, uint16_t h) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ xcb_copy_area(wnd_.conn(), pixmap_.get(), wnd_.get(), gcontext_.get(),
+ x, y, x, y, w, h);
+}
+
+void MonMon::configure(int16_t x, int16_t y, uint16_t w, uint16_t h) {
+ bool draw = false;
+ if (rootpmap_ && (x != x_ || y != y_)) {
+ x_ = x;
+ y_ = y;
+ if (desktop_surface_) {
+ draw = true;
+ io::write(pipe_, "d", 1);
+ }
+ }
+ if (w == w_ && h == h_) return;
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ x::unique_pixmap tmp(pixmap_.shared_conn());
+ x::unique_gcontext gc(tmp.shared_conn());
+ cairo_surface_flush(surface_.get());
+ xcb_create_pixmap(tmp.conn(), depth_, tmp.get(), wnd_.get(), w, h);
+ uint32_t value[1];
+ value[0] = black_pixel_;
+ xcb_create_gc(gc.conn(), gc.get(), tmp.get(), XCB_GC_FOREGROUND, value);
+ uint32_t count = 0;
+ xcb_rectangle_t rect[2];
+ uint16_t copy_width, copy_height;
+ if (w > w_) {
+ copy_width = w_;
+ rect[count].x = w_;
+ rect[count].width = w - w_;
+ rect[count].y = 0;
+ rect[count].height = h_;
+ ++count;
+ } else {
+ copy_width = w;
+ }
+ if (h > h_) {
+ copy_height = h_;
+ rect[count].x = 0;
+ rect[count].width = w;
+ rect[count].y = h_;
+ rect[count].height = h - h_;
+ ++count;
+ } else {
+ copy_height = h;
+ }
+ if (count) {
+ xcb_poly_fill_rectangle(tmp.conn(), tmp.get(), gc.get(), count, rect);
+ }
+ xcb_copy_area(tmp.conn(), pixmap_.get(), tmp.get(), gc.get(), 0, 0, 0, 0,
+ copy_width, copy_height);
+ cairo_xcb_surface_set_drawable(surface_.get(), tmp.get(), w, h);
+ if (w_ != w) {
+ width_changed();
+ w_ = w;
+ }
+ h_ = h;
+ pixmap_ = std::move(tmp);
+ xcb_copy_area(wnd_.conn(), pixmap_.get(), wnd_.get(), gcontext_.get(),
+ 0, 0, 0, 0, w, h);
+ }
+ if (!draw) io::write(pipe_, "d", 1);
+}
+
+void MonMon::update_desktop_window() {
+ if (rootpmap_ == XCB_ATOM_NONE) return;
+
+ xcb_window_t desktop_window = XCB_NONE;
+
+ auto last = wnd_.get();
+ while (true) {
+ auto cookie = xcb_query_tree(wnd_.conn(), last);
+ auto reply = xcb_query_tree_reply(wnd_.conn(), cookie, nullptr);
+ if (!reply) break;
+ auto cookie2 = xcb_get_property(wnd_.conn(), 0, reply->parent, rootpmap_,
+ XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
+ auto reply2 = xcb_get_property_reply(wnd_.conn(), cookie2, nullptr);
+ if (reply2) {
+ if (reply2->type != 0 && reply2->format != 0 && reply2->length != 0) {
+ desktop_window = reply->parent;
+ free(reply2);
+ free(reply);
+ break;
+ }
+ free(reply2);
+ }
+ if (reply->parent == reply->root) {
+ free(reply);
+ break;
+ }
+ last = reply->parent;
+ free(reply);
+ }
+
+ if (desktop_window == desktop_window_) return;
+ unset_desktop_window();
+ if (desktop_window == XCB_NONE) return;
+
+ uint32_t values[1];
+ values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_change_window_attributes(wnd_.conn(), desktop_window,
+ XCB_CW_EVENT_MASK,
+ values);
+ desktop_window_ = desktop_window;
+ update_desktop_pixmap();
+}
+
+void MonMon::update_desktop_window(xcb_window_t window, xcb_atom_t property) {
+ if (window != desktop_window_) return;
+ if (property != rootpmap_) return;
+
+ update_desktop_pixmap();
+}
+
+void MonMon::unset_desktop_window() {
+ if (desktop_window_ == XCB_NONE) return;
+ uint32_t values[1];
+ values[0] = 0;
+ xcb_change_window_attributes(wnd_.conn(), desktop_window_,
+ XCB_CW_EVENT_MASK,
+ values);
+ desktop_window_ = XCB_NONE;
+ update_desktop_pixmap();
+}
+
+void MonMon::update_desktop_pixmap() {
+ xcb_pixmap_t pixmap = XCB_NONE;
+
+ if (desktop_window_ != XCB_NONE) {
+ auto cookie = xcb_get_property(wnd_.conn(), 0, desktop_window_, rootpmap_,
+ XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
+ auto reply = xcb_get_property_reply(wnd_.conn(), cookie, nullptr);
+ if (reply) {
+ if (reply->type == XCB_ATOM_PIXMAP) {
+ pixmap = *reinterpret_cast<xcb_pixmap_t*>(
+ xcb_get_property_value(reply));
+ }
+ free(reply);
+ }
+ }
+
+ if (pixmap == desktop_pixmap_) return;
+
+ auto geometry_cookie = xcb_get_geometry(wnd_.conn(), pixmap);
+ auto attr_cookie = xcb_get_window_attributes(wnd_.conn(), desktop_window_);
+
+ auto geometry_reply = xcb_get_geometry_reply(wnd_.conn(), geometry_cookie,
+ nullptr);
+ uint16_t w, h;
+ uint8_t depth;
+ if (geometry_reply) {
+ depth = geometry_reply->depth;
+ w = geometry_reply->width;
+ h = geometry_reply->height;
+ free(geometry_reply);
+ } else {
+ pixmap = XCB_NONE;
+ depth = 0;
+ }
+
+ auto attr_reply = xcb_get_window_attributes_reply(wnd_.conn(), attr_cookie,
+ nullptr);
+ if (attr_reply) {
+ auto iter = xcb_screen_allowed_depths_iterator(screen_);
+ auto count = xcb_screen_allowed_depths_length(screen_);
+ while (count--) {
+ if (iter.data->depth == depth) {
+ auto visuals = xcb_depth_visuals(iter.data);
+ auto visual_count = xcb_depth_visuals_length(iter.data);
+ while (visual_count--) {
+ if (visuals->visual_id == attr_reply->visual) {
+ if (pixmap != XCB_NONE) {
+ desktop_surface_.reset(cairo_xcb_surface_create(
+ wnd_.conn(), pixmap,
+ visuals, w, h));
+ }
+ break;
+ }
+ ++visuals;
+ }
+ }
+ xcb_depth_next(&iter);
+ }
+ free(attr_reply);
+ } else {
+ pixmap = XCB_NONE;
+ }
+
+ desktop_pixmap_ = pixmap;
+ if (desktop_pixmap_ == XCB_NONE) desktop_surface_.reset();
+
+ io::write(pipe_, "d", 1);
+}
+
+void MonMon::close_pipe() {
+ if (!pipe_) return;
+ looper_->remove(pipe_.read());
+ pipe_.reset();
+}
+
+void MonMon::pipe(Looper*, int, uint8_t event) {
+ if (event & Looper::EV_READ) {
+ char tmp;
+ if (io::read(pipe_, &tmp, 1)) {
+ if (tmp == 'd') {
+ force_draw();
+ return;
+ }
+#if FAKE_MONITOR
+ if (tmp == ' ') {
+ do_toggle_fakes();
+ return;
+ }
+#endif
+ }
+ }
+
+ stop_all_animations();
+ animator_.reset();
+ internal_quit();
+ looper_->exit_when_empty();
+ close_pipe();
+}
+
+void MonMon::draw() {
+ // Animator will draw soon anyway, so let it
+ if (animator_ && animator_->active()) return;
+ force_draw();
+}
+
+// static
+void MonMon::rounded_path(cairo_t* cr, double x, double y,
+ double width, double height) {
+ auto radius = height / 10.0;
+ auto degrees = M_PI / 180.0;
+
+ cairo_new_sub_path(cr);
+ cairo_arc(cr, x + width - radius, y + radius, radius,
+ -90 * degrees, 0 * degrees);
+ cairo_arc(cr, x + width - radius, y + height - radius, radius,
+ 0 * degrees, 90 * degrees);
+ cairo_arc(cr, x + radius, y + height - radius, radius,
+ 90 * degrees, 180 * degrees);
+ cairo_arc(cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
+ cairo_close_path(cr);
+}
+
+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_);
+
+ cairo_surface_flush(surface_.get());
+ xcb_copy_area(wnd_.conn(), pixmap_.get(), wnd_.get(), gcontext_.get(),
+ 0, 0, 0, 0, w_, h_);
+}
+
+void MonMon::force_draw() {
+ internal_draw();
+ xcb_flush(wnd_.conn());
+}
+
+void MonMon::tick(Animator*) {
+ force_draw();
+}
+
+// static
+void MonMon::preload(x::Atoms* atoms) {
+ atoms->preload("_MOTIF_WM_INFO", false);
+ atoms->preload("_MOTIF_WM_HINTS");
+ atoms->preload("_XROOTPMAP_ID", false);
+}
diff --git a/src/monmon.hh b/src/monmon.hh
new file mode 100644
index 0000000..e41be5b
--- /dev/null
+++ b/src/monmon.hh
@@ -0,0 +1,109 @@
+#ifndef MONMON_HH
+#define MONMON_HH
+
+#include <memory>
+#include <mutex>
+
+#include "animator.hh"
+#include "cairo.hh"
+#include "io.hh"
+#include "pango.hh"
+#include "poll_looper.hh"
+#include "x.hh"
+
+class MonMon : protected virtual Animator::Observer {
+public:
+ virtual ~MonMon();
+
+ void init(x::shared_connection const& conn, xcb_screen_t const* screen,
+ x::Format format, std::shared_ptr<x::Atoms> const& atoms,
+ std::shared_ptr<x::Ewmh> const& ewmh,
+ uint16_t width, uint16_t height, bool normal, bool black);
+
+ void quit_from_xcb();
+
+#if FAKE_MONITOR
+ void toggle_fakes();
+#endif
+
+ void expose(int16_t x, int16_t y, uint16_t w, uint16_t h);
+
+ bool match(xcb_window_t wnd) const {
+ return wnd == wnd_.get();
+ }
+
+ void configure(int16_t x, int16_t y, uint16_t w, uint16_t h);
+
+ void update_desktop_window();
+
+ void update_desktop_window(xcb_window_t window, xcb_atom_t property);
+
+ static void preload(x::Atoms* atoms);
+
+protected:
+ MonMon(std::shared_ptr<PollLooper> const& looper);
+
+ virtual void width_changed() {
+ }
+
+#if FAKE_MONITOR
+ virtual void do_toggle_fakes() {
+ }
+#endif
+
+ virtual void stop_all_animations() {
+ }
+
+ virtual void internal_quit() {
+ }
+
+ virtual void draw_content(cairo_t* cairo, PangoLayout* layout,
+ uint16_t w, uint16_t h) = 0;
+
+ static void rounded_path(cairo_t* cr, double x, double y,
+ double width, double height);
+
+ void draw();
+
+ std::shared_ptr<PollLooper> looper_;
+ std::unique_ptr<Animator> animator_;
+ x::unique_window wnd_;
+ double box_height_;
+
+private:
+ void unset_desktop_window();
+
+ void update_desktop_pixmap();
+
+ void close_pipe();
+
+ void pipe(Looper*, int, uint8_t event);
+
+ void internal_draw();
+
+ void force_draw();
+
+ void tick(Animator*) override;
+
+ io::pipe pipe_;
+ std::shared_ptr<x::Atoms> atoms_;
+ x::unique_gcontext gcontext_;
+ x::unique_colormap cmap_;
+ uint8_t depth_;
+ uint32_t black_pixel_;
+ xcb_screen_t const* screen_;
+ std::mutex mutex_;
+ cairo::unique_surface surface_;
+ cairo::unique cairo_;
+ pango::unique_layout layout_;
+ x::unique_pixmap pixmap_;
+ int16_t x_, y_;
+ uint16_t w_, h_;
+ xcb_atom_t rootpmap_;
+ xcb_window_t desktop_window_;
+ xcb_pixmap_t desktop_pixmap_;
+ cairo::unique_surface desktop_surface_;
+};
+
+
+#endif // MONMON_HH
diff --git a/src/pango.hh b/src/pango.hh
new file mode 100644
index 0000000..2b92366
--- /dev/null
+++ b/src/pango.hh
@@ -0,0 +1,41 @@
+#ifndef PANGO_HH
+#define PANGO_HH
+
+#include <memory>
+#include <pango/pangocairo.h>
+
+namespace pango {
+
+namespace priv {
+
+struct GObjectDelete {
+ void operator()(gpointer ptr) const {
+ g_object_unref(ptr);
+ }
+};
+
+struct PangoFontMetricsDelete {
+ void operator()(PangoFontMetrics* ptr) const {
+ pango_font_metrics_unref(ptr);
+ }
+};
+
+struct PangoFontDescriptionDelete {
+ void operator()(PangoFontDescription* ptr) const {
+ pango_font_description_free(ptr);
+ }
+};
+
+} // namespace priv
+
+typedef std::unique_ptr<PangoLayout, priv::GObjectDelete> unique_layout;
+
+typedef std::unique_ptr<PangoFontDescription, priv::PangoFontDescriptionDelete>
+ unique_font_description;
+
+typedef std::unique_ptr<PangoFontMetrics, priv::PangoFontMetricsDelete>
+ unique_font_metrics;
+
+} // namespace pango
+
+#endif // PANGO_HH