diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/config.c | 303 | ||||
-rw-r--r-- | examples/config.h | 39 | ||||
-rw-r--r-- | examples/ini.c | 195 | ||||
-rw-r--r-- | examples/ini.h | 93 | ||||
-rw-r--r-- | examples/meson.build | 2 | ||||
-rw-r--r-- | examples/output-layout.c | 5 | ||||
-rw-r--r-- | examples/pointer.c | 361 | ||||
-rw-r--r-- | examples/rotation.c | 1 | ||||
-rw-r--r-- | examples/shared.c | 148 | ||||
-rw-r--r-- | examples/shared.h | 22 | ||||
-rw-r--r-- | examples/touch.c | 2 | ||||
-rw-r--r-- | examples/wlr-example.ini.example | 57 |
12 files changed, 1015 insertions, 213 deletions
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 |