mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-18 06:47:31 -04:00
Introduce wlr_input_mapper
This commit is contained in:
parent
ebc65649fa
commit
f0f89439de
3 changed files with 359 additions and 0 deletions
123
include/wlr/types/wlr_input_mapper.h
Normal file
123
include/wlr/types/wlr_input_mapper.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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_MAPPER_H
|
||||
#define WLR_TYPES_WLR_INPUT_MAPPER_H
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/util/addon.h>
|
||||
#include <wlr/util/box.h>
|
||||
|
||||
struct wlr_input_device;
|
||||
|
||||
struct wlr_input_constraint {
|
||||
struct wlr_output *output; // NULL if unset
|
||||
struct wlr_box box; // Empty if unset
|
||||
|
||||
// private state
|
||||
|
||||
struct wl_listener output_destroy;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper for converting absolute coordinates received from input devices to
|
||||
* layout-local coordinates and applying coordinate constraints.
|
||||
*
|
||||
* The constraints precendence is as follows:
|
||||
* 1) Device-specific box
|
||||
* 2) Device-specific output
|
||||
* 3) Global box
|
||||
* 4) Global output
|
||||
*
|
||||
* If no output layout is attached to the input mapper, all output constraints
|
||||
* are ignored.
|
||||
*/
|
||||
struct wlr_input_mapper {
|
||||
struct wlr_output_layout *layout;
|
||||
struct wlr_input_constraint global;
|
||||
|
||||
struct wl_list mappings; // wlr_input_mapping.link
|
||||
|
||||
struct {
|
||||
struct wl_signal destroy;
|
||||
} events;
|
||||
|
||||
// private state
|
||||
|
||||
struct wl_listener layout_destroy;
|
||||
};
|
||||
|
||||
struct wlr_input_mapping {
|
||||
struct wlr_input_constraint constraint;
|
||||
struct wl_list link; // wlr_input_mapper.mappings
|
||||
|
||||
// private state
|
||||
|
||||
struct wlr_addon addon; // wlr_input_device.addons
|
||||
};
|
||||
|
||||
struct wlr_input_mapper *wlr_input_mapper_create(void);
|
||||
|
||||
void wlr_input_mapper_destroy(struct wlr_input_mapper *mapper);
|
||||
|
||||
/**
|
||||
* Attach an output layout to the input mapper. This detaches the previous
|
||||
* output layout, if any.
|
||||
*
|
||||
* layout may be NULL.
|
||||
*/
|
||||
void wlr_input_mapper_attach_output_layout(struct wlr_input_mapper *mapper,
|
||||
struct wlr_output_layout *layout);
|
||||
|
||||
/**
|
||||
* Convert absolute coordinates in 0..1 range to layout-local coordinates.
|
||||
*
|
||||
* If device is not NULL, its constraints are used, if any.
|
||||
*
|
||||
* If no matching constraint is found, the absolute coordinates are mapped to
|
||||
* the entire layout, unless none is attached, in which case lx and ly are set
|
||||
* to 0.
|
||||
*/
|
||||
void wlr_input_mapper_absolute_to_layout(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, double x, double y,
|
||||
double *lx, double *ly);
|
||||
|
||||
/**
|
||||
* Get the closest point satisfying constraints from the given point.
|
||||
*
|
||||
* If device is not NULL, its constraints are used, if any.
|
||||
*
|
||||
* If no matching constraint is found, get the closest point from the layout.
|
||||
*/
|
||||
void wlr_input_mapper_closest_point(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, double lx, double ly,
|
||||
double *closest_lx, double *closest_ly);
|
||||
|
||||
/**
|
||||
* Map device to output.
|
||||
*
|
||||
* If device is NULL, sets the default output constraint.
|
||||
* If output is NULL, the output constraint is reset.
|
||||
*
|
||||
* When the output is destroyed, the output constraint is reset.
|
||||
*/
|
||||
void wlr_input_mapper_map_to_output(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, struct wlr_output *output);
|
||||
|
||||
/**
|
||||
* Map device to box.
|
||||
*
|
||||
* If device is NULL, sets the default box constraint.
|
||||
* If box is empty, the box constraint is reset.
|
||||
*/
|
||||
void wlr_input_mapper_map_to_box(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, const struct wlr_box *box);
|
||||
|
||||
#endif
|
||||
|
|
@ -48,6 +48,7 @@ wlr_files += files(
|
|||
'wlr_idle_notify_v1.c',
|
||||
'wlr_input_device.c',
|
||||
'wlr_input_inhibitor.c',
|
||||
'wlr_input_mapper.c',
|
||||
'wlr_input_method_v2.c',
|
||||
'wlr_keyboard.c',
|
||||
'wlr_keyboard_group.c',
|
||||
|
|
|
|||
235
types/wlr_input_mapper.c
Normal file
235
types/wlr_input_mapper.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_input_mapper.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/util/addon.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
static void constraint_detach_output(struct wlr_input_constraint *constraint) {
|
||||
constraint->output = NULL;
|
||||
wl_list_remove(&constraint->output_destroy.link);
|
||||
wl_list_init(&constraint->output_destroy.link);
|
||||
}
|
||||
|
||||
static void constraint_handle_output_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_input_constraint *constraint =
|
||||
wl_container_of(listener, constraint, output_destroy);
|
||||
constraint_detach_output(constraint);
|
||||
}
|
||||
|
||||
static void constraint_init(struct wlr_input_constraint *constraint) {
|
||||
memset(constraint, 0, sizeof(*constraint));
|
||||
|
||||
constraint->output_destroy.notify = constraint_handle_output_destroy;
|
||||
wl_list_init(&constraint->output_destroy.link);
|
||||
}
|
||||
|
||||
static void constraint_finish(struct wlr_input_constraint *constraint) {
|
||||
wl_list_remove(&constraint->output_destroy.link);
|
||||
}
|
||||
|
||||
static void mapping_destroy(struct wlr_input_mapping *mapping) {
|
||||
constraint_finish(&mapping->constraint);
|
||||
wlr_addon_finish(&mapping->addon);
|
||||
wl_list_remove(&mapping->link);
|
||||
free(mapping);
|
||||
}
|
||||
|
||||
static void device_addon_destroy(struct wlr_addon *addon) {
|
||||
struct wlr_input_mapping *mapping =
|
||||
wl_container_of(addon, mapping, addon);
|
||||
mapping_destroy(mapping);
|
||||
}
|
||||
|
||||
static const struct wlr_addon_interface device_addon_impl = {
|
||||
.name = "wlr_input_mapping",
|
||||
.destroy = device_addon_destroy,
|
||||
};
|
||||
|
||||
static struct wlr_input_mapping *mapping_create(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device) {
|
||||
struct wlr_input_mapping *mapping = calloc(1, sizeof(*mapping));
|
||||
if (mapping == NULL) {
|
||||
wlr_log(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
constraint_init(&mapping->constraint);
|
||||
wlr_addon_init(&mapping->addon, &device->addons, mapper, &device_addon_impl);
|
||||
wl_list_insert(&mapper->mappings, &mapping->link);
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static struct wlr_input_mapping *get_mapping(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device) {
|
||||
struct wlr_input_mapping *mapping = NULL;
|
||||
struct wlr_addon *addon = wlr_addon_find(&device->addons,
|
||||
mapper, &device_addon_impl);
|
||||
if (addon != NULL) {
|
||||
mapping = wl_container_of(addon, mapping, addon);
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
static void detach_output_layout(struct wlr_input_mapper *mapper) {
|
||||
mapper->layout = NULL;
|
||||
wl_list_remove(&mapper->layout_destroy.link);
|
||||
wl_list_init(&mapper->layout_destroy.link);
|
||||
}
|
||||
|
||||
static void handle_layout_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_input_mapper *mapper =
|
||||
wl_container_of(listener, mapper, layout_destroy);
|
||||
detach_output_layout(mapper);
|
||||
}
|
||||
|
||||
static void get_constraint(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, struct wlr_box *box) {
|
||||
memset(box, 0, sizeof(*box));
|
||||
|
||||
if (device != NULL) {
|
||||
struct wlr_input_mapping *mapping = get_mapping(mapper, device);
|
||||
if (mapping != NULL) {
|
||||
if (!wlr_box_empty(&mapping->constraint.box)) {
|
||||
*box = mapping->constraint.box;
|
||||
} else if (mapper->layout != NULL &&
|
||||
mapping->constraint.output != NULL) {
|
||||
wlr_output_layout_get_box(mapper->layout,
|
||||
mapping->constraint.output, box);
|
||||
assert(!wlr_box_empty(box));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wlr_box_empty(box)) {
|
||||
if (!wlr_box_empty(&mapper->global.box)) {
|
||||
*box = mapper->global.box;
|
||||
} else if (mapper->layout != NULL &&
|
||||
mapper->global.output != NULL) {
|
||||
wlr_output_layout_get_box(mapper->layout,
|
||||
mapper->global.output, box);
|
||||
assert(!wlr_box_empty(box));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct wlr_input_mapper *wlr_input_mapper_create(void) {
|
||||
struct wlr_input_mapper *mapper = calloc(1, sizeof(*mapper));
|
||||
if (mapper == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
constraint_init(&mapper->global);
|
||||
|
||||
wl_list_init(&mapper->mappings);
|
||||
wl_signal_init(&mapper->events.destroy);
|
||||
|
||||
mapper->layout_destroy.notify = handle_layout_destroy;
|
||||
wl_list_init(&mapper->layout_destroy.link);
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
void wlr_input_mapper_destroy(struct wlr_input_mapper *mapper) {
|
||||
if (mapper == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
wl_signal_emit_mutable(&mapper->events.destroy, NULL);
|
||||
|
||||
struct wlr_input_mapping *mapping, *tmp;
|
||||
wl_list_for_each_safe(mapping, tmp, &mapper->mappings, link) {
|
||||
mapping_destroy(mapping);
|
||||
}
|
||||
|
||||
constraint_finish(&mapper->global);
|
||||
|
||||
wl_list_remove(&mapper->layout_destroy.link);
|
||||
free(mapper);
|
||||
}
|
||||
|
||||
void wlr_input_mapper_attach_output_layout(struct wlr_input_mapper *mapper,
|
||||
struct wlr_output_layout *layout) {
|
||||
detach_output_layout(mapper);
|
||||
mapper->layout = layout;
|
||||
if (layout != NULL) {
|
||||
wl_signal_add(&layout->events.destroy, &mapper->layout_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
void wlr_input_mapper_absolute_to_layout(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, double x, double y,
|
||||
double *lx, double *ly) {
|
||||
struct wlr_box box;
|
||||
get_constraint(mapper, device, &box);
|
||||
if (wlr_box_empty(&box) && mapper->layout != NULL) {
|
||||
wlr_output_layout_get_box(mapper->layout, NULL, &box);
|
||||
}
|
||||
|
||||
// At this point, if no matching constraint was found and the layout
|
||||
// is NULL or empty, box is filled with zeroes
|
||||
*lx = x * box.width + box.x;
|
||||
*ly = y * box.height + box.y;
|
||||
}
|
||||
|
||||
void wlr_input_mapper_closest_point(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, double lx, double ly,
|
||||
double *closest_lx, double *closest_ly) {
|
||||
struct wlr_box box;
|
||||
get_constraint(mapper, device, &box);
|
||||
if (!wlr_box_empty(&box)) {
|
||||
wlr_box_closest_point(&box, lx, ly, closest_lx, closest_ly);
|
||||
} else if (mapper->layout != NULL &&
|
||||
!wl_list_empty(&mapper->layout->outputs)) {
|
||||
wlr_output_layout_closest_point(mapper->layout, NULL,
|
||||
lx, ly, closest_lx, closest_ly);
|
||||
} else {
|
||||
// No constraint was found and the layout is either NULL or
|
||||
// empty. In this case, all points are equally invalid;
|
||||
// for convenience, the original point is returned.
|
||||
*closest_lx = lx;
|
||||
*closest_ly = ly;
|
||||
}
|
||||
}
|
||||
|
||||
void wlr_input_mapper_map_to_output(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, struct wlr_output *output) {
|
||||
struct wlr_input_constraint *constraint = &mapper->global;
|
||||
if (device != NULL) {
|
||||
struct wlr_input_mapping *mapping = get_mapping(mapper, device);
|
||||
if (mapping == NULL) {
|
||||
mapping = mapping_create(mapper, device);
|
||||
if (mapping == NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
constraint = &mapping->constraint;
|
||||
}
|
||||
|
||||
constraint_detach_output(constraint);
|
||||
constraint->output = output;
|
||||
if (output != NULL) {
|
||||
wl_signal_add(&output->events.destroy, &constraint->output_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
void wlr_input_mapper_map_to_box(struct wlr_input_mapper *mapper,
|
||||
struct wlr_input_device *device, const struct wlr_box *box) {
|
||||
struct wlr_input_constraint *constraint = &mapper->global;
|
||||
if (device != NULL) {
|
||||
struct wlr_input_mapping *mapping = get_mapping(mapper, device);
|
||||
if (mapping == NULL) {
|
||||
mapping = mapping_create(mapper, device);
|
||||
if (mapping == NULL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
constraint = &mapping->constraint;
|
||||
}
|
||||
|
||||
constraint->box = *box;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue