diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index a00aed1d..872556a9 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -31,6 +31,11 @@ #define tablet_unset_status(tablet_,s_) ((tablet_)->status &= ~(s_)) #define tablet_has_status(tablet_,s_) (!!((tablet_)->status & (s_))) +#define tablet_get_pressed_buttons(tablet_,field_) \ + ((tablet_)->button_state.field_ & ~((tablet_)->prev_button_state.field_)) +#define tablet_get_released_buttons(tablet_,field_) \ + ((tablet_)->prev_button_state.field_ & ~((tablet_)->button_state.field_)) + static void tablet_process_absolute(struct tablet_dispatch *tablet, struct evdev_device *device, @@ -110,6 +115,37 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, } } +static void +tablet_update_button(struct tablet_dispatch *tablet, + uint32_t evcode, + uint32_t enable) +{ + uint32_t button, *flags; + + /* XXX: This really depends on the expected buttons fitting in the mask */ + if (evcode >= BTN_MISC && evcode <= BTN_TASK) { + flags = &tablet->button_state.pad_buttons; + button = evcode - BTN_MISC; + } else if (evcode >= BTN_TOUCH && evcode <= BTN_STYLUS2) { + flags = &tablet->button_state.stylus_buttons; + button = evcode - BTN_TOUCH; + } else { + log_info("Unhandled button %s (%#x)\n", + libevdev_event_code_get_name(EV_KEY, evcode), evcode); + return; + } + + if (enable) { + (*flags) |= 1 << button; + tablet_set_status(tablet, TABLET_BUTTONS_PRESSED); + } else { + (*flags) &= ~(1 << button); + tablet_set_status(tablet, TABLET_BUTTONS_RELEASED); + } + + assert(button < 32); +} + static void tablet_process_key(struct tablet_dispatch *tablet, struct evdev_device *device, @@ -128,7 +164,11 @@ tablet_process_key(struct tablet_dispatch *tablet, /* These codes have an equivalent libinput_tool value */ tablet_update_tool(tablet, e->code, e->value); break; + case BTN_TOUCH: + case BTN_STYLUS: + case BTN_STYLUS2: default: + tablet_update_button(tablet, e->code, e->value); break; } } @@ -186,12 +226,68 @@ tablet_notify_tool(struct tablet_dispatch *tablet, tablet_notify_tool_update(base, time, new_tool); } +static void +tablet_notify_button_mask(struct tablet_dispatch *tablet, + struct evdev_device *device, + uint32_t time, + uint32_t buttons, + uint32_t button_base, + enum libinput_button_state state) +{ + struct libinput_device *base = &device->base; + int32_t num_button = 0; + + while (buttons) { + int enabled; + + num_button++; + enabled = (buttons & 1); + buttons >>= 1; + + if (!enabled) + continue; + + tablet_notify_button(base, + time, + num_button + button_base - 1, + state); + } +} + +static void +tablet_notify_buttons(struct tablet_dispatch *tablet, + struct evdev_device *device, + uint32_t time, + enum libinput_button_state state) +{ + uint32_t pad_buttons, stylus_buttons; + + if (state == LIBINPUT_BUTTON_STATE_PRESSED) { + pad_buttons = tablet_get_pressed_buttons(tablet, pad_buttons); + stylus_buttons = + tablet_get_pressed_buttons(tablet, stylus_buttons); + } else { + pad_buttons = tablet_get_released_buttons(tablet, pad_buttons); + stylus_buttons = + tablet_get_released_buttons(tablet, stylus_buttons); + } + + tablet_notify_button_mask(tablet, device, time, + pad_buttons, BTN_MISC, state); + tablet_notify_button_mask(tablet, device, time, + stylus_buttons, BTN_TOUCH, state); +} + static void tablet_flush(struct tablet_dispatch *tablet, struct evdev_device *device, uint32_t time) { if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) { + /* Release all stylus buttons */ + tablet->button_state.stylus_buttons = 0; + tablet_set_status(tablet, TABLET_BUTTONS_RELEASED); + /* FIXME: This behavior is not ideal and this memset should be * removed */ memset(&tablet->changed_axes, 0, sizeof(tablet->changed_axes)); @@ -210,10 +306,27 @@ tablet_flush(struct tablet_dispatch *tablet, } } + if (tablet_has_status(tablet, TABLET_BUTTONS_RELEASED)) { + tablet_notify_buttons(tablet, device, time, + LIBINPUT_BUTTON_STATE_RELEASED); + tablet_unset_status(tablet, TABLET_BUTTONS_RELEASED); + } + + if (tablet_has_status(tablet, TABLET_BUTTONS_PRESSED)) { + tablet_notify_buttons(tablet, device, time, + LIBINPUT_BUTTON_STATE_PRESSED); + tablet_unset_status(tablet, TABLET_BUTTONS_PRESSED); + } + if (tablet_has_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY)) { tablet_notify_proximity_out(&device->base, time); tablet_unset_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY); } + + /* Update state */ + memcpy(&tablet->prev_button_state, + &tablet->button_state, + sizeof(tablet->button_state)); } static void diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h index ee6b3333..4498425f 100644 --- a/src/evdev-tablet.h +++ b/src/evdev-tablet.h @@ -31,7 +31,14 @@ enum tablet_status { TABLET_NONE = 0, TABLET_AXES_UPDATED = 1 << 0, TABLET_TOOL_UPDATED = 1 << 1, - TABLET_TOOL_LEAVING_PROXIMITY = 1 << 2 + TABLET_TOOL_LEAVING_PROXIMITY = 1 << 2, + TABLET_BUTTONS_PRESSED = 1 << 3, + TABLET_BUTTONS_RELEASED = 1 << 4 +}; + +struct button_state { + uint32_t pad_buttons; /* bitmask of evcode - BTN_MISC */ + uint32_t stylus_buttons; /* bitmask of evcode - BTN_TOUCH */ }; struct tablet_dispatch { @@ -41,6 +48,9 @@ struct tablet_dispatch { unsigned char changed_axes[NCHARS(LIBINPUT_TABLET_AXIS_CNT)]; double axes[LIBINPUT_TABLET_AXIS_CNT]; + struct button_state button_state; + struct button_state prev_button_state; + enum libinput_tool_type current_tool_type; uint32_t current_tool_serial; }; diff --git a/src/libinput-private.h b/src/libinput-private.h index 3830bd9a..3630f74d 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -219,6 +219,11 @@ tablet_notify_proximity_out(struct libinput_device *device, uint32_t time); void +tablet_notify_button(struct libinput_device *device, + uint32_t time, + int32_t button, + enum libinput_button_state state); +void touch_notify_frame(struct libinput_device *device, uint32_t time); #endif /* LIBINPUT_PRIVATE_H */ diff --git a/src/libinput.c b/src/libinput.c index 4b399e11..1ac62a87 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -82,6 +82,9 @@ struct libinput_event_touch { struct libinput_event_tablet { struct libinput_event base; + uint32_t button; + enum libinput_button_state state; + uint32_t seat_button_count; uint32_t time; double *axes; unsigned char changed_axes[NCHARS(LIBINPUT_TABLET_AXIS_CNT)]; @@ -202,6 +205,7 @@ libinput_event_get_pointer_event(struct libinput_event *event) case LIBINPUT_EVENT_TABLET_AXIS: case LIBINPUT_EVENT_TABLET_TOOL_UPDATE: case LIBINPUT_EVENT_TABLET_PROXIMITY_OUT: + case LIBINPUT_EVENT_TABLET_BUTTON: break; } @@ -231,6 +235,7 @@ libinput_event_get_keyboard_event(struct libinput_event *event) case LIBINPUT_EVENT_TABLET_AXIS: case LIBINPUT_EVENT_TABLET_TOOL_UPDATE: case LIBINPUT_EVENT_TABLET_PROXIMITY_OUT: + case LIBINPUT_EVENT_TABLET_BUTTON: break; } @@ -260,6 +265,7 @@ libinput_event_get_touch_event(struct libinput_event *event) case LIBINPUT_EVENT_TABLET_AXIS: case LIBINPUT_EVENT_TABLET_TOOL_UPDATE: case LIBINPUT_EVENT_TABLET_PROXIMITY_OUT: + case LIBINPUT_EVENT_TABLET_BUTTON: break; } @@ -288,6 +294,7 @@ libinput_event_get_tablet_event(struct libinput_event *event) case LIBINPUT_EVENT_TABLET_AXIS: case LIBINPUT_EVENT_TABLET_TOOL_UPDATE: case LIBINPUT_EVENT_TABLET_PROXIMITY_OUT: + case LIBINPUT_EVENT_TABLET_BUTTON: return (struct libinput_event_tablet *) event; } @@ -316,6 +323,7 @@ libinput_event_get_device_notify_event(struct libinput_event *event) case LIBINPUT_EVENT_TABLET_AXIS: case LIBINPUT_EVENT_TABLET_TOOL_UPDATE: case LIBINPUT_EVENT_TABLET_PROXIMITY_OUT: + case LIBINPUT_EVENT_TABLET_BUTTON: break; } @@ -532,6 +540,24 @@ libinput_event_tablet_get_time(struct libinput_event_tablet *event) return event->time; } +LIBINPUT_EXPORT uint32_t +libinput_event_tablet_get_button(struct libinput_event_tablet *event) +{ + return event->button; +} + +LIBINPUT_EXPORT enum libinput_button_state +libinput_event_tablet_get_button_state(struct libinput_event_tablet *event) +{ + return event->state; +} + +LIBINPUT_EXPORT uint32_t +libinput_event_tablet_get_seat_button_count(struct libinput_event_tablet *event) +{ + return event->seat_button_count; +} + LIBINPUT_EXPORT enum libinput_tool_type libinput_tool_get_type(struct libinput_tool *tool) { @@ -1223,6 +1249,35 @@ tablet_notify_proximity_out(struct libinput_device *device, &proximity_out_update_event->base); } +void +tablet_notify_button(struct libinput_device *device, + uint32_t time, + int32_t button, + enum libinput_button_state state) +{ + struct libinput_event_tablet *button_event; + int32_t seat_button_count; + + button_event = zalloc(sizeof *button_event); + if (!button_event) + return; + + seat_button_count = update_seat_button_count(device->seat, + button, + state); + + *button_event = (struct libinput_event_tablet) { + .time = time, + .button = button, + .state = state, + .seat_button_count = seat_button_count, + }; + + post_device_event(device, + LIBINPUT_EVENT_TABLET_BUTTON, + &button_event->base); +} + static void libinput_post_event(struct libinput *libinput, struct libinput_event *event) diff --git a/src/libinput.h b/src/libinput.h index 9cb33a7d..b4272b2c 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -269,7 +269,8 @@ enum libinput_event_type { * was held down on the stylus are sent before the initial proximity out * event. */ - LIBINPUT_EVENT_TABLET_PROXIMITY_OUT + LIBINPUT_EVENT_TABLET_PROXIMITY_OUT, + LIBINPUT_EVENT_TABLET_BUTTON }; struct libinput; @@ -299,7 +300,8 @@ struct libinput_event_touch; * * Tablet event representing an axis update, button press, or tool update. Valid * event types for this event are @ref LIBINPUT_EVENT_TABLET_AXIS, @ref - * LIBINPUT_EVENT_TABLET_TOOL_UPDATE and @ref LIBINPUT_EVENT_TABLET_TOOL_UPDATE. + * LIBINPUT_EVENT_TABLET_TOOL_UPDATE, @ref LIBINPUT_EVENT_TABLET_TOOL_UPDATE and + * @ref LIBINPUT_EVENT_TABLET_BUTTON. */ struct libinput_event_tablet; @@ -932,6 +934,51 @@ libinput_event_tablet_get_y_transformed(struct libinput_event_tablet *event, struct libinput_tool * libinput_event_tablet_get_tool(struct libinput_event_tablet *event); +/** + * @ingroup event_tablet + * + * Return the button that triggered this event. + * For tablet events that are not of type LIBINPUT_EVENT_TABLET_BUTTON, this + * function returns 0. + * + * @note It is an application bug to call this function for events other than + * LIBINPUT_EVENT_TABLET_BUTTON. + * + * @param event The libinput tablet event + * @return the button triggering this event + */ +uint32_t +libinput_event_tablet_get_button(struct libinput_event_tablet *event); + +/** + * @ingroup event_tablet + * + * Return the button state of the event. + * + * @note It is an application bug to call this function for events other than + * LIBINPUT_EVENT_TABLET_BUTTON. + * + * @param event The libinput tablet event + * @return the button state triggering this event + */ +enum libinput_button_state +libinput_event_tablet_get_button_state(struct libinput_event_tablet *event); + +/** + * @ingroup event_tablet + * + * For the button of a LIBINPUT_EVENT_TABLET_BUTTON event, return the total + * number of buttons pressed on all devices on the associated seat after the + * the event was triggered. + * + " @note It is an application bug to call this function for events other than + * LIBINPUT_EVENT_TABLET_BUTTON. For other events, this function returns 0. + * + * @return the seat wide pressed button count for the key of this event + */ +uint32_t +libinput_event_tablet_get_seat_button_count(struct libinput_event_tablet *event); + /** * @ingroup event_tablet *