#pragma once #include #include #include #include #include #include "common.hpp" #include "components/x11/atoms.hpp" #include "components/x11/types.hpp" #include "components/x11/xutils.hpp" #include "utils/memory.hpp" #include "utils/string.hpp" LEMONBUDDY_NS using xpp_connection = xpp::connection; class connection : public xpp_connection { public: explicit connection() {} explicit connection(xcb_connection_t* conn) : xpp_connection(conn) {} connection& operator=(const connection&) { return *this; } virtual ~connection() {} /** * Preload required xcb atoms */ auto preload_atoms() { for (auto&& a : ATOMS) *a.atom = intern_atom(false, a.len, a.name).atom(); } /** * Check if required X extensions are available */ auto query_extensions() { // damage().query_version(XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION); // if (!extension()->present) // throw application_error("Missing X extension: Damage"); // render().query_version(XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION); // if (!extension()->present) // throw application_error("Missing X extension: Render"); randr().query_version(XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); if (!extension()->present) throw application_error("Missing X extension: RandR"); } /** * Create X window id string */ auto id(xcb_window_t w) const { return string_util::from_stream( std::stringstream() << "0x" << std::hex << std::setw(7) << std::setfill('0') << w); } /** * Get pointer to the default xcb screen */ auto screen() { if (m_screen == nullptr) m_screen = screen_of_display(default_screen()); return m_screen; } /** * Creates an instance of shared_ptr */ auto make_client_message(xcb_atom_t type, xcb_window_t target) const { auto client_message = memory_util::make_malloc_ptr(size_t{32}); client_message->response_type = XCB_CLIENT_MESSAGE; client_message->format = 32; client_message->type = type; client_message->window = target; client_message->sequence = 0; client_message->data.data32[0] = 0; client_message->data.data32[1] = 0; client_message->data.data32[2] = 0; client_message->data.data32[3] = 0; client_message->data.data32[4] = 0; return client_message; } /** * Send client message event */ void send_client_message(shared_ptr message, xcb_window_t target, uint32_t event_mask = 0xFFFFFF, bool propagate = false) const { const char* data = reinterpret_cast(message.get()); send_event(propagate, target, event_mask, data); flush(); } /** * Sends a dummy event to the specified window * Used to interrupt blocking wait call * * @XXX: Find the proper way to interrupt the blocking wait * except the obvious event polling */ auto send_dummy_event( xcb_window_t target, uint32_t event = XCB_EVENT_MASK_STRUCTURE_NOTIFY) const { if (target == XCB_NONE) target = root(); auto message = make_client_message(XCB_NONE, target); send_client_message(message, target, event); } /** * Try to get a visual type for the given screen that * matches the given depth */ optional visual_type(xcb_screen_t* screen, int match_depth = 32) { xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(screen); if (depth_iter.data) { for (; depth_iter.rem; xcb_depth_next(&depth_iter)) if (match_depth == 0 || match_depth == depth_iter.data->depth) for (auto it = xcb_depth_visuals_iterator(depth_iter.data); it.rem; xcb_visualtype_next(&it)) return it.data; if (match_depth > 0) return visual_type(screen, 0); } return {}; } /** * Parse connection error */ static string error_str(int error_code) { switch (error_code) { case XCB_CONN_ERROR: return "Socket, pipe or stream error"; case XCB_CONN_CLOSED_EXT_NOTSUPPORTED: return "Unsupported extension"; case XCB_CONN_CLOSED_MEM_INSUFFICIENT: return "Not enough memory"; case XCB_CONN_CLOSED_REQ_LEN_EXCEED: return "Request length exceeded"; case XCB_CONN_CLOSED_PARSE_ERR: return "Can't parse display string"; case XCB_CONN_CLOSED_INVALID_SCREEN: return "Invalid screen"; case XCB_CONN_CLOSED_FDPASSING_FAILED: return "Failed to pass FD"; default: return "Unknown error"; } } /** * Attach sink to the registry */ template void attach_sink(Sink&& sink, registry::priority prio = 0) { m_registry.attach(prio, forward(sink)); } /** * Detach sink from the registry */ template void detach_sink(Sink&& sink, registry::priority prio = 0) { m_registry.detach(prio, forward(sink)); } /** * Dispatch event through the registry */ void dispatch_event(const shared_ptr& evt) { if (evt != nullptr) m_registry.dispatch(evt); } protected: registry m_registry{*this}; xcb_screen_t* m_screen = nullptr; }; namespace { /** * Configure injection module */ template di::injector configure_connection() { return di::make_injector( di::bind<>().to(factory::generic_singleton(xutils::get_connection()))); } } LEMONBUDDY_NS_END