aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2017-08-29 12:12:18 -0500
committerGitHub <noreply@github.com>2017-08-29 12:12:18 -0500
commit6daf9e9ab2bb6e11987d3b2562fc053fd0f489c1 (patch)
tree11b9bbe78deff0e4baa76bd00e1c13d16340ceb9
parentc46168cf9acd360ae37b4d2a54ed1778f0b89c72 (diff)
parentd9ab631f5d540d67d927e9d0975e2adb782e2e87 (diff)
Merge pull request #120 from acrisci/feature/wlr-cursor
wlr_cursor
-rw-r--r--.gitignore1
-rw-r--r--backend/drm/drm.c27
-rw-r--r--backend/libinput/pointer.c4
-rw-r--r--backend/libinput/tablet_tool.c4
-rw-r--r--backend/libinput/touch.c4
-rw-r--r--backend/wayland/wl_seat.c3
-rw-r--r--examples/config.c303
-rw-r--r--examples/config.h39
-rw-r--r--examples/ini.c195
-rw-r--r--examples/ini.h93
-rw-r--r--examples/meson.build2
-rw-r--r--examples/output-layout.c5
-rw-r--r--examples/pointer.c361
-rw-r--r--examples/rotation.c1
-rw-r--r--examples/shared.c148
-rw-r--r--examples/shared.h22
-rw-r--r--examples/touch.c2
-rw-r--r--examples/wlr-example.ini.example57
-rw-r--r--include/wlr/types/wlr_box.h20
-rw-r--r--include/wlr/types/wlr_cursor.h112
-rw-r--r--include/wlr/types/wlr_input_device.h4
-rw-r--r--include/wlr/types/wlr_output_layout.h31
-rw-r--r--include/wlr/types/wlr_pointer.h4
-rw-r--r--include/wlr/types/wlr_tablet_tool.h8
-rw-r--r--include/wlr/types/wlr_touch.h4
-rw-r--r--types/meson.build2
-rw-r--r--types/wlr_box.c67
-rw-r--r--types/wlr_cursor.c481
-rw-r--r--types/wlr_input_device.c4
-rw-r--r--types/wlr_output_layout.c194
30 files changed, 1939 insertions, 263 deletions
diff --git a/.gitignore b/.gitignore
index ce85118c..d4edde88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ test/
build/
.lvimrc
wayland-*-protocol.*
+wlr-example.ini
diff --git a/backend/drm/drm.c b/backend/drm/drm.c
index 332926b9..5b24e05f 100644
--- a/backend/drm/drm.c
+++ b/backend/drm/drm.c
@@ -598,6 +598,8 @@ static bool wlr_drm_output_set_cursor(struct wlr_output *_output,
wlr_matrix_texture(plane->matrix, plane->width, plane->height,
output->output.transform ^ WL_OUTPUT_TRANSFORM_FLIPPED_180);
+ // TODO the image needs to be rotated depending on the output rotation
+
plane->wlr_rend = wlr_gles2_renderer_create(&backend->backend);
if (!plane->wlr_rend) {
return false;
@@ -651,6 +653,31 @@ static bool wlr_drm_output_move_cursor(struct wlr_output *_output,
struct wlr_drm_output *output = (struct wlr_drm_output *)_output;
struct wlr_drm_backend *backend =
wl_container_of(output->renderer, backend, renderer);
+
+ int width, height, tmp;
+ wlr_output_effective_resolution(_output, &width, &height);
+
+ switch (_output->transform) {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ // nothing to do
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ tmp = x;
+ x = y;
+ y = -(tmp - width);
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ tmp = x;
+ x = -(y - height);
+ y = tmp;
+ break;
+ default:
+ // TODO other transformations
+ wlr_log(L_ERROR, "TODO: handle surface to crtc for transformation = %d",
+ _output->transform);
+ break;
+ }
+
return backend->iface->crtc_move_cursor(backend, output->crtc, x, y);
}
diff --git a/backend/libinput/pointer.c b/backend/libinput/pointer.c
index 8bda205d..005c9516 100644
--- a/backend/libinput/pointer.c
+++ b/backend/libinput/pointer.c
@@ -30,6 +30,7 @@ void handle_pointer_motion(struct libinput_event *event,
struct libinput_event_pointer *pevent =
libinput_event_get_pointer_event(event);
struct wlr_event_pointer_motion wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_pointer_get_time(pevent);
wlr_event.time_usec = libinput_event_pointer_get_time_usec(pevent);
wlr_event.delta_x = libinput_event_pointer_get_dx(pevent);
@@ -48,6 +49,7 @@ void handle_pointer_motion_abs(struct libinput_event *event,
struct libinput_event_pointer *pevent =
libinput_event_get_pointer_event(event);
struct wlr_event_pointer_motion_absolute wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_pointer_get_time(pevent);
wlr_event.time_usec = libinput_event_pointer_get_time_usec(pevent);
wlr_event.x_mm = libinput_event_pointer_get_absolute_x(pevent);
@@ -67,6 +69,7 @@ void handle_pointer_button(struct libinput_event *event,
struct libinput_event_pointer *pevent =
libinput_event_get_pointer_event(event);
struct wlr_event_pointer_button wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_pointer_get_time(pevent);
wlr_event.time_usec = libinput_event_pointer_get_time_usec(pevent);
wlr_event.button = libinput_event_pointer_get_button(pevent);
@@ -92,6 +95,7 @@ void handle_pointer_axis(struct libinput_event *event,
struct libinput_event_pointer *pevent =
libinput_event_get_pointer_event(event);
struct wlr_event_pointer_axis wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_pointer_get_time(pevent);
wlr_event.time_usec = libinput_event_pointer_get_time_usec(pevent);
switch (libinput_event_pointer_get_axis_source(pevent)) {
diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c
index 8b3d34ed..472e8506 100644
--- a/backend/libinput/tablet_tool.c
+++ b/backend/libinput/tablet_tool.c
@@ -30,6 +30,7 @@ void handle_tablet_tool_axis(struct libinput_event *event,
struct libinput_event_tablet_tool *tevent =
libinput_event_get_tablet_tool_event(event);
struct wlr_event_tablet_tool_axis wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_tablet_tool_get_time(tevent);
wlr_event.time_usec = libinput_event_tablet_tool_get_time_usec(tevent);
libinput_device_get_size(libinput_dev, &wlr_event.width_mm, &wlr_event.height_mm);
@@ -83,6 +84,7 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
struct libinput_event_tablet_tool *tevent =
libinput_event_get_tablet_tool_event(event);
struct wlr_event_tablet_tool_proximity wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_tablet_tool_get_time(tevent);
wlr_event.time_usec = libinput_event_tablet_tool_get_time_usec(tevent);
switch (libinput_event_tablet_tool_get_proximity_state(tevent)) {
@@ -109,6 +111,7 @@ void handle_tablet_tool_tip(struct libinput_event *event,
struct libinput_event_tablet_tool *tevent =
libinput_event_get_tablet_tool_event(event);
struct wlr_event_tablet_tool_tip wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_tablet_tool_get_time(tevent);
wlr_event.time_usec = libinput_event_tablet_tool_get_time_usec(tevent);
switch (libinput_event_tablet_tool_get_tip_state(tevent)) {
@@ -134,6 +137,7 @@ void handle_tablet_tool_button(struct libinput_event *event,
struct libinput_event_tablet_tool *tevent =
libinput_event_get_tablet_tool_event(event);
struct wlr_event_tablet_tool_button wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_tablet_tool_get_time(tevent);
wlr_event.time_usec = libinput_event_tablet_tool_get_time_usec(tevent);
wlr_event.button = libinput_event_tablet_tool_get_button(tevent);
diff --git a/backend/libinput/touch.c b/backend/libinput/touch.c
index 9e08d028..2f300482 100644
--- a/backend/libinput/touch.c
+++ b/backend/libinput/touch.c
@@ -30,6 +30,7 @@ void handle_touch_down(struct libinput_event *event,
struct libinput_event_touch *tevent =
libinput_event_get_touch_event(event);
struct wlr_event_touch_down wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_touch_get_time(tevent);
wlr_event.time_usec = libinput_event_touch_get_time_usec(tevent);
wlr_event.slot = libinput_event_touch_get_slot(tevent);
@@ -50,6 +51,7 @@ void handle_touch_up(struct libinput_event *event,
struct libinput_event_touch *tevent =
libinput_event_get_touch_event(event);
struct wlr_event_touch_up wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_touch_get_time(tevent);
wlr_event.time_usec = libinput_event_touch_get_time_usec(tevent);
wlr_event.slot = libinput_event_touch_get_slot(tevent);
@@ -67,6 +69,7 @@ void handle_touch_motion(struct libinput_event *event,
struct libinput_event_touch *tevent =
libinput_event_get_touch_event(event);
struct wlr_event_touch_motion wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_touch_get_time(tevent);
wlr_event.time_usec = libinput_event_touch_get_time_usec(tevent);
wlr_event.slot = libinput_event_touch_get_slot(tevent);
@@ -87,6 +90,7 @@ void handle_touch_cancel(struct libinput_event *event,
struct libinput_event_touch *tevent =
libinput_event_get_touch_event(event);
struct wlr_event_touch_cancel wlr_event = { 0 };
+ wlr_event.device = wlr_dev;
wlr_event.time_sec = libinput_event_touch_get_time(tevent);
wlr_event.time_usec = libinput_event_touch_get_time_usec(tevent);
wlr_event.slot = libinput_event_touch_get_slot(tevent);
diff --git a/backend/wayland/wl_seat.c b/backend/wayland/wl_seat.c
index a4cc0ba5..3e6982a0 100644
--- a/backend/wayland/wl_seat.c
+++ b/backend/wayland/wl_seat.c
@@ -51,6 +51,7 @@ static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
wl_egl_window_get_attached_size(wlr_wl_pointer->current_output->egl_window,
&width, &height);
struct wlr_event_pointer_motion_absolute wlr_event;
+ wlr_event.device = dev;
wlr_event.time_sec = time / 1000;
wlr_event.time_usec = time * 1000;
wlr_event.width_mm = width;
@@ -66,6 +67,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
assert(dev && dev->pointer);
struct wlr_event_pointer_button wlr_event;
+ wlr_event.device = dev;
wlr_event.button = button;
wlr_event.state = state;
wlr_event.time_sec = time / 1000;
@@ -80,6 +82,7 @@ static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
struct wlr_wl_pointer *wlr_wl_pointer = (struct wlr_wl_pointer *)dev->pointer;
struct wlr_event_pointer_axis wlr_event;
+ wlr_event.device = dev;
wlr_event.delta = value;
wlr_event.orientation = axis;
wlr_event.time_sec = time / 1000;
diff --git a/examples/config.c b/examples/config.c
new file mode 100644
index 00000000..954edb06
--- /dev/null
+++ b/examples/config.c
@@ -0,0 +1,303 @@
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <wlr/util/log.h>
+#include <wlr/types/wlr_box.h>
+#include "shared.h"
+#include "config.h"
+#include "ini.h"
+
+static void usage(const char *name, int ret) {
+ fprintf(stderr,
+ "usage: %s [-C <FILE>]\n"
+ "\n"
+ " -C <FILE> Path to the configuration file\n"
+ " (default: wlr-example.ini).\n"
+ " See `examples/wlr-example.ini.example` for config\n"
+ " file documentation.\n", name);
+
+ exit(ret);
+}
+
+static struct wlr_box *parse_geometry(const char *str) {
+ // format: {width}x{height}+{x}+{y}
+ if (strlen(str) > 255) {
+ wlr_log(L_ERROR, "cannot parse geometry string, too long");
+ return NULL;
+ }
+
+ char *buf = strdup(str);
+ struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
+
+ bool has_width = false;
+ bool has_height = false;
+ bool has_x = false;
+ bool has_y = false;
+
+ 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) {
+ box->width = val;
+ has_width = true;
+ } else if (!has_height) {
+ box->height = val;
+ has_height = true;
+ } else if (!has_x) {
+ box->x = val;
+ has_x = true;
+ } else if (!has_y) {
+ box->y = val;
+ has_y = true;
+ } else {
+ break;
+ }
+ pch = strtok(NULL, "x+");
+ }
+
+ if (!has_width || !has_height) {
+ goto invalid_input;
+ }
+
+ free(buf);
+ return box;
+
+invalid_input:
+ wlr_log(L_ERROR, "could not parse geometry string: %s", str);
+ free(buf);
+ free(box);
+ return NULL;
+}
+
+static const char *output_prefix = "output:";
+static const char *device_prefix = "device:";
+
+static int config_ini_handler(void *user, const char *section, const char *name,
+ const char *value) {
+ struct example_config *config = user;
+ if (strncmp(output_prefix, section, strlen(output_prefix)) == 0) {
+ const char *output_name = section + strlen(output_prefix);
+ struct output_config *oc;
+ bool found = false;
+
+ wl_list_for_each(oc, &config->outputs, link) {
+ if (strcmp(oc->name, output_name) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ oc = calloc(1, sizeof(struct output_config));
+ oc->name = strdup(output_name);
+ oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
+ wl_list_insert(&config->outputs, &oc->link);
+ }
+
+ if (strcmp(name, "x") == 0) {
+ oc->x = strtol(value, NULL, 10);
+ } else if (strcmp(name, "y") == 0) {
+ oc->y = strtol(value, NULL, 10);
+ } else if (strcmp(name, "rotate") == 0) {
+ if (strcmp(value, "90") == 0) {
+ oc->transform = WL_OUTPUT_TRANSFORM_90;
+ } else if (strcmp(value, "180") == 0) {
+ oc->transform = WL_OUTPUT_TRANSFORM_180;
+ } else if (strcmp(value, "270") == 0) {
+ oc->transform = WL_OUTPUT_TRANSFORM_270;
+ } else if (strcmp(value, "flipped") == 0) {
+ oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
+ } else if (strcmp(value, "flipped-90") == 0) {
+ oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
+ } else if (strcmp(value, "flipped-180") == 0) {
+ oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
+ } else if (strcmp(value, "flipped-270") == 0) {
+ oc->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
+ } else {
+ wlr_log(L_ERROR, "got unknown transform value: %s", value);
+ }
+ }
+ } else if (strcmp(section, "cursor") == 0) {
+ if (strcmp(name, "map-to-output") == 0) {
+ free(config->cursor.mapped_output);
+ config->cursor.mapped_output = strdup(value);
+ } else if (strcmp(name, "geometry") == 0) {
+ free(config->cursor.mapped_box);
+ config->cursor.mapped_box = parse_geometry(value);
+ } else {
+ wlr_log(L_ERROR, "got unknown cursor config: %s", name);
+ }
+ } else if (strncmp(device_prefix, section, strlen(device_prefix)) == 0) {
+ const char *device_name = section + strlen(device_prefix);
+ struct device_config *dc;
+ bool found = false;
+
+ wl_list_for_each(dc, &config->devices, link) {
+ if (strcmp(dc->name, device_name) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dc = calloc(1, sizeof(struct device_config));
+ dc->name = strdup(device_name);
+ wl_list_insert(&config->devices, &dc->link);
+ }
+
+ if (strcmp(name, "map-to-output") == 0) {
+ free(dc->mapped_output);
+ dc->mapped_output = strdup(value);
+ } else if (strcmp(name, "geometry") == 0) {
+ free(dc->mapped_box);
+ dc->mapped_box = parse_geometry(value);
+ } else {
+ wlr_log(L_ERROR, "got unknown device config: %s", name);
+ }
+ } else {
+ wlr_log(L_ERROR, "got unknown config section: %s", section);
+ }
+
+ return 1;
+}
+
+struct example_config *parse_args(int argc, char *argv[]) {
+ struct example_config *config = calloc(1, sizeof(struct example_config));
+ wl_list_init(&config->outputs);
+ wl_list_init(&config->devices);
+
+ int c;
+ while ((c = getopt(argc, argv, "C:h")) != -1) {
+ switch (c) {
+ case 'C':
+ config->config_path = strdup(optarg);
+ break;
+ case 'h':
+ case '?':
+ usage(argv[0], c != 'h');
+ }
+ }
+
+ if (!config->config_path) {
+ // get the config path from the current directory
+ char cwd[MAXPATHLEN];
+ if (getcwd(cwd, sizeof(cwd)) != NULL) {
+ char buf[MAXPATHLEN];
+ snprintf(buf, MAXPATHLEN, "%s/%s", cwd, "wlr-example.ini");
+ config->config_path = strdup(buf);
+ } else {
+ wlr_log(L_ERROR, "could not get cwd");
+ exit(1);
+ }
+ }
+
+ int result = ini_parse(config->config_path, config_ini_handler, config);
+
+ if (result == -1) {
+ wlr_log(L_DEBUG, "No config file found. Using empty config.");
+ } else if (result == -2) {
+ wlr_log(L_ERROR, "Could not allocate memory to parse config file");
+ exit(1);
+ } else if (result != 0) {
+ wlr_log(L_ERROR, "Could not parse config file");
+ exit(1);
+ }
+
+ return config;
+}
+
+void example_config_destroy(struct example_config *config) {
+ struct output_config *oc, *otmp = NULL;
+ wl_list_for_each_safe(oc, otmp, &config->outputs, link) {
+ free(oc->name);
+ free(oc);
+ }
+
+ struct device_config *dc, *dtmp = NULL;
+ wl_list_for_each_safe(dc, dtmp, &config->devices, link) {
+ free(dc->name);
+ if (dc->mapped_output) {
+ free(dc->mapped_output);
+ }
+ if (dc->mapped_box) {
+ free(dc->mapped_box);
+ }
+ free(dc);
+ }
+
+ if (config->config_path) {
+ free(config->config_path);
+ }
+ if (config->cursor.mapped_output) {
+ free(config->cursor.mapped_output);
+ }
+ if (config->cursor.mapped_box) {
+ free(config->cursor.mapped_box);
+ }
+ free(config);
+}
+
+struct wlr_output_layout *configure_layout(struct example_config *config,
+ struct wl_list *outputs) {
+ struct wlr_output_layout *layout = wlr_output_layout_init();
+ int max_x = INT_MIN;
+ int max_x_y = INT_MIN; // y value for the max_x output
+
+ // first add all the configured outputs
+ struct output_state *output;
+ wl_list_for_each(output, outputs, link) {
+ struct output_config *conf;
+ wl_list_for_each(conf, &config->outputs, link) {
+ if (strcmp(conf->name, output->output->name) == 0) {
+ wlr_output_layout_add(layout, output->output,
+ conf->x, conf->y);
+ wlr_output_transform(output->output, conf->transform);
+ int width, height;
+ wlr_output_effective_resolution(output->output, &width,
+ &height);
+ if (conf->x + width > max_x) {
+ max_x = conf->x + width;
+ max_x_y = conf->y;
+ }
+ break;
+ }
+ }
+ }
+
+ if (max_x == INT_MIN) {
+ // couldn't find a configured output
+ max_x = 0;
+ max_x_y = 0;
+ }
+
+ // now add all the other configured outputs in a sensible position
+ wl_list_for_each(output, outputs, link) {
+ if (wlr_output_layout_get(layout, output->output)) {
+ continue;
+ }
+ wlr_output_layout_add(layout, output->output, max_x, max_x_y);
+ int width, height;
+ wlr_output_effective_resolution(output->output, &width, &height);
+ max_x += width;
+ }
+
+ return layout;
+}
diff --git a/examples/config.h b/examples/config.h
new file mode 100644
index 00000000..2a69c4f4
--- /dev/null
+++ b/examples/config.h
@@ -0,0 +1,39 @@
+#ifndef _EXAMPLE_CONFIG_H
+#define _EXAMPLE_CONFIG_H
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200112L
+#endif
+#include <wlr/types/wlr_output_layout.h>
+
+struct output_config {
+ char *name;
+ enum wl_output_transform transform;
+ int x, y;
+ struct wl_list link;
+};
+
+struct device_config {
+ char *name;
+ char *mapped_output;
+ struct wlr_box *mapped_box;
+ struct wl_list link;
+};
+
+struct example_config {
+ struct {
+ char *mapped_output;
+ struct wlr_box *mapped_box;
+ } cursor;
+
+ struct wl_list outputs;
+ struct wl_list devices;
+ char *config_path;
+};
+
+struct example_config *parse_args(int argc, char *argv[]);
+
+void example_config_destroy(struct example_config *config);
+
+struct wlr_output_layout *configure_layout(struct example_config *config,
+ struct wl_list *outputs);
+#endif
diff --git a/examples/ini.c b/examples/ini.c
new file mode 100644
index 00000000..6be9c44a
--- /dev/null
+++ b/examples/ini.c
@@ -0,0 +1,195 @@
+/* inih -- simple .INI file parser
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+https://github.com/benhoyt/inih
+
+*/
+
+#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "ini.h"
+
+#if !INI_USE_STACK
+#include <stdlib.h>
+#endif
+
+#define MAX_SECTION 50
+#define MAX_NAME 50
+
+/* Strip whitespace chars off end of given string, in place. Return s. */
+static char* rstrip(char* s)
+{
+ char* p = s + strlen(s);
+ while (p > s && isspace((unsigned char)(*--p)))
+ *p = '\0';
+ return s;
+}
+
+/* Return pointer to first non-whitespace char in given string. */
+static char* lskip(const char* s)
+{
+ while (*s && isspace((unsigned char)(*s)))
+ s++;
+ return (char*)s;
+}
+
+/* Return pointer to first char (of chars) or inline comment in given string,
+ or pointer to null at end of string if neither found. Inline comment must
+ be prefixed by a whitespace character to register as a comment. */
+static char* find_chars_or_comment(const char* s, const char* chars)
+{
+#if INI_ALLOW_INLINE_COMMENTS
+ int was_space = 0;
+ while (*s && (!chars || !strchr(chars, *s)) &&
+ !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
+ was_space = isspace((unsigned char)(*s));
+ s++;
+ }
+#else
+ while (*s && (!chars || !strchr(chars, *s))) {
+ s++;
+ }
+#endif
+ return (char*)s;
+}
+
+/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
+static char* strncpy0(char* dest, const char* src, size_t size)
+{
+ strncpy(dest, src, size);
+ dest[size - 1] = '\0';
+ return dest;
+}
+
+/* See documentation in header file. */
+int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
+ void* user)
+{
+ /* Uses a fair bit of stack (use heap instead if you need to) */
+#if INI_USE_STACK
+ char line[INI_MAX_LINE];
+#else
+ char* line;
+#endif
+ char section[MAX_SECTION] = "";
+ char prev_name[MAX_NAME] = "";
+
+ char* start;
+ char* end;
+ char* name;
+ char* value;
+ int lineno = 0;
+ int error = 0;
+
+#if !INI_USE_STACK
+ line = (char*)malloc(INI_MAX_LINE);
+ if (!line) {
+ return -2;
+ }
+#endif
+
+ /* Scan through stream line by line */
+ while (reader(line, INI_MAX_LINE, stream) != NULL) {
+ lineno++;
+
+ start = line;
+#if INI_ALLOW_BOM
+ if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
+ (unsigned char)start[1] == 0xBB &&
+ (unsigned char)start[2] == 0xBF) {
+ start += 3;
+ }
+#endif
+ start = lskip(rstrip(start));
+
+ if (*start == ';' || *start == '#') {
+ /* Per Python configparser, allow both ; and # comments at the
+ start of a line */
+ }
+#if INI_ALLOW_MULTILINE
+ else if (*prev_name && *start && start > line) {
+ /* Non-blank line with leading whitespace, treat as continuation
+ of previous name's value (as per Python configparser). */
+ if (!handler(user, section, prev_name, start) && !error)
+ error = lineno;
+ }
+#endif
+ else if (*start == '[') {
+ /* A "[section]" line */
+ end = find_chars_or_comment(start + 1, "]");
+ if (*end == ']') {
+ *end = '\0';
+ strncpy0(section, start + 1, sizeof(section));
+ *prev_name = '\0';
+ }
+ else if (!error) {
+ /* No ']' found on section line */
+ error = lineno;
+ }
+ }
+ else if (*start) {
+ /* Not a comment, must be a name[=:]value pair */
+ end = find_chars_or_comment(start, "=:");
+ if (*end == '=' || *end == ':') {
+ *end = '\0';
+ name = rstrip(start);
+ value = lskip(end + 1);
+#if INI_ALLOW_INLINE_COMMENTS
+ end = find_chars_or_comment(value, NULL);
+ if (*end)
+ *end = '\0';
+#endif
+ rstrip(value);
+
+ /* Valid name[=:]value pair found, call handler */
+ strncpy0(prev_name, name, sizeof(prev_name));
+ if (!handler(user, section, name, value) && !error)
+ error = lineno;
+ memset(value, 0, strlen(value));
+ }
+ else if (!error) {
+ /* No '=' or ':' found on name[=:]value line */
+ error = lineno;
+ }
+ }
+
+#if INI_STOP_ON_FIRST_ERROR
+ if (error)
+ break;
+#endif
+ }
+
+#if !INI_USE_STACK
+ free(line);
+#endif
+
+ return error;
+}
+
+/* See documentation in header file. */
+int ini_parse_file(FILE* file, ini_handler handler, void* user)
+{
+ return ini_parse_stream((ini_reader)fgets, file, handler, user);
+}
+
+/* See documentation in header file. */
+int ini_parse(const char* filename, ini_handler handler, void* user)
+{
+ FILE* file;
+ int error;
+
+ file = fopen(filename, "r");
+ if (!file)
+ return -1;
+ error = ini_parse_file(file, handler, user);
+ fclose(file);
+ return error;
+}
diff --git a/examples/ini.h b/examples/ini.h
new file mode 100644
index 00000000..2804255b
--- /dev/null
+++ b/examples/ini.h
@@ -0,0 +1,93 @@
+/* inih -- simple .INI file parser
+
+inih is released under the New BSD license (see LICENSE.txt). Go to the project
+home page for more info:
+
+https://github.com/benhoyt/inih
+
+*/
+
+#ifndef __INI_H__
+#define __INI_H__
+
+/* Make this header file easier to include in C++ code */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+/* Typedef for prototype of handler function. */
+typedef int (*ini_handler)(void* user, const char* section,
+ const char* name, const char* value);
+
+/* Typedef for prototype of fgets-style reader function. */
+typedef char* (*ini_reader)(char* str, int num, void* stream);
+
+/* Parse given INI-style file. May have [section]s, name=value pairs
+ (whitespace stripped), and comments starting with ';' (semicolon). Section
+ is "" if name=value pair parsed before any section heading. name:value
+ pairs are also supported as a concession to Python's configparser.
+
+ For each name=value pair parsed, call handler function with given user
+ pointer as well as section, name, and value (data only valid for duration
+ of handler call). Handler should return nonzero on success, zero on error.
+
+ Returns 0 on success, line number of first error on parse error (doesn't
+ stop on first error), -1 on file open error, or -2 on memory allocation
+ error (only when INI_USE_STACK is zero).
+*/
+int ini_parse(const char* filename, ini_handler handler, void* user);
+
+/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
+ close the file when it's finished -- the caller must do that. */
+int ini_parse_file(FILE* file, ini_handler handler, void* user);
+
+/* Same as ini_parse(), but takes an ini_reader function pointer instead of
+ filename. Used for implementing custom or string-based I/O. */
+int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
+ void* user);
+
+/* Nonzero to allow multi-line value parsing, in the style of Python's
+ configparser. If allowed, ini_parse() will call the handler with the same
+ name for each subsequent line parsed. */
+#ifndef INI_ALLOW_MULTILINE
+#define INI_ALLOW_MULTILINE 1
+#endif
+
+/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
+ the file. See http://code.google.com/p/inih/issues/detail?id=21 */
+#ifndef INI_ALLOW_BOM
+#define INI_ALLOW_BOM 1
+#endif
+
+/* Nonzero to allow inline comments (with valid inline comment characters
+ specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
+ Python 3.2+ configparser behaviour. */
+#ifndef INI_ALLOW_INLINE_COMMENTS
+#define INI_ALLOW_INLINE_COMMENTS 1
+#endif
+#ifndef INI_INLINE_COMMENT_PREFIXES
+#define INI_INLINE_COMMENT_PREFIXES ";"
+#endif
+
+/* Nonzero to use stack, zero to use heap (malloc/free). */
+#ifndef INI_USE_STACK
+#define INI_USE_STACK 1
+#endif
+
+/* Stop parsing on first error (default is to keep parsing). */
+#ifndef INI_STOP_ON_FIRST_ERROR
+#define INI_STOP_ON_FIRST_ERROR 0
+#endif
+
+/* Maximum line length for any line in INI file. */
+#ifndef INI_MAX_LINE
+#define INI_MAX_LINE 2000
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INI_H__ */
diff --git a/examples/meson.build b/examples/meson.build
index d4d96984..8fda974f 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -1,5 +1,5 @@
lib_shared = static_library('shared',
- ['shared.c', 'cat.c'],
+ ['shared.c', 'cat.c', 'ini.c', 'config.c'],
dependencies: wlroots)
executable('simple', 'simple.c', dependencies: wlroots, link_with: lib_shared)
diff --git a/examples/output-layout.c b/examples/output-layout.c
index 0dcbc1f8..90ad558f 100644
--- a/examples/output-layout.c
+++ b/examples/output-layout.c
@@ -22,6 +22,7 @@
#include <wlr/types/wlr_keyboard.h>
#include <math.h>
#include "shared.h"
+#include "config.h"
#include "cat.h"
struct sample_state {
@@ -49,8 +50,8 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts
float matrix[16];
// transform global coordinates to local coordinates
- int local_x = sample->x_offs;
- int local_y = sample->y_offs;
+ double local_x = sample->x_offs;
+ double local_y = sample->y_offs;
wlr_output_layout_output_coords(sample->layout, output->output, &local_x,
&local_y);
diff --git a/examples/pointer.c b/examples/pointer.c
index 12253b62..d9a06339 100644
--- a/examples/pointer.c
+++ b/examples/pointer.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <unistd.h>
#include <math.h>
+#include <assert.h>
#include <wayland-server.h>
#include <wayland-server-protocol.h>
#include <xkbcommon/xkbcommon.h>
@@ -16,19 +17,71 @@
#include <wlr/backend.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_keyboard.h>
+#include <wlr/types/wlr_output_layout.h>
#include <wlr/xcursor.h>
+#include <wlr/types/wlr_cursor.h>
#include <wlr/util/log.h>
#include "shared.h"
+#include "config.h"
#include "cat.h"
+struct sample_input_device {
+ struct wlr_input_device *device;
+ struct wl_list link;
+};
+
struct sample_state {
- struct wlr_xcursor *cursor;
+ struct compositor_state *compositor;
+ struct example_config *config;
+ struct wlr_xcursor *xcursor;
+ struct wlr_cursor *cursor;
double cur_x, cur_y;
float default_color[4];
float clear_color[4];
+ struct wlr_output_layout *layout;
+ struct wl_list devices;
+
+ struct wl_listener cursor_motion;
+ struct wl_listener cursor_motion_absolute;
+ struct wl_listener cursor_button;
+ struct wl_listener cursor_axis;
+
+ struct wl_listener touch_motion;
+ struct wl_listener touch_up;
+ struct wl_listener touch_down;
+ struct wl_listener touch_cancel;
+ list_t *touch_points;
+
+ struct wl_listener tablet_tool_axis;
+ struct wl_listener tablet_tool_proxmity;
+ struct wl_listener tablet_tool_tip;
+ struct wl_listener tablet_tool_button;
+};
+
+struct touch_point {
+ int32_t slot;
+ double x, y;
};
-static void handle_output_frame(struct output_state *output, struct timespec *ts) {
+static void warp_to_touch(struct sample_state *sample,
+ struct wlr_input_device *dev) {
+ if (sample->touch_points->length == 0) {
+ return;
+ }
+
+ double x = 0, y = 0;
+ for (size_t i = 0; i < sample->touch_points->length; ++i) {
+ struct touch_point *point = sample->touch_points->items[i];
+ x += point->x;
+ y += point->y;
+ }
+ x /= sample->touch_points->length;
+ y /= sample->touch_points->length;
+ wlr_cursor_warp_absolute(sample->cursor, dev, x, y);
+}
+
+static void handle_output_frame(struct output_state *output,
+ struct timespec *ts) {
struct compositor_state *state = output->compositor;
struct sample_state *sample = state->data;
struct wlr_output *wlr_output = output->output;
@@ -36,65 +89,178 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts
wlr_output_make_current(wlr_output);
glClearColor(sample->clear_color[0], sample->clear_color[1],
- sample->clear_color[2], sample->clear_color[3]);
+ sample->clear_color[2], sample->clear_color[3]);
glClear(GL_COLOR_BUFFER_BIT);
wlr_output_swap_buffers(wlr_output);
}
-static void handle_pointer_motion(struct pointer_state *pstate,
- double d_x, double d_y) {
- struct sample_state *state = pstate->compositor->data;
- state->cur_x += d_x;
- state->cur_y += d_y;
+static void configure_devices(struct sample_state *sample) {
+ struct sample_input_device *dev;
+ struct device_config *dc;
- struct wlr_xcursor_image *image = state->cursor->images[0];
+ // 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_box);
+ }
+ }
+ }
- struct output_state *output;
- wl_list_for_each(output, &pstate->compositor->outputs, link) {
- wlr_output_move_cursor(output->output,
- state->cur_x - image->hotspot_x,
- state->cur_y - image->hotspot_y);
+ struct output_state *ostate;
+ wl_list_for_each(ostate, &sample->compositor->outputs, link) {
+ wl_list_for_each(dc, &sample->config->devices, link) {
+ // configure device to output mappings
+ if (dc->mapped_output &&
+ strcmp(dc->mapped_output, ostate->output->name) == 0) {
+ wl_list_for_each(dev, &sample->devices, link) {
+ if (strcmp(dev->device->name, dc->name) == 0) {
+ wlr_cursor_map_input_to_output(sample->cursor,
+ dev->device, ostate->output);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void handle_output_add(struct output_state *ostate) {
+ struct sample_state *sample = ostate->compositor->data;
+ struct wlr_output *wlr_output = ostate->output;
+ struct wlr_xcursor_image *image = sample->xcursor->images[0];
+
+ // reset layout
+ wlr_output_layout_destroy(sample->layout);
+ sample->layout =
+ configure_layout(sample->config, &ostate->compositor->outputs);
+ wlr_cursor_attach_output_layout(sample->cursor, sample->layout);
+
+ // cursor configuration
+ char *mapped_output = sample->config->cursor.mapped_output;
+ if (mapped_output && strcmp(mapped_output, wlr_output->name) == 0) {
+ wlr_cursor_map_to_output(sample->cursor, wlr_output);
+ }
+
+ configure_devices(sample);
+
+ // TODO move to wlr_cursor
+ if (!wlr_output_set_cursor(wlr_output, image->buffer,
+ image->width, image->width, image->height)) {
+ wlr_log(L_DEBUG, "Failed to set hardware cursor");
+ return;
+ }
+ if (!wlr_output_move_cursor(wlr_output, 0, 0)) {
+ wlr_log(L_DEBUG, "Failed to move hardware cursor");
+ }
+}
+
+static void handle_output_remove(struct output_state *ostate) {
+ struct sample_state *sample = ostate->compositor->data;
+ wlr_output_layout_destroy(sample->layout);
+ sample->layout =
+ configure_layout(sample->config, &ostate->compositor->outputs);
+ wlr_cursor_attach_output_layout(sample->cursor, sample->layout);
+
+ configure_devices(sample);
+
+ 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);
}
}
-static void handle_pointer_motion_absolute(struct pointer_state *pstate,
- double x, double y) {
- struct sample_state *state = pstate->compositor->data;
- state->cur_x = x;
- state->cur_y = y;
+static void handle_output_resolution(struct compositor_state *state,
+ struct output_state *ostate) {
+ struct sample_state *sample = ostate->compositor->data;
+ wlr_output_layout_destroy(sample->layout);
+ sample->layout =
+ configure_layout(sample->config, &ostate->compositor->outputs);
+ wlr_cursor_attach_output_layout(sample->cursor, sample->layout);
+}
+
+static void handle_input_add(struct compositor_state *state,
+ struct wlr_input_device *device) {
+ struct sample_state *sample = state->data;
+
+ if (device->type == WLR_INPUT_DEVICE_POINTER ||
+ device->type == WLR_INPUT_DEVICE_TOUCH ||
+ device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
+ struct sample_input_device *s_device;
+ s_device = calloc(1, sizeof(struct sample_input_device));
+ s_device->device = device;
- struct wlr_xcursor_image *image = state->cursor->images[0];
+ wl_list_insert(&sample->devices, &s_device->link);
+ wlr_cursor_attach_input_device(sample->cursor, device);
+ configure_devices(sample);
+ }
+}
+
+static void handle_input_remove(struct compositor_state *state,
+ struct wlr_input_device *device) {
+ struct sample_state *sample = state->data;
+ struct sample_input_device *s_device, *tmp = NULL;
+ wl_list_for_each_safe(s_device, tmp, &sample->devices, link) {
+ if (s_device->device == device) {
+ wl_list_remove(&s_device->link);
+ free(s_device);
+ }
+ }
+}
+
+static void handle_cursor_motion(struct wl_listener *listener, void *data) {
+ struct sample_state *sample =
+ wl_container_of(listener, sample, cursor_motion);
+ struct wlr_event_pointer_motion *event = data;
+ wlr_cursor_move(sample->cursor, event->device, event->delta_x,
+ event->delta_y);
+}
+
+static void handle_cursor_motion_absolute(struct wl_listener *listener,
+ void *data) {
+ struct sample_state *sample =
+ wl_container_of(listener, sample, cursor_motion_absolute);
+ struct wlr_event_pointer_motion_absolute *event = data;
+
+ sample->cur_x = event->x_mm;
+ sample->cur_y = event->y_mm;
+
+ struct wlr_xcursor_image *image = sample->xcursor->images[0];
struct output_state *output;
- wl_list_for_each(output, &pstate->compositor->outputs, link) {
+ wl_list_for_each(output, &sample->compositor->outputs, link) {
wlr_output_move_cursor(output->output,
- state->cur_x - image->hotspot_x,
- state->cur_y - image->hotspot_y);
+ sample->cur_x - image->hotspot_x,
+ sample->cur_y - image->hotspot_y);
}
}
-static void handle_pointer_button(struct pointer_state *pstate,
- uint32_t button, enum wlr_button_state state) {
- struct sample_state *sample = pstate->compositor->data;
+static void handle_cursor_button(struct wl_listener *listener, void *data) {
+ struct sample_state *sample =
+ wl_container_of(listener, sample, cursor_button);
+ struct wlr_event_pointer_button *event = data;
+
float (*color)[4];
- if (state == WLR_BUTTON_RELEASED) {
+ if (event->state == WLR_BUTTON_RELEASED) {
color = &sample->default_color;
+ memcpy(&sample->clear_color, color, sizeof(*color));
} else {
float red[4] = { 0.25f, 0.25f, 0.25f, 1 };
- red[button % 3] = 1;
+ red[event->button % 3] = 1;
color = &red;
+ memcpy(&sample->clear_color, color, sizeof(*color));
}
- memcpy(&sample->clear_color, color, sizeof(*color));
}
-static void handle_pointer_axis(struct pointer_state *pstate,
- enum wlr_axis_source source,
- enum wlr_axis_orientation orientation,
- double delta) {
- struct sample_state *sample = pstate->compositor->data;
+static void handle_cursor_axis(struct wl_listener *listener, void *data) {
+ struct sample_state *sample =
+ wl_container_of(listener, sample, cursor_axis);
+ struct wlr_event_pointer_axis *event = data;
+
for (size_t i = 0; i < 3; ++i) {
- sample->default_color[i] += delta > 0 ? -0.05f : 0.05f;
+ sample->default_color[i] += event->delta > 0 ? -0.05f : 0.05f;
if (sample->default_color[i] > 1.0f) {
sample->default_color[i] = 1.0f;
}
@@ -102,52 +268,143 @@ static void handle_pointer_axis(struct pointer_state *pstate,
sample->default_color[i] = 0.0f;
}
}
+
memcpy(&sample->clear_color, &sample->default_color,
sizeof(sample->clear_color));
}
-static void handle_output_add(struct output_state *ostate) {
- struct sample_state *state = ostate->compositor->data;
- struct wlr_output *wlr_output = ostate->output;
- struct wlr_xcursor_image *image = state->cursor->images[0];
- if (!wlr_output_set_cursor(wlr_output, image->buffer,
- image->width, image->width, image->height)) {
- wlr_log(L_DEBUG, "Failed to set hardware cursor");
- return;
+static void handle_touch_up(struct wl_listener *listener, void *data) {
+ struct sample_state *sample = wl_container_of(listener, sample, touch_up);
+ struct wlr_event_touch_up *event = data;
+ for (size_t i = 0; i < sample->touch_points->length; ++i) {
+ struct touch_point *point = sample->touch_points->items[i];
+ if (point->slot == event->slot) {
+ list_del(sample->touch_points, i);
+ break;
+ }
}
- if (!wlr_output_move_cursor(wlr_output, 0, 0)) {
- wlr_log(L_DEBUG, "Failed to move hardware cursor");
+
+ warp_to_touch(sample, event->device);
+}
+
+static void handle_touch_down(struct wl_listener *listener, void *data) {
+ struct sample_state *sample = wl_container_of(listener, sample, touch_down);
+ struct wlr_event_touch_down *event = data;
+ struct touch_point *point = calloc(1, sizeof(struct touch_point));
+ point->slot = event->slot;
+ point->x = event->x_mm / event->width_mm;
+ point->y = event->y_mm / event->height_mm;
+ if (list_add(sample->touch_points, point) == -1) {
+ free(point);
+ }
+
+ warp_to_touch(sample, event->device);
+}
+
+static void handle_touch_motion(struct wl_listener *listener, void *data) {
+ struct sample_state *sample =
+ wl_container_of(listener, sample, touch_motion);
+ struct wlr_event_touch_motion *event = data;
+ for (size_t i = 0; i < sample->touch_points->length; ++i) {
+ struct touch_point *point = sample->touch_points->items[i];
+ if (point->slot == event->slot) {
+ point->x = event->x_mm / event->width_mm;
+ point->y = event->y_mm / event->height_mm;
+ break;
+ }
+ }
+
+ warp_to_touch(sample, event->device);
+}
+
+static void handle_touch_cancel(struct wl_listener *listener, void *data) {
+ wlr_log(L_DEBUG, "TODO: touch cancel");
+}
+
+static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) {
+ struct sample_state *sample =
+ wl_container_of(listener, sample, tablet_tool_axis);
+ struct wlr_event_tablet_tool_axis *event = data;
+ if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
+ (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
+ wlr_cursor_warp_absolute(sample->cursor, event->device,
+ event->x_mm / event->width_mm, event->y_mm / event->height_mm);
}
}
int main(int argc, char *argv[]) {
struct sample_state state = {
.default_color = { 0.25f, 0.25f, 0.25f, 1 },
- .clear_color = { 0.25f, 0.25f, 0.25f, 1 }
+ .clear_color = { 0.25f, 0.25f, 0.25f, 1 },
+ .touch_points = list_create(),
};
+
+ state.config = parse_args(argc, argv);
+ state.cursor = wlr_cursor_create();
+ wlr_cursor_map_to_region(state.cursor, state.config->cursor.mapped_box);
+ wl_list_init(&state.devices);
+
+ // pointer events
+ wl_signal_add(&state.cursor->events.motion, &state.cursor_motion);
+ state.cursor_motion.notify = handle_cursor_motion;
+
+ wl_signal_add(&state.cursor->events.motion_absolute,
+ &state.cursor_motion_absolute);
+ state.cursor_motion_absolute.notify = handle_cursor_motion_absolute;
+
+ wl_signal_add(&state.cursor->events.button, &state.cursor_button);
+ state.cursor_button.notify = handle_cursor_button;
+
+ wl_signal_add(&state.cursor->events.axis, &state.cursor_axis);
+ state.cursor_axis.notify = handle_cursor_axis;
+
+ // touch events
+ wl_signal_add(&state.cursor->events.touch_up, &state.touch_up);
+ state.touch_up.notify = handle_touch_up;
+
+ wl_signal_add(&state.cursor->events.touch_down, &state.touch_down);
+ state.touch_down.notify = handle_touch_down;
+
+ wl_signal_add(&state.cursor->events.touch_motion, &state.touch_motion);
+ state.touch_motion.notify = handle_touch_motion;
+
+ wl_signal_add(&state.cursor->events.touch_cancel, &state.touch_cancel);
+ state.touch_cancel.notify = handle_touch_cancel;
+
+ // tool events
+ wl_signal_add(&state.cursor->events.tablet_tool_axis,
+ &state.tablet_tool_axis);
+ state.tablet_tool_axis.notify = handle_tablet_tool_axis;
+
struct compositor_state compositor = { 0 };
compositor.data = &state;
compositor.output_add_cb = handle_output_add;
+ compositor.output_remove_cb = handle_output_remove;
+ compositor.output_resolution_cb = handle_output_resolution;
compositor.output_frame_cb = handle_output_frame;
- compositor.pointer_motion_cb = handle_pointer_motion;
- compositor.pointer_motion_absolute_cb = handle_pointer_motion_absolute;
- compositor.pointer_button_cb = handle_pointer_button;
- compositor.pointer_axis_cb = handle_pointer_axis;
+ compositor.input_add_cb = handle_input_add;
+ compositor.input_remove_cb = handle_input_remove;
+
+ state.compositor = &compositor;
struct wlr_xcursor_theme *theme = wlr_xcursor_theme_load("default", 16);
if (!theme) {
wlr_log(L_ERROR, "Failed to load cursor theme");
return 1;
}
- state.cursor = wlr_xcursor_theme_get_cursor(theme, "left_ptr");
- if (!state.cursor) {
+ state.xcursor = wlr_xcursor_theme_get_cursor(theme, "left_ptr");
+ if (!state.xcursor) {
wlr_log(L_ERROR, "Failed to load left_ptr cursor");
return 1;
}
+ wlr_cursor_set_xcursor(state.cursor, state.xcursor);
+
compositor_init(&compositor);
wl_display_run(compositor.display);
compositor_fini(&compositor);
wlr_xcursor_theme_destroy(theme);
+ example_config_destroy(state.config);
+ wlr_cursor_destroy(state.cursor);
}
diff --git a/examples/rotation.c b/examples/rotation.c
index 1dcbc80f..2596e492 100644
--- a/examples/rotation.c
+++ b/examples/rotation.c
@@ -19,6 +19,7 @@
#include <wlr/util/log.h>
#include <math.h>
#include "shared.h"
+#include "config.h"
#include "cat.h"
struct sample_state {
diff --git a/examples/shared.c b/examples/shared.c
index f37140cf..0346c96d 100644
--- a/examples/shared.c
+++ b/examples/shared.c
@@ -18,145 +18,6 @@
#include <wlr/util/log.h>
#include "shared.h"
-static void usage(const char *name, int ret) {
- fprintf(stderr,
- "usage: %s [-d <name> [-r <rotation> | -f]]*\n"
- "\n"
- " -o <output> The name of the DRM display. e.g. DVI-I-1.\n"
- " -r <rotation> The rotation counter clockwise. Valid values are 90, 180, 270.\n"
- " -x <position> The X-axis coordinate position of this output in the layout.\n"
- " -y <position> The Y-axis coordinate position of this output in the layout.\n"
- " -f Flip the output along the vertical axis.\n", name);
-
- exit(ret);
-}
-
-struct example_config *parse_args(int argc, char *argv[]) {
- struct example_config *config = calloc(1, sizeof(struct example_config));
- wl_list_init(&config->outputs);
- struct output_config *oc = NULL;
-
- int c;
- while ((c = getopt(argc, argv, "o:r:x:y:fh")) != -1) {
- switch (c) {
- case 'o':
- oc = calloc(1, sizeof(*oc));
- oc->name = optarg;
- oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
- wl_list_insert(&config->outputs, &oc->link);
- break;
- case 'r':
- if (!oc) {
- fprintf(stderr, "You must specify an output first\n");
- usage(argv[0], 1);
- }
-
- if (oc->transform != WL_OUTPUT_TRANSFORM_NORMAL
- && oc->transform != WL_OUTPUT_TRANSFORM_FLIPPED) {
- fprintf(stderr, "Rotation for %s already specified\n", oc->name);
- usage(argv[0], 1);
- }
-
- if (strcmp(optarg, "90") == 0) {
- oc->transform += WL_OUTPUT_TRANSFORM_90;
- } else if (strcmp(optarg, "180") == 0) {
- oc->transform += WL_OUTPUT_TRANSFORM_180;
- } else if (strcmp(optarg, "270") == 0) {
- oc->transform += WL_OUTPUT_TRANSFORM_270;
- } else {
- fprintf(stderr, "Invalid rotation '%s'\n", optarg);
- usage(argv[0], 1);
- }
- break;
- case 'x':
- if (!oc) {
- fprintf(stderr, "You must specify an output first\n");
- usage(argv[0], 1);
- }
- oc->x = strtol(optarg, NULL, 0);
- break;
- case 'y':
- if (!oc) {
- fprintf(stderr, "You must specify an output first\n");
- usage(argv[0], 1);
- }
- oc->y = strtol(optarg, NULL, 0);
- break;
- case 'f':
- if (!oc) {
- fprintf(stderr, "You must specify an output first\n");
- usage(argv[0], 1);
- }
-
- if (oc->transform >= WL_OUTPUT_TRANSFORM_FLIPPED) {
- fprintf(stderr, "Flip for %s already specified\n", oc->name);
- usage(argv[0], 1);
- }
-
- oc->transform += WL_OUTPUT_TRANSFORM_FLIPPED;
- break;
- case 'h':
- case '?':
- usage(argv[0], c != 'h');
- }
- }
-
- return config;
-}
-
-void example_config_destroy(struct example_config *config) {
- struct output_config *oc, *tmp = NULL;
- wl_list_for_each_safe(oc, tmp, &config->outputs, link) {
- free(oc);
- }
- free(config);
-}
-
-struct wlr_output_layout *configure_layout(struct example_config *config, struct wl_list *outputs) {
- struct wlr_output_layout *layout = wlr_output_layout_init();
- int max_x = INT_MIN;
- int max_x_y = INT_MIN; // y value for the max_x output
-
- // first add all the configured outputs
- struct output_state *output;
- wl_list_for_each(output, outputs, link) {
- struct output_config *conf;
- wl_list_for_each(conf, &config->outputs, link) {
- if (strcmp(conf->name, output->output->name) == 0) {
- wlr_output_layout_add(layout, output->output,
- conf->x, conf->y);
- wlr_output_transform(output->output, conf->transform);
- int width, height;
- wlr_output_effective_resolution(output->output, &width, &height);
- if (conf->x + width > max_x) {
- max_x = conf->x + width;
- max_x_y = conf->y;
- }
- break;
- }
- }
- }
-
- if (max_x == INT_MIN) {
- // couldn't find a configured output
- max_x = 0;
- max_x_y = 0;
- }
-
- // now add all the other configured outputs in a sensible position
- wl_list_for_each(output, outputs, link) {
- if (wlr_output_layout_get(layout, output->output)) {
- continue;
- }
- wlr_output_layout_add(layout, output->output, max_x, max_x_y);
- int width, height;
- wlr_output_effective_resolution(output->output, &width, &height);
- max_x += width;
- }
-
- return layout;
-}
-
static void keyboard_led_update(struct keyboard_state *kbstate) {
uint32_t leds = 0;
@@ -441,6 +302,10 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
default:
break;
}
+
+ if (state->input_add_cb) {
+ state->input_add_cb(state, device);
+ }
}
static void keyboard_remove(struct wlr_input_device *device, struct compositor_state *state) {
@@ -536,6 +401,11 @@ static void tablet_pad_remove(struct wlr_input_device *device, struct compositor
static void input_remove_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct compositor_state *state = wl_container_of(listener, state, input_remove);
+
+ if (state->input_remove_cb) {
+ state->input_remove_cb(state, device);
+ }
+
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
keyboard_remove(device, state);
diff --git a/examples/shared.h b/examples/shared.h
index d8007753..7cf4db63 100644
--- a/examples/shared.h
+++ b/examples/shared.h
@@ -12,24 +12,6 @@
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_input_device.h>
-struct output_config {
- char *name;
- enum wl_output_transform transform;
- int x, y;
- struct wl_list link;
-};
-
-struct example_config {
- struct wl_list outputs;
-};
-
-struct example_config *parse_args(int argc, char *argv[]);
-
-void example_config_destroy(struct example_config *config);
-
-struct wlr_output_layout *configure_layout(struct example_config *config,
- struct wl_list *outputs);
-
struct output_state {
struct compositor_state *compositor;
struct wlr_output *output;
@@ -93,6 +75,10 @@ struct tablet_pad_state {
};
struct compositor_state {
+ void (*input_add_cb)(struct compositor_state *compositor,
+ struct wlr_input_device *device);
+ void (*input_remove_cb)(struct compositor_state *compositor,
+ struct wlr_input_device *device);
void (*output_add_cb)(struct output_state *s);
void (*keyboard_add_cb)(struct keyboard_state *s);
void (*output_frame_cb)(struct output_state *s, struct timespec *ts);
diff --git a/examples/touch.c b/examples/touch.c
index 7e8d9827..40fc4ca2 100644
--- a/examples/touch.c
+++ b/examples/touch.c
@@ -61,7 +61,7 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts
static void handle_touch_down(struct touch_state *tstate, int32_t slot,
double x, double y, double width, double height) {
struct sample_state *sample = tstate->compositor->data;
- struct touch_point *point = calloc(1, sizeof(struct touch_state));
+ struct touch_point *point = calloc(1, sizeof(struct touch_point));
point->slot = slot;
point->x = x / width;
point->y = y / height;
diff --git a/examples/wlr-example.ini.example b/examples/wlr-example.ini.example
new file mode 100644
index 00000000..1698e0c6
--- /dev/null
+++ b/examples/wlr-example.ini.example
@@ -0,0 +1,57 @@
+# Configuration
+# -------------
+# Some examples will read a configuration file. Not all examples will use all of
+# the configuration options. The configuration file will be loaded from
+# `wlr-example.ini` from the current directory or the path can be specified by the
+# `-C` option given on the command line.
+#
+# Output configuration
+# ~~~~~~~~~~~~~~~~~~~~
+# Each output is specified in a section named [output:{NAME}] where NAME is the
+# drm name for this output.
+#
+# Value "x" specifies the x-coordinate in the output layout.
+#
+# Value "y" specifies the y-coordinate in the output layout.
+#
+# Value "rotate" specifies output rotation and can be 90, 180, 270, flipped,
+# flipped-90, flipped-180, or flipped-270
+[output:HDMI-A-1]
+x=3000
+y=0
+rotate=90
+
+[output:DP-1]
+x=0
+y=0
+rotate=270
+
+[output:DVI-D-1]
+x=1080
+y=232
+
+# Cursor Configuration
+# ~~~~~~~~~~~~~~~~~~~~
+# 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
+# ~~~~~~~~~~~~~~~~~~~~
+# Each device is specified in a section named [device:{NAME}] where NAME is the
+# 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_box.h b/include/wlr/types/wlr_box.h
new file mode 100644
index 00000000..e2b1ab4e
--- /dev/null
+++ b/include/wlr/types/wlr_box.h
@@ -0,0 +1,20 @@
+#ifndef _WLR_TYPES_GEOMETRY_H
+#define _WLR_TYPES_GEOMETRY_H
+#include <stdbool.h>
+
+struct wlr_box {
+ int x, y;
+ int width, height;
+};
+
+void wlr_box_closest_point(struct wlr_box *box, double x, double y,
+ double *dest_x, double *dest_y);
+
+bool wlr_box_intersection(struct wlr_box *box_a,
+ struct wlr_box *box_b, struct wlr_box **dest);
+
+bool wlr_box_contains_point(struct wlr_box *box, double x, double y);
+
+bool wlr_box_empty(struct wlr_box *box);
+
+#endif
diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h
new file mode 100644
index 00000000..5fc0ec76
--- /dev/null
+++ b/include/wlr/types/wlr_cursor.h
@@ -0,0 +1,112 @@
+#ifndef _WLR_TYPES_CURSOR_H
+#define _WLR_TYPES_CURSOR_H
+#include <wayland-server.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/xcursor.h>
+
+struct wlr_cursor_state;
+
+struct wlr_cursor {
+ struct wlr_cursor_state *state;
+ int x, y;
+
+ struct {
+ struct wl_signal motion;
+ struct wl_signal motion_absolute;
+ struct wl_signal button;
+ struct wl_signal axis;
+
+ struct wl_signal touch_up;
+ struct wl_signal touch_down;
+ struct wl_signal touch_motion;
+ struct wl_signal touch_cancel;
+
+ struct wl_signal tablet_tool_axis;
+ struct wl_signal tablet_tool_proximity;
+ struct wl_signal tablet_tool_tip;
+ struct wl_signal tablet_tool_button;
+ } events;
+};
+
+struct wlr_cursor *wlr_cursor_create();
+
+void wlr_cursor_destroy(struct wlr_cursor *cur);
+
+void wlr_cursor_set_xcursor(struct wlr_cursor *cur, struct wlr_xcursor *xcur);
+
+/**
+ * Warp the cursor to the given x and y in layout coordinates. If x and y are
+ * out of the layout boundaries or constraints, no warp will happen.
+ *
+ * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
+ * device mapping constraints will be ignored.
+ *
+ * Returns true when the mouse warp was successful.
+ */
+bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
+ double x, double y);
+
+void wlr_cursor_warp_absolute(struct wlr_cursor *cur,
+ struct wlr_input_device *dev, double x_mm, double y_mm);
+
+/**
+ * Move the cursor in the direction of the given x and y coordinates.
+ *
+ * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
+ * device mapping constraints will be ignored.
+ */
+void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev,
+ double delta_x, double delta_y);
+
+/**
+ * Attaches this input device to this cursor. The input device must be one of:
+ *
+ * - WLR_INPUT_DEVICE_POINTER
+ * - WLR_INPUT_DEVICE_TOUCH
+ * - WLR_INPUT_DEVICE_TABLET_TOOL
+ */
+void wlr_cursor_attach_input_device(struct wlr_cursor *cur,
+ struct wlr_input_device *dev);
+
+void wlr_cursor_detach_input_device(struct wlr_cursor *cur,
+ struct wlr_input_device *dev);
+/**
+ * Uses the given layout to establish the boundaries and movement semantics of
+ * this cursor. Cursors without an output layout allow infinite movement in any
+ * direction and do not support absolute input events.
+ */
+void wlr_cursor_attach_output_layout(struct wlr_cursor *cur,
+ struct wlr_output_layout *l);
+
+/**
+ * Attaches this cursor to the given output, which must be among the outputs in
+ * the current output_layout for this cursor. This call is invalid for a cursor
+ * without an associated output layout.
+ */
+void wlr_cursor_map_to_output(struct wlr_cursor *cur,
+ struct wlr_output *output);
+
+/**
+ * Maps all input from a specific input device to a given output. The input
+ * device must be attached to this cursor and the output must be among the
+ * outputs in the attached output layout.
+ */
+void wlr_cursor_map_input_to_output(struct wlr_cursor *cur,
+ struct wlr_input_device *dev, struct wlr_output *output);
+
+/**
+ * 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_box *box);
+
+/**
+ * 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_box *box);
+
+#endif
diff --git a/include/wlr/types/wlr_input_device.h b/include/wlr/types/wlr_input_device.h
index 642892ff..5a41ce9d 100644
--- a/include/wlr/types/wlr_input_device.h
+++ b/include/wlr/types/wlr_input_device.h
@@ -40,6 +40,10 @@ struct wlr_input_device {
struct wlr_tablet_pad *tablet_pad;
};
+ struct {
+ struct wl_signal destroy;
+ } events;
+
void *data;
};
diff --git a/include/wlr/types/wlr_output_layout.h b/include/wlr/types/wlr_output_layout.h
index f6a1efdd..b1253eb1 100644
--- a/include/wlr/types/wlr_output_layout.h
+++ b/include/wlr/types/wlr_output_layout.h
@@ -4,14 +4,20 @@
#include <wayland-util.h>
#include <stdbool.h>
+struct wlr_output_layout_state;
+
struct wlr_output_layout {
- struct wl_list outputs;
+ struct wl_list outputs;
+ struct wlr_output_layout_state *state;
};
+struct wlr_output_layout_output_state;
+
struct wlr_output_layout_output {
- struct wlr_output *output;
- int x, y;
- struct wl_list link;
+ struct wlr_output *output;
+ int x, y;
+ struct wl_list link;
+ struct wlr_output_layout_output_state *state;
};
struct wlr_output_layout *wlr_output_layout_init();
@@ -38,7 +44,7 @@ void wlr_output_layout_remove(struct wlr_output_layout *layout,
* coordinates relative to the given reference output.
*/
void wlr_output_layout_output_coords(struct wlr_output_layout *layout,
- struct wlr_output *reference, int *x, int *y);
+ struct wlr_output *reference, double *x, double *y);
bool wlr_output_layout_contains_point(struct wlr_output_layout *layout,
struct wlr_output *reference, int x, int y);
@@ -46,4 +52,19 @@ bool wlr_output_layout_contains_point(struct wlr_output_layout *layout,
bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
struct wlr_output *reference, int x1, int y1, int x2, int y2);
+/**
+ * Get the closest point on this layout from the given point from the reference
+ * output. If reference is NULL, gets the closest point from the entire layout.
+ */
+void wlr_output_layout_closest_point(struct wlr_output_layout *layout,
+ struct wlr_output *reference, double x, double y, double *dest_x,
+ double *dest_y);
+
+/**
+ * Get the box of the layout for the given reference output. If `reference`
+ * is NULL, the box will be for the extents of the entire layout.
+ */
+struct wlr_box *wlr_output_layout_get_box(
+ struct wlr_output_layout *layout, struct wlr_output *reference);
+
#endif
diff --git a/include/wlr/types/wlr_pointer.h b/include/wlr/types/wlr_pointer.h
index 13a2d045..9153963a 100644
--- a/include/wlr/types/wlr_pointer.h
+++ b/include/wlr/types/wlr_pointer.h
@@ -20,12 +20,14 @@ struct wlr_pointer {
};
struct wlr_event_pointer_motion {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
double delta_x, delta_y;
};
struct wlr_event_pointer_motion_absolute {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
double x_mm, y_mm;
@@ -33,6 +35,7 @@ struct wlr_event_pointer_motion_absolute {
};
struct wlr_event_pointer_button {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
uint32_t button;
@@ -52,6 +55,7 @@ enum wlr_axis_orientation {
};
struct wlr_event_pointer_axis {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
enum wlr_axis_source source;
diff --git a/include/wlr/types/wlr_tablet_tool.h b/include/wlr/types/wlr_tablet_tool.h
index dcb9c191..9090828a 100644
--- a/include/wlr/types/wlr_tablet_tool.h
+++ b/include/wlr/types/wlr_tablet_tool.h
@@ -32,6 +32,7 @@ enum wlr_tablet_tool_axes {
};
struct wlr_event_tablet_tool_axis {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
uint32_t updated_axes;
@@ -51,9 +52,10 @@ enum wlr_tablet_tool_proximity_state {
};
struct wlr_event_tablet_tool_proximity {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
- double x, y;
+ double x_mm, y_mm;
double width_mm, height_mm;
enum wlr_tablet_tool_proximity_state state;
};
@@ -64,14 +66,16 @@ enum wlr_tablet_tool_tip_state {
};
struct wlr_event_tablet_tool_tip {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
- double x, y;
+ double x_mm, y_mm;
double width_mm, height_mm;
enum wlr_tablet_tool_tip_state state;
};
struct wlr_event_tablet_tool_button {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
uint32_t button;
diff --git a/include/wlr/types/wlr_touch.h b/include/wlr/types/wlr_touch.h
index 93069fcb..1a27cad3 100644
--- a/include/wlr/types/wlr_touch.h
+++ b/include/wlr/types/wlr_touch.h
@@ -19,6 +19,7 @@ struct wlr_touch {
};
struct wlr_event_touch_down {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
int32_t slot;
@@ -27,12 +28,14 @@ struct wlr_event_touch_down {
};
struct wlr_event_touch_up {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
int32_t slot;
};
struct wlr_event_touch_motion {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
int32_t slot;
@@ -41,6 +44,7 @@ struct wlr_event_touch_motion {
};
struct wlr_event_touch_cancel {
+ struct wlr_input_device *device;
uint32_t time_sec;
uint64_t time_usec;
int32_t slot;
diff --git a/types/meson.build b/types/meson.build
index 83b4647c..56390475 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -6,6 +6,7 @@ lib_wlr_types = static_library('wlr_types', files(
'wlr_output.c',
'wlr_output_layout.c',
'wlr_pointer.c',
+ 'wlr_cursor.c',
'wlr_region.c',
'wlr_seat.c',
'wlr_surface.c',
@@ -15,6 +16,7 @@ lib_wlr_types = static_library('wlr_types', files(
'wlr_xdg_shell_v6.c',
'wlr_wl_shell.c',
'wlr_compositor.c',
+ 'wlr_box.c',
),
include_directories: wlr_inc,
dependencies: [wayland_server, pixman, wlr_protos])
diff --git a/types/wlr_box.c b/types/wlr_box.c
new file mode 100644
index 00000000..7e981833
--- /dev/null
+++ b/types/wlr_box.c
@@ -0,0 +1,67 @@
+#include <limits.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdbool.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/util/log.h>
+
+void wlr_box_closest_point(struct wlr_box *box, double x, double y,
+ double *dest_x, double *dest_y) {
+ // find the closest x point
+ if (x < box->x) {
+ *dest_x = box->x;
+ } else if (x > box->x + box->width) {
+ *dest_x = box->x + box->width;
+ } else {
+ *dest_x = x;
+ }
+
+ // find closest y point
+ if (y < box->y) {
+ *dest_y = box->y;
+ } else if (y > box->y + box->height) {
+ *dest_y = box->y + box->height;
+ } else {
+ *dest_y = y;
+ }
+}
+
+bool wlr_box_empty(struct wlr_box *box) {
+ return box == NULL || box->width <= 0 || box->height <= 0;
+}
+
+bool wlr_box_intersection(struct wlr_box *box_a,
+ struct wlr_box *box_b, struct wlr_box **box_dest) {
+ struct wlr_box *dest = *box_dest;
+ bool a_empty = wlr_box_empty(box_a);
+ bool b_empty = wlr_box_empty(box_b);
+
+ if (a_empty || b_empty) {
+ dest->x = 0;
+ dest->y = 0;
+ dest->width = -100;
+ dest->height = -100;
+ return false;
+ }
+
+ int x1 = fmax(box_a->x, box_b->x);
+ int y1 = fmax(box_a->y, box_b->y);
+ int x2 = fmin(box_a->x + box_a->width, box_b->x + box_b->width);
+ int y2 = fmin(box_a->y + box_a->height, box_b->y + box_b->height);
+
+ dest->x = x1;
+ dest->y = y1;
+ dest->width = x2 - x1;
+ dest->height = y2 - y1;
+
+ return !wlr_box_empty(dest);
+}
+
+bool wlr_box_contains_point(struct wlr_box *box, double x, double y) {
+ if (wlr_box_empty(box)) {
+ return false;
+ } else {
+ return x >= box->x && x <= box->x + box->width &&
+ y >= box->y && y <= box->y + box->height;
+ }
+}
diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c
new file mode 100644
index 00000000..476af619
--- /dev/null
+++ b/types/wlr_cursor.c
@@ -0,0 +1,481 @@
+#include <wlr/types/wlr_cursor.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+#include <wlr/util/log.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_input_device.h>
+
+struct wlr_cursor_device {
+ struct wlr_cursor *cursor;
+ struct wlr_input_device *device;
+ struct wl_list link;
+ struct wlr_output *mapped_output;
+ struct wlr_box *mapped_box;
+
+ struct wl_listener motion;
+ struct wl_listener motion_absolute;
+ struct wl_listener button;
+ struct wl_listener axis;
+
+ struct wl_listener touch_down;
+ struct wl_listener touch_up;
+ struct wl_listener touch_motion;
+ struct wl_listener touch_cancel;
+
+ struct wl_listener tablet_tool_axis;
+ struct wl_listener tablet_tool_proximity;
+ struct wl_listener tablet_tool_tip;
+ struct wl_listener tablet_tool_button;
+
+ struct wl_listener destroy;
+};
+
+struct wlr_cursor_state {
+ struct wl_list devices;
+ struct wlr_output_layout *layout;
+ struct wlr_xcursor *xcursor;
+ struct wlr_output *mapped_output;
+ struct wlr_box *mapped_box;
+};
+
+struct wlr_cursor *wlr_cursor_create() {
+ struct wlr_cursor *cur = calloc(1, sizeof(struct wlr_cursor));
+ if (!cur) {
+ wlr_log(L_ERROR, "Failed to allocate wlr_cursor");
+ return NULL;
+ }
+
+ cur->state = calloc(1, sizeof(struct wlr_cursor_state));
+ if (!cur->state) {
+ wlr_log(L_ERROR, "Failed to allocate wlr_cursor_state");
+ free(cur);
+ return NULL;
+ }
+
+ cur->state->mapped_output = NULL;
+
+ wl_list_init(&cur->state->devices);
+
+ // pointer signals
+ wl_signal_init(&cur->events.motion);
+ wl_signal_init(&cur->events.motion_absolute);
+ wl_signal_init(&cur->events.button);
+ wl_signal_init(&cur->events.axis);
+
+ // touch signals
+ wl_signal_init(&cur->events.touch_up);
+ wl_signal_init(&cur->events.touch_down);
+ wl_signal_init(&cur->events.touch_motion);
+ wl_signal_init(&cur->events.touch_cancel);
+
+ // tablet tool signals
+ wl_signal_init(&cur->events.tablet_tool_tip);
+ wl_signal_init(&cur->events.tablet_tool_axis);
+ wl_signal_init(&cur->events.tablet_tool_button);
+ wl_signal_init(&cur->events.tablet_tool_proximity);
+
+ cur->x = 100;
+ cur->y = 100;
+
+ return cur;
+}
+
+void wlr_cursor_destroy(struct wlr_cursor *cur) {
+ struct wlr_cursor_device *device, *device_tmp = NULL;
+ wl_list_for_each_safe(device, device_tmp, &cur->state->devices, link) {
+ wl_list_remove(&device->link);
+ free(device);
+ }
+
+ free(cur);
+}
+
+void wlr_cursor_set_xcursor(struct wlr_cursor *cur, struct wlr_xcursor *xcur) {
+ cur->state->xcursor = xcur;
+}
+
+static struct wlr_cursor_device *get_cursor_device(struct wlr_cursor *cur,
+ struct wlr_input_device *device) {
+ struct wlr_cursor_device *c_device, *ret = NULL;
+ wl_list_for_each(c_device, &cur->state->devices, link) {
+ if (c_device->device == device) {
+ ret = c_device;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void wlr_cursor_warp_unchecked(struct wlr_cursor *cur,
+ double x, double y) {
+ assert(cur->state->layout);
+ int hotspot_x = 0;
+ int hotspot_y = 0;
+
+ if (cur->state->xcursor && cur->state->xcursor->image_count > 0) {
+ struct wlr_xcursor_image *image = cur->state->xcursor->images[0];
+ hotspot_x = image->hotspot_x;
+ hotspot_y = image->hotspot_y;
+ }
+
+
+ struct wlr_output_layout_output *l_output;
+ wl_list_for_each(l_output, &cur->state->layout->outputs, link) {
+ double output_x = x;
+ double output_y = y;
+
+ wlr_output_layout_output_coords(cur->state->layout,
+ l_output->output, &output_x, &output_y);
+ wlr_output_move_cursor(l_output->output, output_x - hotspot_x,
+ output_y - hotspot_y);
+ }
+
+ cur->x = x;
+ cur->y = y;
+}
+
+/**
+ * Get the most specific mapping box for the device in this order:
+ *
+ * 1. device geometry mapping
+ * 2. device output mapping
+ * 3. cursor geometry mapping
+ * 4. cursor output mapping
+ *
+ * Absolute movement for touch and pen devices will be relative to this box and
+ * pointer movement will be constrained to this box.
+ *
+ * If none of these are set, returns NULL and absolute movement should be
+ * relative to the extents of the layout.
+ */
+static struct wlr_box *get_mapping(struct wlr_cursor *cur,
+ struct wlr_input_device *dev) {
+ assert(cur->state->layout);
+ struct wlr_cursor_device *c_device = get_cursor_device(cur, dev);
+
+ if (c_device) {
+ if (c_device->mapped_box) {
+ return c_device->mapped_box;
+ }
+ if (c_device->mapped_output) {
+ return wlr_output_layout_get_box(cur->state->layout,
+ c_device->mapped_output);
+ }
+ }
+
+ if (cur->state->mapped_box) {
+ return cur->state->mapped_box;
+ }
+ if (cur->state->mapped_output) {
+ return wlr_output_layout_get_box(cur->state->layout,
+ cur->state->mapped_output);
+ }
+
+ return NULL;
+}
+
+bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
+ double x, double y) {
+ assert(cur->state->layout);
+ bool result = false;
+
+ struct wlr_box *mapping = get_mapping(cur, dev);
+
+ if (mapping) {
+ if (wlr_box_contains_point(mapping, x, y)) {
+ wlr_cursor_warp_unchecked(cur, x, y);
+ result = true;
+ }
+ } else if (wlr_output_layout_contains_point(cur->state->layout, NULL,
+ x, y)) {
+ wlr_cursor_warp_unchecked(cur, x, y);
+ result = true;
+ }
+
+ return result;
+}
+
+void wlr_cursor_warp_absolute(struct wlr_cursor *cur,
+ struct wlr_input_device *dev, double x_mm, double y_mm) {
+ assert(cur->state->layout);
+
+ struct wlr_box *mapping = get_mapping(cur, dev);
+ if (!mapping) {
+ mapping = wlr_output_layout_get_box(cur->state->layout, NULL);
+ }
+
+ double x = mapping->width * x_mm + mapping->x;
+ double y = mapping->height * y_mm + mapping->y;
+
+ wlr_cursor_warp_unchecked(cur, x, y);
+}
+
+void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev,
+ double delta_x, double delta_y) {
+ assert(cur->state->layout);
+
+ double x = cur->x + delta_x;
+ double y = cur->y + delta_y;
+
+ struct wlr_box *mapping = get_mapping(cur, dev);
+
+ if (mapping) {
+ double closest_x, closest_y;
+ if (!wlr_box_contains_point(mapping, x, y)) {
+ wlr_box_closest_point(mapping, x, y, &closest_x,
+ &closest_y);
+ x = closest_x;
+ y = closest_y;
+ }
+ } else {
+ if (!wlr_output_layout_contains_point(cur->state->layout, NULL, x, y)) {
+ double layout_x, layout_y;
+ wlr_output_layout_closest_point(cur->state->layout, NULL, x, y,
+ &layout_x, &layout_y);
+ x = layout_x;
+ y = layout_y;
+ }
+ }
+
+ wlr_cursor_warp_unchecked(cur, x, y);
+}
+
+static void handle_pointer_motion(struct wl_listener *listener, void *data) {
+ struct wlr_event_pointer_motion *event = data;
+ struct wlr_cursor_device *device =
+ wl_container_of(listener, device, motion);
+ wl_signal_emit(&device->cursor->events.motion, event);
+}
+
+static void handle_pointer_motion_absolute(struct wl_listener *listener,
+ void *data) {
+ struct wlr_event_pointer_motion_absolute *event = data;
+ struct wlr_cursor_device *device =
+ wl_container_of(listener, device, motion_absolute);
+ wl_signal_emit(&device->cursor->events.motion_absolute, event);
+}
+
+static void handle_pointer_button(struct wl_listener *listener, void *data) {
+ struct wlr_event_pointer_button *event = data;
+ struct wlr_cursor_device *device =
+ wl_container_of(listener, device, button);
+ wl_signal_emit(&device->cursor->events.button, event);
+}
+
+static void handle_pointer_axis(struct wl_listener *listener, void *data) {
+ struct wlr_event_pointer_axis *event = data;
+ struct wlr_cursor_device *device = wl_container_of(listener, device, axis);
+ wl_signal_emit(&device->cursor->events.axis, event);
+}
+
+static void handle_touch_up(struct wl_listener *listener, void *data) {
+ struct wlr_event_touch_up *event = data;
+ struct wlr_cursor_device *device;
+ device = wl_container_of(listener, device, touch_up);
+ wl_signal_emit(&device->cursor->events.touch_up, event);
+}
+
+static void handle_touch_down(struct wl_listener *listener, void *data) {
+ struct wlr_event_touch_down *event = data;
+ struct wlr_cursor_device *device;
+ device = wl_container_of(listener, device, touch_down);
+ wl_signal_emit(&device->cursor->events.touch_down, event);
+}
+
+static void handle_touch_motion(struct wl_listener *listener, void *data) {
+ struct wlr_event_touch_motion *event = data;
+ struct wlr_cursor_device *device;
+ device = wl_container_of(listener, device, touch_motion);
+ wl_signal_emit(&device->cursor->events.touch_motion, event);
+}
+
+static void handle_touch_cancel(struct wl_listener *listener, void *data) {
+ struct wlr_event_touch_cancel *event = data;
+ struct wlr_cursor_device *device;
+ device = wl_container_of(listener, device, touch_cancel);
+ wl_signal_emit(&device->cursor->events.touch_cancel, event);
+}
+
+static void handle_tablet_tool_tip(struct wl_listener *listener, void *data) {
+ struct wlr_event_tablet_tool_tip *event = data;
+ struct wlr_cursor_device *device;
+ device = wl_container_of(listener, device, tablet_tool_tip);
+ wl_signal_emit(&device->cursor->events.tablet_tool_tip, event);
+}
+
+static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) {
+ struct wlr_event_tablet_tool_axis *event = data;
+ struct wlr_cursor_device *device;
+ device = wl_container_of(listener, device, tablet_tool_axis);
+ wl_signal_emit(&device->cursor->events.tablet_tool_axis, event);
+}
+
+static void handle_tablet_tool_button(struct wl_listener *listener,
+ void *data) {
+ struct wlr_event_tablet_tool_button *event = data;
+ struct wlr_cursor_device *device;
+ device = wl_container_of(listener, device, tablet_tool_button);
+ wl_signal_emit(&device->cursor->events.tablet_tool_button, event);
+}
+
+static void handle_tablet_tool_proximity(struct wl_listener *listener,
+ void *data) {
+ struct wlr_event_tablet_tool_proximity *event = data;
+ struct wlr_cursor_device *device;
+ device = wl_container_of(listener, device, tablet_tool_proximity);
+ wl_signal_emit(&device->cursor->events.tablet_tool_proximity, event);
+}
+
+static void handle_device_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_cursor_device *c_device;
+ c_device = wl_container_of(listener, c_device, destroy);
+ wlr_cursor_detach_input_device(c_device->cursor, c_device->device);
+}
+
+void wlr_cursor_attach_input_device(struct wlr_cursor *cur,
+ struct wlr_input_device *dev) {
+ if (dev->type != WLR_INPUT_DEVICE_POINTER &&
+ dev->type != WLR_INPUT_DEVICE_TOUCH &&
+ dev->type != WLR_INPUT_DEVICE_TABLET_TOOL) {
+ wlr_log(L_ERROR, "only device types of pointer, touch or tablet tool"
+ "are supported");
+ return;
+ }
+
+ // make sure it is not already attached
+ struct wlr_cursor_device *_dev;
+ wl_list_for_each(_dev, &cur->state->devices, link) {
+ if (_dev->device == dev) {
+ return;
+ }
+ }
+
+ struct wlr_cursor_device *device;
+ device = calloc(1, sizeof(struct wlr_cursor_device));
+ if (!device) {
+ wlr_log(L_ERROR, "Failed to allocate wlr_cursor_device");
+ return;
+ }
+
+ device->cursor = cur;
+ device->device = dev;
+
+ // listen to events
+
+ wl_signal_add(&dev->events.destroy, &device->destroy);
+ device->destroy.notify = handle_device_destroy;
+
+ if (dev->type == WLR_INPUT_DEVICE_POINTER) {
+ wl_signal_add(&dev->pointer->events.motion, &device->motion);
+ device->motion.notify = handle_pointer_motion;
+
+ wl_signal_add(&dev->pointer->events.motion_absolute,
+ &device->motion_absolute);
+ device->motion_absolute.notify = handle_pointer_motion_absolute;
+
+ wl_signal_add(&dev->pointer->events.button, &device->button);
+ device->button.notify = handle_pointer_button;
+
+ wl_signal_add(&dev->pointer->events.axis, &device->axis);
+ device->axis.notify = handle_pointer_axis;
+ } else if (dev->type == WLR_INPUT_DEVICE_TOUCH) {
+ wl_signal_add(&dev->touch->events.motion, &device->touch_motion);
+ device->touch_motion.notify = handle_touch_motion;
+
+ wl_signal_add(&dev->touch->events.down, &device->touch_down);
+ device->touch_down.notify = handle_touch_down;
+
+ wl_signal_add(&dev->touch->events.up, &device->touch_up);
+ device->touch_up.notify = handle_touch_up;
+
+ wl_signal_add(&dev->touch->events.cancel, &device->touch_cancel);
+ device->touch_cancel.notify = handle_touch_cancel;
+ } else if (dev->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
+ wl_signal_add(&dev->tablet_tool->events.tip, &device->tablet_tool_tip);
+ device->tablet_tool_tip.notify = handle_tablet_tool_tip;
+
+ wl_signal_add(&dev->tablet_tool->events.proximity,
+ &device->tablet_tool_proximity);
+ device->tablet_tool_proximity.notify = handle_tablet_tool_proximity;
+
+ wl_signal_add(&dev->tablet_tool->events.axis,
+ &device->tablet_tool_axis);
+ device->tablet_tool_axis.notify = handle_tablet_tool_axis;
+
+ wl_signal_add(&dev->tablet_tool->events.button,
+ &device->tablet_tool_button);
+ device->tablet_tool_button.notify = handle_tablet_tool_button;
+ }
+
+ wl_list_insert(&cur->state->devices, &device->link);
+}
+
+void wlr_cursor_detach_input_device(struct wlr_cursor *cur,
+ struct wlr_input_device *dev) {
+ struct wlr_cursor_device *target_device = NULL, *_device = NULL;
+ wl_list_for_each(_device, &cur->state->devices, link) {
+ if (_device->device == dev) {
+ target_device = _device;
+ break;
+ }
+ }
+
+ if (target_device) {
+ wl_list_remove(&target_device->link);
+ free(target_device);
+ }
+}
+
+void wlr_cursor_attach_output_layout(struct wlr_cursor *cur,
+ struct wlr_output_layout *l) {
+ cur->state->layout = l;
+}
+
+void wlr_cursor_map_to_output(struct wlr_cursor *cur,
+ struct wlr_output *output) {
+ cur->state->mapped_output = output;
+}
+
+void wlr_cursor_map_input_to_output(struct wlr_cursor *cur,
+ struct wlr_input_device *dev, struct wlr_output *output) {
+ struct wlr_cursor_device *c_device = get_cursor_device(cur, dev);
+ if (!c_device) {
+ wlr_log(L_ERROR, "Cannot map device \"%s\" to output"
+ "(not found in this cursor)", dev->name);
+ return;
+ }
+
+ c_device->mapped_output = output;
+}
+
+void wlr_cursor_map_to_region(struct wlr_cursor *cur,
+ struct wlr_box *box) {
+ if (box && wlr_box_empty(box)) {
+ wlr_log(L_ERROR, "cannot map cursor to an empty region");
+ return;
+ }
+
+ cur->state->mapped_box = box;
+}
+
+void wlr_cursor_map_input_to_region(struct wlr_cursor *cur,
+ struct wlr_input_device *dev, struct wlr_box *box) {
+ if (box && wlr_box_empty(box)) {
+ wlr_log(L_ERROR, "cannot map device \"%s\" input to an empty region",
+ dev->name);
+ return;
+ }
+
+ 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_box = box;
+}
diff --git a/types/wlr_input_device.c b/types/wlr_input_device.c
index 409e8bd1..002c2b54 100644
--- a/types/wlr_input_device.c
+++ b/types/wlr_input_device.c
@@ -20,12 +20,16 @@ void wlr_input_device_init(struct wlr_input_device *dev,
dev->name = strdup(name);
dev->vendor = vendor;
dev->product = product;
+
+ wl_signal_init(&dev->events.destroy);
}
void wlr_input_device_destroy(struct wlr_input_device *dev) {
if (!dev) {
return;
}
+
+ wl_signal_emit(&dev->events.destroy, dev);
if (dev->_device) {
switch (dev->type) {
diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c
index 2e2032b3..7c98837d 100644
--- a/types/wlr_output_layout.c
+++ b/types/wlr_output_layout.c
@@ -1,15 +1,37 @@
#include <wlr/util/log.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_box.h>
+#include <limits.h>
+#include <float.h>
#include <stdlib.h>
#include <assert.h>
+struct wlr_output_layout_state {
+ struct wlr_box *_box;
+};
+
+struct wlr_output_layout_output_state {
+ struct wlr_box *_box;
+};
+
struct wlr_output_layout *wlr_output_layout_init() {
- struct wlr_output_layout *layout = calloc(1, sizeof(struct wlr_output_layout));
+ struct wlr_output_layout *layout =
+ calloc(1, sizeof(struct wlr_output_layout));
+ layout->state = calloc(1, sizeof(struct wlr_output_layout_state));
+ layout->state->_box = calloc(1, sizeof(struct wlr_box));
wl_list_init(&layout->outputs);
return layout;
}
+static void wlr_output_layout_output_destroy(
+ struct wlr_output_layout_output *l_output) {
+ wl_list_remove(&l_output->link);
+ free(l_output->state->_box);
+ free(l_output->state);
+ free(l_output);
+}
+
void wlr_output_layout_destroy(struct wlr_output_layout *layout) {
if (!layout) {
return;
@@ -17,28 +39,32 @@ void wlr_output_layout_destroy(struct wlr_output_layout *layout) {
struct wlr_output_layout_output *_output, *temp = NULL;
wl_list_for_each_safe(_output, temp, &layout->outputs, link) {
- wl_list_remove(&_output->link);
- free(_output);
+ wlr_output_layout_output_destroy(_output);
}
+ free(layout->state->_box);
+ free(layout->state);
free(layout);
}
void wlr_output_layout_add(struct wlr_output_layout *layout,
struct wlr_output *output, int x, int y) {
- struct wlr_output_layout_output *layout_output = calloc(1, sizeof(struct wlr_output_layout_output));
- layout_output->output = output;
- layout_output->x = x;
- layout_output->y = y;
- wl_list_insert(&layout->outputs, &layout_output->link);
+ struct wlr_output_layout_output *l_output;
+ l_output= calloc(1, sizeof(struct wlr_output_layout_output));
+ l_output->state = calloc(1, sizeof(struct wlr_output_layout_output_state));
+ l_output->state->_box = calloc(1, sizeof(struct wlr_box));
+ l_output->output = output;
+ l_output->x = x;
+ l_output->y = y;
+ wl_list_insert(&layout->outputs, &l_output->link);
}
struct wlr_output_layout_output *wlr_output_layout_get(
struct wlr_output_layout *layout, struct wlr_output *reference) {
- struct wlr_output_layout_output *_output;
- wl_list_for_each(_output, &layout->outputs, link) {
- if (_output->output == reference) {
- return _output;
+ struct wlr_output_layout_output *l_output;
+ wl_list_for_each(l_output, &layout->outputs, link) {
+ if (l_output->output == reference) {
+ return l_output;
}
}
return NULL;
@@ -52,15 +78,21 @@ static bool output_contains_point( struct wlr_output_layout_output *l_output,
bool wlr_output_layout_contains_point(struct wlr_output_layout *layout,
struct wlr_output *reference, int x, int y) {
- struct wlr_output_layout_output *layout_output = wlr_output_layout_get(layout, reference);
- int width, height;
- wlr_output_effective_resolution(layout_output->output, &width, &height);
- return output_contains_point(layout_output, x, y, width, height);
+ if (reference) {
+ struct wlr_output_layout_output *layout_output =
+ wlr_output_layout_get(layout, reference);
+ int width, height;
+ wlr_output_effective_resolution(layout_output->output, &width, &height);
+ return output_contains_point(layout_output, x, y, width, height);
+ } else {
+ return !!wlr_output_layout_output_at(layout, x, y);
+ }
}
bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
struct wlr_output *reference, int x1, int y1, int x2, int y2) {
- struct wlr_output_layout_output *l_output = wlr_output_layout_get(layout, reference);
+ struct wlr_output_layout_output *l_output =
+ wlr_output_layout_get(layout, reference);
if (!l_output) {
return false;
}
@@ -76,15 +108,15 @@ bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout,
double x, double y) {
- struct wlr_output_layout_output *_output;
- wl_list_for_each(_output, &layout->outputs, link) {
- if (_output->output) {
+ struct wlr_output_layout_output *l_output;
+ wl_list_for_each(l_output, &layout->outputs, link) {
+ if (l_output->output) {
int width, height;
- wlr_output_effective_resolution(_output->output, &width, &height);
- bool has_x = x >= _output->x && x <= _output->x + width;
- bool has_y = y >= _output->y && y <= _output->y + height;
+ wlr_output_effective_resolution(l_output->output, &width, &height);
+ bool has_x = x >= l_output->x && x <= l_output->x + width;
+ bool has_y = y >= l_output->y && y <= l_output->y + height;
if (has_x && has_y) {
- return _output->output;
+ return l_output->output;
}
}
}
@@ -93,36 +125,112 @@ struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout,
void wlr_output_layout_move(struct wlr_output_layout *layout,
struct wlr_output *output, int x, int y) {
- struct wlr_output_layout_output *layout_output =
- wlr_output_layout_get(layout, output);
- if (layout_output) {
- layout_output->x = x;
- layout_output->y = y;
+ struct wlr_output_layout_output *l_output =
+ wlr_output_layout_get(layout, output);
+ if (l_output) {
+ l_output->x = x;
+ l_output->y = y;
}
}
void wlr_output_layout_remove(struct wlr_output_layout *layout,
struct wlr_output *output) {
- struct wlr_output_layout_output *layout_output =
- wlr_output_layout_get(layout, output);
- if (layout_output) {
- wl_list_remove(&layout_output->link);
- free(layout_output);
+ struct wlr_output_layout_output *l_output;
+ l_output= wlr_output_layout_get(layout, output);
+ if (l_output) {
+ wlr_output_layout_output_destroy(l_output);
}
}
void wlr_output_layout_output_coords(struct wlr_output_layout *layout,
- struct wlr_output *reference, int *x, int *y) {
+ struct wlr_output *reference, double *x, double *y) {
assert(layout && reference);
- int src_x = *x;
- int src_y = *y;
-
- struct wlr_output_layout_output *_output;
- wl_list_for_each(_output, &layout->outputs, link) {
- if (_output->output == reference) {
- *x = src_x - _output->x;
- *y = src_y - _output->y;
+ double src_x = *x;
+ double src_y = *y;
+
+ struct wlr_output_layout_output *l_output;
+ wl_list_for_each(l_output, &layout->outputs, link) {
+ if (l_output->output == reference) {
+ *x = src_x - (double)l_output->x;
+ *y = src_y - (double)l_output->y;
return;
}
}
}
+
+static struct wlr_box *wlr_output_layout_output_get_box(
+ struct wlr_output_layout_output *l_output) {
+ l_output->state->_box->x = l_output->x;
+ l_output->state->_box->y = l_output->y;
+ wlr_output_effective_resolution(l_output->output,
+ &l_output->state->_box->width, &l_output->state->_box->height);
+ return l_output->state->_box;
+}
+
+void wlr_output_layout_closest_point(struct wlr_output_layout *layout,
+ struct wlr_output *reference, double x, double y, double *dest_x,
+ double *dest_y) {
+ double min_x = DBL_MAX, min_y = DBL_MAX, min_distance = DBL_MAX;
+ struct wlr_output_layout_output *l_output;
+ wl_list_for_each(l_output, &layout->outputs, link) {
+ if (reference != NULL && reference != l_output->output) {
+ continue;
+ }
+
+ double output_x, output_y, output_distance;
+ struct wlr_box *box = wlr_output_layout_output_get_box(l_output);
+ wlr_box_closest_point(box, x, y, &output_x, &output_y);
+
+ // calculate squared distance suitable for comparison
+ output_distance =
+ (x - output_x) * (x - output_x) + (y - output_y) * (y - output_y);
+
+ if (output_distance < min_distance) {
+ min_x = output_x;
+ min_y = output_y;
+ min_distance = output_distance;
+ }
+ }
+
+ *dest_x = min_x;
+ *dest_y = min_y;
+}
+
+struct wlr_box *wlr_output_layout_get_box(
+ struct wlr_output_layout *layout, struct wlr_output *reference) {
+ struct wlr_output_layout_output *l_output;
+ if (reference) {
+ // output extents
+ l_output= wlr_output_layout_get(layout, reference);
+ return wlr_output_layout_output_get_box(l_output);
+ } else {
+ // layout extents
+ int min_x = INT_MAX, min_y = INT_MAX;
+ int max_x = INT_MIN, max_y = INT_MIN;
+ wl_list_for_each(l_output, &layout->outputs, link) {
+ int width, height;
+ wlr_output_effective_resolution(l_output->output, &width, &height);
+ if (l_output->x < min_x) {
+ min_x = l_output->x;
+ }
+ if (l_output->y < min_y) {
+ min_y = l_output->y;
+ }
+ if (l_output->x + width > max_x) {
+ max_x = l_output->x + width;
+ }
+ if (l_output->y + height > max_y) {
+ max_y = l_output->y + height;
+ }
+ }
+
+ layout->state->_box->x = min_x;
+ layout->state->_box->y = min_y;
+ layout->state->_box->width = max_x - min_x;
+ layout->state->_box->height = max_y - min_y;
+
+ return layout->state->_box;
+ }
+
+ // not reached
+}