From 0a97b68278a621882c712b55ffe851101e5902d0 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 25 Aug 2017 13:26:13 -0400 Subject: [PATCH] implement cursor and device geometry mapping --- examples/config.c | 75 ++++++++++++++++++++++++++++++++ examples/config.h | 2 + examples/pointer.c | 15 +++++-- examples/wlr-example.ini.example | 8 ++++ include/wlr/types/wlr_cursor.h | 6 ++- include/wlr/types/wlr_geometry.h | 12 +++++ types/meson.build | 1 + types/wlr_cursor.c | 35 +++++++++++++++ types/wlr_geometry.c | 36 +++++++++++++++ types/wlr_output_layout.c | 1 + 10 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 include/wlr/types/wlr_geometry.h create mode 100644 types/wlr_geometry.c diff --git a/examples/config.c b/examples/config.c index 31aaa6d7..829350c1 100644 --- a/examples/config.c +++ b/examples/config.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "shared.h" #include "config.h" #include "ini.h" @@ -21,6 +22,64 @@ static void usage(const char *name, int ret) { exit(ret); } +static struct wlr_geometry *parse_geometry(const char *str) { + // format: {width}x{height}+{x}+{y} + if (strlen(str) > 255l) { + wlr_log(L_ERROR, "cannot parse geometry string, too long"); + return NULL; + } + + char *buf = strdup(str); + struct wlr_geometry *geo = calloc(1, sizeof(struct wlr_geometry)); + + bool has_width, has_height, has_x, has_y; + char *pch = strtok(buf, "x+"); + while (pch != NULL) { + errno = 0; + char *endptr; + long val = strtol(pch, &endptr, 0); + + if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) + || (errno != 0 && val == 0)) { + goto invalid_input; + } + + if (endptr == pch) { + goto invalid_input; + } + + if (!has_width) { + geo->width = val; + has_width = true; + } else if (!has_height) { + geo->height = val; + has_height = true; + } else if (!has_x) { + geo->x = val; + has_x = true; + } else if (!has_y) { + geo->y = val; + has_y = true; + } else { + goto invalid_input; + } + pch = strtok(NULL, "x+"); + } + + if (!has_width || !has_height || !has_x || !has_y) { + goto invalid_input; + } + + free(buf); + return geo; + +invalid_input: + wlr_log(L_ERROR, "could not parse geometry string: %s", str); + free(buf); + free(geo); + return NULL; +} + static const char *output_prefix = "output:"; static const char *device_prefix = "device:"; @@ -71,6 +130,11 @@ static int config_ini_handler(void *user, const char *section, const char *name, } else if (strcmp(section, "cursor") == 0) { if (strcmp(name, "map-to-output") == 0) { config->cursor.mapped_output = strdup(value); + } else if (strcmp(name, "geometry") == 0) { + if (config->cursor.mapped_geo) { + free(config->cursor.mapped_geo); + } + config->cursor.mapped_geo = parse_geometry(value); } else { wlr_log(L_ERROR, "got unknown cursor config: %s", name); } @@ -97,6 +161,11 @@ static int config_ini_handler(void *user, const char *section, const char *name, free(dc->mapped_output); } dc->mapped_output = strdup(value); + } else if (strcmp(name, "geometry") == 0) { + if (dc->mapped_geo) { + free(dc->mapped_geo); + } + dc->mapped_geo = parse_geometry(value); } else { wlr_log(L_ERROR, "got unknown device config: %s", name); } @@ -166,6 +235,9 @@ void example_config_destroy(struct example_config *config) { if (dc->mapped_output) { free(dc->mapped_output); } + if (dc->mapped_geo) { + free(dc->mapped_geo); + } free(dc); } @@ -175,6 +247,9 @@ void example_config_destroy(struct example_config *config) { if (config->cursor.mapped_output) { free(config->cursor.mapped_output); } + if (config->cursor.mapped_geo) { + free(config->cursor.mapped_geo); + } free(config); } diff --git a/examples/config.h b/examples/config.h index e1765c57..cd19dc5e 100644 --- a/examples/config.h +++ b/examples/config.h @@ -15,12 +15,14 @@ struct output_config { struct device_config { char *name; char *mapped_output; + struct wlr_geometry *mapped_geo; struct wl_list link; }; struct example_config { struct { char *mapped_output; + struct wlr_geometry *mapped_geo; } cursor; struct wl_list outputs; diff --git a/examples/pointer.c b/examples/pointer.c index bec71dff..9492adab 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -63,14 +63,21 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts static void configure_devices(struct sample_state *sample) { struct sample_input_device *dev; - // reset device to output mappings + struct device_config *dc; + + // reset device mappings wl_list_for_each(dev, &sample->devices, link) { wlr_cursor_map_input_to_output(sample->cursor, dev->device, NULL); + wl_list_for_each(dc, &sample->config->devices, link) { + if (strcmp(dev->device->name, dc->name) == 0) { + wlr_cursor_map_input_to_region(sample->cursor, dev->device, + dc->mapped_geo); + } + } } struct output_state *ostate; wl_list_for_each(ostate, &sample->compositor->outputs, link) { - struct device_config *dc; wl_list_for_each(dc, &sample->config->devices, link) { // configure device to output mappings if (dc->mapped_output && @@ -123,7 +130,8 @@ static void handle_output_remove(struct output_state *ostate) { configure_devices(sample); - if (strcmp(sample->config->cursor.mapped_output, ostate->output->name) == 0) { + char *mapped_output = sample->config->cursor.mapped_output; + if (mapped_output && strcmp(mapped_output, ostate->output->name) == 0) { wlr_cursor_map_to_output(sample->cursor, NULL); } } @@ -216,6 +224,7 @@ int main(int argc, char *argv[]) { state.config = parse_args(argc, argv); state.cursor = wlr_cursor_init(); + wlr_cursor_map_to_region(state.cursor, state.config->cursor.mapped_geo); wl_list_init(&state.devices); wl_signal_add(&state.cursor->events.motion, &state.cursor_motion); diff --git a/examples/wlr-example.ini.example b/examples/wlr-example.ini.example index ffb6229e..1698e0c6 100644 --- a/examples/wlr-example.ini.example +++ b/examples/wlr-example.ini.example @@ -34,8 +34,12 @@ y=232 # ~~~~~~~~~~~~~~~~~~~~ # Value "map-to-output" specifies the output to which the cursor is # constrained. +# +# Value "geometry" specifies the geometry (widthxheight+x+y) to which the cursor +# is constrained. [cursor] map-to-output=HDMI-A-1 +geometry=500x700+50+50 # Device Configuration # ~~~~~~~~~~~~~~~~~~~~ @@ -43,7 +47,11 @@ map-to-output=HDMI-A-1 # name given to this device. See a log file for device names. # # Value "map-to-output" specifies the output to which the device is constrained. +# +# Value "geometry" specifies the geometry (widthxheight+x+y) to which the device +# is constrained. [device:Razer Razer DeathAdder 2013] map-to-output=DP-1 +geometry=500x700+50+50 # vim:filetype=dosini diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index 64a75e4f..30495d44 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -4,6 +4,7 @@ #include #include #include +#include #include struct wlr_cursor_state; @@ -87,12 +88,13 @@ void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, /** * Maps this cursor to an arbitrary region on the associated wlr_output_layout. */ -//void wlr_cursor_map_to_region(struct wlr_cursor *cur, struct wlr_geometry *geo); +void wlr_cursor_map_to_region(struct wlr_cursor *cur, struct wlr_geometry *geo); /** * Maps inputs from this input device to an arbitrary region on the associated * wlr_output_layout. */ -//void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, struct wlr_input_device *dev, struct wlr_geometry *geo); +void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, + struct wlr_input_device *dev, struct wlr_geometry *geo); #endif diff --git a/include/wlr/types/wlr_geometry.h b/include/wlr/types/wlr_geometry.h new file mode 100644 index 00000000..3e218bed --- /dev/null +++ b/include/wlr/types/wlr_geometry.h @@ -0,0 +1,12 @@ +#ifndef _WLR_TYPES_GEOMETRY_H +#define _WLR_TYPES_GEOMETRY_H + +struct wlr_geometry { + int x, y; + int width, height; +}; + +void wlr_geometry_closest_boundary(struct wlr_geometry *geo, double x, double y, + int *dest_x, int *dest_y, double *distance); + +#endif diff --git a/types/meson.build b/types/meson.build index 3992c6e9..d0ed85fe 100644 --- a/types/meson.build +++ b/types/meson.build @@ -16,6 +16,7 @@ lib_wlr_types = static_library('wlr_types', files( 'wlr_xdg_shell_v6.c', 'wlr_wl_shell.c', 'wlr_compositor.c', + 'wlr_geometry.c', ), include_directories: wlr_inc, dependencies: [wayland_server, pixman, wlr_protos]) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 6917526e..c0b2b6ae 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -12,6 +12,7 @@ struct wlr_cursor_device { struct wlr_input_device *device; struct wl_list link; struct wlr_output *mapped_output; + struct wlr_geometry *mapped_geometry; struct wl_listener motion; struct wl_listener motion_absolute; @@ -24,6 +25,7 @@ struct wlr_cursor_state { struct wlr_output_layout *layout; struct wlr_xcursor *xcursor; struct wlr_output *mapped_output; + struct wlr_geometry *mapped_geometry; }; struct wlr_cursor *wlr_cursor_init() { @@ -145,6 +147,25 @@ void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev, double x = cur->x + delta_x; double y = cur->y + delta_y; + // cursor geometry constraints + if (cur->state->mapped_geometry) { + int closest_x, closest_y; + wlr_geometry_closest_boundary(cur->state->mapped_geometry, x, y, + &closest_x, &closest_y, NULL); + x = closest_x; + y = closest_y; + } + + // device constraints + if (c_device->mapped_geometry) { + int closest_x, closest_y; + wlr_geometry_closest_boundary(c_device->mapped_geometry, x, y, + &closest_x, &closest_y, NULL); + x = closest_x; + y = closest_y; + } + + // layout constraints struct wlr_output *output; output = wlr_output_layout_output_at(cur->state->layout, x, y); @@ -271,3 +292,17 @@ void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, c_device->mapped_output = output; } + +void wlr_cursor_map_to_region(struct wlr_cursor *cur, struct wlr_geometry *geo) { + cur->state->mapped_geometry = geo; +} + +void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, + struct wlr_input_device *dev, struct wlr_geometry *geo) { + struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); + if (!c_device) { + wlr_log(L_ERROR, "Cannot map device \"%s\" to geometry (not found in this cursor)", dev->name); + return; + } + c_device->mapped_geometry = geo; +} diff --git a/types/wlr_geometry.c b/types/wlr_geometry.c new file mode 100644 index 00000000..8358d887 --- /dev/null +++ b/types/wlr_geometry.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +static double get_distance(double x1, double y1, double x2, double y2) { + double distance; + distance = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + return distance; +} + +void wlr_geometry_closest_boundary(struct wlr_geometry *geo, double x, double y, + int *dest_x, int *dest_y, double *distance) { + // find the closest x point + if (x < geo->x) { + *dest_x = geo->x; + } else if (x > geo->x + geo->width) { + *dest_x = geo->x + geo->width; + } else { + *dest_x = x; + } + + // find closest y point + if (y < geo->y) { + *dest_y = geo->y; + } else if (y > geo->y + geo->height) { + *dest_y = geo->y + geo->height; + } else { + *dest_y = y; + } + + // calculate distance + if (distance) { + *distance = get_distance(*dest_x, *dest_y, x, y); + } +} diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c index 5e2067da..7dcb4651 100644 --- a/types/wlr_output_layout.c +++ b/types/wlr_output_layout.c @@ -149,6 +149,7 @@ void wlr_output_layout_closest_boundary(struct wlr_output_layout *layout, wlr_output_effective_resolution(l_output->output, &width, &height); // find the closest x point + // TODO use wlr_geometry_closest_boundary if (x < l_output->x) { output_x = l_output->x; } else if (x > l_output->x + width) {