diff options
Diffstat (limited to 'rootston')
-rw-r--r-- | rootston/config.c | 269 | ||||
-rw-r--r-- | rootston/cursor.c | 225 | ||||
-rw-r--r-- | rootston/desktop.c | 102 | ||||
-rw-r--r-- | rootston/ini.c | 195 | ||||
-rw-r--r-- | rootston/input.c | 112 | ||||
-rw-r--r-- | rootston/keyboard.c | 75 | ||||
-rw-r--r-- | rootston/main.c | 46 | ||||
-rw-r--r-- | rootston/meson.build | 16 | ||||
-rw-r--r-- | rootston/output.c | 140 | ||||
-rw-r--r-- | rootston/pointer.c | 23 | ||||
-rw-r--r-- | rootston/wl_shell.c | 84 | ||||
-rw-r--r-- | rootston/xdg_shell_v6.c | 100 | ||||
-rw-r--r-- | rootston/xwayland.c | 49 |
13 files changed, 1436 insertions, 0 deletions
diff --git a/rootston/config.c b/rootston/config.c new file mode 100644 index 00000000..3280c6d1 --- /dev/null +++ b/rootston/config.c @@ -0,0 +1,269 @@ +#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 "rootston/config.h" +#include "rootston/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: rootston.ini).\n" + " See `rootston.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 roots_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 roots_config *parse_args(int argc, char *argv[]) { + struct roots_config *config = calloc(1, sizeof(struct roots_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, "rootston.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 roots_config_destroy(struct roots_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); + free(dc->mapped_output); + free(dc->mapped_box); + free(dc); + } + + free(config->config_path); + free(config->cursor.mapped_output); + free(config->cursor.mapped_box); + free(config); +} + +struct output_config *config_get_output(struct roots_config *config, + struct wlr_output *output) { + struct output_config *o_config; + wl_list_for_each(o_config, &config->outputs, link) { + if (strcmp(o_config->name, output->name) == 0) { + return o_config; + } + } + + return NULL; +} + +struct device_config *config_get_device(struct roots_config *config, + struct wlr_input_device *device) { + struct device_config *d_config; + wl_list_for_each(d_config, &config->devices, link) { + if (strcmp(d_config->name, device->name) == 0) { + return d_config; + } + } + + return NULL; +} diff --git a/rootston/cursor.c b/rootston/cursor.c new file mode 100644 index 00000000..89f5874e --- /dev/null +++ b/rootston/cursor.c @@ -0,0 +1,225 @@ +#include <stdint.h> +#include <string.h> +// TODO: BSD et al +#include <linux/input-event-codes.h> +#include <wayland-server.h> +#include <wlr/types/wlr_cursor.h> +#include <wlr/util/log.h> +#include "rootston/config.h" +#include "rootston/input.h" +#include "rootston/desktop.h" + +void cursor_update_position(struct roots_input *input, uint32_t time) { + struct roots_desktop *desktop = input->server->desktop; + struct roots_view *view; + switch (input->mode) { + case ROOTS_CURSOR_PASSTHROUGH: + view = view_at(desktop, input->cursor->x, input->cursor->y); + if (view) { + struct wlr_box box; + view_get_input_bounds(view, &box); + double sx = input->cursor->x - view->x; + double sy = input->cursor->y - view->y; + wlr_seat_pointer_enter(input->wl_seat, view->wlr_surface, sx, sy); + wlr_seat_pointer_send_motion(input->wl_seat, time, sx, sy); + } else { + wlr_seat_pointer_clear_focus(input->wl_seat); + } + break; + case ROOTS_CURSOR_MOVE: + if (input->active_view) { + input->active_view->x = input->cursor->x - input->offs_x; + input->active_view->y = input->cursor->y - input->offs_y; + } + break; + case ROOTS_CURSOR_RESIZE: + break; + case ROOTS_CURSOR_ROTATE: + break; + } +} + +static void set_view_focus(struct roots_input *input, + struct roots_desktop *desktop, struct roots_view *view) { + if (input->active_view == view) { + return; + } + struct roots_view *_view; + wl_list_for_each(_view, &desktop->views, link) { + view_activate(_view, _view == view); + } + input->active_view = view; + input->mode = ROOTS_CURSOR_PASSTHROUGH; +} + +static void handle_cursor_motion(struct wl_listener *listener, void *data) { + struct roots_input *input = wl_container_of(listener, input, cursor_motion); + struct wlr_event_pointer_motion *event = data; + wlr_cursor_move(input->cursor, event->device, + event->delta_x, event->delta_y); + cursor_update_position(input, (uint32_t)event->time_usec); +} + +static void handle_cursor_motion_absolute(struct wl_listener *listener, + void *data) { + struct roots_input *input = wl_container_of(listener, + input, cursor_motion_absolute); + struct wlr_event_pointer_motion_absolute *event = data; + wlr_cursor_warp_absolute(input->cursor, event->device, + event->x_mm / event->width_mm, event->y_mm / event->height_mm); + cursor_update_position(input, (uint32_t)event->time_usec); +} + +static void handle_cursor_axis(struct wl_listener *listener, void *data) { + struct roots_input *input = + wl_container_of(listener, input, cursor_axis); + struct wlr_event_pointer_axis *event = data; + wlr_seat_pointer_send_axis(input->wl_seat, event->time_sec, + event->orientation, event->delta); +} + +static void do_cursor_button_press(struct roots_input *input, + struct wlr_cursor *cursor, struct wlr_input_device *device, + uint32_t time, uint32_t button, uint32_t state) { + struct roots_desktop *desktop = input->server->desktop; + struct roots_view *view = view_at(desktop, + input->cursor->x, input->cursor->y); + uint32_t serial = wlr_seat_pointer_send_button( + input->wl_seat, time, button, state); + int i; + switch (state) { + case WLR_BUTTON_RELEASED: + input->active_view = NULL; + input->mode = ROOTS_CURSOR_PASSTHROUGH; + break; + case WLR_BUTTON_PRESSED: + i = input->input_events_idx; + input->input_events[i].serial = serial; + input->input_events[i].cursor = cursor; + input->input_events[i].device = device; + input->input_events_idx = (i + 1) + % (sizeof(input->input_events) / sizeof(input->input_events[0])); + set_view_focus(input, desktop, view); + if (view) { + wlr_seat_keyboard_enter(input->wl_seat, view->wlr_surface); + } + break; + } +} + +static void handle_cursor_button(struct wl_listener *listener, void *data) { + struct roots_input *input = wl_container_of(listener, input, cursor_button); + struct wlr_event_pointer_button *event = data; + do_cursor_button_press(input, input->cursor, event->device, + (uint32_t)event->time_usec, event->button, event->state); +} + +static void handle_tool_axis(struct wl_listener *listener, void *data) { + struct roots_input *input = wl_container_of(listener, input, cursor_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(input->cursor, event->device, + event->x_mm / event->width_mm, event->y_mm / event->height_mm); + cursor_update_position(input, (uint32_t)event->time_usec); + } +} + +static void handle_tool_tip(struct wl_listener *listener, void *data) { + struct roots_input *input = wl_container_of(listener, input, cursor_tool_tip); + struct wlr_event_tablet_tool_tip *event = data; + do_cursor_button_press(input, input->cursor, event->device, + (uint32_t)event->time_usec, BTN_LEFT, event->state); +} + +void cursor_initialize(struct roots_input *input) { + struct wlr_cursor *cursor = input->cursor; + + wl_list_init(&input->cursor_motion.link); + wl_signal_add(&cursor->events.motion, &input->cursor_motion); + input->cursor_motion.notify = handle_cursor_motion; + + wl_list_init(&input->cursor_motion_absolute.link); + wl_signal_add(&cursor->events.motion_absolute, + &input->cursor_motion_absolute); + input->cursor_motion_absolute.notify = handle_cursor_motion_absolute; + + wl_list_init(&input->cursor_button.link); + wl_signal_add(&cursor->events.button, &input->cursor_button); + input->cursor_button.notify = handle_cursor_button; + + wl_list_init(&input->cursor_axis.link); + wl_signal_add(&cursor->events.axis, &input->cursor_axis); + input->cursor_axis.notify = handle_cursor_axis; + + wl_list_init(&input->cursor_tool_axis.link); + wl_signal_add(&cursor->events.tablet_tool_axis, &input->cursor_tool_axis); + input->cursor_tool_axis.notify = handle_tool_axis; + + wl_list_init(&input->cursor_tool_tip.link); + wl_signal_add(&cursor->events.tablet_tool_tip, &input->cursor_tool_tip); + input->cursor_tool_tip.notify = handle_tool_tip; +} + +static void reset_device_mappings(struct roots_config *config, + struct wlr_cursor *cursor, struct wlr_input_device *device) { + wlr_cursor_map_input_to_output(cursor, device, NULL); + struct device_config *dconfig; + if ((dconfig = config_get_device(config, device))) { + wlr_cursor_map_input_to_region(cursor, device, dconfig->mapped_box); + } +} + +static void set_device_output_mappings(struct roots_config *config, + struct wlr_cursor *cursor, struct wlr_output *output, + struct wlr_input_device *device) { + struct device_config *dconfig; + dconfig = config_get_device(config, device); + if (dconfig && dconfig->mapped_output && + strcmp(dconfig->mapped_output, output->name) == 0) { + wlr_cursor_map_input_to_output(cursor, device, output); + } +} + +void cursor_load_config(struct roots_config *config, + struct wlr_cursor *cursor, + struct roots_input *input, + struct roots_desktop *desktop) { + struct roots_pointer *pointer; + struct roots_touch *touch; + struct roots_tablet_tool *tablet_tool; + struct roots_output *output; + + // reset mappings + wlr_cursor_map_to_output(cursor, NULL); + wl_list_for_each(pointer, &input->pointers, link) { + reset_device_mappings(config, cursor, pointer->device); + } + wl_list_for_each(touch, &input->touch, link) { + reset_device_mappings(config, cursor, touch->device); + } + wl_list_for_each(tablet_tool, &input->tablet_tools, link) { + reset_device_mappings(config, cursor, tablet_tool->device); + } + + // configure device to output mappings + const char *mapped_output = config->cursor.mapped_output; + wl_list_for_each(output, &desktop->outputs, link) { + if (mapped_output && strcmp(mapped_output, output->wlr_output->name) == 0) { + wlr_cursor_map_to_output(cursor, output->wlr_output); + } + + wl_list_for_each(pointer, &input->pointers, link) { + set_device_output_mappings(config, cursor, output->wlr_output, + pointer->device); + } + wl_list_for_each(tablet_tool, &input->tablet_tools, link) { + set_device_output_mappings(config, cursor, output->wlr_output, + tablet_tool->device); + } + wl_list_for_each(touch, &input->touch, link) { + set_device_output_mappings(config, cursor, output->wlr_output, + touch->device); + } + } +} diff --git a/rootston/desktop.c b/rootston/desktop.c new file mode 100644 index 00000000..697c83df --- /dev/null +++ b/rootston/desktop.c @@ -0,0 +1,102 @@ +#define _POSIX_C_SOURCE 199309L +#include <time.h> +#include <stdlib.h> +#include <wlr/types/wlr_box.h> +#include <wlr/types/wlr_compositor.h> +#include <wlr/types/wlr_cursor.h> +#include <wlr/types/wlr_gamma_control.h> +#include <wlr/types/wlr_output_layout.h> +#include <wlr/types/wlr_wl_shell.h> +#include <wlr/types/wlr_xdg_shell_v6.h> +#include <wlr/util/log.h> +#include "rootston/desktop.h" +#include "rootston/server.h" + +void view_destroy(struct roots_view *view) { + wl_list_remove(&view->link); + free(view); +} + +void view_get_input_bounds(struct roots_view *view, struct wlr_box *box) { + if (view->get_input_bounds) { + view->get_input_bounds(view, box); + return; + } + box->x = box->y = 0; + box->width = view->wlr_surface->current.width; + box->height = view->wlr_surface->current.height; +} + +void view_activate(struct roots_view *view, bool activate) { + if (view->activate) { + view->activate(view, activate); + } +} + +struct roots_view *view_at(struct roots_desktop *desktop, int x, int y) { + struct roots_view *view; + wl_list_for_each(view, &desktop->views, link) { + struct wlr_box box; + view_get_input_bounds(view, &box); + box.x += view->x; + box.y += view->y; + if (wlr_box_contains_point(&box, x, y)) { + return view; + } + } + return NULL; +} + +struct roots_desktop *desktop_create(struct roots_server *server, + struct roots_config *config) { + struct roots_desktop *desktop = calloc(1, sizeof(struct roots_desktop)); + wlr_log(L_DEBUG, "Initializing roots desktop"); + + wl_list_init(&desktop->views); + wl_list_init(&desktop->outputs); + wl_list_init(&desktop->output_add.link); + desktop->output_add.notify = output_add_notify; + wl_list_init(&desktop->output_remove.link); + desktop->output_remove.notify = output_remove_notify; + + wl_signal_add(&server->backend->events.output_add, + &desktop->output_add); + wl_signal_add(&server->backend->events.output_remove, + &desktop->output_remove); + + desktop->server = server; + desktop->config = config; + desktop->layout = wlr_output_layout_create(); + desktop->compositor = wlr_compositor_create( + server->wl_display, server->renderer); + + wlr_cursor_attach_output_layout(server->input->cursor, desktop->layout); + wlr_cursor_map_to_region(server->input->cursor, config->cursor.mapped_box); + cursor_load_config(config, server->input->cursor, + server->input, desktop); + + desktop->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display); + wl_signal_add(&desktop->xdg_shell_v6->events.new_surface, + &desktop->xdg_shell_v6_surface); + desktop->xdg_shell_v6_surface.notify = handle_xdg_shell_v6_surface; + + desktop->wl_shell = wlr_wl_shell_create(server->wl_display); + wl_signal_add(&desktop->wl_shell->events.new_surface, + &desktop->wl_shell_surface); + desktop->wl_shell_surface.notify = handle_wl_shell_surface; + + desktop->xwayland = wlr_xwayland_create(server->wl_display, + desktop->compositor); + wl_signal_add(&desktop->xwayland->events.new_surface, + &desktop->xwayland_surface); + desktop->xwayland_surface.notify = handle_xwayland_surface; + + desktop->gamma_control_manager = wlr_gamma_control_manager_create( + server->wl_display); + + return desktop; +} + +void desktop_destroy(struct roots_desktop *desktop) { + // TODO +} diff --git a/rootston/ini.c b/rootston/ini.c new file mode 100644 index 00000000..56cc9ea6 --- /dev/null +++ b/rootston/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 "rootston/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/rootston/input.c b/rootston/input.c new file mode 100644 index 00000000..b4427212 --- /dev/null +++ b/rootston/input.c @@ -0,0 +1,112 @@ +#include <assert.h> +#include <stdlib.h> +#include <wayland-server.h> +#include <wlr/types/wlr_cursor.h> +#include <wlr/util/log.h> +#include <wlr/xcursor.h> +#include "rootston/server.h" +#include "rootston/config.h" +#include "rootston/input.h" + +static const char *device_type(enum wlr_input_device_type type) { + switch (type) { + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_POINTER: + return "pointer"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet pad"; + } + return NULL; +} + +static void input_add_notify(struct wl_listener *listener, void *data) { + struct wlr_input_device *device = data; + struct roots_input *input = wl_container_of(listener, input, input_add); + wlr_log(L_DEBUG, "New input device: %s (%d:%d) %s", device->name, + device->vendor, device->product, device_type(device->type)); + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + keyboard_add(device, input); + break; + case WLR_INPUT_DEVICE_POINTER: + pointer_add(device, input); + break; + case WLR_INPUT_DEVICE_TOUCH: + //touch_add(device, input); + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + //tablet_tool_add(device, input); + break; + default: + break; + } +} + +static void input_remove_notify(struct wl_listener *listener, void *data) { + struct wlr_input_device *device = data; + struct roots_input *input = wl_container_of(listener, input, input_remove); + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + keyboard_remove(device, input); + break; + case WLR_INPUT_DEVICE_POINTER: + pointer_remove(device, input); + break; + case WLR_INPUT_DEVICE_TOUCH: + //touch_remove(device, input); + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + //tablet_tool_remove(device, input); + break; + default: + break; + } +} + +struct roots_input *input_create(struct roots_server *server, + struct roots_config *config) { + wlr_log(L_DEBUG, "Initializing roots input"); + struct roots_input *input = calloc(1, sizeof(struct roots_input)); + assert(input); + + input->config = config; + input->server = server; + + struct wlr_xcursor_theme *theme; + assert(theme = wlr_xcursor_theme_load("default", 16)); + assert(input->xcursor = wlr_xcursor_theme_get_cursor(theme, "left_ptr")); + + assert(input->wl_seat = wlr_seat_create(server->wl_display, "seat0")); + wlr_seat_set_capabilities(input->wl_seat, WL_SEAT_CAPABILITY_KEYBOARD + | WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH); + + wl_list_init(&input->keyboards); + wl_list_init(&input->pointers); + wl_list_init(&input->touch); + wl_list_init(&input->tablet_tools); + + wl_list_init(&input->input_add.link); + input->input_add.notify = input_add_notify; + wl_list_init(&input->input_remove.link); + input->input_remove.notify = input_remove_notify; + + wl_signal_add(&server->backend->events.input_add, + &input->input_add); + wl_signal_add(&server->backend->events.input_remove, + &input->input_remove); + + input->cursor = wlr_cursor_create(); + cursor_initialize(input); + wlr_cursor_set_xcursor(input->cursor, input->xcursor); + + return input; +} + +void input_destroy(struct roots_input *input) { + // TODO +} diff --git a/rootston/keyboard.c b/rootston/keyboard.c new file mode 100644 index 00000000..c4c98c91 --- /dev/null +++ b/rootston/keyboard.c @@ -0,0 +1,75 @@ +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <wayland-server.h> +#include <wlr/types/wlr_input_device.h> +#include <wlr/types/wlr_pointer.h> +#include <wlr/backend/multi.h> +#include <wlr/backend/session.h> +#include <wlr/util/log.h> +#include <xkbcommon/xkbcommon.h> +#include "rootston/input.h" + +static void keyboard_key_notify(struct wl_listener *listener, void *data) { + struct wlr_event_keyboard_key *event = data; + struct roots_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct roots_input *input = keyboard->input; + struct roots_server *server = input->server; + + enum wlr_key_state key_state = event->state; + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, + keycode, &syms); + for (int i = 0; i < nsyms; ++i) { + xkb_keysym_t sym = syms[i]; + if (sym == XKB_KEY_Escape) { + // TEMPORARY, probably + wl_display_terminate(server->wl_display); + } else if (key_state == WLR_KEY_PRESSED && + sym >= XKB_KEY_XF86Switch_VT_1 && + sym <= XKB_KEY_XF86Switch_VT_12) { + if (wlr_backend_is_multi(server->backend)) { + struct wlr_session *session = + wlr_multi_get_session(server->backend); + if (session) { + wlr_session_change_vt(session, sym - XKB_KEY_XF86Switch_VT_1 + 1); + } + } + } + } +} + +void keyboard_add(struct wlr_input_device *device, struct roots_input *input) { + struct roots_keyboard *keyboard = calloc(sizeof(struct roots_keyboard), 1); + device->data = keyboard; + keyboard->device = device; + keyboard->input = input; + wl_list_init(&keyboard->key.link); + keyboard->key.notify = keyboard_key_notify; + wl_signal_add(&device->keyboard->events.key, &keyboard->key); + wl_list_insert(&input->keyboards, &keyboard->link); + + struct xkb_rule_names rules; + memset(&rules, 0, sizeof(rules)); + rules.rules = getenv("XKB_DEFAULT_RULES"); + rules.model = getenv("XKB_DEFAULT_MODEL"); + rules.layout = getenv("XKB_DEFAULT_LAYOUT"); + rules.variant = getenv("XKB_DEFAULT_VARIANT"); + rules.options = getenv("XKB_DEFAULT_OPTIONS"); + struct xkb_context *context; + assert(context = xkb_context_new(XKB_CONTEXT_NO_FLAGS)); + wlr_keyboard_set_keymap(device->keyboard, + xkb_map_new_from_names(context, &rules, + XKB_KEYMAP_COMPILE_NO_FLAGS)); + xkb_context_unref(context); + wlr_seat_attach_keyboard(input->wl_seat, device); +} + +void keyboard_remove(struct wlr_input_device *device, struct roots_input *input) { + struct roots_keyboard *keyboard = device->data; + wlr_seat_detach_keyboard(input->wl_seat, device->keyboard); + wl_list_remove(&keyboard->key.link); + wl_list_remove(&keyboard->link); + free(keyboard); +} diff --git a/rootston/main.c b/rootston/main.c new file mode 100644 index 00000000..c8ec1249 --- /dev/null +++ b/rootston/main.c @@ -0,0 +1,46 @@ +#define _POSIX_C_SOURCE 200112L +#include <assert.h> +#include <stdlib.h> +#include <wayland-server.h> +#include <wlr/backend.h> +#include <wlr/render.h> +#include <wlr/render/gles2.h> +#include <wlr/util/log.h> +#include "rootston/config.h" +#include "rootston/server.h" + +struct roots_server server = { 0 }; + +int main(int argc, char **argv) { + assert(server.config = parse_args(argc, argv)); + assert(server.wl_display = wl_display_create()); + assert(server.wl_event_loop = wl_display_get_event_loop(server.wl_display)); + + assert(server.backend = wlr_backend_autocreate(server.wl_display)); + + assert(server.renderer = wlr_gles2_renderer_create(server.backend)); + wl_display_init_shm(server.wl_display); + server.input = input_create(&server, server.config); + server.desktop = desktop_create(&server, server.config); + server.data_device_manager = wlr_data_device_manager_create( + server.wl_display); + + const char *socket = wl_display_add_socket_auto(server.wl_display); + if (!socket) { + wlr_log_errno(L_ERROR, "Unable to open wayland socket"); + wlr_backend_destroy(server.backend); + return 1; + } + + wlr_log(L_INFO, "Running compositor on wayland display '%s'", socket); + setenv("_WAYLAND_DISPLAY", socket, true); + + if (!wlr_backend_start(server.backend)) { + wlr_log(L_ERROR, "Failed to start backend"); + wlr_backend_destroy(server.backend); + return 1; + } + + wl_display_run(server.wl_display); + return 0; +} diff --git a/rootston/meson.build b/rootston/meson.build new file mode 100644 index 00000000..59f73e96 --- /dev/null +++ b/rootston/meson.build @@ -0,0 +1,16 @@ +executable( + 'rootston', [ + 'config.c', + 'cursor.c', + 'desktop.c', + 'ini.c', + 'input.c', + 'keyboard.c', + 'main.c', + 'output.c', + 'pointer.c', + 'xdg_shell_v6.c', + 'xwayland.c', + 'wl_shell.c', + ], dependencies: wlroots +) diff --git a/rootston/output.c b/rootston/output.c new file mode 100644 index 00000000..0f015fb6 --- /dev/null +++ b/rootston/output.c @@ -0,0 +1,140 @@ +#define _POSIX_C_SOURCE 199309L +#include <time.h> +#include <stdlib.h> +#include <stdbool.h> +#include <wlr/types/wlr_output_layout.h> +#include <wlr/types/wlr_compositor.h> +#include <wlr/types/wlr_wl_shell.h> +#include <wlr/types/wlr_xdg_shell_v6.h> +#include <wlr/render/matrix.h> +#include <wlr/util/log.h> +#include "rootston/server.h" +#include "rootston/desktop.h" +#include "rootston/config.h" + +static inline int64_t timespec_to_msec(const struct timespec *a) { + return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; +} + +static void render_view(struct roots_desktop *desktop, + struct wlr_output *wlr_output, struct timespec *when, + struct roots_view *view, double ox, double oy) { + struct wlr_surface *surface = view->wlr_surface; + float matrix[16]; + float transform[16]; + wlr_surface_flush_damage(surface); + if (surface->texture->valid) { + wlr_matrix_translate(&transform, ox, oy, 0); + wlr_surface_get_matrix(surface, &matrix, + &wlr_output->transform_matrix, &transform); + wlr_render_with_matrix(desktop->server->renderer, + surface->texture, &matrix); + + struct wlr_frame_callback *cb, *cnext; + wl_list_for_each_safe(cb, cnext, &surface->frame_callback_list, link) { + wl_callback_send_done(cb->resource, timespec_to_msec(when)); + wl_resource_destroy(cb->resource); + } + } +} + +static void output_frame_notify(struct wl_listener *listener, void *data) { + struct wlr_output *wlr_output = data; + struct roots_output *output = wl_container_of(listener, output, frame); + struct roots_desktop *desktop = output->desktop; + struct roots_server *server = desktop->server; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + wlr_output_make_current(wlr_output); + wlr_renderer_begin(server->renderer, wlr_output); + + struct roots_view *view; + wl_list_for_each(view, &desktop->views, link) { + int width = view->wlr_surface->current.buffer_width; + int height = view->wlr_surface->current.buffer_height; + + if (wlr_output_layout_intersects(desktop->layout, wlr_output, + view->x, view->y, view->x + width, view->y + height)) { + double ox = view->x, oy = view->y; + wlr_output_layout_output_coords( + desktop->layout, wlr_output, &ox, &oy); + render_view(desktop, wlr_output, &now, view, ox, oy); + } + } + + wlr_renderer_end(server->renderer); + wlr_output_swap_buffers(wlr_output); + + output->last_frame = desktop->last_frame = now; +} + +void output_add_notify(struct wl_listener *listener, void *data) { + struct wlr_output *wlr_output = data; + struct roots_desktop *desktop = wl_container_of(listener, desktop, output_add); + struct roots_input *input = desktop->server->input; + struct roots_config *config = desktop->config; + + wlr_log(L_DEBUG, "Output '%s' added", wlr_output->name); + wlr_log(L_DEBUG, "%s %s %"PRId32"mm x %"PRId32"mm", + wlr_output->make, wlr_output->model, + wlr_output->phys_width, wlr_output->phys_height); + if (wlr_output->modes->length > 0) { + wlr_output_set_mode(wlr_output, wlr_output->modes->items[0]); + } + + struct roots_output *output = calloc(1, sizeof(struct roots_output)); + clock_gettime(CLOCK_MONOTONIC, &output->last_frame); + output->desktop = desktop; + output->wlr_output = wlr_output; + output->frame.notify = output_frame_notify; + wl_list_init(&output->frame.link); + wl_signal_add(&wlr_output->events.frame, &output->frame); + wl_list_insert(&desktop->outputs, &output->link); + + struct output_config *output_config = config_get_output(config, wlr_output); + if (output_config) { + wlr_output_transform(wlr_output, output_config->transform); + wlr_output_layout_add(desktop->layout, + wlr_output, output_config->x, output_config->y); + } else { + wlr_output_layout_add_auto(desktop->layout, wlr_output); + } + + cursor_load_config(config, input->cursor, input, desktop); + + struct wlr_xcursor_image *image = input->xcursor->images[0]; + // TODO the cursor must be set depending on which surface it is displayed + // over which should happen in the compositor. + 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; + } + + wlr_cursor_warp(input->cursor, NULL, input->cursor->x, input->cursor->y); +} + +void output_remove_notify(struct wl_listener *listener, void *data) { + struct wlr_output *wlr_output = data; + struct roots_desktop *desktop = wl_container_of(listener, desktop, output_remove); + struct roots_output *output = NULL, *_output; + wl_list_for_each(_output, &desktop->outputs, link) { + if (_output->wlr_output == wlr_output) { + output = _output; + break; + } + } + if (!output) { + return; // We are unfamiliar with this output + } + wlr_output_layout_remove(desktop->layout, output->wlr_output); + // TODO: cursor + //example_config_configure_cursor(sample->config, sample->cursor, + // sample->compositor); + wl_list_remove(&output->link); + wl_list_remove(&output->frame.link); + wl_list_remove(&output->resolution.link); + free(output); +} diff --git a/rootston/pointer.c b/rootston/pointer.c new file mode 100644 index 00000000..299ecdfc --- /dev/null +++ b/rootston/pointer.c @@ -0,0 +1,23 @@ +#include <stdlib.h> +#include <wayland-server.h> +#include <wlr/types/wlr_input_device.h> +#include <wlr/types/wlr_pointer.h> +#include "rootston/input.h" + +void pointer_add(struct wlr_input_device *device, struct roots_input *input) { + struct roots_pointer *pointer = calloc(sizeof(struct roots_pointer), 1); + device->data = pointer; + pointer->device = device; + pointer->input = input; + wl_list_insert(&input->pointers, &pointer->link); + wlr_cursor_attach_input_device(input->cursor, device); + cursor_load_config(input->server->config, input->cursor, + input, input->server->desktop); +} + +void pointer_remove(struct wlr_input_device *device, struct roots_input *input) { + struct roots_pointer *pointer = device->data; + wlr_cursor_detach_input_device(input->cursor, device); + wl_list_remove(&pointer->link); + free(pointer); +} diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c new file mode 100644 index 00000000..72725c7f --- /dev/null +++ b/rootston/wl_shell.c @@ -0,0 +1,84 @@ +#include <assert.h> +#include <stdlib.h> +#include <stdbool.h> +#include <wayland-server.h> +#include <wlr/types/wlr_box.h> +#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_wl_shell.h> +#include <wlr/util/log.h> +#include "rootston/desktop.h" +#include "rootston/server.h" +#include "rootston/input.h" + +static void handle_move(struct wl_listener *listener, void *data) { + struct roots_wl_shell_surface *roots_surface = + wl_container_of(listener, roots_surface, request_move); + struct roots_view *view = roots_surface->view; + struct roots_input *input = view->desktop->server->input; + struct wlr_wl_shell_surface_move_event *e = data; + + // TODO: Some of this might want to live in cursor.c I guess + struct roots_input_event *event = NULL; + size_t len = sizeof(input->input_events) / sizeof(*input->input_events); + for (size_t i = 0; i < len; ++i) { + if (input->input_events[i].cursor + && input->input_events[i].serial == e->serial) { + event = &input->input_events[i]; + break; + } + } + if (!event || input->mode != ROOTS_CURSOR_PASSTHROUGH) { + return; + } + input->mode = ROOTS_CURSOR_MOVE; + input->offs_x = input->cursor->x - view->x; + input->offs_y = input->cursor->y - view->y; + wlr_seat_pointer_clear_focus(input->wl_seat); +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct roots_wl_shell_surface *roots_surface = + wl_container_of(listener, roots_surface, destroy); + wl_list_remove(&roots_surface->destroy.link); + wl_list_remove(&roots_surface->ping_timeout.link); + wl_list_remove(&roots_surface->request_move.link); + wl_list_remove(&roots_surface->request_resize.link); + wl_list_remove(&roots_surface->request_set_fullscreen.link); + wl_list_remove(&roots_surface->request_set_maximized.link); + view_destroy(roots_surface->view); + free(roots_surface); +} + +void handle_wl_shell_surface(struct wl_listener *listener, void *data) { + struct roots_desktop *desktop = + wl_container_of(listener, desktop, wl_shell_surface); + + struct wlr_wl_shell_surface *surface = data; + wlr_log(L_DEBUG, "new shell surface: title=%s, class=%s", + surface->title, surface->class); + wlr_wl_shell_surface_ping(surface); + + struct roots_wl_shell_surface *roots_surface = + calloc(1, sizeof(struct roots_wl_shell_surface)); + // TODO: all of the trimmings + wl_list_init(&roots_surface->destroy.link); + roots_surface->destroy.notify = handle_destroy; + wl_signal_add(&surface->events.destroy, &roots_surface->destroy); + wl_list_init(&roots_surface->ping_timeout.link); + wl_list_init(&roots_surface->request_move.link); + roots_surface->request_move.notify = handle_move; + wl_signal_add(&surface->events.request_move, &roots_surface->request_move); + wl_list_init(&roots_surface->request_resize.link); + wl_list_init(&roots_surface->request_set_fullscreen.link); + wl_list_init(&roots_surface->request_set_maximized.link); + + struct roots_view *view = calloc(1, sizeof(struct roots_view)); + view->type = ROOTS_WL_SHELL_VIEW; + view->x = view->y = 200; + view->wl_shell_surface = surface; + view->roots_wl_shell_surface = roots_surface; + view->wlr_surface = surface->surface; + view->desktop = desktop; + roots_surface->view = view; + wl_list_insert(&desktop->views, &view->link); +} diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c new file mode 100644 index 00000000..4a809930 --- /dev/null +++ b/rootston/xdg_shell_v6.c @@ -0,0 +1,100 @@ +#include <assert.h> +#include <stdlib.h> +#include <stdbool.h> +#include <wayland-server.h> +#include <wlr/types/wlr_box.h> +#include <wlr/types/wlr_surface.h> +#include <wlr/types/wlr_xdg_shell_v6.h> +#include <wlr/util/log.h> +#include "rootston/desktop.h" +#include "rootston/server.h" +#include "rootston/input.h" + +static void get_input_bounds(struct roots_view *view, struct wlr_box *box) { + assert(view->type == ROOTS_XDG_SHELL_V6_VIEW); + struct wlr_xdg_surface_v6 *surf = view->xdg_surface_v6; + memcpy(box, surf->geometry, sizeof(struct wlr_box)); +} + +static void activate(struct roots_view *view, bool active) { + assert(view->type == ROOTS_XDG_SHELL_V6_VIEW); + struct wlr_xdg_surface_v6 *surf = view->xdg_surface_v6; + if (surf->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + wlr_xdg_toplevel_v6_set_activated(surf, active); + } +} + +static void handle_request_move(struct wl_listener *listener, void *data) { + struct roots_xdg_surface_v6 *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, request_move); + struct roots_view *view = roots_xdg_surface->view; + struct roots_input *input = view->desktop->server->input; + struct wlr_xdg_toplevel_v6_move_event *e = data; + + // TODO: Some of this might want to live in cursor.c I guess + struct roots_input_event *event = NULL; + size_t len = sizeof(input->input_events) / sizeof(*input->input_events); + for (size_t i = 0; i < len; ++i) { + if (input->input_events[i].cursor + && input->input_events[i].serial == e->serial) { + event = &input->input_events[i]; + break; + } + } + if (!event || input->mode != ROOTS_CURSOR_PASSTHROUGH) { + return; + } + input->mode = ROOTS_CURSOR_MOVE; + input->offs_x = input->cursor->x - view->x; + input->offs_y = input->cursor->y - view->y; + wlr_seat_pointer_clear_focus(input->wl_seat); +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct roots_xdg_surface_v6 *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, destroy); + wl_list_remove(&roots_xdg_surface->destroy.link); + wl_list_remove(&roots_xdg_surface->ping_timeout.link); + wl_list_remove(&roots_xdg_surface->request_move.link); + wl_list_remove(&roots_xdg_surface->request_resize.link); + wl_list_remove(&roots_xdg_surface->request_show_window_menu.link); + wl_list_remove(&roots_xdg_surface->request_minimize.link); + view_destroy(roots_xdg_surface->view); + free(roots_xdg_surface); +} + +void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { + struct roots_desktop *desktop = + wl_container_of(listener, desktop, xdg_shell_v6_surface); + + struct wlr_xdg_surface_v6 *surface = data; + wlr_log(L_DEBUG, "new xdg surface: title=%s, app_id=%s", + surface->title, surface->app_id); + wlr_xdg_surface_v6_ping(surface); + + struct roots_xdg_surface_v6 *roots_surface = + calloc(1, sizeof(struct roots_xdg_surface_v6)); + // TODO: all of the trimmings + wl_list_init(&roots_surface->destroy.link); + roots_surface->destroy.notify = handle_destroy; + wl_signal_add(&surface->events.destroy, &roots_surface->destroy); + wl_list_init(&roots_surface->ping_timeout.link); + wl_list_init(&roots_surface->request_minimize.link); + wl_list_init(&roots_surface->request_move.link); + roots_surface->request_move.notify = handle_request_move; + wl_signal_add(&surface->events.request_move, &roots_surface->request_move); + wl_list_init(&roots_surface->request_resize.link); + wl_list_init(&roots_surface->request_show_window_menu.link); + + struct roots_view *view = calloc(1, sizeof(struct roots_view)); + view->type = ROOTS_XDG_SHELL_V6_VIEW; + view->x = view->y = 200; + view->xdg_surface_v6 = surface; + view->roots_xdg_surface_v6 = roots_surface; + view->wlr_surface = surface->surface; + view->get_input_bounds = get_input_bounds; + view->activate = activate; + view->desktop = desktop; + roots_surface->view = view; + wl_list_insert(&desktop->views, &view->link); +} diff --git a/rootston/xwayland.c b/rootston/xwayland.c new file mode 100644 index 00000000..88965f0d --- /dev/null +++ b/rootston/xwayland.c @@ -0,0 +1,49 @@ +#include <assert.h> +#include <stdlib.h> +#include <stdbool.h> +#include <wayland-server.h> +#include <wlr/types/wlr_box.h> +#include <wlr/types/wlr_surface.h> +#include <wlr/xwayland.h> +#include <wlr/util/log.h> +#include "rootston/desktop.h" +#include "rootston/server.h" + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct roots_xwayland_surface *roots_surface = + wl_container_of(listener, roots_surface, destroy); + wl_list_remove(&roots_surface->destroy.link); + view_destroy(roots_surface->view); + free(roots_surface); +} + +static void x11_activate(struct roots_view *view, bool active) { + wlr_xwayland_surface_activate(view->desktop->xwayland, + view->xwayland_surface); +} + +void handle_xwayland_surface(struct wl_listener *listener, void *data) { + struct roots_desktop *desktop = + wl_container_of(listener, desktop, xwayland_surface); + + struct wlr_xwayland_surface *surface = data; + // TODO: get and log title, class, etc + wlr_log(L_DEBUG, "new xwayland surface"); + + struct roots_xwayland_surface *roots_surface = + calloc(1, sizeof(struct roots_xwayland_surface)); + wl_list_init(&roots_surface->destroy.link); + roots_surface->destroy.notify = handle_destroy; + wl_signal_add(&surface->events.destroy, &roots_surface->destroy); + + struct roots_view *view = calloc(1, sizeof(struct roots_view)); + view->type = ROOTS_XWAYLAND_VIEW; + view->x = view->y = 200; + view->xwayland_surface = surface; + view->roots_xwayland_surface = roots_surface; + view->wlr_surface = surface->surface; + view->desktop = desktop; + view->activate = x11_activate; + roots_surface->view = view; + wl_list_insert(&desktop->views, &view->link); +} |