diff options
| author | Federico Angelilli <code@fedang.net> | 2024-11-22 14:25:36 +0100 |
|---|---|---|
| committer | Federico Angelilli <code@fedang.net> | 2024-11-22 14:25:36 +0100 |
| commit | 669fb0b98bc6802a34d7577f597f1a5aeb691e22 (patch) | |
| tree | 35bf1e66f18e1e1d63b19f70ddf09f03c4c16adf /src/event.c | |
| parent | 10d61d0a765ff3a55aab32e4d28e5b7298d9e89c (diff) | |
Track mouse hovering
Diffstat (limited to 'src/event.c')
| -rw-r--r-- | src/event.c | 281 |
1 files changed, 159 insertions, 122 deletions
diff --git a/src/event.c b/src/event.c index c07f2e8..3c1ae81 100644 --- a/src/event.c +++ b/src/event.c @@ -9,22 +9,26 @@ const char *event_type_to_string(event_type_t type) { const char *types[] = { - "hover", + "trigger", "left_click", "middle_click", "right_click", "scroll_up", "scroll_down", - "trigger", + "hover_start", + "hover_move", + "hover_stop", }; - assert(type >= EVENT_HOVER && type <= EVENT_TRIGGER); + assert(type >= EVENT_TRIGGER && type <= EVENT_HOVER_STOP); return types[type]; } bool event_is_hover(event_t event) { - return event.type == EVENT_HOVER; + return event.type == EVENT_HOVER_START + || event.type == EVENT_HOVER_MOVE + || event.type == EVENT_HOVER_STOP; } bool event_is_click(event_t event) @@ -40,27 +44,13 @@ bool event_is_scroll(event_t event) || event.type == EVENT_SCROLL_DOWN; } -static bool event_dispatch_mouse(layout_t *layout, event_t event) +static void event_dispatch_callback(layout_t *layout, event_t event) { - int width = layout->width - 2 * layout->x_padding; - int height = layout->height - 2 * layout->y_padding; - - if (!check_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_mouse(&layout->children[i], event)) - return true; - } - - // XXX - if (event_is_hover(event)) return false; - block_event_t event_fn = layout->block->type == BLOCK_SPEC ? ((block_spec_t *)layout->block)->event_fn : NULL; - log_value_debug("Block received a mouse interaction", + log_value_debug("Block received a mouse event", "s:event", event_type_to_string(event.type), "i:event_x", event.x, "i:event_y", event.y, @@ -75,143 +65,190 @@ static bool event_dispatch_mouse(layout_t *layout, event_t event) event_fn(layout, event); log_trace("Completed event callback"); } +} - return true; +static bool event_dispatch_block(layout_t *layout, block_t *block, event_t event) +{ + if (layout->block == block) { + event_dispatch_callback(layout, event); + return true; + } + + for (int i = 0; i < layout->n_children; i++) { + if (event_dispatch_block(&layout->children[i], block, event)) + return true; + } + + return false; } -static void event_dispatch_once(window_t *window, layout_t *layout, xcb_generic_event_t *xevent) +static bool event_dispatch_mouse(event_state_t *state, layout_t *layout, event_t event) { - int type = xevent->response_type & ~0x80; - switch (type) { - case 0: { - xcb_generic_error_t *error = (xcb_generic_error_t *)xevent; - - const char *extension = NULL; - const char *name = xcb_errors_get_name_for_error(window->display->errors, error->error_code, &extension); - const char *major = xcb_errors_get_name_for_major_code(window->display->errors, error->major_code); - const char *minor = xcb_errors_get_name_for_minor_code(window->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; - } + int width = layout->width - 2 * layout->x_padding; + int height = layout->height - 2 * layout->y_padding; + + if (!check_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_mouse(state, &layout->children[i], event)) + return true; + } - case XCB_EXPOSE: { - xcb_expose_event_t *expose = (xcb_expose_event_t *)xevent; - log_trace("Processing 'Expose' event"); + if (event_is_hover(event)) { + if (layout->block != state->hovered) { + event.type = EVENT_HOVER_STOP; + event_dispatch_block(state->layout, state->hovered, event); - // Redraw - (void)expose; - break; + event.type = EVENT_HOVER_START; + state->hovered = layout->block; } + } + + event_dispatch_callback(layout, event); + return true; +} - case XCB_CREATE_NOTIFY: { - xcb_create_notify_event_t *create = (xcb_create_notify_event_t *)xevent; - log_trace("Processing 'CreateNotify' event"); +void event_dispatch(event_state_t *state, window_t *window) +{ + xcb_generic_event_t *xevent; + xcb_motion_notify_event_t *motion = NULL; - (void)create; + while ((xevent = xcb_poll_for_event(window->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 = NULL; + const char *name = xcb_errors_get_name_for_error(window->display->errors, error->error_code, &extension); + const char *major = xcb_errors_get_name_for_major_code(window->display->errors, error->major_code); + const char *minor = xcb_errors_get_name_for_minor_code(window->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; + } - if (window->cw_params.override_redirect) break; - xcb_circulate_window(window->display->connection, XCB_CIRCULATE_RAISE_LOWEST, window->window); - break; - } + case XCB_EXPOSE: { + xcb_expose_event_t *expose = (xcb_expose_event_t *)xevent; + log_trace("Processing 'Expose' event"); - case XCB_LEAVE_NOTIFY: { - xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *)xevent; - log_trace("Processing 'LeaveNotify' event"); + // Redraw + (void)expose; + break; + } - if (leave->event != window->window) break; - // Leaving - break; - } + case XCB_CREATE_NOTIFY: { + xcb_create_notify_event_t *create = (xcb_create_notify_event_t *)xevent; + log_trace("Processing 'CreateNotify' event"); - case XCB_BUTTON_RELEASE: { - xcb_button_release_event_t *button = (xcb_button_release_event_t *)xevent; - log_trace("Processing 'ButtonRelease' event"); + (void)create; - if (button->detail < XCB_BUTTON_INDEX_1 || button->detail > XCB_BUTTON_INDEX_5) { - log_value_trace("Ignoring button release", - "u:value", button->detail); + if (window->cw_params.override_redirect) break; + xcb_circulate_window(window->display->connection, XCB_CIRCULATE_RAISE_LOWEST, window->window); break; } - event_t event = { - .x = button->event_x, - .y = button->event_y, - .type = button->detail, - }; + case XCB_LEAVE_NOTIFY: { + xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *)xevent; + log_trace("Processing 'LeaveNotify' event"); - event_dispatch_mouse(layout, event); - break; - } + if (leave->event != window->window) break; + if (state->hovered == NULL) break; - case XCB_PROPERTY_NOTIFY: { - xcb_property_notify_event_t *property = (xcb_property_notify_event_t *)xevent; - log_trace("Processing 'PropertyNotify' event"); + event_t event = { + .x = leave->event_x, + .y = leave->event_y, + .type = EVENT_HOVER_STOP, + }; - if (property->atom == XCB_ATOM_RESOURCE_MANAGER) { - display_update_xrm(window->display); - display_update_scale(window->display); - // Redraw + event_dispatch_block(state->layout, state->hovered, event); + state->hovered = NULL; + + if (motion != NULL && motion->time <= leave->time) { + log_trace("Suppressed older 'MotionNotify' event"); + + free(motion); + motion = NULL; + } + break; } - break; - } - case XCB_MOTION_NOTIFY: { - xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)xevent; - log_trace("Processing 'MotionNotify' event"); + 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 = motion->event_x, - .y = motion->event_y, - .type = EVENT_HOVER, - }; + if (button->detail < XCB_BUTTON_INDEX_1 || button->detail > XCB_BUTTON_INDEX_5) { + log_value_trace("Ignoring button release", + "u:value", button->detail); + break; + } - event_dispatch_mouse(layout, event); - break; - } + event_t event = { + .x = button->event_x, + .y = button->event_y, + .type = button->detail, + }; + + event_dispatch_mouse(state, state->layout, event); + break; + } - default: { - const char *extension = NULL; - const char *name = xcb_errors_get_name_for_xcb_event(window->display->errors, xevent, &extension); + case XCB_PROPERTY_NOTIFY: { + xcb_property_notify_event_t *property = (xcb_property_notify_event_t *)xevent; + log_trace("Processing 'PropertyNotify' event"); - log_value_trace("Ignoring Xcb event", - "s:name", name, - "u:type", type, - "s:extension", extension ? extension : "none"); - break; - } - } -} + if (property->atom == XCB_ATOM_RESOURCE_MANAGER) { + display_update_xrm(window->display); + display_update_scale(window->display); + // Redraw + } + break; + } -void event_dispatch(window_t *window, layout_t *layout) -{ - xcb_generic_event_t *xevent, *motion = NULL; - while ((xevent = xcb_poll_for_event(window->display->connection)) != NULL) { + case XCB_MOTION_NOTIFY: { + log_trace("Deferred processing 'MotionNotify' event"); - int type = xevent->response_type & ~0x80; - if (type == XCB_MOTION_NOTIFY) { - free(motion); - motion = xevent; - continue; - } + free(motion); + motion = (xcb_motion_notify_event_t *)xevent; + continue; + } - event_dispatch_once(window, layout, xevent); + default: { + const char *extension = NULL; + const char *name = xcb_errors_get_name_for_xcb_event(window->display->errors, xevent, &extension); + + log_value_trace("Ignoring Xcb event", + "s:name", name, + "u:type", type, + "s:extension", extension ? extension : "none"); + break; + } + } free(xevent); } // We only consider the last motion notify received // if (motion != NULL) { - event_dispatch_once(window, layout, motion); + log_trace("Processing 'MotionNotify' event"); + + event_t event = { + .x = motion->event_x, + .y = motion->event_y, + .type = EVENT_HOVER_MOVE, + }; + + event_dispatch_mouse(state, state->layout, event); free(motion); } } |
