From 734fab3f43e497aae9189bc380f3856f5f85ca1f Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 15 Aug 2018 10:56:55 -0400 Subject: Tiny Wayland compositor --- .gitignore | 3 + Makefile | 24 +++ tinywl.c | 620 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 647 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 tinywl.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b5bb6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +tinywl +*-protocol.c +*-protocol.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3ecd467 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +WAYLAND_PROTOCOLS=/usr/share/wayland-protocols + +xdg-shell-protocol.h: + wayland-scanner server-header \ + $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ + +xdg-shell-protocol.c: xdg-shell-protocol.h + wayland-scanner private-code \ + $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ + +tinywl: tinywl.c xdg-shell-protocol.h xdg-shell-protocol.c + $(CC) $(CFLAGS) \ + -g -Werror -I. \ + -DWLR_USE_UNSTABLE \ + $(shell pkg-config --cflags --libs wlroots) \ + $(shell pkg-config --cflags --libs wayland-server) \ + $(shell pkg-config --cflags --libs xkbcommon) \ + -o $@ $< + +clean: + rm -f tinywl xdg-shell-protocol.h xdg-shell-protocol.c + +.DEFAULT_GOAL=tinywl +.PHONY: clean diff --git a/tinywl.c b/tinywl.c new file mode 100644 index 0000000..101cd53 --- /dev/null +++ b/tinywl.c @@ -0,0 +1,620 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum tinywl_cursor_mode { + TINYWL_CURSOR_PASSTHROUGH, + TINYWL_CURSOR_MOVE, + TINYWL_CURSOR_RESIZE, +}; + +struct tinywl_server { + struct wl_display *wl_display; + struct wlr_backend *backend; + struct wlr_renderer *renderer; + + struct wlr_xdg_shell *xdg_shell; + struct wl_listener new_xdg_surface; + struct wl_list views; + + struct wlr_cursor *cursor; + struct wlr_xcursor_manager *cursor_mgr; + struct wl_listener cursor_motion; + struct wl_listener cursor_motion_absolute; + struct wl_listener cursor_button; + struct wl_listener cursor_axis; + + struct wlr_seat *seat; + struct wl_listener new_input; + struct wl_listener request_cursor; + struct wl_list keyboards; + enum tinywl_cursor_mode cursor_mode; + struct tinywl_view *grabbed_view; + double grab_x, grab_y; + int grab_width, grab_height; + uint32_t resize_edges; + + struct wlr_output_layout *output_layout; + struct wl_list outputs; + struct wl_listener new_output; +}; + +struct tinywl_output { + struct wl_list link; + struct tinywl_server *server; + struct wlr_output *wlr_output; + struct wl_listener frame; +}; + +struct tinywl_view { + struct wl_list link; + struct tinywl_server *server; + struct wlr_xdg_surface *xdg_surface; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener request_move; + struct wl_listener request_resize; + bool mapped; + int x, y; +}; + +struct tinywl_keyboard { + struct wl_list link; + struct tinywl_server *server; + struct wlr_input_device *device; + + struct wl_listener modifiers; + struct wl_listener key; +}; + +struct tinywl_pointer { + struct wl_list link; + struct tinywl_server *server; + struct wlr_input_device *device; +}; + +static void keyboard_handle_modifiers( + struct wl_listener *listener, void *data) { + struct tinywl_keyboard *keyboard = + wl_container_of(listener, keyboard, modifiers); + wlr_seat_set_keyboard(keyboard->server->seat, keyboard->device); + wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, + &keyboard->device->keyboard->modifiers); +} + +static void keyboard_handle_key( + struct wl_listener *listener, void *data) { + struct tinywl_keyboard *keyboard = + wl_container_of(listener, keyboard, key); + struct wlr_event_keyboard_key *event = data; + struct wlr_seat *seat = keyboard->server->seat; + // TODO: keybindings for moving windows about + wlr_seat_set_keyboard(seat, keyboard->device); + wlr_seat_keyboard_notify_key(seat, event->time_msec, + event->keycode, event->state); +} + +static void server_new_keyboard(struct tinywl_server *server, + struct wlr_input_device *device) { + struct tinywl_keyboard *keyboard = + calloc(1, sizeof(struct tinywl_keyboard)); + keyboard->server = server; + keyboard->device = device; + + struct xkb_rule_names rules = { 0 }; + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, + XKB_KEYMAP_COMPILE_NO_FLAGS); + + wlr_keyboard_set_keymap(device->keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); + + keyboard->modifiers.notify = keyboard_handle_modifiers; + wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); + keyboard->key.notify = keyboard_handle_key; + wl_signal_add(&device->keyboard->events.key, &keyboard->key); + + wlr_seat_set_keyboard(server->seat, device); + + wl_list_insert(&server->keyboards, &keyboard->link); +} + +static void server_new_pointer(struct tinywl_server *server, + struct wlr_input_device *device) { + wlr_cursor_attach_input_device(server->cursor, device); +} + +static void server_new_input(struct wl_listener *listener, void *data) { + struct tinywl_server *server = + wl_container_of(listener, server, new_input); + struct wlr_input_device *device = data; + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + server_new_keyboard(server, device); + break; + case WLR_INPUT_DEVICE_POINTER: + server_new_pointer(server, device); + break; + } + uint32_t caps = WL_SEAT_CAPABILITY_POINTER; + if (!wl_list_empty(&server->keyboards)) { + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + } + wlr_seat_set_capabilities(server->seat, caps); +} + +static void seat_request_cursor(struct wl_listener *listener, void *data) { + struct tinywl_server *server = wl_container_of( + listener, server, request_cursor); + struct wlr_seat_pointer_request_set_cursor_event *event = data; + struct wlr_seat_client *focused_client = + server->seat->pointer_state.focused_client; + if (focused_client == event->seat_client) { + wlr_cursor_set_surface(server->cursor, event->surface, + event->hotspot_x, event->hotspot_y); + } +} + +static bool view_at(struct tinywl_view *view, + double lx, double ly, struct wlr_surface **surface, + double *sx, double *sy) { + double view_sx = lx - view->x; + double view_sy = ly - view->y; + + struct wlr_surface_state *state = &view->xdg_surface->surface->current; + struct wlr_box box = { + .x = 0, .y = 0, + .width = state->width, .height = state->height, + }; + + double _sx, _sy; + struct wlr_surface *_surface = NULL; + _surface = wlr_xdg_surface_surface_at( + view->xdg_surface, view_sx, view_sy, &_sx, &_sy); + + if (_surface != NULL) { + *sx = _sx; + *sy = _sy; + *surface = _surface; + return true; + } + + return false; +} + +static struct tinywl_view *desktop_view_at( + struct tinywl_server *server, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + struct tinywl_view *view; + wl_list_for_each(view, &server->views, link) { + if (view_at(view, lx, ly, surface, sx, sy)) { + return view; + } + } + return NULL; +} + +static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { + struct tinywl_server *server = view->server; + struct wlr_seat *seat = server->seat; + struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; + if (prev_surface == surface) { + return; + } + if (prev_surface) { + struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( + seat->keyboard_state.focused_surface); + wlr_xdg_toplevel_set_activated(previous, false); + } + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); + wl_list_remove(&view->link); + wl_list_insert(&server->views, &view->link); + wlr_xdg_toplevel_set_activated(view->xdg_surface, true); + wlr_seat_keyboard_notify_enter(seat, view->xdg_surface->surface, + keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); +} + +static void process_cursor_move(struct tinywl_server *server, uint32_t time) { + server->grabbed_view->x = server->cursor->x - server->grab_x; + server->grabbed_view->y = server->cursor->y - server->grab_y; +} + +static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { + struct tinywl_view *view = server->grabbed_view; + double dx = server->cursor->x - server->grab_x; + double dy = server->cursor->y - server->grab_y; + double x = view->x; + double y = view->y; + int width = server->grab_width; + int height = server->grab_height; + if (server->resize_edges & WLR_EDGE_TOP) { + y = server->grab_y + dy; + height -= dy; + if (height < 1) { + y += height; + } + } else if (server->resize_edges & WLR_EDGE_BOTTOM) { + height += dy; + } + if (server->resize_edges & WLR_EDGE_LEFT) { + x = server->grab_x + dx; + width -= dx; + if (width < 1) { + x += width; + } + } else if (server->resize_edges & WLR_EDGE_RIGHT) { + width += dx; + } + view->x = x; + view->y = y; + wlr_xdg_toplevel_set_size(view->xdg_surface, width, height); +} + +static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { + if (server->cursor_mode == TINYWL_CURSOR_MOVE) { + process_cursor_move(server, time); + return; + } else if (server->cursor_mode == TINYWL_CURSOR_RESIZE) { + process_cursor_resize(server, time); + return; + } + + double sx, sy; + struct wlr_seat *seat = server->seat; + struct wlr_surface *surface; + struct tinywl_view *view = desktop_view_at(server, + server->cursor->x, server->cursor->y, &surface, &sx, &sy); + if (!view) { + wlr_xcursor_manager_set_cursor_image( + server->cursor_mgr, "left_ptr", server->cursor); + return; + } + + if (surface) { + bool focus_changed = seat->pointer_state.focused_surface != surface; + wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + if (!focus_changed) { + wlr_seat_pointer_notify_motion(seat, time, sx, sy); + } + } else { + wlr_seat_pointer_clear_focus(seat); + } +} + +static void server_cursor_motion(struct wl_listener *listener, void *data) { + struct tinywl_server *server = + wl_container_of(listener, server, cursor_motion_absolute); + struct wlr_event_pointer_motion *event = data; + wlr_cursor_move(server->cursor, event->device, + event->delta_x, event->delta_y); + process_cursor_motion(server, event->time_msec); +} + +static void server_cursor_motion_absolute( + struct wl_listener *listener, void *data) { + struct tinywl_server *server = + wl_container_of(listener, server, cursor_motion_absolute); + struct wlr_event_pointer_motion_absolute *event = data; + wlr_cursor_warp_absolute(server->cursor, event->device, event->x, event->y); + process_cursor_motion(server, event->time_msec); +} + +static void server_cursor_button(struct wl_listener *listener, void *data) { + struct tinywl_server *server = + wl_container_of(listener, server, cursor_button); + struct wlr_event_pointer_button *event = data; + wlr_seat_pointer_notify_button(server->seat, + event->time_msec, event->button, event->state); + double sx, sy; + struct wlr_seat *seat = server->seat; + struct wlr_surface *surface; + struct tinywl_view *view = desktop_view_at(server, + server->cursor->x, server->cursor->y, &surface, &sx, &sy); + if (event->state == WLR_BUTTON_RELEASED) { + server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; + } else { + focus_view(view, surface); + } +} + +static void server_cursor_axis(struct wl_listener *listener, void *data) { + struct tinywl_server *server = + wl_container_of(listener, server, cursor_axis); + struct wlr_event_pointer_axis *event = data; + wlr_seat_pointer_notify_axis(server->seat, + event->time_msec, event->orientation, event->delta, + event->delta_discrete, event->source); +} + +struct render_data { + struct wlr_output *output; + struct wlr_renderer *renderer; + struct tinywl_view *view; + struct timespec *when; +}; + +static void render_surface(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct render_data *rdata = data; + struct tinywl_view *view = rdata->view; + struct wlr_output *output = rdata->output; + + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (texture == NULL) { + return; + } + + double ox, oy; + wlr_output_layout_output_coords( + view->server->output_layout, output, &ox, &oy); + ox += view->x + sx, oy += view->y + sy; + + struct wlr_box box = { + .x = ox * output->scale, + .y = oy * output->scale, + .width = surface->current.width * output->scale, + .height = surface->current.height * output->scale, + }; + + float matrix[9]; + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + wlr_matrix_project_box(matrix, &box, transform, 0, + output->transform_matrix); + + wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); + + wlr_surface_send_frame_done(surface, rdata->when); +} + +static void output_frame(struct wl_listener *listener, void *data) { + struct tinywl_output *output = + wl_container_of(listener, output, frame); + struct wlr_renderer *renderer = output->server->renderer; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + wlr_output_make_current(output->wlr_output, NULL); + int width, height; + wlr_output_effective_resolution(output->wlr_output, &width, &height); + wlr_renderer_begin(renderer, width, height); + + float color[4] = {0.3, 0.3, 0.3, 1.0}; + wlr_renderer_clear(renderer, color); + + struct tinywl_view *view; + wl_list_for_each_reverse(view, &output->server->views, link) { + if (!view->mapped) { + continue; + } + struct render_data rdata = { + .output = output->wlr_output, + .view = view, + .renderer = renderer, + .when = &now, + }; + wlr_xdg_surface_for_each_surface(view->xdg_surface, + render_surface, &rdata); + } + + wlr_output_swap_buffers(output->wlr_output, NULL, NULL); + wlr_renderer_end(renderer); +} + +static void server_new_output(struct wl_listener *listener, void *data) { + struct tinywl_server *server = + wl_container_of(listener, server, new_output); + struct wlr_output *wlr_output = data; + + struct tinywl_output *output = + calloc(1, sizeof(struct tinywl_output)); + output->wlr_output = wlr_output; + output->server = server; + output->frame.notify = output_frame; + wl_signal_add(&wlr_output->events.frame, &output->frame); + wl_list_insert(&server->outputs, &output->link); + + wlr_output_layout_add_auto(server->output_layout, wlr_output); + + wlr_output_create_global(wlr_output); +} + +static void xdg_surface_map(struct wl_listener *listener, void *data) { + struct tinywl_view *view = wl_container_of(listener, view, map); + view->mapped = true; + focus_view(view, view->xdg_surface->surface); +} + +static void xdg_surface_unmap(struct wl_listener *listener, void *data) { + struct tinywl_view *view = wl_container_of(listener, view, unmap); + view->mapped = false; +} + +static void xdg_surface_destroy(struct wl_listener *listener, void *data) { + struct tinywl_view *view = wl_container_of(listener, view, destroy); + wl_list_remove(&view->link); + free(view); +} + +static void begin_interactive(struct tinywl_view *view, + enum tinywl_cursor_mode mode, uint32_t edges) { + struct tinywl_server *server = view->server; + struct wlr_surface *focused_surface = + server->seat->pointer_state.focused_surface; + if (view->xdg_surface->surface != focused_surface) { + return; + } + server->grabbed_view = view; + server->cursor_mode = mode; + struct wlr_box geo_box; + wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + if (mode == TINYWL_CURSOR_MOVE) { + server->grab_x = server->cursor->x - view->x; + server->grab_y = server->cursor->y - view->y; + } else { + server->grab_x = server->cursor->x + geo_box.x; + server->grab_y = server->cursor->y + geo_box.y; + } + server->grab_width = geo_box.width; + server->grab_height = geo_box.height; + server->resize_edges = edges; +} + +static void xdg_toplevel_request_move( + struct wl_listener *listener, void *data) { + struct tinywl_view *view = wl_container_of(listener, view, request_move); + begin_interactive(view, TINYWL_CURSOR_MOVE, 0); +} + +static void xdg_toplevel_request_resize( + struct wl_listener *listener, void *data) { + struct wlr_xdg_toplevel_resize_event *event = data; + struct tinywl_view *view = wl_container_of(listener, view, request_resize); + begin_interactive(view, TINYWL_CURSOR_RESIZE, event->edges); +} + +static void server_new_xdg_surface(struct wl_listener *listener, void *data) { + struct tinywl_server *server = + wl_container_of(listener, server, new_xdg_surface); + struct wlr_xdg_surface *xdg_surface = data; + if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return; + } + + struct tinywl_view *view = + calloc(1, sizeof(struct tinywl_view)); + view->server = server; + view->xdg_surface = xdg_surface; + + view->map.notify = xdg_surface_map; + wl_signal_add(&xdg_surface->events.map, &view->map); + view->unmap.notify = xdg_surface_unmap; + wl_signal_add(&xdg_surface->events.unmap, &view->unmap); + view->destroy.notify = xdg_surface_destroy; + wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + + struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; + view->request_move.notify = xdg_toplevel_request_move; + wl_signal_add(&toplevel->events.request_move, &view->request_move); + view->request_resize.notify = xdg_toplevel_request_resize; + wl_signal_add(&toplevel->events.request_resize, &view->request_resize); + + wl_list_insert(&server->views, &view->link); +} + +int main(int argc, char *argv[]) { + char *startup_cmd = NULL; + + int c; + while ((c = getopt(argc, argv, "s:h")) != -1) { + switch (c) { + case 's': + startup_cmd = optarg; + break; + default: + printf("Usage: %s [-s startup command]\n", argv[0]); + return 0; + } + } + if (optind < argc) { + printf("Usage: %s [-s startup command]\n", argv[0]); + return 0; + } + + struct tinywl_server server; + server.wl_display = wl_display_create(); + server.backend = wlr_backend_autocreate(server.wl_display, NULL); + + server.renderer = wlr_backend_get_renderer(server.backend); + wlr_renderer_init_wl_display(server.renderer, server.wl_display); + + wlr_compositor_create(server.wl_display, server.renderer); + wlr_linux_dmabuf_v1_create(server.wl_display, server.renderer); + wlr_data_device_manager_create(server.wl_display); + + server.output_layout = wlr_output_layout_create(); + + wl_list_init(&server.outputs); + server.new_output.notify = server_new_output; + wl_signal_add(&server.backend->events.new_output, &server.new_output); + + wl_list_init(&server.views); + server.xdg_shell = wlr_xdg_shell_create(server.wl_display); + server.new_xdg_surface.notify = server_new_xdg_surface; + wl_signal_add(&server.xdg_shell->events.new_surface, + &server.new_xdg_surface); + + server.cursor = wlr_cursor_create(); + wlr_cursor_attach_output_layout(server.cursor, server.output_layout); + + server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); + wlr_xcursor_manager_load(server.cursor_mgr, 1); + + server.cursor_motion.notify = server_cursor_motion; + wl_signal_add(&server.cursor->events.motion, &server.cursor_motion); + server.cursor_motion_absolute.notify = server_cursor_motion_absolute; + wl_signal_add(&server.cursor->events.motion_absolute, + &server.cursor_motion_absolute); + server.cursor_button.notify = server_cursor_button; + wl_signal_add(&server.cursor->events.button, &server.cursor_button); + server.cursor_axis.notify = server_cursor_axis; + wl_signal_add(&server.cursor->events.axis, &server.cursor_axis); + + wl_list_init(&server.keyboards); + server.new_input.notify = server_new_input; + wl_signal_add(&server.backend->events.new_input, &server.new_input); + server.seat = wlr_seat_create(server.wl_display, "seat0"); + server.request_cursor.notify = seat_request_cursor; + wl_signal_add(&server.seat->events.request_set_cursor, + &server.request_cursor); + + const char *socket = wl_display_add_socket_auto(server.wl_display); + if (!socket) { + wlr_backend_destroy(server.backend); + return 1; + } + + if (!wlr_backend_start(server.backend)) { + wlr_backend_destroy(server.backend); + wl_display_destroy(server.wl_display); + return 1; + } + + setenv("WAYLAND_DISPLAY", socket, true); + if (startup_cmd) { + if (fork() == 0) { + execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); + } + } + wl_display_run(server.wl_display); + + wl_display_destroy_clients(server.wl_display); + wl_display_destroy(server.wl_display); + return 0; +} -- cgit v1.2.3 From dc9fc13c68a9fd4f1f4d7027370ffaed72ad9047 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 15 Aug 2018 18:48:31 -0400 Subject: Fix various bugs --- tinywl.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tinywl.c b/tinywl.c index 101cd53..d542bee 100644 --- a/tinywl.c +++ b/tinywl.c @@ -220,6 +220,9 @@ static struct tinywl_view *desktop_view_at( } static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { + if (view == NULL) { + return; + } struct tinywl_server *server = view->server; struct wlr_seat *seat = server->seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; @@ -286,15 +289,13 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { double sx, sy; struct wlr_seat *seat = server->seat; - struct wlr_surface *surface; + struct wlr_surface *surface = NULL; struct tinywl_view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (!view) { wlr_xcursor_manager_set_cursor_image( server->cursor_mgr, "left_ptr", server->cursor); - return; } - if (surface) { bool focus_changed = seat->pointer_state.focused_surface != surface; wlr_seat_pointer_notify_enter(seat, surface, sx, sy); @@ -308,7 +309,7 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { static void server_cursor_motion(struct wl_listener *listener, void *data) { struct tinywl_server *server = - wl_container_of(listener, server, cursor_motion_absolute); + wl_container_of(listener, server, cursor_motion); struct wlr_event_pointer_motion *event = data; wlr_cursor_move(server->cursor, event->device, event->delta_x, event->delta_y); @@ -400,7 +401,9 @@ static void output_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_output_make_current(output->wlr_output, NULL); + if (!wlr_output_make_current(output->wlr_output, NULL)) { + return; + } int width, height; wlr_output_effective_resolution(output->wlr_output, &width, &height); wlr_renderer_begin(renderer, width, height); @@ -423,8 +426,8 @@ static void output_frame(struct wl_listener *listener, void *data) { render_surface, &rdata); } - wlr_output_swap_buffers(output->wlr_output, NULL, NULL); wlr_renderer_end(renderer); + wlr_output_swap_buffers(output->wlr_output, NULL, NULL); } static void server_new_output(struct wl_listener *listener, void *data) { @@ -432,6 +435,12 @@ static void server_new_output(struct wl_listener *listener, void *data) { wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = + wl_container_of(wlr_output->modes.prev, mode, link); + wlr_output_set_mode(wlr_output, mode); + } + struct tinywl_output *output = calloc(1, sizeof(struct tinywl_output)); output->wlr_output = wlr_output; @@ -529,6 +538,7 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { } int main(int argc, char *argv[]) { + wlr_log_init(WLR_DEBUG, NULL); char *startup_cmd = NULL; int c; -- cgit v1.2.3 From fc3609dba2fa809904ecce19a10d21ac01466f28 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 15 Aug 2018 18:54:34 -0400 Subject: Fix uninitialized variable --- tinywl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index d542bee..9c749d0 100644 --- a/tinywl.c +++ b/tinywl.c @@ -370,7 +370,7 @@ static void render_surface(struct wlr_surface *surface, return; } - double ox, oy; + double ox = 0, oy = 0; wlr_output_layout_output_coords( view->server->output_layout, output, &ox, &oy); ox += view->x + sx, oy += view->y + sy; -- cgit v1.2.3 From 4ebe2c7188275d9e86b054e1db9f1e8198f922ee Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 15 Aug 2018 19:12:27 -0400 Subject: Implement Alt+F1 (next window) and Alt+Esc (exit) --- tinywl.c | 97 +++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 28 deletions(-) diff --git a/tinywl.c b/tinywl.c index 9c749d0..6939b60 100644 --- a/tinywl.c +++ b/tinywl.c @@ -96,6 +96,29 @@ struct tinywl_pointer { struct wlr_input_device *device; }; +static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { + if (view == NULL) { + return; + } + struct tinywl_server *server = view->server; + struct wlr_seat *seat = server->seat; + struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; + if (prev_surface == surface) { + return; + } + if (prev_surface) { + struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( + seat->keyboard_state.focused_surface); + wlr_xdg_toplevel_set_activated(previous, false); + } + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); + wl_list_remove(&view->link); + wl_list_insert(&server->views, &view->link); + wlr_xdg_toplevel_set_activated(view->xdg_surface, true); + wlr_seat_keyboard_notify_enter(seat, view->xdg_surface->surface, + keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); +} + static void keyboard_handle_modifiers( struct wl_listener *listener, void *data) { struct tinywl_keyboard *keyboard = @@ -105,16 +128,57 @@ static void keyboard_handle_modifiers( &keyboard->device->keyboard->modifiers); } +static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { + switch (sym) { + case XKB_KEY_Escape: + wl_display_terminate(server->wl_display); + break; + case XKB_KEY_F1: + /* Cycle to the next view */ + if (wl_list_length(&server->views) < 2) { + break; + } + struct tinywl_view *current_view = wl_container_of( + server->views.next, current_view, link); + struct tinywl_view *next_view = wl_container_of( + current_view->link.next, next_view, link); + focus_view(next_view, next_view->xdg_surface->surface); + /* Move the previous view to the end of the list */ + wl_list_remove(¤t_view->link); + wl_list_insert(server->views.prev, ¤t_view->link); + break; + default: + return false; + } + return true; +} + static void keyboard_handle_key( struct wl_listener *listener, void *data) { struct tinywl_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct tinywl_server *server = keyboard->server; struct wlr_event_keyboard_key *event = data; - struct wlr_seat *seat = keyboard->server->seat; - // TODO: keybindings for moving windows about - wlr_seat_set_keyboard(seat, keyboard->device); - wlr_seat_keyboard_notify_key(seat, event->time_msec, - event->keycode, event->state); + struct wlr_seat *seat = server->seat; + + 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); + + bool handled = false; + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); + if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_BUTTON_PRESSED) { + for (int i = 0; i < nsyms; i++) { + handled = handle_keybinding(server, syms[i]); + } + } + + if (!handled) { + wlr_seat_set_keyboard(seat, keyboard->device); + wlr_seat_keyboard_notify_key(seat, event->time_msec, + event->keycode, event->state); + } } static void server_new_keyboard(struct tinywl_server *server, @@ -219,29 +283,6 @@ static struct tinywl_view *desktop_view_at( return NULL; } -static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { - if (view == NULL) { - return; - } - struct tinywl_server *server = view->server; - struct wlr_seat *seat = server->seat; - struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; - if (prev_surface == surface) { - return; - } - if (prev_surface) { - struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( - seat->keyboard_state.focused_surface); - wlr_xdg_toplevel_set_activated(previous, false); - } - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); - wl_list_remove(&view->link); - wl_list_insert(&server->views, &view->link); - wlr_xdg_toplevel_set_activated(view->xdg_surface, true); - wlr_seat_keyboard_notify_enter(seat, view->xdg_surface->surface, - keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); -} - static void process_cursor_move(struct tinywl_server *server, uint32_t time) { server->grabbed_view->x = server->cursor->x - server->grab_x; server->grabbed_view->y = server->cursor->y - server->grab_y; -- cgit v1.2.3 From 7a63d7898086b9965f84f3b8e03343eff1d64afc Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 15 Aug 2018 19:17:20 -0400 Subject: CC0 --- LICENSE | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0153f30 --- /dev/null +++ b/LICENSE @@ -0,0 +1,125 @@ +This work is licensed under CC0, which effectively puts it in the public domain. + +--- + +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. -- cgit v1.2.3 From ba9cdd6c66fe2b85dbe9516eb9f6a65a2a7686c7 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 15 Aug 2018 19:19:41 -0400 Subject: Add README --- README | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 README diff --git a/README b/README new file mode 100644 index 0000000..eb39a33 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ + TinyWL + +Tiny Wayland compositor. + +Send patches to: ~sircmpwn/public-inbox@lists.sr.ht -- cgit v1.2.3 From 5e44d3b49815ea1badf3276b763bb6eb5a2c00b9 Mon Sep 17 00:00:00 2001 From: Louis Taylor Date: Mon, 26 Nov 2018 22:35:20 +0000 Subject: Correct WLR_BUTTON_PRESSED to WLR_KEY_PRESSED This worked since it was the same value, but results in compile warnings. --- tinywl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index 6939b60..bf98495 100644 --- a/tinywl.c +++ b/tinywl.c @@ -168,7 +168,7 @@ static void keyboard_handle_key( bool handled = false; uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); - if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_BUTTON_PRESSED) { + if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_KEY_PRESSED) { for (int i = 0; i < nsyms; i++) { handled = handle_keybinding(server, syms[i]); } -- cgit v1.2.3 From 87300491f85b8f137bd2ba31f02efd05aa2d7724 Mon Sep 17 00:00:00 2001 From: Louis Taylor Date: Tue, 27 Nov 2018 20:26:02 +0000 Subject: view_at: remove unused variable --- tinywl.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tinywl.c b/tinywl.c index bf98495..6a63e03 100644 --- a/tinywl.c +++ b/tinywl.c @@ -251,10 +251,6 @@ static bool view_at(struct tinywl_view *view, double view_sy = ly - view->y; struct wlr_surface_state *state = &view->xdg_surface->surface->current; - struct wlr_box box = { - .x = 0, .y = 0, - .width = state->width, .height = state->height, - }; double _sx, _sy; struct wlr_surface *_surface = NULL; -- cgit v1.2.3 From 35f6c05c89520d271f6d4b14c0789a7ab29b7776 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 3 Jan 2019 10:10:03 -0500 Subject: Update READMEs per tinywl merge --- README | 5 ----- README.md | 33 +++++++++++++++++++++++++++++++++ tinywl.c | 2 ++ 3 files changed, 35 insertions(+), 5 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index eb39a33..0000000 --- a/README +++ /dev/null @@ -1,5 +0,0 @@ - TinyWL - -Tiny Wayland compositor. - -Send patches to: ~sircmpwn/public-inbox@lists.sr.ht diff --git a/README.md b/README.md new file mode 100644 index 0000000..4be9852 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# TinyWL + +This is the "minimum viable product" Wayland compositor based on wlroots. It +aims to implement a Wayland compositor in the fewest lines of code possible, +while still supporting a reasonable set of features. Reading this code is the +best starting point for anyone looking to build their own Wayland compositor +based on wlroots. + +## Building TinyWL + +TinyWL is disconencted from the main wlroots build system, in order to make it +easier to understand the build requirements for your own Wayland compositors. +Simply install the dependencies: + +- wlroots +- wayland-protocols + +And run `make`. + +## Running TinyWL + +You can run TinyWL with `./tinywl`. In an existing Wayland or X11 session, +tinywl will open a Wayland or X11 window respectively to act as a virtual +display. You can then open Wayland windows by setting `WAYLAND_DISPLAY` to the +value shown in the logs. You can also run `./tinywl` from a TTY. + +In either case, you will likely want to specify `-s [cmd]` to run a command at +startup, such as a terminal emulator. This will be necessary to start any new +programs from within the compositor, as TinyWL does not support any custom +keybindings. TinyWL supports the following keybindings: + +- `Alt+Escape`: Terminate the compositor +- `Alt+F1`: Cycle between windows diff --git a/tinywl.c b/tinywl.c index 6a63e03..d370b04 100644 --- a/tinywl.c +++ b/tinywl.c @@ -659,6 +659,8 @@ int main(int argc, char *argv[]) { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); } } + wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", + socket); wl_display_run(server.wl_display); wl_display_destroy_clients(server.wl_display); -- cgit v1.2.3 From 411499469b029ee59d1e0f8e0a75c4a98b51ee49 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 3 Jan 2019 10:55:14 -0500 Subject: Annotate the source --- Makefile | 3 + README.md | 14 ++++ tinywl.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+) diff --git a/Makefile b/Makefile index 3ecd467..4d988fa 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ WAYLAND_PROTOCOLS=/usr/share/wayland-protocols +# wayland-scanner is a tool which generates C headers and rigging for Wayland +# protocols, which are specified in XML. wlroots requires you to rig these up +# to your build system yourself and provide them in the include path. xdg-shell-protocol.h: wayland-scanner server-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ diff --git a/README.md b/README.md index 4be9852..caacc41 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,17 @@ keybindings. TinyWL supports the following keybindings: - `Alt+Escape`: Terminate the compositor - `Alt+F1`: Cycle between windows + +## Limitations + +Notable omissions from TinyWL: + +- HiDPI support +- Any kind of configuration, e.g. output layout +- Any protocol other than xdg-shell (e.g. layer-shell, for + panels/taskbars/etc; or Xwayland, for proxied X11 windows) +- Optional protocols, e.g. screen capture, primary selection, virtual + keyboard, etc. Most of these are plug-and-play with wlroots, but they're + omitted for brevity. +- Damage tracking, which tracks which parts of the screen are changing and + minimizes redraws accordingly. diff --git a/tinywl.c b/tinywl.c index d370b04..069f645 100644 --- a/tinywl.c +++ b/tinywl.c @@ -24,6 +24,7 @@ #include #include +/* For brevity's sake, struct members are annotated where they are used. */ enum tinywl_cursor_mode { TINYWL_CURSOR_PASSTHROUGH, TINYWL_CURSOR_MOVE, @@ -97,6 +98,7 @@ struct tinywl_pointer { }; static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { + /* Note: this function only deals with keyboard focus. */ if (view == NULL) { return; } @@ -104,31 +106,60 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { struct wlr_seat *seat = server->seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; if (prev_surface == surface) { + /* Don't re-focus an already focused surface. */ return; } if (prev_surface) { + /* + * Deactivate the previously focused surface. This lets the client know + * it no longer has focus and the client will repaint accordingly, e.g. + * stop displaying a caret. + */ struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( seat->keyboard_state.focused_surface); wlr_xdg_toplevel_set_activated(previous, false); } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); + /* Move the view to the front */ wl_list_remove(&view->link); wl_list_insert(&server->views, &view->link); + /* Activate the new surface */ wlr_xdg_toplevel_set_activated(view->xdg_surface, true); + /* + * Tell the seat to have the keyboard enter this surface. wlroots will keep + * track of this and automatically send key events to the appropriate + * clients without additional work on your part. + */ wlr_seat_keyboard_notify_enter(seat, view->xdg_surface->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } static void keyboard_handle_modifiers( struct wl_listener *listener, void *data) { + /* This event is raised when a modifier key, such as shift or alt, is + * pressed. We simply communicate this to the client. */ struct tinywl_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers); + /* + * A seat can only have one keyboard, but this is a limitation of the + * Wayland protocol - not wlroots. We assign all connected keyboards to the + * same seat. You can swap out the underlying wlr_keyboard like this and + * wlr_seat handles this transparently. + */ wlr_seat_set_keyboard(keyboard->server->seat, keyboard->device); + /* Send modifiers to the client. */ wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, &keyboard->device->keyboard->modifiers); } static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { + /* + * Here we handle compositor keybindings. This is when the compositor is + * processing keys, rather than passing them on to the client for its own + * processing. + * + * This function assumes Alt is held down. + */ switch (sym) { case XKB_KEY_Escape: wl_display_terminate(server->wl_display); @@ -155,13 +186,16 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { static void keyboard_handle_key( struct wl_listener *listener, void *data) { + /* This event is raised when a key is pressed or released. */ struct tinywl_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct tinywl_server *server = keyboard->server; struct wlr_event_keyboard_key *event = data; struct wlr_seat *seat = server->seat; + /* Translate libinput keycode -> xkbcommon */ uint32_t keycode = event->keycode + 8; + /* Get a list of keysyms based on the keymap for this keyboard */ const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms( keyboard->device->keyboard->xkb_state, keycode, &syms); @@ -169,12 +203,15 @@ static void keyboard_handle_key( bool handled = false; uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_KEY_PRESSED) { + /* If alt is held down and this button was _pressed_, we attempt to + * process it as a compositor keybinding. */ for (int i = 0; i < nsyms; i++) { handled = handle_keybinding(server, syms[i]); } } if (!handled) { + /* Otherwise, we pass it along to the client. */ wlr_seat_set_keyboard(seat, keyboard->device); wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state); @@ -188,6 +225,8 @@ static void server_new_keyboard(struct tinywl_server *server, keyboard->server = server; keyboard->device = device; + /* We need to prepare an XKB keymap and assign it to the keyboard. This + * assumes the defaults (e.g. layout = "us"). */ struct xkb_rule_names rules = { 0 }; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, @@ -198,6 +237,7 @@ static void server_new_keyboard(struct tinywl_server *server, xkb_context_unref(context); wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); + /* Here we set up listeners for keyboard events. */ keyboard->modifiers.notify = keyboard_handle_modifiers; wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); keyboard->key.notify = keyboard_handle_key; @@ -205,15 +245,22 @@ static void server_new_keyboard(struct tinywl_server *server, wlr_seat_set_keyboard(server->seat, device); + /* And add the keyboard to our list of keyboards */ wl_list_insert(&server->keyboards, &keyboard->link); } static void server_new_pointer(struct tinywl_server *server, struct wlr_input_device *device) { + /* We don't do anything special with pointers. All of our pointer handling + * is proxied through wlr_cursor. On another compositor, you might take this + * opportunity to do libinput configuration on the device to set + * acceleration, etc. */ wlr_cursor_attach_input_device(server->cursor, device); } static void server_new_input(struct wl_listener *listener, void *data) { + /* This event is raised by the backend when a new input device becomes + * available. */ struct tinywl_server *server = wl_container_of(listener, server, new_input); struct wlr_input_device *device = data; @@ -225,6 +272,9 @@ static void server_new_input(struct wl_listener *listener, void *data) { server_new_pointer(server, device); break; } + /* We need to let the wlr_seat know what our capabilities are, which is + * communiciated to the client. In TinyWL we always have a cursor, even if + * there are no pointer devices, so we always include that capability. */ uint32_t caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&server->keyboards)) { caps |= WL_SEAT_CAPABILITY_KEYBOARD; @@ -235,10 +285,17 @@ static void server_new_input(struct wl_listener *listener, void *data) { static void seat_request_cursor(struct wl_listener *listener, void *data) { struct tinywl_server *server = wl_container_of( listener, server, request_cursor); + /* This event is rasied by the seat when a client provides a cursor image */ struct wlr_seat_pointer_request_set_cursor_event *event = data; struct wlr_seat_client *focused_client = server->seat->pointer_state.focused_client; + /* This can be sent by any client, so we check to make sure this one is + * actually has pointer focus first. */ if (focused_client == event->seat_client) { + /* Once we've vetted the client, we can tell the cursor to use the + * provided surface as the cursor image. It will set the hardware cursor + * on the output that it's currently on and continue to do so as the + * cursor moves between outputs. */ wlr_cursor_set_surface(server->cursor, event->surface, event->hotspot_x, event->hotspot_y); } @@ -247,6 +304,13 @@ static void seat_request_cursor(struct wl_listener *listener, void *data) { static bool view_at(struct tinywl_view *view, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { + /* + * XDG toplevels may have nested surfaces, such as popup windows for context + * menus or tooltips. This function tests if any of those are underneath the + * coordinates lx and ly (in output Layout Coordinates). If so, it sets the + * surface pointer to that wlr_surface and the sx and sy coordinates to the + * coordinates relative to that surface's top-left corner. + */ double view_sx = lx - view->x; double view_sy = ly - view->y; @@ -270,6 +334,8 @@ static bool view_at(struct tinywl_view *view, static struct tinywl_view *desktop_view_at( struct tinywl_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { + /* This iterates over all of our surfaces and attempts to find one under the + * cursor. This relies on server->views being ordered from top-to-bottom. */ struct tinywl_view *view; wl_list_for_each(view, &server->views, link) { if (view_at(view, lx, ly, surface, sx, sy)) { @@ -280,11 +346,22 @@ static struct tinywl_view *desktop_view_at( } static void process_cursor_move(struct tinywl_server *server, uint32_t time) { + /* Move the grabbed view to the new position. */ server->grabbed_view->x = server->cursor->x - server->grab_x; server->grabbed_view->y = server->cursor->y - server->grab_y; } static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { + /* + * Resizing the grabbed view can be a little bit complicated, because we + * could be resizing from any corner or edge. This not only resizes the view + * on one or two axes, but can also move the view if you resize from the top + * or left edges (or top-left corner). + * + * Note that I took some shortcuts here. In a more fleshed-out compositor, + * you'd wait for the client to prepare a buffer at the new size, then + * commit any movement that was prepared. + */ struct tinywl_view *view = server->grabbed_view; double dx = server->cursor->x - server->grab_x; double dy = server->cursor->y - server->grab_y; @@ -316,6 +393,7 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { } static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { + /* If the mode is non-passthrough, delegate to those functions. */ if (server->cursor_mode == TINYWL_CURSOR_MOVE) { process_cursor_move(server, time); return; @@ -324,30 +402,53 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { return; } + /* Otherwise, find the view under the pointer and send the event along. */ double sx, sy; struct wlr_seat *seat = server->seat; struct wlr_surface *surface = NULL; struct tinywl_view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (!view) { + /* If there's no view under the cursor, set the cursor image to a + * default. This is what makes the cursor image appear when you move it + * around the screen, not over any views. */ wlr_xcursor_manager_set_cursor_image( server->cursor_mgr, "left_ptr", server->cursor); } if (surface) { bool focus_changed = seat->pointer_state.focused_surface != surface; + /* + * "Enter" the surface if necessary. This lets the client know that the + * cursor has entered one of its surfaces. + * + * Note that this gives the surface "pointer focus", which is distinct + * from keyboard focus. You get pointer focus by moving the pointer over + * a window. + */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); if (!focus_changed) { + /* The enter event contains coordinates, so we only need to notify + * on motion if the focus did not change. */ wlr_seat_pointer_notify_motion(seat, time, sx, sy); } } else { + /* Clear pointer focus so future button events and such are not sent to + * the last client to have the cursor over it. */ wlr_seat_pointer_clear_focus(seat); } } static void server_cursor_motion(struct wl_listener *listener, void *data) { + /* This event is forwarded by the cursor when a pointer emits a _relative_ + * pointer motion event (i.e. a delta) */ struct tinywl_server *server = wl_container_of(listener, server, cursor_motion); struct wlr_event_pointer_motion *event = data; + /* The cursor doesn't move unless we tell it to. The cursor automatically + * handles constraining the motion to the output layout, as well as any + * special configuration applied for the specific input device which + * generated the event. You can pass NULL for the device if you want to move + * the cursor around without any input. */ wlr_cursor_move(server->cursor, event->device, event->delta_x, event->delta_y); process_cursor_motion(server, event->time_msec); @@ -355,6 +456,12 @@ static void server_cursor_motion(struct wl_listener *listener, void *data) { static void server_cursor_motion_absolute( struct wl_listener *listener, void *data) { + /* This event is forwarded by the cursor when a pointer emits an _absolute_ + * motion event, from 0..1 on each axis. This happens, for example, when + * wlroots is running under a Wayland window rather than KMS+DRM, and you + * move the mouse over the window. You could enter the window from any edge, + * so we have to warp the mouse there. There is also some hardware which + * emits these events. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_motion_absolute); struct wlr_event_pointer_motion_absolute *event = data; @@ -363,9 +470,12 @@ static void server_cursor_motion_absolute( } static void server_cursor_button(struct wl_listener *listener, void *data) { + /* This event is forwarded by the cursor when a pointer emits a button + * event. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_button); struct wlr_event_pointer_button *event = data; + /* Notify the client with pointer focus that a button press has occured */ wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); double sx, sy; @@ -374,21 +484,28 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { struct tinywl_view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { + /* If you released any buttons, we exit interactive move/resize mode. */ server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; } else { + /* Focus that client if the button was _pressed_ */ focus_view(view, surface); } } static void server_cursor_axis(struct wl_listener *listener, void *data) { + /* This event is forwarded by the cursor when a pointer emits an axis event, + * for example when you move the scroll wheel. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_axis); struct wlr_event_pointer_axis *event = data; + /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(server->seat, event->time_msec, event->orientation, event->delta, event->delta_discrete, event->source); } +/* Used to move all of the data necessary to render a surface from the top-level + * frame handler to the per-surface render function. */ struct render_data { struct wlr_output *output; struct wlr_renderer *renderer; @@ -398,20 +515,32 @@ struct render_data { static void render_surface(struct wlr_surface *surface, int sx, int sy, void *data) { + /* This function is called for every surface that needs to be rendered. */ struct render_data *rdata = data; struct tinywl_view *view = rdata->view; struct wlr_output *output = rdata->output; + /* We first obtain a wlr_texture, which is a GPU resource. wlroots + * automatically handles negotiating these with the client. The underlying + * resource could be an opaque handle passed from the client, or the client + * could have sent a pixel buffer which we copied to the GPU, or a few other + * means. You don't have to worry about this, wlroots takes care of it. */ struct wlr_texture *texture = wlr_surface_get_texture(surface); if (texture == NULL) { return; } + /* The view has a position in layout coordinates. If you have two displays, + * one next to the other, both 1080p, a view on the rightmost display might + * have layout coordinates of 2000,100. We need to translate that to + * output-local coordinates, or (2000 - 1920). */ double ox = 0, oy = 0; wlr_output_layout_output_coords( view->server->output_layout, output, &ox, &oy); ox += view->x + sx, oy += view->y + sy; + /* We also have to apply the scale factor for HiDPI outputs. This is only + * part of the puzzle, TinyWL does not fully support HiDPI. */ struct wlr_box box = { .x = ox * output->scale, .y = oy * output->scale, @@ -419,18 +548,35 @@ static void render_surface(struct wlr_surface *surface, .height = surface->current.height * output->scale, }; + /* + * Those familiar with OpenGL are also familiar with the role of matricies + * in graphics programming. We need to prepare a matrix to render the view + * with. wlr_matrix_project_box is a helper which takes a box with a desired + * x, y coodrinates, width and height, and an output geometry, then + * prepares an orthographic projection and multiplies the necessary + * transforms to produce a model-view-projection matrix. + * + * Naturally you can do this any way you like, for example to make a 3D + * compositor. + */ float matrix[9]; enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform); wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix); + /* This takes our matrix, the texture, and an alpha, and performs the actual + * rendering on the GPU. */ wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); + /* This lets the client know that we've displayed that frame and it can + * prepare another one now if it likes. */ wlr_surface_send_frame_done(surface, rdata->when); } static void output_frame(struct wl_listener *listener, void *data) { + /* This function is called every time an output is ready to display a frame, + * generally at the output's refresh rate (e.g. 60Hz). */ struct tinywl_output *output = wl_container_of(listener, output, frame); struct wlr_renderer *renderer = output->server->renderer; @@ -438,19 +584,25 @@ static void output_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); + /* wlr_output_make_current makes the OpenGL context current. */ if (!wlr_output_make_current(output->wlr_output, NULL)) { return; } + /* The "effective" resolution can change if you rotate your outputs. */ int width, height; wlr_output_effective_resolution(output->wlr_output, &width, &height); + /* Begin the renderer (calls glViewport and some other GL sanity checks) */ wlr_renderer_begin(renderer, width, height); float color[4] = {0.3, 0.3, 0.3, 1.0}; wlr_renderer_clear(renderer, color); + /* Each subsequent window we render is rendered on top of the last. Because + * our view list is ordered front-to-back, we iterate over it backwards. */ struct tinywl_view *view; wl_list_for_each_reverse(view, &output->server->views, link) { if (!view->mapped) { + /* An unmapped view should not be rendered. */ continue; } struct render_data rdata = { @@ -459,50 +611,73 @@ static void output_frame(struct wl_listener *listener, void *data) { .renderer = renderer, .when = &now, }; + /* This calls our render_surface function for each surface among the + * xdg_surface's toplevel and popups. */ wlr_xdg_surface_for_each_surface(view->xdg_surface, render_surface, &rdata); } + /* Conclude rendering and swap the buffers, showing the final frame + * on-screen. */ wlr_renderer_end(renderer); wlr_output_swap_buffers(output->wlr_output, NULL, NULL); } static void server_new_output(struct wl_listener *listener, void *data) { + /* This event is rasied by the backend when a new output (aka a display or + * monitor) becomes available. */ struct tinywl_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + /* Some backends don't have modes. DRM+KMS does, and we need to set a mode + * before we can use the output. The mode is a tuple of (width, height, + * refresh rate), and each monitor supports only a specific set of modes. We + * just pick the first, a more sophisticated compositor would let the user + * configure it or pick the mode the display advertises as preferred. */ if (!wl_list_empty(&wlr_output->modes)) { struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.prev, mode, link); wlr_output_set_mode(wlr_output, mode); } + /* Allocates and configures our state for this output */ struct tinywl_output *output = calloc(1, sizeof(struct tinywl_output)); output->wlr_output = wlr_output; output->server = server; + /* Sets up a listener for the frame notify event. */ output->frame.notify = output_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); wl_list_insert(&server->outputs, &output->link); + /* Adds this to the output layout. The add_auto function arranges outputs + * from left-to-right in the order they appear. A more sophisticated + * compositor would let the user configure the arrangement of outputs in the + * layout. */ wlr_output_layout_add_auto(server->output_layout, wlr_output); + /* Creating the global adds a wl_output global to the display, which Wayland + * clients can see to find out information about the output (such as + * DPI, scale factor, manufacturer, etc). */ wlr_output_create_global(wlr_output); } static void xdg_surface_map(struct wl_listener *listener, void *data) { + /* Called when the surface is mapped, or ready to display on-screen. */ struct tinywl_view *view = wl_container_of(listener, view, map); view->mapped = true; focus_view(view, view->xdg_surface->surface); } static void xdg_surface_unmap(struct wl_listener *listener, void *data) { + /* Called when the surface is unmapped, and should no longer be shown. */ struct tinywl_view *view = wl_container_of(listener, view, unmap); view->mapped = false; } static void xdg_surface_destroy(struct wl_listener *listener, void *data) { + /* Called when the surface is destroyed and should never be shown again. */ struct tinywl_view *view = wl_container_of(listener, view, destroy); wl_list_remove(&view->link); free(view); @@ -510,10 +685,14 @@ static void xdg_surface_destroy(struct wl_listener *listener, void *data) { static void begin_interactive(struct tinywl_view *view, enum tinywl_cursor_mode mode, uint32_t edges) { + /* This function sets up an interactive move or resize operation, where the + * compositor stops propegating pointer events to clients and instead + * consumes them itself, to move or resize windows. */ struct tinywl_server *server = view->server; struct wlr_surface *focused_surface = server->seat->pointer_state.focused_surface; if (view->xdg_surface->surface != focused_surface) { + /* Deny move/resize requests from unfocused clients. */ return; } server->grabbed_view = view; @@ -534,18 +713,30 @@ static void begin_interactive(struct tinywl_view *view, static void xdg_toplevel_request_move( struct wl_listener *listener, void *data) { + /* This event is raised when a client would like to begin an interactive + * move, typically because the user clicked on their client-side + * decorations. Note that a more sophisticated compositor should check the + * provied serial against a list of button press serials sent to this + * client, to prevent the client from requesting this whenver they want. */ struct tinywl_view *view = wl_container_of(listener, view, request_move); begin_interactive(view, TINYWL_CURSOR_MOVE, 0); } static void xdg_toplevel_request_resize( struct wl_listener *listener, void *data) { + /* This event is raised when a client would like to begin an interactive + * resize, typically because the user clicked on their client-side + * decorations. Note that a more sophisticated compositor should check the + * provied serial against a list of button press serials sent to this + * client, to prevent the client from requesting this whenver they want. */ struct wlr_xdg_toplevel_resize_event *event = data; struct tinywl_view *view = wl_container_of(listener, view, request_resize); begin_interactive(view, TINYWL_CURSOR_RESIZE, event->edges); } static void server_new_xdg_surface(struct wl_listener *listener, void *data) { + /* This event is raised when wlr_xdg_shell receives a new xdg surface from a + * client, either a toplevel (application window) or popup. */ struct tinywl_server *server = wl_container_of(listener, server, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; @@ -553,11 +744,13 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { return; } + /* Allocate a tinywl_view for this surface */ struct tinywl_view *view = calloc(1, sizeof(struct tinywl_view)); view->server = server; view->xdg_surface = xdg_surface; + /* Listen to the various events it can emit */ view->map.notify = xdg_surface_map; wl_signal_add(&xdg_surface->events.map, &view->map); view->unmap.notify = xdg_surface_unmap; @@ -565,12 +758,14 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { view->destroy.notify = xdg_surface_destroy; wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + /* cotd */ struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; view->request_move.notify = xdg_toplevel_request_move; wl_signal_add(&toplevel->events.request_move, &view->request_move); view->request_resize.notify = xdg_toplevel_request_resize; wl_signal_add(&toplevel->events.request_resize, &view->request_resize); + /* Add it to the list of views. */ wl_list_insert(&server->views, &view->link); } @@ -595,34 +790,83 @@ int main(int argc, char *argv[]) { } struct tinywl_server server; + /* The Wayland display is managed by libwayland. It handles accepting + * clients from the Unix socket, manging Wayland globals, and so on. */ server.wl_display = wl_display_create(); + /* The backend is a wlroots feature which abstracts the underlying input and + * output hardware. The autocreate option will choose the most suitable + * backend based on the current environment, such as opening an X11 window + * if an X11 server is running. The NULL argument here optionally allows you + * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The + * backend uses the renderer, for example, to fall back to software cursors + * if the backend does not support hardware cursors (some older GPUs + * don't). */ server.backend = wlr_backend_autocreate(server.wl_display, NULL); + /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us. + * The renderer is responsible for defining the various pixel formats it + * supports for shared memory, this configures that for clients. */ server.renderer = wlr_backend_get_renderer(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); + /* This creates some hands-off wlroots interfaces. The compositor is + * necessary for clients to allocate surfaces, dmabuf allows them to use + * opaque GPU handles for buffers to avoid copying pixels on the CPU, and + * the data device manager handles the clipboard. Each of these wlroots + * interfaces has room for you to dig your fingers in and play with their + * behavior if you want. */ wlr_compositor_create(server.wl_display, server.renderer); wlr_linux_dmabuf_v1_create(server.wl_display, server.renderer); wlr_data_device_manager_create(server.wl_display); + /* Creates an output layout, which a wlroots utility for working with an + * arrangement of screens in a physical layout. */ server.output_layout = wlr_output_layout_create(); + /* Configure a listener to be notified when new outputs are available on the + * backend. */ wl_list_init(&server.outputs); server.new_output.notify = server_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); + /* Set up our list of views and the xdg-shell. The xdg-shell is a Wayland + * protocol which is used for application windows. For more detail on + * shells, refer to my article: + * + * https://drewdevault.com/2018/07/29/Wayland-shells.html + */ wl_list_init(&server.views); server.xdg_shell = wlr_xdg_shell_create(server.wl_display); server.new_xdg_surface.notify = server_new_xdg_surface; wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface); + /* + * Creates a cursor, which is a wlroots utility for tracking the cursor + * image shown on screen. + */ server.cursor = wlr_cursor_create(); wlr_cursor_attach_output_layout(server.cursor, server.output_layout); + /* Creates an xcursor manager, another wlroots utility which loads up + * Xcursor themes to source cursor images from and makes sure that cursor + * images are available at all scale factors on the screen (necessary for + * HiDPI support). We add a cursor theme at scale factor 1 to begin with. */ server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); wlr_xcursor_manager_load(server.cursor_mgr, 1); + /* + * wlr_cursor *only* displays an image on screen. It does not move around + * when the pointer moves. However, we can attach input devices to it, and + * it will generate aggregate events for all of them. In these events, we + * can choose how we want to process them, forwarding them to clients and + * moving the cursor around. More detail on this process is described in my + * input handling blog post: + * + * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html + * + * And more comments are sprinkled throughout the notify functions above. + */ server.cursor_motion.notify = server_cursor_motion; wl_signal_add(&server.cursor->events.motion, &server.cursor_motion); server.cursor_motion_absolute.notify = server_cursor_motion_absolute; @@ -633,6 +877,12 @@ int main(int argc, char *argv[]) { server.cursor_axis.notify = server_cursor_axis; wl_signal_add(&server.cursor->events.axis, &server.cursor_axis); + /* + * Configures a seat, which is a single "seat" at which a user sits and + * operates the computer. This conceptually includes up to one keyboard, + * pointer, touch, and drawing tablet device. We also rig up a listener to + * let us know when new input devices are available on the backend. + */ wl_list_init(&server.keyboards); server.new_input.notify = server_new_input; wl_signal_add(&server.backend->events.new_input, &server.new_input); @@ -641,28 +891,38 @@ int main(int argc, char *argv[]) { wl_signal_add(&server.seat->events.request_set_cursor, &server.request_cursor); + /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wlr_backend_destroy(server.backend); return 1; } + /* Start the backend. This will enumerate outputs and inputs, become the DRM + * master, etc */ if (!wlr_backend_start(server.backend)) { wlr_backend_destroy(server.backend); wl_display_destroy(server.wl_display); return 1; } + /* Set the WAYLAND_DISPLAY environment variable to our socket and run the + * startup command if requested. */ setenv("WAYLAND_DISPLAY", socket, true); if (startup_cmd) { if (fork() == 0) { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); } } + /* Run the Wayland event loop. This does not return until you exit the + * compositor. Starting the backend rigged up all of the necessary event + * loop configuration to listen to libinput events, DRM events, generate + * frame events at the refresh rate, and so on. */ wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); wl_display_run(server.wl_display); + /* Once wl_display_run returns, we shut down the server. */ wl_display_destroy_clients(server.wl_display); wl_display_destroy(server.wl_display); return 0; -- cgit v1.2.3 From 4c980b6cf1a52c265b48f64da21927aab72b56d1 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 3 Jan 2019 15:39:04 -0500 Subject: Use pkg-config for tinywl deps --- Makefile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 4d988fa..92d2a51 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,26 @@ -WAYLAND_PROTOCOLS=/usr/share/wayland-protocols +WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) +WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) +LIBS=\ + $(shell pkg-config --cflags --libs wlroots) \ + $(shell pkg-config --cflags --libs wayland-server) \ + $(shell pkg-config --cflags --libs xkbcommon) # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up # to your build system yourself and provide them in the include path. xdg-shell-protocol.h: - wayland-scanner server-header \ + $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ xdg-shell-protocol.c: xdg-shell-protocol.h - wayland-scanner private-code \ + $(WAYLAND_SCANNER) private-code \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ tinywl: tinywl.c xdg-shell-protocol.h xdg-shell-protocol.c $(CC) $(CFLAGS) \ -g -Werror -I. \ -DWLR_USE_UNSTABLE \ - $(shell pkg-config --cflags --libs wlroots) \ - $(shell pkg-config --cflags --libs wayland-server) \ - $(shell pkg-config --cflags --libs xkbcommon) \ + $(LIBS) \ -o $@ $< clean: -- cgit v1.2.3 From c5d14579f7beec1378cc28475a3549da5a9c9342 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 10 Jan 2019 09:30:14 +0100 Subject: Fix a few typos --- tinywl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tinywl.c b/tinywl.c index 069f645..57ac5cc 100644 --- a/tinywl.c +++ b/tinywl.c @@ -475,7 +475,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { struct tinywl_server *server = wl_container_of(listener, server, cursor_button); struct wlr_event_pointer_button *event = data; - /* Notify the client with pointer focus that a button press has occured */ + /* Notify the client with pointer focus that a button press has occurred */ wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); double sx, sy; @@ -552,7 +552,7 @@ static void render_surface(struct wlr_surface *surface, * Those familiar with OpenGL are also familiar with the role of matricies * in graphics programming. We need to prepare a matrix to render the view * with. wlr_matrix_project_box is a helper which takes a box with a desired - * x, y coodrinates, width and height, and an output geometry, then + * x, y coordinates, width and height, and an output geometry, then * prepares an orthographic projection and multiplies the necessary * transforms to produce a model-view-projection matrix. * @@ -717,7 +717,7 @@ static void xdg_toplevel_request_move( * move, typically because the user clicked on their client-side * decorations. Note that a more sophisticated compositor should check the * provied serial against a list of button press serials sent to this - * client, to prevent the client from requesting this whenver they want. */ + * client, to prevent the client from requesting this whenever they want. */ struct tinywl_view *view = wl_container_of(listener, view, request_move); begin_interactive(view, TINYWL_CURSOR_MOVE, 0); } @@ -728,7 +728,7 @@ static void xdg_toplevel_request_resize( * resize, typically because the user clicked on their client-side * decorations. Note that a more sophisticated compositor should check the * provied serial against a list of button press serials sent to this - * client, to prevent the client from requesting this whenver they want. */ + * client, to prevent the client from requesting this whenever they want. */ struct wlr_xdg_toplevel_resize_event *event = data; struct tinywl_view *view = wl_container_of(listener, view, request_resize); begin_interactive(view, TINYWL_CURSOR_RESIZE, event->edges); -- cgit v1.2.3 From aa526dcbec7b3bd42f95ffeb0195c795866f07dd Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 10 Jan 2019 21:18:36 -0500 Subject: Remove unused struct from tinywl --- tinywl.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/tinywl.c b/tinywl.c index 57ac5cc..49bce1f 100644 --- a/tinywl.c +++ b/tinywl.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -91,12 +90,6 @@ struct tinywl_keyboard { struct wl_listener key; }; -struct tinywl_pointer { - struct wl_list link; - struct tinywl_server *server; - struct wlr_input_device *device; -}; - static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ if (view == NULL) { @@ -810,13 +803,10 @@ int main(int argc, char *argv[]) { wlr_renderer_init_wl_display(server.renderer, server.wl_display); /* This creates some hands-off wlroots interfaces. The compositor is - * necessary for clients to allocate surfaces, dmabuf allows them to use - * opaque GPU handles for buffers to avoid copying pixels on the CPU, and - * the data device manager handles the clipboard. Each of these wlroots - * interfaces has room for you to dig your fingers in and play with their - * behavior if you want. */ + * necessary for clients to allocate surfaces and the data device manager + * handles the clipboard. Each of these wlroots interfaces has room for you + * to dig your fingers in and play with their behavior if you want. */ wlr_compositor_create(server.wl_display, server.renderer); - wlr_linux_dmabuf_v1_create(server.wl_display, server.renderer); wlr_data_device_manager_create(server.wl_display); /* Creates an output layout, which a wlroots utility for working with an -- cgit v1.2.3 From 3048372ad8d1e117b6ffd60e12c276ca575a6f74 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sun, 13 Jan 2019 17:22:18 +0100 Subject: Fix software cursor rendering for tinywl and some examples --- tinywl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tinywl.c b/tinywl.c index 49bce1f..14a1d08 100644 --- a/tinywl.c +++ b/tinywl.c @@ -610,6 +610,14 @@ static void output_frame(struct wl_listener *listener, void *data) { render_surface, &rdata); } + /* Hardware cursors are rendered by the GPU on a separate plane, and can be + * moved around without re-rendering what's beneath them - which is more + * efficient. However, not all hardware supports hardware cursors. For this + * reason, wlroots provides a software fallback, which we ask it to render + * here. wlr_cursor handles configuring hardware vs software cursors for you, + * and this function is a no-op when hardware cursors are in use. */ + wlr_output_render_software_cursors(output->wlr_output, NULL); + /* Conclude rendering and swap the buffers, showing the final frame * on-screen. */ wlr_renderer_end(renderer); -- cgit v1.2.3 From 2745fca168498fd8424482a0aaf4d412692e7ce4 Mon Sep 17 00:00:00 2001 From: David Kraeutmann Date: Wed, 16 Jan 2019 16:39:14 +0100 Subject: Fix tinywl linking order (#1463) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 92d2a51..505666f 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,8 @@ tinywl: tinywl.c xdg-shell-protocol.h xdg-shell-protocol.c $(CC) $(CFLAGS) \ -g -Werror -I. \ -DWLR_USE_UNSTABLE \ - $(LIBS) \ - -o $@ $< + -o $@ $< \ + $(LIBS) clean: rm -f tinywl xdg-shell-protocol.h xdg-shell-protocol.c -- cgit v1.2.3 From fdd96b4f547cf056ad87d27a14146a592b8a67fe Mon Sep 17 00:00:00 2001 From: Yong Joseph Bakos Date: Sun, 27 Jan 2019 00:47:48 -0800 Subject: tinywl/README: Fix misspelling. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index caacc41..e0385d2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ based on wlroots. ## Building TinyWL -TinyWL is disconencted from the main wlroots build system, in order to make it +TinyWL is disconnected from the main wlroots build system, in order to make it easier to understand the build requirements for your own Wayland compositors. Simply install the dependencies: -- cgit v1.2.3 From b4e1c4ae2ef8034ef668c3d28be5691c2e84d4be Mon Sep 17 00:00:00 2001 From: athrungithub Date: Mon, 25 Feb 2019 20:56:07 -0300 Subject: clang compile fix #1572 clang consider error no enum handled, in BSD and Linux --- tinywl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tinywl.c b/tinywl.c index 14a1d08..66e00c0 100644 --- a/tinywl.c +++ b/tinywl.c @@ -264,6 +264,8 @@ static void server_new_input(struct wl_listener *listener, void *data) { case WLR_INPUT_DEVICE_POINTER: server_new_pointer(server, device); break; + default: + break; } /* We need to let the wlr_seat know what our capabilities are, which is * communiciated to the client. In TinyWL we always have a cursor, even if -- cgit v1.2.3 From 417c67302d2539b397ae568e950651f14ab18d44 Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 2 Mar 2019 11:41:03 +0100 Subject: tinywl: send pointer frame events Fixes https://github.com/swaywm/wlroots/issues/1544 --- tinywl.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tinywl.c b/tinywl.c index 66e00c0..16c1f85 100644 --- a/tinywl.c +++ b/tinywl.c @@ -45,6 +45,7 @@ struct tinywl_server { struct wl_listener cursor_motion_absolute; struct wl_listener cursor_button; struct wl_listener cursor_axis; + struct wl_listener cursor_frame; struct wlr_seat *seat; struct wl_listener new_input; @@ -499,6 +500,17 @@ static void server_cursor_axis(struct wl_listener *listener, void *data) { event->delta_discrete, event->source); } +static void server_cursor_frame(struct wl_listener *listener, void *data) { + /* This event is forwarded by the cursor when a pointer emits an frame + * event. Frame events are sent after regular pointer events to group + * multiple events together. For instance, two axis events may happen at the + * same time, in which case a frame event won't be sent in between. */ + struct tinywl_server *server = + wl_container_of(listener, server, cursor_frame); + /* Notify the client with pointer focus of the frame event. */ + wlr_seat_pointer_notify_frame(server->seat); +} + /* Used to move all of the data necessary to render a surface from the top-level * frame handler to the per-surface render function. */ struct render_data { @@ -876,6 +888,8 @@ int main(int argc, char *argv[]) { wl_signal_add(&server.cursor->events.button, &server.cursor_button); server.cursor_axis.notify = server_cursor_axis; wl_signal_add(&server.cursor->events.axis, &server.cursor_axis); + server.cursor_frame.notify = server_cursor_frame; + wl_signal_add(&server.cursor->events.frame, &server.cursor_frame); /* * Configures a seat, which is a single "seat" at which a user sits and -- cgit v1.2.3 From 132df104fbfa9e32b955ecfaeba46f0a781c26ed Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 22 Apr 2019 12:42:37 +0300 Subject: output: refactor frame submission API This is necessary for direct scan-out and other upcoming features. This patch changes the output API to look like the wl_surface API. Outputs now have some double-buffered state: the frame to be submitted (currently only wlr_renderer frames are supported) and the damaged region. To attach a pending frame, use wlr_output_attach_render. To set the pending damaged region, use wlr_output_set_damage. To submit the pending state, call wlr_output_commit. This will submit the pending frame to the backend. To migrate from the old API to the new one: - Replace wlr_output_make_current calls by wlr_output_attach_render - Replace wlr_output_swap_buffers calls by wlr_output_set_damage and wlr_output_commit --- tinywl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tinywl.c b/tinywl.c index 16c1f85..ca5cdc9 100644 --- a/tinywl.c +++ b/tinywl.c @@ -591,8 +591,8 @@ static void output_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - /* wlr_output_make_current makes the OpenGL context current. */ - if (!wlr_output_make_current(output->wlr_output, NULL)) { + /* wlr_output_attach_render makes the OpenGL context current. */ + if (!wlr_output_attach_render(output->wlr_output, NULL)) { return; } /* The "effective" resolution can change if you rotate your outputs. */ @@ -635,7 +635,7 @@ static void output_frame(struct wl_listener *listener, void *data) { /* Conclude rendering and swap the buffers, showing the final frame * on-screen. */ wlr_renderer_end(renderer); - wlr_output_swap_buffers(output->wlr_output, NULL, NULL); + wlr_output_commit(output->wlr_output); } static void server_new_output(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From 5f589038c8fec55521fc6a09117e70345528157a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 27 Jul 2019 11:53:54 +0300 Subject: Remove all wayland-server.h includes The documentation for wayland-server.h says: > Use of this header file is discouraged. Prefer including > wayland-server-core.h instead, which does not include the server protocol > header and as such only defines the library PI, excluding the deprecated API > below. Replacing wayland-server.h with wayland-server-core.h allows us to drop the WL_HIDE_DEPRECATED declaration. --- tinywl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index ca5cdc9..3ea7d07 100644 --- a/tinywl.c +++ b/tinywl.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 8067c84101d9966aac2fbf22a4a61fa35efd71de Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 28 Dec 2019 13:18:38 +0100 Subject: tinywl: enable and commit output when modesetting While at it, choose the preferred mode instead of the last one. --- tinywl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tinywl.c b/tinywl.c index 3ea7d07..e98aaf3 100644 --- a/tinywl.c +++ b/tinywl.c @@ -648,12 +648,15 @@ static void server_new_output(struct wl_listener *listener, void *data) { /* Some backends don't have modes. DRM+KMS does, and we need to set a mode * before we can use the output. The mode is a tuple of (width, height, * refresh rate), and each monitor supports only a specific set of modes. We - * just pick the first, a more sophisticated compositor would let the user - * configure it or pick the mode the display advertises as preferred. */ + * just pick the monitor's preferred mode, a more sophisticated compositor + * would let the user configure it. */ if (!wl_list_empty(&wlr_output->modes)) { - struct wlr_output_mode *mode = - wl_container_of(wlr_output->modes.prev, mode, link); + struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); wlr_output_set_mode(wlr_output, mode); + wlr_output_enable(wlr_output, true); + if (!wlr_output_commit(wlr_output)) { + return; + } } /* Allocates and configures our state for this output */ -- cgit v1.2.3 From bb1fb4bbbf9fa63ee033cb7ca94cdab0dd6523ad Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 15 Apr 2020 12:36:34 +0200 Subject: tinywl: remove redundant create output global call --- tinywl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tinywl.c b/tinywl.c index e98aaf3..ec2e549 100644 --- a/tinywl.c +++ b/tinywl.c @@ -672,13 +672,13 @@ static void server_new_output(struct wl_listener *listener, void *data) { /* Adds this to the output layout. The add_auto function arranges outputs * from left-to-right in the order they appear. A more sophisticated * compositor would let the user configure the arrangement of outputs in the - * layout. */ + * layout. + * + * The output layout utility automatically adds a wl_output global to the + * display, which Wayland clients can see to find out information about the + * output (such as DPI, scale factor, manufacturer, etc). + */ wlr_output_layout_add_auto(server->output_layout, wlr_output); - - /* Creating the global adds a wl_output global to the display, which Wayland - * clients can see to find out information about the output (such as - * DPI, scale factor, manufacturer, etc). */ - wlr_output_create_global(wlr_output); } static void xdg_surface_map(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From ce07a9bde4cda0a0b0a7ceff1ad568f7761acebc Mon Sep 17 00:00:00 2001 From: Kalyan Sriram Date: Thu, 23 Apr 2020 01:00:06 -0700 Subject: tinywl: fix geo_box bug in cursor resizing While trying out the tinywl code, I found that the resize mode was behaving weirdly ... so I looked into code. Turns out the `begin_interactive` method stores the cursor position plus the geo_box position; however, `process_cursor_resize` wasn't taking this into account, causing windows to jump down in size unexpectedly when resized and lose alignment with the cursor. To fix this, I simply added a member to the `tinywl_server` struct that stores the geo_box when the mouse enters grab mode, and I referenced that data in the resize method. I considered polling for this data every time instead of storing it in the server struct, but 1) since changes in this value are not relevant and 2) it could potentially decrease performance (I don't know enough about wlroots to know how much) I decided to just store it. I can change this if desired. --- tinywl.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tinywl.c b/tinywl.c index ec2e549..c44a717 100644 --- a/tinywl.c +++ b/tinywl.c @@ -54,6 +54,7 @@ struct tinywl_server { enum tinywl_cursor_mode cursor_mode; struct tinywl_view *grabbed_view; double grab_x, grab_y; + struct wlr_box grab_geo_box; int grab_width, grab_height; uint32_t resize_edges; @@ -366,22 +367,22 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { int width = server->grab_width; int height = server->grab_height; if (server->resize_edges & WLR_EDGE_TOP) { - y = server->grab_y + dy; - height -= dy; + y = server->grab_y + dy - server->grab_geo_box.y; + height -= dy + server->grab_geo_box.y; if (height < 1) { y += height; } } else if (server->resize_edges & WLR_EDGE_BOTTOM) { - height += dy; + height += dy + server->grab_geo_box.y; } if (server->resize_edges & WLR_EDGE_LEFT) { - x = server->grab_x + dx; - width -= dx; + x = server->grab_x + dx - server->grab_geo_box.x; + width -= dx + server->grab_geo_box.x; if (width < 1) { x += width; } } else if (server->resize_edges & WLR_EDGE_RIGHT) { - width += dx; + width += dx + server->grab_geo_box.x; } view->x = x; view->y = y; @@ -715,17 +716,16 @@ static void begin_interactive(struct tinywl_view *view, } server->grabbed_view = view; server->cursor_mode = mode; - struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + wlr_xdg_surface_get_geometry(view->xdg_surface, &server->grab_geo_box); if (mode == TINYWL_CURSOR_MOVE) { server->grab_x = server->cursor->x - view->x; server->grab_y = server->cursor->y - view->y; } else { - server->grab_x = server->cursor->x + geo_box.x; - server->grab_y = server->cursor->y + geo_box.y; + server->grab_x = server->cursor->x + server->grab_geo_box.x; + server->grab_y = server->cursor->y + server->grab_geo_box.y; } - server->grab_width = geo_box.width; - server->grab_height = geo_box.height; + server->grab_width = server->grab_geo_box.width; + server->grab_height = server->grab_geo_box.height; server->resize_edges = edges; } -- cgit v1.2.3 From d214b27fd7d1d2d2839209e18ac3de667c79e6d0 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 25 Apr 2020 01:28:09 +0200 Subject: tinywl: remove unused variables --- tinywl.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/tinywl.c b/tinywl.c index c44a717..3fe65c1 100644 --- a/tinywl.c +++ b/tinywl.c @@ -311,8 +311,6 @@ static bool view_at(struct tinywl_view *view, double view_sx = lx - view->x; double view_sy = ly - view->y; - struct wlr_surface_state *state = &view->xdg_surface->surface->current; - double _sx, _sy; struct wlr_surface *_surface = NULL; _surface = wlr_xdg_surface_surface_at( @@ -476,7 +474,6 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); double sx, sy; - struct wlr_seat *seat = server->seat; struct wlr_surface *surface; struct tinywl_view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); -- cgit v1.2.3 From f13f2d8c4ac4c4cdc1d211a17275492219de5c2c Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 25 Apr 2020 01:44:45 +0200 Subject: tinywl: handle request set selection --- tinywl.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index 3fe65c1..70e1323 100644 --- a/tinywl.c +++ b/tinywl.c @@ -50,6 +50,7 @@ struct tinywl_server { struct wlr_seat *seat; struct wl_listener new_input; struct wl_listener request_cursor; + struct wl_listener request_set_selection; struct wl_list keyboards; enum tinywl_cursor_mode cursor_mode; struct tinywl_view *grabbed_view; @@ -298,6 +299,17 @@ static void seat_request_cursor(struct wl_listener *listener, void *data) { } } +static void seat_request_set_selection(struct wl_listener *listener, void *data) { + /* This event is raised by the seat when a client wants to set the selection, + * usually when the user copies something. wlroots allows compositors to + * ignore such requests if they so choose, but in tinywl we always honor + */ + struct tinywl_server *server = wl_container_of( + listener, server, request_set_selection); + struct wlr_seat_request_set_selection_event *event = data; + wlr_seat_set_selection(server->seat, event->source, event->serial); +} + static bool view_at(struct tinywl_view *view, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { @@ -827,7 +839,9 @@ int main(int argc, char *argv[]) { /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you - * to dig your fingers in and play with their behavior if you want. */ + * to dig your fingers in and play with their behavior if you want. Note that + * the clients cannot set the selection directly without compositor approval, + * see the handling of the request_set_selection event below.*/ wlr_compositor_create(server.wl_display, server.renderer); wlr_data_device_manager_create(server.wl_display); @@ -904,6 +918,9 @@ int main(int argc, char *argv[]) { server.request_cursor.notify = seat_request_cursor; wl_signal_add(&server.seat->events.request_set_cursor, &server.request_cursor); + server.request_set_selection.notify = seat_request_set_selection; + wl_signal_add(&server.seat->events.request_set_selection, + &server.request_set_selection); /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(server.wl_display); -- cgit v1.2.3 From 3cd164925b6d5ee15d1245bf1ef2afd7f356eeac Mon Sep 17 00:00:00 2001 From: Greg Depoire--Ferrer Date: Sun, 26 Apr 2020 17:45:45 +0200 Subject: tinywl: Fix wrong anchor point while resizing a window Previously, when dragging the left border of a window with the mouse in tinywl, there was a bug where it snap the top level surface's geometry X coordinate directly to the position of the mouse, as if you started the resize right on the border. This also affected the other (right, top and bottom) borders. I think that the previous resize code was hard to understand. Honestly I have not spent a lot of time trying to understand why it didn't work and I wrote another resize algorithm instead: now, instead of working directly with widths and heights which are complicated, we work with the borders (left, right, top, bottom). This is easier to understand IMO. Note: I originally fixed this [in the waybox compositor](https://github.com/wizbright/waybox/pull/23) but then I realized that the code was taken from tinywl and that it had the same issues so I copied my fix for tinywl. --- tinywl.c | 72 ++++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/tinywl.c b/tinywl.c index 70e1323..84a7636 100644 --- a/tinywl.c +++ b/tinywl.c @@ -55,8 +55,7 @@ struct tinywl_server { enum tinywl_cursor_mode cursor_mode; struct tinywl_view *grabbed_view; double grab_x, grab_y; - struct wlr_box grab_geo_box; - int grab_width, grab_height; + struct wlr_box grab_geobox; uint32_t resize_edges; struct wlr_output_layout *output_layout; @@ -370,33 +369,44 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { * commit any movement that was prepared. */ struct tinywl_view *view = server->grabbed_view; - double dx = server->cursor->x - server->grab_x; - double dy = server->cursor->y - server->grab_y; - double x = view->x; - double y = view->y; - int width = server->grab_width; - int height = server->grab_height; + double border_x = server->cursor->x - server->grab_x; + double border_y = server->cursor->y - server->grab_y; + int new_left = server->grab_geobox.x; + int new_right = server->grab_geobox.x + server->grab_geobox.width; + int new_top = server->grab_geobox.y; + int new_bottom = server->grab_geobox.y + server->grab_geobox.height; + if (server->resize_edges & WLR_EDGE_TOP) { - y = server->grab_y + dy - server->grab_geo_box.y; - height -= dy + server->grab_geo_box.y; - if (height < 1) { - y += height; + new_top = border_y; + if (new_top >= new_bottom) { + new_top = new_bottom - 1; } } else if (server->resize_edges & WLR_EDGE_BOTTOM) { - height += dy + server->grab_geo_box.y; + new_bottom = border_y; + if (new_bottom <= new_top) { + new_bottom = new_top + 1; + } } if (server->resize_edges & WLR_EDGE_LEFT) { - x = server->grab_x + dx - server->grab_geo_box.x; - width -= dx + server->grab_geo_box.x; - if (width < 1) { - x += width; + new_left = border_x; + if (new_left >= new_right) { + new_left = new_right - 1; } } else if (server->resize_edges & WLR_EDGE_RIGHT) { - width += dx + server->grab_geo_box.x; + new_right = border_x; + if (new_right <= new_left) { + new_right = new_left + 1; + } } - view->x = x; - view->y = y; - wlr_xdg_toplevel_set_size(view->xdg_surface, width, height); + + struct wlr_box geo_box; + wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + view->x = new_left - geo_box.x; + view->y = new_top - geo_box.y; + + int new_width = new_right - new_left; + int new_height = new_bottom - new_top; + wlr_xdg_toplevel_set_size(view->xdg_surface, new_width, new_height); } static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { @@ -725,17 +735,25 @@ static void begin_interactive(struct tinywl_view *view, } server->grabbed_view = view; server->cursor_mode = mode; - wlr_xdg_surface_get_geometry(view->xdg_surface, &server->grab_geo_box); + if (mode == TINYWL_CURSOR_MOVE) { server->grab_x = server->cursor->x - view->x; server->grab_y = server->cursor->y - view->y; } else { - server->grab_x = server->cursor->x + server->grab_geo_box.x; - server->grab_y = server->cursor->y + server->grab_geo_box.y; + struct wlr_box geo_box; + wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + + double border_x = (view->x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); + double border_y = (view->y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); + server->grab_x = server->cursor->x - border_x; + server->grab_y = server->cursor->y - border_y; + + server->grab_geobox = geo_box; + server->grab_geobox.x += view->x; + server->grab_geobox.y += view->y; + + server->resize_edges = edges; } - server->grab_width = server->grab_geo_box.width; - server->grab_height = server->grab_geo_box.height; - server->resize_edges = edges; } static void xdg_toplevel_request_move( -- cgit v1.2.3 From de19cd7c75993e4ef8bdd8ca8ac0d6b1a46a52e9 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 21 Oct 2020 17:21:23 +0200 Subject: Replace wlr_key_state with wl_keyboard_key_state There's no reason to have duplicate enums --- tinywl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tinywl.c b/tinywl.c index 84a7636..e2cf30a 100644 --- a/tinywl.c +++ b/tinywl.c @@ -197,7 +197,7 @@ static void keyboard_handle_key( bool handled = false; uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); - if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_KEY_PRESSED) { + if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { /* If alt is held down and this button was _pressed_, we attempt to * process it as a compositor keybinding. */ for (int i = 0; i < nsyms; i++) { @@ -374,7 +374,7 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { int new_left = server->grab_geobox.x; int new_right = server->grab_geobox.x + server->grab_geobox.width; int new_top = server->grab_geobox.y; - int new_bottom = server->grab_geobox.y + server->grab_geobox.height; + int new_bottom = server->grab_geobox.y + server->grab_geobox.height; if (server->resize_edges & WLR_EDGE_TOP) { new_top = border_y; -- cgit v1.2.3 From aeace45a14db8d778540e42daa7d813be771873e Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Tue, 5 Jan 2021 22:08:22 -0500 Subject: tinywl: fix wlr_backend_autocreate call --- tinywl.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tinywl.c b/tinywl.c index e2cf30a..3232df3 100644 --- a/tinywl.c +++ b/tinywl.c @@ -841,12 +841,8 @@ int main(int argc, char *argv[]) { /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window - * if an X11 server is running. The NULL argument here optionally allows you - * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The - * backend uses the renderer, for example, to fall back to software cursors - * if the backend does not support hardware cursors (some older GPUs - * don't). */ - server.backend = wlr_backend_autocreate(server.wl_display, NULL); + * if an X11 server is running. */ + server.backend = wlr_backend_autocreate(server.wl_display); /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us. * The renderer is responsible for defining the various pixel formats it -- cgit v1.2.3 From dec131b71af4dd18695758d883fc424f01b53375 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 16 Apr 2021 09:05:39 +0200 Subject: Stop specifying xkb_rule_names If a NULL xkb_rule_names pointer is passed to xkb_keymap_new_from_names, libxkbcommon will default to reading the XKB_* env variables. So there's no need to do it ourselves. Also s/xkb_map_new_from_names/xkb_keymap_new_from_names/ since the latter is more consistent with the returned struct name. [1]: https://xkbcommon.org/doc/current/structxkb__rule__names.html --- tinywl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tinywl.c b/tinywl.c index 3232df3..2f2257e 100644 --- a/tinywl.c +++ b/tinywl.c @@ -222,9 +222,8 @@ static void server_new_keyboard(struct tinywl_server *server, /* We need to prepare an XKB keymap and assign it to the keyboard. This * assumes the defaults (e.g. layout = "us"). */ - struct xkb_rule_names rules = { 0 }; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, + struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(device->keyboard, keymap); -- cgit v1.2.3 From 1979956219d60738e18022c109f121db14277afa Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 20 Aug 2021 16:02:17 +0200 Subject: tinywl: simplify logic for sending pointer events --- tinywl.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tinywl.c b/tinywl.c index 2f2257e..8697d2b 100644 --- a/tinywl.c +++ b/tinywl.c @@ -432,21 +432,19 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { server->cursor_mgr, "left_ptr", server->cursor); } if (surface) { - bool focus_changed = seat->pointer_state.focused_surface != surface; /* - * "Enter" the surface if necessary. This lets the client know that the - * cursor has entered one of its surfaces. + * Send pointer enter and motion events. * - * Note that this gives the surface "pointer focus", which is distinct + * The enter event gives the surface "pointer focus", which is distinct * from keyboard focus. You get pointer focus by moving the pointer over * a window. + * + * Note that wlroots will avoid sending duplicate enter/motion events if + * the surface has already has pointer focus or if the client is already + * aware of the coordinates passed. */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); - if (!focus_changed) { - /* The enter event contains coordinates, so we only need to notify - * on motion if the focus did not change. */ - wlr_seat_pointer_notify_motion(seat, time, sx, sy); - } + wlr_seat_pointer_notify_motion(seat, time, sx, sy); } else { /* Clear pointer focus so future button events and such are not sent to * the last client to have the cursor over it. */ -- cgit v1.2.3 From 8d7cfe99a6f827e8ab34aaca32230d5a16f8d305 Mon Sep 17 00:00:00 2001 From: Elyes HAOUAS Date: Sat, 2 Oct 2021 08:29:27 +0200 Subject: Fix spelling errors Signed-off-by: Elyes HAOUAS --- tinywl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tinywl.c b/tinywl.c index 8697d2b..b11549b 100644 --- a/tinywl.c +++ b/tinywl.c @@ -281,7 +281,7 @@ static void server_new_input(struct wl_listener *listener, void *data) { static void seat_request_cursor(struct wl_listener *listener, void *data) { struct tinywl_server *server = wl_container_of( listener, server, request_cursor); - /* This event is rasied by the seat when a client provides a cursor image */ + /* This event is raised by the seat when a client provides a cursor image */ struct wlr_seat_pointer_request_set_cursor_event *event = data; struct wlr_seat_client *focused_client = server->seat->pointer_state.focused_client; @@ -656,7 +656,7 @@ static void output_frame(struct wl_listener *listener, void *data) { } static void server_new_output(struct wl_listener *listener, void *data) { - /* This event is rasied by the backend when a new output (aka a display or + /* This event is raised by the backend when a new output (aka a display or * monitor) becomes available. */ struct tinywl_server *server = wl_container_of(listener, server, new_output); @@ -758,7 +758,7 @@ static void xdg_toplevel_request_move( /* This event is raised when a client would like to begin an interactive * move, typically because the user clicked on their client-side * decorations. Note that a more sophisticated compositor should check the - * provied serial against a list of button press serials sent to this + * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ struct tinywl_view *view = wl_container_of(listener, view, request_move); begin_interactive(view, TINYWL_CURSOR_MOVE, 0); @@ -769,7 +769,7 @@ static void xdg_toplevel_request_resize( /* This event is raised when a client would like to begin an interactive * resize, typically because the user clicked on their client-side * decorations. Note that a more sophisticated compositor should check the - * provied serial against a list of button press serials sent to this + * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ struct wlr_xdg_toplevel_resize_event *event = data; struct tinywl_view *view = wl_container_of(listener, view, request_resize); -- cgit v1.2.3 From 5d2d84776e1e398513525a779d2d5df979b0a57c Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 13:42:06 -0500 Subject: tinywl: autocreate allocator and init output --- tinywl.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tinywl.c b/tinywl.c index b11549b..82f0977 100644 --- a/tinywl.c +++ b/tinywl.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ struct tinywl_server { struct wl_display *wl_display; struct wlr_backend *backend; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_surface; @@ -676,6 +678,10 @@ static void server_new_output(struct wl_listener *listener, void *data) { } } + /* Configures the output created by the backend to use our allocator + * and our renderer */ + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + /* Allocates and configures our state for this output */ struct tinywl_output *output = calloc(1, sizeof(struct tinywl_output)); @@ -841,12 +847,20 @@ int main(int argc, char *argv[]) { * if an X11 server is running. */ server.backend = wlr_backend_autocreate(server.wl_display); - /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us. + /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user + * can also specify a renderer using the WLR_RENDERER env var. * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ - server.renderer = wlr_backend_get_renderer(server.backend); + server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); + /* Autocreates an allocator for us. + * The allocator is the bridge between the renderer and the backend. It + * handles the buffer creation, allowing wlroots to render onto the + * screen */ + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); + /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you -- cgit v1.2.3 From 22cfa303bdc758a98bb8342f45546e2ea0c6c146 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 19 Nov 2021 10:17:04 -0500 Subject: tinywl: init output render before commit --- tinywl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tinywl.c b/tinywl.c index 82f0977..ab5ed7c 100644 --- a/tinywl.c +++ b/tinywl.c @@ -664,6 +664,10 @@ static void server_new_output(struct wl_listener *listener, void *data) { wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + /* Configures the output created by the backend to use our allocator + * and our renderer. Must be done once, before commiting the output */ + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + /* Some backends don't have modes. DRM+KMS does, and we need to set a mode * before we can use the output. The mode is a tuple of (width, height, * refresh rate), and each monitor supports only a specific set of modes. We @@ -678,10 +682,6 @@ static void server_new_output(struct wl_listener *listener, void *data) { } } - /* Configures the output created by the backend to use our allocator - * and our renderer */ - wlr_output_init_render(wlr_output, server->allocator, server->renderer); - /* Allocates and configures our state for this output */ struct tinywl_output *output = calloc(1, sizeof(struct tinywl_output)); -- cgit v1.2.3 From 71ec61ed1624e9a9b0e8e8781d1fbc85de1f03b3 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Thu, 18 Nov 2021 16:29:05 -0500 Subject: tinywl: build with meson if examples option is enabled --- meson.build | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 meson.build diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..82d31d2 --- /dev/null +++ b/meson.build @@ -0,0 +1,5 @@ +executable( + 'tinywl', + ['tinywl.c', protocols_client_header['xdg-shell']], + dependencies: wlroots, +) -- cgit v1.2.3 From 6c7e8461f85f5ed0eac52c79c1fef18f73ce949b Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 25 Nov 2021 20:28:54 +0100 Subject: tinywl: use wlr_scene --- tinywl.c | 268 +++++++++++++++++++++------------------------------------------ 1 file changed, 89 insertions(+), 179 deletions(-) diff --git a/tinywl.c b/tinywl.c index ab5ed7c..5ce7c4e 100644 --- a/tinywl.c +++ b/tinywl.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200112L +#include #include #include #include @@ -14,10 +15,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ struct tinywl_server { struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; + struct wlr_scene *scene; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_surface; @@ -76,12 +78,12 @@ struct tinywl_view { struct wl_list link; struct tinywl_server *server; struct wlr_xdg_surface *xdg_surface; + struct wlr_scene_node *scene_node; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; - bool mapped; int x, y; }; @@ -118,6 +120,7 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); /* Move the view to the front */ + wlr_scene_node_raise_to_top(view->scene_node); wl_list_remove(&view->link); wl_list_insert(&server->views, &view->link); /* Activate the new surface */ @@ -166,14 +169,9 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { if (wl_list_length(&server->views) < 2) { break; } - struct tinywl_view *current_view = wl_container_of( - server->views.next, current_view, link); struct tinywl_view *next_view = wl_container_of( - current_view->link.next, next_view, link); + server->views.prev, next_view, link); focus_view(next_view, next_view->xdg_surface->surface); - /* Move the previous view to the end of the list */ - wl_list_remove(¤t_view->link); - wl_list_insert(server->views.prev, ¤t_view->link); break; default: return false; @@ -199,7 +197,8 @@ static void keyboard_handle_key( bool handled = false; uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); - if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if ((modifiers & WLR_MODIFIER_ALT) && + event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { /* If alt is held down and this button was _pressed_, we attempt to * process it as a compositor keybinding. */ for (int i = 0; i < nsyms; i++) { @@ -310,52 +309,32 @@ static void seat_request_set_selection(struct wl_listener *listener, void *data) wlr_seat_set_selection(server->seat, event->source, event->serial); } -static bool view_at(struct tinywl_view *view, - double lx, double ly, struct wlr_surface **surface, - double *sx, double *sy) { - /* - * XDG toplevels may have nested surfaces, such as popup windows for context - * menus or tooltips. This function tests if any of those are underneath the - * coordinates lx and ly (in output Layout Coordinates). If so, it sets the - * surface pointer to that wlr_surface and the sx and sy coordinates to the - * coordinates relative to that surface's top-left corner. - */ - double view_sx = lx - view->x; - double view_sy = ly - view->y; - - double _sx, _sy; - struct wlr_surface *_surface = NULL; - _surface = wlr_xdg_surface_surface_at( - view->xdg_surface, view_sx, view_sy, &_sx, &_sy); - - if (_surface != NULL) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return true; - } - - return false; -} - static struct tinywl_view *desktop_view_at( struct tinywl_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - /* This iterates over all of our surfaces and attempts to find one under the - * cursor. This relies on server->views being ordered from top-to-bottom. */ - struct tinywl_view *view; - wl_list_for_each(view, &server->views, link) { - if (view_at(view, lx, ly, surface, sx, sy)) { - return view; - } + /* This returns the topmost node in the scene at the given layout coords. + * we only care about surface nodes as we are specifically looking for a + * surface in the surface tree of a tinywl_view. */ + struct wlr_scene_node *node = wlr_scene_node_at( + &server->scene->node, lx, ly, sx, sy); + if (node == NULL || node->type != WLR_SCENE_NODE_SURFACE) { + return NULL; + } + *surface = wlr_scene_surface_from_node(node)->surface; + /* Find the node corresponding to the tinywl_view at the root of this + * surface tree, it is the only one for which we set the data field. */ + while (node != NULL && node->data == NULL) { + node = node->parent; } - return NULL; + return node->data; } static void process_cursor_move(struct tinywl_server *server, uint32_t time) { /* Move the grabbed view to the new position. */ - server->grabbed_view->x = server->cursor->x - server->grab_x; - server->grabbed_view->y = server->cursor->y - server->grab_y; + struct tinywl_view *view = server->grabbed_view; + view->x = server->cursor->x - server->grab_x; + view->y = server->cursor->y - server->grab_y; + wlr_scene_node_set_position(view->scene_node, view->x, view->y); } static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { @@ -404,6 +383,7 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); view->x = new_left - geo_box.x; view->y = new_top - geo_box.y; + wlr_scene_node_set_position(view->scene_node, view->x, view->y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; @@ -530,74 +510,12 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_frame(server->seat); } -/* Used to move all of the data necessary to render a surface from the top-level - * frame handler to the per-surface render function. */ -struct render_data { - struct wlr_output *output; - struct wlr_renderer *renderer; - struct tinywl_view *view; - struct timespec *when; -}; - -static void render_surface(struct wlr_surface *surface, +// TODO: We should avoid sending the frame done event twice if a surface +// appears on multiple outputs. +// https://github.com/swaywm/wlroots/issues/3210 +static void send_frame_done(struct wlr_surface *surface, int sx, int sy, void *data) { - /* This function is called for every surface that needs to be rendered. */ - struct render_data *rdata = data; - struct tinywl_view *view = rdata->view; - struct wlr_output *output = rdata->output; - - /* We first obtain a wlr_texture, which is a GPU resource. wlroots - * automatically handles negotiating these with the client. The underlying - * resource could be an opaque handle passed from the client, or the client - * could have sent a pixel buffer which we copied to the GPU, or a few other - * means. You don't have to worry about this, wlroots takes care of it. */ - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - return; - } - - /* The view has a position in layout coordinates. If you have two displays, - * one next to the other, both 1080p, a view on the rightmost display might - * have layout coordinates of 2000,100. We need to translate that to - * output-local coordinates, or (2000 - 1920). */ - double ox = 0, oy = 0; - wlr_output_layout_output_coords( - view->server->output_layout, output, &ox, &oy); - ox += view->x + sx, oy += view->y + sy; - - /* We also have to apply the scale factor for HiDPI outputs. This is only - * part of the puzzle, TinyWL does not fully support HiDPI. */ - struct wlr_box box = { - .x = ox * output->scale, - .y = oy * output->scale, - .width = surface->current.width * output->scale, - .height = surface->current.height * output->scale, - }; - - /* - * Those familiar with OpenGL are also familiar with the role of matricies - * in graphics programming. We need to prepare a matrix to render the view - * with. wlr_matrix_project_box is a helper which takes a box with a desired - * x, y coordinates, width and height, and an output geometry, then - * prepares an orthographic projection and multiplies the necessary - * transforms to produce a model-view-projection matrix. - * - * Naturally you can do this any way you like, for example to make a 3D - * compositor. - */ - float matrix[9]; - enum wl_output_transform transform = - wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &box, transform, 0, - output->transform_matrix); - - /* This takes our matrix, the texture, and an alpha, and performs the actual - * rendering on the GPU. */ - wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); - - /* This lets the client know that we've displayed that frame and it can - * prepare another one now if it likes. */ - wlr_surface_send_frame_done(surface, rdata->when); + wlr_surface_send_frame_done(surface, data); } static void output_frame(struct wl_listener *listener, void *data) { @@ -605,56 +523,16 @@ static void output_frame(struct wl_listener *listener, void *data) { * generally at the output's refresh rate (e.g. 60Hz). */ struct tinywl_output *output = wl_container_of(listener, output, frame); - struct wlr_renderer *renderer = output->server->renderer; - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( + output->server->scene, output->wlr_output); - /* wlr_output_attach_render makes the OpenGL context current. */ - if (!wlr_output_attach_render(output->wlr_output, NULL)) { - return; - } - /* The "effective" resolution can change if you rotate your outputs. */ - int width, height; - wlr_output_effective_resolution(output->wlr_output, &width, &height); - /* Begin the renderer (calls glViewport and some other GL sanity checks) */ - wlr_renderer_begin(renderer, width, height); - - float color[4] = {0.3, 0.3, 0.3, 1.0}; - wlr_renderer_clear(renderer, color); - - /* Each subsequent window we render is rendered on top of the last. Because - * our view list is ordered front-to-back, we iterate over it backwards. */ - struct tinywl_view *view; - wl_list_for_each_reverse(view, &output->server->views, link) { - if (!view->mapped) { - /* An unmapped view should not be rendered. */ - continue; - } - struct render_data rdata = { - .output = output->wlr_output, - .view = view, - .renderer = renderer, - .when = &now, - }; - /* This calls our render_surface function for each surface among the - * xdg_surface's toplevel and popups. */ - wlr_xdg_surface_for_each_surface(view->xdg_surface, - render_surface, &rdata); - } + /* Render the scene if needed and commit the output */ + wlr_scene_output_commit(scene_output); - /* Hardware cursors are rendered by the GPU on a separate plane, and can be - * moved around without re-rendering what's beneath them - which is more - * efficient. However, not all hardware supports hardware cursors. For this - * reason, wlroots provides a software fallback, which we ask it to render - * here. wlr_cursor handles configuring hardware vs software cursors for you, - * and this function is a no-op when hardware cursors are in use. */ - wlr_output_render_software_cursors(output->wlr_output, NULL); - - /* Conclude rendering and swap the buffers, showing the final frame - * on-screen. */ - wlr_renderer_end(renderer); - wlr_output_commit(output->wlr_output); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_for_each_surface(scene_output, send_frame_done, &now); } static void server_new_output(struct wl_listener *listener, void *data) { @@ -704,23 +582,32 @@ static void server_new_output(struct wl_listener *listener, void *data) { wlr_output_layout_add_auto(server->output_layout, wlr_output); } -static void xdg_surface_map(struct wl_listener *listener, void *data) { +static void xdg_toplevel_map(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ struct tinywl_view *view = wl_container_of(listener, view, map); - view->mapped = true; + + wl_list_insert(&view->server->views, &view->link); + focus_view(view, view->xdg_surface->surface); } -static void xdg_surface_unmap(struct wl_listener *listener, void *data) { +static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ struct tinywl_view *view = wl_container_of(listener, view, unmap); - view->mapped = false; + + wl_list_remove(&view->link); } -static void xdg_surface_destroy(struct wl_listener *listener, void *data) { +static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { /* Called when the surface is destroyed and should never be shown again. */ struct tinywl_view *view = wl_container_of(listener, view, destroy); - wl_list_remove(&view->link); + + wl_list_remove(&view->map.link); + wl_list_remove(&view->unmap.link); + wl_list_remove(&view->destroy.link); + wl_list_remove(&view->request_move.link); + wl_list_remove(&view->request_resize.link); + free(view); } @@ -746,8 +633,10 @@ static void begin_interactive(struct tinywl_view *view, struct wlr_box geo_box; wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); - double border_x = (view->x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); - double border_y = (view->y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); + double border_x = (view->x + geo_box.x) + + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); + double border_y = (view->y + geo_box.y) + + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); server->grab_x = server->cursor->x - border_x; server->grab_y = server->cursor->y - border_y; @@ -788,22 +677,38 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { struct tinywl_server *server = wl_container_of(listener, server, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + + /* We must add xdg popups to the scene graph so they get rendered. The + * wlroots scene graph provides a helper for this, but to use it we must + * provide the proper parent scene node of the xdg popup. To enable this, + * we always set the user data field of xdg_surfaces to the corresponding + * scene node. */ + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface( + xdg_surface->popup->parent); + struct wlr_scene_node *parent_node = parent->data; + xdg_surface->data = wlr_scene_xdg_surface_create( + parent_node, xdg_surface); return; } + assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); /* Allocate a tinywl_view for this surface */ struct tinywl_view *view = calloc(1, sizeof(struct tinywl_view)); view->server = server; view->xdg_surface = xdg_surface; + view->scene_node = wlr_scene_xdg_surface_create( + &view->server->scene->node, view->xdg_surface); + view->scene_node->data = view; + xdg_surface->data = view->scene_node; /* Listen to the various events it can emit */ - view->map.notify = xdg_surface_map; + view->map.notify = xdg_toplevel_map; wl_signal_add(&xdg_surface->events.map, &view->map); - view->unmap.notify = xdg_surface_unmap; + view->unmap.notify = xdg_toplevel_unmap; wl_signal_add(&xdg_surface->events.unmap, &view->unmap); - view->destroy.notify = xdg_surface_destroy; + view->destroy.notify = xdg_toplevel_destroy; wl_signal_add(&xdg_surface->events.destroy, &view->destroy); /* cotd */ @@ -812,9 +717,6 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { wl_signal_add(&toplevel->events.request_move, &view->request_move); view->request_resize.notify = xdg_toplevel_request_resize; wl_signal_add(&toplevel->events.request_resize, &view->request_resize); - - /* Add it to the list of views. */ - wl_list_insert(&server->views, &view->link); } int main(int argc, char *argv[]) { @@ -880,9 +782,17 @@ int main(int argc, char *argv[]) { server.new_output.notify = server_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); - /* Set up our list of views and the xdg-shell. The xdg-shell is a Wayland - * protocol which is used for application windows. For more detail on - * shells, refer to my article: + /* Create a scene graph. This is a wlroots abstraction that handles all + * rendering and damage tracking. All the compositor author needs to do + * is add things that should be rendered to the scene graph at the proper + * positions and then call wlr_scene_output_commit() to render a frame if + * necessary. + */ + server.scene = wlr_scene_create(); + wlr_scene_attach_output_layout(server.scene, server.output_layout); + + /* Set up the xdg-shell. The xdg-shell is a Wayland protocol which is used + * for application windows. For more detail on shells, refer to my article: * * https://drewdevault.com/2018/07/29/Wayland-shells.html */ -- cgit v1.2.3 From c89264cf2250b35978dd00f4d10641635ac5194a Mon Sep 17 00:00:00 2001 From: Quantum Date: Wed, 1 Dec 2021 02:31:45 -0500 Subject: Fix uninitialized variable errors in release mode When using `meson --buildtype=release`, `-Wextra -Werror` is passed. This includes `-Werror=maybe-uninitialized`, which complains about the instances fixed in this commit. --- tinywl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index 5ce7c4e..4877574 100644 --- a/tinywl.c +++ b/tinywl.c @@ -475,7 +475,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); double sx, sy; - struct wlr_surface *surface; + struct wlr_surface *surface = NULL; struct tinywl_view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { -- cgit v1.2.3 From bd3ba5c0e40ed921b53d0614296f796bd8ad6e7d Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 13 Dec 2021 16:13:03 +0100 Subject: tinywl: use wlr_scene_send_frame_done() --- tinywl.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tinywl.c b/tinywl.c index 4877574..581fcfc 100644 --- a/tinywl.c +++ b/tinywl.c @@ -510,29 +510,21 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_frame(server->seat); } -// TODO: We should avoid sending the frame done event twice if a surface -// appears on multiple outputs. -// https://github.com/swaywm/wlroots/issues/3210 -static void send_frame_done(struct wlr_surface *surface, - int sx, int sy, void *data) { - wlr_surface_send_frame_done(surface, data); -} - static void output_frame(struct wl_listener *listener, void *data) { /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ - struct tinywl_output *output = - wl_container_of(listener, output, frame); + struct tinywl_output *output = wl_container_of(listener, output, frame); + struct wlr_scene *scene = output->server->scene; struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( - output->server->scene, output->wlr_output); + scene, output->wlr_output); /* Render the scene if needed and commit the output */ wlr_scene_output_commit(scene_output); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_for_each_surface(scene_output, send_frame_done, &now); + wlr_scene_send_frame_done(scene, output->wlr_output, &now); } static void server_new_output(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From ce3d4b267dfd51db134ca5d752371498961c711f Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 13 Dec 2021 17:23:47 +0100 Subject: scene: fix wlr_scene_send_frame_done() API This doesn't work if scene outputs are not used as the primary output of scene surfaces will always be NULL. Therefore, take a wlr_scene_output instead of separate wlr_scene and wlr_output arguments and rename the function to wlr_scene_output_send_frame_done(). The actual behavior of the function is unchanged. --- tinywl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index 581fcfc..faa0a9a 100644 --- a/tinywl.c +++ b/tinywl.c @@ -524,7 +524,7 @@ static void output_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_send_frame_done(scene, output->wlr_output, &now); + wlr_scene_output_send_frame_done(scene_output, &now); } static void server_new_output(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From dd4ca1818a148d44e42b1504e73fe5b562af7481 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 13 Jan 2022 11:55:09 +0300 Subject: subcompositor: split out from compositor --- tinywl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index faa0a9a..722abd1 100644 --- a/tinywl.c +++ b/tinywl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -756,12 +757,14 @@ int main(int argc, char *argv[]) { server.renderer); /* This creates some hands-off wlroots interfaces. The compositor is - * necessary for clients to allocate surfaces and the data device manager + * necessary for clients to allocate surfaces, the subcompositor allows to + * assign the role of subsurfaces to surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the handling of the request_set_selection event below.*/ wlr_compositor_create(server.wl_display, server.renderer); + wlr_subcompositor_create(server.wl_display); wlr_data_device_manager_create(server.wl_display); /* Creates an output layout, which a wlroots utility for working with an -- cgit v1.2.3 From c06da9913835dcd96c82d03658c458f660f585b6 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 14 Jan 2022 20:46:20 +0100 Subject: tinywl: fix check whether client is focused or not Currently this check is too strict and denies the move/resize request if a subsurface of the client has pointer focus. --- tinywl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index 722abd1..6f62883 100644 --- a/tinywl.c +++ b/tinywl.c @@ -612,7 +612,8 @@ static void begin_interactive(struct tinywl_view *view, struct tinywl_server *server = view->server; struct wlr_surface *focused_surface = server->seat->pointer_state.focused_surface; - if (view->xdg_surface->surface != focused_surface) { + if (view->xdg_surface->surface != + wlr_surface_get_root_surface(focused_surface)) { /* Deny move/resize requests from unfocused clients. */ return; } -- cgit v1.2.3 From c218da787c49c3e1b726c487f65d66502147e4c6 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 8 Jan 2022 22:52:51 +0300 Subject: xdg-toplevel: fix functions' main argument type With this commit, `wlr_xdg_toplevel_*()` functions now expect a `wlr_xdg_toplevel` instead of a `wlr_xdg_surface`. --- tinywl.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tinywl.c b/tinywl.c index 6f62883..a4fcc26 100644 --- a/tinywl.c +++ b/tinywl.c @@ -78,7 +78,7 @@ struct tinywl_output { struct tinywl_view { struct wl_list link; struct tinywl_server *server; - struct wlr_xdg_surface *xdg_surface; + struct wlr_xdg_toplevel *xdg_toplevel; struct wlr_scene_node *scene_node; struct wl_listener map; struct wl_listener unmap; @@ -117,7 +117,8 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { */ struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( seat->keyboard_state.focused_surface); - wlr_xdg_toplevel_set_activated(previous, false); + assert(previous->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + wlr_xdg_toplevel_set_activated(previous->toplevel, false); } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); /* Move the view to the front */ @@ -125,13 +126,13 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { wl_list_remove(&view->link); wl_list_insert(&server->views, &view->link); /* Activate the new surface */ - wlr_xdg_toplevel_set_activated(view->xdg_surface, true); + wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true); /* * Tell the seat to have the keyboard enter this surface. wlroots will keep * track of this and automatically send key events to the appropriate * clients without additional work on your part. */ - wlr_seat_keyboard_notify_enter(seat, view->xdg_surface->surface, + wlr_seat_keyboard_notify_enter(seat, view->xdg_toplevel->base->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } @@ -172,7 +173,7 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { } struct tinywl_view *next_view = wl_container_of( server->views.prev, next_view, link); - focus_view(next_view, next_view->xdg_surface->surface); + focus_view(next_view, next_view->xdg_toplevel->base->surface); break; default: return false; @@ -381,14 +382,14 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { } struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); view->x = new_left - geo_box.x; view->y = new_top - geo_box.y; wlr_scene_node_set_position(view->scene_node, view->x, view->y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; - wlr_xdg_toplevel_set_size(view->xdg_surface, new_width, new_height); + wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height); } static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { @@ -581,7 +582,7 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { wl_list_insert(&view->server->views, &view->link); - focus_view(view, view->xdg_surface->surface); + focus_view(view, view->xdg_toplevel->base->surface); } static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { @@ -612,7 +613,7 @@ static void begin_interactive(struct tinywl_view *view, struct tinywl_server *server = view->server; struct wlr_surface *focused_surface = server->seat->pointer_state.focused_surface; - if (view->xdg_surface->surface != + if (view->xdg_toplevel->base->surface != wlr_surface_get_root_surface(focused_surface)) { /* Deny move/resize requests from unfocused clients. */ return; @@ -625,7 +626,7 @@ static void begin_interactive(struct tinywl_view *view, server->grab_y = server->cursor->y - view->y; } else { struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); double border_x = (view->x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); @@ -691,9 +692,9 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { struct tinywl_view *view = calloc(1, sizeof(struct tinywl_view)); view->server = server; - view->xdg_surface = xdg_surface; + view->xdg_toplevel = xdg_surface->toplevel; view->scene_node = wlr_scene_xdg_surface_create( - &view->server->scene->node, view->xdg_surface); + &view->server->scene->node, view->xdg_toplevel->base); view->scene_node->data = view; xdg_surface->data = view->scene_node; -- cgit v1.2.3 From e8a997d7cd2fba760c7c32140a287f63a85801f1 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 7 Mar 2022 11:01:49 -0500 Subject: tinywl: destroy keyboard on wlr_input_device event --- tinywl.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tinywl.c b/tinywl.c index a4fcc26..2abcfb1 100644 --- a/tinywl.c +++ b/tinywl.c @@ -95,6 +95,7 @@ struct tinywl_keyboard { struct wl_listener modifiers; struct wl_listener key; + struct wl_listener destroy; }; static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { @@ -216,6 +217,20 @@ static void keyboard_handle_key( } } +static void keyboard_handle_destroy(struct wl_listener *listener, void *data) { + /* This event is raised by the keyboard base wlr_input_device to signal + * the destruction of the wlr_keyboard. It will no longer receive events + * and should be destroyed. + */ + struct tinywl_keyboard *keyboard = + wl_container_of(listener, keyboard, destroy); + wl_list_remove(&keyboard->modifiers.link); + wl_list_remove(&keyboard->key.link); + wl_list_remove(&keyboard->destroy.link); + wl_list_remove(&keyboard->link); + free(keyboard); +} + static void server_new_keyboard(struct tinywl_server *server, struct wlr_input_device *device) { struct tinywl_keyboard *keyboard = @@ -239,6 +254,8 @@ static void server_new_keyboard(struct tinywl_server *server, wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); keyboard->key.notify = keyboard_handle_key; wl_signal_add(&device->keyboard->events.key, &keyboard->key); + keyboard->destroy.notify = keyboard_handle_destroy; + wl_signal_add(&device->events.destroy, &keyboard->destroy); wlr_seat_set_keyboard(server->seat, device); -- cgit v1.2.3 From 5766dc59474df90bf937fd91d3ba2c3badee8942 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 4 Mar 2022 21:59:23 -0500 Subject: tinywl: init cursor_mode valgrind complains on a use-before-init for the cursor mode. --- tinywl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tinywl.c b/tinywl.c index 2abcfb1..e21d473 100644 --- a/tinywl.c +++ b/tinywl.c @@ -842,6 +842,7 @@ int main(int argc, char *argv[]) { * * And more comments are sprinkled throughout the notify functions above. */ + server.cursor_mode = TINYWL_CURSOR_PASSTHROUGH; server.cursor_motion.notify = server_cursor_motion; wl_signal_add(&server.cursor->events.motion, &server.cursor_motion); server.cursor_motion_absolute.notify = server_cursor_motion_absolute; -- cgit v1.2.3 From eca3273ea360b155ddda2b75f09a442452fb0918 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Wed, 9 Mar 2022 14:52:27 -0500 Subject: types/wlr_pointer: uniformize events name --- tinywl.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tinywl.c b/tinywl.c index e21d473..fae16ac 100644 --- a/tinywl.c +++ b/tinywl.c @@ -458,13 +458,13 @@ static void server_cursor_motion(struct wl_listener *listener, void *data) { * pointer motion event (i.e. a delta) */ struct tinywl_server *server = wl_container_of(listener, server, cursor_motion); - struct wlr_event_pointer_motion *event = data; + struct wlr_pointer_motion_event *event = data; /* The cursor doesn't move unless we tell it to. The cursor automatically * handles constraining the motion to the output layout, as well as any * special configuration applied for the specific input device which * generated the event. You can pass NULL for the device if you want to move * the cursor around without any input. */ - wlr_cursor_move(server->cursor, event->device, + wlr_cursor_move(server->cursor, &event->pointer->base, event->delta_x, event->delta_y); process_cursor_motion(server, event->time_msec); } @@ -479,8 +479,9 @@ static void server_cursor_motion_absolute( * emits these events. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_motion_absolute); - struct wlr_event_pointer_motion_absolute *event = data; - wlr_cursor_warp_absolute(server->cursor, event->device, event->x, event->y); + struct wlr_pointer_motion_absolute_event *event = data; + wlr_cursor_warp_absolute(server->cursor, &event->pointer->base, event->x, + event->y); process_cursor_motion(server, event->time_msec); } @@ -489,7 +490,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { * event. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_button); - struct wlr_event_pointer_button *event = data; + struct wlr_pointer_button_event *event = data; /* Notify the client with pointer focus that a button press has occurred */ wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); @@ -511,7 +512,7 @@ static void server_cursor_axis(struct wl_listener *listener, void *data) { * for example when you move the scroll wheel. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_axis); - struct wlr_event_pointer_axis *event = data; + struct wlr_pointer_axis_event *event = data; /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(server->seat, event->time_msec, event->orientation, event->delta, -- cgit v1.2.3 From 9dde60ef6bf826ae632ceffed3a5ea1c9208873a Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Wed, 9 Mar 2022 15:05:12 -0500 Subject: types/wlr_keyboard: uniformize events name --- tinywl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index fae16ac..da965c4 100644 --- a/tinywl.c +++ b/tinywl.c @@ -188,7 +188,7 @@ static void keyboard_handle_key( struct tinywl_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct tinywl_server *server = keyboard->server; - struct wlr_event_keyboard_key *event = data; + struct wlr_keyboard_key_event *event = data; struct wlr_seat *seat = server->seat; /* Translate libinput keycode -> xkbcommon */ -- cgit v1.2.3 From 8c85f04e9f3eef9bf647e6930f50b30c8188de3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Mon, 21 Mar 2022 17:37:17 -0600 Subject: seat: take wlr_keyboard in wlr_seat_set_keyboard() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Leonardo Hernández Hernández --- tinywl.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tinywl.c b/tinywl.c index da965c4..290dd09 100644 --- a/tinywl.c +++ b/tinywl.c @@ -91,7 +91,7 @@ struct tinywl_view { struct tinywl_keyboard { struct wl_list link; struct tinywl_server *server; - struct wlr_input_device *device; + struct wlr_keyboard *wlr_keyboard; struct wl_listener modifiers; struct wl_listener key; @@ -149,10 +149,10 @@ static void keyboard_handle_modifiers( * same seat. You can swap out the underlying wlr_keyboard like this and * wlr_seat handles this transparently. */ - wlr_seat_set_keyboard(keyboard->server->seat, keyboard->device); + wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr_keyboard); /* Send modifiers to the client. */ wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, - &keyboard->device->keyboard->modifiers); + &keyboard->wlr_keyboard->modifiers); } static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { @@ -196,10 +196,10 @@ static void keyboard_handle_key( /* Get a list of keysyms based on the keymap for this keyboard */ const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms( - keyboard->device->keyboard->xkb_state, keycode, &syms); + keyboard->wlr_keyboard->xkb_state, keycode, &syms); bool handled = false; - uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); + uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr_keyboard); if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { /* If alt is held down and this button was _pressed_, we attempt to @@ -211,7 +211,7 @@ static void keyboard_handle_key( if (!handled) { /* Otherwise, we pass it along to the client. */ - wlr_seat_set_keyboard(seat, keyboard->device); + wlr_seat_set_keyboard(seat, keyboard->wlr_keyboard); wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state); } @@ -236,7 +236,7 @@ static void server_new_keyboard(struct tinywl_server *server, struct tinywl_keyboard *keyboard = calloc(1, sizeof(struct tinywl_keyboard)); keyboard->server = server; - keyboard->device = device; + keyboard->wlr_keyboard = device->keyboard; /* We need to prepare an XKB keymap and assign it to the keyboard. This * assumes the defaults (e.g. layout = "us"). */ @@ -257,7 +257,7 @@ static void server_new_keyboard(struct tinywl_server *server, keyboard->destroy.notify = keyboard_handle_destroy; wl_signal_add(&device->events.destroy, &keyboard->destroy); - wlr_seat_set_keyboard(server->seat, device); + wlr_seat_set_keyboard(server->seat, keyboard->wlr_keyboard); /* And add the keyboard to our list of keyboards */ wl_list_insert(&server->keyboards, &keyboard->link); -- cgit v1.2.3 From 3751af63c4c4f13f499b2322dedaa4ffbb87f795 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 1 Apr 2022 11:38:25 +0300 Subject: tinywl: don't generate xdg-shell-protocol.c It's unused, and wlroots-based compositors don't need to do this anyway. --- Makefile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 505666f..68bac25 100644 --- a/Makefile +++ b/Makefile @@ -12,11 +12,7 @@ xdg-shell-protocol.h: $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ -xdg-shell-protocol.c: xdg-shell-protocol.h - $(WAYLAND_SCANNER) private-code \ - $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ - -tinywl: tinywl.c xdg-shell-protocol.h xdg-shell-protocol.c +tinywl: tinywl.c xdg-shell-protocol.h $(CC) $(CFLAGS) \ -g -Werror -I. \ -DWLR_USE_UNSTABLE \ -- cgit v1.2.3 From 5283ff56872f0602d2af8e1d40a52d6aad3ffb62 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 30 Mar 2022 16:10:41 +0300 Subject: tinywl: remove outdated non-feature from README.md Now that tinywl uses wlr_scene under the hood, damage tracking comes for free. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index e0385d2..7fc83b9 100644 --- a/README.md +++ b/README.md @@ -43,5 +43,3 @@ Notable omissions from TinyWL: - Optional protocols, e.g. screen capture, primary selection, virtual keyboard, etc. Most of these are plug-and-play with wlroots, but they're omitted for brevity. -- Damage tracking, which tracks which parts of the screen are changing and - minimizes redraws accordingly. -- cgit v1.2.3 From 466bf8f5f3e58e43f73a74baaea28e5160ec019f Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 11 Apr 2022 21:33:15 +0300 Subject: xdg-toplevel: don't schedule configures on state requests --- tinywl.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tinywl.c b/tinywl.c index 290dd09..2ab0bcc 100644 --- a/tinywl.c +++ b/tinywl.c @@ -85,6 +85,8 @@ struct tinywl_view { struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; + struct wl_listener request_maximize; + struct wl_listener request_fullscreen; int x, y; }; @@ -619,6 +621,8 @@ static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&view->destroy.link); wl_list_remove(&view->request_move.link); wl_list_remove(&view->request_resize.link); + wl_list_remove(&view->request_maximize.link); + wl_list_remove(&view->request_fullscreen.link); free(view); } @@ -684,6 +688,26 @@ static void xdg_toplevel_request_resize( begin_interactive(view, TINYWL_CURSOR_RESIZE, event->edges); } +static void xdg_toplevel_request_maximize( + struct wl_listener *listener, void *data) { + /* This event is raised when a client would like to maximize itself, + * typically because the user clicked on the maximize button on + * client-side decorations. tinywl doesn't support maximization, but + * to conform to xdg-shell protocol we still must send a configure. + * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ + struct tinywl_view *view = + wl_container_of(listener, view, request_maximize); + wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); +} + +static void xdg_toplevel_request_fullscreen( + struct wl_listener *listener, void *data) { + /* Just as with request_maximize, we must send a configure here. */ + struct tinywl_view *view = + wl_container_of(listener, view, request_fullscreen); + wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); +} + static void server_new_xdg_surface(struct wl_listener *listener, void *data) { /* This event is raised when wlr_xdg_shell receives a new xdg surface from a * client, either a toplevel (application window) or popup. */ @@ -730,6 +754,12 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { wl_signal_add(&toplevel->events.request_move, &view->request_move); view->request_resize.notify = xdg_toplevel_request_resize; wl_signal_add(&toplevel->events.request_resize, &view->request_resize); + view->request_maximize.notify = xdg_toplevel_request_maximize; + wl_signal_add(&toplevel->events.request_maximize, + &view->request_maximize); + view->request_fullscreen.notify = xdg_toplevel_request_fullscreen; + wl_signal_add(&toplevel->events.request_fullscreen, + &view->request_fullscreen); } int main(int argc, char *argv[]) { -- cgit v1.2.3 From 65192f0e637cafeacbad92d45bf270783e0795db Mon Sep 17 00:00:00 2001 From: xiaoyaobing <1109050030@qq.com> Date: Tue, 3 May 2022 00:08:32 +0800 Subject: tinywl/tinywl: clean up tinywl_output when wlr_output is gone Add destroy event processing. Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3416 --- tinywl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tinywl.c b/tinywl.c index 2ab0bcc..7f6de6b 100644 --- a/tinywl.c +++ b/tinywl.c @@ -73,6 +73,7 @@ struct tinywl_output { struct tinywl_server *server; struct wlr_output *wlr_output; struct wl_listener frame; + struct wl_listener destroy; }; struct tinywl_view { @@ -549,6 +550,15 @@ static void output_frame(struct wl_listener *listener, void *data) { wlr_scene_output_send_frame_done(scene_output, &now); } +static void output_destroy(struct wl_listener *listener, void *data) { + struct tinywl_output *output = wl_container_of(listener, output, destroy); + + wl_list_remove(&output->frame.link); + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->link); + free(output); +} + static void server_new_output(struct wl_listener *listener, void *data) { /* This event is raised by the backend when a new output (aka a display or * monitor) becomes available. */ @@ -582,6 +592,11 @@ static void server_new_output(struct wl_listener *listener, void *data) { /* Sets up a listener for the frame notify event. */ output->frame.notify = output_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); + + /* Sets up a listener for the destroy notify event. */ + output->destroy.notify = output_destroy; + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + wl_list_insert(&server->outputs, &output->link); /* Adds this to the output layout. The add_auto function arranges outputs -- cgit v1.2.3 From f64991b5a125aa8ae51eab1de7e56e45d3b94dd9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 19 Mar 2022 14:00:43 +0100 Subject: tinywl: don't crash when there is no keyboard Running with WLR_BACKENDS=headless, there is no keyboard device. Avoid crashes like so: ../tinywl/tinywl.c:136:2: runtime error: member access within null pointer of type 'struct wlr_keyboard' ../tinywl/tinywl.c:136:2: runtime error: member access within null pointer of type 'struct wlr_keyboard' AddressSanitizer:DEADLYSIGNAL ================================================================= ==331107==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000120 (pc 0x556ed03e4e99 bp 0x7ffce834bc10 sp 0x7ffce834bbb0 T0) ==331107==The signal is caused by a READ memory access. ==331107==Hint: address points to the zero page. #0 0x556ed03e4e99 in focus_view ../tinywl/tinywl.c:136 #1 0x556ed03eb3be in xdg_toplevel_map ../tinywl/tinywl.c:603 #2 0x7f75d6f768db in wlr_signal_emit_safe ../util/signal.c:29 #3 0x7f75d6e9cac7 in xdg_surface_role_commit ../types/xdg_shell/wlr_xdg_surface.c:315 #4 0x7f75d6eb6944 in surface_commit_state ../types/wlr_compositor.c:466 #5 0x7f75d6eb7b02 in surface_handle_commit ../types/wlr_compositor.c:523 #6 0x7f75d5714d49 (/usr/lib/libffi.so.8+0x6d49) #7 0x7f75d5714266 (/usr/lib/libffi.so.8+0x6266) #8 0x7f75d68cb322 (/usr/lib/libwayland-server.so.0+0xd322) #9 0x7f75d68c65cb (/usr/lib/libwayland-server.so.0+0x85cb) #10 0x7f75d68c91c9 in wl_event_loop_dispatch (/usr/lib/libwayland-server.so.0+0xb1c9) #11 0x7f75d68c6d36 in wl_display_run (/usr/lib/libwayland-server.so.0+0x8d36) #12 0x556ed03eef55 in main ../tinywl/tinywl.c:905 #13 0x7f75d5d2330f in __libc_start_call_main (/usr/lib/libc.so.6+0x2d30f) #14 0x7f75d5d233c0 in __libc_start_main@GLIBC_2.2.5 (/usr/lib/libc.so.6+0x2d3c0) #15 0x556ed03e46e4 in _start (/home/simon/src/wlroots/build/tinywl/tinywl+0x136e4) --- tinywl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tinywl.c b/tinywl.c index 7f6de6b..633b3f2 100644 --- a/tinywl.c +++ b/tinywl.c @@ -136,8 +136,10 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { * track of this and automatically send key events to the appropriate * clients without additional work on your part. */ - wlr_seat_keyboard_notify_enter(seat, view->xdg_toplevel->base->surface, - keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); + if (keyboard != NULL) { + wlr_seat_keyboard_notify_enter(seat, view->xdg_toplevel->base->surface, + keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); + } } static void keyboard_handle_modifiers( -- cgit v1.2.3 From a755670648459d6c341a75a384942c3f44c5c85b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 11 May 2022 13:57:51 +0200 Subject: xdg-shell: specify version in wlr_xdg_shell_create With protocol additions such as [1], compositors currently have no way to opt out of the version upgrade. The protocol upgrade will always be backwards-compatible but may require new compositor features. The status quo doesn't make it possible to ship a protocol addition without breaking the wlroots API. This will be an issue for API stabilization [2]. To address this, let compositors provide a maximum version in the function creating the global. We need to support all previous versions of the interface anyways because of older clients. This mechanism works the same way as Wayland clients passing a version in wl_global.bind. [1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3514 [2]: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/1008 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3397 --- tinywl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tinywl.c b/tinywl.c index 633b3f2..783cc18 100644 --- a/tinywl.c +++ b/tinywl.c @@ -853,13 +853,14 @@ int main(int argc, char *argv[]) { server.scene = wlr_scene_create(); wlr_scene_attach_output_layout(server.scene, server.output_layout); - /* Set up the xdg-shell. The xdg-shell is a Wayland protocol which is used - * for application windows. For more detail on shells, refer to my article: + /* Set up xdg-shell version 2. The xdg-shell is a Wayland protocol which is + * used for application windows. For more detail on shells, refer to my + * article: * * https://drewdevault.com/2018/07/29/Wayland-shells.html */ wl_list_init(&server.views); - server.xdg_shell = wlr_xdg_shell_create(server.wl_display); + server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 2); server.new_xdg_surface.notify = server_new_xdg_surface; wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface); -- cgit v1.2.3 From dd1d163bf8ab94b3f59209193c2cd209c5b472b4 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 19 May 2022 14:26:50 -0400 Subject: wlr_scene: Refactor wlr_scene_surface to be a helper on top of wlr_scene_buffer --- tinywl.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tinywl.c b/tinywl.c index 783cc18..bf3fb43 100644 --- a/tinywl.c +++ b/tinywl.c @@ -341,10 +341,17 @@ static struct tinywl_view *desktop_view_at( * surface in the surface tree of a tinywl_view. */ struct wlr_scene_node *node = wlr_scene_node_at( &server->scene->node, lx, ly, sx, sy); - if (node == NULL || node->type != WLR_SCENE_NODE_SURFACE) { + if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { return NULL; } - *surface = wlr_scene_surface_from_node(node)->surface; + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_buffer(scene_buffer); + if (!scene_surface) { + return NULL; + } + + *surface = scene_surface->surface; /* Find the node corresponding to the tinywl_view at the root of this * surface tree, it is the only one for which we set the data field. */ while (node != NULL && node->data == NULL) { -- cgit v1.2.3 From 4d0c1bd431602a3e83d70656e53fef528b2cf0ef Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 23 May 2022 21:19:02 +0300 Subject: tinywl: use xdg-shell v3 --- tinywl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tinywl.c b/tinywl.c index bf3fb43..d8e171e 100644 --- a/tinywl.c +++ b/tinywl.c @@ -860,14 +860,14 @@ int main(int argc, char *argv[]) { server.scene = wlr_scene_create(); wlr_scene_attach_output_layout(server.scene, server.output_layout); - /* Set up xdg-shell version 2. The xdg-shell is a Wayland protocol which is + /* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is * used for application windows. For more detail on shells, refer to my * article: * * https://drewdevault.com/2018/07/29/Wayland-shells.html */ wl_list_init(&server.views); - server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 2); + server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); server.new_xdg_surface.notify = server_new_xdg_surface; wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface); -- cgit v1.2.3 From 758d978a2df6e7934cbab9c372f1c102a262982e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 30 May 2022 19:26:08 -0400 Subject: wlr_scene: Refactor wlr_scene (the root element) to encase a wlr_scene_tree Co-authored-by: Isaac Freund --- tinywl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tinywl.c b/tinywl.c index d8e171e..49d33b4 100644 --- a/tinywl.c +++ b/tinywl.c @@ -340,7 +340,7 @@ static struct tinywl_view *desktop_view_at( * we only care about surface nodes as we are specifically looking for a * surface in the surface tree of a tinywl_view. */ struct wlr_scene_node *node = wlr_scene_node_at( - &server->scene->node, lx, ly, sx, sy); + &server->scene->tree.node, lx, ly, sx, sy); if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { return NULL; } @@ -760,7 +760,7 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { view->server = server; view->xdg_toplevel = xdg_surface->toplevel; view->scene_node = wlr_scene_xdg_surface_create( - &view->server->scene->node, view->xdg_toplevel->base); + &view->server->scene->tree.node, view->xdg_toplevel->base); view->scene_node->data = view; xdg_surface->data = view->scene_node; -- cgit v1.2.3 From c006fe5b18fb1eaef45f70cc83c50bb9d460bc9d Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 30 May 2022 19:23:27 -0400 Subject: wlr_scene: Only allow parenting on a wlr_scene_tree --- tinywl.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tinywl.c b/tinywl.c index 49d33b4..f96fe79 100644 --- a/tinywl.c +++ b/tinywl.c @@ -80,7 +80,7 @@ struct tinywl_view { struct wl_list link; struct tinywl_server *server; struct wlr_xdg_toplevel *xdg_toplevel; - struct wlr_scene_node *scene_node; + struct wlr_scene_tree *scene_tree; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -126,7 +126,7 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); /* Move the view to the front */ - wlr_scene_node_raise_to_top(view->scene_node); + wlr_scene_node_raise_to_top(&view->scene_tree->node); wl_list_remove(&view->link); wl_list_insert(&server->views, &view->link); /* Activate the new surface */ @@ -354,10 +354,11 @@ static struct tinywl_view *desktop_view_at( *surface = scene_surface->surface; /* Find the node corresponding to the tinywl_view at the root of this * surface tree, it is the only one for which we set the data field. */ - while (node != NULL && node->data == NULL) { - node = node->parent; + struct wlr_scene_tree *tree = node->parent; + while (tree != NULL && tree->node.data == NULL) { + tree = tree->node.parent; } - return node->data; + return tree->node.data; } static void process_cursor_move(struct tinywl_server *server, uint32_t time) { @@ -365,7 +366,7 @@ static void process_cursor_move(struct tinywl_server *server, uint32_t time) { struct tinywl_view *view = server->grabbed_view; view->x = server->cursor->x - server->grab_x; view->y = server->cursor->y - server->grab_y; - wlr_scene_node_set_position(view->scene_node, view->x, view->y); + wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); } static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { @@ -414,7 +415,7 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); view->x = new_left - geo_box.x; view->y = new_top - geo_box.y; - wlr_scene_node_set_position(view->scene_node, view->x, view->y); + wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; @@ -747,9 +748,9 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface( xdg_surface->popup->parent); - struct wlr_scene_node *parent_node = parent->data; + struct wlr_scene_tree *parent_tree = parent->data; xdg_surface->data = wlr_scene_xdg_surface_create( - parent_node, xdg_surface); + parent_tree, xdg_surface); return; } assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); @@ -759,10 +760,10 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { calloc(1, sizeof(struct tinywl_view)); view->server = server; view->xdg_toplevel = xdg_surface->toplevel; - view->scene_node = wlr_scene_xdg_surface_create( - &view->server->scene->tree.node, view->xdg_toplevel->base); - view->scene_node->data = view; - xdg_surface->data = view->scene_node; + view->scene_tree = wlr_scene_xdg_surface_create( + &view->server->scene->tree, view->xdg_toplevel->base); + view->scene_tree->node.data = view; + xdg_surface->data = view->scene_tree; /* Listen to the various events it can emit */ view->map.notify = xdg_toplevel_map; -- cgit v1.2.3 From 0c0db4372dfd39cd161d4ee4eac6d0b5787d6670 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 20 Jun 2022 16:57:29 +0200 Subject: wlr_input_device: remove anon union field This union is unnecessary since the recent input device refactor and can now be replaced by wlr_*_from_input_device() functions. --- tinywl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tinywl.c b/tinywl.c index f96fe79..2b58a4b 100644 --- a/tinywl.c +++ b/tinywl.c @@ -238,10 +238,12 @@ static void keyboard_handle_destroy(struct wl_listener *listener, void *data) { static void server_new_keyboard(struct tinywl_server *server, struct wlr_input_device *device) { + struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device); + struct tinywl_keyboard *keyboard = calloc(1, sizeof(struct tinywl_keyboard)); keyboard->server = server; - keyboard->wlr_keyboard = device->keyboard; + keyboard->wlr_keyboard = wlr_keyboard; /* We need to prepare an XKB keymap and assign it to the keyboard. This * assumes the defaults (e.g. layout = "us"). */ @@ -249,16 +251,16 @@ static void server_new_keyboard(struct tinywl_server *server, struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); - wlr_keyboard_set_keymap(device->keyboard, keymap); + wlr_keyboard_set_keymap(wlr_keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); - wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); + wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600); /* Here we set up listeners for keyboard events. */ keyboard->modifiers.notify = keyboard_handle_modifiers; - wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); + wl_signal_add(&wlr_keyboard->events.modifiers, &keyboard->modifiers); keyboard->key.notify = keyboard_handle_key; - wl_signal_add(&device->keyboard->events.key, &keyboard->key); + wl_signal_add(&wlr_keyboard->events.key, &keyboard->key); keyboard->destroy.notify = keyboard_handle_destroy; wl_signal_add(&device->events.destroy, &keyboard->destroy); -- cgit v1.2.3 From 43b052721ee72b4231431a353ce7371e3b2083ad Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Thu, 23 Jun 2022 12:23:37 -0400 Subject: tinywl: exit on backend, renderer or allocator creation failure --- tinywl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tinywl.c b/tinywl.c index 2b58a4b..4fc8477 100644 --- a/tinywl.c +++ b/tinywl.c @@ -818,12 +818,21 @@ int main(int argc, char *argv[]) { * backend based on the current environment, such as opening an X11 window * if an X11 server is running. */ server.backend = wlr_backend_autocreate(server.wl_display); + if (server.backend == NULL) { + wlr_log(WLR_ERROR, "failed to create wlr_backend"); + return 1; + } /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user * can also specify a renderer using the WLR_RENDERER env var. * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ server.renderer = wlr_renderer_autocreate(server.backend); + if (server.renderer == NULL) { + wlr_log(WLR_ERROR, "failed to create wlr_renderer"); + return 1; + } + wlr_renderer_init_wl_display(server.renderer, server.wl_display); /* Autocreates an allocator for us. @@ -832,6 +841,10 @@ int main(int argc, char *argv[]) { * screen */ server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); + if (server.allocator == NULL) { + wlr_log(WLR_ERROR, "failed to create wlr_allocator"); + return 1; + } /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces, the subcompositor allows to -- cgit v1.2.3 From f8b869bd19c1b128fd3a4d5005abd297b07045c4 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 7 Nov 2022 19:55:40 +0300 Subject: tinywl: handle view unmap while grabbed Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3372 --- tinywl.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tinywl.c b/tinywl.c index 4fc8477..7e3c8c1 100644 --- a/tinywl.c +++ b/tinywl.c @@ -363,6 +363,12 @@ static struct tinywl_view *desktop_view_at( return tree->node.data; } +static void reset_cursor_mode(struct tinywl_server *server) { + /* Reset the cursor mode to passthrough. */ + server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; + server->grabbed_view = NULL; +} + static void process_cursor_move(struct tinywl_server *server, uint32_t time) { /* Move the grabbed view to the new position. */ struct tinywl_view *view = server->grabbed_view; @@ -515,7 +521,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { /* If you released any buttons, we exit interactive move/resize mode. */ - server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; + reset_cursor_mode(server); } else { /* Focus that client if the button was _pressed_ */ focus_view(view, surface); @@ -636,6 +642,11 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ struct tinywl_view *view = wl_container_of(listener, view, unmap); + /* Reset the cursor mode if the grabbed view was unmapped. */ + if (view == view->server->grabbed_view) { + reset_cursor_mode(view->server); + } + wl_list_remove(&view->link); } -- cgit v1.2.3