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 +#include +#include +#include +#include +#include +#include +#include +#include "shared.h" +#include "config.h" +#include "ini.h" + +static void usage(const char *name, int ret) { + fprintf(stderr, + "usage: %s [-C ]\n" + "\n" + " -C 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 + +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 +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#include +#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 + +/* 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 #include #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 #include #include +#include #include #include #include @@ -16,19 +17,71 @@ #include #include #include +#include #include +#include #include #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; }; -static void handle_output_frame(struct output_state *output, struct timespec *ts) { +struct touch_point { + int32_t slot; + double x, y; +}; + +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,80 +89,64 @@ 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]; - - 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); - } -} - -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; - - struct wlr_xcursor_image *image = state->cursor->images[0]; - - 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); - } -} - -static void handle_pointer_button(struct pointer_state *pstate, - uint32_t button, enum wlr_button_state state) { - struct sample_state *sample = pstate->compositor->data; - float (*color)[4]; - if (state == WLR_BUTTON_RELEASED) { - color = &sample->default_color; - } else { - float red[4] = { 0.25f, 0.25f, 0.25f, 1 }; - red[button % 3] = 1; - color = &red; - } - 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; - for (size_t i = 0; i < 3; ++i) { - sample->default_color[i] += delta > 0 ? -0.05f : 0.05f; - if (sample->default_color[i] > 1.0f) { - sample->default_color[i] = 1.0f; - } - if (sample->default_color[i] < 0.0f) { - sample->default_color[i] = 0.0f; + // 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 *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); + } + } + } } } - 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 sample_state *sample = ostate->compositor->data; struct wlr_output *wlr_output = ostate->output; - struct wlr_xcursor_image *image = state->cursor->images[0]; + 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"); @@ -120,34 +157,254 @@ static void handle_output_add(struct output_state *ostate) { } } +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_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; + + 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, &sample->compositor->outputs, link) { + wlr_output_move_cursor(output->output, + sample->cur_x - image->hotspot_x, + sample->cur_y - image->hotspot_y); + } +} + +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 (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[event->button % 3] = 1; + color = &red; + memcpy(&sample->clear_color, color, sizeof(*color)); + } +} + +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] += event->delta > 0 ? -0.05f : 0.05f; + if (sample->default_color[i] > 1.0f) { + sample->default_color[i] = 1.0f; + } + if (sample->default_color[i] < 0.0f) { + sample->default_color[i] = 0.0f; + } + } + + memcpy(&sample->clear_color, &sample->default_color, + sizeof(sample->clear_color)); +} + +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; + } + } + + 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 #include #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 #include "shared.h" -static void usage(const char *name, int ret) { - fprintf(stderr, - "usage: %s [-d [-r | -f]]*\n" - "\n" - " -o The name of the DRM display. e.g. DVI-I-1.\n" - " -r The rotation counter clockwise. Valid values are 90, 180, 270.\n" - " -x The X-axis coordinate position of this output in the layout.\n" - " -y 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 #include -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 + +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 +#include +#include +#include +#include +#include + +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 #include +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 +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 #include #include +#include +#include +#include #include #include +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; + double src_x = *x; + double 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; + 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 +}