diff options
| author | Federico Angelilli <code@fedang.net> | 2024-07-09 17:02:42 +0200 |
|---|---|---|
| committer | Federico Angelilli <code@fedang.net> | 2024-07-09 17:02:42 +0200 |
| commit | 0c567c67933cc48ac57f08167d88a57ce0504524 (patch) | |
| tree | da7c5ccc0935093cf5ecc661371db324ea0005cb /src | |
| parent | 28adc6b395d2fb7545189636cec3651b9c2a5f73 (diff) | |
Add event handling
Diffstat (limited to 'src')
| -rw-r--r-- | src/block.h | 4 | ||||
| -rw-r--r-- | src/comet.c | 30 | ||||
| -rw-r--r-- | src/display.c | 115 | ||||
| -rw-r--r-- | src/display.h | 6 | ||||
| -rw-r--r-- | src/event.c | 136 | ||||
| -rw-r--r-- | src/event.h | 6 | ||||
| -rw-r--r-- | src/layout.c | 4 | ||||
| -rw-r--r-- | src/util.c | 35 | ||||
| -rw-r--r-- | src/util.h | 29 |
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 |
