aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFederico Angelilli <code@fedang.net>2024-07-09 17:02:42 +0200
committerFederico Angelilli <code@fedang.net>2024-07-09 17:02:42 +0200
commit0c567c67933cc48ac57f08167d88a57ce0504524 (patch)
treeda7c5ccc0935093cf5ecc661371db324ea0005cb /src
parent28adc6b395d2fb7545189636cec3651b9c2a5f73 (diff)
Add event handling
Diffstat (limited to 'src')
-rw-r--r--src/block.h4
-rw-r--r--src/comet.c30
-rw-r--r--src/display.c115
-rw-r--r--src/display.h6
-rw-r--r--src/event.c136
-rw-r--r--src/event.h6
-rw-r--r--src/layout.c4
-rw-r--r--src/util.c35
-rw-r--r--src/util.h29
9 files changed, 286 insertions, 79 deletions
diff --git a/src/block.h b/src/block.h
index 2179348..33fc797 100644
--- a/src/block.h
+++ b/src/block.h
@@ -32,7 +32,7 @@ typedef struct block block_t;
// Triggered when an event is directed towards the block
//
-typedef void (*block_event_t)(block_t *block, event_t *event);
+typedef void (*block_event_t)(block_t *block, event_t event);
// Regularly called depending on the inverval passed from the last update
//
@@ -47,7 +47,7 @@ struct block {
int line_width;
int x_padding, y_padding;
int min_width, max_width;
- int update_interval;
+ struct timespec update_interval;
struct timespec update_last;
block_update_t update_cb;
block_event_t event_cb;
diff --git a/src/comet.c b/src/comet.c
index d496590..64d24c0 100644
--- a/src/comet.c
+++ b/src/comet.c
@@ -4,10 +4,32 @@
#include "window.h"
#include "layout.h"
+#include "util.h"
+#include "event.h"
#define ANY_LOG_IMPLEMENT
#include "any_log.h"
+void update(block_t *block)
+{
+ if (block->update_cb != NULL) {
+ struct timespec now, diff;
+ timespec_get(&now, TIME_UTC);
+ diff = timespec_diff(block->update_last, now);
+
+ if (timespec_greater(diff, block->update_interval)) {
+ log_debug("Updating block");
+ block->update_cb(block);
+ block->update_last = now;
+ }
+ }
+
+ if (block->type == BLOCK_GROUP) {
+ for (int i = 0; i < block->group.n_children; i++)
+ update(&block->group.children[i]);
+ }
+}
+
cairo_surface_t *render(layout_t *layout, layout_info_t info)
{
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, info.width, info.height);
@@ -30,7 +52,6 @@ cairo_surface_t *render(layout_t *layout, layout_info_t info)
return surface;
}
-
int main(int argc, char **argv)
{
setlocale(LC_CTYPE, "");
@@ -124,13 +145,16 @@ int main(int argc, char **argv)
window_move(&window, x_padding, y_padding);
while (true) {
+ update(&top);
+
layout_t *layout = layout_create(&top, info);
+
+ event_dispatch(&display, layout);
+
cairo_surface_t *surface = render(layout, info);
window_present(&window, surface, width, height);
cairo_surface_destroy(surface);
layout_free(layout);
-
- sleep(1);
}
window_close(&window);
diff --git a/src/display.c b/src/display.c
index 09549ce..a34e609 100644
--- a/src/display.c
+++ b/src/display.c
@@ -6,77 +6,6 @@
#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));
@@ -149,8 +78,7 @@ void display_init(display_t *display)
display->database = xcb_xrm_database_from_default(display->connection);
assert(display->database != NULL);
- // TODO: Dpi aware scaling
- update_scale(display);
+ display_update_scale(display);
assert(xcb_errors_context_new(display->connection, &display->errors) == 0);
log_debug("Xcb errors loaded");
@@ -158,6 +86,47 @@ void display_init(display_t *display)
xcb_flush(display->connection);
}
+bool display_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;
+}
+
+void display_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");
+}
+
+void display_update_scale(display_t *display)
+{
+ char *dpi_value;
+ if (display_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);
+}
+
void display_close(display_t *display)
{
xcb_ewmh_connection_wipe(&display->ewmh);
diff --git a/src/display.h b/src/display.h
index 8d99cad..48245e6 100644
--- a/src/display.h
+++ b/src/display.h
@@ -23,6 +23,12 @@ typedef struct {
void display_init(display_t *display);
+bool display_query_xrm(display_t *display, const char *resource, char **value);
+
+void display_update_xrm(display_t *display);
+
+void display_update_scale(display_t *display);
+
void display_close(display_t *display);
#endif
diff --git a/src/event.c b/src/event.c
new file mode 100644
index 0000000..1c16c49
--- /dev/null
+++ b/src/event.c
@@ -0,0 +1,136 @@
+#include "util.h"
+#include "layout.h"
+#include "event.h"
+#include "any_log.h"
+
+bool event_dispatch_block(layout_t *layout, event_t event)
+{
+ int width = layout->width - 2 * layout->x_padding;
+ int height = layout->height - 2 * layout->y_padding;
+
+ if (!in_capsule(event.x, event.y, layout->x, layout->y, width, height))
+ return false;
+
+ for (int i = 0; i < layout->n_children; i++) {
+ if (event_dispatch_block(layout->children[i], event))
+ return true;
+ }
+
+ log_value_debug("Dispatching event to block",
+ "i:event_x", event.x,
+ "i:event_y", event.y,
+ "i:x", layout->x,
+ "i:y", layout->y,
+ "i:width", layout->width,
+ "i:height", layout->height);
+
+ if (layout->block->event_cb != NULL) {
+ layout->block->event_cb(layout->block, event);
+ log_debug("Called event callback");
+ }
+
+ return true;
+}
+
+void event_dispatch(display_t *display, layout_t *layout)
+{
+ xcb_generic_event_t *xevent;
+
+ while ((xevent = xcb_poll_for_event(display->connection)) != NULL) {
+ int type = xevent->response_type & ~0x80;
+ switch (type) {
+ case 0: {
+ xcb_generic_error_t *error = (xcb_generic_error_t *)xevent;
+
+ const char *extension;
+ const char *name = xcb_errors_get_name_for_error(display->errors, error->error_code, &extension);
+ const char *major = xcb_errors_get_name_for_major_code(display->errors, error->major_code);
+ const char *minor = xcb_errors_get_name_for_minor_code(display->errors, error->major_code, error->minor_code);
+
+ log_value_error("Xcb error",
+ "s:name", name,
+ "s:extension", extension ? extension : "none",
+ "s:major", major,
+ "s:minor", minor ? minor : "none"
+ "u:resource", (unsigned int)error->resource_id,
+ "u:sequence", (unsigned int)error->sequence);
+
+ // TODO: Handle errors instead of aborting
+ abort();
+ break;
+ }
+
+ case XCB_EXPOSE: {
+ xcb_expose_event_t *expose = (xcb_expose_event_t *)xevent;
+ log_trace("Processing 'Expose' event");
+
+ // Redraw
+ break;
+ }
+
+ case XCB_CREATE_NOTIFY: {
+ xcb_create_notify_event_t *create = (xcb_create_notify_event_t *)xevent;
+ log_trace("Processing 'CreateNotify' event");
+
+ // TODO: Circulate top the window if override_redirect == 0
+ break;
+ }
+
+ case XCB_BUTTON_RELEASE: {
+ xcb_button_release_event_t *button = (xcb_button_release_event_t *)xevent;
+ log_trace("Processing 'ButtonRelease' event");
+
+ event_t event = {
+ .x = button->event_x,
+ .y = button->event_y,
+ };
+
+ switch (button->detail) {
+ case XCB_BUTTON_INDEX_1:
+ event.type = EVENT_RIGHT_CLICK;
+ event_dispatch_block(layout, event);
+ break;
+
+ case XCB_BUTTON_INDEX_2:
+ event.type = EVENT_LEFT_CLICK;
+ event_dispatch_block(layout, event);
+ break;
+
+ case XCB_BUTTON_INDEX_3:
+ event.type = EVENT_MIDDLE_CLICK;
+ event_dispatch_block(layout, event);
+ break;
+
+ default:
+ log_debug("Ignoring button release", button->detail);
+ break;
+ }
+ break;
+ }
+
+ case XCB_PROPERTY_NOTIFY: {
+ xcb_property_notify_event_t *property = (xcb_property_notify_event_t *)xevent;
+ log_trace("Processing 'PropertyNotify' event");
+
+ if (property->atom == XCB_ATOM_RESOURCE_MANAGER) {
+ display_update_xrm(display);
+ display_update_scale(display);
+ // Redraw
+ }
+ break;
+ }
+
+ default: {
+ const char *extension;
+ const char *name = xcb_errors_get_name_for_xcb_event(display->errors, xevent, &extension);
+
+ log_value_debug("Ignoring Xcb event",
+ "s:name", name,
+ "u:type", xevent->response_type & 0x7f,
+ "s:extension", extension ? extension : "none");
+ break;
+ }
+ }
+ free(xevent);
+ }
+}
diff --git a/src/event.h b/src/event.h
index 30618ac..609458a 100644
--- a/src/event.h
+++ b/src/event.h
@@ -1,7 +1,7 @@
#ifndef COMET_EVENT_H
#define COMET_EVENT_H
-#include <stddef.h>
+#include "display.h"
typedef enum {
EVENT_LEFT_CLICK,
@@ -15,4 +15,8 @@ typedef struct {
int x, y;
} event_t;
+typedef struct layout layout_t;
+
+void event_dispatch(display_t *display, layout_t *layout);
+
#endif
diff --git a/src/layout.c b/src/layout.c
index db6e435..126e2f5 100644
--- a/src/layout.c
+++ b/src/layout.c
@@ -20,6 +20,7 @@ layout_t *layout_create(block_t *block, layout_info_t info)
if (block->type == BLOCK_GROUP) {
layout->children = calloc(block->group.n_children, sizeof(layout_t));
+ int x = info.x_offset;
for (int i = 0; i < block->group.n_children; i++) {
if (block->group.children[i].hidden)
@@ -29,6 +30,9 @@ layout_t *layout_create(block_t *block, layout_info_t info)
info.x_offset += child->width + block->group.spacing;
layout->children[layout->n_children++] = child;
}
+
+ layout_t *last = layout->children[layout->n_children - 1];
+ layout->width = last->x + last->width - x;
} else if (block->type == BLOCK_TEXT) {
layout->pl = pango_layout_new(info.context);
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..075c55c
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,35 @@
+#include <assert.h>
+
+#include "util.h"
+
+struct timespec timespec_diff(struct timespec a, struct timespec b)
+{
+ bool over = (b.tv_nsec - a.tv_nsec) < 0;
+ struct timespec diff = {
+ .tv_sec = b.tv_sec - a.tv_sec - over,
+ .tv_nsec = b.tv_nsec - a.tv_nsec + over * 1000000000ul,
+ };
+ return diff;
+}
+
+bool timespec_greater(struct timespec a, struct timespec b)
+{
+ return a.tv_sec > b.tv_sec
+ || (a.tv_sec == b.tv_sec && a.tv_nsec > b.tv_nsec);
+}
+
+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);
+}
+
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..9e84a7c
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,29 @@
+#ifndef COMET_UTIL_H
+#define COMET_UTIL_H
+
+#include <time.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+struct timespec timespec_diff(struct timespec a, struct timespec b);
+
+bool timespec_greater(struct timespec a, struct timespec b);
+
+// 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)
+bool in_capsule(int px, int py, int x, int y, int w, int h);
+
+#endif