#include #include #include #include #include "any_log.h" #include "display.h" static bool query_xrm(display_t *display, const char *resource, char **value) { bool found = xcb_xrm_resource_get_string(display->database, resource, NULL, value) >= 0; log_value_debug("Xrm query", "b:found", found, "s:resource", resource, "s:value", *value ? *value : ""); return found; } static void update_xrm(display_t *display) { xcb_flush(display->connection); xcb_xrm_database_t *database = xcb_xrm_database_from_default(display->connection); if (database == NULL) { log_warn("Xrm database couldn't be updated"); return; } xcb_xrm_database_free(display->database); display->database = database; log_debug("Xrm database updated"); } static void update_scale(display_t *display) { char *dpi_value; if (query_xrm(display, "Xft.dpi", &dpi_value)) { display->screen_dpi = strtod(dpi_value, NULL); free(dpi_value); // Ignore invalid values if (display->screen_dpi != 0) return; } display->screen_dpi = (double)display->screen_size->height * 25.4 / (double)display->screen_size->mheight; log_debug("Fallback dpi value '%.2lf'", display->screen_dpi); } // Check if point (px, py) is inside a rectangle in (x, y), (x+w, y), (x, y+h) and (w+h, y+h) static inline bool in_rect(int px, int py, int x, int y, int w, int h) { return px >= x && px <= x + w && py >= y && py <= y + h; } // Check if point (px, py) is inside a circle of radius r and center (x, y) static inline bool in_circle(int px, int py, int x, int y, int r) { int dx = x - px; int dy = y - py; return (dx * dx + dy * dy) <= r * r; } // Check if point (px, py) is inside a capsule in (x, y), (x+w, y), (x, y+h) and (w+h, y+h) static inline bool in_capsule(int px, int py, int x, int y, int w, int h) { assert(w >= h); int radius = h / 2; // Trivial case if (w == h) return in_circle(px, py, x + radius, y + radius, radius); // General case return in_circle(px, py, x + radius, y + radius, radius) || in_circle(px, py, x + w - radius, y + radius, radius) || in_rect(px, py, x + radius, y, w - 2 * radius, h); } void display_init(display_t *display) { memset(display, 0, sizeof(display_t)); int preferred_screen = 0; display->connection = xcb_connect(NULL, &preferred_screen); assert(display->connection != NULL && !xcb_connection_has_error(display->connection)); log_value_debug("Xcb connection established", "i:preferred_screen", preferred_screen); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(display->connection)); while (preferred_screen != 0 && iter.rem) { xcb_screen_next(&iter); preferred_screen--; } display->screen = iter.data; xcb_generic_error_t *error; xcb_randr_query_version_cookie_t version_cookie = xcb_randr_query_version(display->connection, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); xcb_randr_query_version_reply_t *randr_version = xcb_randr_query_version_reply(display->connection, version_cookie, &error); assert(error == NULL); log_value_debug("RandR loaded", "i:major", randr_version->major_version, "i:minor", randr_version->minor_version); assert(randr_version->major_version >= 1); free(randr_version); xcb_randr_get_screen_info_cookie_t cookie = xcb_randr_get_screen_info(display->connection, display->screen->root); display->info_reply = xcb_randr_get_screen_info_reply(display->connection, cookie, &error); assert(error == NULL); display->screen_size = xcb_randr_get_screen_info_sizes(display->info_reply); assert(display->screen_size != NULL); log_debug("Display size [width=%d, height=%d]", display->screen_size->width, display->screen_size->height); xcb_randr_select_input(display->connection, display->screen->root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); log_debug("Searching 32 bit visual"); display->visual_type = xcb_aux_find_visual_by_attrs(display->screen, XCB_VISUAL_CLASS_TRUE_COLOR, 32); display->screen_depth = 32; if (display->visual_type == NULL) { // NOTE: 24 bit visuals don't have the alpha channel for transparency log_debug("Falling back to 24 bit visual"); display->visual_type = xcb_aux_find_visual_by_attrs(display->screen, XCB_VISUAL_CLASS_TRUE_COLOR, 24); display->screen_depth = 24; } assert(display->visual_type != 0); xcb_intern_atom_cookie_t *ewmh_cookie = xcb_ewmh_init_atoms(display->connection, &display->ewmh); assert(xcb_ewmh_init_atoms_replies(&display->ewmh, ewmh_cookie, &error)); assert(error == NULL); log_debug("Xcb EWMH initialized"); display->database = xcb_xrm_database_from_default(display->connection); assert(display->database != NULL); // TODO: Dpi aware scaling update_scale(display); assert(xcb_errors_context_new(display->connection, &display->errors) == 0); log_debug("Xcb errors loaded"); xcb_flush(display->connection); } void display_close(display_t *display) { xcb_ewmh_connection_wipe(&display->ewmh); xcb_errors_context_free(display->errors); xcb_xrm_database_free(display->database); xcb_disconnect(display->connection); free(display->info_reply); }