diff options
| author | Federico Angelilli <code@fedang.net> | 2024-07-08 16:41:06 +0200 |
|---|---|---|
| committer | Federico Angelilli <code@fedang.net> | 2024-07-08 16:41:06 +0200 |
| commit | 8e392c583c7c0b68bae6204bf98e7f8e077c5bbf (patch) | |
| tree | eec9e15918194a6f4bca9ce4de479783c0ddbada /src/display.c | |
| parent | 5d170a634ead0119f6e5a9f63c23b2b064126f75 (diff) | |
Rewrite display code
Diffstat (limited to 'src/display.c')
| -rw-r--r-- | src/display.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..09549ce --- /dev/null +++ b/src/display.c @@ -0,0 +1,168 @@ +#include <stdlib.h> +#include <stdbool.h> +#include <assert.h> +#include <string.h> + +#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); +} |
