From a111aa2afa191cde10ae39b7d9284b2b4867ffcb Mon Sep 17 00:00:00 2001 From: Stacy Harper Date: Sat, 14 Aug 2021 17:31:28 +0200 Subject: [PATCH] mouse and touch support on wayland --- client/common/common.c | 6 +- lib/bemenu.h | 111 +++++++++++- lib/internal.h | 28 +++ lib/menu.c | 264 ++++++++++++++++++++++++++++ lib/renderers/wayland/registry.c | 283 ++++++++++++++++++++++++++++++- lib/renderers/wayland/wayland.c | 127 +++++++++++++- lib/renderers/wayland/wayland.h | 36 ++++ 7 files changed, 850 insertions(+), 5 deletions(-) diff --git a/client/common/common.c b/client/common/common.c index 69ad84f..bed6d63 100644 --- a/client/common/common.c +++ b/client/common/common.c @@ -502,11 +502,15 @@ run_menu(const struct client *client, struct bm_menu *menu, void (*item_cb)(cons uint32_t unicode; enum bm_key key; + struct bm_pointer pointer; + struct bm_touch touch; enum bm_run_result status = BM_RUN_RESULT_RUNNING; do { bm_menu_render(menu); key = bm_menu_poll_key(menu, &unicode); - } while ((status = bm_menu_run_with_key(menu, key, unicode)) == BM_RUN_RESULT_RUNNING); + pointer = bm_menu_poll_pointer(menu); + touch = bm_menu_poll_touch(menu); + } while ((status = bm_menu_run_with_events(menu, key, pointer, touch, unicode)) == BM_RUN_RESULT_RUNNING); switch (status) { case BM_RUN_RESULT_SELECTED: diff --git a/lib/bemenu.h b/lib/bemenu.h index 7fdf94f..f5fcede 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -253,6 +253,70 @@ enum bm_key { BM_KEY_LAST }; +enum bm_pointer_key { + BM_POINTER_KEY_NONE, + BM_POINTER_KEY_PRIMARY, +}; + +enum bm_pointer_axis { + BM_POINTER_AXIS_VERTICAL = 0, + BM_POINTER_AXIS_HORIZONTAL = 1, +}; + +enum bm_pointer_event_mask { + POINTER_EVENT_ENTER = 1 << 1, + POINTER_EVENT_LEAVE = 1 << 2, + POINTER_EVENT_MOTION = 1 << 3, + POINTER_EVENT_BUTTON = 1 << 4, + POINTER_EVENT_AXIS = 1 << 5, + POINTER_EVENT_AXIS_SOURCE = 1 << 6, + POINTER_EVENT_AXIS_STOP = 1 << 7, + POINTER_EVENT_AXIS_DISCRETE = 1 << 8, +}; + +enum bm_pointer_state_mask { + POINTER_STATE_RELEASED, + POINTER_STATE_PRESSED, +}; + +struct bm_pointer { + uint32_t event_mask; + uint32_t pos_x, pos_y; + uint32_t button, state; + uint32_t time; + struct { + bool valid; + int32_t value; + int32_t discrete; + } axes[2]; + uint32_t axis_source; +}; + +enum bm_touch_event_mask { + TOUCH_EVENT_DOWN = 1 << 0, + TOUCH_EVENT_UP = 1 << 1, + TOUCH_EVENT_MOTION = 1 << 2, + TOUCH_EVENT_CANCEL = 1 << 3, + TOUCH_EVENT_SHAPE = 1 << 4, + TOUCH_EVENT_ORIENTATION = 1 << 5, +}; + +struct bm_touch_point { + bool valid; + int32_t id; + uint32_t event_mask; + int32_t start_x, start_y; + int32_t pos_x, pos_y; + uint32_t major, minor; + uint32_t orientation; +}; + +struct bm_touch { + uint32_t time; + uint32_t serial; + struct bm_touch_point points[2]; +}; + /** * Colorable element constants. * @@ -486,6 +550,22 @@ BM_PUBLIC void bm_menu_set_cursor_height(struct bm_menu *menu, uint32_t cursor_h */ BM_PUBLIC uint32_t bm_menu_get_cursor_height(struct bm_menu *menu); +/** + * Get with of menu in pixels. + * + * @param menu bm_menu instance where to get line height. + * @return uint32_t for max amount the menu height. + */ +BM_PUBLIC uint32_t bm_menu_get_height(struct bm_menu *menu); + +/** + * Get with of menu in pixels. + * + * @param menu bm_menu instance where to get line width. + * @return uint32_t for max amount the menu width. + */ +BM_PUBLIC uint32_t bm_menu_get_width(struct bm_menu *menu); + /** * Set a hexadecimal color for element. * @@ -809,15 +889,44 @@ BM_PUBLIC void bm_menu_filter(struct bm_menu *menu); */ BM_PUBLIC enum bm_key bm_menu_poll_key(struct bm_menu *menu, uint32_t *out_unicode); +/** + * Poll pointer and unicode from underlying UI toolkit. + * + * This function will block on **curses** renderer. + * + * @param menu bm_menu instance from which to poll. + * @return bm_pointer for polled pointer. + */ +BM_PUBLIC struct bm_pointer bm_menu_poll_pointer(struct bm_menu *menu); + +/** + * Poll touch and unicode from underlying UI toolkit. + * + * This function will block on **curses** renderer. + * + * @param menu bm_menu instance from which to poll. + * @return bm_touch for polled touch. + */ +BM_PUBLIC struct bm_touch bm_menu_poll_touch(struct bm_menu *menu); + +/** + * Enforce a release of the touches. + * + * @param menu bm_menu instance from which to poll. + */ +BM_PUBLIC void bm_menu_release_touch(struct bm_menu *menu); + /** * Advances menu logic with key and unicode as input. * * @param menu bm_menu instance to be advanced. * @param key Key input that will advance menu logic. + * @param pointer Pointer input that will advance menu logic. + * @param touch Touch input that will advance menu logic. * @param unicode Unicode input that will advance menu logic. * @return bm_run_result for menu state. */ -BM_PUBLIC enum bm_run_result bm_menu_run_with_key(struct bm_menu *menu, enum bm_key key, uint32_t unicode); +BM_PUBLIC enum bm_run_result bm_menu_run_with_events(struct bm_menu *menu, enum bm_key key, struct bm_pointer pointer, struct bm_touch touch, uint32_t unicode); /** @} Menu Logic */ diff --git a/lib/internal.h b/lib/internal.h index fa46ae4..d82704e 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -66,12 +66,40 @@ struct render_api { */ uint32_t (*get_displayed_count)(const struct bm_menu *menu); + /** + * Get height by the underlying renderer; + */ + uint32_t (*get_height)(const struct bm_menu *menu); + + /** + * Get width by the underlying renderer; + */ + uint32_t (*get_width)(const struct bm_menu *menu); + /** * If the underlying renderer is a UI toolkit. (curses, etc...) * There might be possibility to get user input, and this should be thus implemented. */ enum bm_key (*poll_key)(const struct bm_menu *menu, uint32_t *unicode); + /** + * If the underlying renderer is a UI toolkit. (curses, etc...) + * There might be possibility to get user pointer, and this should be thus implemented. + */ + struct bm_pointer (*poll_pointer)(const struct bm_menu *menu); + + /** + * If the underlying renderer is a UI toolkit. (curses, etc...) + * There might be possibility to get user touch, and this should be thus implemented. + */ + struct bm_touch (*poll_touch)(const struct bm_menu *menu); + + /** + * Enforce a release of the touches + * There might be possibility to get user touch, and this should be thus implemented. + */ + void (*release_touch)(const struct bm_menu *menu); + /** * Tells underlying renderer to draw the menu. */ diff --git a/lib/menu.c b/lib/menu.c index 2ea9424..1b4603e 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -293,6 +293,7 @@ uint32_t bm_menu_get_line_height(struct bm_menu *menu) { assert(menu); + return menu->line_height; } @@ -310,6 +311,34 @@ bm_menu_get_cursor_height(struct bm_menu *menu) return menu->cursor_height; } +uint32_t +bm_menu_get_height(struct bm_menu *menu) +{ + assert(menu); + + uint32_t height = 0; + + if (menu->renderer->api.get_height) { + height = menu->renderer->api.get_height(menu); + } + + return height; +} + +uint32_t +bm_menu_get_width(struct bm_menu *menu) +{ + assert(menu); + + uint32_t width = 0; + + if (menu->renderer->api.get_width) { + width = menu->renderer->api.get_width(menu); + } + + return width; +} + bool bm_menu_set_color(struct bm_menu *menu, enum bm_color color, const char *hex) { @@ -707,6 +736,41 @@ bm_menu_poll_key(struct bm_menu *menu, uint32_t *out_unicode) return key; } +struct bm_pointer +bm_menu_poll_pointer(struct bm_menu *menu) +{ + assert(menu); + + struct bm_pointer pointer = {0}; + + if (menu->renderer->api.poll_pointer) + pointer = menu->renderer->api.poll_pointer(menu); + + return pointer; +} + +struct bm_touch +bm_menu_poll_touch(struct bm_menu *menu) +{ + assert(menu); + + struct bm_touch touch = {0}; + + if (menu->renderer->api.poll_touch) + touch = menu->renderer->api.poll_touch(menu); + + return touch; +} + +void +bm_menu_release_touch(struct bm_menu *menu) +{ + assert(menu); + + if (menu->renderer->api.release_touch) + menu->renderer->api.release_touch(menu); +} + static void menu_next(struct bm_menu *menu, uint32_t count, bool wrap) { @@ -727,6 +791,41 @@ menu_prev(struct bm_menu *menu, uint32_t count, bool wrap) } } +static void +menu_point_select(struct bm_menu *menu, uint32_t posx, uint32_t posy, uint32_t displayed) +{ + (void) posx; + uint32_t selected_line = posy / (bm_menu_get_height(menu) / displayed); + uint16_t current_page_index = menu->index / menu->lines; + + if (0 == selected_line) { // Mouse over title bar + return; + } + + if (selected_line >= displayed) { // This might be useless + return; + } + + menu->index = current_page_index * menu->lines + (selected_line - 1); +} + +static void +menu_scroll_down(struct bm_menu *menu, uint16_t count) +{ + if (menu->index / menu->lines != count / menu->lines) { // not last page + menu->index = ((menu->index / menu->lines) + 1) * menu->lines; + } +} + +static void +menu_scroll_up(struct bm_menu *menu, uint16_t count) +{ + (void) count; + if (menu->index / menu->lines) { // not first page + menu->index = ((menu->index / menu->lines) - 1) * menu->lines + menu->lines - 1; + } +} + enum bm_run_result bm_menu_run_with_key(struct bm_menu *menu, enum bm_key key, uint32_t unicode) { @@ -954,4 +1053,169 @@ bm_menu_run_with_key(struct bm_menu *menu, enum bm_key key, uint32_t unicode) return BM_RUN_RESULT_RUNNING; } +enum bm_run_result +bm_menu_run_with_pointer(struct bm_menu *menu, struct bm_pointer pointer, uint32_t unicode) +{ + (void) unicode; + uint32_t count; + bm_menu_get_filtered_items(menu, &count); + + uint32_t displayed = 0; + if (menu->renderer->api.get_displayed_count) + displayed = menu->renderer->api.get_displayed_count(menu); + + if (!displayed) + displayed = count; + + if (!menu->lines) { + if (pointer.axes[BM_POINTER_AXIS_VERTICAL].valid) { + if (0 < pointer.axes[BM_POINTER_AXIS_VERTICAL].value) { + menu_next(menu, count, menu->wrap); + } else { + menu_prev(menu, count, menu->wrap); + } + } + if (pointer.event_mask & POINTER_EVENT_BUTTON && pointer.state == POINTER_STATE_PRESSED) { + switch (pointer.button) { + case BM_POINTER_KEY_PRIMARY: + { + struct bm_item *highlighted = bm_menu_get_highlighted_item(menu); + if (highlighted && !bm_menu_item_is_selected(menu, highlighted)) + list_add_item(&menu->selection, highlighted); + } + return BM_RUN_RESULT_SELECTED; + } + } + return BM_RUN_RESULT_RUNNING; + } + + if (pointer.axes[BM_POINTER_AXIS_VERTICAL].valid) { + if (0 < pointer.axes[BM_POINTER_AXIS_VERTICAL].value) { + menu_scroll_down(menu, count); + } else { + menu_scroll_up(menu, count); + } + } + + if (pointer.event_mask & POINTER_EVENT_MOTION) { + menu_point_select(menu, pointer.pos_x, pointer.pos_y, displayed); + } + + if (pointer.event_mask & POINTER_EVENT_BUTTON && pointer.state == POINTER_STATE_PRESSED) { + switch (pointer.button) { + case BM_POINTER_KEY_PRIMARY: + { + struct bm_item *highlighted = bm_menu_get_highlighted_item(menu); + if (highlighted && !bm_menu_item_is_selected(menu, highlighted)) + list_add_item(&menu->selection, highlighted); + } + return BM_RUN_RESULT_SELECTED; + } + } + + return BM_RUN_RESULT_RUNNING; +} + +enum bm_run_result +bm_menu_run_with_touch(struct bm_menu *menu, struct bm_touch touch, uint32_t unicode) +{ + (void) unicode; + uint32_t count; + bm_menu_get_filtered_items(menu, &count); + + uint32_t displayed = 0; + if (menu->renderer->api.get_displayed_count) + displayed = menu->renderer->api.get_displayed_count(menu); + + if (!displayed) + displayed = count; + + if (!menu->lines) { + // Not implemented yet + return BM_RUN_RESULT_RUNNING; + } + + uint16_t count_down = 0; + for (size_t i = 0; i < 2; ++i) { + struct bm_touch_point point = touch.points[i]; + if (point.event_mask & TOUCH_EVENT_DOWN) { + count_down += 1; + } + } + + if (count_down == 2) { + int16_t scroll_count = 0; + int16_t scroll_directions[2]; + int16_t distance_trigger = displayed * bm_menu_get_line_height(menu) / 4; + for (size_t i = 0; i < 2; ++i) { + struct bm_touch_point point = touch.points[i]; + if (!(point.event_mask & TOUCH_EVENT_DOWN)) + continue; + + int32_t movement_y = point.pos_y - point.start_y; + if (abs(movement_y) > distance_trigger) { + scroll_directions[i] = movement_y / abs(movement_y); + scroll_count++; + } + } + + int16_t scroll_direction_sum = scroll_directions[0] + scroll_directions[1]; + if (2 == abs(scroll_direction_sum)) { + uint16_t current_page = menu->index / menu->lines; + if (scroll_direction_sum < 0 && current_page != count / menu->lines) { // not already the first page + menu_scroll_down(menu, count); + bm_menu_release_touch(menu); + } else if (scroll_direction_sum > 0 && current_page) { // not already the first page + menu_scroll_up(menu, count); + bm_menu_release_touch(menu); + } + } + } + + if (count_down != 1) + return BM_RUN_RESULT_RUNNING; + + for (size_t i = 0; i < 2; ++i) { + struct bm_touch_point point = touch.points[i]; + if (!(point.event_mask & TOUCH_EVENT_DOWN)) + continue; + + menu_point_select(menu, point.pos_x, point.pos_y, displayed); + + if (point.event_mask & TOUCH_EVENT_UP) { + if (point.pos_y < (int32_t) (bm_menu_get_height(menu) / displayed)) { + menu_scroll_up(menu, count); + } else if ((uint32_t) point.pos_y > bm_menu_get_height(menu)) { + menu_scroll_down(menu, count); + } else if (point.pos_x > 0 + && (uint32_t) point.pos_x < bm_menu_get_width(menu)) { + { + struct bm_item *highlighted = bm_menu_get_highlighted_item(menu); + if (highlighted && !bm_menu_item_is_selected(menu, highlighted)) + list_add_item(&menu->selection, highlighted); + } + return BM_RUN_RESULT_SELECTED; + } + } + } + return BM_RUN_RESULT_RUNNING; +} + +enum bm_run_result +bm_menu_run_with_events(struct bm_menu *menu, enum bm_key key, + struct bm_pointer pointer, struct bm_touch touch, uint32_t unicode) +{ + enum bm_run_result key_result = bm_menu_run_with_key(menu, key, unicode); + if (key_result != BM_RUN_RESULT_RUNNING) { + return key_result; + } + + enum bm_run_result pointer_result = bm_menu_run_with_pointer(menu, pointer, unicode); + if (pointer_result != BM_RUN_RESULT_RUNNING) { + return pointer_result; + } + + return bm_menu_run_with_touch(menu, touch, unicode); +} + /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/renderers/wayland/registry.c b/lib/renderers/wayland/registry.c index 3a5943e..11af85e 100644 --- a/lib/renderers/wayland/registry.c +++ b/lib/renderers/wayland/registry.c @@ -201,6 +201,258 @@ keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t ra set_repeat_info(data, rate, delay); } +static void +pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + (void)wl_pointer, (void)surface; + struct input *input = data; + input->pointer_event.event_mask |= POINTER_EVENT_ENTER; + input->pointer_event.serial = serial; + input->pointer_event.surface_x = surface_x, + input->pointer_event.surface_y = surface_y; +} + +static void +pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) +{ + (void)wl_pointer, (void)surface; + struct input *input = data; + input->pointer_event.serial = serial; + input->pointer_event.event_mask |= POINTER_EVENT_LEAVE; +} + +static void +pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + (void)wl_pointer; + struct input *input = data; + input->pointer_event.event_mask |= POINTER_EVENT_MOTION; + input->pointer_event.time = time; + input->pointer_event.surface_x = surface_x, + input->pointer_event.surface_y = surface_y; +} + +static void +pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, + uint32_t time, uint32_t button, uint32_t state) +{ + (void)wl_pointer; + struct input *input = data; + input->pointer_event.event_mask |= POINTER_EVENT_BUTTON; + input->pointer_event.time = time; + input->pointer_event.serial = serial; + input->pointer_event.button = button, + input->pointer_event.state |= state; +} + +static void +pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, + uint32_t axis, wl_fixed_t value) +{ + (void)wl_pointer; + struct input *input = data; + input->pointer_event.event_mask |= POINTER_EVENT_AXIS; + input->pointer_event.time = time; + input->pointer_event.axes[axis].valid = true; + input->pointer_event.axes[axis].value = value; +} + +static void +pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) +{ + (void)wl_pointer; + struct input *input = data; + input->pointer_event.event_mask |= POINTER_EVENT_AXIS_SOURCE; + input->pointer_event.axis_source = axis_source; +} + +static void +pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) +{ + (void)wl_pointer; + struct input *input = data; + input->pointer_event.time = time; + input->pointer_event.event_mask |= POINTER_EVENT_AXIS_STOP; + input->pointer_event.axes[axis].valid = true; +} + +static void +pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) +{ + (void)wl_pointer; + struct input *input = data; + input->pointer_event.event_mask |= POINTER_EVENT_AXIS_DISCRETE; + input->pointer_event.axes[axis].valid = true; + input->pointer_event.axes[axis].discrete = discrete; +} + + +static void +pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) +{ + (void) data, (void) wl_pointer; +} + +static struct touch_point * +get_touch_point(struct input *input, int32_t id) +{ + struct touch_event *touch = &input->touch_event; + int invalid = -1; + for (size_t i = 0; i < 2; ++i) { + if (touch->points[i].id == id) { + invalid = i; + } + if (invalid == -1 && !touch->points[i].valid) { + invalid = i; + } + } + if (invalid == -1) { + return NULL; + } + touch->points[invalid].id = id; + return &touch->points[invalid]; +} + +static void +reset_all_start_position(struct input *input) +{ + struct touch_event *event = &input->touch_event; + for (size_t i = 0; i < 2; ++i) { + struct touch_point *point = &event->points[i]; + + if (!point->valid) + continue; + + point->surface_start_x = point->surface_x; + point->surface_start_y = point->surface_y; + } +} + +static void +revalidate_all_released(struct input *input) +{ + struct touch_event *event = &input->touch_event; + for (size_t i = 0; i < 2; ++i) { + struct touch_point *point = &event->points[i]; + + if (!point->valid && point->event_mask & TOUCH_EVENT_DOWN) + point->valid = true; + } +} + +static void +touch_handle_down(void *data, struct wl_touch *wl_touch, uint32_t serial, + uint32_t time, struct wl_surface *surface, int32_t id, + wl_fixed_t x, wl_fixed_t y) +{ + (void) wl_touch, (void) surface; + struct input *input = data; + struct touch_point *point = get_touch_point(input, id); + if (point == NULL) { + return; + } + point->valid = true; + point->event_mask = TOUCH_EVENT_DOWN; + point->surface_x = x, + point->surface_y = y; + input->touch_event.time = time; + input->touch_event.serial = serial; + input->touch_event.active += 1; + + revalidate_all_released(input); + reset_all_start_position(input); +} + +static void +touch_handle_up(void *data, struct wl_touch *wl_touch, uint32_t serial, + uint32_t time, int32_t id) +{ + (void) time, (void) wl_touch, (void) serial; + struct input *input = data; + struct touch_point *point = get_touch_point(input, id); + if (point == NULL) { + return; + } + point->event_mask |= TOUCH_EVENT_UP; + input->touch_event.active -= 1; + + reset_all_start_position(input); +} + +static void +touch_handle_motion(void *data, struct wl_touch *wl_touch, uint32_t time, + int32_t id, wl_fixed_t x, wl_fixed_t y) +{ + (void) wl_touch; + struct input *input = data; + struct touch_point *point = get_touch_point(input, id); + if (point == NULL) { + return; + } + point->event_mask |= TOUCH_EVENT_MOTION; + point->surface_x = x, point->surface_y = y; + input->touch_event.time = time; +} + +static void +touch_handle_cancel(void *data, struct wl_touch *wl_touch) +{ + (void) wl_touch, (void) data; +} + +static void +touch_handle_shape(void *data, struct wl_touch *wl_touch, + int32_t id, wl_fixed_t major, wl_fixed_t minor) +{ + (void) wl_touch; + struct input *input = data; + struct touch_point *point = get_touch_point(input, id); + if (point == NULL) { + return; + } + point->event_mask |= TOUCH_EVENT_SHAPE; + point->major = major, point->minor = minor; +} + +static void +touch_handle_orientation(void *data, struct wl_touch *wl_touch, + int32_t id, wl_fixed_t orientation) +{ + (void) wl_touch; + struct input *input = data; + struct touch_point *point = get_touch_point(input, id); + if (point == NULL) { + return; + } + point->event_mask |= TOUCH_EVENT_ORIENTATION; + point->orientation = orientation; +} + +static void +touch_handle_frame(void *data, struct wl_touch *wl_touch) +{ + (void) data, (void) wl_touch; +} + +static const struct wl_pointer_listener pointer_listener = { + .enter = pointer_handle_enter, + .leave = pointer_handle_leave, + .motion = pointer_handle_motion, + .button = pointer_handle_button, + .axis = pointer_handle_axis, + .frame = pointer_handle_frame, + .axis_source = pointer_handle_axis_source, + .axis_stop = pointer_handle_axis_stop, + .axis_discrete = pointer_handle_axis_discrete, +}; + static const struct wl_keyboard_listener keyboard_listener = { .keymap = keyboard_handle_keymap, .enter = keyboard_handle_enter, @@ -210,19 +462,46 @@ static const struct wl_keyboard_listener keyboard_listener = { .repeat_info = keyboard_handle_repeat_info }; +static const struct wl_touch_listener wl_touch_listener = { + .down = touch_handle_down, + .up = touch_handle_up, + .motion = touch_handle_motion, + .frame = touch_handle_frame, + .cancel = touch_handle_cancel, + .shape = touch_handle_shape, + .orientation = touch_handle_orientation, +}; + static void seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps) { struct input *input = data; - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->seat) { + if (!input->seat) { input->seat = seat; + } + + if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { input->keyboard = wl_seat_get_keyboard(seat); wl_keyboard_add_listener(input->keyboard, &keyboard_listener, data); - } else if (seat == input->seat && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + } + + if (caps & WL_SEAT_CAPABILITY_POINTER) { + input->pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(input->pointer, &pointer_listener, data); + } + + if (caps & WL_SEAT_CAPABILITY_TOUCH) { + input->touch = wl_seat_get_touch(seat); + wl_touch_add_listener(input->touch, &wl_touch_listener, data); + } + + if (seat == input->seat && !(caps & WL_SEAT_CAPABILITY_KEYBOARD) && !(caps & WL_SEAT_CAPABILITY_POINTER)) { wl_keyboard_destroy(input->keyboard); input->seat = NULL; input->keyboard = NULL; + input->pointer = NULL; + input->touch = NULL; } } diff --git a/lib/renderers/wayland/wayland.c b/lib/renderers/wayland/wayland.c index 92036a1..cff9171 100644 --- a/lib/renderers/wayland/wayland.c +++ b/lib/renderers/wayland/wayland.c @@ -43,7 +43,9 @@ render(const struct bm_menu *menu) } } - if (wayland->input.code != wayland->input.last_code) { + if (wayland->input.code != wayland->input.last_code || + wayland->input.touch_event.active || + wayland->input.pointer_event.event_mask) { wl_list_for_each(window, &wayland->windows, link) { bm_wl_window_schedule_render(window); } @@ -217,6 +219,96 @@ poll_key(const struct bm_menu *menu, unsigned int *unicode) return BM_KEY_UNICODE; } +struct bm_pointer +poll_pointer(const struct bm_menu *menu) +{ + struct wayland *wayland = menu->renderer->internal; + struct input *input = &wayland->input; + struct pointer_event *event = &input->pointer_event; + assert(wayland && event); + + struct bm_pointer bm_pointer; + + bm_pointer.event_mask = event->event_mask; + bm_pointer.pos_x = wl_fixed_to_int(event->surface_x); + bm_pointer.pos_y = wl_fixed_to_int(event->surface_y); + bm_pointer.time = event->time; + bm_pointer.axes[0].valid = event->axes[0].valid; + bm_pointer.axes[0].value = event->axes[0].value; + bm_pointer.axes[0].discrete = event->axes[0].discrete; + bm_pointer.axes[1].valid = event->axes[1].valid; + bm_pointer.axes[1].value = event->axes[1].value; + bm_pointer.axes[1].discrete = event->axes[1].discrete; + bm_pointer.axis_source = event->axis_source; + + bm_pointer.button = BM_POINTER_KEY_NONE; + switch (event->button) { + case BTN_LEFT: + bm_pointer.button = BM_POINTER_KEY_PRIMARY; + break; + } + + if (event->state & WL_POINTER_BUTTON_STATE_PRESSED) { + bm_pointer.state |= POINTER_STATE_PRESSED; + } + if (event->state & WL_POINTER_BUTTON_STATE_RELEASED) { + bm_pointer.state |= POINTER_STATE_RELEASED; + } + + memset(event, 0, sizeof(*event)); + return bm_pointer; +} + +struct bm_touch +poll_touch(const struct bm_menu *menu) +{ + struct wayland *wayland = menu->renderer->internal; + struct input *input = &wayland->input; + struct touch_event *event = &input->touch_event; + assert(wayland && event); + + struct bm_touch bm_touch; + + for (size_t i = 0; i < 2; ++i) { + struct touch_point *point = &event->points[i]; + + if (!point->valid) { + bm_touch.points[i].event_mask = 0; + continue; + } + + bm_touch.points[i].event_mask = point->event_mask; + bm_touch.points[i].pos_x = wl_fixed_to_int(point->surface_x); + bm_touch.points[i].pos_y = wl_fixed_to_int(point->surface_y); + bm_touch.points[i].start_x = wl_fixed_to_int(point->surface_start_x); + bm_touch.points[i].start_y = wl_fixed_to_int(point->surface_start_y); + bm_touch.points[i].major = point->major; + bm_touch.points[i].minor = point->minor; + bm_touch.points[i].orientation = point->orientation; + + if (point->event_mask & TOUCH_EVENT_UP) { + point->valid = false; + point->event_mask = 0; + } + } + + return bm_touch; +} + +void +release_touch(const struct bm_menu *menu) +{ + struct wayland *wayland = menu->renderer->internal; + struct input *input = &wayland->input; + struct touch_event *event = &input->touch_event; + assert(wayland && event); + + for (size_t i = 0; i < 2; ++i) { + struct touch_point *point = &event->points[i]; + point->valid = false; + } +} + static uint32_t get_displayed_count(const struct bm_menu *menu) { @@ -231,6 +323,34 @@ get_displayed_count(const struct bm_menu *menu) return max; } +static uint32_t +get_height(const struct bm_menu *menu) +{ + struct wayland *wayland = menu->renderer->internal; + assert(wayland); + uint32_t max = 0; + struct window *window; + wl_list_for_each(window, &wayland->windows, link) { + if (window->displayed > max) + max = window->height; + } + return max; +} + +static uint32_t +get_width(const struct bm_menu *menu) +{ + struct wayland *wayland = menu->renderer->internal; + assert(wayland); + uint32_t max = 0; + struct window *window; + wl_list_for_each(window, &wayland->windows, link) { + if (window->displayed > max) + max = window->width; + } + return max; +} + static void set_width(const struct bm_menu *menu, uint32_t margin, float factor) { @@ -447,7 +567,12 @@ register_renderer(struct render_api *api) api->constructor = constructor; api->destructor = destructor; api->get_displayed_count = get_displayed_count; + api->get_height = get_height; + api->get_width = get_width; api->poll_key = poll_key; + api->poll_pointer = poll_pointer; + api->poll_touch = poll_touch; + api->release_touch = release_touch; api->render = render; api->set_align = set_align; api->set_width = set_width; diff --git a/lib/renderers/wayland/wayland.h b/lib/renderers/wayland/wayland.h index a141671..059ba62 100644 --- a/lib/renderers/wayland/wayland.h +++ b/lib/renderers/wayland/wayland.h @@ -5,6 +5,7 @@ #include #include +#include #include "wlr-layer-shell-unstable-v1.h" #include "xdg-output-unstable-v1.h" @@ -45,11 +46,46 @@ struct xkb { xkb_mod_mask_t masks[MASK_LAST]; }; +struct pointer_event { + uint32_t event_mask; + wl_fixed_t surface_x, surface_y; + uint32_t button, state; + uint32_t time; + uint32_t serial; + struct { + bool valid; + wl_fixed_t value; + int32_t discrete; + } axes[2]; + uint32_t axis_source; +}; + +struct touch_point { + bool valid; + int32_t id; + uint32_t event_mask; + wl_fixed_t surface_x, surface_y; + wl_fixed_t surface_start_x, surface_start_y; + wl_fixed_t major, minor; + wl_fixed_t orientation; +}; + +struct touch_event { + uint32_t time; + uint32_t serial; + uint16_t active; + struct touch_point points[2]; +}; + struct input { int *repeat_fd; struct wl_seat *seat; struct wl_keyboard *keyboard; + struct wl_pointer *pointer; + struct wl_touch *touch; + struct pointer_event pointer_event; + struct touch_event touch_event; struct xkb xkb; xkb_keysym_t sym;