diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index a1ff0c317..fef0c1ee1 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -4,6 +4,7 @@ #include #include #include +#include struct libseat; @@ -88,6 +89,27 @@ struct wlr_device_change_event { }; }; +/** + * A session input router layer which handles key presses for keysyms from + * XKB_KEY_XF86Switch_VT_1 to XKB_KEY_XF86Switch_VT_12 inclusive and calls + * wlr_session_change_vt() accordingly. + */ +struct wlr_session_input_router_layer { + struct wlr_input_router *router; + struct wlr_session *session; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wlr_input_router_keyboard keyboard; + + struct wl_listener router_destroy; + struct wl_listener session_destroy; + } WLR_PRIVATE; +}; + /* * Opens a session, taking control of the current virtual terminal. * This should not be called if another program is already in control @@ -143,4 +165,12 @@ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt); ssize_t wlr_session_find_gpus(struct wlr_session *session, size_t ret_len, struct wlr_device **ret); +bool wlr_session_input_router_layer_register(int32_t priority); + +struct wlr_session_input_router_layer *wlr_session_input_router_layer_create( + struct wlr_input_router *router, struct wlr_session *session); + +void wlr_session_input_router_layer_destroy( + struct wlr_session_input_router_layer *layer); + #endif diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index c36b1a5cb..f5ef0c4fb 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -10,6 +10,7 @@ #define WLR_TYPES_WLR_DATA_DEVICE_H #include +#include #include struct wlr_data_device_manager { @@ -151,6 +152,50 @@ struct wlr_drag_drop_event { uint32_t time; }; +enum wlr_drag_input_router_layer_type { + WLR_DRAG_INPUT_ROUTER_LAYER_POINTER, + WLR_DRAG_INPUT_ROUTER_LAYER_TOUCH, +}; + +/** + * A data device drag input router layer which sends wl_data_device events based + * on the pointer or a touch point position and focus. It is automatically + * destroyed when the originating action (e.g. a button press) is reverted. + */ +struct wlr_drag_input_router_layer { + struct wlr_input_router *router; + struct wlr_input_router_implicit_grab *implicit_grab; + struct wlr_drag *drag; + + enum wlr_drag_input_router_layer_type type; + + struct { + // Global position, hotspot is not included + double x, y; + } icon_position; + + struct { + struct wl_signal destroy; + struct wl_signal set_icon_position; + } events; + + struct { + union { + struct { + struct wlr_input_router_pointer pointer; + uint32_t pointer_button; + }; + struct { + struct wlr_input_router_touch touch; + int32_t touch_id; + }; + }; + + struct wl_listener router_destroy; + struct wl_listener drag_destroy; + } WLR_PRIVATE; +}; + /** * Create a wl_data_device_manager global for this display. */ @@ -210,6 +255,20 @@ void wlr_seat_start_pointer_drag(struct wlr_seat *seat, struct wlr_drag *drag, void wlr_seat_start_touch_drag(struct wlr_seat *seat, struct wlr_drag *drag, uint32_t serial, struct wlr_touch_point *point); +void wlr_drag_start(struct wlr_drag *drag); + +void wlr_drag_enter(struct wlr_drag *drag, struct wlr_surface *surface, + double sx, double sy); + +void wlr_drag_clear_focus(struct wlr_drag *drag); + +void wlr_drag_send_motion(struct wlr_drag *drag, uint32_t time_msec, + double sx, double sy); + +void wlr_drag_drop_and_destroy(struct wlr_drag *drag, uint32_t time_msec); + +void wlr_drag_destroy(struct wlr_drag *drag); + /** * Initializes the data source with the provided implementation. */ @@ -261,4 +320,14 @@ void wlr_data_source_dnd_finish(struct wlr_data_source *source); void wlr_data_source_dnd_action(struct wlr_data_source *source, enum wl_data_device_manager_dnd_action action); +bool wlr_drag_input_router_layer_register(int32_t priority); + +struct wlr_drag_input_router_layer *wlr_drag_input_router_layer_create_pointer( + struct wlr_input_router *router, struct wlr_drag *drag, uint32_t button); + +struct wlr_drag_input_router_layer *wlr_drag_input_router_layer_create_touch( + struct wlr_input_router *router, struct wlr_drag *drag, int32_t id); + +void wlr_drag_input_router_layer_destroy(struct wlr_drag_input_router_layer *layer); + #endif diff --git a/include/wlr/types/wlr_input_method_v2.h b/include/wlr/types/wlr_input_method_v2.h index 02216ddad..86555fb17 100644 --- a/include/wlr/types/wlr_input_method_v2.h +++ b/include/wlr/types/wlr_input_method_v2.h @@ -14,6 +14,8 @@ #include #include +struct wlr_text_input_v3; + struct wlr_input_method_v2_preedit_string { char *text; int32_t cursor_begin; @@ -104,6 +106,43 @@ struct wlr_input_method_manager_v2 { } WLR_PRIVATE; }; +/** + * A zwp_input_method_v2 input router layer which redirects keyboard events to + * an active zwp_input_method_keyboard_grab_v2 object, if one exists. This layer + * detects virtual keyboard devices belonging to the input method client and + * does not process events from them. + */ +struct wlr_input_method_v2_input_router_layer { + struct wlr_input_router *router; + struct wlr_input_method_v2 *input_method; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wlr_input_router_keyboard keyboard; + + struct wlr_input_method_keyboard_grab_v2 *grab; + bool device_grabbed; + + uint32_t forwarded_keys[WLR_KEYBOARD_KEYS_CAP]; + size_t n_forwarded_keys; + + struct wlr_text_input_v3 *active_text_input; + + struct wl_listener active_text_input_destroy; + struct wl_listener active_text_input_commit; + + struct wl_listener input_method_destroy; + struct wl_listener input_method_commit; + struct wl_listener input_method_grab_keyboard; + + struct wl_listener router_destroy; + struct wl_listener grab_destroy; + } WLR_PRIVATE; +}; + struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create( struct wl_display *display); @@ -147,4 +186,18 @@ void wlr_input_method_keyboard_grab_v2_set_keyboard( void wlr_input_method_keyboard_grab_v2_destroy( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab); +bool wlr_input_method_v2_input_router_layer_register(int32_t priority); + +struct wlr_input_method_v2_input_router_layer *wlr_input_method_v2_input_router_layer_create( + struct wlr_input_router *router); +void wlr_input_method_v2_input_router_layer_destroy( + struct wlr_input_method_v2_input_router_layer *layer); + +void wlr_input_method_v2_input_router_layer_set_input_method( + struct wlr_input_method_v2_input_router_layer *layer, + struct wlr_input_method_v2 *input_method); +void wlr_input_method_v2_input_router_layer_set_active_text_input( + struct wlr_input_method_v2_input_router_layer *layer, + struct wlr_text_input_v3 *text_input); + #endif diff --git a/include/wlr/types/wlr_input_router.h b/include/wlr/types/wlr_input_router.h new file mode 100644 index 000000000..c313d5aea --- /dev/null +++ b/include/wlr/types/wlr_input_router.h @@ -0,0 +1,551 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_INPUT_ROUTER_H +#define WLR_TYPES_WLR_INPUT_ROUTER_H + +#include +#include + +#include +#include +#include + +#define WLR_INPUT_ROUTER_MAX_POINTER_BUTTONS 32 +#define WLR_INPUT_ROUTER_MAX_TOUCH_POINTS 16 + +struct wlr_input_router_handler { + struct { + int32_t priority; + struct wlr_input_router_handler *head; + struct wlr_input_router_handler *next; + } WLR_PRIVATE; +}; + +struct wlr_input_router_handler_interface { + const char *name; +}; + +/** + * A registry of input event handler interface priorities. + */ +struct wlr_input_router_handler_priority_list { + struct { + struct wl_array entries; // struct wlr_input_router_handler_priority_entry + } WLR_PRIVATE; +}; + +enum wlr_input_router_focus_type { + WLR_INPUT_ROUTER_FOCUS_NONE, + WLR_INPUT_ROUTER_FOCUS_SURFACE, + WLR_INPUT_ROUTER_FOCUS_USER, +}; + +/** + * A helper object to store focus information. When the underlying object is + * destroyed, the focus is automatically reset. + * + * Input router focus readers accept NULL, which is treated the same way as an + * empty focus. + */ +struct wlr_input_router_focus { + enum wlr_input_router_focus_type type; + union { + struct wlr_surface *surface; + struct { + void *user; + struct wl_signal *destroy_signal; + }; + }; + + struct { + struct wl_listener destroy; + } WLR_PRIVATE; +}; + +struct wlr_input_router; + +struct wlr_input_router_interface { + const char *name; + + void (*at)(struct wlr_input_router *router, double x, double y, + struct wlr_input_router_focus *focus, double *local_x, double *local_y); + bool (*get_surface_position)(struct wlr_input_router *router, + struct wlr_surface *surface, double *x, double *y); +}; + +struct wlr_input_router_keyboard; + +/** + * Event notifying of a new keyboard focus. It is not guaranteed that the focus + * has actually changed. + */ +struct wlr_input_router_keyboard_focus_event { + const struct wlr_input_router_focus *focus; +}; + +/** + * Event notifying of a new active keyboard device. + */ +struct wlr_input_router_keyboard_device_event { + // May be NULL + struct wlr_keyboard *device; +}; + +/** + * Event notifying of a key press or release. It is guaranteed that the current + * keyboard device is not NULL. + */ +struct wlr_input_router_keyboard_key_event { + uint32_t time_msec; + uint32_t key; + enum wl_keyboard_key_state state; + + // If true, this event has already been consumed and should only be used for + // bookkeeping. + bool intercepted; +}; + +/** + * Event notifying that the keyboard device modifiers have changed. It is + * guaranteed that the current keyboard device is not NULL. + */ +struct wlr_input_router_keyboard_modifiers_event { + struct { + char unused; + } WLR_PRIVATE; +}; + +struct wlr_input_router_keyboard_interface { + struct wlr_input_router_handler_interface base; + + uint32_t (*focus)(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_focus_event *event); + void (*device)(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_device_event *event); + uint32_t (*key)(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_key_event *event); + void (*modifiers)(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_modifiers_event *event); +}; + +struct wlr_input_router_keyboard { + struct wlr_input_router_handler base; + const struct wlr_input_router_keyboard_interface *impl; + + struct wlr_input_router_focus focus; + struct wlr_keyboard *device; + + struct { + struct wl_listener device_destroy; + } WLR_PRIVATE; +}; + +/** + * Event notifying of a pointer position. The position is not guaranteed to + * be different from the previous one. + */ +struct wlr_input_router_pointer_position_event { + uint32_t time_msec; + + double x, y; + const struct wlr_input_router_focus *focus; + /** + * If true, the focus provided with this event should be prioritized over + * focus determined by the handler implementation. + */ + bool explicit_focus; + + /** + * If true, this event has not been caused by a physical action. + */ + bool synthetic; + + /** + * Components of pointer motion vectors. It is not guaranteed that (x - dx, + * y - dy) is equal to the previous position. + */ + double dx, dy; + double unaccel_dx, unaccel_dy; +}; + +/** + * Event notifying of a pointer button press or release. + */ +struct wlr_input_router_pointer_button_event { + uint32_t time_msec; + + uint32_t button; + enum wl_pointer_button_state state; + + // The index of the button in the input router pointer, set automatically. + size_t index; +}; + +struct wlr_input_router_pointer_axis_event { + uint32_t time_msec; + + enum wl_pointer_axis_source source; + enum wl_pointer_axis orientation; + enum wl_pointer_axis_relative_direction relative_direction; + double delta; + int32_t delta_discrete; +}; + +struct wlr_input_router_pointer_frame_event { + struct { + char unused; + } WLR_PRIVATE; +}; + +struct wlr_input_router_pointer; + +struct wlr_input_router_pointer_interface { + struct wlr_input_router_handler_interface base; + + uint32_t (*position)(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event); + uint32_t (*button)(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_button_event *event); + void (*axis)(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_axis_event *event); + void (*frame)(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_frame_event *event); +}; + +struct wlr_input_router_pointer_button { + uint32_t button; + // The number of times the button has been pressed. + uint32_t count; +}; + +struct wlr_input_router_pointer { + struct wlr_input_router_handler base; + const struct wlr_input_router_pointer_interface *impl; + + double x, y; + struct wlr_input_router_focus focus; + + struct wlr_input_router_pointer_button buttons[WLR_INPUT_ROUTER_MAX_POINTER_BUTTONS]; + uint32_t n_buttons; +}; + +/** + * Event notifying of a touch point position. The position is not guaranteed to + * be different from the previous one. id is guaranteed to abe a valid touch + * point ID. + */ +struct wlr_input_router_touch_position_event { + uint32_t time_msec; + int32_t id; + + double x, y; + const struct wlr_input_router_focus *focus; + + // The index of the touch point in the input router touch, set + // automatically. + size_t index; +}; + +/** + * Event notifying of a new touch point. id is guaranteed to be unique among + * all touch points. This event adds a touch point. + */ +struct wlr_input_router_touch_down_event { + uint32_t time_msec; + int32_t id; + + double x, y; + const struct wlr_input_router_focus *focus; + + // The index of the touch point in the input router touch, set + // automatically. + size_t index; +}; + +/** + * Event notifying that a touch point has disappeared. id is guaranteed to be + * a valid touch point ID. This event removes the touch point. + */ +struct wlr_input_router_touch_up_event { + uint32_t time_msec; + int32_t id; + + // The index of the touch point in the input router touch, set + // automatically. + size_t index; +}; + +/** + * Event notifying that a touch point has been cancelled. id is guaranteed to be + * a valid touch point ID. This event removes the touch point. + */ +struct wlr_input_router_touch_cancel_event { + int32_t id; + + // The index of the touch point in the input router touch, set + // automatically. + size_t index; +}; + +struct wlr_input_router_touch_frame_event { + struct { + char unused; + } WLR_PRIVATE; +}; + +struct wlr_input_router_touch; + +struct wlr_input_router_touch_interface { + struct wlr_input_router_handler_interface base; + + void (*position)(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_position_event *event); + uint32_t (*down)(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_down_event *event); + uint32_t (*up)(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_up_event *event); + void (*cancel)(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_cancel_event *event); + void (*frame)(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_frame_event *event); +}; + +struct wlr_input_router_touch_point { + int32_t id; + + double x, y; + struct wlr_input_router_focus focus; +}; + +struct wlr_input_router_touch { + struct wlr_input_router_handler base; + const struct wlr_input_router_touch_interface *impl; + + struct wlr_input_router_touch_point points[WLR_INPUT_ROUTER_MAX_TOUCH_POINTS]; + size_t n_points; +}; + +/** + * An input router is an object which has keyboard, pointer, and touch event + * handler chains. + * + * Each handler can receive events and send events to the next handler in the + * chain. If the function in the handler interface responsible for handling + * events of a specific type is NULL, events of that type are automatically + * passed along to the next handler. + * + * When an input event handler is added, it's placed accordingly to its + * priority, which must be registered beforehand. The newly added handler copies + * the state from the next handler in the chain, if one exists. + */ +struct wlr_input_router { + struct wlr_input_router_keyboard keyboard; + struct wlr_input_router_pointer pointer; + struct wlr_input_router_touch touch; + + const struct wlr_input_router_interface *impl; + + struct { + struct wl_signal destroy; + } events; + + struct wlr_addon_set addons; + + void *data; +}; + +/** + * An input router focus layer which assigns focus for revelant pointer and + * touch events based on position. + */ +struct wlr_input_router_focus_layer { + struct wlr_input_router *router; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wlr_input_router_pointer pointer; + struct wlr_input_router_touch touch; + + struct wlr_input_router_focus focus; + + struct wl_listener router_destroy; + } WLR_PRIVATE; +}; + +struct wlr_input_router_implicit_grab_layer_touch_point { + struct { + uint32_t serial; + struct wlr_input_router_focus focus; + } WLR_PRIVATE; +}; + +/** + * An input router implicit grab layer which implements implicit grab semantics. + * For pointer, it means that the focus is locked if at least one button is + * pressed. For touch, it means the focus received with a new touch point always + * stays the same. + */ +struct wlr_input_router_implicit_grab_layer { + struct wlr_input_router *router; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wlr_input_router_pointer pointer; + struct wlr_input_router_focus pointer_focus; + uint32_t pointer_init_button; + uint32_t pointer_init_serial; + bool pointer_grabbed; + + struct wlr_input_router_touch touch; + struct wlr_input_router_implicit_grab_layer_touch_point + touch_points[WLR_INPUT_ROUTER_MAX_TOUCH_POINTS]; + + struct wl_listener router_destroy; + } WLR_PRIVATE; +}; + +void wlr_input_router_init(struct wlr_input_router *router, + const struct wlr_input_router_interface *impl); +void wlr_input_router_finish(struct wlr_input_router *router); + +void wlr_input_router_focus_init(struct wlr_input_router_focus *focus); +void wlr_input_router_focus_finish(struct wlr_input_router_focus *focus); + +bool wlr_input_router_focus_is_none(const struct wlr_input_router_focus *focus); +struct wlr_surface *wlr_input_router_focus_get_surface( + const struct wlr_input_router_focus *focus); +void *wlr_input_router_focus_get_user(const struct wlr_input_router_focus *focus); + +void wlr_input_router_focus_clear(struct wlr_input_router_focus *focus); +void wlr_input_router_focus_set_surface(struct wlr_input_router_focus *focus, + struct wlr_surface *surface); +void wlr_input_router_focus_set_user(struct wlr_input_router_focus *focus, + void *user, struct wl_signal *destroy_signal); + +void wlr_input_router_focus_copy(struct wlr_input_router_focus *dst, + const struct wlr_input_router_focus *src); + +// TODO: this is only required by the focus layer, remove? +void wlr_input_router_at(struct wlr_input_router *router, double x, double y, + struct wlr_input_router_focus *focus, double *local_x, double *local_y); + +bool wlr_input_router_get_surface_position(struct wlr_input_router *router, + struct wlr_surface *surface, double *x, double *y); + +bool wlr_input_router_register_handler_interface( + const struct wlr_input_router_handler_interface *iface, + int32_t priority, struct wlr_input_router_handler_priority_list *priority_list); + +void wlr_input_router_handler_init(struct wlr_input_router_handler *handler, + struct wlr_input_router_handler *head, + const struct wlr_input_router_handler_interface *impl, + const struct wlr_input_router_handler_priority_list *priority_list); + +void wlr_input_router_handler_finish(struct wlr_input_router_handler *handler); + +uint32_t wlr_input_router_keyboard_notify_focus( + struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_focus_event *event); + +void wlr_input_router_keyboard_notify_device( + struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_device_event *event); + +uint32_t wlr_input_router_keyboard_notify_key( + struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_key_event *event); + +void wlr_input_router_keyboard_notify_modifiers( + struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_modifiers_event *event); + +bool wlr_input_router_keyboard_register_interface( + const struct wlr_input_router_keyboard_interface *iface, int32_t priority); + +void wlr_input_router_keyboard_init( + struct wlr_input_router_keyboard *handler, struct wlr_input_router *router, + const struct wlr_input_router_keyboard_interface *impl); + +void wlr_input_router_keyboard_finish(struct wlr_input_router_keyboard *handler); + +uint32_t wlr_input_router_pointer_notify_position( + struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event); + +uint32_t wlr_input_router_pointer_notify_button( + struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_button_event *event); + +void wlr_input_router_pointer_notify_axis(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_axis_event *event); + +void wlr_input_router_pointer_notify_frame(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_frame_event *event); + +uint32_t wlr_input_router_pointer_refresh_position(struct wlr_input_router_pointer *pointer); + +uint32_t wlr_input_router_pointer_clear_focus(struct wlr_input_router_pointer *pointer); + +bool wlr_input_router_pointer_register_interface( + const struct wlr_input_router_pointer_interface *iface, int32_t priority); + +void wlr_input_router_pointer_init(struct wlr_input_router_pointer *pointer, + struct wlr_input_router *router, const struct wlr_input_router_pointer_interface *impl); +void wlr_input_router_pointer_finish(struct wlr_input_router_pointer *pointer); + +void wlr_input_router_touch_notify_position(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_position_event *event); + +uint32_t wlr_input_router_touch_notify_down(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_down_event *event); + +uint32_t wlr_input_router_touch_notify_up(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_up_event *event); + +void wlr_input_router_touch_notify_cancel(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_cancel_event *event); + +void wlr_input_router_touch_notify_frame(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_frame_event *event); + +bool wlr_input_router_touch_register_interface( + const struct wlr_input_router_touch_interface *iface, int32_t priority); + +void wlr_input_router_touch_init(struct wlr_input_router_touch *touch, + struct wlr_input_router *router, const struct wlr_input_router_touch_interface *impl); +void wlr_input_router_touch_finish(struct wlr_input_router_touch *touch); + +bool wlr_input_router_focus_layer_register(int32_t priority); + +struct wlr_input_router_focus_layer *wlr_input_router_focus_layer_create( + struct wlr_input_router *router); +void wlr_input_router_focus_layer_destroy(struct wlr_input_router_focus_layer *layer); + +bool wlr_input_router_implicit_grab_layer_register(int32_t priority); + +struct wlr_input_router_implicit_grab_layer *wlr_input_router_implicit_grab_layer_create( + struct wlr_input_router *router); +void wlr_input_router_implicit_grab_layer_destroy( + struct wlr_input_router_implicit_grab_layer *layer); + +bool wlr_input_router_implicit_grab_layer_validate_pointer_serial( + struct wlr_input_router_implicit_grab_layer *layer, struct wlr_surface *origin, + uint32_t serial, uint32_t *button); + +bool wlr_input_router_implicit_grab_layer_validate_touch_serial( + struct wlr_input_router_implicit_grab_layer *layer, struct wlr_surface *origin, + uint32_t serial, int32_t *id); + +#endif diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index bccb23090..718a7b1e7 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "pointer-constraints-unstable-v1-protocol.h" @@ -89,6 +90,53 @@ struct wlr_pointer_constraints_v1 { } WLR_PRIVATE; }; +/** + * A zwp_pointer_constraints_v1 input router layer which modifiers pointer + * position based on the constraint of an active surface. + */ +struct wlr_pointer_constraints_v1_input_router_layer { + struct wlr_input_router *router; + struct wlr_pointer_constraints_v1 *constraints; + struct wlr_seat *seat; + + struct { + struct wl_signal destroy; + + /** + * Emitted when a pointer lock with a cursor hint is unlocked. The + * compositor should then warp the pointer to the specified position. + */ + struct wl_signal cursor_hint; + } events; + + struct { + struct wlr_input_router_pointer pointer; + + struct wlr_surface *active_surface; + struct wlr_pointer_constraint_v1 *active; + + double last_x, last_y; + double lock_sx, lock_sy; + bool lock_applied; + + struct wl_listener active_surface_destroy; + + struct wl_listener active_destroy; + struct wl_listener active_set_region; + + struct wl_listener constraints_destroy; + struct wl_listener constraints_new_constraint; + + struct wl_listener router_destroy; + struct wl_listener seat_destroy; + } WLR_PRIVATE; +}; + +struct wlr_pointer_constraints_v1_input_router_layer_cursor_hint_event { + // Global position to warp the pointer to + double x, y; +}; + struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( struct wl_display *display); @@ -105,4 +153,17 @@ void wlr_pointer_constraint_v1_send_activated( void wlr_pointer_constraint_v1_send_deactivated( struct wlr_pointer_constraint_v1 *constraint); +bool wlr_pointer_constraints_v1_input_router_layer_register(int32_t priority); + +struct wlr_pointer_constraints_v1_input_router_layer * +wlr_pointer_constraints_v1_input_router_layer_create( + struct wlr_input_router *router, struct wlr_pointer_constraints_v1 *constraints, + struct wlr_seat *seat); +void wlr_pointer_constraints_v1_input_router_layer_destroy( + struct wlr_pointer_constraints_v1_input_router_layer *layer); + +void wlr_pointer_constraints_v1_input_router_layer_set_active_surface( + struct wlr_pointer_constraints_v1_input_router_layer *layer, + struct wlr_surface *surface); + #endif diff --git a/include/wlr/types/wlr_relative_pointer_v1.h b/include/wlr/types/wlr_relative_pointer_v1.h index 4db7b5220..dcfcc24fd 100644 --- a/include/wlr/types/wlr_relative_pointer_v1.h +++ b/include/wlr/types/wlr_relative_pointer_v1.h @@ -10,6 +10,7 @@ #define WLR_TYPES_WLR_RELATIVE_POINTER_V1_H #include +#include /** * This protocol specifies a set of interfaces used for making clients able to @@ -61,6 +62,29 @@ struct wlr_relative_pointer_v1 { } WLR_PRIVATE; }; +/** + * A zwp_relative_pointer_v1 input router layer which + * zwp_relative_pointer_v1.relative_motion events based on pointer position + * events. + */ +struct wlr_relative_pointer_v1_input_router_layer { + struct wlr_input_router *router; + struct wlr_relative_pointer_manager_v1 *manager; + struct wlr_seat *seat; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wlr_input_router_pointer pointer; + + struct wl_listener router_destroy; + struct wl_listener manager_destroy; + struct wl_listener seat_destroy; + } WLR_PRIVATE; +}; + struct wlr_relative_pointer_manager_v1 *wlr_relative_pointer_manager_v1_create( struct wl_display *display); @@ -79,4 +103,13 @@ void wlr_relative_pointer_manager_v1_send_relative_motion( struct wlr_relative_pointer_v1 *wlr_relative_pointer_v1_from_resource( struct wl_resource *resource); +bool wlr_relative_pointer_v1_input_router_layer_register(int32_t priority); + +struct wlr_relative_pointer_v1_input_router_layer * +wlr_relative_pointer_v1_input_router_layer_create( + struct wlr_input_router *router, struct wlr_relative_pointer_manager_v1 *manager, + struct wlr_seat *seat); +void wlr_relative_pointer_v1_input_router_layer_destroy( + struct wlr_relative_pointer_v1_input_router_layer *layer); + #endif diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 74b4341dd..a591f9462 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -346,6 +347,39 @@ struct wlr_seat_keyboard_focus_change_event { struct wlr_surface *old_surface, *new_surface; }; +struct wlr_seat_input_router_layer_touch_point { + struct { + struct wlr_seat_client *seat_client; + struct wl_listener seat_client_destroy; + wl_fixed_t sx, sy; + } WLR_PRIVATE; +}; + +/** + * A wl_seat input router layer which sends wl_keyboard, wl_pointer, and + * wl_touch events. + */ +struct wlr_seat_input_router_layer { + struct wlr_input_router *router; + struct wlr_seat *seat; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wlr_input_router_keyboard keyboard; + struct wlr_input_router_pointer pointer; + + struct wlr_input_router_touch touch; + struct wlr_seat_input_router_layer_touch_point + touch_points[WLR_INPUT_ROUTER_MAX_TOUCH_POINTS]; + + struct wl_listener router_destroy; + struct wl_listener seat_destroy; + } WLR_PRIVATE; +}; + /** * Allocates a new struct wlr_seat and adds a wl_seat global to the display. */ @@ -762,4 +796,11 @@ struct wlr_seat_client *wlr_seat_client_from_pointer_resource( */ bool wlr_surface_accepts_touch(struct wlr_surface *surface, struct wlr_seat *wlr_seat); +bool wlr_seat_input_router_layer_register(int32_t priority); + +struct wlr_seat_input_router_layer *wlr_seat_input_router_layer_create( + struct wlr_input_router *router, struct wlr_seat *seat); +void wlr_seat_input_router_layer_destroy(struct wlr_seat_input_router_layer *layer); + + #endif diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index 77f7a672e..e15d4a2d3 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -10,6 +10,7 @@ #define WLR_TYPES_WLR_TEXT_INPUT_V3_H #include +#include #include #include @@ -83,6 +84,40 @@ struct wlr_text_input_manager_v3 { } WLR_PRIVATE; }; +/** + * A zwp_text_input_v3 input router layer which synchronizes focuses of text + * inputs to the keyboard focus and tracks the currently active text input. + */ +struct wlr_text_input_v3_input_router_layer { + struct wlr_input_router *router; + struct wlr_text_input_manager_v3 *manager; + struct wlr_seat *seat; + + struct { + struct wl_signal destroy; + + // struct wlr_text_input_v3_input_router_layer_set_active_event + struct wl_signal set_active_text_input; + } events; + + struct { + struct wlr_input_router_keyboard keyboard; + + struct wl_list text_inputs; + struct wlr_text_input_v3 *active_text_input; + + struct wl_listener manager_destroy; + struct wl_listener manager_text_input; + + struct wl_listener router_destroy; + struct wl_listener seat_destroy; + } WLR_PRIVATE; +}; + +struct wlr_text_input_v3_input_router_layer_set_active_event { + struct wlr_text_input_v3 *active_text_input; +}; + struct wlr_text_input_manager_v3 *wlr_text_input_manager_v3_create( struct wl_display *wl_display); @@ -100,4 +135,12 @@ void wlr_text_input_v3_send_delete_surrounding_text( uint32_t after_length); void wlr_text_input_v3_send_done(struct wlr_text_input_v3 *text_input); +bool wlr_text_input_v3_input_router_layer_register(int32_t priority); + +struct wlr_text_input_v3_input_router_layer *wlr_text_input_v3_input_router_layer_create( + struct wlr_input_router *router, struct wlr_text_input_manager_v3 *manager, + struct wlr_seat *seat); +void wlr_text_input_v3_input_router_layer_destroy( + struct wlr_text_input_v3_input_router_layer *layer); + #endif diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 3e9fd2b70..d8ec24d11 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -26,6 +26,7 @@ struct wlr_xdg_shell { struct wl_signal new_surface; // struct wlr_xdg_surface struct wl_signal new_toplevel; // struct wlr_xdg_toplevel struct wl_signal new_popup; // struct wlr_xdg_popup + struct wl_signal popup_grab; // struct wlr_xdg_shell_popup_grab_event struct wl_signal destroy; } events; @@ -36,6 +37,12 @@ struct wlr_xdg_shell { } WLR_PRIVATE; }; +struct wlr_xdg_shell_popup_grab_event { + struct wlr_xdg_popup *popup; + struct wlr_seat_client *seat_client; + uint32_t serial; +}; + struct wlr_xdg_client { struct wlr_xdg_shell *shell; struct wl_resource *resource; @@ -340,6 +347,32 @@ struct wlr_xdg_toplevel_show_window_menu_event { int32_t x, y; }; +/** + * A input router layer which implements xdg_popup.grab semantics. It is + * destroyed automatically when the grab is dismissed. + */ +struct wlr_xdg_popup_grab_input_router_layer { + struct wlr_input_router *router; + struct wlr_xdg_popup *popup; + + struct { + struct wl_signal destroy; + } events; + + struct { + struct wlr_input_router_keyboard keyboard; + struct wlr_input_router_focus keyboard_focus; + + struct wlr_input_router_pointer pointer; + + struct wlr_input_router_touch touch; + + struct wlr_addon router_addon; + + struct wl_listener popup_destroy; + } WLR_PRIVATE; +}; + /** * Create the xdg_wm_base global with the specified version. */ @@ -580,4 +613,12 @@ void wlr_xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface, */ uint32_t wlr_xdg_surface_schedule_configure(struct wlr_xdg_surface *surface); +bool wlr_xdg_popup_grab_input_router_layer_register(int32_t priority); + +struct wlr_xdg_popup_grab_input_router_layer *wlr_xdg_popup_grab_input_router_layer_get_or_create( + struct wlr_input_router *router, struct wlr_xdg_popup *popup); + +void wlr_xdg_popup_grab_input_router_layer_destroy( + struct wlr_xdg_popup_grab_input_router_layer *layer); + #endif diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index b780eedac..1c9beaabc 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -10,6 +10,8 @@ #include #include "types/wlr_data_device.h" +// TODO: drop seat grabs + static void drag_handle_seat_client_destroy(struct wl_listener *listener, void *data) { struct wlr_drag *drag = @@ -539,3 +541,54 @@ void wlr_seat_start_touch_drag(struct wlr_seat *seat, struct wlr_drag *drag, wlr_seat_start_drag(seat, drag, serial); } + +void wlr_drag_start(struct wlr_drag *drag) { + wlr_seat_start_drag(drag->seat, drag, 0); +} + +void wlr_drag_enter(struct wlr_drag *drag, struct wlr_surface *surface, + double sx, double sy) { + drag_set_focus(drag, surface, sx, sy); +} + +void wlr_drag_clear_focus(struct wlr_drag *drag) { + drag_set_focus(drag, NULL, 0, 0); +} + +void wlr_drag_send_motion(struct wlr_drag *drag, uint32_t time_msec, + double sx, double sy) { + if (drag->focus != NULL && drag->focus_client != NULL) { + struct wl_resource *resource; + wl_resource_for_each(resource, &drag->focus_client->data_devices) { + wl_data_device_send_motion(resource, time_msec, wl_fixed_from_double(sx), + wl_fixed_from_double(sy)); + } + + struct wlr_drag_motion_event event = { + .drag = drag, + .time = time_msec, + .sx = sx, + .sy = sy, + }; + wl_signal_emit_mutable(&drag->events.motion, &event); + } +} + +void wlr_drag_drop_and_destroy(struct wlr_drag *drag, uint32_t time_msec) { + if (drag->source != NULL) { + if (drag->focus_client != NULL && drag->source->current_dnd_action && + drag->source->accepted) { + drag_drop(drag, time_msec); + } else if (drag->source->impl->dnd_finish) { + // This will end the grab and free `drag` + wlr_data_source_destroy(drag->source); + return; + } + } + + drag_destroy(drag); +} + +void wlr_drag_destroy(struct wlr_drag *drag) { + drag_destroy(drag); +} diff --git a/types/input_router/drag.c b/types/input_router/drag.c new file mode 100644 index 000000000..46457fa70 --- /dev/null +++ b/types/input_router/drag.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include +#include +#include + +static void update_position(struct wlr_drag_input_router_layer *layer, bool synthetic, + uint32_t time_msec, const struct wlr_input_router_focus *focus, double x, double y) { + struct wlr_surface *surface = wlr_input_router_focus_get_surface(focus); + double sx = 0, sy = 0; + + if (surface != NULL) { + double surface_x, surface_y; + if (!wlr_input_router_get_surface_position(layer->router, surface, + &surface_x, &surface_y)) { + goto out; + } + sx = x - surface_x; + sy = y - surface_y; + } + + // One of these will short-circuit + wlr_drag_enter(layer->drag, surface, sx, sy); + wlr_drag_send_motion(layer->drag, time_msec, sx, sy); + +out: + if (layer->icon_position.x != x || layer->icon_position.y != y) { + layer->icon_position.x = x; + layer->icon_position.y = y; + } + wl_signal_emit_mutable(&layer->events.set_icon_position, NULL); +} + +static uint32_t pointer_position(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event) { + struct wlr_drag_input_router_layer *layer = wl_container_of(pointer, layer, pointer); + update_position(layer, false, event->time_msec, event->focus, event->x, event->y); + return 0; +} + +static uint32_t pointer_button(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_button_event *event) { + struct wlr_drag_input_router_layer *layer = wl_container_of(pointer, layer, pointer); + uint32_t serial = wlr_input_router_pointer_notify_button(pointer, event); + + if (event->button == layer->pointer_button && + event->state == WL_POINTER_BUTTON_STATE_RELEASED) { + wlr_drag_drop_and_destroy(layer->drag, event->time_msec); + return 0; + } + + return serial; +} + +static void pointer_axis(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_axis_event *event) { + // Consumed +} + +static void pointer_frame(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_frame_event *event) { + // Consumed +} + +static const struct wlr_input_router_pointer_interface pointer_impl = { + .base = { + .name = "wlr_drag_input_router_layer-pointer", + }, + .position = pointer_position, + .button = pointer_button, + .axis = pointer_axis, + .frame = pointer_frame, +}; + +static void touch_position(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_position_event *event) { + struct wlr_drag_input_router_layer *layer = wl_container_of(touch, layer, touch); + if (event->id == layer->touch_id) { + update_position(layer, false, event->time_msec, event->focus, event->x, event->y); + } else { + wlr_input_router_touch_notify_position(touch, event); + } +} + +static uint32_t touch_down(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_down_event *event) { + struct wlr_drag_input_router_layer *layer = wl_container_of(touch, layer, touch); + assert(event->id != layer->touch_id); + return wlr_input_router_touch_notify_down(touch, event); +} + +static uint32_t touch_up(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_up_event *event) { + struct wlr_drag_input_router_layer *layer = wl_container_of(touch, layer, touch); + if (event->id == layer->touch_id) { + wlr_drag_drop_and_destroy(layer->drag, event->time_msec); + return 0; + } else { + return wlr_input_router_touch_notify_up(touch, event); + } +} + +static void touch_cancel(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_cancel_event *event) { + struct wlr_drag_input_router_layer *layer = wl_container_of(touch, layer, touch); + if (event->id == layer->touch_id) { + wlr_drag_destroy(layer->drag); + } else { + wlr_input_router_touch_notify_cancel(touch, event); + } +} + +static void touch_frame(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_frame_event *event) { + // Consumed +} + +static const struct wlr_input_router_touch_interface touch_impl = { + .base = { + .name = "wlr_drag_input_router_layer-touch", + }, + .position = touch_position, + .down = touch_down, + .up = touch_up, + .cancel = touch_cancel, + .frame = touch_frame, +}; + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_drag_input_router_layer *layer = wl_container_of(listener, layer, router_destroy); + wlr_drag_input_router_layer_destroy(layer); +} + +static void handle_drag_destroy(struct wl_listener *listener, void *data) { + struct wlr_drag_input_router_layer *layer = wl_container_of(listener, layer, drag_destroy); + wlr_drag_input_router_layer_destroy(layer); +} + +static struct wlr_drag_input_router_layer *layer_create(struct wlr_input_router *router, + struct wlr_drag *drag, enum wlr_drag_input_router_layer_type type) { + struct wlr_drag_input_router_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + layer->drag = drag; + layer->drag_destroy.notify = handle_drag_destroy; + wl_signal_add(&drag->events.destroy, &layer->drag_destroy); + + wl_signal_init(&layer->events.destroy); + wl_signal_init(&layer->events.set_icon_position); + + layer->icon_position.x = NAN; + layer->icon_position.y = NAN; + + layer->type = type; + + wlr_drag_start(drag); + + switch (type) { + case WLR_DRAG_INPUT_ROUTER_LAYER_POINTER: + wlr_input_router_pointer_init(&layer->pointer, router, &pointer_impl); + wlr_input_router_pointer_clear_focus(&layer->pointer); + update_position(layer, 0, true, &layer->pointer.focus, + layer->pointer.x, layer->pointer.y); + break; + case WLR_DRAG_INPUT_ROUTER_LAYER_TOUCH: + wlr_input_router_touch_init(&layer->touch, router, &touch_impl); + wlr_input_router_touch_notify_cancel(&layer->touch, + &(struct wlr_input_router_touch_cancel_event){ + .id = layer->touch_id, + }); + for (size_t i = 0; i < layer->touch.n_points; i++) { + struct wlr_input_router_touch_point *point = &layer->touch.points[i]; + if (point != NULL) { + update_position(layer, 0, true, &point->focus, point->x, point->y); + break; + } + } + break; + } + + return layer; +} + +bool wlr_drag_input_router_layer_register(int32_t priority) { + if (!wlr_input_router_pointer_register_interface(&pointer_impl, priority)) { + return false; + } + if (!wlr_input_router_touch_register_interface(&touch_impl, priority)) { + return false; + } + return true; +} + +struct wlr_drag_input_router_layer *wlr_drag_input_router_layer_create_pointer( + struct wlr_input_router *router, struct wlr_drag *drag, uint32_t button) { + struct wlr_drag_input_router_layer *layer = + layer_create(router, drag, WLR_DRAG_INPUT_ROUTER_LAYER_POINTER); + if (layer == NULL) { + return NULL; + } + layer->pointer_button = button; + + return layer; +} + +struct wlr_drag_input_router_layer *wlr_drag_input_router_layer_create_touch( + struct wlr_input_router *router, struct wlr_drag *drag, int32_t id) { + struct wlr_drag_input_router_layer *layer = + layer_create(router, drag, WLR_DRAG_INPUT_ROUTER_LAYER_TOUCH); + if (layer == NULL) { + return NULL; + } + layer->touch_id = id; + + return layer; +} + +void wlr_drag_input_router_layer_destroy(struct wlr_drag_input_router_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + assert(wl_list_empty(&layer->events.set_icon_position.listener_list)); + + switch (layer->type) { + case WLR_DRAG_INPUT_ROUTER_LAYER_POINTER: + wlr_input_router_pointer_refresh_position(&layer->pointer); + wlr_input_router_pointer_finish(&layer->pointer); + break; + case WLR_DRAG_INPUT_ROUTER_LAYER_TOUCH: + wlr_input_router_touch_finish(&layer->touch); + break; + } + + wl_list_remove(&layer->router_destroy.link); + wl_list_remove(&layer->drag_destroy.link); + + free(layer); +} diff --git a/types/input_router/focus.c b/types/input_router/focus.c new file mode 100644 index 000000000..3cdcaa0da --- /dev/null +++ b/types/input_router/focus.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +static void update_focus(struct wlr_input_router_focus_layer *layer, double x, double y) { + wlr_input_router_at(layer->router, x, y, &layer->focus, NULL, NULL); +} + +static uint32_t pointer_position(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event) { + struct wlr_input_router_focus_layer *layer = wl_container_of(pointer, layer, pointer); + + struct wlr_input_router_pointer_position_event copy; + if (!event->explicit_focus) { + update_focus(layer, event->x, event->y); + copy = *event; + copy.focus = &layer->focus; + event = © + } + return wlr_input_router_pointer_notify_position(pointer, event); +} + +static const struct wlr_input_router_pointer_interface pointer_impl = { + .base = { + .name = "wlr_input_router_focus_layer-pointer", + }, + .position = pointer_position, +}; + +static void touch_position(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_position_event *event) { + struct wlr_input_router_focus_layer *layer = wl_container_of(touch, layer, touch); + update_focus(layer, event->x, event->y); + + struct wlr_input_router_touch_position_event relayed = *event; + relayed.focus = &layer->focus; + wlr_input_router_touch_notify_position(touch, &relayed); +} + +static uint32_t touch_down(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_down_event *event) { + struct wlr_input_router_focus_layer *layer = wl_container_of(touch, layer, touch); + update_focus(layer, event->x, event->y); + + struct wlr_input_router_touch_down_event relayed = *event; + relayed.focus = &layer->focus; + return wlr_input_router_touch_notify_down(touch, &relayed); +} + +static const struct wlr_input_router_touch_interface touch_impl = { + .base = { + .name = "wlr_input_router_focus_layer-touch", + }, + .position = touch_position, + .down = touch_down, +}; + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_router_focus_layer *layer = wl_container_of(listener, layer, router_destroy); + wlr_input_router_focus_layer_destroy(layer); +} + +bool wlr_input_router_focus_layer_register(int32_t priority) { + if (!wlr_input_router_pointer_register_interface(&pointer_impl, priority)) { + return false; + } + if (!wlr_input_router_touch_register_interface(&touch_impl, priority)) { + return false; + } + return true; +} + +struct wlr_input_router_focus_layer *wlr_input_router_focus_layer_create( + struct wlr_input_router *router) { + struct wlr_input_router_focus_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_pointer_init(&layer->pointer, router, &pointer_impl); + wlr_input_router_touch_init(&layer->touch, router, &touch_impl); + + wlr_input_router_focus_init(&layer->focus); + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + wl_signal_init(&layer->events.destroy); + + return layer; +} + +void wlr_input_router_focus_layer_destroy(struct wlr_input_router_focus_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + + wlr_input_router_pointer_finish(&layer->pointer); + wlr_input_router_touch_finish(&layer->touch); + + wlr_input_router_focus_finish(&layer->focus); + + wl_list_remove(&layer->router_destroy.link); + + free(layer); + +} diff --git a/types/input_router/implicit_grab.c b/types/input_router/implicit_grab.c new file mode 100644 index 000000000..d552f27b2 --- /dev/null +++ b/types/input_router/implicit_grab.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include + +static uint32_t pointer_position(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event) { + struct wlr_input_router_implicit_grab_layer *layer = wl_container_of(pointer, layer, pointer); + + if (event->explicit_focus) { + // Invalidate implicit grab serial: the grab is no longer implicit + layer->pointer_init_serial = 0; + } + + struct wlr_input_router_pointer_position_event copy; + if (!event->explicit_focus && layer->pointer_grabbed) { + copy = *event; + copy.focus = &layer->pointer_focus; + event = © + } else { + wlr_input_router_focus_copy(&layer->pointer_focus, event->focus); + } + return wlr_input_router_pointer_notify_position(pointer, event); +} + +static uint32_t pointer_button(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_button_event *event) { + struct wlr_input_router_implicit_grab_layer *layer = wl_container_of(pointer, layer, pointer); + + uint32_t serial = wlr_input_router_pointer_notify_button(pointer, event); + + if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (pointer->n_buttons == 1) { + layer->pointer_grabbed = true; + layer->pointer_init_button = event->button; + layer->pointer_init_serial = serial; + } else { + // Ensure we didn't miss the first button press event + assert(layer->pointer_grabbed); + } + } else { + if (event->button == layer->pointer_init_button) { + // Invalidate implicit grab serial: the furst button has been released + layer->pointer_init_serial = 0; + } + + if (pointer->n_buttons == 0) { + // Ensure we didn't miss the first button release event + assert(layer->pointer_init_serial == 0); + layer->pointer_grabbed = false; + wlr_input_router_pointer_refresh_position(pointer); + } + } + + return serial; +} + +static const struct wlr_input_router_pointer_interface pointer_impl = { + .base = { + .name = "wlr_input_router_implicit_grab_layer-pointer", + }, + .position = pointer_position, + .button = pointer_button, +}; + +static void touch_position(struct wlr_input_router_touch *pointer, + const struct wlr_input_router_touch_position_event *event) { + struct wlr_input_router_implicit_grab_layer *layer = + wl_container_of(pointer, layer, touch); + + struct wlr_input_router_touch_position_event relayed = *event; + relayed.focus = &layer->touch_points[event->index].focus; + wlr_input_router_touch_notify_position(pointer, &relayed); +} + +static uint32_t touch_down(struct wlr_input_router_touch *pointer, + const struct wlr_input_router_touch_down_event *event) { + struct wlr_input_router_implicit_grab_layer *layer = + wl_container_of(pointer, layer, touch); + + uint32_t serial = wlr_input_router_touch_notify_down(pointer, event); + + struct wlr_input_router_implicit_grab_layer_touch_point *point = + &layer->touch_points[event->index]; + point->serial = serial; + wlr_input_router_focus_copy(&point->focus, event->focus); + + return serial; +} + +static const struct wlr_input_router_touch_interface touch_impl = { + .base = { + .name = "wlr_input_router_implicit_grab_layer-touch", + }, + .position = touch_position, + .down = touch_down, +}; + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_router_implicit_grab_layer *layer = + wl_container_of(listener, layer, router_destroy); + wlr_input_router_implicit_grab_layer_destroy(layer); +} + +bool wlr_input_router_implicit_grab_layer_validate_pointer_serial( + struct wlr_input_router_implicit_grab_layer *layer, struct wlr_surface *origin, + uint32_t serial, uint32_t *button) { + if (serial == 0) { + return false; + } + + if (layer->pointer_init_serial != serial) { + return false; + } + if (origin != NULL && origin != wlr_input_router_focus_get_surface(&layer->pointer_focus)) { + return false; + } + + if (button != NULL) { + *button = layer->pointer_init_button; + } + return true; +} + +bool wlr_input_router_implicit_grab_layer_validate_touch_serial( + struct wlr_input_router_implicit_grab_layer *layer, struct wlr_surface *origin, + uint32_t serial, int32_t *id) { + if (serial == 0) { + return false; + } + + for (size_t i = 0; i < layer->touch.n_points; i++) { + struct wlr_input_router_implicit_grab_layer_touch_point *point = &layer->touch_points[i]; + if (layer->touch_points[i].serial != serial) { + continue; + } + if (origin != NULL && origin != wlr_input_router_focus_get_surface(&point->focus)) { + continue; + } + + if (id != NULL) { + *id = layer->touch.points[i].id; + } + return true; + } + + return false; +} + +bool wlr_input_router_implicit_grab_layer_register(int32_t priority) { + if (!wlr_input_router_pointer_register_interface(&pointer_impl, priority)) { + return false; + } + if (!wlr_input_router_touch_register_interface(&touch_impl, priority)) { + return false; + } + return true; +} + +struct wlr_input_router_implicit_grab_layer *wlr_input_router_implicit_grab_layer_create( + struct wlr_input_router *router) { + struct wlr_input_router_implicit_grab_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_pointer_init(&layer->pointer, router, &pointer_impl); + wlr_input_router_focus_init(&layer->pointer_focus); + + wlr_input_router_touch_init(&layer->touch, router, &touch_impl); + for (size_t i = 0; i < WLR_INPUT_ROUTER_MAX_TOUCH_POINTS; i++) { + wlr_input_router_focus_init(&layer->touch_points[i].focus); + } + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + wl_signal_init(&layer->events.destroy); + + return layer; +} + +void wlr_input_router_implicit_grab_layer_destroy( + struct wlr_input_router_implicit_grab_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + + wlr_input_router_pointer_finish(&layer->pointer); + wlr_input_router_focus_finish(&layer->pointer_focus); + + wlr_input_router_touch_finish(&layer->touch); + for (size_t i = 0; i < WLR_INPUT_ROUTER_MAX_TOUCH_POINTS; i++) { + wlr_input_router_focus_finish(&layer->touch_points[i].focus); + } + + wl_list_remove(&layer->router_destroy.link); + + free(layer); +} diff --git a/types/input_router/input_method.c b/types/input_router/input_method.c new file mode 100644 index 000000000..36525fe41 --- /dev/null +++ b/types/input_router/input_method.c @@ -0,0 +1,409 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct text_input { + struct wlr_input_method_v2_input_router_layer *layer; + struct wlr_text_input_v3 *wlr_text_input; + + struct wl_list link; + + struct wl_listener destroy; + struct wl_listener enable; + struct wl_listener disable; + struct wl_listener commit; +}; + +static void update_device_grab(struct wlr_input_method_v2_input_router_layer *layer) { + layer->device_grabbed = false; + struct wlr_keyboard *device = layer->keyboard.device; + if (layer->grab == NULL || device == NULL) { + return; + } + + struct wlr_virtual_keyboard_v1 *virtual_device = + wlr_input_device_get_virtual_keyboard(&device->base); + if (virtual_device != NULL && wl_resource_get_client(virtual_device->resource) == + wl_resource_get_client(layer->grab->resource)) { + // The current device is a virtual keyboard created by the input method + // client to relay events to other clients, don't grab it + return; + } + + layer->device_grabbed = true; + if (layer->grab->keyboard != device) { + wlr_input_method_keyboard_grab_v2_set_keyboard(layer->grab, device); + + // We can't relay the effective keyboard state without + // zwp_input_method_keyboard_grab_v2.key. + layer->n_forwarded_keys = 0; + } +} + +// Returns true if needs wlr_input_method_v2_send_done() afterwards +static bool update_input_method_active(struct wlr_input_method_v2_input_router_layer *layer) { + assert(layer->input_method != NULL); + + bool active = layer->active_text_input != NULL; + if (layer->input_method->active != active) { + if (active) { + wlr_input_method_v2_send_activate(layer->input_method); + } else { + wlr_input_method_v2_send_deactivate(layer->input_method); + } + return true; + } + + return false; +} + +// Returns true if needs wlr_input_method_v2_send_done() afterwards +static bool active_text_input_state(struct wlr_input_method_v2_input_router_layer *layer, + bool send_full) { + assert(layer->input_method != NULL); + + struct wlr_text_input_v3 *text_input = layer->active_text_input; + if (text_input == NULL) { + return false; + } + struct wlr_text_input_v3_state *state = &text_input->current; + + bool sent = false; + if (send_full) { + if (text_input->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) { + wlr_input_method_v2_send_surrounding_text(layer->input_method, + state->surrounding.text, state->surrounding.cursor, state->surrounding.anchor); + sent = true; + } + if (text_input->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) { + wlr_input_method_v2_send_content_type(layer->input_method, + state->content_type.hint, state->content_type.purpose); + sent = true; + } + } else { + if (state->features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) { + wlr_input_method_v2_send_surrounding_text(layer->input_method, + state->surrounding.text, state->surrounding.cursor, state->surrounding.anchor); + sent = true; + } + + // TODO: add proper wlr_text_input_v3 state field bitmask + if (true) { + wlr_input_method_v2_send_text_change_cause(layer->input_method, + state->text_change_cause); + sent = true; + } + + if (state->features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) { + wlr_input_method_v2_send_content_type(layer->input_method, + state->content_type.hint, state->content_type.purpose); + sent = true; + } + } + + return sent; +} + +static void set_grab(struct wlr_input_method_v2_input_router_layer *layer, + struct wlr_input_method_keyboard_grab_v2 *grab) { + if (layer->grab == grab) { + return; + } + + layer->grab = grab; + wl_list_remove(&layer->grab_destroy.link); + if (grab != NULL) { + wl_signal_add(&grab->events.destroy, &layer->grab_destroy); + } else { + wl_list_init(&layer->grab_destroy.link); + } + + update_device_grab(layer); +} + +static void keyboard_device(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_device_event *event) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(keyboard, layer, keyboard); + update_device_grab(layer); + + if (layer->device_grabbed) { + return; + } + + wlr_input_router_keyboard_notify_device(keyboard, event); +} + +static uint32_t keyboard_key(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_key_event *event) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(keyboard, layer, keyboard); + if (layer->device_grabbed) { + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + // We can't relay the effective keyboard state without + // zwp_input_method_keyboard_grab_v2.key, so the event is dropped. + if (event->intercepted) { + return 0; + } + assert(layer->n_forwarded_keys < WLR_KEYBOARD_KEYS_CAP); + layer->forwarded_keys[layer->n_forwarded_keys++] = event->key; + } else { + bool found = false; + for (size_t i = 0; i < layer->n_forwarded_keys; i++) { + if (layer->forwarded_keys[i] == event->key) { + layer->forwarded_keys[i] = layer->forwarded_keys[--layer->n_forwarded_keys]; + found = true; + break; + } + } + // We can't relay the effective keyboard state without + // zwp_input_method_keyboard_grab_v2.key, so intercepted releases of + // relayed pressed keys are sent too. + if (!found) { + return 0; + } + } + + wlr_input_method_keyboard_grab_v2_send_key(layer->grab, + event->time_msec, event->key, event->state); + return 0; + } + + return wlr_input_router_keyboard_notify_key(keyboard, event); +} + +static void keyboard_modifiers(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_modifiers_event *event) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(keyboard, layer, keyboard); + if (layer->device_grabbed) { + wlr_input_method_keyboard_grab_v2_send_modifiers(layer->grab, + &keyboard->device->modifiers); + return; + } + + wlr_input_router_keyboard_notify_modifiers(keyboard, event); +} + +static const struct wlr_input_router_keyboard_interface keyboard_impl = { + .base = { + .name = "wlr_input_method_v2_input_router_layer-keyboard", + }, + .device = keyboard_device, + .key = keyboard_key, + .modifiers = keyboard_modifiers, +}; + +static void handle_active_text_input_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(listener, layer, active_text_input_destroy); + wlr_input_method_v2_input_router_layer_set_active_text_input(layer, NULL); +} + +static void handle_active_text_input_commit(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(listener, layer, active_text_input_commit); + if (layer->input_method != NULL && active_text_input_state(layer, false)) { + wlr_input_method_v2_send_done(layer->input_method); + } +} + +static void handle_input_method_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(listener, layer, input_method_destroy); + wlr_input_method_v2_input_router_layer_set_input_method(layer, NULL); +} + +static void handle_input_method_commit(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(listener, layer, input_method_commit); + struct wlr_text_input_v3 *text_input = layer->active_text_input; + if (text_input == NULL) { + return; + } + + struct wlr_input_method_v2_state *state = &layer->input_method->current; + bool sent = false; + + if (state->commit_text != NULL) { + wlr_text_input_v3_send_commit_string(text_input, state->commit_text); + sent = true; + } + if (state->delete.before_length != 0 || state->delete.after_length != 0) { + wlr_text_input_v3_send_delete_surrounding_text(text_input, + state->delete.before_length, state->delete.after_length); + sent = true; + } + if (state->preedit.text != NULL) { + wlr_text_input_v3_send_preedit_string(text_input, state->preedit.text, + state->preedit.cursor_begin, state->preedit.cursor_end); + sent = true; + } + + if (sent) { + wlr_text_input_v3_send_done(text_input); + } +} + +static void handle_input_method_grab_keyboard(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(listener, layer, input_method_grab_keyboard); + struct wlr_input_method_keyboard_grab_v2 *grab = data; + set_grab(layer, grab); +} + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(listener, layer, router_destroy); + wlr_input_method_v2_input_router_layer_destroy(layer); +} + +static void handle_grab_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_method_v2_input_router_layer *layer = + wl_container_of(listener, layer, grab_destroy); + set_grab(layer, NULL); +} + +void wlr_input_method_v2_input_router_layer_set_input_method( + struct wlr_input_method_v2_input_router_layer *layer, + struct wlr_input_method_v2 *input_method) { + if (layer->input_method == input_method) { + return; + } + + if (layer->input_method != NULL && layer->input_method->active) { + // XXX: this shouldn't be possible actually + wlr_input_method_v2_send_deactivate(layer->input_method); + wlr_input_method_v2_send_done(layer->input_method); + } + + layer->input_method = input_method; + + wl_list_remove(&layer->input_method_destroy.link); + wl_list_remove(&layer->input_method_commit.link); + wl_list_remove(&layer->input_method_grab_keyboard.link); + + if (input_method != NULL) { + wl_signal_add(&input_method->events.destroy, &layer->input_method_destroy); + wl_signal_add(&input_method->events.commit, &layer->input_method_commit); + wl_signal_add(&input_method->events.grab_keyboard, &layer->input_method_grab_keyboard); + + if (update_input_method_active(layer)) { + wlr_input_method_v2_send_done(layer->input_method); + } + } else { + wl_list_init(&layer->input_method_destroy.link); + wl_list_init(&layer->input_method_commit.link); + wl_list_init(&layer->input_method_grab_keyboard.link); + } + + set_grab(layer, input_method != NULL ? input_method->keyboard_grab : NULL); +} + +void wlr_input_method_v2_input_router_layer_set_active_text_input( + struct wlr_input_method_v2_input_router_layer *layer, + struct wlr_text_input_v3 *text_input) { + if (layer->active_text_input == text_input) { + return; + } + + layer->active_text_input = text_input; + + wl_list_remove(&layer->active_text_input_destroy.link); + wl_list_remove(&layer->active_text_input_commit.link); + + if (text_input != NULL) { + wl_signal_add(&text_input->events.destroy, &layer->active_text_input_destroy); + wl_signal_add(&text_input->events.commit, &layer->active_text_input_commit); + } else { + wl_list_init(&layer->active_text_input_destroy.link); + wl_list_init(&layer->active_text_input_commit.link); + } + + if (layer->input_method != NULL) { + bool sent = false; + sent |= update_input_method_active(layer); + sent |= active_text_input_state(layer, true); + if (sent) { + wlr_input_method_v2_send_done(layer->input_method); + } + } +} + +bool wlr_input_method_v2_input_router_layer_register(int32_t priority) { + if (!wlr_input_router_keyboard_register_interface(&keyboard_impl, priority)) { + return false; + } + return true; +} + +struct wlr_input_method_v2_input_router_layer *wlr_input_method_v2_input_router_layer_create( + struct wlr_input_router *router) { + struct wlr_input_method_v2_input_router_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_keyboard_init(&layer->keyboard, + router, &keyboard_impl); + + layer->active_text_input_destroy.notify = handle_active_text_input_destroy; + wl_list_init(&layer->active_text_input_destroy.link); + layer->active_text_input_commit.notify = handle_active_text_input_commit; + wl_list_init(&layer->active_text_input_commit.link); + + layer->input_method_destroy.notify = handle_input_method_destroy; + wl_list_init(&layer->input_method_destroy.link); + layer->input_method_commit.notify = handle_input_method_commit; + wl_list_init(&layer->input_method_commit.link); + layer->input_method_grab_keyboard.notify = handle_input_method_grab_keyboard; + wl_list_init(&layer->input_method_grab_keyboard.link); + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + layer->grab_destroy.notify = handle_grab_destroy; + wl_list_init(&layer->grab_destroy.link); + + wl_signal_init(&layer->events.destroy); + + update_device_grab(layer); + + return layer; +} + +void wlr_input_method_v2_input_router_layer_destroy( + struct wlr_input_method_v2_input_router_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + + wlr_input_router_keyboard_notify_device(&layer->keyboard, + &(struct wlr_input_router_keyboard_device_event){ + .device = layer->keyboard.device, + }); + wlr_input_router_keyboard_finish(&layer->keyboard); + + wl_list_remove(&layer->active_text_input_destroy.link); + wl_list_remove(&layer->active_text_input_commit.link); + + wl_list_remove(&layer->input_method_destroy.link); + wl_list_remove(&layer->input_method_commit.link); + wl_list_remove(&layer->input_method_grab_keyboard.link); + + wl_list_remove(&layer->router_destroy.link); + wl_list_remove(&layer->grab_destroy.link); + + free(layer); +} diff --git a/types/input_router/keyboard.c b/types/input_router/keyboard.c new file mode 100644 index 000000000..384ec2911 --- /dev/null +++ b/types/input_router/keyboard.c @@ -0,0 +1,110 @@ +#include +#include + +static struct wlr_input_router_handler_priority_list keyboard_priority_list = {0}; + +static void set_device(struct wlr_input_router_keyboard *keyboard, struct wlr_keyboard *device) { + keyboard->device = device; + wl_list_remove(&keyboard->device_destroy.link); + if (device != NULL) { + wl_signal_add(&device->base.events.destroy, &keyboard->device_destroy); + } else { + wl_list_init(&keyboard->device_destroy.link); + } +} + +static void handle_device_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_router_keyboard *keyboard = + wl_container_of(listener, keyboard, device_destroy); +} + +uint32_t wlr_input_router_keyboard_notify_focus(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_focus_event *event) { + while ((keyboard = wl_container_of(keyboard->base.next, keyboard, base)) != NULL) { + wlr_input_router_focus_copy(&keyboard->focus, event->focus); + + if (keyboard->impl->focus != NULL) { + return keyboard->impl->focus(keyboard, event); + } + } + return 0; +} + +void wlr_input_router_keyboard_notify_device(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_device_event *event) { + while ((keyboard = wl_container_of(keyboard->base.next, keyboard, base)) != NULL) { + if (keyboard->device == event->device) { + return; + } + set_device(keyboard, event->device); + + if (keyboard->impl->device != NULL) { + keyboard->impl->device(keyboard, event); + return; + } + } +} + +uint32_t wlr_input_router_keyboard_notify_key(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_key_event *event) { + while ((keyboard = wl_container_of(keyboard->base.next, keyboard, base)) != NULL) { + if (keyboard->device == NULL) { + wlr_log(WLR_ERROR, "%s received a key event without an active device", + keyboard->impl->base.name); + return 0; + } + + if (keyboard->impl->key != NULL) { + return keyboard->impl->key(keyboard, event); + } + } + return 0; +} + +void wlr_input_router_keyboard_notify_modifiers(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_modifiers_event *event) { + while ((keyboard = wl_container_of(keyboard->base.next, keyboard, base)) != NULL) { + if (keyboard->device == NULL) { + wlr_log(WLR_ERROR, "%s received a modifiers event without an active device", + keyboard->impl->base.name); + return; + } + + if (keyboard->impl->modifiers != NULL) { + keyboard->impl->modifiers(keyboard, event); + return; + } + } +} + +bool wlr_input_router_keyboard_register_interface( + const struct wlr_input_router_keyboard_interface *iface, int32_t priority) { + return wlr_input_router_register_handler_interface(&iface->base, + priority, &keyboard_priority_list); +} + +void wlr_input_router_keyboard_init( + struct wlr_input_router_keyboard *keyboard, struct wlr_input_router *router, + const struct wlr_input_router_keyboard_interface *impl) { + *keyboard = (struct wlr_input_router_keyboard){ + .impl = impl, + }; + wlr_input_router_handler_init(&keyboard->base, &router->keyboard.base, + &impl->base, &keyboard_priority_list); + wlr_input_router_focus_init(&keyboard->focus); + + keyboard->device_destroy.notify = handle_device_destroy; + wl_list_init(&keyboard->device_destroy.link); + + struct wlr_input_router_keyboard *next = wl_container_of(keyboard->base.next, next, base); + if (next != NULL) { + wlr_input_router_focus_copy(&keyboard->focus, &next->focus); + set_device(keyboard, next->device); + } +} + +void wlr_input_router_keyboard_finish(struct wlr_input_router_keyboard *keyboard) { + wlr_input_router_focus_finish(&keyboard->focus); + wl_list_remove(&keyboard->device_destroy.link); + wlr_input_router_handler_finish(&keyboard->base); +} diff --git a/types/input_router/pointer.c b/types/input_router/pointer.c new file mode 100644 index 000000000..ddab9a14f --- /dev/null +++ b/types/input_router/pointer.c @@ -0,0 +1,141 @@ +#include +#include + +static struct wlr_input_router_handler_priority_list pointer_priority_list = {0}; + +uint32_t wlr_input_router_pointer_notify_position(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event) { + while ((pointer = wl_container_of(pointer->base.next, pointer, base)) != NULL) { + pointer->x = event->x; + pointer->y = event->y; + wlr_input_router_focus_copy(&pointer->focus, event->focus); + + if (pointer->impl->position != NULL) { + return pointer->impl->position(pointer, event); + } + } + return 0; +} + +uint32_t wlr_input_router_pointer_notify_button(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_button_event *event) { + while ((pointer = wl_container_of(pointer->base.next, pointer, base)) != NULL) { + struct wlr_input_router_pointer_button *button = NULL; + for (size_t i = 0; i < pointer->n_buttons; i++) { + if (pointer->buttons[i].button == event->button) { + button = &pointer->buttons[i]; + break; + } + } + + if (event->state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (button == NULL) { + if (pointer->n_buttons == WLR_INPUT_ROUTER_MAX_POINTER_BUTTONS) { + wlr_log(WLR_ERROR, "%s has too many pressed buttons, ignoring %"PRIi32, + pointer->impl->base.name, event->button); + return 0; + } + button = &pointer->buttons[pointer->n_buttons++]; + *button = (struct wlr_input_router_pointer_button){ + .button = event->button, + }; + } + ++button->count; + if (button->count != 1) { + // Already pressed + return 0; + } + } else { + if (button == NULL) { + wlr_log(WLR_ERROR, "%s received a release for a non-pressed button %"PRIi32, + pointer->impl->base.name, event->button); + return 0; + } + --button->count; + if (button->count != 0) { + // Still pressed + return 0; + } + *button = pointer->buttons[--pointer->n_buttons]; + } + + if (pointer->impl->button != NULL) { + struct wlr_input_router_pointer_button_event relayed = *event; + relayed.index = button - pointer->buttons; + return pointer->impl->button(pointer, &relayed); + } + } + return 0; +} + +void wlr_input_router_pointer_notify_axis(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_axis_event *event) { + while ((pointer = wl_container_of(pointer->base.next, pointer, base)) != NULL) { + if (pointer->impl->axis != NULL) { + pointer->impl->axis(pointer, event); + } + } +} + +void wlr_input_router_pointer_notify_frame(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_frame_event *event) { + while ((pointer = wl_container_of(pointer->base.next, pointer, base)) != NULL) { + if (pointer->impl->frame != NULL) { + pointer->impl->frame(pointer, event); + return; + } + } +} + +uint32_t wlr_input_router_pointer_refresh_position(struct wlr_input_router_pointer *pointer) { + return wlr_input_router_pointer_notify_position(pointer, + &(struct wlr_input_router_pointer_position_event){ + .x = pointer->x, + .y = pointer->y, + .focus = &pointer->focus, + .synthetic = true, + }); +} + +uint32_t wlr_input_router_pointer_clear_focus(struct wlr_input_router_pointer *pointer) { + return wlr_input_router_pointer_notify_position(pointer, + &(struct wlr_input_router_pointer_position_event){ + .x = pointer->x, + .y = pointer->y, + .focus = NULL, + .explicit_focus = true, + .synthetic = true, + }); +} + +bool wlr_input_router_pointer_register_interface( + const struct wlr_input_router_pointer_interface *iface, int32_t priority) { + return wlr_input_router_register_handler_interface(&iface->base, + priority, &pointer_priority_list); +} + +void wlr_input_router_pointer_init(struct wlr_input_router_pointer *pointer, + struct wlr_input_router *router, const struct wlr_input_router_pointer_interface *impl) { + *pointer = (struct wlr_input_router_pointer){ + .impl = impl, + }; + wlr_input_router_handler_init(&pointer->base, &router->pointer.base, + &impl->base, &pointer_priority_list); + wlr_input_router_focus_init(&pointer->focus); + + struct wlr_input_router_pointer *next = wl_container_of(pointer->base.next, next, base); + if (next != NULL) { + pointer->x = next->x; + pointer->y = next->y; + wlr_input_router_focus_copy(&pointer->focus, &next->focus); + for (size_t i = 0; i < next->n_buttons; i++) { + pointer->buttons[i] = next->buttons[i]; + } + pointer->n_buttons = next->n_buttons; + } +} + +void wlr_input_router_pointer_finish(struct wlr_input_router_pointer *pointer) { + wlr_input_router_focus_finish(&pointer->focus); + wlr_input_router_handler_finish(&pointer->base); +} diff --git a/types/input_router/pointer_constraints.c b/types/input_router/pointer_constraints.c new file mode 100644 index 000000000..9a64d47a4 --- /dev/null +++ b/types/input_router/pointer_constraints.c @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include +#include + +static void closest_point(pixman_region32_t *region, double x, double y, + double *out_x, double *out_y) { + *out_x = x; + *out_y = y; + double d2_best = INFINITY; + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); + for (int i = 0; i < nrects; i++) { + pixman_box32_t *rect = &rects[i]; + double box_x = x < rect->x1 ? rect->x1 : x >= rect->x2 ? rect->x2 - 1 : x; + double box_y = y < rect->y1 ? rect->y1 : y >= rect->y2 ? rect->y2 - 1 : y; + double dx = box_x - x, dy = box_y - y; + double d2 = dx * dx + dy * dy; + if (d2 <= d2_best) { + d2_best = d2; + *out_x = box_x; + *out_y = box_y; + } + if (d2_best == 0) { + break; + } + } +} + +static uint32_t update_position(struct wlr_pointer_constraints_v1_input_router_layer *layer, + const struct wlr_input_router_pointer_position_event *event) { + struct wlr_pointer_constraint_v1 *constraint = layer->active; + + struct wlr_input_router_pointer_position_event copy; + if (constraint != NULL) { + double surface_x, surface_y; + if (!wlr_input_router_get_surface_position(layer->router, constraint->surface, + &surface_x, &surface_y)) { + goto out; + } + double sx = event->x - surface_x, sy = event->y - surface_y; + + copy = *event; + switch (constraint->type) { + case WLR_POINTER_CONSTRAINT_V1_LOCKED: + if (!layer->lock_applied) { + closest_point(&constraint->region, sx, sy, &layer->lock_sx, &layer->lock_sy); + layer->lock_applied = true; + } + copy.x = surface_x + layer->lock_sx; + copy.y = surface_y + layer->lock_sy; + break; + case WLR_POINTER_CONSTRAINT_V1_CONFINED: + if (!wlr_region_confine(&constraint->region, layer->last_x - surface_x, + layer->last_y - surface_y, sx, sy, &sx, &sy)) { + closest_point(&constraint->region, sx, sy, &sx, &sy); + } + copy.x = surface_x + sx; + copy.y = surface_y + sy; + break; + } + + event = © + } + +out: + layer->last_x = event->x; + layer->last_y = event->y; + return wlr_input_router_pointer_notify_position(&layer->pointer, event); +} + +static uint32_t pointer_position(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = + wl_container_of(pointer, layer, pointer); + return update_position(layer, event); +} + +static const struct wlr_input_router_pointer_interface pointer_impl = { + .base = { + .name = "wlr_pointer_constraints_v1_input_router_layer-pointer", + }, + .position = pointer_position, +}; + +static void refresh_position(struct wlr_pointer_constraints_v1_input_router_layer *layer) { + update_position(layer, + &(struct wlr_input_router_pointer_position_event){ + .x = layer->pointer.x, + .y = layer->pointer.y, + .focus = &layer->pointer.focus, + .synthetic = true, + }); +} + +static void set_active(struct wlr_pointer_constraints_v1_input_router_layer *layer, + struct wlr_pointer_constraint_v1 *constraint) { + struct wlr_pointer_constraint_v1 *prev = layer->active; + if (constraint == prev) { + return; + } + + layer->lock_applied = false; + layer->active = constraint; + + wl_list_remove(&layer->active_destroy.link); + wl_list_remove(&layer->active_set_region.link); + + if (prev != NULL) { + if (prev->type == WLR_POINTER_CONSTRAINT_V1_LOCKED && prev->current.cursor_hint.enabled) { + double surface_x, surface_y; + if (wlr_input_router_get_surface_position(layer->router, prev->surface, + &surface_x, &surface_y)) { + struct wlr_pointer_constraints_v1_input_router_layer_cursor_hint_event event = { + .x = surface_x + prev->current.cursor_hint.x, + .y = surface_y + prev->current.cursor_hint.y, + }; + wl_signal_emit_mutable(&layer->events.cursor_hint, &event); + } + } + + wlr_pointer_constraint_v1_send_deactivated(prev); + } + + if (constraint != NULL) { + wl_signal_add(&constraint->events.destroy, &layer->active_destroy); + wl_signal_add(&constraint->events.set_region, &layer->active_set_region); + + wlr_pointer_constraint_v1_send_activated(constraint); + } else { + wl_list_init(&layer->active_destroy.link); + wl_list_init(&layer->active_set_region.link); + } + + refresh_position(layer); +} + +static void handle_active_surface_destroy(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = + wl_container_of(listener, layer, active_surface_destroy); + wlr_pointer_constraints_v1_input_router_layer_set_active_surface(layer, NULL); +} + +static void handle_active_destroy(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = + wl_container_of(listener, layer, active_destroy); + set_active(layer, NULL); +} + +static void handle_active_set_region(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = + wl_container_of(listener, layer, active_set_region); + layer->lock_applied = false; + refresh_position(layer); +} + +static void handle_constraints_destroy(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = + wl_container_of(listener, layer, constraints_destroy); + wlr_pointer_constraints_v1_input_router_layer_destroy(layer); +} + +static void handle_constraints_new_constraint(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = + wl_container_of(listener, layer, constraints_new_constraint); + struct wlr_pointer_constraint_v1 *constraint = data; + if (layer->active_surface == constraint->surface) { + set_active(layer, constraint); + } +} + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = + wl_container_of(listener, layer, router_destroy); + wlr_pointer_constraints_v1_input_router_layer_destroy(layer); +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = + wl_container_of(listener, layer, seat_destroy); + wlr_pointer_constraints_v1_input_router_layer_destroy(layer); +} + +void wlr_pointer_constraints_v1_input_router_layer_set_active_surface( + struct wlr_pointer_constraints_v1_input_router_layer *layer, + struct wlr_surface *surface) { + if (layer->active_surface == surface) { + return; + } + + layer->active_surface = surface; + + wl_list_remove(&layer->active_surface_destroy.link); + if (surface != NULL) { + wl_signal_add(&surface->events.destroy, &layer->active_surface_destroy); + } else { + wl_list_init(&layer->active_surface_destroy.link); + } + + struct wlr_pointer_constraint_v1 *constraint = + wlr_pointer_constraints_v1_constraint_for_surface(layer->constraints, + surface, layer->seat); + set_active(layer, constraint); +} + +bool wlr_pointer_constraints_v1_input_router_layer_register(int32_t priority) { + if (!wlr_input_router_pointer_register_interface(&pointer_impl, priority)) { + return false; + } + return true; +} + +struct wlr_pointer_constraints_v1_input_router_layer * +wlr_pointer_constraints_v1_input_router_layer_create( + struct wlr_input_router *router, struct wlr_pointer_constraints_v1 *constraints, + struct wlr_seat *seat) { + struct wlr_pointer_constraints_v1_input_router_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_pointer_init(&layer->pointer, router, &pointer_impl); + + layer->active_surface_destroy.notify = handle_active_surface_destroy; + wl_list_init(&layer->active_surface_destroy.link); + + layer->active_destroy.notify = handle_active_destroy; + wl_list_init(&layer->active_destroy.link); + + layer->active_set_region.notify = handle_active_set_region; + wl_list_init(&layer->active_set_region.link); + + layer->constraints = constraints; + + layer->constraints_destroy.notify = handle_constraints_destroy; + wl_signal_add(&constraints->events.destroy, &layer->constraints_destroy); + layer->constraints_new_constraint.notify = handle_constraints_new_constraint; + wl_signal_add(&constraints->events.new_constraint, &layer->constraints_new_constraint); + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + layer->seat = seat; + layer->seat_destroy.notify = handle_seat_destroy; + wl_signal_add(&seat->events.destroy, &layer->seat_destroy); + + wl_signal_init(&layer->events.destroy); + wl_signal_init(&layer->events.cursor_hint); + + return layer; +} + +void wlr_pointer_constraints_v1_input_router_layer_destroy( + struct wlr_pointer_constraints_v1_input_router_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + assert(wl_list_empty(&layer->events.cursor_hint.listener_list)); + + wlr_input_router_pointer_finish(&layer->pointer); + + wl_list_remove(&layer->active_surface_destroy.link); + wl_list_remove(&layer->active_destroy.link); + wl_list_remove(&layer->active_set_region.link); + wl_list_remove(&layer->constraints_new_constraint.link); + wl_list_remove(&layer->constraints_destroy.link); + wl_list_remove(&layer->router_destroy.link); + wl_list_remove(&layer->seat_destroy.link); + + free(layer); +} diff --git a/types/input_router/relative_pointer.c b/types/input_router/relative_pointer.c new file mode 100644 index 000000000..bfb8e3a27 --- /dev/null +++ b/types/input_router/relative_pointer.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include + +static uint32_t pointer_position(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event) { + struct wlr_relative_pointer_v1_input_router_layer *layer = + wl_container_of(pointer, layer, pointer); + wlr_relative_pointer_manager_v1_send_relative_motion(layer->manager, + layer->seat, (uint64_t)event->time_msec * 1000, event->dx, event->dy, + event->unaccel_dx, event->unaccel_dy); + return wlr_input_router_pointer_notify_position(pointer, event); +} + +static const struct wlr_input_router_pointer_interface pointer_impl = { + .base = { + .name = "wlr_relative_pointer_v1_input_router_layer-pointer", + }, + .position = pointer_position, +}; + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_relative_pointer_v1_input_router_layer *layer = + wl_container_of(listener, layer, router_destroy); + wlr_relative_pointer_v1_input_router_layer_destroy(layer); +} + +static void handle_manager_destroy(struct wl_listener *listener, void *data) { + struct wlr_relative_pointer_v1_input_router_layer *layer = + wl_container_of(listener, layer, manager_destroy); + wlr_relative_pointer_v1_input_router_layer_destroy(layer); +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct wlr_relative_pointer_v1_input_router_layer *layer = + wl_container_of(listener, layer, seat_destroy); + wlr_relative_pointer_v1_input_router_layer_destroy(layer); +} + +bool wlr_relative_pointer_v1_input_router_layer_register(int32_t priority) { + if (!wlr_input_router_pointer_register_interface(&pointer_impl, priority)) { + return false; + } + return true; +} + +struct wlr_relative_pointer_v1_input_router_layer * +wlr_relative_pointer_v1_input_router_layer_create( + struct wlr_input_router *router, struct wlr_relative_pointer_manager_v1 *manager, + struct wlr_seat *seat) { + struct wlr_relative_pointer_v1_input_router_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_pointer_init(&layer->pointer, router, &pointer_impl); + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + layer->manager = manager; + layer->manager_destroy.notify = handle_manager_destroy; + wl_signal_add(&manager->events.destroy, &layer->manager_destroy); + + layer->seat = seat; + layer->seat_destroy.notify = handle_seat_destroy; + wl_signal_add(&seat->events.destroy, &layer->seat_destroy); + + wl_signal_init(&layer->events.destroy); + + return layer; +} + +void wlr_relative_pointer_v1_input_router_layer_destroy( + struct wlr_relative_pointer_v1_input_router_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + + wlr_input_router_pointer_finish(&layer->pointer); + + wl_list_remove(&layer->router_destroy.link); + wl_list_remove(&layer->manager_destroy.link); + wl_list_remove(&layer->seat_destroy.link); + + free(layer); +} diff --git a/types/input_router/router.c b/types/input_router/router.c new file mode 100644 index 000000000..8123dcd36 --- /dev/null +++ b/types/input_router/router.c @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include + +struct wlr_input_router_handler_priority_entry { + const struct wlr_input_router_handler_interface *iface; + int32_t priority; +}; + +static const struct wlr_input_router_keyboard_interface keyboard_impl = { + .base = { + .name = "wlr_input_router-keyboard", + }, +}; + +static const struct wlr_input_router_pointer_interface pointer_impl = { + .base = { + .name = "wlr_input_router-pointer", + }, +}; + +static const struct wlr_input_router_touch_interface touch_impl = { + .base = { + .name = "wlr_input_router-touch", + }, +}; + +static void focus_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_input_router_focus *focus = wl_container_of(listener, focus, destroy); + wlr_input_router_focus_clear(focus); +} + +static void focus_set_generic(struct wlr_input_router_focus *focus, + enum wlr_input_router_focus_type type, struct wl_signal *destroy_signal) { + focus->type = type; + wl_list_remove(&focus->destroy.link); + if (destroy_signal != NULL) { + wl_signal_add(destroy_signal, &focus->destroy); + } else { + wl_list_init(&focus->destroy.link); + } +} + +bool wlr_input_router_register_handler_interface( + const struct wlr_input_router_handler_interface *iface, + int32_t priority, struct wlr_input_router_handler_priority_list *priority_list) { + struct wlr_input_router_handler_priority_entry *entry; + wl_array_for_each(entry, &priority_list->entries) { + if (entry->iface == iface) { + if (entry->priority == priority) { + // Already registered with the same priority + return true; + } + + wlr_log(WLR_ERROR, + "Tried to register an already registered input handler interface %s", + entry->iface->name); + abort(); + } else if (entry->priority == priority) { + wlr_log(WLR_ERROR, + "Tried to register a input handler interface %s with the same priority %"PRIi32 + " as %s", + iface->name, priority, entry->iface->name); + abort(); + } + } + + entry = wl_array_add(&priority_list->entries, sizeof(*entry)); + if (entry == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return false; + } + *entry = (struct wlr_input_router_handler_priority_entry){ + .iface = iface, + .priority = priority, + }; + return true; +} + +void wlr_input_router_handler_init(struct wlr_input_router_handler *handler, + struct wlr_input_router_handler *head, + const struct wlr_input_router_handler_interface *impl, + const struct wlr_input_router_handler_priority_list *priority_list) { + *handler = (struct wlr_input_router_handler){ + .head = head, + }; + + if (handler == head) { + // Special case: initializing the top of the chain. Do nothing else. + return; + } + + bool found = false; + struct wlr_input_router_handler_priority_entry *priority_entry; + wl_array_for_each(priority_entry, &priority_list->entries) { + if (priority_entry->iface == impl) { + handler->priority = priority_entry->priority; + found = true; + break; + } + } + if (!found) { + wlr_log(WLR_ERROR, + "Tried to init an input handler with unregistered interface %s", impl->name); + abort(); + } + + struct wlr_input_router_handler **target_ptr = &head->next; + struct wlr_input_router_handler *target; + while (true) { + target = *target_ptr; + if (target == NULL) { + break; + } + if (target->priority == handler->priority) { + wlr_log(WLR_ERROR, + "Tried to init an input handler with interface %s twice", impl->name); + abort(); + } else if (handler->priority > target->priority) { + break; + } + target_ptr = &target->next; + } + + handler->next = target; + *target_ptr = handler; +} + +void wlr_input_router_handler_finish(struct wlr_input_router_handler *handler) { + if (handler->head == handler) { + // Special case: finalizing the top of the chain. Do nothing else. + return; + } + + struct wlr_input_router_handler **target_ptr = &handler->head->next; + while (true) { + struct wlr_input_router_handler *target = *target_ptr; + if (target == handler) { + *target_ptr = target->next; + break; + } + target_ptr = &target->next; + } +} + +void wlr_input_router_at(struct wlr_input_router *router, double x, double y, + struct wlr_input_router_focus *focus, double *local_x, double *local_y) { + struct wlr_input_router_focus focus_placeholder; + wlr_input_router_focus_init(&focus_placeholder); + if (focus == NULL) { + focus = &focus_placeholder; + } + + double local_x_placeholder, local_y_placeholder; + if (local_x == NULL) { + local_x = &local_x_placeholder; + } + if (local_y == NULL) { + local_y = &local_y_placeholder; + } + + wlr_input_router_focus_clear(focus); + *local_x = NAN; + *local_y = NAN; + + if (router->impl->at != NULL) { + router->impl->at(router, x, y, focus, local_x, local_y); + } + + wlr_input_router_focus_finish(&focus_placeholder); +} + +bool wlr_input_router_get_surface_position(struct wlr_input_router *router, + struct wlr_surface *surface, double *x, double *y) { + double x_placeholder, y_placeholder; + if (x == NULL) { + x = &x_placeholder; + } + if (y == NULL) { + y = &y_placeholder; + } + + *x = NAN; + *y = NAN; + + if (router->impl->get_surface_position != NULL) { + return router->impl->get_surface_position(router, surface, x, y); + } + return false; +} + +void wlr_input_router_init(struct wlr_input_router *router, + const struct wlr_input_router_interface *impl) { + *router = (struct wlr_input_router){ + .impl = impl, + }; + + wlr_input_router_keyboard_init(&router->keyboard, router, &keyboard_impl); + wlr_input_router_pointer_init(&router->pointer, router, &pointer_impl); + wlr_input_router_touch_init(&router->touch, router, &touch_impl); + + wlr_addon_set_init(&router->addons); + + wl_signal_init(&router->events.destroy); +} + +void wlr_input_router_finish(struct wlr_input_router *router) { + wl_signal_emit_mutable(&router->events.destroy, NULL); + + wlr_addon_set_finish(&router->addons); + + assert(wl_list_empty(&router->events.destroy.listener_list)); + + assert(router->keyboard.base.next == NULL); + assert(router->pointer.base.next == NULL); + assert(router->touch.base.next == NULL); + + wlr_input_router_keyboard_finish(&router->keyboard); + wlr_input_router_pointer_finish(&router->pointer); + wlr_input_router_touch_finish(&router->touch); +} + +void wlr_input_router_focus_init(struct wlr_input_router_focus *focus) { + *focus = (struct wlr_input_router_focus) { + .type = WLR_INPUT_ROUTER_FOCUS_NONE, + .destroy.notify = focus_handle_destroy, + }; + wl_list_init(&focus->destroy.link); +} + +void wlr_input_router_focus_finish(struct wlr_input_router_focus *focus) { + wl_list_remove(&focus->destroy.link); +} + +bool wlr_input_router_focus_is_none(const struct wlr_input_router_focus *focus) { + return focus == NULL || focus->type == WLR_INPUT_ROUTER_FOCUS_NONE; +} + +struct wlr_surface *wlr_input_router_focus_get_surface( + const struct wlr_input_router_focus *focus) { + return focus != NULL && focus->type == WLR_INPUT_ROUTER_FOCUS_SURFACE ? focus->surface : NULL; +} + +void *wlr_input_router_focus_get_user(const struct wlr_input_router_focus *focus) { + return focus != NULL && focus->type == WLR_INPUT_ROUTER_FOCUS_USER ? focus->user : NULL; +} + +void wlr_input_router_focus_clear(struct wlr_input_router_focus *focus) { + focus_set_generic(focus, WLR_INPUT_ROUTER_FOCUS_NONE, NULL); +} + +void wlr_input_router_focus_set_surface(struct wlr_input_router_focus *focus, + struct wlr_surface *surface) { + if (surface != NULL) { + focus_set_generic(focus, WLR_INPUT_ROUTER_FOCUS_SURFACE, &surface->events.destroy); + focus->surface = surface; + } else { + wlr_input_router_focus_clear(focus); + } +} + +void wlr_input_router_focus_set_user(struct wlr_input_router_focus *focus, + void *user, struct wl_signal *destroy_signal) { + if (user != NULL) { + focus_set_generic(focus, WLR_INPUT_ROUTER_FOCUS_USER, destroy_signal); + focus->user = user; + focus->destroy_signal = destroy_signal; + } else { + wlr_input_router_focus_clear(focus); + } +} + +void wlr_input_router_focus_copy(struct wlr_input_router_focus *dst, + const struct wlr_input_router_focus *src) { + if (src == NULL) { + wlr_input_router_focus_clear(dst); + return; + } + + switch (src->type) { + case WLR_INPUT_ROUTER_FOCUS_NONE: + wlr_input_router_focus_clear(dst); + break; + case WLR_INPUT_ROUTER_FOCUS_SURFACE: + wlr_input_router_focus_set_surface(dst, src->surface); + break; + case WLR_INPUT_ROUTER_FOCUS_USER: + wlr_input_router_focus_set_user(dst, src->user, src->destroy_signal); + break; + } +} diff --git a/types/input_router/seat.c b/types/input_router/seat.c new file mode 100644 index 000000000..3443febd5 --- /dev/null +++ b/types/input_router/seat.c @@ -0,0 +1,387 @@ +#include +#include +#include +#include +#include +#include + +// TODO: wlr_seat_touch_send_*() functions are currently useless +// https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3478 + +// For touch points, seat_client is NULL if the client is not aware of the point + +static void clear_touch_point_seat_client(struct wlr_seat_input_router_layer_touch_point *point) { + point->seat_client = NULL; + wl_list_remove(&point->seat_client_destroy.link); + wl_list_init(&point->seat_client_destroy.link); +} + +static void touch_point_handle_seat_client_destroy(struct wl_listener *listener, void *data) { + struct wlr_seat_input_router_layer_touch_point *point = + wl_container_of(listener, point, seat_client_destroy); + clear_touch_point_seat_client(point); +} + +static void init_touch_point(struct wlr_seat_input_router_layer_touch_point *point) { + *point = (struct wlr_seat_input_router_layer_touch_point){0}; + point->seat_client_destroy.notify = touch_point_handle_seat_client_destroy; + wl_list_init(&point->seat_client_destroy.link); +} + +static void finish_touch_point(struct wlr_seat_input_router_layer_touch_point *point) { + wl_list_remove(&point->seat_client_destroy.link); +} + +static uint32_t keyboard_send_enter(struct wlr_seat_input_router_layer *layer, + struct wlr_surface *surface) { + uint32_t *keycodes = NULL; + size_t num_keycodes = 0; + const struct wlr_keyboard_modifiers *modifiers = NULL; + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(layer->seat); + if (keyboard != NULL) { + keycodes = keyboard->keycodes; + num_keycodes = keyboard->num_keycodes; + modifiers = &keyboard->modifiers; + } + + wlr_seat_keyboard_enter(layer->seat, surface, keycodes, num_keycodes, modifiers); + // TODO: get a serial from enter + return 0; +} + +static uint32_t keyboard_focus(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_focus_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(keyboard, layer, keyboard); + return keyboard_send_enter(layer, wlr_input_router_focus_get_surface(event->focus)); +} + +static void keyboard_device(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_device_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(keyboard, layer, keyboard); + struct wlr_keyboard *device = event->device; + + if (wlr_seat_get_keyboard(layer->seat) == device) { + return; + } + + // Avoid sending new modifiers with old keys + struct wlr_surface *surface = layer->seat->keyboard_state.focused_surface; + wlr_seat_keyboard_clear_focus(layer->seat); + + wlr_seat_set_keyboard(layer->seat, device); + + keyboard_send_enter(layer, surface); +} + +static uint32_t keyboard_key(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_key_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(keyboard, layer, keyboard); + if (event->intercepted) { + // Update the client state without sending a wl_keyboard.key event + // XXX: this is suboptimal, wl_keyboard.keys would be better + // See https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/406 + struct wlr_surface *surface = layer->seat->keyboard_state.focused_surface; + wlr_seat_keyboard_clear_focus(layer->seat); + keyboard_send_enter(layer, surface); + return 0; + } else { + wlr_seat_keyboard_send_key(layer->seat, event->time_msec, event->key, event->state); + // TODO: get a serial from key + return 0; + } +} + +static void keyboard_modifiers(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_modifiers_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(keyboard, layer, keyboard); + wlr_seat_keyboard_send_modifiers(layer->seat, &keyboard->device->modifiers); +} + +static const struct wlr_input_router_keyboard_interface keyboard_impl = { + .base = { + .name = "wlr_seat_input_router_layer-keyboard", + }, + .focus = keyboard_focus, + .device = keyboard_device, + .key = keyboard_key, + .modifiers = keyboard_modifiers, +}; + +static uint32_t pointer_position(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(pointer, layer, pointer); + + struct wlr_surface *surface = wlr_input_router_focus_get_surface(event->focus); + double sx = 0, sy = 0; + + if (surface != NULL) { + double surface_x, surface_y; + if (!wlr_input_router_get_surface_position(layer->router, surface, + &surface_x, &surface_y)) { + return 0; + } + sx = event->x - surface_x; + sy = event->y - surface_y; + } + + // One of these will short-circuit + wlr_seat_pointer_enter(layer->seat, surface, sx, sy); + wlr_seat_pointer_send_motion(layer->seat, event->time_msec, sx, sy); + + // TODO: get a serial from enter + return 0; +} + +static uint32_t pointer_button(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_button_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(pointer, layer, pointer); + return wlr_seat_pointer_send_button(layer->seat, + event->time_msec, event->button, event->state); +} + +static void pointer_axis(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_axis_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(pointer, layer, pointer); + wlr_seat_pointer_send_axis(layer->seat, event->time_msec, event->orientation, + event->delta, event->delta_discrete, event->source, event->relative_direction); +} + +static void pointer_frame(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_frame_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(pointer, layer, pointer); + wlr_seat_pointer_send_frame(layer->seat); +} + +static const struct wlr_input_router_pointer_interface pointer_impl = { + .base = { + .name = "wlr_seat_input_router_layer-pointer", + }, + .position = pointer_position, + .button = pointer_button, + .axis = pointer_axis, + .frame = pointer_frame, +}; + +static void touch_position(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_position_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(touch, layer, touch); + + struct wlr_seat_input_router_layer_touch_point *point = &layer->touch_points[event->index]; + struct wlr_surface *surface = wlr_input_router_focus_get_surface(event->focus); + if (surface == NULL) { + return; + } + + double surface_x, surface_y; + if (!wlr_input_router_get_surface_position(layer->router, surface, &surface_x, &surface_y)) { + return; + } + double sx = event->x - surface_x, sy = event->y - surface_y; + + wl_fixed_t sx_fixed = wl_fixed_from_double(sx), sy_fixed = wl_fixed_from_double(sy); + if (point->sx == sx_fixed && point->sy == sy_fixed) { + // Avoid sending same values + return; + } + point->sx = sx_fixed; + point->sy = sy_fixed; + + struct wl_resource *resource; + wl_resource_for_each(resource, &point->seat_client->touches) { + wl_touch_send_motion(resource, event->time_msec, event->id, sx_fixed, sy_fixed); + } + point->seat_client->needs_touch_frame = true; +} + +static uint32_t touch_down(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_down_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(touch, layer, touch); + + struct wlr_seat_input_router_layer_touch_point *point = &layer->touch_points[event->index]; + init_touch_point(point); + + struct wlr_surface *surface = wlr_input_router_focus_get_surface(event->focus); + if (surface == NULL) { + return 0; + } + + double surface_x, surface_y; + if (!wlr_input_router_get_surface_position(layer->router, surface, &surface_x, &surface_y)) { + return 0; + } + double sx = event->x - surface_x, sy = event->y - surface_y; + + struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(layer->seat, + wl_resource_get_client(surface->resource)); + if (seat_client == NULL) { + return 0; + } + + point->seat_client = seat_client; + wl_signal_add(&seat_client->events.destroy, &point->seat_client_destroy); + + wl_fixed_t sx_fixed = wl_fixed_from_double(sx), sy_fixed = wl_fixed_from_double(sy); + point->sx = sx_fixed; + point->sy = sy_fixed; + + uint32_t serial = wlr_seat_client_next_serial(seat_client); + + struct wl_resource *resource; + wl_resource_for_each(resource, &seat_client->touches) { + wl_touch_send_down(resource, serial, event->time_msec, surface->resource, + event->id, sx_fixed, sy_fixed); + } + seat_client->needs_touch_frame = true; + + return serial; +} + +static uint32_t touch_up(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_up_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(touch, layer, touch); + + struct wlr_seat_input_router_layer_touch_point *point = &layer->touch_points[event->index]; + struct wlr_seat_client *seat_client = point->seat_client; + finish_touch_point(point); + + if (seat_client == NULL) { + return 0; + } + + uint32_t serial = wlr_seat_client_next_serial(seat_client); + + struct wl_resource *resource; + wl_resource_for_each(resource, &seat_client->touches) { + wl_touch_send_up(resource, serial, event->time_msec, event->id); + } + seat_client->needs_touch_frame = true; + + return serial; +} + +static void touch_cancel(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_cancel_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(touch, layer, touch); + + struct wlr_seat_input_router_layer_touch_point *point = &layer->touch_points[event->index]; + struct wlr_seat_client *seat_client = point->seat_client; + if (seat_client == NULL) { + return; + } + + // Cancels are client-wide + for (size_t i = 0; i < touch->n_points; i++) { + point = &layer->touch_points[i]; + if (point->seat_client == seat_client) { + clear_touch_point_seat_client(point); + } + } + + struct wl_resource *resource; + wl_resource_for_each(resource, &seat_client->touches) { + wl_touch_send_cancel(resource); + } +} + +static void touch_frame(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_frame_event *event) { + struct wlr_seat_input_router_layer *layer = wl_container_of(touch, layer, touch); + + for (size_t i = 0; i < touch->n_points; i++) { + struct wlr_seat_input_router_layer_touch_point *point = &layer->touch_points[i]; + struct wlr_seat_client *seat_client = point->seat_client; + if (seat_client != NULL && seat_client->needs_touch_frame) { + struct wl_resource *resource; + wl_resource_for_each(resource, &seat_client->touches) { + wl_touch_send_frame(resource); + } + seat_client->needs_touch_frame = false; + } + } +} + +static const struct wlr_input_router_touch_interface touch_impl = { + .base = { + .name = "wlr_seat_input_router_layer-touch", + }, + .position = touch_position, + .down = touch_down, + .up = touch_up, + .cancel = touch_cancel, + .frame = touch_frame, +}; + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_seat_input_router_layer *layer = wl_container_of(listener, layer, router_destroy); + wlr_seat_input_router_layer_destroy(layer); +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct wlr_seat_input_router_layer *layer = wl_container_of(listener, layer, seat_destroy); + wlr_seat_input_router_layer_destroy(layer); +} + +bool wlr_seat_input_router_layer_register(int32_t priority) { + if (!wlr_input_router_keyboard_register_interface(&keyboard_impl, priority)) { + return false; + } + if (!wlr_input_router_pointer_register_interface(&pointer_impl, priority)) { + return false; + } + if (!wlr_input_router_touch_register_interface(&touch_impl, priority)) { + return false; + } + return true; +} + +struct wlr_seat_input_router_layer *wlr_seat_input_router_layer_create( + struct wlr_input_router *router, struct wlr_seat *seat) { + struct wlr_seat_input_router_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_keyboard_init(&layer->keyboard, router, &keyboard_impl); + wlr_input_router_pointer_init(&layer->pointer, router, &pointer_impl); + + wlr_input_router_touch_init(&layer->touch, router, &touch_impl); + for (size_t i = 0; i < layer->touch.n_points; i++) { + init_touch_point(&layer->touch_points[i]); + } + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + layer->seat = seat; + layer->seat_destroy.notify = handle_seat_destroy; + wl_signal_add(&seat->events.destroy, &layer->seat_destroy); + + wl_signal_init(&layer->events.destroy); + + return layer; +} + +void wlr_seat_input_router_layer_destroy(struct wlr_seat_input_router_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + + wlr_input_router_keyboard_finish(&layer->keyboard); + wlr_input_router_pointer_finish(&layer->pointer); + + for (size_t i = 0; i < layer->touch.n_points; i++) { + finish_touch_point(&layer->touch_points[i]); + } + wlr_input_router_touch_finish(&layer->touch); + + wl_list_remove(&layer->router_destroy.link); + wl_list_remove(&layer->seat_destroy.link); + free(layer); +} diff --git a/types/input_router/session.c b/types/input_router/session.c new file mode 100644 index 000000000..c9fcdd144 --- /dev/null +++ b/types/input_router/session.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include + +static uint32_t keyboard_key(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_key_event *event) { + struct wlr_session_input_router_layer *layer = wl_container_of(keyboard, layer, keyboard); + + struct xkb_state *xkb_state = layer->keyboard.device->xkb_state; + + struct wlr_input_router_keyboard_key_event copy; + if (xkb_state != NULL) { + bool intercepted = false; + + const xkb_keysym_t *syms; + int n_syms = xkb_state_key_get_syms(xkb_state, event->key + 8, &syms); + for (int i = 0; i < n_syms; i++) { + xkb_keysym_t sym = syms[i]; + if (sym >= XKB_KEY_XF86Switch_VT_1 && sym <= XKB_KEY_XF86Switch_VT_12) { + copy.intercepted = true; + + unsigned int vt = sym + 1 - XKB_KEY_XF86Switch_VT_1; + wlr_session_change_vt(layer->session, vt); + } + } + + if (intercepted) { + copy = *event; + copy.intercepted = true; + event = © + } + } + + return wlr_input_router_keyboard_notify_key(keyboard, event); +} + +static const struct wlr_input_router_keyboard_interface keyboard_impl = { + .base = { + .name = "wlr_session_input_router_layer-keyboard", + }, + .key = keyboard_key, +}; + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_session_input_router_layer *layer = + wl_container_of(listener, layer, router_destroy); + wlr_session_input_router_layer_destroy(layer); +} + +static void handle_session_destroy(struct wl_listener *listener, void *data) { + struct wlr_session_input_router_layer *layer = + wl_container_of(listener, layer, session_destroy); + wlr_session_input_router_layer_destroy(layer); +} + +bool wlr_session_input_router_layer_register(int32_t priority) { + if (!wlr_input_router_keyboard_register_interface(&keyboard_impl, priority)) { + return false; + } + return true; +} + +struct wlr_session_input_router_layer *wlr_session_input_router_layer_create( + struct wlr_input_router *router, struct wlr_session *session) { + struct wlr_session_input_router_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_keyboard_init(&layer->keyboard, + router, &keyboard_impl); + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + layer->session = session; + layer->session_destroy.notify = handle_session_destroy; + wl_signal_add(&session->events.destroy, &layer->session_destroy); + + wl_signal_init(&layer->events.destroy); + + return layer; +} + +void wlr_session_input_router_layer_destroy(struct wlr_session_input_router_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + + wlr_input_router_keyboard_finish(&layer->keyboard); + + wl_list_remove(&layer->router_destroy.link); + wl_list_remove(&layer->session_destroy.link); + + free(layer); +} diff --git a/types/input_router/text_input.c b/types/input_router/text_input.c new file mode 100644 index 000000000..4e88fc24b --- /dev/null +++ b/types/input_router/text_input.c @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include "wlr/util/log.h" + +struct text_input { + struct wlr_text_input_v3_input_router_layer *layer; + struct wlr_text_input_v3 *wlr_text_input; + + struct wl_list link; + + struct wl_listener destroy; + struct wl_listener enable; + struct wl_listener disable; +}; + +// TODO: improve wlr_text_input_v3 focus API +static void text_input_safe_enter(struct text_input *text_input, struct wlr_surface *surface) { + struct wlr_text_input_v3 *wlr_text_input = text_input->wlr_text_input; + + if (surface != NULL && wl_resource_get_client(wlr_text_input->resource) != + wl_resource_get_client(surface->resource)) { + surface = NULL; + } + + if (wlr_text_input->focused_surface == surface) { + return; + } + + if (wlr_text_input->focused_surface != NULL) { + wlr_text_input_v3_send_leave(wlr_text_input); + } + if (surface != NULL) { + wlr_text_input_v3_send_enter(wlr_text_input, surface); + } +} + +static void update_active_text_input(struct wlr_text_input_v3_input_router_layer *layer) { + struct wlr_text_input_v3 *active_text_input = NULL; + struct wlr_surface *surface = wlr_input_router_focus_get_surface(&layer->keyboard.focus); + if (surface != NULL) { + struct text_input *text_input; + wl_list_for_each(text_input, &layer->text_inputs, link) { + struct wlr_text_input_v3 *wlr_text_input = text_input->wlr_text_input; + if (wlr_text_input->current_enabled && wlr_text_input->focused_surface == surface) { + active_text_input = wlr_text_input; + break; + } + } + } + + if (layer->active_text_input == active_text_input) { + return; + } + layer->active_text_input = active_text_input; + + struct wlr_text_input_v3_input_router_layer_set_active_event event = { + .active_text_input = active_text_input, + }; + wl_signal_emit_mutable(&layer->events.set_active_text_input, &event); +} + +static void destroy_text_input(struct text_input *text_input) { + wl_list_remove(&text_input->link); + + struct wlr_text_input_v3_input_router_layer *layer = text_input->layer; + if (layer->active_text_input == text_input->wlr_text_input) { + update_active_text_input(layer); + } + + wl_list_remove(&text_input->destroy.link); + wl_list_remove(&text_input->enable.link); + wl_list_remove(&text_input->disable.link); + free(text_input); +} + +static void text_input_handle_destroy(struct wl_listener *listener, void *data) { + struct text_input *text_input = wl_container_of(listener, text_input, destroy); + destroy_text_input(text_input); +} + +static void text_input_handle_enable(struct wl_listener *listener, void *data) { + struct text_input *text_input = wl_container_of(listener, text_input, enable); + struct wlr_text_input_v3_input_router_layer *layer = text_input->layer; + if (layer->active_text_input == NULL) { + update_active_text_input(layer); + } +} + +static void text_input_handle_disable(struct wl_listener *listener, void *data) { + struct text_input *text_input = wl_container_of(listener, text_input, disable); + struct wlr_text_input_v3_input_router_layer *layer = text_input->layer; + if (layer->active_text_input == text_input->wlr_text_input) { + update_active_text_input(layer); + } +} + +static void create_text_input(struct wlr_text_input_v3_input_router_layer *layer, + struct wlr_text_input_v3 *wlr_text_input) { + if (wlr_text_input->seat != layer->seat) { + return; + } + + struct text_input *text_input = calloc(1, sizeof(*text_input)); + if (text_input == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return; + } + + text_input->wlr_text_input = wlr_text_input; + text_input->layer = layer; + + wl_list_insert(&layer->text_inputs, &text_input->link); + + text_input->destroy.notify = text_input_handle_destroy; + wl_signal_add(&wlr_text_input->events.destroy, &text_input->destroy); + text_input->enable.notify = text_input_handle_enable; + wl_signal_add(&wlr_text_input->events.enable, &text_input->enable); + text_input->disable.notify = text_input_handle_disable; + wl_signal_add(&wlr_text_input->events.disable, &text_input->disable); + + struct wlr_surface *surface = wlr_input_router_focus_get_surface(&layer->keyboard.focus); + text_input_safe_enter(text_input, surface); + update_active_text_input(layer); +} + +static uint32_t keyboard_focus(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_focus_event *event) { + struct wlr_text_input_v3_input_router_layer *layer = + wl_container_of(keyboard, layer, keyboard); + + // Relay the event first; "the text-input focus follows the keyboard focus". + uint32_t serial = wlr_input_router_keyboard_notify_focus(keyboard, event); + + struct wlr_surface *surface = wlr_input_router_focus_get_surface(event->focus); + struct text_input *text_input; + wl_list_for_each(text_input, &layer->text_inputs, link) { + text_input_safe_enter(text_input, surface); + } + update_active_text_input(layer); + + return serial; +} + +static const struct wlr_input_router_keyboard_interface keyboard_impl = { + .base = { + .name = "wlr_text_input_v3_input_router_layer-keyboard", + }, + .focus = keyboard_focus, +}; + +static void handle_manager_destroy(struct wl_listener *listener, void *data) { + struct wlr_text_input_v3_input_router_layer *layer = + wl_container_of(listener, layer, manager_destroy); + wlr_text_input_v3_input_router_layer_destroy(layer); +} + +static void handle_manager_text_input(struct wl_listener *listener, void *data) { + struct wlr_text_input_v3_input_router_layer *layer = + wl_container_of(listener, layer, manager_text_input); + struct wlr_text_input_v3 *wlr_text_input = data; + create_text_input(layer, wlr_text_input); +} + +static void handle_router_destroy(struct wl_listener *listener, void *data) { + struct wlr_text_input_v3_input_router_layer *layer = + wl_container_of(listener, layer, router_destroy); + wlr_text_input_v3_input_router_layer_destroy(layer); +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct wlr_text_input_v3_input_router_layer *layer = + wl_container_of(listener, layer, seat_destroy); + wlr_text_input_v3_input_router_layer_destroy(layer); +} + +bool wlr_text_input_v3_input_router_layer_register(int32_t priority) { + if (!wlr_input_router_keyboard_register_interface(&keyboard_impl, priority)) { + return false; + } + return true; +} + +struct wlr_text_input_v3_input_router_layer *wlr_text_input_v3_input_router_layer_create( + struct wlr_input_router *router, struct wlr_text_input_manager_v3 *manager, + struct wlr_seat *seat) { + struct wlr_text_input_v3_input_router_layer *layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_keyboard_init(&layer->keyboard, + router, &keyboard_impl); + + layer->manager = manager; + layer->manager_destroy.notify = handle_manager_destroy; + wl_signal_add(&manager->events.destroy, &layer->manager_destroy); + layer->manager_text_input.notify = handle_manager_text_input; + wl_signal_add(&manager->events.text_input, &layer->manager_text_input); + + layer->router = router; + layer->router_destroy.notify = handle_router_destroy; + wl_signal_add(&router->events.destroy, &layer->router_destroy); + + layer->seat = seat; + layer->seat_destroy.notify = handle_seat_destroy; + wl_signal_add(&seat->events.destroy, &layer->seat_destroy); + + wl_list_init(&layer->text_inputs); + + struct wlr_text_input_v3 *wlr_text_input; + wl_list_for_each(wlr_text_input, &manager->text_inputs, link) { + create_text_input(layer, wlr_text_input); + } + + wl_signal_init(&layer->events.destroy); + wl_signal_init(&layer->events.set_active_text_input); + + return layer; +} + +void wlr_text_input_v3_input_router_layer_destroy( + struct wlr_text_input_v3_input_router_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + assert(wl_list_empty(&layer->events.set_active_text_input.listener_list)); + + layer->active_text_input = NULL; + + struct text_input *text_input, *tmp; + wl_list_for_each_safe(text_input, tmp, &layer->text_inputs, link) { + destroy_text_input(text_input); + } + + wlr_input_router_keyboard_finish(&layer->keyboard); + + wl_list_remove(&layer->manager_destroy.link); + wl_list_remove(&layer->manager_text_input.link); + + wl_list_remove(&layer->router_destroy.link); + wl_list_remove(&layer->seat_destroy.link); + + free(layer); +} diff --git a/types/input_router/touch.c b/types/input_router/touch.c new file mode 100644 index 000000000..0b5d1e48c --- /dev/null +++ b/types/input_router/touch.c @@ -0,0 +1,164 @@ +#include +#include + +static struct wlr_input_router_handler_priority_list touch_priority_list = {0}; + +static struct wlr_input_router_touch_point *find_point( + struct wlr_input_router_touch *touch, int32_t id) { + for (size_t i = 0; i < touch->n_points; i++) { + if (touch->points[i].id == id) { + return &touch->points[i]; + } + } + return NULL; +} + +static void remove_point(struct wlr_input_router_touch *touch, + struct wlr_input_router_touch_point *point) { + struct wlr_input_router_touch_point *last = &touch->points[--touch->n_points]; + point->x = last->x; + point->y = last->y; + wlr_input_router_focus_copy(&point->focus, &last->focus); + wlr_input_router_focus_finish(&last->focus); +} + +void wlr_input_router_touch_notify_position(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_position_event *event) { + while ((touch = wl_container_of(touch->base.next, touch, base)) != NULL) { + struct wlr_input_router_touch_point *point = find_point(touch, event->id); + if (point == NULL) { + // Skip silently to avoid noise + return; + } + + point->x = event->x; + point->y = event->y; + wlr_input_router_focus_copy(&point->focus, event->focus); + + if (touch->impl->position != NULL) { + struct wlr_input_router_touch_position_event relayed = *event; + relayed.index = point - touch->points; + touch->impl->position(touch, &relayed); + return; + } + } +} + +uint32_t wlr_input_router_touch_notify_down(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_down_event *event) { + while ((touch = wl_container_of(touch->base.next, touch, base)) != NULL) { + struct wlr_input_router_touch_point *point = find_point(touch, event->id); + if (find_point(touch, event->id) != NULL) { + wlr_log(WLR_ERROR, "%s received down for an existing touch point %"PRIi32, + touch->impl->base.name, event->id); + return 0; + } else if (touch->n_points == WLR_INPUT_ROUTER_MAX_TOUCH_POINTS) { + wlr_log(WLR_ERROR, "%s has too many touch points, ignoring %"PRIi32, + touch->impl->base.name, event->id); + return 0; + } + + point = &touch->points[touch->n_points++]; + wlr_input_router_focus_init(&point->focus); + + point->id = event->id; + point->x = event->x; + point->y = event->y; + wlr_input_router_focus_copy(&point->focus, event->focus); + + if (touch->impl->down != NULL) { + struct wlr_input_router_touch_down_event relayed = *event; + relayed.index = point - touch->points; + return touch->impl->down(touch, &relayed); + } + } + return 0; +} + +uint32_t wlr_input_router_touch_notify_up(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_up_event *event) { + while ((touch = wl_container_of(touch->base.next, touch, base)) != NULL) { + struct wlr_input_router_touch_point *point = find_point(touch, event->id); + if (point == NULL) { + wlr_log(WLR_ERROR, "%s received up for an unknown touch point %"PRIi32, + touch->impl->base.name, event->id); + return 0; + } + remove_point(touch, point); + + if (touch->impl->up != NULL) { + struct wlr_input_router_touch_up_event relayed = *event; + relayed.index = point - touch->points; + return touch->impl->up(touch, &relayed); + } + } + return 0; +} + +void wlr_input_router_touch_notify_cancel(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_cancel_event *event) { + while ((touch = wl_container_of(touch->base.next, touch, base)) != NULL) { + struct wlr_input_router_touch_point *point = find_point(touch, event->id); + if (point == NULL) { + wlr_log(WLR_ERROR, "%s received cancel for an unknown touch point %"PRIi32, + touch->impl->base.name, event->id); + return; + } + remove_point(touch, point); + + if (touch->impl->cancel != NULL) { + struct wlr_input_router_touch_cancel_event relayed = *event; + relayed.index = point - touch->points; + touch->impl->cancel(touch, &relayed); + return; + } + } +} + +void wlr_input_router_touch_notify_frame(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_frame_event *event) { + while ((touch = wl_container_of(touch->base.next, touch, base)) != NULL) { + if (touch->impl->frame != NULL) { + touch->impl->frame(touch, event); + return; + } + } +} + +bool wlr_input_router_touch_register_interface( + const struct wlr_input_router_touch_interface *iface, int32_t priority) { + return wlr_input_router_register_handler_interface(&iface->base, + priority, &touch_priority_list); +} + +void wlr_input_router_touch_init(struct wlr_input_router_touch *touch, + struct wlr_input_router *router, const struct wlr_input_router_touch_interface *impl) { + *touch = (struct wlr_input_router_touch){ + .impl = impl, + }; + wlr_input_router_handler_init(&touch->base, &router->touch.base, + &impl->base, &touch_priority_list); + + struct wlr_input_router_touch *next = wl_container_of(touch->base.next, next, base); + if (next != NULL) { + for (size_t i = 0; i < next->n_points; i++) { + struct wlr_input_router_touch_point *dst = &touch->points[i]; + wlr_input_router_focus_init(&dst->focus); + + const struct wlr_input_router_touch_point *src = &next->points[i]; + dst->id = src->id; + dst->x = src->x; + dst->y = src->y; + wlr_input_router_focus_copy(&dst->focus, &src->focus); + } + touch->n_points = next->n_points; + } +} + +void wlr_input_router_touch_finish(struct wlr_input_router_touch *touch) { + for (size_t i = 0; i < touch->n_points; i++) { + wlr_input_router_focus_finish(&touch->points[i].focus); + } + + wlr_input_router_handler_finish(&touch->base); +} diff --git a/types/input_router/xdg_popup_grab.c b/types/input_router/xdg_popup_grab.c new file mode 100644 index 000000000..bfd501d1a --- /dev/null +++ b/types/input_router/xdg_popup_grab.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include +#include + +// TODO: better xdg_popup grab state management + +static const struct wlr_input_router_focus *filter_focus( + struct wlr_xdg_popup_grab_input_router_layer *layer, + const struct wlr_input_router_focus *focus) { + struct wlr_surface *surface = wlr_input_router_focus_get_surface(focus); + return surface != NULL && wl_resource_get_client(surface->resource) == + wl_resource_get_client(layer->popup->resource) ? focus : NULL; +} + +static void dismiss_grab(struct wlr_xdg_popup_grab_input_router_layer *layer) { + struct wlr_xdg_popup *popup = layer->popup; + wlr_xdg_popup_grab_input_router_layer_destroy(layer); + + // Find the grabbing popup chain start + struct wlr_surface *parent; + while ((parent = popup->parent) != NULL) { + struct wlr_xdg_popup *parent_popup = wlr_xdg_popup_try_from_wlr_surface(parent); + // Note: wlr_xdg_popup.seat is non-NULL if it's a grabbing popup + if (parent_popup == NULL || parent_popup->seat == NULL) { + break; + } + popup = parent_popup; + } + + wlr_xdg_popup_destroy(popup); +} + +static uint32_t keyboard_focus(struct wlr_input_router_keyboard *keyboard, + const struct wlr_input_router_keyboard_focus_event *event) { + struct wlr_xdg_popup_grab_input_router_layer *layer = + wl_container_of(keyboard, layer, keyboard); + + struct wlr_input_router_keyboard_focus_event relayed = *event; + relayed.focus = &layer->keyboard_focus; + return wlr_input_router_keyboard_notify_focus(keyboard, &relayed); +} + +static const struct wlr_input_router_keyboard_interface keyboard_impl = { + .base = { + .name = "wlr_xdg_popup_grab_input_router_layer-keyboard", + }, + .focus = keyboard_focus, +}; + +static uint32_t pointer_position(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_position_event *event) { + struct wlr_xdg_popup_grab_input_router_layer *layer = wl_container_of(pointer, layer, pointer); + + struct wlr_input_router_pointer_position_event relayed = *event; + relayed.focus = event->explicit_focus ? event->focus : filter_focus(layer, event->focus); + relayed.explicit_focus = true; + return wlr_input_router_pointer_notify_position(pointer, &relayed); +} + +static uint32_t pointer_button(struct wlr_input_router_pointer *pointer, + const struct wlr_input_router_pointer_button_event *event) { + struct wlr_xdg_popup_grab_input_router_layer *layer = wl_container_of(pointer, layer, pointer); + uint32_t serial = wlr_input_router_pointer_notify_button(pointer, event); + + if (event->state == WL_POINTER_BUTTON_STATE_PRESSED && + filter_focus(layer, &pointer->focus) == NULL) { + dismiss_grab(layer); + } + + return serial; +} + +static const struct wlr_input_router_pointer_interface pointer_impl = { + .base = { + .name = "wlr_xdg_popup_grab_input_router_layer-pointer", + }, + .position = pointer_position, + .button = pointer_button, +}; + +static void touch_position(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_position_event *event) { + struct wlr_xdg_popup_grab_input_router_layer *layer = wl_container_of(touch, layer, touch); + + struct wlr_input_router_touch_position_event relayed = *event; + relayed.focus = filter_focus(layer, event->focus); + wlr_input_router_touch_notify_position(touch, &relayed); +} + +static uint32_t touch_down(struct wlr_input_router_touch *touch, + const struct wlr_input_router_touch_down_event *event) { + struct wlr_xdg_popup_grab_input_router_layer *layer = wl_container_of(touch, layer, touch); + + struct wlr_input_router_touch_down_event relayed = *event; + relayed.focus = filter_focus(layer, event->focus); + uint32_t serial = wlr_input_router_touch_notify_down(touch, &relayed); + + if (relayed.focus == NULL) { + dismiss_grab(layer); + } + + return serial; +} + +static const struct wlr_input_router_touch_interface touch_impl = { + .base = { + .name = "wlr_xdg_popup_grab_input_router_layer-touch", + }, + .position = touch_position, + .down = touch_down, +}; + +static void set_topmost_popup(struct wlr_xdg_popup_grab_input_router_layer *layer, + struct wlr_xdg_popup *popup) { + layer->popup = popup; + + wl_list_remove(&layer->popup_destroy.link); + wl_signal_add(&popup->events.destroy, &layer->popup_destroy); + + wlr_input_router_focus_set_surface(&layer->keyboard_focus, popup->base->surface); + wlr_input_router_keyboard_notify_focus(&layer->keyboard, + &(struct wlr_input_router_keyboard_focus_event){ + .focus = &layer->keyboard_focus, + }); +} + +static void router_addon_destroy(struct wlr_addon *addon) { + struct wlr_xdg_popup_grab_input_router_layer *layer = + wl_container_of(addon, layer, router_addon); + wlr_xdg_popup_grab_input_router_layer_destroy(layer); +} + +static const struct wlr_addon_interface router_addon_impl = { + .name = "wlr_xdg_popup_grab_input_router_layer", + .destroy = router_addon_destroy, +}; + +static void handle_popup_destroy(struct wl_listener *listener, void *data) { + struct wlr_xdg_popup_grab_input_router_layer *layer = + wl_container_of(listener, layer, popup_destroy); + struct wlr_surface *parent = layer->popup->parent; + if (parent != NULL) { + struct wlr_xdg_popup *parent_popup = wlr_xdg_popup_try_from_wlr_surface(parent); + // Note: wlr_xdg_popup.seat is non-NULL if it's a grabbing popup + if (parent_popup != NULL && parent_popup->seat != NULL) { + set_topmost_popup(layer, parent_popup); + return; + } + } + + wlr_xdg_popup_grab_input_router_layer_destroy(layer); +} + +bool wlr_xdg_popup_grab_input_router_layer_register(int32_t priority) { + if (!wlr_input_router_keyboard_register_interface(&keyboard_impl, priority)) { + return false; + } + if (!wlr_input_router_pointer_register_interface(&pointer_impl, priority)) { + return false; + } + if (!wlr_input_router_touch_register_interface(&touch_impl, priority)) { + return false; + } + return true; +} + +struct wlr_xdg_popup_grab_input_router_layer *wlr_xdg_popup_grab_input_router_layer_get_or_create( + struct wlr_input_router *router, struct wlr_xdg_popup *popup) { + struct wlr_xdg_popup_grab_input_router_layer *layer; + + struct wlr_addon *addon = wlr_addon_find(&router->addons, NULL, &router_addon_impl); + if (addon != NULL) { + layer = wl_container_of(addon, layer, router_addon); + set_topmost_popup(layer, popup); + return layer; + } + + layer = calloc(1, sizeof(*layer)); + if (layer == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_input_router_keyboard_init(&layer->keyboard, + router, &keyboard_impl); + wlr_input_router_focus_init(&layer->keyboard_focus); + + wlr_input_router_pointer_init(&layer->pointer, router, &pointer_impl); + + wlr_input_router_touch_init(&layer->touch, router, &touch_impl); + + layer->popup = popup; + layer->popup_destroy.notify = handle_popup_destroy; + wl_list_init(&layer->popup_destroy.link); + + layer->router = router; + wlr_addon_init(&layer->router_addon, &router->addons, NULL, &router_addon_impl); + + wl_signal_init(&layer->events.destroy); + + set_topmost_popup(layer, popup); + + wlr_input_router_pointer_clear_focus(&layer->pointer); + + return layer; +} + +void wlr_xdg_popup_grab_input_router_layer_destroy( + struct wlr_xdg_popup_grab_input_router_layer *layer) { + if (layer == NULL) { + return; + } + + wl_signal_emit_mutable(&layer->events.destroy, NULL); + + assert(wl_list_empty(&layer->events.destroy.listener_list)); + + wlr_input_router_focus_finish(&layer->keyboard_focus); + + wlr_input_router_keyboard_notify_focus(&layer->keyboard, + &(struct wlr_input_router_keyboard_focus_event){ + .focus = &layer->keyboard.focus, + }); + wlr_input_router_keyboard_finish(&layer->keyboard); + + wlr_input_router_pointer_refresh_position(&layer->pointer); + wlr_input_router_pointer_finish(&layer->pointer); + + wlr_input_router_touch_finish(&layer->touch); + + wlr_addon_finish(&layer->router_addon); + wl_list_remove(&layer->popup_destroy.link); + + free(layer); +} diff --git a/types/meson.build b/types/meson.build index c6b33cec8..db87d1269 100644 --- a/types/meson.build +++ b/types/meson.build @@ -5,6 +5,19 @@ wlr_files += files( 'data_device/wlr_drag.c', 'ext_image_capture_source_v1/base.c', 'ext_image_capture_source_v1/output.c', + 'input_router/drag.c', + 'input_router/focus.c', + 'input_router/implicit_grab.c', + 'input_router/input_method.c', + 'input_router/keyboard.c', + 'input_router/pointer.c', + 'input_router/pointer_constraints.c', + 'input_router/relative_pointer.c', + 'input_router/router.c', + 'input_router/seat.c', + 'input_router/text_input.c', + 'input_router/touch.c', + 'input_router/xdg_popup_grab.c', 'output/cursor.c', 'output/output.c', 'output/render.c', @@ -106,3 +119,9 @@ if features.get('drm-backend') 'wlr_drm_lease_v1.c', ) endif + +if features.get('session') + wlr_files += files( + 'input_router/session.c', + ) +endif diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index 25c07c8c5..c801ad2eb 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -306,6 +306,14 @@ static void xdg_popup_handle_grab(struct wl_client *client, &popup_grab->keyboard_grab); wlr_seat_touch_start_grab(seat_client->seat, &popup_grab->touch_grab); + + struct wlr_xdg_shell_popup_grab_event event = { + .popup = popup, + .seat_client = seat_client, + .serial = serial, + }; + + wl_signal_emit_mutable(&popup->base->client->shell->events.popup_grab, &event); } static void xdg_popup_handle_reposition( diff --git a/types/xdg_shell/wlr_xdg_shell.c b/types/xdg_shell/wlr_xdg_shell.c index 0cbadc271..7d21cb5df 100644 --- a/types/xdg_shell/wlr_xdg_shell.c +++ b/types/xdg_shell/wlr_xdg_shell.c @@ -133,6 +133,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { assert(wl_list_empty(&xdg_shell->events.new_surface.listener_list)); assert(wl_list_empty(&xdg_shell->events.new_toplevel.listener_list)); assert(wl_list_empty(&xdg_shell->events.new_popup.listener_list)); + assert(wl_list_empty(&xdg_shell->events.popup_grab.listener_list)); assert(wl_list_empty(&xdg_shell->events.destroy.listener_list)); wl_list_remove(&xdg_shell->display_destroy.link); @@ -166,6 +167,7 @@ struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display, wl_signal_init(&xdg_shell->events.new_surface); wl_signal_init(&xdg_shell->events.new_toplevel); wl_signal_init(&xdg_shell->events.new_popup); + wl_signal_init(&xdg_shell->events.popup_grab); wl_signal_init(&xdg_shell->events.destroy); xdg_shell->display_destroy.notify = handle_display_destroy;