#include #include #include #include #include #include #include "log.h" #include "connection.h" struct Connection { xcb_connection_t *connection; xcb_screen_t *screen; xcb_randr_screen_size_t *screen_size; double screen_dpi; xcb_visualtype_t *visual_type; xcb_depth_t *depth; xcb_xrm_database_t *database; xcb_errors_context_t *errors; xcb_ewmh_connection_t ewmh; }; static bool query_xrm(Connection *con, const char *res, char **value) { if (xcb_xrm_resource_get_string(con->database, res, res, value) >= 0) { log_debug("Xrm query '%s' found '%s'", res, *value); return true; } log_debug("Xrm query '%s' not found", res); return false; } static void update_scale(Connection *con) { char *dpi_value; if (query_xrm(con, "Xft.dpi", &dpi_value)) { con->screen_dpi = strtod(dpi_value, NULL); g_free(dpi_value); } else { con->screen_dpi = (double)con->screen_size->height * 25.4 / (double)con->screen_size->mheight; log_debug("Fallback dpi value '%.2lf'", con->screen_dpi); } } Connection *connection_create() { Connection *con = g_malloc0(sizeof(Connection)); g_assert_nonnull(con); int preferred_screen = 0; con->connection = xcb_connect(NULL, &preferred_screen); g_assert_true(con->connection != NULL && !xcb_connection_has_error(con->connection)); log_debug("Xcb connection established"); log_debug("Default screen %d", preferred_screen); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(con->connection)); while (preferred_screen != 0 && iter.rem) { xcb_screen_next(&iter); preferred_screen--; } con->screen = iter.data; xcb_generic_error_t *error; xcb_randr_query_version_cookie_t version_cookie = xcb_randr_query_version(con->connection, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); xcb_randr_query_version_reply_t *randr_version = xcb_randr_query_version_reply(con->connection, version_cookie, &error); g_assert_null(error); log_debug("RandR loaded [version=%d.%d]", randr_version->major_version, randr_version->minor_version); g_assert_cmpint(randr_version->major_version, >=, 1); xcb_randr_get_screen_info_cookie_t cookie = xcb_randr_get_screen_info(con->connection, con->screen->root); xcb_randr_get_screen_info_reply_t *info_reply = xcb_randr_get_screen_info_reply(con->connection, cookie, &error); g_assert_null(error); con->screen_size = xcb_randr_get_screen_info_sizes(info_reply); g_assert_nonnull(con->screen_size); log_debug("Screen size [width=%d, height=%d]", con->screen_size->width, con->screen_size->height); xcb_randr_select_input(con->connection, con->screen->root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); con->database = xcb_xrm_database_from_default(con->connection); g_assert_nonnull(con->database); g_assert(xcb_errors_context_new(con->connection, &con->errors) == 0); log_debug("Xcb errors loaded"); xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(con->screen); while (depth_iter.rem) { xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(depth_iter.data); while (visual_iter.rem) { if (con->screen->root_visual == visual_iter.data->visual_id) { con->visual_type = visual_iter.data; con->depth = depth_iter.data; goto found_visual; } xcb_visualtype_next(&visual_iter); } xcb_depth_next(&depth_iter); } found_visual: log_debug("Xcb visual type found [id=%u]", con->visual_type->visual_id); xcb_intern_atom_cookie_t *ewmh_cookie = xcb_ewmh_init_atoms(con->connection, &con->ewmh); g_assert_true(xcb_ewmh_init_atoms_replies(&con->ewmh, ewmh_cookie, &error)); g_assert_null(error); log_debug("Xcb ewmh initialized"); update_scale(con); xcb_flush(con->connection); log_debug("Xcb set up"); return con; } void connection_destroy(Connection *con) { xcb_ewmh_connection_wipe(&con->ewmh); xcb_errors_context_free(con->errors); xcb_xrm_database_free(con->database); xcb_disconnect(con->connection); g_free(con); }