draconisplusplus/src/wrappers/xcb.hpp
2025-05-05 23:19:41 -04:00

198 lines
6.2 KiB
C++

#pragma once
#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
// clang-format off
#include <xcb/xcb.h> // XCB library
#include "src/util/defs.hpp"
#include "src/util/types.hpp"
// clang-format on
namespace xcb {
using util::types::u8, util::types::i32, util::types::CStr, util::types::None;
using connection_t = xcb_connection_t;
using setup_t = xcb_setup_t;
using screen_t = xcb_screen_t;
using window_t = xcb_window_t;
using atom_t = xcb_atom_t;
using generic_error_t = xcb_generic_error_t;
using intern_atom_cookie_t = xcb_intern_atom_cookie_t;
using intern_atom_reply_t = xcb_intern_atom_reply_t;
using get_property_cookie_t = xcb_get_property_cookie_t;
using get_property_reply_t = xcb_get_property_reply_t;
constexpr atom_t ATOM_WINDOW = XCB_ATOM_WINDOW;
enum ConnError : u8 {
Generic = XCB_CONN_ERROR,
ExtNotSupported = XCB_CONN_CLOSED_EXT_NOTSUPPORTED,
MemInsufficient = XCB_CONN_CLOSED_MEM_INSUFFICIENT,
ReqLenExceed = XCB_CONN_CLOSED_REQ_LEN_EXCEED,
ParseErr = XCB_CONN_CLOSED_PARSE_ERR,
InvalidScreen = XCB_CONN_CLOSED_INVALID_SCREEN,
FdPassingFailed = XCB_CONN_CLOSED_FDPASSING_FAILED,
};
// NOLINTBEGIN(readability-identifier-naming)
inline fn getConnError(const util::types::i32 code) -> util::types::Option<ConnError> {
switch (code) {
case XCB_CONN_ERROR: return Generic;
case XCB_CONN_CLOSED_EXT_NOTSUPPORTED: return ExtNotSupported;
case XCB_CONN_CLOSED_MEM_INSUFFICIENT: return MemInsufficient;
case XCB_CONN_CLOSED_REQ_LEN_EXCEED: return ReqLenExceed;
case XCB_CONN_CLOSED_PARSE_ERR: return ParseErr;
case XCB_CONN_CLOSED_INVALID_SCREEN: return InvalidScreen;
case XCB_CONN_CLOSED_FDPASSING_FAILED: return FdPassingFailed;
default: return None;
}
}
inline fn connect(const char* displayname, int* screenp) -> connection_t* {
return xcb_connect(displayname, screenp);
}
inline fn disconnect(connection_t* conn) -> void {
xcb_disconnect(conn);
}
inline fn connection_has_error(connection_t* conn) -> int {
return xcb_connection_has_error(conn);
}
inline fn intern_atom(connection_t* conn, const uint8_t only_if_exists, const uint16_t name_len, const char* name)
-> intern_atom_cookie_t {
return xcb_intern_atom(conn, only_if_exists, name_len, name);
}
inline fn intern_atom_reply(connection_t* conn, const intern_atom_cookie_t cookie, generic_error_t** err)
-> intern_atom_reply_t* {
return xcb_intern_atom_reply(conn, cookie, err);
}
inline fn get_property(
connection_t* conn,
const uint8_t _delete,
const window_t window,
const atom_t property,
const atom_t type,
const uint32_t long_offset,
const uint32_t long_length
) -> get_property_cookie_t {
return xcb_get_property(conn, _delete, window, property, type, long_offset, long_length);
}
inline fn get_property_reply(connection_t* conn, const get_property_cookie_t cookie, generic_error_t** err)
-> get_property_reply_t* {
return xcb_get_property_reply(conn, cookie, err);
}
inline fn get_property_value_length(const get_property_reply_t* reply) -> int {
return xcb_get_property_value_length(reply);
}
inline fn get_property_value(const get_property_reply_t* reply) -> void* {
return xcb_get_property_value(reply);
}
// NOLINTEND(readability-identifier-naming)
/**
* RAII wrapper for X11 Display connections
* Automatically handles resource acquisition and cleanup
*/
class DisplayGuard {
connection_t* m_connection = nullptr;
public:
/**
* Opens an XCB connection
* @param name Display name (nullptr for default)
*/
explicit DisplayGuard(const util::types::CStr name = nullptr)
: m_connection(connect(name, nullptr)) {}
~DisplayGuard() {
if (m_connection)
disconnect(m_connection);
}
// Non-copyable
DisplayGuard(const DisplayGuard&) = delete;
fn operator=(const DisplayGuard&)->DisplayGuard& = delete;
// Movable
DisplayGuard(DisplayGuard&& other) noexcept
: m_connection(std::exchange(other.m_connection, nullptr)) {}
fn operator=(DisplayGuard&& other) noexcept -> DisplayGuard& {
if (this != &other) {
if (m_connection)
disconnect(m_connection);
m_connection = std::exchange(other.m_connection, nullptr);
}
return *this;
}
[[nodiscard]] explicit operator bool() const {
return m_connection && !connection_has_error(m_connection);
}
[[nodiscard]] fn get() const -> connection_t* {
return m_connection;
}
[[nodiscard]] fn setup() const -> const setup_t* {
return m_connection ? xcb_get_setup(m_connection) : nullptr;
}
[[nodiscard]] fn rootScreen() const -> screen_t* {
const setup_t* setup = this->setup();
return setup ? xcb_setup_roots_iterator(setup).data : nullptr;
}
};
/**
* RAII wrapper for XCB replies
* Handles automatic cleanup of various XCB reply objects
*/
template <typename T>
class ReplyGuard {
T* m_reply = nullptr;
public:
ReplyGuard() = default;
explicit ReplyGuard(T* reply)
: m_reply(reply) {}
~ReplyGuard() {
if (m_reply)
free(m_reply);
}
// Non-copyable
ReplyGuard(const ReplyGuard&) = delete;
fn operator=(const ReplyGuard&)->ReplyGuard& = delete;
// Movable
ReplyGuard(ReplyGuard&& other) noexcept
: m_reply(std::exchange(other.m_reply, nullptr)) {}
fn operator=(ReplyGuard&& other) noexcept -> ReplyGuard& {
if (this != &other) {
if (m_reply)
free(m_reply);
m_reply = std::exchange(other.m_reply, nullptr);
}
return *this;
}
[[nodiscard]] explicit operator bool() const {
return m_reply != nullptr;
}
[[nodiscard]] fn get() const -> T* {
return m_reply;
}
[[nodiscard]] fn operator->() const->T* {
return m_reply;
}
[[nodiscard]] fn operator*() const->T& {
return *m_reply;
}
};
} // namespace xcb
#endif // __linux__ || __FreeBSD__ || __DragonFly__ || __NetBSD__