From 61cc8c3c55767556b81474eebe6712f7e9b42706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:08:48 +0100 Subject: [PATCH] wayland: implement wayl_init() Wayland instantiation is now done by the wayland backend, not in main. --- main.c | 509 +++------------------------------------------------- render.c | 56 +++--- search.c | 4 +- terminal.h | 2 +- wayland.c | 518 ++++++++++++++++++++++++++++++++++++++++++++++++++++- wayland.h | 6 +- 6 files changed, 569 insertions(+), 526 deletions(-) diff --git a/main.c b/main.c index 5dd37dfb..eb91ce5b 100644 --- a/main.c +++ b/main.c @@ -33,371 +33,17 @@ #include "fdm.h" #include "font.h" #include "grid.h" -#include "input.h" -#include "render.h" -#include "selection.h" #include "shm.h" #include "slave.h" #include "terminal.h" #include "tokenize.h" #include "version.h" +#include "render.h" #include "vt.h" #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) -static void -shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) -{ - struct wayland *wayl = data; - if (format == WL_SHM_FORMAT_ARGB8888) - wayl->have_argb8888 = true; -} - -static const struct wl_shm_listener shm_listener = { - .format = &shm_format, -}; - -static void -xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) -{ - LOG_DBG("wm base ping"); - xdg_wm_base_pong(shell, serial); -} - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = &xdg_wm_base_ping, -}; - -static void -seat_handle_capabilities(void *data, struct wl_seat *wl_seat, - enum wl_seat_capability caps) -{ - struct wayland *wayl = data; - - if (wayl->keyboard != NULL) { - wl_keyboard_release(wayl->keyboard); - wayl->keyboard = NULL; - } - - if (wayl->pointer.pointer != NULL) { - wl_pointer_release(wayl->pointer.pointer); - wayl->pointer.pointer = NULL; - } - - if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { - wayl->keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl); - } - - if (caps & WL_SEAT_CAPABILITY_POINTER) { - wayl->pointer.pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl); - } -} - -static void -seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) -{ -} - -static const struct wl_seat_listener seat_listener = { - .capabilities = seat_handle_capabilities, - .name = seat_handle_name, -}; - -static void -output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, - int32_t physical_width, int32_t physical_height, - int32_t subpixel, const char *make, const char *model, - int32_t transform) -{ - struct monitor *mon = data; - mon->width_mm = physical_width; - mon->height_mm = physical_height; -} - -static void -output_mode(void *data, struct wl_output *wl_output, uint32_t flags, - int32_t width, int32_t height, int32_t refresh) -{ - if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) - return; - - struct monitor *mon = data; - mon->refresh = (float)refresh / 1000; -} - -static void -output_done(void *data, struct wl_output *wl_output) -{ -} - -static void -output_scale(void *data, struct wl_output *wl_output, int32_t factor) -{ - struct monitor *mon = data; - struct terminal *term = mon->wayl->term; - - mon->scale = factor; - - render_resize(term, term->width / term->scale, term->height / term->scale); - render_reload_cursor_theme(term); -} - -static const struct wl_output_listener output_listener = { - .geometry = &output_geometry, - .mode = &output_mode, - .done = &output_done, - .scale = &output_scale, -}; - -static void -xdg_output_handle_logical_position( - void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) -{ - struct monitor *mon = data; - mon->x = x; - mon->y = y; -} - -static void -xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, - int32_t width, int32_t height) -{ - struct monitor *mon = data; - mon->width_px = width; - mon->height_px = height; -} - -static void -xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) -{ -} - -static void -xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, - const char *name) -{ - struct monitor *mon = data; - mon->name = strdup(name); -} - -static void -xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, - const char *description) -{ -} - -static struct zxdg_output_v1_listener xdg_output_listener = { - .logical_position = xdg_output_handle_logical_position, - .logical_size = xdg_output_handle_logical_size, - .done = xdg_output_handle_done, - .name = xdg_output_handle_name, - .description = xdg_output_handle_description, -}; - -static void -handle_global(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version) -{ - LOG_DBG("global: %s, version=%u", interface, version); - struct wayland *wayl = data; - - if (strcmp(interface, wl_compositor_interface.name) == 0) { - wayl->compositor = wl_registry_bind( - wayl->registry, name, &wl_compositor_interface, 4); - } - - else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { - wayl->sub_compositor = wl_registry_bind( - wayl->registry, name, &wl_subcompositor_interface, 1); - } - - else if (strcmp(interface, wl_shm_interface.name) == 0) { - wayl->shm = wl_registry_bind( - wayl->registry, name, &wl_shm_interface, 1); - wl_shm_add_listener(wayl->shm, &shm_listener, wayl); - wl_display_roundtrip(wayl->display); - } - - else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - wayl->shell = wl_registry_bind( - wayl->registry, name, &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(wayl->shell, &xdg_wm_base_listener, wayl); - } - - else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) - wayl->xdg_decoration_manager = wl_registry_bind( - wayl->registry, name, &zxdg_decoration_manager_v1_interface, 1); - - else if (strcmp(interface, wl_seat_interface.name) == 0) { - wayl->seat = wl_registry_bind( - wayl->registry, name, &wl_seat_interface, 5); - wl_seat_add_listener(wayl->seat, &seat_listener, wayl); - wl_display_roundtrip(wayl->display); - } - - else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { - wayl->xdg_output_manager = wl_registry_bind( - wayl->registry, name, &zxdg_output_manager_v1_interface, min(version, 2)); - } - - else if (strcmp(interface, wl_output_interface.name) == 0) { - struct wl_output *output = wl_registry_bind( - wayl->registry, name, &wl_output_interface, 3); - - tll_push_back( - wayl->monitors, ((struct monitor){.wayl = wayl, .output = output})); - - struct monitor *mon = &tll_back(wayl->monitors); - wl_output_add_listener(output, &output_listener, mon); - - mon->xdg = zxdg_output_manager_v1_get_xdg_output( - wayl->xdg_output_manager, mon->output); - zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon); - wl_display_roundtrip(wayl->display); - } - - else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { - wayl->data_device_manager = wl_registry_bind( - wayl->registry, name, &wl_data_device_manager_interface, 1); - } - - else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) { - wayl->primary_selection_device_manager = wl_registry_bind( - wayl->registry, name, &zwp_primary_selection_device_manager_v1_interface, 1); - } -} - -static void -surface_enter(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct wayland *wayl = data; - struct terminal *term = wayl_terminal_from_surface(wayl, wl_surface); - - tll_foreach(wayl->monitors, it) { - if (it->item.output == wl_output) { - LOG_DBG("mapped on %s", it->item.name); - tll_push_back(term->window.on_outputs, &it->item); - - /* Resize, since scale-to-use may have changed */ - render_resize(term, term->width / term->scale, term->height / term->scale); - render_reload_cursor_theme(term); - return; - } - } - - LOG_ERR("mapped on unknown output"); -} - -static void -surface_leave(void *data, struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct wayland *wayl = data; - struct terminal *term = wayl_terminal_from_surface(wayl, wl_surface); - - tll_foreach(term->window.on_outputs, it) { - if (it->item->output != wl_output) - continue; - - LOG_DBG("unmapped from %s", it->item->name); - tll_remove(term->window.on_outputs, it); - - /* Resize, since scale-to-use may have changed */ - render_resize(term, term->width / term->scale, term->height / term->scale); - render_reload_cursor_theme(term); - return; - } - - LOG_ERR("unmapped from unknown output"); -} - -static const struct wl_surface_listener surface_listener = { - .enter = &surface_enter, - .leave = &surface_leave, -}; - -static void -xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, - int32_t width, int32_t height, struct wl_array *states) -{ - LOG_DBG("xdg-toplevel: configure: %dx%d", width, height); - - if (width <= 0 || height <= 0) - return; - - struct wayland *wayl = data; - struct terminal *term = wayl_terminal_from_xdg_toplevel(wayl, xdg_toplevel); - render_resize(term, width, height); -} - -static void -xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) -{ - struct wayland *wayl = data; - struct terminal *term = wayl_terminal_from_xdg_toplevel(wayl, xdg_toplevel); - LOG_DBG("xdg-toplevel: close"); - - term->quit = true; - wl_display_roundtrip(wayl->display); -} - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = &xdg_toplevel_configure, - .close = &xdg_toplevel_close, -}; - -static void -xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, - uint32_t serial) -{ - //LOG_DBG("xdg-surface: configure"); - xdg_surface_ack_configure(xdg_surface, serial); -} - -static const struct xdg_surface_listener xdg_surface_listener = { - .configure = &xdg_surface_configure, -}; - -static void -xdg_toplevel_decoration_configure(void *data, - struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, - uint32_t mode) -{ - switch (mode) { - case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: - LOG_ERR("unimplemented: client-side decorations"); - break; - - case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: - LOG_DBG("using server-side decorations"); - break; - - default: - LOG_ERR("unimplemented: unknown XDG toplevel decoration mode: %u", mode); - break; - } -} - -static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = { - .configure = &xdg_toplevel_decoration_configure, -}; - -static void -handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) -{ - LOG_WARN("global removed: %u", name); - assert(false); -} - -static const struct wl_registry_listener registry_listener = { - .global = &handle_global, - .global_remove = &handle_global_remove, -}; - static bool fdm_wayl(struct fdm *fdm, int fd, int events, void *data) { @@ -470,7 +116,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) * ourselves we just received keyboard input, and in * this case *not* delay rendering? */ - if (term->window.frame_callback == NULL) { + if (term->window->frame_callback == NULL) { /* First timeout - reset each time we receive input. */ timerfd_settime( term->delayed_render_timer.lower_fd, 0, @@ -491,6 +137,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) return !(events & EPOLLHUP); } +#include "input.h" static bool fdm_repeat(struct fdm *fdm, int fd, int events, void *data) { @@ -727,16 +374,6 @@ main(int argc, char *const *argv) setlocale(LC_ALL, ""); setenv("TERM", conf.term, 1); - struct fdm *fdm = NULL; - - struct wayland wayl = { - .kbd = { - .repeat = { - .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), - }, - }, - }; - struct terminal term = { .quit = false, .ptmx = posix_openpt(O_RDWR | O_NOCTTY), @@ -799,7 +436,6 @@ main(int argc, char *const *argv) .normal = {.damage = tll_init(), .scroll_damage = tll_init()}, .alt = {.damage = tll_init(), .scroll_damage = tll_init()}, .grid = &term.normal, - .wl = &wayl, .render = { .scrollback_lines = conf.scrollback_lines, .workers = { @@ -840,6 +476,18 @@ main(int argc, char *const *argv) memcpy(term.colors.table, term.colors.default_table, sizeof(term.colors.table)); } + struct fdm *fdm = NULL; + struct wayland *wayl = NULL; + + if ((fdm = fdm_init()) == NULL) + goto out; + + if ((wayl = wayl_init(fdm)) == NULL) + goto out; + + term.wl = wayl; + wayl->term = &term; + if (term.ptmx == -1) { LOG_ERR("failed to open pseudo terminal"); goto out; @@ -897,133 +545,19 @@ main(int argc, char *const *argv) term.cell_height = (int)ceil(term.fextents.height); LOG_INFO("cell width=%d, height=%d", term.cell_width, term.cell_height); - term.wl->term = &term; - term.wl->display = wl_display_connect(NULL); - if (term.wl->display == NULL) { - LOG_ERR("failed to connect to wayland; no compositor running?"); - goto out; - } - - term.wl->registry = wl_display_get_registry(term.wl->display); - if (term.wl->registry == NULL) { - LOG_ERR("failed to get wayland registry"); - goto out; - } - - wl_registry_add_listener(term.wl->registry, ®istry_listener, term.wl); - wl_display_roundtrip(term.wl->display); - - if (term.wl->compositor == NULL) { - LOG_ERR("no compositor"); - goto out; - } - if (term.wl->shm == NULL) { - LOG_ERR("no shared memory buffers interface"); - goto out; - } - if (term.wl->shell == NULL) { - LOG_ERR("no XDG shell interface"); - goto out; - } - if (!term.wl->have_argb8888) { - LOG_ERR("compositor does not support ARGB surfaces"); - goto out; - } - if (term.wl->seat == NULL) { - LOG_ERR("no seat available"); - goto out; - } - if (term.wl->data_device_manager == NULL) { - LOG_ERR("no clipboard available " - "(wl_data_device_manager not implemented by server)"); - goto out; - } - if (term.wl->primary_selection_device_manager == NULL) - LOG_WARN("no primary selection available"); - - tll_foreach(term.wl->monitors, it) { - LOG_INFO("%s: %dx%d+%dx%d (scale=%d, refresh=%.2fHz)", - it->item.name, it->item.width_px, it->item.height_px, - it->item.x, it->item.y, it->item.scale, it->item.refresh); - } - - /* Clipboard */ - term.wl->data_device = wl_data_device_manager_get_data_device( - term.wl->data_device_manager, term.wl->seat); - wl_data_device_add_listener(term.wl->data_device, &data_device_listener, term.wl); - - /* Primary selection */ - if (term.wl->primary_selection_device_manager != NULL) { - term.wl->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device( - term.wl->primary_selection_device_manager, term.wl->seat); - zwp_primary_selection_device_v1_add_listener( - term.wl->primary_selection_device, &primary_selection_device_listener, term.wl); - } - - /* Cursor */ - unsigned cursor_size = 24; - const char *cursor_theme = getenv("XCURSOR_THEME"); - - { - const char *env_cursor_size = getenv("XCURSOR_SIZE"); - if (env_cursor_size != NULL) { - unsigned size; - if (sscanf(env_cursor_size, "%u", &size) == 1) - cursor_size = size; - } - } - - /* Note: theme is (re)loaded on scale and output changes */ - LOG_INFO("cursor theme: %s, size: %u", cursor_theme, cursor_size); - term.wl->pointer.size = cursor_size; - term.wl->pointer.theme_name = cursor_theme != NULL ? strdup(cursor_theme) : NULL; - - term.wl->pointer.surface = wl_compositor_create_surface(term.wl->compositor); - if (term.wl->pointer.surface == NULL) { - LOG_ERR("failed to create cursor surface"); - goto out; - } - /* Main window */ - term.window.surface = wl_compositor_create_surface(term.wl->compositor); - if (term.window.surface == NULL) { - LOG_ERR("failed to create wayland surface"); + term.window = wayl_win_init(wayl); + if (term.window == NULL) goto out; - } - wl_surface_add_listener(term.window.surface, &surface_listener, term.wl); - - term.window.xdg_surface = xdg_wm_base_get_xdg_surface(term.wl->shell, term.window.surface); - xdg_surface_add_listener(term.window.xdg_surface, &xdg_surface_listener, term.wl); - - term.window.xdg_toplevel = xdg_surface_get_toplevel(term.window.xdg_surface); - xdg_toplevel_add_listener(term.window.xdg_toplevel, &xdg_toplevel_listener, term.wl); - - xdg_toplevel_set_app_id(term.window.xdg_toplevel, "foot"); term_set_window_title(&term, "foot"); - /* Request server-side decorations */ - term.window.xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - term.wl->xdg_decoration_manager, term.window.xdg_toplevel); - zxdg_toplevel_decoration_v1_set_mode( - term.window.xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - zxdg_toplevel_decoration_v1_add_listener( - term.window.xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, term.wl); - - /* Scrollback search box */ - term.window.search_surface = wl_compositor_create_surface(term.wl->compositor); - term.window.search_sub_surface = wl_subcompositor_get_subsurface( - term.wl->sub_compositor, term.window.search_surface, term.window.surface); - wl_subsurface_set_desync(term.window.search_sub_surface); - - wl_surface_commit(term.window.surface); - wl_display_roundtrip(term.wl->display); - if (conf.width == -1) { assert(conf.height == -1); conf.width = 80 * term.cell_width; conf.height = 24 * term.cell_height; } + conf.width = max(conf.width, term.cell_width); conf.height = max(conf.height, term.cell_height); render_resize(&term, conf.width, conf.height); @@ -1116,9 +650,6 @@ main(int argc, char *const *argv) } } - if ((fdm = fdm_init()) == NULL) - goto out; - fdm_add(fdm, wl_display_get_fd(term.wl->display), EPOLLIN, &fdm_wayl, term.wl); fdm_add(fdm, term.ptmx, EPOLLIN, &fdm_ptmx, &term); fdm_add(fdm, term.wl->kbd.repeat.fd, EPOLLIN, &fdm_repeat, term.wl); @@ -1163,8 +694,8 @@ out: shm_fini(); - wayl_win_destroy(&term.window); - wayl_destroy(&wayl); + wayl_win_destroy(term.window); + wayl_destroy(wayl); free(term.vt.osc.data); for (int row = 0; row < term.normal.num_rows; row++) diff --git a/render.c b/render.c index 75f319bf..09e4d7d0 100644 --- a/render.c +++ b/render.c @@ -331,7 +331,7 @@ grid_render_scroll(struct terminal *term, struct buffer *buf, height * buf->stride); wl_surface_damage_buffer( - term->window.surface, term->x_margin, dst_y, term->width - term->x_margin, height); + term->window->surface, term->x_margin, dst_y, term->width - term->x_margin, height); } } @@ -357,7 +357,7 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, height * buf->stride); wl_surface_damage_buffer( - term->window.surface, term->x_margin, dst_y, term->width - term->x_margin, height); + term->window->surface, term->x_margin, dst_y, term->width - term->x_margin, height); } } @@ -441,7 +441,7 @@ grid_render(struct terminal *term) assert(term->height > 0); struct buffer *buf = shm_get_buffer(term->wl->shm, term->width, term->height, 1 + term->render.workers.count); - wl_surface_attach(term->window.surface, buf->wl_buf, 0, 0); + wl_surface_attach(term->window->surface, buf->wl_buf, 0, 0); pixman_image_t *pix = buf->pix; bool all_clean = tll_length(term->grid->scroll_damage) == 0; @@ -489,13 +489,13 @@ grid_render(struct terminal *term) {0, bmargin, term->width, bmargin_height}}); /* Bottom */ wl_surface_damage_buffer( - term->window.surface, 0, 0, term->width, term->y_margin); + term->window->surface, 0, 0, term->width, term->y_margin); wl_surface_damage_buffer( - term->window.surface, 0, 0, term->x_margin, term->height); + term->window->surface, 0, 0, term->x_margin, term->height); wl_surface_damage_buffer( - term->window.surface, rmargin, 0, rmargin_width, term->height); + term->window->surface, rmargin, 0, rmargin_width, term->height); wl_surface_damage_buffer( - term->window.surface, 0, bmargin, term->width, bmargin_height); + term->window->surface, 0, bmargin, term->width, bmargin_height); /* Force a full grid refresh */ term_damage_view(term); @@ -516,7 +516,7 @@ grid_render(struct terminal *term) render_cell(term, pix, cell, at.col, at.row, false); wl_surface_damage_buffer( - term->window.surface, + term->window->surface, term->x_margin + at.col * term->cell_width, term->y_margin + at.row * term->cell_height, term->cell_width, term->cell_height); @@ -569,7 +569,7 @@ grid_render(struct terminal *term) all_clean = false; wl_surface_damage_buffer( - term->window.surface, + term->window->surface, term->x_margin, term->y_margin + r * term->cell_height, term->width - term->x_margin, term->cell_height); } @@ -592,7 +592,7 @@ grid_render(struct terminal *term) all_clean = false; wl_surface_damage_buffer( - term->window.surface, + term->window->surface, term->x_margin, term->y_margin + r * term->cell_height, term->width - term->x_margin, term->cell_height); } @@ -674,7 +674,7 @@ grid_render(struct terminal *term) term, pix, cell, term->cursor.col, view_aligned_row, true); wl_surface_damage_buffer( - term->window.surface, + term->window->surface, term->x_margin + term->cursor.col * term->cell_width, term->y_margin + view_aligned_row * term->cell_height, cols_updated * term->cell_width, term->cell_height); @@ -694,18 +694,18 @@ grid_render(struct terminal *term) 1, &(pixman_rectangle16_t){0, 0, term->width, term->height}); wl_surface_damage_buffer( - term->window.surface, 0, 0, term->width, term->height); + term->window->surface, 0, 0, term->width, term->height); } assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows); assert(term->grid->view >= 0 && term->grid->view < term->grid->num_rows); - assert(term->window.frame_callback == NULL); - term->window.frame_callback = wl_surface_frame(term->window.surface); - wl_callback_add_listener(term->window.frame_callback, &frame_listener, term); + assert(term->window->frame_callback == NULL); + term->window->frame_callback = wl_surface_frame(term->window->surface); + wl_callback_add_listener(term->window->frame_callback, &frame_listener, term); - wl_surface_set_buffer_scale(term->window.surface, term->scale); - wl_surface_commit(term->window.surface); + wl_surface_set_buffer_scale(term->window->surface, term->scale); + wl_surface_commit(term->window->surface); #if TIME_FRAME_RENDERING struct timeval end_time; @@ -723,16 +723,16 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da { struct terminal *term = data; - assert(term->window.frame_callback == wl_callback); + assert(term->window->frame_callback == wl_callback); wl_callback_destroy(wl_callback); - term->window.frame_callback = NULL; + term->window->frame_callback = NULL; grid_render(term); } void render_search_box(struct terminal *term) { - assert(term->window.search_sub_surface != NULL); + assert(term->window->search_sub_surface != NULL); /* TODO: at least sway allows the subsurface to extend outside the * main window. Do we want that? */ @@ -780,13 +780,13 @@ render_search_box(struct terminal *term) draw_bar(term, buf->pix, font, &fg, x, y); wl_subsurface_set_position( - term->window.search_sub_surface, + term->window->search_sub_surface, term->width - width - margin, term->height - height - margin); - wl_surface_damage_buffer(term->window.search_surface, 0, 0, width, height); - wl_surface_attach(term->window.search_surface, buf->wl_buf, 0, 0); - wl_surface_set_buffer_scale(term->window.search_surface, scale); - wl_surface_commit(term->window.search_surface); + wl_surface_damage_buffer(term->window->search_surface, 0, 0, width, height); + wl_surface_attach(term->window->search_surface, buf->wl_buf, 0, 0); + wl_surface_set_buffer_scale(term->window->search_surface, scale); + wl_surface_commit(term->window->search_surface); } static void @@ -818,7 +818,7 @@ void render_resize(struct terminal *term, int width, int height) { int scale = -1; - tll_foreach(term->window.on_outputs, it) { + tll_foreach(term->window->on_outputs, it) { if (it->item->scale > scale) scale = it->item->scale; } @@ -939,7 +939,7 @@ render_resize(struct terminal *term, int width, int height) void render_set_title(struct terminal *term, const char *title) { - xdg_toplevel_set_title(term->window.xdg_toplevel, title); + xdg_toplevel_set_title(term->window->xdg_toplevel, title); } bool @@ -1000,6 +1000,6 @@ render_update_cursor_surface(struct terminal *term) void render_refresh(struct terminal *term) { - if (term->window.frame_callback == NULL) + if (term->window->frame_callback == NULL) grid_render(term); } diff --git a/search.c b/search.c index e055b478..134c3527 100644 --- a/search.c +++ b/search.c @@ -19,8 +19,8 @@ static void search_cancel_keep_selection(struct terminal *term) { - wl_surface_attach(term->window.search_surface, NULL, 0, 0); - wl_surface_commit(term->window.search_surface); + wl_surface_attach(term->window->search_surface, NULL, 0, 0); + wl_surface_commit(term->window->search_surface); free(term->search.buf); term->search.buf = NULL; diff --git a/terminal.h b/terminal.h index 724bc6f1..2f3fcdc5 100644 --- a/terminal.h +++ b/terminal.h @@ -247,7 +247,7 @@ struct terminal { } fextents; struct wayland *wl; - struct wl_window window; + struct wl_window *window; struct { int scrollback_lines; diff --git a/wayland.c b/wayland.c index c3d7657f..227f9fc4 100644 --- a/wayland.c +++ b/wayland.c @@ -1,5 +1,8 @@ #include "wayland.h" +#include +#include + #include #include #include @@ -14,10 +17,471 @@ #include "tllist.h" #include "terminal.h" +#include "input.h" +#include "render.h" +#include "selection.h" -void -wayl_init(struct wayland *wayl) +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) + +static void +shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) { + struct wayland *wayl = data; + if (format == WL_SHM_FORMAT_ARGB8888) + wayl->have_argb8888 = true; +} + +static const struct wl_shm_listener shm_listener = { + .format = &shm_format, +}; + +static void +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) +{ + LOG_DBG("wm base ping"); + xdg_wm_base_pong(shell, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = &xdg_wm_base_ping, +}; + +static void +seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) +{ + struct wayland *wayl = data; + + if (wayl->keyboard != NULL) { + wl_keyboard_release(wayl->keyboard); + wayl->keyboard = NULL; + } + + if (wayl->pointer.pointer != NULL) { + wl_pointer_release(wayl->pointer.pointer); + wayl->pointer.pointer = NULL; + } + + if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { + wayl->keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl); + } + + if (caps & WL_SEAT_CAPABILITY_POINTER) { + wayl->pointer.pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl); + } +} + +static void +seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) +{ +} + +static const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = seat_handle_name, +}; + +static void +output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, + int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) +{ + struct monitor *mon = data; + mon->width_mm = physical_width; + mon->height_mm = physical_height; +} + +static void +output_mode(void *data, struct wl_output *wl_output, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) +{ + if ((flags & WL_OUTPUT_MODE_CURRENT) == 0) + return; + + struct monitor *mon = data; + mon->refresh = (float)refresh / 1000; +} + +static void +output_done(void *data, struct wl_output *wl_output) +{ +} + +static void +output_scale(void *data, struct wl_output *wl_output, int32_t factor) +{ + struct monitor *mon = data; + + mon->scale = factor; + + struct terminal *term = mon->wayl->term; + if (term != NULL) { + render_resize(term, term->width / term->scale, term->height / term->scale); + render_reload_cursor_theme(term); + } +} + +static const struct wl_output_listener output_listener = { + .geometry = &output_geometry, + .mode = &output_mode, + .done = &output_done, + .scale = &output_scale, +}; + +static void +xdg_output_handle_logical_position( + void *data, struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) +{ + struct monitor *mon = data; + mon->x = x; + mon->y = y; +} + +static void +xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, + int32_t width, int32_t height) +{ + struct monitor *mon = data; + mon->width_px = width; + mon->height_px = height; +} + +static void +xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) +{ +} + +static void +xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, + const char *name) +{ + struct monitor *mon = data; + mon->name = strdup(name); +} + +static void +xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, + const char *description) +{ +} + +static struct zxdg_output_v1_listener xdg_output_listener = { + .logical_position = xdg_output_handle_logical_position, + .logical_size = xdg_output_handle_logical_size, + .done = xdg_output_handle_done, + .name = xdg_output_handle_name, + .description = xdg_output_handle_description, +}; + +static void +handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + LOG_DBG("global: %s, version=%u", interface, version); + struct wayland *wayl = data; + + if (strcmp(interface, wl_compositor_interface.name) == 0) { + wayl->compositor = wl_registry_bind( + wayl->registry, name, &wl_compositor_interface, 4); + } + + else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { + wayl->sub_compositor = wl_registry_bind( + wayl->registry, name, &wl_subcompositor_interface, 1); + } + + else if (strcmp(interface, wl_shm_interface.name) == 0) { + wayl->shm = wl_registry_bind( + wayl->registry, name, &wl_shm_interface, 1); + wl_shm_add_listener(wayl->shm, &shm_listener, wayl); + wl_display_roundtrip(wayl->display); + } + + else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + wayl->shell = wl_registry_bind( + wayl->registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(wayl->shell, &xdg_wm_base_listener, wayl); + } + + else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) + wayl->xdg_decoration_manager = wl_registry_bind( + wayl->registry, name, &zxdg_decoration_manager_v1_interface, 1); + + else if (strcmp(interface, wl_seat_interface.name) == 0) { + wayl->seat = wl_registry_bind( + wayl->registry, name, &wl_seat_interface, 5); + wl_seat_add_listener(wayl->seat, &seat_listener, wayl); + wl_display_roundtrip(wayl->display); + } + + else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { + wayl->xdg_output_manager = wl_registry_bind( + wayl->registry, name, &zxdg_output_manager_v1_interface, min(version, 2)); + } + + else if (strcmp(interface, wl_output_interface.name) == 0) { + struct wl_output *output = wl_registry_bind( + wayl->registry, name, &wl_output_interface, 3); + + tll_push_back( + wayl->monitors, ((struct monitor){.wayl = wayl, .output = output})); + + struct monitor *mon = &tll_back(wayl->monitors); + wl_output_add_listener(output, &output_listener, mon); + + mon->xdg = zxdg_output_manager_v1_get_xdg_output( + wayl->xdg_output_manager, mon->output); + zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon); + wl_display_roundtrip(wayl->display); + } + + else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { + wayl->data_device_manager = wl_registry_bind( + wayl->registry, name, &wl_data_device_manager_interface, 1); + } + + else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) { + wayl->primary_selection_device_manager = wl_registry_bind( + wayl->registry, name, &zwp_primary_selection_device_manager_v1_interface, 1); + } +} + +static void +surface_enter(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) +{ + struct wayland *wayl = data; + struct terminal *term = wayl_terminal_from_surface(wayl, wl_surface); + + tll_foreach(wayl->monitors, it) { + if (it->item.output == wl_output) { + LOG_DBG("mapped on %s", it->item.name); + tll_push_back(term->window->on_outputs, &it->item); + + /* Resize, since scale-to-use may have changed */ + render_resize(term, term->width / term->scale, term->height / term->scale); + render_reload_cursor_theme(term); + return; + } + } + + LOG_ERR("mapped on unknown output"); +} + +static void +surface_leave(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) +{ + struct wayland *wayl = data; + struct terminal *term = wayl_terminal_from_surface(wayl, wl_surface); + + tll_foreach(term->window->on_outputs, it) { + if (it->item->output != wl_output) + continue; + + LOG_DBG("unmapped from %s", it->item->name); + tll_remove(term->window->on_outputs, it); + + /* Resize, since scale-to-use may have changed */ + render_resize(term, term->width / term->scale, term->height / term->scale); + render_reload_cursor_theme(term); + return; + } + + LOG_ERR("unmapped from unknown output"); +} + +static const struct wl_surface_listener surface_listener = { + .enter = &surface_enter, + .leave = &surface_leave, +}; + +static void +xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, struct wl_array *states) +{ + LOG_DBG("xdg-toplevel: configure: %dx%d", width, height); + + if (width <= 0 || height <= 0) + return; + + struct wayland *wayl = data; + struct terminal *term = wayl_terminal_from_xdg_toplevel(wayl, xdg_toplevel); + render_resize(term, width, height); +} + +static void +xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) +{ + struct wayland *wayl = data; + struct terminal *term = wayl_terminal_from_xdg_toplevel(wayl, xdg_toplevel); + LOG_DBG("xdg-toplevel: close"); + + term->quit = true; + wl_display_roundtrip(wayl->display); +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = &xdg_toplevel_configure, + .close = &xdg_toplevel_close, +}; + +static void +xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, + uint32_t serial) +{ + //LOG_DBG("xdg-surface: configure"); + xdg_surface_ack_configure(xdg_surface, serial); +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = &xdg_surface_configure, +}; + +static void +xdg_toplevel_decoration_configure(void *data, + struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + uint32_t mode) +{ + switch (mode) { + case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: + LOG_ERR("unimplemented: client-side decorations"); + break; + + case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: + LOG_DBG("using server-side decorations"); + break; + + default: + LOG_ERR("unimplemented: unknown XDG toplevel decoration mode: %u", mode); + break; + } +} + +static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = { + .configure = &xdg_toplevel_decoration_configure, +}; + +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + LOG_WARN("global removed: %u", name); + assert(false); +} + +static const struct wl_registry_listener registry_listener = { + .global = &handle_global, + .global_remove = &handle_global_remove, +}; + +struct wayland * +wayl_init(struct fdm *fdm) +{ + struct wayland *wayl = calloc(1, sizeof(*wayl)); + wayl->fdm = fdm; + + wayl->display = wl_display_connect(NULL); + if (wayl->display == NULL) { + LOG_ERR("failed to connect to wayland; no compositor running?"); + goto out; + } + + wayl->registry = wl_display_get_registry(wayl->display); + if (wayl->registry == NULL) { + LOG_ERR("failed to get wayland registry"); + goto out; + } + + wl_registry_add_listener(wayl->registry, ®istry_listener, wayl); + wl_display_roundtrip(wayl->display); + + if (wayl->compositor == NULL) { + LOG_ERR("no compositor"); + goto out; + } + if (wayl->shm == NULL) { + LOG_ERR("no shared memory buffers interface"); + goto out; + } + if (wayl->shell == NULL) { + LOG_ERR("no XDG shell interface"); + goto out; + } + if (!wayl->have_argb8888) { + LOG_ERR("compositor does not support ARGB surfaces"); + goto out; + } + if (wayl->seat == NULL) { + LOG_ERR("no seat available"); + goto out; + } + if (wayl->data_device_manager == NULL) { + LOG_ERR("no clipboard available " + "(wl_data_device_manager not implemented by server)"); + goto out; + } + if (wayl->primary_selection_device_manager == NULL) + LOG_WARN("no primary selection available"); + + tll_foreach(wayl->monitors, it) { + LOG_INFO("%s: %dx%d+%dx%d (scale=%d, refresh=%.2fHz)", + it->item.name, it->item.width_px, it->item.height_px, + it->item.x, it->item.y, it->item.scale, it->item.refresh); + } + + /* Clipboard */ + wayl->data_device = wl_data_device_manager_get_data_device( + wayl->data_device_manager, wayl->seat); + wl_data_device_add_listener(wayl->data_device, &data_device_listener, wayl); + + /* Primary selection */ + if (wayl->primary_selection_device_manager != NULL) { + wayl->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device( + wayl->primary_selection_device_manager, wayl->seat); + zwp_primary_selection_device_v1_add_listener( + wayl->primary_selection_device, &primary_selection_device_listener, wayl); + } + + /* Cursor */ + unsigned cursor_size = 24; + const char *cursor_theme = getenv("XCURSOR_THEME"); + + { + const char *env_cursor_size = getenv("XCURSOR_SIZE"); + if (env_cursor_size != NULL) { + unsigned size; + if (sscanf(env_cursor_size, "%u", &size) == 1) + cursor_size = size; + } + } + + /* Note: theme is (re)loaded on scale and output changes */ + LOG_INFO("cursor theme: %s, size: %u", cursor_theme, cursor_size); + wayl->pointer.size = cursor_size; + wayl->pointer.theme_name = cursor_theme != NULL ? strdup(cursor_theme) : NULL; + + wayl->pointer.surface = wl_compositor_create_surface(wayl->compositor); + if (wayl->pointer.surface == NULL) { + LOG_ERR("failed to create cursor surface"); + goto out; + } + + wayl->kbd.repeat.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK); + if (wayl->kbd.repeat.fd == -1) { + LOG_ERRNO("failed to create keyboard repeat timer FD"); + goto out; + } + + return wayl; + +out: + if (wayl != NULL) + wayl_destroy(wayl); + return NULL; } void @@ -93,6 +557,52 @@ wayl_destroy(struct wayland *wayl) wl_display_disconnect(wayl->display); } +struct wl_window * +wayl_win_init(struct wayland *wayl) +{ + struct wl_window *win = calloc(1, sizeof(*win)); + + win->surface = wl_compositor_create_surface(wayl->compositor); + if (win->surface == NULL) { + LOG_ERR("failed to create wayland surface"); + goto out; + } + + wl_surface_add_listener(win->surface, &surface_listener, wayl); + + win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface); + xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, wayl); + + win->xdg_toplevel = xdg_surface_get_toplevel(win->xdg_surface); + xdg_toplevel_add_listener(win->xdg_toplevel, &xdg_toplevel_listener, wayl); + + xdg_toplevel_set_app_id(win->xdg_toplevel, "foot"); + + /* Request server-side decorations */ + win->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + wayl->xdg_decoration_manager, win->xdg_toplevel); + zxdg_toplevel_decoration_v1_set_mode( + win->xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + zxdg_toplevel_decoration_v1_add_listener( + win->xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, wayl); + + /* Scrollback search box */ + win->search_surface = wl_compositor_create_surface(wayl->compositor); + win->search_sub_surface = wl_subcompositor_get_subsurface( + wayl->sub_compositor, win->search_surface, win->surface); + wl_subsurface_set_desync(win->search_sub_surface); + + wl_surface_commit(win->surface); + wl_display_roundtrip(wayl->display); + + return win; + +out: + if (win != NULL) + wayl_win_destroy(win); + return NULL; +} + void wayl_win_destroy(struct wl_window *win) { @@ -116,7 +626,7 @@ wayl_win_destroy(struct wl_window *win) struct terminal * wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface) { - assert(surface == wayl->term->window.surface); + assert(surface == wayl->term->window->surface); return wayl->term; } @@ -124,6 +634,6 @@ struct terminal * wayl_terminal_from_xdg_toplevel(struct wayland *wayl, struct xdg_toplevel *toplevel) { - assert(toplevel == wayl->term->window.xdg_toplevel); + assert(toplevel == wayl->term->window->xdg_toplevel); return wayl->term; } diff --git a/wayland.h b/wayland.h index e0f7cd2f..29c30518 100644 --- a/wayland.h +++ b/wayland.h @@ -9,6 +9,7 @@ #include #include +#include "fdm.h" #include "tllist.h" struct monitor { @@ -89,6 +90,7 @@ struct wl_window { struct terminal; struct wayland { + struct fdm *fdm; struct wl_display *display; struct wl_registry *registry; struct wl_compositor *compositor; @@ -149,8 +151,7 @@ struct wayland { struct terminal *moused; }; -/* TODO: return allocated pointer */ -void wayl_init(struct wayland *wayl); +struct wayland *wayl_init(struct fdm *fdm); void wayl_destroy(struct wayland *wayl); struct terminal *wayl_terminal_from_surface( @@ -160,4 +161,5 @@ struct terminal *wayl_terminal_from_xdg_surface( struct terminal *wayl_terminal_from_xdg_toplevel( struct wayland *wayl, struct xdg_toplevel *toplevel); +struct wl_window *wayl_win_init(struct wayland *wayl); void wayl_win_destroy(struct wl_window *win);