polybar-dwm/include/x11/connection.hpp
2022-04-16 00:16:49 +02:00

172 lines
5.6 KiB
C++

#pragma once
#include <xcb/xcb.h>
#include <cstdlib>
#include <xpp/core.hpp>
#include <xpp/generic/factory.hpp>
#include <xpp/proto/x.hpp>
#include "common.hpp"
#include "components/screen.hpp"
#include "x11/extensions/all.hpp"
#include "x11/registry.hpp"
#include "x11/types.hpp"
POLYBAR_NS
namespace detail {
template <typename Connection, typename... Extensions>
class interfaces : public xpp::x::extension::interface<interfaces<Connection, Extensions...>, Connection>,
public Extensions::template interface<interfaces<Connection, Extensions...>, Connection>... {
public:
const Connection& connection() const {
return static_cast<const Connection&>(*this);
}
};
template <typename Derived, typename... Extensions>
class connection_base : public xpp::core,
public xpp::generic::error_dispatcher,
public detail::interfaces<connection_base<Derived, Extensions...>, Extensions...>,
private xpp::x::extension,
private xpp::x::extension::error_dispatcher,
private Extensions...,
private Extensions::error_dispatcher... {
public:
explicit connection_base(xcb_connection_t* c, int s)
: xpp::core(c)
, interfaces<connection_base<Derived, Extensions...>, Extensions...>(*this)
, Extensions(m_c.get())...
, Extensions::error_dispatcher(static_cast<Extensions&>(*this).get())... {
core::m_screen = s;
m_root_window = screen_of_display(default_screen())->root;
}
virtual ~connection_base() {}
void operator()(const shared_ptr<xcb_generic_error_t>& error) const override {
check<xpp::x::extension, Extensions...>(error);
}
template <typename Extension>
const Extension& extension() const {
return static_cast<const Extension&>(*this);
}
template <typename WindowType = xcb_window_t>
WindowType root() const {
using make = xpp::generic::factory::make<connection_base, xcb_window_t, WindowType>;
return make()(*this, m_root_window);
}
shared_ptr<xcb_generic_event_t> wait_for_event() const override {
try {
return core::wait_for_event();
} catch (const shared_ptr<xcb_generic_error_t>& error) {
check<xpp::x::extension, Extensions...>(error);
}
throw; // re-throw exception
}
shared_ptr<xcb_generic_event_t> wait_for_special_event(xcb_special_event_t* se) const override {
try {
return core::wait_for_special_event(se);
} catch (const shared_ptr<xcb_generic_error_t>& error) {
check<xpp::x::extension, Extensions...>(error);
}
throw; // re-throw exception
}
private:
xcb_window_t m_root_window;
template <typename Extension, typename Next, typename... Rest>
void check(const shared_ptr<xcb_generic_error_t>& error) const {
check<Extension>(error);
check<Next, Rest...>(error);
}
template <typename Extension>
void check(const shared_ptr<xcb_generic_error_t>& error) const {
using error_dispatcher = typename Extension::error_dispatcher;
auto& dispatcher = static_cast<const error_dispatcher&>(*this);
dispatcher(error);
}
};
} // namespace detail
class connection : public detail::connection_base<connection&, XPP_EXTENSION_LIST> {
public:
using base_type = detail::connection_base<connection&, XPP_EXTENSION_LIST>;
using make_type = connection&;
static make_type make(xcb_connection_t* conn = nullptr, int default_screen = 0);
explicit connection(xcb_connection_t* c, int default_screen);
~connection();
const connection& operator=(const connection& o) {
return o;
}
static void pack_values(uint32_t mask, const void* src, std::array<uint32_t, 32>& dest);
xcb_screen_t* screen(bool realloc = false);
string id(xcb_window_t w) const;
void ensure_event_mask(xcb_window_t win, unsigned int event);
void clear_event_mask(xcb_window_t win);
xcb_client_message_event_t make_client_message(xcb_atom_t type, xcb_window_t target) const;
void send_client_message(const xcb_client_message_event_t& message, xcb_window_t target,
unsigned int event_mask = 0xFFFFFF, bool propagate = false) const;
xcb_visualtype_t* visual_type(xcb_visual_class_t class_, int match_depth);
xcb_visualtype_t* visual_type_for_id(xcb_visualid_t visual_id);
bool root_pixmap(xcb_pixmap_t* pixmap, int* depth, xcb_rectangle_t* rect);
static string error_str(int error_code);
void dispatch_event(const shared_ptr<xcb_generic_event_t>& evt) const;
template <typename Event, unsigned int ResponseType>
void wait_for_response(function<bool(const Event*)> check_event) {
int fd = get_file_descriptor();
shared_ptr<xcb_generic_event_t> evt{};
while (!connection_has_error()) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if (!select(fd + 1, &fds, nullptr, nullptr, nullptr)) {
continue;
} else if ((evt = shared_ptr<xcb_generic_event_t>(xcb_poll_for_event(*this), free)) == nullptr) {
continue;
} else if (evt->response_type != ResponseType) {
continue;
} else if (check_event(reinterpret_cast<const Event*>(&*evt))) {
break;
}
}
}
template <typename Sink>
void attach_sink(Sink&& sink, registry::priority prio = 0) {
m_registry.attach(prio, forward<Sink>(sink));
}
template <typename Sink>
void detach_sink(Sink&& sink, registry::priority prio = 0) {
m_registry.detach(prio, forward<Sink>(sink));
}
protected:
registry m_registry{*this};
xcb_screen_t* m_screen{nullptr};
};
POLYBAR_NS_END