aboutsummaryrefslogtreecommitdiff
path: root/src/display.c
diff options
context:
space:
mode:
authorFederico Angelilli <code@fedang.net>2024-07-08 16:41:06 +0200
committerFederico Angelilli <code@fedang.net>2024-07-08 16:41:06 +0200
commit8e392c583c7c0b68bae6204bf98e7f8e077c5bbf (patch)
treeeec9e15918194a6f4bca9ce4de479783c0ddbada /src/display.c
parent5d170a634ead0119f6e5a9f63c23b2b064126f75 (diff)
Rewrite display code
Diffstat (limited to 'src/display.c')
-rw-r--r--src/display.c168
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);
+}