#include #include #include #include #include #include #include "log.h" #include "connection.h" typedef struct { GSource source; Connection *con; gpointer fd_tag; xcb_generic_event_t *event; } EventSource; static gboolean source_check(GSource *source) { EventSource *xsource = (EventSource *)source; xcb_flush(xsource->con->connection); g_assert_null(xsource->event); GIOCondition flags = g_source_query_unix_fd(source, xsource->fd_tag); if (flags & G_IO_IN) { g_assert_false(xcb_connection_has_error(xsource->con->connection)); xsource->event = xcb_poll_for_event(xsource->con->connection); } return xsource->event != NULL; } static gboolean source_dispatch(GSource *source, GSourceFunc callback, gpointer data) { EventSource *xsource = (EventSource *)source; g_assert_nonnull(xsource->event); do { switch (xsource->event->response_type & ~0x80) { case 0: { xcb_generic_error_t *error = (xcb_generic_error_t *)xsource->event; const char *extension; const char *name = xcb_errors_get_name_for_error(xsource->con->errors, error->error_code, &extension); const char *major = xcb_errors_get_name_for_major_code(xsource->con->errors, error->major_code); const char *minor = xcb_errors_get_name_for_minor_code(xsource->con->errors, error->major_code, error->minor_code); // TODO: Handle errors instead of aborting log_error("Xcb error '%s' [extension=%s, major=%s, minor=%s, resource=%u, sequence=%u]", name, extension ? extension : "none", major, minor ? minor : "none", (unsigned int)error->resource_id, (unsigned int)error->sequence); break; } case XCB_EXPOSE: { xcb_expose_event_t *expose = (xcb_expose_event_t *)xsource->event; log_debug("Processing event 'Expose' [type=%d]", XCB_EXPOSE); // Redraw //callback(data); break; } case XCB_BUTTON_RELEASE: { log_debug("Mouse event"); break; } case XCB_PROPERTY_NOTIFY: { xcb_property_notify_event_t *property = (xcb_property_notify_event_t *)xsource->event; log_debug("Processing event 'PropertyNotify' [type=%d]", XCB_PROPERTY_NOTIFY); if (property->atom == XCB_ATOM_RESOURCE_MANAGER) { //window_update_scale(data); // Redraw //callback(data); } break; } default: { const char *extension; const char *name = xcb_errors_get_name_for_xcb_event(xsource->con->errors, xsource->event, &extension); log_debug("Ignoring event '%s' [type=%d, extension=%s]", name, xsource->event->response_type & 0x7f, extension ? extension : "none"); } } } while ((xsource->event = xcb_poll_for_event(xsource->con->connection)) != NULL); return G_SOURCE_CONTINUE; } static GSourceFuncs source_fns = { NULL, source_check, source_dispatch, NULL, }; static void attach_source(Connection *con) { EventSource *source = (EventSource *)g_source_new(&source_fns, sizeof(EventSource)); con->source = (GSource *)source; g_source_set_static_name(con->source, "EventSource"); source->con = con; source->event = NULL; source->fd_tag = g_source_add_unix_fd(con->source, xcb_get_file_descriptor(con->connection), G_IO_IN | G_IO_HUP | G_IO_ERR); // TODO: Draw should be decoupled from this file //g_source_set_callback(con->source, G_SOURCE_FUNC(draw), win, NULL); g_source_attach(con->source, NULL); } 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"); attach_source(con); log_debug("Xcb event loop attached"); return con; } void connection_destroy(Connection *con) { g_source_destroy(con->source); g_source_unref(con->source); 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); } // vim: ts=4 sw=4 et