Merge branch 'input-router' into 'master'

Add input router

See merge request wlroots/wlroots!4950
This commit is contained in:
Kirill Primak 2025-01-27 17:18:07 +00:00
commit 5d3fd61b9c
27 changed files with 4048 additions and 0 deletions

View file

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <sys/types.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_input_router.h>
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

View file

@ -10,6 +10,7 @@
#define WLR_TYPES_WLR_DATA_DEVICE_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_seat.h>
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

View file

@ -14,6 +14,8 @@
#include <wlr/types/wlr_seat.h>
#include <wlr/util/box.h>
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

View file

@ -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 <stdint.h>
#include <wayland-server-protocol.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/util/addon.h>
#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

View file

@ -13,6 +13,7 @@
#include <wayland-server-core.h>
#include <pixman.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_seat.h>
#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

View file

@ -10,6 +10,7 @@
#define WLR_TYPES_WLR_RELATIVE_POINTER_V1_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_input_router.h>
/**
* 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

View file

@ -12,6 +12,7 @@
#include <time.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_pointer.h>
@ -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

View file

@ -10,6 +10,7 @@
#define WLR_TYPES_WLR_TEXT_INPUT_V3_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/box.h>
@ -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

View file

@ -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

View file

@ -10,6 +10,8 @@
#include <wlr/util/log.h>
#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);
}

250
types/input_router/drag.c Normal file
View file

@ -0,0 +1,250 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/util/log.h>
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);
}

114
types/input_router/focus.c Normal file
View file

@ -0,0 +1,114 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/util/log.h>
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 = &copy;
}
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);
}

View file

@ -0,0 +1,206 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/util/log.h>
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 = &copy;
} 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);
}

View file

@ -0,0 +1,409 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_input_method_v2.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_text_input_v3.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/util/log.h>
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);
}

View file

@ -0,0 +1,110 @@
#include <wlr/types/wlr_input_router.h>
#include <wlr/util/log.h>
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);
}

View file

@ -0,0 +1,141 @@
#include <wlr/types/wlr_input_router.h>
#include <wlr/util/log.h>
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);
}

View file

@ -0,0 +1,280 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
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 = &copy;
}
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);
}

View file

@ -0,0 +1,96 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/log.h>
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);
}

293
types/input_router/router.c Normal file
View file

@ -0,0 +1,293 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/util/log.h>
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;
}
}

387
types/input_router/seat.c Normal file
View file

@ -0,0 +1,387 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/log.h>
// 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);
}

View file

@ -0,0 +1,105 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/log.h>
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 = &copy;
}
}
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);
}

View file

@ -0,0 +1,251 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_text_input_v3.h>
#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);
}

164
types/input_router/touch.c Normal file
View file

@ -0,0 +1,164 @@
#include <wlr/types/wlr_input_router.h>
#include <wlr/util/log.h>
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);
}

View file

@ -0,0 +1,238 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_input_router.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/addon.h>
#include <wlr/util/log.h>
// 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);
}

View file

@ -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

View file

@ -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(

View file

@ -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;