From 5fefb950b3a1de2c8c8e2cbfecf4ed3790ecffef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 11:46:18 +0100 Subject: [PATCH 01/30] fdm: use the FDM's poll loop --- fdm.c | 124 ++++++++++++++ fdm.h | 15 ++ main.c | 464 +++++++++++++++++++++++++++++----------------------- meson.build | 1 + terminal.h | 7 + 5 files changed, 409 insertions(+), 202 deletions(-) create mode 100644 fdm.c create mode 100644 fdm.h diff --git a/fdm.c b/fdm.c new file mode 100644 index 00000000..9523ab5f --- /dev/null +++ b/fdm.c @@ -0,0 +1,124 @@ +#include "fdm.h" + +#include +#include +#include +#include +#include + +#define LOG_MODULE "fdm" +#define LOG_ENABLE_DBG 0 +#include "log.h" +#include "tllist.h" + +struct fd { + int fd; + void *data; + fdm_handler_t handler; +}; + +struct fdm { + int epoll_fd; + tll(struct fd) fds; +}; + +struct fdm * +fdm_init(void) +{ + int epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (epoll_fd == -1) { + LOG_ERRNO("failed to create epoll FD"); + return NULL; + } + + struct fdm *fdm = malloc(sizeof(*fdm)); + *fdm = (struct fdm){ + .epoll_fd = epoll_fd, + .fds = tll_init(), + }; + return fdm; +} + +void +fdm_destroy(struct fdm *fdm) +{ + if (fdm == NULL) + return; + + if (tll_length(fdm->fds) > 0) + LOG_WARN("FD list not empty"); + + assert(tll_length(fdm->fds) == 0); + + tll_free(fdm->fds); + close(fdm->epoll_fd); + free(fdm); +} + +bool +fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data) +{ +#if defined(_DEBUG) + tll_foreach(fdm->fds, it) { + if (it->item.fd == fd) { + LOG_ERR("FD=%d already registered", fd); + return false; + } + } +#endif + + tll_push_back( + fdm->fds, ((struct fd){.fd = fd, .data = data, .handler = handler})); + + struct epoll_event ev = { + .events = events, + .data = {.ptr = &tll_back(fdm->fds)}, + }; + + if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + LOG_ERRNO("failed to register FD with epoll"); + tll_pop_back(fdm->fds); + return false; + } + + return true; +} + +bool +fdm_del(struct fdm *fdm, int fd) +{ + tll_foreach(fdm->fds, it) { + if (it->item.fd == fd) { + if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0) + LOG_ERRNO("failed to unregister FD=%d from epoll", fd); + + tll_remove(fdm->fds, it); + return true; + } + } + + LOG_ERR("no such FD: %d", fd); + return false; +} + +bool +fdm_poll(struct fdm *fdm) +{ + struct epoll_event events[tll_length(fdm->fds)]; + int ret = epoll_wait(fdm->epoll_fd, events, tll_length(fdm->fds), -1); + if (ret == -1) { + if (errno == EINTR) + return true; + + LOG_ERRNO("failed to epoll"); + return false; + } + + for (int i = 0; i < ret; i++) { + struct fd *fd = events[i].data.ptr; + if (!fd->handler(fdm, fd->fd, events[i].events, fd->data)) + return false; + } + + return true; +} diff --git a/fdm.h b/fdm.h new file mode 100644 index 00000000..cd0fee2a --- /dev/null +++ b/fdm.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct fdm; + +typedef bool (*fdm_handler_t)(struct fdm *fdm, int fd, int events, void *data); + +struct fdm *fdm_init(void); +void fdm_destroy(struct fdm *fdm); + +bool fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data); +bool fdm_del(struct fdm *fdm, int fd); + +bool fdm_poll(struct fdm *fdm); diff --git a/main.c b/main.c index dfb1982d..92104e6e 100644 --- a/main.c +++ b/main.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -26,10 +27,11 @@ #include #define LOG_MODULE "main" -#define LOG_ENABLE_DBG 0 +#define LOG_ENABLE_DBG 1 #include "log.h" #include "config.h" +#include "fdm.h" #include "font.h" #include "grid.h" #include "input.h" @@ -389,6 +391,228 @@ static const struct wl_registry_listener registry_listener = { .global_remove = &handle_global_remove, }; +static bool +fdm_wayl(struct fdm *fdm, int fd, int events, void *data) +{ + struct terminal *term = data; + int event_count = wl_display_dispatch(term->wl.display); + + if (events & EPOLLHUP) { + LOG_ERR("disconnected from Wayland"); + return false; + } + + return event_count != -1 && !term->quit; +} + +static bool +fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) +{ + struct terminal *term = data; + + if (events & EPOLLHUP) { + term->quit = true; + + if (!(events & EPOLLIN)) + return false; + } + + assert(events & EPOLLIN); + + uint8_t buf[24 * 1024]; + ssize_t count = read(term->ptmx, buf, sizeof(buf)); + + if (count < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read from pseudo terminal"); + return false; + } + + vt_from_slave(term, buf, count); + + /* + * We likely need to re-render. But, we don't want to + * do it immediately. Often, a single client operation + * is done through multiple writes. Many times, we're + * so fast that we render mid-operation frames. + * + * For example, we might end up rendering a frame + * where the client just erased a line, while in the + * next frame, the client wrote to the same line. This + * causes screen "flashes". + * + * Mitigate by always incuring a small delay before + * rendering the next frame. This gives the client + * some time to finish the operation (and thus gives + * us time to receive the last writes before doing any + * actual rendering). + * + * We incur this delay *every* time we receive + * input. To ensure we don't delay rendering + * indefinitely, we start a second timer that is only + * reset when we render. + * + * Note that when the client is producing data at a + * very high pace, we're rate limited by the wayland + * compositor anyway. The delay we introduce here only + * has any effect when the renderer is idle. + * + * TODO: this adds input latency. Can we somehow hint + * ourselves we just received keyboard input, and in + * this case *not* delay rendering? + */ + if (term->render.frame_callback == NULL) { + /* First timeout - reset each time we receive input. */ + timerfd_settime( + term->delayed_render_timer.lower_fd, 0, + &(struct itimerspec){.it_value = {.tv_nsec = 1000000}}, + NULL); + + /* Second timeout - only reset when we render. Set to one + * frame (assuming 60Hz) */ + if (!term->delayed_render_timer.is_armed) { + timerfd_settime( + term->delayed_render_timer.upper_fd, 0, + &(struct itimerspec){.it_value = {.tv_nsec = 16666666}}, + NULL); + term->delayed_render_timer.is_armed = true; + } + } + + return !(events & EPOLLHUP); +} + +static bool +fdm_repeat(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct terminal *term = data; + uint64_t expiration_count; + ssize_t ret = read( + term->kbd.repeat.fd, &expiration_count, sizeof(expiration_count)); + + if (ret < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read repeat key from repeat timer fd"); + return false; + } + + term->kbd.repeat.dont_re_repeat = true; + for (size_t i = 0; i < expiration_count; i++) + input_repeat(term, term->kbd.repeat.key); + term->kbd.repeat.dont_re_repeat = false; + return true; +} + +static bool +fdm_flash(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct terminal *term = data; + uint64_t expiration_count; + ssize_t ret = read( + term->flash.fd, &expiration_count, sizeof(expiration_count)); + + if (ret < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read flash timer"); + return false; + } + + LOG_DBG("flash timer expired %llu times", + (unsigned long long)expiration_count); + + term->flash.active = false; + term_damage_view(term); + render_refresh(term); + return true; +} + +static bool +fdm_blink(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct terminal *term = data; + uint64_t expiration_count; + ssize_t ret = read( + term->blink.fd, &expiration_count, sizeof(expiration_count)); + + if (ret < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read blink timer"); + return false; + } + + LOG_DBG("blink timer expired %llu times", + (unsigned long long)expiration_count); + + term->blink.state = term->blink.state == BLINK_ON + ? BLINK_OFF : BLINK_ON; + + /* Scan all visible cells and mark rows with blinking cells dirty */ + for (int r = 0; r < term->rows; r++) { + struct row *row = grid_row_in_view(term->grid, r); + for (int col = 0; col < term->cols; col++) { + struct cell *cell = &row->cells[col]; + + if (cell->attrs.blink) { + cell->attrs.clean = 0; + row->dirty = true; + } + } + } + + render_refresh(term); + return true; +} + +static bool +fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct terminal *term = data; + assert(term->delayed_render_timer.is_armed); + + uint64_t unused; + ssize_t ret1 = 0; + ssize_t ret2 = 0; + + if (fd == term->delayed_render_timer.lower_fd) + ret1 = read(term->delayed_render_timer.lower_fd, &unused, sizeof(unused)); + if (fd == term->delayed_render_timer.upper_fd) + ret2 = read(term->delayed_render_timer.upper_fd, &unused, sizeof(unused)); + + if ((ret1 < 0 || ret2 < 0) && errno != EAGAIN) + LOG_ERRNO("failed to read timeout timer"); + else if (ret1 > 0 || ret2 > 0) { + render_refresh(term); + + /* Reset timers */ + term->delayed_render_timer.is_armed = false; + timerfd_settime(term->delayed_render_timer.lower_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL); + timerfd_settime(term->delayed_render_timer.upper_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL); + } else + assert(false); + + return true; +} + static void print_usage(const char *prog_name) { @@ -496,6 +720,8 @@ main(int argc, char *const *argv) setlocale(LC_ALL, ""); setenv("TERM", conf.term, 1); + struct fdm *fdm = NULL; + struct terminal term = { .quit = false, .ptmx = posix_openpt(O_RDWR | O_NOCTTY), @@ -570,6 +796,11 @@ main(int argc, char *const *argv) .queue = tll_init(), }, }, + .delayed_render_timer = { + .is_armed = false, + .lower_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), + .upper_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), + }, }; LOG_INFO("using %zu rendering threads", term.render.workers.count); @@ -873,215 +1104,42 @@ main(int argc, char *const *argv) } } - bool timeout_is_armed = false; - int delay_render_timer_lower = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); - int delay_render_timer_upper = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); + if ((fdm = fdm_init()) == NULL) + goto out; + + fdm_add(fdm, wl_display_get_fd(term.wl.display), EPOLLIN, &fdm_wayl, &term); + fdm_add(fdm, term.ptmx, EPOLLIN, &fdm_ptmx, &term); + fdm_add(fdm, term.kbd.repeat.fd, EPOLLIN, &fdm_repeat, &term); + fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); + fdm_add(fdm, term.blink.fd, EPOLLIN, &fdm_blink, &term); + fdm_add(fdm, term.delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, &term); + fdm_add(fdm, term.delayed_render_timer.upper_fd, EPOLLIN, &fdm_delayed_render, &term); while (true) { - struct pollfd fds[] = { - {.fd = wl_display_get_fd(term.wl.display), .events = POLLIN}, - {.fd = term.ptmx, .events = POLLIN}, - {.fd = term.kbd.repeat.fd, .events = POLLIN}, - {.fd = term.flash.fd, .events = POLLIN}, - {.fd = term.blink.fd, .events = POLLIN}, - {.fd = delay_render_timer_lower, .events = POLLIN}, - {.fd = delay_render_timer_upper, .events = POLLIN}, - }; - - const size_t WL_FD = 0; - const size_t PTMX_FD = 1; - const size_t KBD_REPEAT_FD = 2; - const size_t FLASH_FD = 3; - const size_t BLINK_FD = 4; - const size_t DELAY_LOWER_FD = 5; - const size_t DELAY_UPPER_FD = 6; - wl_display_flush(term.wl.display); - int pret = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); - - if (pret == -1) { - if (errno == EINTR) - continue; - - LOG_ERRNO("failed to poll file descriptors"); + if (!fdm_poll(fdm)) break; - } - - /* Delayed rendering timers (created when we receive input) */ - if (fds[DELAY_LOWER_FD].revents & POLLIN || - fds[DELAY_UPPER_FD].revents & POLLIN) - { - assert(timeout_is_armed); - - uint64_t unused; - ssize_t ret1 = 0; - ssize_t ret2 = 0; - - if (fds[DELAY_LOWER_FD].revents & POLLIN) - ret1 = read(delay_render_timer_lower, &unused, sizeof(unused)); - if (fds[DELAY_UPPER_FD].revents & POLLIN) - ret2 = read(delay_render_timer_upper, &unused, sizeof(unused)); - - if ((ret1 < 0 || ret2 < 0) && errno != EAGAIN) - LOG_ERRNO("failed to read timeout timer"); - else if (ret1 > 0 || ret2 > 0) { - render_refresh(&term); - - /* Reset timers */ - timeout_is_armed = false; - timerfd_settime(delay_render_timer_lower, 0, &(struct itimerspec){.it_value = {0}}, NULL); - timerfd_settime(delay_render_timer_upper, 0, &(struct itimerspec){.it_value = {0}}, NULL); - } else - assert(false); - } - - if (fds[WL_FD].revents & POLLIN) { - wl_display_dispatch(term.wl.display); - if (term.quit) { - ret = EXIT_SUCCESS; - break; - } - } - - if (fds[WL_FD].revents & POLLHUP) { - LOG_WARN("disconnected from wayland"); - break; - } - - if (fds[PTMX_FD].revents & POLLIN) { - uint8_t data[24 * 1024]; - ssize_t count = read(term.ptmx, data, sizeof(data)); - if (count < 0 && errno != EAGAIN) { - LOG_ERRNO("failed to read from pseudo terminal"); - break; - } - - if (count > 0) { - vt_from_slave(&term, data, count); - - /* - * We likely need to re-render. But, we don't want to - * do it immediately. Often, a single client operation - * is done through multiple writes. Many times, we're - * so fast that we render mid-operation frames. - * - * For example, we might end up rendering a frame - * where the client just erased a line, while in the - * next frame, the client wrote to the same line. This - * causes screen "flashes". - * - * Mitigate by always incuring a small delay before - * rendering the next frame. This gives the client - * some time to finish the operation (and thus gives - * us time to receive the last writes before doing any - * actual rendering). - * - * We incur this delay *every* time we receive - * input. To ensure we don't delay rendering - * indefinitely, we start a second timer that is only - * reset when we render. - * - * Note that when the client is producing data at a - * very high pace, we're rate limited by the wayland - * compositor anyway. The delay we introduce here only - * has any effect when the renderer is idle. - * - * TODO: this adds input latency. Can we somehow hint - * ourselves we just received keyboard input, and in - * this case *not* delay rendering? - */ - if (term.render.frame_callback == NULL) { - /* First timeout - reset each time we receive input. */ - timerfd_settime( - delay_render_timer_lower, 0, - &(struct itimerspec){.it_value = {.tv_nsec = 1000000}}, - NULL); - - /* Second timeout - only reset when we render. Set to one frame (assuming 60Hz) */ - if (!timeout_is_armed) { - timerfd_settime( - delay_render_timer_upper, 0, - &(struct itimerspec){.it_value = {.tv_nsec = 16666666}}, - NULL); - timeout_is_armed = true; - } - } - } - } - - if (fds[PTMX_FD].revents & POLLHUP) { - ret = EXIT_SUCCESS; - break; - } - - if (fds[KBD_REPEAT_FD].revents & POLLIN) { - uint64_t expiration_count; - ssize_t ret = read( - term.kbd.repeat.fd, &expiration_count, sizeof(expiration_count)); - - if (ret < 0 && errno != EAGAIN) - LOG_ERRNO("failed to read repeat key from repeat timer fd"); - else if (ret > 0) { - term.kbd.repeat.dont_re_repeat = true; - for (size_t i = 0; i < expiration_count; i++) - input_repeat(&term, term.kbd.repeat.key); - term.kbd.repeat.dont_re_repeat = false; - } - } - - if (fds[FLASH_FD].revents & POLLIN) { - uint64_t expiration_count; - ssize_t ret = read( - term.flash.fd, &expiration_count, sizeof(expiration_count)); - - if (ret < 0 && errno != EAGAIN) - LOG_ERRNO("failed to read flash timer"); - else if (ret > 0) { - LOG_DBG("flash timer expired %llu times", - (unsigned long long)expiration_count); - - term.flash.active = false; - term_damage_view(&term); - render_refresh(&term); - } - } - - if (fds[BLINK_FD].revents & POLLIN) { - uint64_t expiration_count; - ssize_t ret = read( - term.blink.fd, &expiration_count, sizeof(expiration_count)); - - if (ret < 0 && errno != EAGAIN) - LOG_ERRNO("failed to read blink timer"); - else if (ret > 0) { - LOG_DBG("blink timer expired %llu times", - (unsigned long long)expiration_count); - - term.blink.state = term.blink.state == BLINK_ON - ? BLINK_OFF : BLINK_ON; - - /* Scan all visible cells and mark rows with blinking cells dirty */ - for (int r = 0; r < term.rows; r++) { - struct row *row = grid_row_in_view(term.grid, r); - for (int col = 0; col < term.cols; col++) { - struct cell *cell = &row->cells[col]; - - if (cell->attrs.blink) { - cell->attrs.clean = 0; - row->dirty = true; - } - } - } - - render_refresh(&term); - } - } } - close(delay_render_timer_lower); - close(delay_render_timer_upper); + if (term.quit) + ret = EXIT_SUCCESS; out: + if (fdm != NULL) { + fdm_del(fdm, wl_display_get_fd(term.wl.display)); + fdm_del(fdm, term.ptmx); + fdm_del(fdm, term.kbd.repeat.fd); + fdm_del(fdm, term.flash.fd); + fdm_del(fdm, term.blink.fd); + fdm_del(fdm, term.delayed_render_timer.lower_fd); + fdm_del(fdm, term.delayed_render_timer.upper_fd); + } + + if (term.delayed_render_timer.lower_fd != -1) + close(term.delayed_render_timer.lower_fd); + if (term.delayed_render_timer.upper_fd != -1) + close(term.delayed_render_timer.upper_fd); + mtx_lock(&term.render.workers.lock); assert(tll_length(term.render.workers.queue) == 0); for (size_t i = 0; i < term.render.workers.count; i++) { @@ -1230,6 +1288,8 @@ out: ret = child_ret; } + fdm_destroy(fdm); + config_free(conf); return ret; diff --git a/meson.build b/meson.build index 22987aa4..590f5f8f 100644 --- a/meson.build +++ b/meson.build @@ -68,6 +68,7 @@ executable( 'config.c', 'config.h', 'commands.c', 'commands.h', 'csi.c', 'csi.h', + 'fdm.c', 'fdm.h', 'font.c', 'font.h', 'grid.c', 'grid.h', 'input.c', 'input.h', diff --git a/terminal.h b/terminal.h index 6f6e1d53..a10e1928 100644 --- a/terminal.h +++ b/terminal.h @@ -401,6 +401,13 @@ struct terminal { bool was_flashing; /* Flash was active last time we rendered */ bool was_searching; } render; + + /* Temporary: for FDM */ + struct { + bool is_armed; + int lower_fd; + int upper_fd; + } delayed_render_timer; }; void term_reset(struct terminal *term, bool hard); From f63458ef3387eac67a402e6ce7bedd91eb49d9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 12:57:37 +0100 Subject: [PATCH 02/30] term: move per-window wayland objects from wayland struct to terminal struct Short term, we want to break out the wayland backend from the terminal struct. Long term, we might want to support multiple windows. One step towards both the above is separating global wayland objects from per-window objects. --- main.c | 82 +++++++++++++++++++++++++++--------------------------- render.c | 48 +++++++++++++++++--------------- search.c | 4 +-- terminal.h | 30 ++++++++++---------- 4 files changed, 84 insertions(+), 80 deletions(-) diff --git a/main.c b/main.c index 92104e6e..78a70fe4 100644 --- a/main.c +++ b/main.c @@ -221,13 +221,13 @@ handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - term->wl.shell = wl_registry_bind( + term->window.shell = wl_registry_bind( term->wl.registry, name, &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(term->wl.shell, &xdg_wm_base_listener, term); + xdg_wm_base_add_listener(term->window.shell, &xdg_wm_base_listener, term); } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) - term->wl.xdg_decoration_manager = wl_registry_bind( + term->window.xdg_decoration_manager = wl_registry_bind( term->wl.registry, name, &zxdg_decoration_manager_v1_interface, 1); else if (strcmp(interface, wl_seat_interface.name) == 0) { @@ -278,7 +278,7 @@ surface_enter(void *data, struct wl_surface *wl_surface, tll_foreach(term->wl.monitors, it) { if (it->item.output == wl_output) { LOG_DBG("mapped on %s", it->item.name); - tll_push_back(term->wl.on_outputs, &it->item); + 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); @@ -295,12 +295,12 @@ surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { struct terminal *term = data; - tll_foreach(term->wl.on_outputs, it) { + 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->wl.on_outputs, it); + 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); @@ -909,7 +909,7 @@ main(int argc, char *const *argv) LOG_ERR("no shared memory buffers interface"); goto out; } - if (term.wl.shell == NULL) { + if (term.window.shell == NULL) { LOG_ERR("no XDG shell interface"); goto out; } @@ -973,38 +973,38 @@ main(int argc, char *const *argv) } /* Main window */ - term.wl.surface = wl_compositor_create_surface(term.wl.compositor); - if (term.wl.surface == NULL) { + term.window.surface = wl_compositor_create_surface(term.wl.compositor); + if (term.window.surface == NULL) { LOG_ERR("failed to create wayland surface"); goto out; } - wl_surface_add_listener(term.wl.surface, &surface_listener, &term); + wl_surface_add_listener(term.window.surface, &surface_listener, &term); - term.wl.xdg_surface = xdg_wm_base_get_xdg_surface(term.wl.shell, term.wl.surface); - xdg_surface_add_listener(term.wl.xdg_surface, &xdg_surface_listener, &term); + term.window.xdg_surface = xdg_wm_base_get_xdg_surface(term.window.shell, term.window.surface); + xdg_surface_add_listener(term.window.xdg_surface, &xdg_surface_listener, &term); - term.wl.xdg_toplevel = xdg_surface_get_toplevel(term.wl.xdg_surface); - xdg_toplevel_add_listener(term.wl.xdg_toplevel, &xdg_toplevel_listener, &term); + term.window.xdg_toplevel = xdg_surface_get_toplevel(term.window.xdg_surface); + xdg_toplevel_add_listener(term.window.xdg_toplevel, &xdg_toplevel_listener, &term); - xdg_toplevel_set_app_id(term.wl.xdg_toplevel, "foot"); + xdg_toplevel_set_app_id(term.window.xdg_toplevel, "foot"); term_set_window_title(&term, "foot"); /* Request server-side decorations */ - term.wl.xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - term.wl.xdg_decoration_manager, term.wl.xdg_toplevel); + term.window.xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + term.window.xdg_decoration_manager, term.window.xdg_toplevel); zxdg_toplevel_decoration_v1_set_mode( - term.wl.xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + term.window.xdg_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); zxdg_toplevel_decoration_v1_add_listener( - term.wl.xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, &term); + term.window.xdg_toplevel_decoration, &xdg_toplevel_decoration_listener, &term); /* Scrollback search box */ - term.wl.search_surface = wl_compositor_create_surface(term.wl.compositor); - term.wl.search_sub_surface = wl_subcompositor_get_subsurface( - term.wl.sub_compositor, term.wl.search_surface, term.wl.surface); - wl_subsurface_set_desync(term.wl.search_sub_surface); + 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.wl.surface); + wl_surface_commit(term.window.surface); wl_display_roundtrip(term.wl.display); if (conf.width == -1) { @@ -1151,7 +1151,7 @@ out: shm_fini(); - tll_free(term.wl.on_outputs); + tll_free(term.window.on_outputs); tll_foreach(term.wl.monitors, it) { free(it->item.name); if (it->item.xdg != NULL) @@ -1193,24 +1193,24 @@ out: zwp_primary_selection_device_manager_v1_destroy(term.wl.primary_selection_device_manager); if (term.wl.seat != NULL) wl_seat_destroy(term.wl.seat); - if (term.wl.search_sub_surface != NULL) - wl_subsurface_destroy(term.wl.search_sub_surface); - if (term.wl.search_surface != NULL) - wl_surface_destroy(term.wl.search_surface); + if (term.window.search_sub_surface != NULL) + wl_subsurface_destroy(term.window.search_sub_surface); + if (term.window.search_surface != NULL) + wl_surface_destroy(term.window.search_surface); if (term.render.frame_callback != NULL) wl_callback_destroy(term.render.frame_callback); - if (term.wl.xdg_toplevel_decoration != NULL) - zxdg_toplevel_decoration_v1_destroy(term.wl.xdg_toplevel_decoration); - if (term.wl.xdg_decoration_manager != NULL) - zxdg_decoration_manager_v1_destroy(term.wl.xdg_decoration_manager); - if (term.wl.xdg_toplevel != NULL) - xdg_toplevel_destroy(term.wl.xdg_toplevel); - if (term.wl.xdg_surface != NULL) - xdg_surface_destroy(term.wl.xdg_surface); - if (term.wl.shell != NULL) - xdg_wm_base_destroy(term.wl.shell); - if (term.wl.surface != NULL) - wl_surface_destroy(term.wl.surface); + if (term.window.xdg_toplevel_decoration != NULL) + zxdg_toplevel_decoration_v1_destroy(term.window.xdg_toplevel_decoration); + if (term.window.xdg_decoration_manager != NULL) + zxdg_decoration_manager_v1_destroy(term.window.xdg_decoration_manager); + if (term.window.xdg_toplevel != NULL) + xdg_toplevel_destroy(term.window.xdg_toplevel); + if (term.window.xdg_surface != NULL) + xdg_surface_destroy(term.window.xdg_surface); + if (term.window.shell != NULL) + xdg_wm_base_destroy(term.window.shell); + if (term.window.surface != NULL) + wl_surface_destroy(term.window.surface); if (term.wl.shm != NULL) wl_shm_destroy(term.wl.shm); if (term.wl.sub_compositor != NULL) diff --git a/render.c b/render.c index ff05d501..9f9d720c 100644 --- a/render.c +++ b/render.c @@ -330,7 +330,8 @@ grid_render_scroll(struct terminal *term, struct buffer *buf, raw + src_y * buf->stride, height * buf->stride); - wl_surface_damage_buffer(term->wl.surface, term->x_margin, dst_y, term->width - term->x_margin, height); + wl_surface_damage_buffer( + term->window.surface, term->x_margin, dst_y, term->width - term->x_margin, height); } } @@ -355,7 +356,8 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, raw + src_y * buf->stride, height * buf->stride); - wl_surface_damage_buffer(term->wl.surface, term->x_margin, dst_y, term->width - term->x_margin, height); + wl_surface_damage_buffer( + term->window.surface, term->x_margin, dst_y, term->width - term->x_margin, height); } } @@ -439,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->wl.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; @@ -487,13 +489,13 @@ grid_render(struct terminal *term) {0, bmargin, term->width, bmargin_height}}); /* Bottom */ wl_surface_damage_buffer( - term->wl.surface, 0, 0, term->width, term->y_margin); + term->window.surface, 0, 0, term->width, term->y_margin); wl_surface_damage_buffer( - term->wl.surface, 0, 0, term->x_margin, term->height); + term->window.surface, 0, 0, term->x_margin, term->height); wl_surface_damage_buffer( - term->wl.surface, rmargin, 0, rmargin_width, term->height); + term->window.surface, rmargin, 0, rmargin_width, term->height); wl_surface_damage_buffer( - term->wl.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); @@ -514,7 +516,7 @@ grid_render(struct terminal *term) render_cell(term, pix, cell, at.col, at.row, false); wl_surface_damage_buffer( - term->wl.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); @@ -567,7 +569,7 @@ grid_render(struct terminal *term) all_clean = false; wl_surface_damage_buffer( - term->wl.surface, + term->window.surface, term->x_margin, term->y_margin + r * term->cell_height, term->width - term->x_margin, term->cell_height); } @@ -590,7 +592,7 @@ grid_render(struct terminal *term) all_clean = false; wl_surface_damage_buffer( - term->wl.surface, + term->window.surface, term->x_margin, term->y_margin + r * term->cell_height, term->width - term->x_margin, term->cell_height); } @@ -672,7 +674,7 @@ grid_render(struct terminal *term) term, pix, cell, term->cursor.col, view_aligned_row, true); wl_surface_damage_buffer( - term->wl.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); @@ -692,18 +694,18 @@ grid_render(struct terminal *term) 1, &(pixman_rectangle16_t){0, 0, term->width, term->height}); wl_surface_damage_buffer( - term->wl.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->render.frame_callback == NULL); - term->render.frame_callback = wl_surface_frame(term->wl.surface); + term->render.frame_callback = wl_surface_frame(term->window.surface); wl_callback_add_listener(term->render.frame_callback, &frame_listener, term); - wl_surface_set_buffer_scale(term->wl.surface, term->scale); - wl_surface_commit(term->wl.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; @@ -730,7 +732,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da void render_search_box(struct terminal *term) { - assert(term->wl.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? */ @@ -778,13 +780,13 @@ render_search_box(struct terminal *term) draw_bar(term, buf->pix, font, &fg, x, y); wl_subsurface_set_position( - term->wl.search_sub_surface, + term->window.search_sub_surface, term->width - width - margin, term->height - height - margin); - wl_surface_damage_buffer(term->wl.search_surface, 0, 0, width, height); - wl_surface_attach(term->wl.search_surface, buf->wl_buf, 0, 0); - wl_surface_set_buffer_scale(term->wl.search_surface, scale); - wl_surface_commit(term->wl.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 @@ -816,7 +818,7 @@ void render_resize(struct terminal *term, int width, int height) { int scale = -1; - tll_foreach(term->wl.on_outputs, it) { + tll_foreach(term->window.on_outputs, it) { if (it->item->scale > scale) scale = it->item->scale; } @@ -937,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->wl.xdg_toplevel, title); + xdg_toplevel_set_title(term->window.xdg_toplevel, title); } bool diff --git a/search.c b/search.c index 45ca675e..59ac0152 100644 --- a/search.c +++ b/search.c @@ -19,8 +19,8 @@ static void search_cancel_keep_selection(struct terminal *term) { - wl_surface_attach(term->wl.search_surface, NULL, 0, 0); - wl_surface_commit(term->wl.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 a10e1928..643eaa65 100644 --- a/terminal.h +++ b/terminal.h @@ -67,22 +67,8 @@ struct wayland { char *theme_name; } pointer; - /* Main window */ - struct wl_surface *surface; - struct xdg_wm_base *shell; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; - - struct zxdg_decoration_manager_v1 *xdg_decoration_manager; - struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; - - /* Scrollback search */ - struct wl_surface *search_surface; - struct wl_subsurface *search_sub_surface; - bool have_argb8888; tll(struct monitor) monitors; /* All available outputs */ - tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ }; struct rgb { float r, g, b; }; @@ -375,6 +361,22 @@ struct terminal { } fextents; struct wayland wl; + struct { + struct wl_surface *surface; + struct xdg_wm_base *shell; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + + struct zxdg_decoration_manager_v1 *xdg_decoration_manager; + struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; + + /* Scrollback search */ + struct wl_surface *search_surface; + struct wl_subsurface *search_sub_surface; + + tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ + } window; + struct { int scrollback_lines; struct wl_callback *frame_callback; From a2b3cc6cfeda64f9f18d80776a354f5d137c3335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 13:04:26 +0100 Subject: [PATCH 03/30] term: convert anonymous 'window' struct to named 'wl_window' struct --- terminal.h | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/terminal.h b/terminal.h index 643eaa65..71f190cc 100644 --- a/terminal.h +++ b/terminal.h @@ -38,6 +38,22 @@ struct monitor { float refresh; }; +struct wl_window { + struct wl_surface *surface; + struct xdg_wm_base *shell; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + + struct zxdg_decoration_manager_v1 *xdg_decoration_manager; + struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; + + /* Scrollback search */ + struct wl_surface *search_surface; + struct wl_subsurface *search_sub_surface; + + tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ +}; + struct wayland { struct wl_display *display; struct wl_registry *registry; @@ -361,21 +377,7 @@ struct terminal { } fextents; struct wayland wl; - struct { - struct wl_surface *surface; - struct xdg_wm_base *shell; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; - - struct zxdg_decoration_manager_v1 *xdg_decoration_manager; - struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; - - /* Scrollback search */ - struct wl_surface *search_surface; - struct wl_subsurface *search_sub_surface; - - tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ - } window; + struct wl_window window; struct { int scrollback_lines; From 0120c57ed5a8189508d315e4c650f54572a935a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 13:06:39 +0100 Subject: [PATCH 04/30] wayland: move wayland struct definitions to a separate file --- meson.build | 1 + terminal.h | 69 +------------------------------------------------ wayland.h | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 68 deletions(-) create mode 100644 wayland.h diff --git a/meson.build b/meson.build index 590f5f8f..62fceb6e 100644 --- a/meson.build +++ b/meson.build @@ -84,6 +84,7 @@ executable( 'tokenize.c', 'tokenize.h', 'tllist.h', 'vt.c', 'vt.h', + 'wayland.h', wl_proto_src + wl_proto_headers, version, dependencies: [threads, math, freetype, fontconfig, pixman, wayland_client, wayland_cursor, xkb], install: true) diff --git a/terminal.h b/terminal.h index 71f190cc..e1756632 100644 --- a/terminal.h +++ b/terminal.h @@ -15,78 +15,11 @@ #include "font.h" #include "tllist.h" +#include "wayland.h" #define likely(c) __builtin_expect(!!(c), 1) #define unlikely(c) __builtin_expect(!!(c), 0) -struct monitor { - struct terminal *term; - struct wl_output *output; - struct zxdg_output_v1 *xdg; - char *name; - - int x; - int y; - - int width_mm; - int height_mm; - - int width_px; - int height_px; - - int scale; - float refresh; -}; - -struct wl_window { - struct wl_surface *surface; - struct xdg_wm_base *shell; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; - - struct zxdg_decoration_manager_v1 *xdg_decoration_manager; - struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; - - /* Scrollback search */ - struct wl_surface *search_surface; - struct wl_subsurface *search_sub_surface; - - tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ -}; - -struct wayland { - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - struct wl_subcompositor *sub_compositor; - struct wl_shm *shm; - - struct wl_seat *seat; - struct wl_keyboard *keyboard; - struct zxdg_output_manager_v1 *xdg_output_manager; - - /* Clipboard */ - struct wl_data_device_manager *data_device_manager; - struct wl_data_device *data_device; - struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; - struct zwp_primary_selection_device_v1 *primary_selection_device; - - /* Cursor */ - struct { - struct wl_pointer *pointer; - uint32_t serial; - - struct wl_surface *surface; - struct wl_cursor_theme *theme; - struct wl_cursor *cursor; - int size; - char *theme_name; - } pointer; - - bool have_argb8888; - tll(struct monitor) monitors; /* All available outputs */ -}; - struct rgb { float r, g, b; }; /* diff --git a/wayland.h b/wayland.h new file mode 100644 index 00000000..970cb8cf --- /dev/null +++ b/wayland.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +#include "tllist.h" + +struct monitor { + struct terminal *term; + struct wl_output *output; + struct zxdg_output_v1 *xdg; + char *name; + + int x; + int y; + + int width_mm; + int height_mm; + + int width_px; + int height_px; + + int scale; + float refresh; +}; + +struct wl_window { + struct wl_surface *surface; + struct xdg_wm_base *shell; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + + struct zxdg_decoration_manager_v1 *xdg_decoration_manager; + struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; + + /* Scrollback search */ + struct wl_surface *search_surface; + struct wl_subsurface *search_sub_surface; + + tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ +}; + +struct wayland { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_subcompositor *sub_compositor; + struct wl_shm *shm; + + struct wl_seat *seat; + struct wl_keyboard *keyboard; + struct zxdg_output_manager_v1 *xdg_output_manager; + + /* Clipboard */ + struct wl_data_device_manager *data_device_manager; + struct wl_data_device *data_device; + struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; + struct zwp_primary_selection_device_v1 *primary_selection_device; + + /* Cursor */ + struct { + struct wl_pointer *pointer; + uint32_t serial; + + struct wl_surface *surface; + struct wl_cursor_theme *theme; + struct wl_cursor *cursor; + int size; + char *theme_name; + } pointer; + + bool have_argb8888; + tll(struct monitor) monitors; /* All available outputs */ +}; From 942ff566a2e8f9f4dc866b74ba10bce838a0d606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 15:57:23 +0100 Subject: [PATCH 05/30] wayland: implement wayl_destroy() --- main.c | 62 +++++++++---------------------------------------- meson.build | 2 +- wayland.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++ wayland.h | 7 ++++++ 4 files changed, 85 insertions(+), 52 deletions(-) create mode 100644 wayland.c diff --git a/main.c b/main.c index 78a70fe4..7487705f 100644 --- a/main.c +++ b/main.c @@ -1152,47 +1152,26 @@ out: shm_fini(); tll_free(term.window.on_outputs); - tll_foreach(term.wl.monitors, it) { - free(it->item.name); - if (it->item.xdg != NULL) - zxdg_output_v1_destroy(it->item.xdg); - if (it->item.output != NULL) - wl_output_destroy(it->item.output); - tll_remove(term.wl.monitors, it); - } - - if (term.wl.xdg_output_manager != NULL) - zxdg_output_manager_v1_destroy(term.wl.xdg_output_manager); - - free(term.wl.pointer.theme_name); - if (term.wl.pointer.theme != NULL) - wl_cursor_theme_destroy(term.wl.pointer.theme); - if (term.wl.pointer.pointer != NULL) - wl_pointer_destroy(term.wl.pointer.pointer); - if (term.wl.pointer.surface != NULL) - wl_surface_destroy(term.wl.pointer.surface); - if (term.wl.keyboard != NULL) - wl_keyboard_destroy(term.wl.keyboard); if (term.selection.clipboard.data_source != NULL) wl_data_source_destroy(term.selection.clipboard.data_source); if (term.selection.clipboard.data_offer != NULL) wl_data_offer_destroy(term.selection.clipboard.data_offer); free(term.selection.clipboard.text); - if (term.wl.data_device != NULL) - wl_data_device_destroy(term.wl.data_device); - if (term.wl.data_device_manager != NULL) - wl_data_device_manager_destroy(term.wl.data_device_manager); if (term.selection.primary.data_source != NULL) zwp_primary_selection_source_v1_destroy(term.selection.primary.data_source); if (term.selection.primary.data_offer != NULL) zwp_primary_selection_offer_v1_destroy(term.selection.primary.data_offer); free(term.selection.primary.text); - if (term.wl.primary_selection_device != NULL) - zwp_primary_selection_device_v1_destroy(term.wl.primary_selection_device); - if (term.wl.primary_selection_device_manager != NULL) - zwp_primary_selection_device_manager_v1_destroy(term.wl.primary_selection_device_manager); - if (term.wl.seat != NULL) - wl_seat_destroy(term.wl.seat); + if (term.kbd.xkb_compose_state != NULL) + xkb_compose_state_unref(term.kbd.xkb_compose_state); + if (term.kbd.xkb_compose_table != NULL) + xkb_compose_table_unref(term.kbd.xkb_compose_table); + if (term.kbd.xkb_keymap != NULL) + xkb_keymap_unref(term.kbd.xkb_keymap); + if (term.kbd.xkb_state != NULL) + xkb_state_unref(term.kbd.xkb_state); + if (term.kbd.xkb != NULL) + xkb_context_unref(term.kbd.xkb); if (term.window.search_sub_surface != NULL) wl_subsurface_destroy(term.window.search_sub_surface); if (term.window.search_surface != NULL) @@ -1211,26 +1190,7 @@ out: xdg_wm_base_destroy(term.window.shell); if (term.window.surface != NULL) wl_surface_destroy(term.window.surface); - if (term.wl.shm != NULL) - wl_shm_destroy(term.wl.shm); - if (term.wl.sub_compositor != NULL) - wl_subcompositor_destroy(term.wl.sub_compositor); - if (term.wl.compositor != NULL) - wl_compositor_destroy(term.wl.compositor); - if (term.wl.registry != NULL) - wl_registry_destroy(term.wl.registry); - if (term.wl.display != NULL) - wl_display_disconnect(term.wl.display); - if (term.kbd.xkb_compose_state != NULL) - xkb_compose_state_unref(term.kbd.xkb_compose_state); - if (term.kbd.xkb_compose_table != NULL) - xkb_compose_table_unref(term.kbd.xkb_compose_table); - if (term.kbd.xkb_keymap != NULL) - xkb_keymap_unref(term.kbd.xkb_keymap); - if (term.kbd.xkb_state != NULL) - xkb_state_unref(term.kbd.xkb_state); - if (term.kbd.xkb != NULL) - xkb_context_unref(term.kbd.xkb); + wayl_destroy(&term.wl); free(term.vt.osc.data); for (int row = 0; row < term.normal.num_rows; row++) diff --git a/meson.build b/meson.build index 62fceb6e..da91ff91 100644 --- a/meson.build +++ b/meson.build @@ -84,7 +84,7 @@ executable( 'tokenize.c', 'tokenize.h', 'tllist.h', 'vt.c', 'vt.h', - 'wayland.h', + 'wayland.c', 'wayland.h', wl_proto_src + wl_proto_headers, version, dependencies: [threads, math, freetype, fontconfig, pixman, wayland_client, wayland_cursor, xkb], install: true) diff --git a/wayland.c b/wayland.c new file mode 100644 index 00000000..4a8ad23f --- /dev/null +++ b/wayland.c @@ -0,0 +1,66 @@ +#include "wayland.h" + +#include +#include +#include +#include + +#include +#include + +#define LOG_MODULE "wayland" +#define LOG_ENABLE_DBG 0 +#include "log.h" + +#include "tllist.h" + +void +wayl_init(struct wayland *wayl) +{ +} + +void +wayl_destroy(struct wayland *wayl) +{ + tll_foreach(wayl->monitors, it) { + free(it->item.name); + if (it->item.xdg != NULL) + zxdg_output_v1_destroy(it->item.xdg); + if (it->item.output != NULL) + wl_output_destroy(it->item.output); + tll_remove(wayl->monitors, it); + } + + if (wayl->xdg_output_manager != NULL) + zxdg_output_manager_v1_destroy(wayl->xdg_output_manager); + + free(wayl->pointer.theme_name); + if (wayl->pointer.theme != NULL) + wl_cursor_theme_destroy(wayl->pointer.theme); + if (wayl->pointer.pointer != NULL) + wl_pointer_destroy(wayl->pointer.pointer); + if (wayl->pointer.surface != NULL) + wl_surface_destroy(wayl->pointer.surface); + if (wayl->keyboard != NULL) + wl_keyboard_destroy(wayl->keyboard); + if (wayl->data_device != NULL) + wl_data_device_destroy(wayl->data_device); + if (wayl->data_device_manager != NULL) + wl_data_device_manager_destroy(wayl->data_device_manager); + if (wayl->primary_selection_device != NULL) + zwp_primary_selection_device_v1_destroy(wayl->primary_selection_device); + if (wayl->primary_selection_device_manager != NULL) + zwp_primary_selection_device_manager_v1_destroy(wayl->primary_selection_device_manager); + if (wayl->seat != NULL) + wl_seat_destroy(wayl->seat); + if (wayl->shm != NULL) + wl_shm_destroy(wayl->shm); + if (wayl->sub_compositor != NULL) + wl_subcompositor_destroy(wayl->sub_compositor); + if (wayl->compositor != NULL) + wl_compositor_destroy(wayl->compositor); + if (wayl->registry != NULL) + wl_registry_destroy(wayl->registry); + if (wayl->display != NULL) + wl_display_disconnect(wayl->display); +} diff --git a/wayland.h b/wayland.h index 970cb8cf..81d622fe 100644 --- a/wayland.h +++ b/wayland.h @@ -3,6 +3,9 @@ #include #include +#include +#include + #include "tllist.h" struct monitor { @@ -72,3 +75,7 @@ struct wayland { bool have_argb8888; tll(struct monitor) monitors; /* All available outputs */ }; + +/* TODO: return allocated pointer */ +void wayl_init(struct wayland *wayl); +void wayl_destroy(struct wayland *wayl); From 9e6c28f5b6b0be6788d91bf3233323362cac732a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 16:01:03 +0100 Subject: [PATCH 06/30] wayland: implement wayl_win_destroy() --- main.c | 23 +++-------------------- render.c | 12 ++++++------ terminal.h | 1 - wayland.c | 24 ++++++++++++++++++++++++ wayland.h | 4 ++++ 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/main.c b/main.c index 7487705f..267bd89f 100644 --- a/main.c +++ b/main.c @@ -463,7 +463,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->render.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, @@ -1151,7 +1151,6 @@ out: shm_fini(); - tll_free(term.window.on_outputs); if (term.selection.clipboard.data_source != NULL) wl_data_source_destroy(term.selection.clipboard.data_source); if (term.selection.clipboard.data_offer != NULL) @@ -1172,24 +1171,8 @@ out: xkb_state_unref(term.kbd.xkb_state); if (term.kbd.xkb != NULL) xkb_context_unref(term.kbd.xkb); - if (term.window.search_sub_surface != NULL) - wl_subsurface_destroy(term.window.search_sub_surface); - if (term.window.search_surface != NULL) - wl_surface_destroy(term.window.search_surface); - if (term.render.frame_callback != NULL) - wl_callback_destroy(term.render.frame_callback); - if (term.window.xdg_toplevel_decoration != NULL) - zxdg_toplevel_decoration_v1_destroy(term.window.xdg_toplevel_decoration); - if (term.window.xdg_decoration_manager != NULL) - zxdg_decoration_manager_v1_destroy(term.window.xdg_decoration_manager); - if (term.window.xdg_toplevel != NULL) - xdg_toplevel_destroy(term.window.xdg_toplevel); - if (term.window.xdg_surface != NULL) - xdg_surface_destroy(term.window.xdg_surface); - if (term.window.shell != NULL) - xdg_wm_base_destroy(term.window.shell); - if (term.window.surface != NULL) - wl_surface_destroy(term.window.surface); + + wayl_win_destroy(&term.window); wayl_destroy(&term.wl); free(term.vt.osc.data); diff --git a/render.c b/render.c index 9f9d720c..c2840eb8 100644 --- a/render.c +++ b/render.c @@ -700,9 +700,9 @@ grid_render(struct terminal *term) 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->render.frame_callback == NULL); - term->render.frame_callback = wl_surface_frame(term->window.surface); - wl_callback_add_listener(term->render.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); @@ -723,9 +723,9 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da { struct terminal *term = data; - assert(term->render.frame_callback == wl_callback); + assert(term->window.frame_callback == wl_callback); wl_callback_destroy(wl_callback); - term->render.frame_callback = NULL; + term->window.frame_callback = NULL; grid_render(term); } @@ -1000,6 +1000,6 @@ render_update_cursor_surface(struct terminal *term) void render_refresh(struct terminal *term) { - if (term->render.frame_callback == NULL) + if (term->window.frame_callback == NULL) grid_render(term); } diff --git a/terminal.h b/terminal.h index e1756632..df70347d 100644 --- a/terminal.h +++ b/terminal.h @@ -314,7 +314,6 @@ struct terminal { struct { int scrollback_lines; - struct wl_callback *frame_callback; struct { size_t count; diff --git a/wayland.c b/wayland.c index 4a8ad23f..9b00f5bb 100644 --- a/wayland.c +++ b/wayland.c @@ -64,3 +64,27 @@ wayl_destroy(struct wayland *wayl) if (wayl->display != NULL) wl_display_disconnect(wayl->display); } + +void +wayl_win_destroy(struct wl_window *win) +{ + tll_free(win->on_outputs); + if (win->search_sub_surface != NULL) + wl_subsurface_destroy(win->search_sub_surface); + if (win->search_surface != NULL) + wl_surface_destroy(win->search_surface); + if (win->frame_callback != NULL) + wl_callback_destroy(win->frame_callback); + if (win->xdg_toplevel_decoration != NULL) + zxdg_toplevel_decoration_v1_destroy(win->xdg_toplevel_decoration); + if (win->xdg_decoration_manager != NULL) + zxdg_decoration_manager_v1_destroy(win->xdg_decoration_manager); + if (win->xdg_toplevel != NULL) + xdg_toplevel_destroy(win->xdg_toplevel); + if (win->xdg_surface != NULL) + xdg_surface_destroy(win->xdg_surface); + if (win->shell != NULL) + xdg_wm_base_destroy(win->shell); + if (win->surface != NULL) + wl_surface_destroy(win->surface); +} diff --git a/wayland.h b/wayland.h index 81d622fe..cbd0392c 100644 --- a/wayland.h +++ b/wayland.h @@ -40,6 +40,8 @@ struct wl_window { struct wl_surface *search_surface; struct wl_subsurface *search_sub_surface; + struct wl_callback *frame_callback; + tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ }; @@ -79,3 +81,5 @@ struct wayland { /* TODO: return allocated pointer */ void wayl_init(struct wayland *wayl); void wayl_destroy(struct wayland *wayl); + +void wayl_win_destroy(struct wl_window *win); From 5ca1ee701bc4297180fa9c248d9f8810ef82550c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 16:15:32 +0100 Subject: [PATCH 07/30] wayland: move clipboard/primary structs into the wayland struct These are application global and very wayland specific --- main.c | 10 ---------- selection.c | 35 ++++++++++++++++------------------- terminal.h | 16 ---------------- wayland.c | 11 +++++++++++ wayland.h | 18 ++++++++++++++++++ 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/main.c b/main.c index 267bd89f..0bdfe69a 100644 --- a/main.c +++ b/main.c @@ -1151,16 +1151,6 @@ out: shm_fini(); - if (term.selection.clipboard.data_source != NULL) - wl_data_source_destroy(term.selection.clipboard.data_source); - if (term.selection.clipboard.data_offer != NULL) - wl_data_offer_destroy(term.selection.clipboard.data_offer); - free(term.selection.clipboard.text); - if (term.selection.primary.data_source != NULL) - zwp_primary_selection_source_v1_destroy(term.selection.primary.data_source); - if (term.selection.primary.data_offer != NULL) - zwp_primary_selection_offer_v1_destroy(term.selection.primary.data_offer); - free(term.selection.primary.text); if (term.kbd.xkb_compose_state != NULL) xkb_compose_state_unref(term.kbd.xkb_compose_state); if (term.kbd.xkb_compose_table != NULL) diff --git a/selection.c b/selection.c index d5b89dba..e53ace8b 100644 --- a/selection.c +++ b/selection.c @@ -319,7 +319,7 @@ static void send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, int32_t fd) { - const struct clipboard *clipboard + const struct wl_clipboard *clipboard = wl_data_source_get_user_data(wl_data_source); assert(clipboard != NULL); @@ -346,7 +346,7 @@ send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, static void cancelled(void *data, struct wl_data_source *wl_data_source) { - struct clipboard *clipboard = wl_data_source_get_user_data(wl_data_source); + struct wl_clipboard *clipboard = wl_data_source_get_user_data(wl_data_source); assert(clipboard->data_source == wl_data_source); wl_data_source_destroy(clipboard->data_source); @@ -386,7 +386,7 @@ primary_send(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1, const char *mime_type, int32_t fd) { - const struct primary *primary + const struct wl_primary *primary = zwp_primary_selection_source_v1_get_user_data(zwp_primary_selection_source_v1); assert(primary != NULL); @@ -414,7 +414,7 @@ static void primary_cancelled(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1) { - struct primary *primary = zwp_primary_selection_source_v1_get_user_data( + struct wl_primary *primary = zwp_primary_selection_source_v1_get_user_data( zwp_primary_selection_source_v1); //assert(primary->data_source == zwp_primary_selection_source_v1); @@ -434,10 +434,10 @@ static const struct zwp_primary_selection_source_v1_listener primary_selection_s bool text_to_clipboard(struct terminal *term, char *text, uint32_t serial) { - if (term->selection.clipboard.data_source != NULL) { - /* Kill previous data source */ - struct clipboard *clipboard = &term->selection.clipboard; + struct wl_clipboard *clipboard = &term->wl.clipboard; + if (term->wl.clipboard.data_source != NULL) { + /* Kill previous data source */ assert(clipboard->serial != 0); wl_data_device_set_selection(term->wl.data_device, NULL, clipboard->serial); wl_data_source_destroy(clipboard->data_source); @@ -447,8 +447,6 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial) clipboard->serial = 0; } - struct clipboard *clipboard = &term->selection.clipboard; - clipboard->data_source = wl_data_device_manager_create_data_source(term->wl.data_device_manager); @@ -484,7 +482,7 @@ text_from_clipboard(struct terminal *term, uint32_t serial, void (*cb)(const char *data, size_t size, void *user), void *user) { - struct clipboard *clipboard = &term->selection.clipboard; + struct wl_clipboard *clipboard = &term->wl.clipboard; if (clipboard->data_offer == NULL) return; @@ -541,7 +539,7 @@ from_clipboard_cb(const char *data, size_t size, void *user) void selection_from_clipboard(struct terminal *term, uint32_t serial) { - struct clipboard *clipboard = &term->selection.clipboard; + struct wl_clipboard *clipboard = &term->wl.clipboard; if (clipboard->data_offer == NULL) return; @@ -560,10 +558,11 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial) if (term->wl.primary_selection_device_manager == NULL) return false; + struct wl_primary *primary = &term->wl.primary; + /* TODO: somehow share code with the clipboard equivalent */ - if (term->selection.primary.data_source != NULL) { + if (term->wl.primary.data_source != NULL) { /* Kill previous data source */ - struct primary *primary = &term->selection.primary; assert(primary->serial != 0); zwp_primary_selection_device_v1_set_selection( @@ -575,8 +574,6 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial) primary->serial = 0; } - struct primary *primary = &term->selection.primary; - primary->data_source = zwp_primary_selection_device_manager_v1_create_source( term->wl.primary_selection_device_manager); @@ -620,7 +617,7 @@ text_from_primary( if (term->wl.primary_selection_device_manager == NULL) return; - struct primary *primary = &term->selection.primary; + struct wl_primary *primary = &term->wl.primary; if (primary->data_offer == NULL) return; @@ -673,7 +670,7 @@ selection_from_primary(struct terminal *term) if (term->wl.primary_selection_device_manager == NULL) return; - struct clipboard *clipboard = &term->selection.clipboard; + struct wl_clipboard *clipboard = &term->wl.clipboard; if (clipboard->data_offer == NULL) return; @@ -746,7 +743,7 @@ selection(void *data, struct wl_data_device *wl_data_device, /* Selection offer from other client */ struct terminal *term = data; - struct clipboard *clipboard = &term->selection.clipboard; + struct wl_clipboard *clipboard = &term->wl.clipboard; if (clipboard->data_offer != NULL) wl_data_offer_destroy(clipboard->data_offer); @@ -795,7 +792,7 @@ primary_selection(void *data, /* Selection offer from other client, for primary */ struct terminal *term = data; - struct primary *primary = &term->selection.primary; + struct wl_primary *primary = &term->wl.primary; if (primary->data_offer != NULL) zwp_primary_selection_offer_v1_destroy(primary->data_offer); diff --git a/terminal.h b/terminal.h index df70347d..999b72e0 100644 --- a/terminal.h +++ b/terminal.h @@ -171,20 +171,6 @@ enum mouse_reporting { MOUSE_URXVT, /* ?1015h */ }; -struct clipboard { - struct wl_data_source *data_source; - struct wl_data_offer *data_offer; - char *text; - uint32_t serial; -}; - -struct primary { - struct zwp_primary_selection_source_v1 *data_source; - struct zwp_primary_selection_offer_v1 *data_offer; - char *text; - uint32_t serial; -}; - enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR }; struct terminal { @@ -279,8 +265,6 @@ struct terminal { struct { struct coord start; struct coord end; - struct clipboard clipboard; - struct primary primary; } selection; bool is_searching; diff --git a/wayland.c b/wayland.c index 9b00f5bb..96ba3641 100644 --- a/wayland.c +++ b/wayland.c @@ -34,6 +34,17 @@ wayl_destroy(struct wayland *wayl) if (wayl->xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(wayl->xdg_output_manager); + if (wayl->clipboard.data_source != NULL) + wl_data_source_destroy(wayl->clipboard.data_source); + if (wayl->clipboard.data_offer != NULL) + wl_data_offer_destroy(wayl->clipboard.data_offer); + free(wayl->clipboard.text); + if (wayl->primary.data_source != NULL) + zwp_primary_selection_source_v1_destroy(wayl->primary.data_source); + if (wayl->primary.data_offer != NULL) + zwp_primary_selection_offer_v1_destroy(wayl->primary.data_offer); + free(wayl->primary.text); + free(wayl->pointer.theme_name); if (wayl->pointer.theme != NULL) wl_cursor_theme_destroy(wayl->pointer.theme); diff --git a/wayland.h b/wayland.h index cbd0392c..1b680b2c 100644 --- a/wayland.h +++ b/wayland.h @@ -27,6 +27,20 @@ struct monitor { float refresh; }; +struct wl_clipboard { + struct wl_data_source *data_source; + struct wl_data_offer *data_offer; + char *text; + uint32_t serial; +}; + +struct wl_primary { + struct zwp_primary_selection_source_v1 *data_source; + struct zwp_primary_selection_offer_v1 *data_offer; + char *text; + uint32_t serial; +}; + struct wl_window { struct wl_surface *surface; struct xdg_wm_base *shell; @@ -74,6 +88,10 @@ struct wayland { char *theme_name; } pointer; + /* Clipboard */ + struct wl_clipboard clipboard; + struct wl_primary primary; + bool have_argb8888; tll(struct monitor) monitors; /* All available outputs */ }; From 061bbd704961b17419fd69344af6268487abb926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 16:21:19 +0100 Subject: [PATCH 08/30] kbd: break out XKB kbd struct to a separate file --- kbd.c | 18 ++++++++++++++++++ kbd.h | 35 +++++++++++++++++++++++++++++++++++ main.c | 13 +------------ meson.build | 1 + terminal.h | 33 +-------------------------------- 5 files changed, 56 insertions(+), 44 deletions(-) create mode 100644 kbd.c create mode 100644 kbd.h diff --git a/kbd.c b/kbd.c new file mode 100644 index 00000000..c38e77ba --- /dev/null +++ b/kbd.c @@ -0,0 +1,18 @@ +#include "kbd.h" + +#include + +void +kbd_destroy(struct kbd *kbd) +{ + if (kbd->xkb_compose_state != NULL) + xkb_compose_state_unref(kbd->xkb_compose_state); + if (kbd->xkb_compose_table != NULL) + xkb_compose_table_unref(kbd->xkb_compose_table); + if (kbd->xkb_keymap != NULL) + xkb_keymap_unref(kbd->xkb_keymap); + if (kbd->xkb_state != NULL) + xkb_state_unref(kbd->xkb_state); + if (kbd->xkb != NULL) + xkb_context_unref(kbd->xkb); +} diff --git a/kbd.h b/kbd.h new file mode 100644 index 00000000..06de5b50 --- /dev/null +++ b/kbd.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include + +struct kbd { + struct xkb_context *xkb; + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + struct xkb_compose_table *xkb_compose_table; + struct xkb_compose_state *xkb_compose_state; + struct { + int fd; + + bool dont_re_repeat; + int32_t delay; + int32_t rate; + uint32_t key; + } repeat; + + xkb_mod_index_t mod_shift; + xkb_mod_index_t mod_alt; + xkb_mod_index_t mod_ctrl; + xkb_mod_index_t mod_meta; + + /* Enabled modifiers */ + bool shift; + bool alt; + bool ctrl; + bool meta; +}; + +void kbd_destroy(struct kbd *kbd); diff --git a/main.c b/main.c index 0bdfe69a..7c8de292 100644 --- a/main.c +++ b/main.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -1151,17 +1150,7 @@ out: shm_fini(); - if (term.kbd.xkb_compose_state != NULL) - xkb_compose_state_unref(term.kbd.xkb_compose_state); - if (term.kbd.xkb_compose_table != NULL) - xkb_compose_table_unref(term.kbd.xkb_compose_table); - if (term.kbd.xkb_keymap != NULL) - xkb_keymap_unref(term.kbd.xkb_keymap); - if (term.kbd.xkb_state != NULL) - xkb_state_unref(term.kbd.xkb_state); - if (term.kbd.xkb != NULL) - xkb_context_unref(term.kbd.xkb); - + kbd_destroy(&term.kbd); wayl_win_destroy(&term.window); wayl_destroy(&term.wl); diff --git a/meson.build b/meson.build index da91ff91..cc6bc7f4 100644 --- a/meson.build +++ b/meson.build @@ -72,6 +72,7 @@ executable( 'font.c', 'font.h', 'grid.c', 'grid.h', 'input.c', 'input.h', + 'kbd.c', 'kbd.h', 'log.c', 'log.h', 'main.c', 'osc.c', 'osc.h', diff --git a/terminal.h b/terminal.h index 999b72e0..7ee45ca7 100644 --- a/terminal.h +++ b/terminal.h @@ -8,14 +8,10 @@ #include #include -#include -#include -#include -#include - #include "font.h" #include "tllist.h" #include "wayland.h" +#include "kbd.h" #define likely(c) __builtin_expect(!!(c), 1) #define unlikely(c) __builtin_expect(!!(c), 0) @@ -123,33 +119,6 @@ struct vt { struct attributes saved_attrs; }; -struct kbd { - struct xkb_context *xkb; - struct xkb_keymap *xkb_keymap; - struct xkb_state *xkb_state; - struct xkb_compose_table *xkb_compose_table; - struct xkb_compose_state *xkb_compose_state; - struct { - int fd; - - bool dont_re_repeat; - int32_t delay; - int32_t rate; - uint32_t key; - } repeat; - - xkb_mod_index_t mod_shift; - xkb_mod_index_t mod_alt; - xkb_mod_index_t mod_ctrl; - xkb_mod_index_t mod_meta; - - /* Enabled modifiers */ - bool shift; - bool alt; - bool ctrl; - bool meta; -}; - enum cursor_keys { CURSOR_KEYS_DONTCARE, CURSOR_KEYS_NORMAL, CURSOR_KEYS_APPLICATION}; enum keypad_keys { KEYPAD_DONTCARE, KEYPAD_NUMERICAL, KEYPAD_APPLICATION }; enum charset { CHARSET_ASCII, CHARSET_GRAPHIC }; From c9455d5f211616ce3750e58a472e1b5a4251b8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 17:07:44 +0100 Subject: [PATCH 09/30] kbd: move into wayland --- input.c | 136 ++++++++++++++++++++++++++-------------------------- kbd.c | 18 ------- kbd.h | 35 -------------- main.c | 31 ++++++------ meson.build | 1 - search.c | 16 +++---- selection.c | 2 +- terminal.c | 6 +-- terminal.h | 2 - wayland.c | 11 +++++ wayland.h | 38 +++++++++++++-- 11 files changed, 141 insertions(+), 155 deletions(-) delete mode 100644 kbd.c delete mode 100644 kbd.h diff --git a/input.c b/input.c index 00397974..975923a3 100644 --- a/input.c +++ b/input.c @@ -35,45 +35,45 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); /* TODO: free old context + keymap */ - if (term->kbd.xkb_compose_state != NULL) { - xkb_compose_state_unref(term->kbd.xkb_compose_state); - term->kbd.xkb_compose_state = NULL; + if (term->wl.kbd.xkb_compose_state != NULL) { + xkb_compose_state_unref(term->wl.kbd.xkb_compose_state); + term->wl.kbd.xkb_compose_state = NULL; } - if (term->kbd.xkb_compose_table != NULL) { - xkb_compose_table_unref(term->kbd.xkb_compose_table); - term->kbd.xkb_compose_table = NULL; + if (term->wl.kbd.xkb_compose_table != NULL) { + xkb_compose_table_unref(term->wl.kbd.xkb_compose_table); + term->wl.kbd.xkb_compose_table = NULL; } - if (term->kbd.xkb_keymap != NULL) { - xkb_keymap_unref(term->kbd.xkb_keymap); - term->kbd.xkb_keymap = NULL; + if (term->wl.kbd.xkb_keymap != NULL) { + xkb_keymap_unref(term->wl.kbd.xkb_keymap); + term->wl.kbd.xkb_keymap = NULL; } - if (term->kbd.xkb_state != NULL) { - xkb_state_unref(term->kbd.xkb_state); - term->kbd.xkb_state = NULL; + if (term->wl.kbd.xkb_state != NULL) { + xkb_state_unref(term->wl.kbd.xkb_state); + term->wl.kbd.xkb_state = NULL; } - if (term->kbd.xkb != NULL) { - xkb_context_unref(term->kbd.xkb); - term->kbd.xkb = NULL; + if (term->wl.kbd.xkb != NULL) { + xkb_context_unref(term->wl.kbd.xkb); + term->wl.kbd.xkb = NULL; } - term->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - term->kbd.xkb_keymap = xkb_keymap_new_from_string( - term->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, + term->wl.kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + term->wl.kbd.xkb_keymap = xkb_keymap_new_from_string( + term->wl.kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); /* TODO: initialize in enter? */ - term->kbd.xkb_state = xkb_state_new(term->kbd.xkb_keymap); + term->wl.kbd.xkb_state = xkb_state_new(term->wl.kbd.xkb_keymap); - term->kbd.mod_shift = xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Shift"); - term->kbd.mod_alt = xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Mod1") ; - term->kbd.mod_ctrl = xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Control"); - term->kbd.mod_meta = xkb_keymap_mod_get_index(term->kbd.xkb_keymap, "Mod4"); + term->wl.kbd.mod_shift = xkb_keymap_mod_get_index(term->wl.kbd.xkb_keymap, "Shift"); + term->wl.kbd.mod_alt = xkb_keymap_mod_get_index(term->wl.kbd.xkb_keymap, "Mod1") ; + term->wl.kbd.mod_ctrl = xkb_keymap_mod_get_index(term->wl.kbd.xkb_keymap, "Control"); + term->wl.kbd.mod_meta = xkb_keymap_mod_get_index(term->wl.kbd.xkb_keymap, "Mod4"); /* Compose (dead keys) */ - term->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( - term->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); - term->kbd.xkb_compose_state = xkb_compose_state_new( - term->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + term->wl.kbd.xkb_compose_table = xkb_compose_table_new_from_locale( + term->wl.kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); + term->wl.kbd.xkb_compose_state = xkb_compose_state_new( + term->wl.kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); munmap(map_str, size); close(fd); @@ -91,12 +91,12 @@ keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, static bool start_repeater(struct terminal *term, uint32_t key) { - if (term->kbd.repeat.dont_re_repeat) + if (term->wl.kbd.repeat.dont_re_repeat) return true; struct itimerspec t = { - .it_value = {.tv_sec = 0, .tv_nsec = term->kbd.repeat.delay * 1000000}, - .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / term->kbd.repeat.rate}, + .it_value = {.tv_sec = 0, .tv_nsec = term->wl.kbd.repeat.delay * 1000000}, + .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / term->wl.kbd.repeat.rate}, }; if (t.it_value.tv_nsec >= 1000000000) { @@ -107,22 +107,22 @@ start_repeater(struct terminal *term, uint32_t key) t.it_interval.tv_sec += t.it_interval.tv_nsec / 1000000000; t.it_interval.tv_nsec %= 1000000000; } - if (timerfd_settime(term->kbd.repeat.fd, 0, &t, NULL) < 0) { + if (timerfd_settime(term->wl.kbd.repeat.fd, 0, &t, NULL) < 0) { LOG_ERRNO("failed to arm keyboard repeat timer"); return false; } - term->kbd.repeat.key = key; + term->wl.kbd.repeat.key = key; return true; } static bool stop_repeater(struct terminal *term, uint32_t key) { - if (key != -1 && key != term->kbd.repeat.key) + if (key != -1 && key != term->wl.kbd.repeat.key) return true; - if (timerfd_settime(term->kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) { + if (timerfd_settime(term->wl.kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) { LOG_ERRNO("failed to disarm keyboard repeat timer"); return false; } @@ -146,10 +146,10 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, { struct terminal *term = data; - const xkb_mod_mask_t ctrl = 1 << term->kbd.mod_ctrl; - const xkb_mod_mask_t alt = 1 << term->kbd.mod_alt; - const xkb_mod_mask_t shift = 1 << term->kbd.mod_shift; - const xkb_mod_mask_t meta = 1 << term->kbd.mod_meta; + const xkb_mod_mask_t ctrl = 1 << term->wl.kbd.mod_ctrl; + const xkb_mod_mask_t alt = 1 << term->wl.kbd.mod_alt; + const xkb_mod_mask_t shift = 1 << term->wl.kbd.mod_shift; + const xkb_mod_mask_t meta = 1 << term->wl.kbd.mod_meta; if (state == XKB_KEY_UP) { stop_repeater(term, key); @@ -157,7 +157,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, } key += 8; - xkb_keysym_t sym = xkb_state_key_get_one_sym(term->kbd.xkb_state, key); + xkb_keysym_t sym = xkb_state_key_get_one_sym(term->wl.kbd.xkb_state, key); #if 0 char foo[100]; @@ -165,16 +165,16 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, LOG_INFO("%s", foo); #endif - xkb_compose_state_feed(term->kbd.xkb_compose_state, sym); + xkb_compose_state_feed(term->wl.kbd.xkb_compose_state, sym); enum xkb_compose_status compose_status = xkb_compose_state_get_status( - term->kbd.xkb_compose_state); + term->wl.kbd.xkb_compose_state); if (compose_status == XKB_COMPOSE_COMPOSING) return; xkb_mod_mask_t mods = xkb_state_serialize_mods( - term->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED); - //xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(term->kbd.xkb_state, key); + term->wl.kbd.xkb_state, XKB_STATE_MODS_DEPRESSED); + //xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(term->wl.kbd.xkb_state, key); xkb_mod_mask_t consumed = 0x0; xkb_mod_mask_t significant = ctrl | alt | shift | meta; xkb_mod_mask_t effective_mods = mods & ~consumed & significant; @@ -188,7 +188,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, #if 0 for (size_t i = 0; i < 32; i++) { if (mods & (1 << i)) { - LOG_INFO("%s", xkb_keymap_mod_get_name(term->kbd.xkb_keymap, i)); + LOG_INFO("%s", xkb_keymap_mod_get_name(term->wl.kbd.xkb_keymap, i)); } } #endif @@ -200,10 +200,10 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, bool found_map = false; enum modifier keymap_mods = MOD_NONE; - keymap_mods |= term->kbd.shift ? MOD_SHIFT : MOD_NONE; - keymap_mods |= term->kbd.alt ? MOD_ALT : MOD_NONE; - keymap_mods |= term->kbd.ctrl ? MOD_CTRL : MOD_NONE; - keymap_mods |= term->kbd.meta ? MOD_META : MOD_NONE; + keymap_mods |= term->wl.kbd.shift ? MOD_SHIFT : MOD_NONE; + keymap_mods |= term->wl.kbd.alt ? MOD_ALT : MOD_NONE; + keymap_mods |= term->wl.kbd.ctrl ? MOD_CTRL : MOD_NONE; + keymap_mods |= term->wl.kbd.meta ? MOD_META : MOD_NONE; if (effective_mods == shift) { if (sym == XKB_KEY_Page_Up) { @@ -268,11 +268,11 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, if (compose_status == XKB_COMPOSE_COMPOSED) { count = xkb_compose_state_get_utf8( - term->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); - xkb_compose_state_reset(term->kbd.xkb_compose_state); + term->wl.kbd.xkb_compose_state, (char *)buf, sizeof(buf)); + xkb_compose_state_reset(term->wl.kbd.xkb_compose_state); } else { count = xkb_state_key_get_utf8( - term->kbd.xkb_state, key, (char *)buf, sizeof(buf)); + term->wl.kbd.xkb_state, key, (char *)buf, sizeof(buf)); } if (count > 0) { @@ -338,17 +338,17 @@ keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, mods_depressed, mods_latched, mods_locked, group); xkb_state_update_mask( - term->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); + term->wl.kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); /* Update state of modifiers we're interrested in for e.g mouse events */ - term->kbd.shift = xkb_state_mod_index_is_active( - term->kbd.xkb_state, term->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); - term->kbd.alt = xkb_state_mod_index_is_active( - term->kbd.xkb_state, term->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); - term->kbd.ctrl = xkb_state_mod_index_is_active( - term->kbd.xkb_state, term->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); - term->kbd.meta = xkb_state_mod_index_is_active( - term->kbd.xkb_state, term->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); + term->wl.kbd.shift = xkb_state_mod_index_is_active( + term->wl.kbd.xkb_state, term->wl.kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); + term->wl.kbd.alt = xkb_state_mod_index_is_active( + term->wl.kbd.xkb_state, term->wl.kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); + term->wl.kbd.ctrl = xkb_state_mod_index_is_active( + term->wl.kbd.xkb_state, term->wl.kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); + term->wl.kbd.meta = xkb_state_mod_index_is_active( + term->wl.kbd.xkb_state, term->wl.kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); } static void @@ -357,8 +357,8 @@ keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, { struct terminal *term = data; LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay); - term->kbd.repeat.rate = rate; - term->kbd.repeat.delay = delay; + term->wl.kbd.repeat.rate = rate; + term->wl.kbd.repeat.delay = delay; } const struct wl_keyboard_listener keyboard_listener = { @@ -430,7 +430,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, term_mouse_motion( term, term->mouse.button, term->mouse.row, term->mouse.col, - term->kbd.shift, term->kbd.alt, term->kbd.ctrl); + term->wl.kbd.shift, term->wl.kbd.alt, term->wl.kbd.ctrl); } static void @@ -467,7 +467,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, case 2: selection_mark_word(term, term->mouse.col, term->mouse.row, - term->kbd.ctrl, serial); + term->wl.kbd.ctrl, serial); break; case 3: @@ -484,7 +484,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, term->mouse.last_button = button; term->mouse.last_time = now; term_mouse_down(term, button, term->mouse.row, term->mouse.col, - term->kbd.shift, term->kbd.alt, term->kbd.ctrl); + term->wl.kbd.shift, term->wl.kbd.alt, term->wl.kbd.ctrl); break; } @@ -496,7 +496,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, term->mouse.button = 0; /* For motion events */ term_mouse_up(term, button, term->mouse.row, term->mouse.col, - term->kbd.shift, term->kbd.alt, term->kbd.ctrl); + term->wl.kbd.shift, term->wl.kbd.alt, term->wl.kbd.ctrl); break; } } @@ -520,7 +520,7 @@ mouse_scroll(struct terminal *term, int amount) */ xkb_keycode_t key = xkb_keymap_key_by_name( - term->kbd.xkb_keymap, button == BTN_BACK ? "UP" : "DOWN"); + term->wl.kbd.xkb_keymap, button == BTN_BACK ? "UP" : "DOWN"); for (int i = 0; i < amount; i++) keyboard_key(term, NULL, term->input_serial, 0, key - 8, XKB_KEY_DOWN); @@ -528,7 +528,7 @@ mouse_scroll(struct terminal *term, int amount) } else { for (int i = 0; i < amount; i++) term_mouse_down(term, button, term->mouse.row, term->mouse.col, - term->kbd.shift, term->kbd.alt, term->kbd.ctrl); + term->wl.kbd.shift, term->wl.kbd.alt, term->wl.kbd.ctrl); scrollback(term, amount); } diff --git a/kbd.c b/kbd.c deleted file mode 100644 index c38e77ba..00000000 --- a/kbd.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "kbd.h" - -#include - -void -kbd_destroy(struct kbd *kbd) -{ - if (kbd->xkb_compose_state != NULL) - xkb_compose_state_unref(kbd->xkb_compose_state); - if (kbd->xkb_compose_table != NULL) - xkb_compose_table_unref(kbd->xkb_compose_table); - if (kbd->xkb_keymap != NULL) - xkb_keymap_unref(kbd->xkb_keymap); - if (kbd->xkb_state != NULL) - xkb_state_unref(kbd->xkb_state); - if (kbd->xkb != NULL) - xkb_context_unref(kbd->xkb); -} diff --git a/kbd.h b/kbd.h deleted file mode 100644 index 06de5b50..00000000 --- a/kbd.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include - -#include - -struct kbd { - struct xkb_context *xkb; - struct xkb_keymap *xkb_keymap; - struct xkb_state *xkb_state; - struct xkb_compose_table *xkb_compose_table; - struct xkb_compose_state *xkb_compose_state; - struct { - int fd; - - bool dont_re_repeat; - int32_t delay; - int32_t rate; - uint32_t key; - } repeat; - - xkb_mod_index_t mod_shift; - xkb_mod_index_t mod_alt; - xkb_mod_index_t mod_ctrl; - xkb_mod_index_t mod_meta; - - /* Enabled modifiers */ - bool shift; - bool alt; - bool ctrl; - bool meta; -}; - -void kbd_destroy(struct kbd *kbd); diff --git a/main.c b/main.c index 7c8de292..90f9ef2a 100644 --- a/main.c +++ b/main.c @@ -492,7 +492,7 @@ fdm_repeat(struct fdm *fdm, int fd, int events, void *data) struct terminal *term = data; uint64_t expiration_count; ssize_t ret = read( - term->kbd.repeat.fd, &expiration_count, sizeof(expiration_count)); + term->wl.kbd.repeat.fd, &expiration_count, sizeof(expiration_count)); if (ret < 0) { if (errno == EAGAIN) @@ -502,10 +502,10 @@ fdm_repeat(struct fdm *fdm, int fd, int events, void *data) return false; } - term->kbd.repeat.dont_re_repeat = true; + term->wl.kbd.repeat.dont_re_repeat = true; for (size_t i = 0; i < expiration_count; i++) - input_repeat(term, term->kbd.repeat.key); - term->kbd.repeat.dont_re_repeat = false; + input_repeat(term, term->wl.kbd.repeat.key); + term->wl.kbd.repeat.dont_re_repeat = false; return true; } @@ -742,11 +742,6 @@ main(int argc, char *const *argv) //.background = conf.colors.bg }, }, - .kbd = { - .repeat = { - .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), - }, - }, .colors = { .default_fg = conf.colors.fg, .default_bg = conf.colors.bg, @@ -788,6 +783,13 @@ 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 = { + .kbd = { + .repeat = { + .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), + }, + }, + }, .render = { .scrollback_lines = conf.scrollback_lines, .workers = { @@ -833,7 +835,7 @@ main(int argc, char *const *argv) goto out; } - if (term.flash.fd == -1 || term.blink.fd == -1 || term.kbd.repeat.fd == -1) { + if (term.flash.fd == -1 || term.blink.fd == -1 || term.wl.kbd.repeat.fd == -1) { LOG_ERR("failed to create timers"); goto out; } @@ -1108,7 +1110,7 @@ main(int argc, char *const *argv) fdm_add(fdm, wl_display_get_fd(term.wl.display), EPOLLIN, &fdm_wayl, &term); fdm_add(fdm, term.ptmx, EPOLLIN, &fdm_ptmx, &term); - fdm_add(fdm, term.kbd.repeat.fd, EPOLLIN, &fdm_repeat, &term); + fdm_add(fdm, term.wl.kbd.repeat.fd, EPOLLIN, &fdm_repeat, &term); fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); fdm_add(fdm, term.blink.fd, EPOLLIN, &fdm_blink, &term); fdm_add(fdm, term.delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, &term); @@ -1127,7 +1129,7 @@ out: if (fdm != NULL) { fdm_del(fdm, wl_display_get_fd(term.wl.display)); fdm_del(fdm, term.ptmx); - fdm_del(fdm, term.kbd.repeat.fd); + fdm_del(fdm, term.wl.kbd.repeat.fd); fdm_del(fdm, term.flash.fd); fdm_del(fdm, term.blink.fd); fdm_del(fdm, term.delayed_render_timer.lower_fd); @@ -1150,7 +1152,6 @@ out: shm_fini(); - kbd_destroy(&term.kbd); wayl_win_destroy(&term.window); wayl_destroy(&term.wl); @@ -1174,8 +1175,8 @@ out: close(term.flash.fd); if (term.blink.fd != -1) close(term.blink.fd); - if (term.kbd.repeat.fd != -1) - close(term.kbd.repeat.fd); + if (term.wl.kbd.repeat.fd != -1) + close(term.wl.kbd.repeat.fd); if (term.ptmx != -1) close(term.ptmx); diff --git a/meson.build b/meson.build index cc6bc7f4..da91ff91 100644 --- a/meson.build +++ b/meson.build @@ -72,7 +72,6 @@ executable( 'font.c', 'font.h', 'grid.c', 'grid.h', 'input.c', 'input.h', - 'kbd.c', 'kbd.h', 'log.c', 'log.h', 'main.c', 'osc.c', 'osc.h', diff --git a/search.c b/search.c index 59ac0152..e9da3770 100644 --- a/search.c +++ b/search.c @@ -294,13 +294,13 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask { LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods); - const xkb_mod_mask_t ctrl = 1 << term->kbd.mod_ctrl; - const xkb_mod_mask_t alt = 1 << term->kbd.mod_alt; - //const xkb_mod_mask_t shift = 1 << term->kbd.mod_shift; - //const xkb_mod_mask_t meta = 1 << term->kbd.mod_meta; + const xkb_mod_mask_t ctrl = 1 << term->wl.kbd.mod_ctrl; + const xkb_mod_mask_t alt = 1 << term->wl.kbd.mod_alt; + //const xkb_mod_mask_t shift = 1 << term->wl.kbd.mod_shift; + //const xkb_mod_mask_t meta = 1 << term->wl.kbd.mod_meta; enum xkb_compose_status compose_status = xkb_compose_state_get_status( - term->kbd.xkb_compose_state); + term->wl.kbd.xkb_compose_state); /* Cancel search */ if ((mods == 0 && sym == XKB_KEY_Escape) || @@ -449,11 +449,11 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask if (compose_status == XKB_COMPOSE_COMPOSED) { count = xkb_compose_state_get_utf8( - term->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); - xkb_compose_state_reset(term->kbd.xkb_compose_state); + term->wl.kbd.xkb_compose_state, (char *)buf, sizeof(buf)); + xkb_compose_state_reset(term->wl.kbd.xkb_compose_state); } else { count = xkb_state_key_get_utf8( - term->kbd.xkb_state, key, (char *)buf, sizeof(buf)); + term->wl.kbd.xkb_state, key, (char *)buf, sizeof(buf)); } const char *src = (const char *)buf; diff --git a/selection.c b/selection.c index e53ace8b..33875e94 100644 --- a/selection.c +++ b/selection.c @@ -22,7 +22,7 @@ selection_enabled(const struct terminal *term) { return term->mouse_tracking == MOUSE_NONE || - term->kbd.shift || + term->wl.kbd.shift || term->is_searching; } diff --git a/terminal.c b/terminal.c index 95a60682..ada701cc 100644 --- a/terminal.c +++ b/terminal.c @@ -480,7 +480,7 @@ void term_mouse_down(struct terminal *term, int button, int row, int col, bool shift, bool alt, bool ctrl) { - if (term->kbd.shift) { + if (term->wl.kbd.shift) { /* "raw" mouse mode */ return; } @@ -513,7 +513,7 @@ void term_mouse_up(struct terminal *term, int button, int row, int col, bool shift, bool alt, bool ctrl) { - if (term->kbd.shift) { + if (term->wl.kbd.shift) { /* "raw" mouse mode */ return; } @@ -551,7 +551,7 @@ void term_mouse_motion(struct terminal *term, int button, int row, int col, bool shift, bool alt, bool ctrl) { - if (term->kbd.shift) { + if (term->wl.kbd.shift) { /* "raw" mouse mode */ return; } diff --git a/terminal.h b/terminal.h index 7ee45ca7..cd8d7768 100644 --- a/terminal.h +++ b/terminal.h @@ -11,7 +11,6 @@ #include "font.h" #include "tllist.h" #include "wayland.h" -#include "kbd.h" #define likely(c) __builtin_expect(!!(c), 1) #define unlikely(c) __builtin_expect(!!(c), 0) @@ -176,7 +175,6 @@ struct terminal { } blink; struct vt vt; - struct kbd kbd; int scale; int width; /* pixels */ diff --git a/wayland.c b/wayland.c index 96ba3641..e6701de6 100644 --- a/wayland.c +++ b/wayland.c @@ -34,6 +34,17 @@ wayl_destroy(struct wayland *wayl) if (wayl->xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(wayl->xdg_output_manager); + if (wayl->kbd.xkb_compose_state != NULL) + xkb_compose_state_unref(wayl->kbd.xkb_compose_state); + if (wayl->kbd.xkb_compose_table != NULL) + xkb_compose_table_unref(wayl->kbd.xkb_compose_table); + if (wayl->kbd.xkb_keymap != NULL) + xkb_keymap_unref(wayl->kbd.xkb_keymap); + if (wayl->kbd.xkb_state != NULL) + xkb_state_unref(wayl->kbd.xkb_state); + if (wayl->kbd.xkb != NULL) + xkb_context_unref(wayl->kbd.xkb); + if (wayl->clipboard.data_source != NULL) wl_data_source_destroy(wayl->clipboard.data_source); if (wayl->clipboard.data_offer != NULL) diff --git a/wayland.h b/wayland.h index 1b680b2c..643d5ac5 100644 --- a/wayland.h +++ b/wayland.h @@ -5,6 +5,7 @@ #include #include +#include #include "tllist.h" @@ -27,6 +28,33 @@ struct monitor { float refresh; }; +struct kbd { + struct xkb_context *xkb; + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + struct xkb_compose_table *xkb_compose_table; + struct xkb_compose_state *xkb_compose_state; + struct { + int fd; + + bool dont_re_repeat; + int32_t delay; + int32_t rate; + uint32_t key; + } repeat; + + xkb_mod_index_t mod_shift; + xkb_mod_index_t mod_alt; + xkb_mod_index_t mod_ctrl; + xkb_mod_index_t mod_meta; + + /* Enabled modifiers */ + bool shift; + bool alt; + bool ctrl; + bool meta; +}; + struct wl_clipboard { struct wl_data_source *data_source; struct wl_data_offer *data_offer; @@ -70,12 +98,18 @@ struct wayland { struct wl_keyboard *keyboard; struct zxdg_output_manager_v1 *xdg_output_manager; + /* Keyboard */ + struct kbd kbd; + /* Clipboard */ struct wl_data_device_manager *data_device_manager; struct wl_data_device *data_device; struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; struct zwp_primary_selection_device_v1 *primary_selection_device; + struct wl_clipboard clipboard; + struct wl_primary primary; + /* Cursor */ struct { struct wl_pointer *pointer; @@ -88,10 +122,6 @@ struct wayland { char *theme_name; } pointer; - /* Clipboard */ - struct wl_clipboard clipboard; - struct wl_primary primary; - bool have_argb8888; tll(struct monitor) monitors; /* All available outputs */ }; From eda04d0560f3009a3e9e16317e44a4bb5b9cf35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 17:13:29 +0100 Subject: [PATCH 10/30] wayland: add back-pointer to terminal This can, in the future, be extended to e.g. a list, to support multiple windows. --- main.c | 1 + wayland.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/main.c b/main.c index 90f9ef2a..c1043171 100644 --- a/main.c +++ b/main.c @@ -887,6 +887,7 @@ 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?"); diff --git a/wayland.h b/wayland.h index 643d5ac5..43c4179f 100644 --- a/wayland.h +++ b/wayland.h @@ -87,6 +87,7 @@ struct wl_window { tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */ }; +struct terminal; struct wayland { struct wl_display *display; struct wl_registry *registry; @@ -124,6 +125,9 @@ struct wayland { bool have_argb8888; tll(struct monitor) monitors; /* All available outputs */ + + /* TODO: turn into a list to support multiple windows */ + struct terminal *term; }; /* TODO: return allocated pointer */ From 9f0b1f94c284e76ff1579e7f34f4a7418ef98b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 18:43:07 +0100 Subject: [PATCH 11/30] wayland: change all wayland callbacks to take a wayland pointer Instead of passing the terminal to the wayland callbacks, pass a 'struct wayland' pointer. --- input.c | 260 ++++++++++++++++++++++++++++------------------------ main.c | 138 +++++++++++++++------------- osc.c | 6 +- search.c | 2 +- selection.c | 8 +- terminal.h | 14 --- wayland.c | 25 ++++- wayland.h | 31 ++++++- 8 files changed, 269 insertions(+), 215 deletions(-) diff --git a/input.c b/input.c index 975923a3..56da84d9 100644 --- a/input.c +++ b/input.c @@ -30,50 +30,50 @@ static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { - struct terminal *term = data; + struct wayland *wayl = data; char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); /* TODO: free old context + keymap */ - if (term->wl.kbd.xkb_compose_state != NULL) { - xkb_compose_state_unref(term->wl.kbd.xkb_compose_state); - term->wl.kbd.xkb_compose_state = NULL; + if (wayl->kbd.xkb_compose_state != NULL) { + xkb_compose_state_unref(wayl->kbd.xkb_compose_state); + wayl->kbd.xkb_compose_state = NULL; } - if (term->wl.kbd.xkb_compose_table != NULL) { - xkb_compose_table_unref(term->wl.kbd.xkb_compose_table); - term->wl.kbd.xkb_compose_table = NULL; + if (wayl->kbd.xkb_compose_table != NULL) { + xkb_compose_table_unref(wayl->kbd.xkb_compose_table); + wayl->kbd.xkb_compose_table = NULL; } - if (term->wl.kbd.xkb_keymap != NULL) { - xkb_keymap_unref(term->wl.kbd.xkb_keymap); - term->wl.kbd.xkb_keymap = NULL; + if (wayl->kbd.xkb_keymap != NULL) { + xkb_keymap_unref(wayl->kbd.xkb_keymap); + wayl->kbd.xkb_keymap = NULL; } - if (term->wl.kbd.xkb_state != NULL) { - xkb_state_unref(term->wl.kbd.xkb_state); - term->wl.kbd.xkb_state = NULL; + if (wayl->kbd.xkb_state != NULL) { + xkb_state_unref(wayl->kbd.xkb_state); + wayl->kbd.xkb_state = NULL; } - if (term->wl.kbd.xkb != NULL) { - xkb_context_unref(term->wl.kbd.xkb); - term->wl.kbd.xkb = NULL; + if (wayl->kbd.xkb != NULL) { + xkb_context_unref(wayl->kbd.xkb); + wayl->kbd.xkb = NULL; } - term->wl.kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - term->wl.kbd.xkb_keymap = xkb_keymap_new_from_string( - term->wl.kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, + wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + wayl->kbd.xkb_keymap = xkb_keymap_new_from_string( + wayl->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); /* TODO: initialize in enter? */ - term->wl.kbd.xkb_state = xkb_state_new(term->wl.kbd.xkb_keymap); + wayl->kbd.xkb_state = xkb_state_new(wayl->kbd.xkb_keymap); - term->wl.kbd.mod_shift = xkb_keymap_mod_get_index(term->wl.kbd.xkb_keymap, "Shift"); - term->wl.kbd.mod_alt = xkb_keymap_mod_get_index(term->wl.kbd.xkb_keymap, "Mod1") ; - term->wl.kbd.mod_ctrl = xkb_keymap_mod_get_index(term->wl.kbd.xkb_keymap, "Control"); - term->wl.kbd.mod_meta = xkb_keymap_mod_get_index(term->wl.kbd.xkb_keymap, "Mod4"); + wayl->kbd.mod_shift = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Shift"); + wayl->kbd.mod_alt = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod1") ; + wayl->kbd.mod_ctrl = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Control"); + wayl->kbd.mod_meta = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod4"); /* Compose (dead keys) */ - term->wl.kbd.xkb_compose_table = xkb_compose_table_new_from_locale( - term->wl.kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); - term->wl.kbd.xkb_compose_state = xkb_compose_state_new( - term->wl.kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + wayl->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( + wayl->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); + wayl->kbd.xkb_compose_state = xkb_compose_state_new( + wayl->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); munmap(map_str, size); close(fd); @@ -83,20 +83,22 @@ static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - struct terminal *term = data; - term->input_serial = serial; - term_focus_in(term); + struct wayland *wayl = data; + wayl->input_serial = serial; + wayl->focused = wayl_terminal_from_surface(wayl, surface); + assert(wayl->focused != NULL); + term_focus_in(wayl->focused); } static bool -start_repeater(struct terminal *term, uint32_t key) +start_repeater(struct wayland *wayl, uint32_t key) { - if (term->wl.kbd.repeat.dont_re_repeat) + if (wayl->kbd.repeat.dont_re_repeat) return true; struct itimerspec t = { - .it_value = {.tv_sec = 0, .tv_nsec = term->wl.kbd.repeat.delay * 1000000}, - .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / term->wl.kbd.repeat.rate}, + .it_value = {.tv_sec = 0, .tv_nsec = wayl->kbd.repeat.delay * 1000000}, + .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / wayl->kbd.repeat.rate}, }; if (t.it_value.tv_nsec >= 1000000000) { @@ -107,22 +109,22 @@ start_repeater(struct terminal *term, uint32_t key) t.it_interval.tv_sec += t.it_interval.tv_nsec / 1000000000; t.it_interval.tv_nsec %= 1000000000; } - if (timerfd_settime(term->wl.kbd.repeat.fd, 0, &t, NULL) < 0) { + if (timerfd_settime(wayl->kbd.repeat.fd, 0, &t, NULL) < 0) { LOG_ERRNO("failed to arm keyboard repeat timer"); return false; } - term->wl.kbd.repeat.key = key; + wayl->kbd.repeat.key = key; return true; } static bool -stop_repeater(struct terminal *term, uint32_t key) +stop_repeater(struct wayland *wayl, uint32_t key) { - if (key != -1 && key != term->wl.kbd.repeat.key) + if (key != -1 && key != wayl->kbd.repeat.key) return true; - if (timerfd_settime(term->wl.kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) { + if (timerfd_settime(wayl->kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) { LOG_ERRNO("failed to disarm keyboard repeat timer"); return false; } @@ -134,30 +136,33 @@ static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { - struct terminal *term = data; + struct wayland *wayl = data; - stop_repeater(term, -1); - term_focus_out(term); + stop_repeater(wayl, -1); + term_focus_out(wayl->focused); + wayl->focused = NULL; } static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - struct terminal *term = data; + struct wayland *wayl = data; + struct terminal *term = wayl->focused; + assert(term != NULL); - const xkb_mod_mask_t ctrl = 1 << term->wl.kbd.mod_ctrl; - const xkb_mod_mask_t alt = 1 << term->wl.kbd.mod_alt; - const xkb_mod_mask_t shift = 1 << term->wl.kbd.mod_shift; - const xkb_mod_mask_t meta = 1 << term->wl.kbd.mod_meta; + const xkb_mod_mask_t ctrl = 1 << wayl->kbd.mod_ctrl; + const xkb_mod_mask_t alt = 1 << wayl->kbd.mod_alt; + const xkb_mod_mask_t shift = 1 << wayl->kbd.mod_shift; + const xkb_mod_mask_t meta = 1 << wayl->kbd.mod_meta; if (state == XKB_KEY_UP) { - stop_repeater(term, key); + stop_repeater(wayl, key); return; } key += 8; - xkb_keysym_t sym = xkb_state_key_get_one_sym(term->wl.kbd.xkb_state, key); + xkb_keysym_t sym = xkb_state_key_get_one_sym(wayl->kbd.xkb_state, key); #if 0 char foo[100]; @@ -165,22 +170,22 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, LOG_INFO("%s", foo); #endif - xkb_compose_state_feed(term->wl.kbd.xkb_compose_state, sym); + xkb_compose_state_feed(wayl->kbd.xkb_compose_state, sym); enum xkb_compose_status compose_status = xkb_compose_state_get_status( - term->wl.kbd.xkb_compose_state); + wayl->kbd.xkb_compose_state); if (compose_status == XKB_COMPOSE_COMPOSING) return; xkb_mod_mask_t mods = xkb_state_serialize_mods( - term->wl.kbd.xkb_state, XKB_STATE_MODS_DEPRESSED); - //xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(term->wl.kbd.xkb_state, key); + wayl->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED); + //xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(wayl->kbd.xkb_state, key); xkb_mod_mask_t consumed = 0x0; xkb_mod_mask_t significant = ctrl | alt | shift | meta; xkb_mod_mask_t effective_mods = mods & ~consumed & significant; if (term->is_searching) { - start_repeater(term, key - 8); + start_repeater(wayl, key - 8); search_input(term, key, sym, effective_mods); return; } @@ -188,7 +193,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, #if 0 for (size_t i = 0; i < 32; i++) { if (mods & (1 << i)) { - LOG_INFO("%s", xkb_keymap_mod_get_name(term->wl.kbd.xkb_keymap, i)); + LOG_INFO("%s", xkb_keymap_mod_get_name(wayl->kbd.xkb_keymap, i)); } } #endif @@ -200,10 +205,10 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, bool found_map = false; enum modifier keymap_mods = MOD_NONE; - keymap_mods |= term->wl.kbd.shift ? MOD_SHIFT : MOD_NONE; - keymap_mods |= term->wl.kbd.alt ? MOD_ALT : MOD_NONE; - keymap_mods |= term->wl.kbd.ctrl ? MOD_CTRL : MOD_NONE; - keymap_mods |= term->wl.kbd.meta ? MOD_META : MOD_NONE; + keymap_mods |= wayl->kbd.shift ? MOD_SHIFT : MOD_NONE; + keymap_mods |= wayl->kbd.alt ? MOD_ALT : MOD_NONE; + keymap_mods |= wayl->kbd.ctrl ? MOD_CTRL : MOD_NONE; + keymap_mods |= wayl->kbd.meta ? MOD_META : MOD_NONE; if (effective_mods == shift) { if (sym == XKB_KEY_Page_Up) { @@ -268,11 +273,11 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, if (compose_status == XKB_COMPOSE_COMPOSED) { count = xkb_compose_state_get_utf8( - term->wl.kbd.xkb_compose_state, (char *)buf, sizeof(buf)); - xkb_compose_state_reset(term->wl.kbd.xkb_compose_state); + wayl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); + xkb_compose_state_reset(wayl->kbd.xkb_compose_state); } else { count = xkb_state_key_get_utf8( - term->wl.kbd.xkb_state, key, (char *)buf, sizeof(buf)); + wayl->kbd.xkb_state, key, (char *)buf, sizeof(buf)); } if (count > 0) { @@ -324,7 +329,7 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, } } - start_repeater(term, key - 8); + start_repeater(wayl, key - 8); } static void @@ -332,33 +337,33 @@ keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { - struct terminal *term = data; + struct wayland *wayl = data; LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u", mods_depressed, mods_latched, mods_locked, group); xkb_state_update_mask( - term->wl.kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); + wayl->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); /* Update state of modifiers we're interrested in for e.g mouse events */ - term->wl.kbd.shift = xkb_state_mod_index_is_active( - term->wl.kbd.xkb_state, term->wl.kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); - term->wl.kbd.alt = xkb_state_mod_index_is_active( - term->wl.kbd.xkb_state, term->wl.kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); - term->wl.kbd.ctrl = xkb_state_mod_index_is_active( - term->wl.kbd.xkb_state, term->wl.kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); - term->wl.kbd.meta = xkb_state_mod_index_is_active( - term->wl.kbd.xkb_state, term->wl.kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); + wayl->kbd.shift = xkb_state_mod_index_is_active( + wayl->kbd.xkb_state, wayl->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); + wayl->kbd.alt = xkb_state_mod_index_is_active( + wayl->kbd.xkb_state, wayl->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); + wayl->kbd.ctrl = xkb_state_mod_index_is_active( + wayl->kbd.xkb_state, wayl->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); + wayl->kbd.meta = xkb_state_mod_index_is_active( + wayl->kbd.xkb_state, wayl->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); } static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { - struct terminal *term = data; + struct wayland *wayl = data; LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay); - term->wl.kbd.repeat.rate = rate; - term->wl.kbd.repeat.delay = delay; + wayl->kbd.repeat.rate = rate; + wayl->kbd.repeat.delay = delay; } const struct wl_keyboard_listener keyboard_listener = { @@ -373,7 +378,7 @@ const struct wl_keyboard_listener keyboard_listener = { void input_repeat(struct terminal *term, uint32_t key) { - keyboard_key(term, NULL, 0, 0, key, XKB_KEY_DOWN); + keyboard_key(&term->wl, NULL, 0, 0, key, XKB_KEY_DOWN); } static void @@ -381,13 +386,16 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { - struct terminal *term = data; + struct wayland *wayl = data; + struct terminal *term = wayl_terminal_from_surface(wayl, surface); + + wayl->moused = term; int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; - term->mouse.col = x / term->cell_width; - term->mouse.row = y / term->cell_height; + wayl->mouse.col = x / term->cell_width; + wayl->mouse.row = y / term->cell_height; render_update_cursor_surface(term); } @@ -396,13 +404,17 @@ static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { + struct wayland *wayl = data; + wayl->moused = NULL; } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - struct terminal *term = data; + struct wayland *wayl = data; + struct terminal *term = wayl->moused; + assert(term != NULL); int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; @@ -413,24 +425,24 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, if (col < 0 || row < 0 || col >= term->cols || row >= term->rows) return; - bool update_selection = term->mouse.button == BTN_LEFT; + bool update_selection = wayl->mouse.button == BTN_LEFT; bool update_selection_early = term->selection.end.row == -1; if (update_selection && update_selection_early) selection_update(term, col, row); - if (col == term->mouse.col && row == term->mouse.row) + if (col == wayl->mouse.col && row == wayl->mouse.row) return; - term->mouse.col = col; - term->mouse.row = row; + wayl->mouse.col = col; + wayl->mouse.row = row; if (update_selection && !update_selection_early) selection_update(term, col, row); term_mouse_motion( - term, term->mouse.button, term->mouse.row, term->mouse.col, - term->wl.kbd.shift, term->wl.kbd.alt, term->wl.kbd.ctrl); + term, wayl->mouse.button, wayl->mouse.row, wayl->mouse.col, + wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl); } static void @@ -439,7 +451,9 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, { LOG_DBG("BUTTON: button=%x, state=%u", button, state); - struct terminal *term = data; + struct wayland *wayl = data; + struct terminal *term = wayl->moused; + assert(term != NULL); search_cancel(term); @@ -448,43 +462,43 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, /* Time since last click */ struct timeval now, since_last; gettimeofday(&now, NULL); - timersub(&now, &term->mouse.last_time, &since_last); + timersub(&now, &wayl->mouse.last_time, &since_last); /* Double- or triple click? */ - if (button == term->mouse.last_button && + if (button == wayl->mouse.last_button && since_last.tv_sec == 0 && since_last.tv_usec <= 300 * 1000) { - term->mouse.count++; + wayl->mouse.count++; } else - term->mouse.count = 1; + wayl->mouse.count = 1; if (button == BTN_LEFT) { - switch (term->mouse.count) { + switch (wayl->mouse.count) { case 1: - selection_start(term, term->mouse.col, term->mouse.row); + selection_start(term, wayl->mouse.col, wayl->mouse.row); break; case 2: - selection_mark_word(term, term->mouse.col, term->mouse.row, - term->wl.kbd.ctrl, serial); + selection_mark_word(term, wayl->mouse.col, wayl->mouse.row, + wayl->kbd.ctrl, serial); break; case 3: - selection_mark_row(term, term->mouse.row, serial); + selection_mark_row(term, wayl->mouse.row, serial); break; } } else { - if (term->mouse.count == 1 && button == BTN_MIDDLE && selection_enabled(term)) + if (wayl->mouse.count == 1 && button == BTN_MIDDLE && selection_enabled(term)) selection_from_primary(term); selection_cancel(term); } - term->mouse.button = button; /* For motion events */ - term->mouse.last_button = button; - term->mouse.last_time = now; - term_mouse_down(term, button, term->mouse.row, term->mouse.col, - term->wl.kbd.shift, term->wl.kbd.alt, term->wl.kbd.ctrl); + wayl->mouse.button = button; /* For motion events */ + wayl->mouse.last_button = button; + wayl->mouse.last_time = now; + term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col, + wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl); break; } @@ -494,16 +508,19 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, else selection_finalize(term, serial); - term->mouse.button = 0; /* For motion events */ - term_mouse_up(term, button, term->mouse.row, term->mouse.col, - term->wl.kbd.shift, term->wl.kbd.alt, term->wl.kbd.ctrl); + wayl->mouse.button = 0; /* For motion events */ + term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col, + wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl); break; } } static void -mouse_scroll(struct terminal *term, int amount) +mouse_scroll(struct wayland *wayl, int amount) { + struct terminal *term = wayl->moused; + assert(term != NULL); + int button = amount < 0 ? BTN_BACK : BTN_FORWARD; void (*scrollback)(struct terminal *term, int rows) @@ -520,15 +537,15 @@ mouse_scroll(struct terminal *term, int amount) */ xkb_keycode_t key = xkb_keymap_key_by_name( - term->wl.kbd.xkb_keymap, button == BTN_BACK ? "UP" : "DOWN"); + wayl->kbd.xkb_keymap, button == BTN_BACK ? "UP" : "DOWN"); for (int i = 0; i < amount; i++) - keyboard_key(term, NULL, term->input_serial, 0, key - 8, XKB_KEY_DOWN); - keyboard_key(term, NULL, term->input_serial, 0, key - 8, XKB_KEY_UP); + keyboard_key(term, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_DOWN); + keyboard_key(term, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_UP); } else { for (int i = 0; i < amount; i++) - term_mouse_down(term, button, term->mouse.row, term->mouse.col, - term->wl.kbd.shift, term->wl.kbd.alt, term->wl.kbd.ctrl); + term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col, + wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl); scrollback(term, amount); } @@ -541,11 +558,12 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; - struct terminal *term = data; - if (term->mouse.have_discrete) + struct wayland *wayl = data; + + if (wayl->mouse.have_discrete) return; - mouse_scroll(term, wl_fixed_to_int(value)); + mouse_scroll(wayl, wl_fixed_to_int(value)); } static void @@ -555,16 +573,16 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; - struct terminal *term = data; - term->mouse.have_discrete = true; - mouse_scroll(term, discrete); + struct wayland *wayl = data; + wayl->mouse.have_discrete = true; + mouse_scroll(wayl, discrete); } static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { - struct terminal *term = data; - term->mouse.have_discrete = false; + struct wayland *wayl = data; + wayl->mouse.have_discrete = false; } static void diff --git a/main.c b/main.c index c1043171..b70b8420 100644 --- a/main.c +++ b/main.c @@ -49,9 +49,9 @@ static void shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) { - struct terminal *term = data; + struct wayland *wayl = data; if (format == WL_SHM_FORMAT_ARGB8888) - term->wl.have_argb8888 = true; + wayl->have_argb8888 = true; } static const struct wl_shm_listener shm_listener = { @@ -73,26 +73,26 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { - struct terminal *term = data; + struct wayland *wayl = data; - if (term->wl.keyboard != NULL) { - wl_keyboard_release(term->wl.keyboard); - term->wl.keyboard = NULL; + if (wayl->keyboard != NULL) { + wl_keyboard_release(wayl->keyboard); + wayl->keyboard = NULL; } - if (term->wl.pointer.pointer != NULL) { - wl_pointer_release(term->wl.pointer.pointer); - term->wl.pointer.pointer = NULL; + if (wayl->pointer.pointer != NULL) { + wl_pointer_release(wayl->pointer.pointer); + wayl->pointer.pointer = NULL; } if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { - term->wl.keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(term->wl.keyboard, &keyboard_listener, term); + wayl->keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl); } if (caps & WL_SEAT_CAPABILITY_POINTER) { - term->wl.pointer.pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(term->wl.pointer.pointer, &pointer_listener, term); + wayl->pointer.pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl); } } @@ -137,10 +137,12 @@ 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(mon->term, mon->term->width / mon->term->scale, mon->term->height / mon->term->scale); - render_reload_cursor_theme(mon->term); + render_resize(term, term->width / term->scale, term->height / term->scale); + render_reload_cursor_theme(term); } static const struct wl_output_listener output_listener = { @@ -200,72 +202,71 @@ 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 terminal *term = data; + struct wayland *wayl = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { - term->wl.compositor = wl_registry_bind( - term->wl.registry, name, &wl_compositor_interface, 4); + wayl->compositor = wl_registry_bind( + wayl->registry, name, &wl_compositor_interface, 4); } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { - term->wl.sub_compositor = wl_registry_bind( - term->wl.registry, name, &wl_subcompositor_interface, 1); + wayl->sub_compositor = wl_registry_bind( + wayl->registry, name, &wl_subcompositor_interface, 1); } else if (strcmp(interface, wl_shm_interface.name) == 0) { - term->wl.shm = wl_registry_bind( - term->wl.registry, name, &wl_shm_interface, 1); - wl_shm_add_listener(term->wl.shm, &shm_listener, term); - wl_display_roundtrip(term->wl.display); + 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) { - term->window.shell = wl_registry_bind( - term->wl.registry, name, &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(term->window.shell, &xdg_wm_base_listener, term); + 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) - term->window.xdg_decoration_manager = wl_registry_bind( - term->wl.registry, name, &zxdg_decoration_manager_v1_interface, 1); + 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) { - term->wl.seat = wl_registry_bind( - term->wl.registry, name, &wl_seat_interface, 5); - wl_seat_add_listener(term->wl.seat, &seat_listener, term); - wl_display_roundtrip(term->wl.display); + 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) { - term->wl.xdg_output_manager = wl_registry_bind( - term->wl.registry, name, &zxdg_output_manager_v1_interface, min(version, 2)); + 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( - term->wl.registry, name, &wl_output_interface, 3); + wayl->registry, name, &wl_output_interface, 3); tll_push_back( - term->wl.monitors, ((struct monitor){ - .term = term, .output = output})); + wayl->monitors, ((struct monitor){.wayl = wayl, .output = output})); - struct monitor *mon = &tll_back(term->wl.monitors); + struct monitor *mon = &tll_back(wayl->monitors); wl_output_add_listener(output, &output_listener, mon); mon->xdg = zxdg_output_manager_v1_get_xdg_output( - term->wl.xdg_output_manager, mon->output); + wayl->xdg_output_manager, mon->output); zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon); - wl_display_roundtrip(term->wl.display); + wl_display_roundtrip(wayl->display); } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { - term->wl.data_device_manager = wl_registry_bind( - term->wl.registry, name, &wl_data_device_manager_interface, 1); + 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) { - term->wl.primary_selection_device_manager = wl_registry_bind( - term->wl.registry, name, &zwp_primary_selection_device_manager_v1_interface, 1); + wayl->primary_selection_device_manager = wl_registry_bind( + wayl->registry, name, &zwp_primary_selection_device_manager_v1_interface, 1); } } @@ -273,8 +274,10 @@ static void surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { - struct terminal *term = data; - tll_foreach(term->wl.monitors, it) { + 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); @@ -293,7 +296,9 @@ static void surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { - struct terminal *term = data; + 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; @@ -324,17 +329,20 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, if (width <= 0 || height <= 0) return; - struct terminal *term = data; + 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 terminal *term = data; + 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(term->wl.display); + wl_display_roundtrip(wayl->display); } static const struct xdg_toplevel_listener xdg_toplevel_listener = { @@ -393,15 +401,15 @@ static const struct wl_registry_listener registry_listener = { static bool fdm_wayl(struct fdm *fdm, int fd, int events, void *data) { - struct terminal *term = data; - int event_count = wl_display_dispatch(term->wl.display); + struct wayland *wayl = data; + int event_count = wl_display_dispatch(wayl->display); if (events & EPOLLHUP) { LOG_ERR("disconnected from Wayland"); return false; } - return event_count != -1 && !term->quit; + return event_count != -1 && !wayl->term->quit; } static bool @@ -900,7 +908,7 @@ main(int argc, char *const *argv) goto out; } - wl_registry_add_listener(term.wl.registry, ®istry_listener, &term); + wl_registry_add_listener(term.wl.registry, ®istry_listener, &term.wl); wl_display_roundtrip(term.wl.display); if (term.wl.compositor == NULL) { @@ -911,7 +919,7 @@ main(int argc, char *const *argv) LOG_ERR("no shared memory buffers interface"); goto out; } - if (term.window.shell == NULL) { + if (term.wl.shell == NULL) { LOG_ERR("no XDG shell interface"); goto out; } @@ -940,14 +948,14 @@ main(int argc, char *const *argv) /* 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_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); + term.wl.primary_selection_device, &primary_selection_device_listener, &term.wl); } /* Cursor */ @@ -981,24 +989,24 @@ main(int argc, char *const *argv) goto out; } - wl_surface_add_listener(term.window.surface, &surface_listener, &term); + wl_surface_add_listener(term.window.surface, &surface_listener, &term.wl); - term.window.xdg_surface = xdg_wm_base_get_xdg_surface(term.window.shell, term.window.surface); - xdg_surface_add_listener(term.window.xdg_surface, &xdg_surface_listener, &term); + 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); + 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.window.xdg_decoration_manager, term.window.xdg_toplevel); + 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); + 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); @@ -1109,7 +1117,7 @@ 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); + 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); fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); diff --git a/osc.c b/osc.c index 694547c8..b506f0bb 100644 --- a/osc.c +++ b/osc.c @@ -30,14 +30,14 @@ osc_to_clipboard(struct terminal *term, const char *target, switch (*t) { case 'c': { char *copy = strdup(decoded); - if (!text_to_clipboard(term, copy, term->input_serial)) + if (!text_to_clipboard(term, copy, term->wl.input_serial)) free(copy); break; } case 'p': { char *copy = strdup(decoded); - if (!text_to_primary(term, copy, term->input_serial)) + if (!text_to_primary(term, copy, term->wl.input_serial)) free(copy); break; } @@ -130,7 +130,7 @@ osc_from_clipboard(struct terminal *term, const char *source) switch (src) { case 'c': - text_from_clipboard(term, term->input_serial, &from_clipboard_cb, &ctx); + text_from_clipboard(term, term->wl.input_serial, &from_clipboard_cb, &ctx); break; case 'p': diff --git a/search.c b/search.c index e9da3770..5c0ce1b7 100644 --- a/search.c +++ b/search.c @@ -317,7 +317,7 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask /* "Commit" search - copy selection to primary and cancel search */ else if (mods == 0 && sym == XKB_KEY_Return) { - selection_finalize(term, term->input_serial); + selection_finalize(term, term->wl.input_serial); search_cancel_keep_selection(term); return; } diff --git a/selection.c b/selection.c index 33875e94..a3c88307 100644 --- a/selection.c +++ b/selection.c @@ -742,8 +742,8 @@ selection(void *data, struct wl_data_device *wl_data_device, { /* Selection offer from other client */ - struct terminal *term = data; - struct wl_clipboard *clipboard = &term->wl.clipboard; + struct wayland *wayl = data; + struct wl_clipboard *clipboard = &wayl->clipboard; if (clipboard->data_offer != NULL) wl_data_offer_destroy(clipboard->data_offer); @@ -791,8 +791,8 @@ primary_selection(void *data, { /* Selection offer from other client, for primary */ - struct terminal *term = data; - struct wl_primary *primary = &term->wl.primary; + struct wayland *wayl = data; + struct wl_primary *primary = &wayl->primary; if (primary->data_offer != NULL) zwp_primary_selection_offer_v1_destroy(primary->data_offer); diff --git a/terminal.h b/terminal.h index cd8d7768..7d7d1fcd 100644 --- a/terminal.h +++ b/terminal.h @@ -200,19 +200,6 @@ struct terminal { uint32_t default_table[256]; } colors; - struct { - int col; - int row; - int button; - - int count; - int last_button; - struct timeval last_time; - - /* We used a discrete axis event in the current pointer frame */ - bool have_discrete; - } mouse; - struct coord cursor; struct coord saved_cursor; struct coord alt_saved_cursor; @@ -228,7 +215,6 @@ struct terminal { uint32_t cursor; } cursor_color; - uint32_t input_serial; struct { struct coord start; struct coord end; diff --git a/wayland.c b/wayland.c index e6701de6..c3d7657f 100644 --- a/wayland.c +++ b/wayland.c @@ -13,6 +13,7 @@ #include "log.h" #include "tllist.h" +#include "terminal.h" void wayl_init(struct wayland *wayl) @@ -33,6 +34,11 @@ wayl_destroy(struct wayland *wayl) if (wayl->xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(wayl->xdg_output_manager); + if (wayl->shell != NULL) + xdg_wm_base_destroy(wayl->shell); + + if (wayl->xdg_decoration_manager != NULL) + zxdg_decoration_manager_v1_destroy(wayl->xdg_decoration_manager); if (wayl->kbd.xkb_compose_state != NULL) xkb_compose_state_unref(wayl->kbd.xkb_compose_state); @@ -99,14 +105,25 @@ wayl_win_destroy(struct wl_window *win) wl_callback_destroy(win->frame_callback); if (win->xdg_toplevel_decoration != NULL) zxdg_toplevel_decoration_v1_destroy(win->xdg_toplevel_decoration); - if (win->xdg_decoration_manager != NULL) - zxdg_decoration_manager_v1_destroy(win->xdg_decoration_manager); if (win->xdg_toplevel != NULL) xdg_toplevel_destroy(win->xdg_toplevel); if (win->xdg_surface != NULL) xdg_surface_destroy(win->xdg_surface); - if (win->shell != NULL) - xdg_wm_base_destroy(win->shell); if (win->surface != NULL) wl_surface_destroy(win->surface); } + +struct terminal * +wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface) +{ + assert(surface == wayl->term->window.surface); + return wayl->term; +} + +struct terminal * +wayl_terminal_from_xdg_toplevel(struct wayland *wayl, + struct xdg_toplevel *toplevel) +{ + assert(toplevel == wayl->term->window.xdg_toplevel); + return wayl->term; +} diff --git a/wayland.h b/wayland.h index 43c4179f..9b02b84e 100644 --- a/wayland.h +++ b/wayland.h @@ -10,7 +10,8 @@ #include "tllist.h" struct monitor { - struct terminal *term; + //struct terminal *term; + struct wayland *wayl; struct wl_output *output; struct zxdg_output_v1 *xdg; char *name; @@ -71,11 +72,9 @@ struct wl_primary { struct wl_window { struct wl_surface *surface; - struct xdg_wm_base *shell; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; - struct zxdg_decoration_manager_v1 *xdg_decoration_manager; struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; /* Scrollback search */ @@ -99,10 +98,14 @@ struct wayland { struct wl_keyboard *keyboard; struct zxdg_output_manager_v1 *xdg_output_manager; + struct xdg_wm_base *shell; + struct zxdg_decoration_manager_v1 *xdg_decoration_manager; + /* Keyboard */ struct kbd kbd; /* Clipboard */ + uint32_t input_serial; struct wl_data_device_manager *data_device_manager; struct wl_data_device *data_device; struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; @@ -123,15 +126,37 @@ struct wayland { char *theme_name; } pointer; + struct { + int col; + int row; + int button; + + int count; + int last_button; + struct timeval last_time; + + /* We used a discrete axis event in the current pointer frame */ + bool have_discrete; + } mouse; + bool have_argb8888; tll(struct monitor) monitors; /* All available outputs */ /* TODO: turn into a list to support multiple windows */ struct terminal *term; + struct terminal *focused; + struct terminal *moused; }; /* TODO: return allocated pointer */ void wayl_init(struct wayland *wayl); void wayl_destroy(struct wayland *wayl); +struct terminal *wayl_terminal_from_surface( + struct wayland *wayl, struct wl_surface *surface); +struct terminal *wayl_terminal_from_xdg_surface( + struct wayland *wayl, struct xdg_surface *surface); +struct terminal *wayl_terminal_from_xdg_toplevel( + struct wayland *wayl, struct xdg_toplevel *toplevel); + void wayl_win_destroy(struct wl_window *win); From 8c534d0520fd2fa2a505a86c8f2ec6e290e079f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 18:44:58 +0100 Subject: [PATCH 12/30] input: input_repeat() takes a 'struct wayland' pointer --- input.c | 4 ++-- input.h | 4 ++-- main.c | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/input.c b/input.c index 56da84d9..b91fea2f 100644 --- a/input.c +++ b/input.c @@ -376,9 +376,9 @@ const struct wl_keyboard_listener keyboard_listener = { }; void -input_repeat(struct terminal *term, uint32_t key) +input_repeat(struct wayland *wayl, uint32_t key) { - keyboard_key(&term->wl, NULL, 0, 0, key, XKB_KEY_DOWN); + keyboard_key(wayl, NULL, 0, 0, key, XKB_KEY_DOWN); } static void diff --git a/input.h b/input.h index 8ffa1c15..2483389a 100644 --- a/input.h +++ b/input.h @@ -2,9 +2,9 @@ #include -#include "terminal.h" +#include "wayland.h" extern const struct wl_keyboard_listener keyboard_listener; extern const struct wl_pointer_listener pointer_listener; -void input_repeat(struct terminal *term, uint32_t key); +void input_repeat(struct wayland *wayl, uint32_t key); diff --git a/main.c b/main.c index b70b8420..f2fff1e3 100644 --- a/main.c +++ b/main.c @@ -497,10 +497,10 @@ fdm_repeat(struct fdm *fdm, int fd, int events, void *data) if (events & EPOLLHUP) return false; - struct terminal *term = data; + struct wayland *wayl = data; uint64_t expiration_count; ssize_t ret = read( - term->wl.kbd.repeat.fd, &expiration_count, sizeof(expiration_count)); + wayl->kbd.repeat.fd, &expiration_count, sizeof(expiration_count)); if (ret < 0) { if (errno == EAGAIN) @@ -510,10 +510,10 @@ fdm_repeat(struct fdm *fdm, int fd, int events, void *data) return false; } - term->wl.kbd.repeat.dont_re_repeat = true; + wayl->kbd.repeat.dont_re_repeat = true; for (size_t i = 0; i < expiration_count; i++) - input_repeat(term, term->wl.kbd.repeat.key); - term->wl.kbd.repeat.dont_re_repeat = false; + input_repeat(wayl, wayl->kbd.repeat.key); + wayl->kbd.repeat.dont_re_repeat = false; return true; } @@ -1119,7 +1119,7 @@ main(int argc, char *const *argv) 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); + fdm_add(fdm, term.wl.kbd.repeat.fd, EPOLLIN, &fdm_repeat, &term.wl); fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); fdm_add(fdm, term.blink.fd, EPOLLIN, &fdm_blink, &term); fdm_add(fdm, term.delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, &term); From 33e4b8a5b8ceef76156ef5ae179b8e4393457ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 18:46:36 +0100 Subject: [PATCH 13/30] wayland: pull in sys/time.h, for 'struct timeval' --- wayland.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wayland.h b/wayland.h index 9b02b84e..f82d2630 100644 --- a/wayland.h +++ b/wayland.h @@ -3,6 +3,8 @@ #include #include +#include + #include #include #include From 1adab32906b5455781921b779019a6d3f0e4fe56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 18:51:14 +0100 Subject: [PATCH 14/30] term: wayland struct is now not a part of the terminal struct We do however need access to it, so provide a pointer. The difference is that now we can have a *single* wayland instance, but multiple terminal instances. --- main.c | 110 ++++++++++++++++++++++++++-------------------------- osc.c | 6 +-- render.c | 44 ++++++++++----------- search.c | 18 ++++----- selection.c | 42 ++++++++++---------- terminal.c | 6 +-- terminal.h | 2 +- wayland.h | 1 - 8 files changed, 115 insertions(+), 114 deletions(-) diff --git a/main.c b/main.c index f2fff1e3..5dd37dfb 100644 --- a/main.c +++ b/main.c @@ -729,6 +729,14 @@ main(int argc, char *const *argv) 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), @@ -791,13 +799,7 @@ 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 = { - .kbd = { - .repeat = { - .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), - }, - }, - }, + .wl = &wayl, .render = { .scrollback_lines = conf.scrollback_lines, .workers = { @@ -843,7 +845,7 @@ main(int argc, char *const *argv) goto out; } - if (term.flash.fd == -1 || term.blink.fd == -1 || term.wl.kbd.repeat.fd == -1) { + if (term.flash.fd == -1 || term.blink.fd == -1 || term.wl->kbd.repeat.fd == -1) { LOG_ERR("failed to create timers"); goto out; } @@ -895,67 +897,67 @@ 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) { + 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) { + 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); + wl_registry_add_listener(term.wl->registry, ®istry_listener, term.wl); + wl_display_roundtrip(term.wl->display); - if (term.wl.compositor == NULL) { + if (term.wl->compositor == NULL) { LOG_ERR("no compositor"); goto out; } - if (term.wl.shm == NULL) { + if (term.wl->shm == NULL) { LOG_ERR("no shared memory buffers interface"); goto out; } - if (term.wl.shell == NULL) { + if (term.wl->shell == NULL) { LOG_ERR("no XDG shell interface"); goto out; } - if (!term.wl.have_argb8888) { + if (!term.wl->have_argb8888) { LOG_ERR("compositor does not support ARGB surfaces"); goto out; } - if (term.wl.seat == NULL) { + if (term.wl->seat == NULL) { LOG_ERR("no seat available"); goto out; } - if (term.wl.data_device_manager == NULL) { + 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) + if (term.wl->primary_selection_device_manager == NULL) LOG_WARN("no primary selection available"); - tll_foreach(term.wl.monitors, it) { + 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); + 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); + 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); + term.wl->primary_selection_device, &primary_selection_device_listener, term.wl); } /* Cursor */ @@ -973,49 +975,49 @@ main(int argc, char *const *argv) /* 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.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) { + 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); + term.window.surface = wl_compositor_create_surface(term.wl->compositor); if (term.window.surface == NULL) { LOG_ERR("failed to create wayland surface"); goto out; } - wl_surface_add_listener(term.window.surface, &surface_listener, &term.wl); + 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_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_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); + 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); + 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_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); + 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); + wl_display_roundtrip(term.wl->display); if (conf.width == -1) { assert(conf.height == -1); @@ -1026,7 +1028,7 @@ main(int argc, char *const *argv) conf.height = max(conf.height, term.cell_height); render_resize(&term, conf.width, conf.height); - wl_display_dispatch_pending(term.wl.display); + wl_display_dispatch_pending(term.wl->display); { int fork_pipe[2]; @@ -1102,7 +1104,7 @@ main(int argc, char *const *argv) { - int fd = wl_display_get_fd(term.wl.display); + int fd = wl_display_get_fd(term.wl->display); int fd_flags = fcntl(fd, F_GETFL); if (fd_flags == -1) { LOG_ERRNO("failed to set non blocking mode on Wayland display connection"); @@ -1117,16 +1119,16 @@ 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, 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); + fdm_add(fdm, term.wl->kbd.repeat.fd, EPOLLIN, &fdm_repeat, term.wl); fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); fdm_add(fdm, term.blink.fd, EPOLLIN, &fdm_blink, &term); fdm_add(fdm, term.delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, &term); fdm_add(fdm, term.delayed_render_timer.upper_fd, EPOLLIN, &fdm_delayed_render, &term); while (true) { - wl_display_flush(term.wl.display); + wl_display_flush(term.wl->display); if (!fdm_poll(fdm)) break; } @@ -1136,9 +1138,9 @@ main(int argc, char *const *argv) out: if (fdm != NULL) { - fdm_del(fdm, wl_display_get_fd(term.wl.display)); + fdm_del(fdm, wl_display_get_fd(term.wl->display)); fdm_del(fdm, term.ptmx); - fdm_del(fdm, term.wl.kbd.repeat.fd); + fdm_del(fdm, term.wl->kbd.repeat.fd); fdm_del(fdm, term.flash.fd); fdm_del(fdm, term.blink.fd); fdm_del(fdm, term.delayed_render_timer.lower_fd); @@ -1162,7 +1164,7 @@ out: shm_fini(); wayl_win_destroy(&term.window); - wayl_destroy(&term.wl); + wayl_destroy(&wayl); free(term.vt.osc.data); for (int row = 0; row < term.normal.num_rows; row++) @@ -1184,8 +1186,8 @@ out: close(term.flash.fd); if (term.blink.fd != -1) close(term.blink.fd); - if (term.wl.kbd.repeat.fd != -1) - close(term.wl.kbd.repeat.fd); + if (term.wl->kbd.repeat.fd != -1) + close(term.wl->kbd.repeat.fd); if (term.ptmx != -1) close(term.ptmx); diff --git a/osc.c b/osc.c index b506f0bb..e860edcd 100644 --- a/osc.c +++ b/osc.c @@ -30,14 +30,14 @@ osc_to_clipboard(struct terminal *term, const char *target, switch (*t) { case 'c': { char *copy = strdup(decoded); - if (!text_to_clipboard(term, copy, term->wl.input_serial)) + if (!text_to_clipboard(term, copy, term->wl->input_serial)) free(copy); break; } case 'p': { char *copy = strdup(decoded); - if (!text_to_primary(term, copy, term->wl.input_serial)) + if (!text_to_primary(term, copy, term->wl->input_serial)) free(copy); break; } @@ -130,7 +130,7 @@ osc_from_clipboard(struct terminal *term, const char *source) switch (src) { case 'c': - text_from_clipboard(term, term->wl.input_serial, &from_clipboard_cb, &ctx); + text_from_clipboard(term, term->wl->input_serial, &from_clipboard_cb, &ctx); break; case 'p': diff --git a/render.c b/render.c index c2840eb8..75f319bf 100644 --- a/render.c +++ b/render.c @@ -440,7 +440,7 @@ grid_render(struct terminal *term) assert(term->width > 0); assert(term->height > 0); - struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height, 1 + term->render.workers.count); + 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); pixman_image_t *pix = buf->pix; @@ -741,7 +741,7 @@ render_search_box(struct terminal *term) const int width = 2 * margin + max(20, term->search.len) * term->cell_width; const int height = 2 * margin + 1 * term->cell_height; - struct buffer *buf = shm_get_buffer(term->wl.shm, width, height, 1); + struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, 1); /* Background - yellow on empty/match, red on mismatch */ pixman_color_t color = color_hex_to_pixman( @@ -945,28 +945,28 @@ render_set_title(struct terminal *term, const char *title) bool render_reload_cursor_theme(struct terminal *term) { - if (term->wl.pointer.size == 0) + if (term->wl->pointer.size == 0) return true; - if (term->wl.pointer.theme != NULL) { - wl_cursor_theme_destroy(term->wl.pointer.theme); - term->wl.pointer.theme = NULL; - term->wl.pointer.cursor = NULL; + if (term->wl->pointer.theme != NULL) { + wl_cursor_theme_destroy(term->wl->pointer.theme); + term->wl->pointer.theme = NULL; + term->wl->pointer.cursor = NULL; } LOG_DBG("reloading cursor theme: %s@%d", - term->wl.pointer.theme_name, term->wl.pointer.size); + term->wl->pointer.theme_name, term->wl->pointer.size); - term->wl.pointer.theme = wl_cursor_theme_load( - term->wl.pointer.theme_name, term->wl.pointer.size * term->scale, term->wl.shm); - if (term->wl.pointer.theme == NULL) { + term->wl->pointer.theme = wl_cursor_theme_load( + term->wl->pointer.theme_name, term->wl->pointer.size * term->scale, term->wl->shm); + if (term->wl->pointer.theme == NULL) { LOG_ERR("failed to load cursor theme"); return false; } - term->wl.pointer.cursor = wl_cursor_theme_get_cursor( - term->wl.pointer.theme, "left_ptr"); - assert(term->wl.pointer.cursor != NULL); + term->wl->pointer.cursor = wl_cursor_theme_get_cursor( + term->wl->pointer.theme, "left_ptr"); + assert(term->wl->pointer.cursor != NULL); render_update_cursor_surface(term); return true; @@ -975,26 +975,26 @@ render_reload_cursor_theme(struct terminal *term) void render_update_cursor_surface(struct terminal *term) { - if (term->wl.pointer.cursor == NULL) + if (term->wl->pointer.cursor == NULL) return; const int scale = term->scale; - wl_surface_set_buffer_scale(term->wl.pointer.surface, scale); + wl_surface_set_buffer_scale(term->wl->pointer.surface, scale); - struct wl_cursor_image *image = term->wl.pointer.cursor->images[0]; + struct wl_cursor_image *image = term->wl->pointer.cursor->images[0]; wl_surface_attach( - term->wl.pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); + term->wl->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); wl_pointer_set_cursor( - term->wl.pointer.pointer, term->wl.pointer.serial, - term->wl.pointer.surface, + term->wl->pointer.pointer, term->wl->pointer.serial, + term->wl->pointer.surface, image->hotspot_x / scale, image->hotspot_y / scale); wl_surface_damage_buffer( - term->wl.pointer.surface, 0, 0, INT32_MAX, INT32_MAX); + term->wl->pointer.surface, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_commit(term->wl.pointer.surface); + wl_surface_commit(term->wl->pointer.surface); } void diff --git a/search.c b/search.c index 5c0ce1b7..e055b478 100644 --- a/search.c +++ b/search.c @@ -294,13 +294,13 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask { LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods); - const xkb_mod_mask_t ctrl = 1 << term->wl.kbd.mod_ctrl; - const xkb_mod_mask_t alt = 1 << term->wl.kbd.mod_alt; - //const xkb_mod_mask_t shift = 1 << term->wl.kbd.mod_shift; - //const xkb_mod_mask_t meta = 1 << term->wl.kbd.mod_meta; + const xkb_mod_mask_t ctrl = 1 << term->wl->kbd.mod_ctrl; + const xkb_mod_mask_t alt = 1 << term->wl->kbd.mod_alt; + //const xkb_mod_mask_t shift = 1 << term->wl->kbd.mod_shift; + //const xkb_mod_mask_t meta = 1 << term->wl->kbd.mod_meta; enum xkb_compose_status compose_status = xkb_compose_state_get_status( - term->wl.kbd.xkb_compose_state); + term->wl->kbd.xkb_compose_state); /* Cancel search */ if ((mods == 0 && sym == XKB_KEY_Escape) || @@ -317,7 +317,7 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask /* "Commit" search - copy selection to primary and cancel search */ else if (mods == 0 && sym == XKB_KEY_Return) { - selection_finalize(term, term->wl.input_serial); + selection_finalize(term, term->wl->input_serial); search_cancel_keep_selection(term); return; } @@ -449,11 +449,11 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask if (compose_status == XKB_COMPOSE_COMPOSED) { count = xkb_compose_state_get_utf8( - term->wl.kbd.xkb_compose_state, (char *)buf, sizeof(buf)); - xkb_compose_state_reset(term->wl.kbd.xkb_compose_state); + term->wl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); + xkb_compose_state_reset(term->wl->kbd.xkb_compose_state); } else { count = xkb_state_key_get_utf8( - term->wl.kbd.xkb_state, key, (char *)buf, sizeof(buf)); + term->wl->kbd.xkb_state, key, (char *)buf, sizeof(buf)); } const char *src = (const char *)buf; diff --git a/selection.c b/selection.c index a3c88307..d2ed7354 100644 --- a/selection.c +++ b/selection.c @@ -22,7 +22,7 @@ selection_enabled(const struct terminal *term) { return term->mouse_tracking == MOUSE_NONE || - term->wl.kbd.shift || + term->wl->kbd.shift || term->is_searching; } @@ -434,12 +434,12 @@ static const struct zwp_primary_selection_source_v1_listener primary_selection_s bool text_to_clipboard(struct terminal *term, char *text, uint32_t serial) { - struct wl_clipboard *clipboard = &term->wl.clipboard; + struct wl_clipboard *clipboard = &term->wl->clipboard; - if (term->wl.clipboard.data_source != NULL) { + if (term->wl->clipboard.data_source != NULL) { /* Kill previous data source */ assert(clipboard->serial != 0); - wl_data_device_set_selection(term->wl.data_device, NULL, clipboard->serial); + wl_data_device_set_selection(term->wl->data_device, NULL, clipboard->serial); wl_data_source_destroy(clipboard->data_source); free(clipboard->text); @@ -448,7 +448,7 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial) } clipboard->data_source - = wl_data_device_manager_create_data_source(term->wl.data_device_manager); + = wl_data_device_manager_create_data_source(term->wl->data_device_manager); if (clipboard->data_source == NULL) { LOG_ERR("failed to create clipboard data source"); @@ -460,7 +460,7 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial) /* Configure source */ wl_data_source_offer(clipboard->data_source, "text/plain;charset=utf-8"); wl_data_source_add_listener(clipboard->data_source, &data_source_listener, term); - wl_data_device_set_selection(term->wl.data_device, clipboard->data_source, serial); + wl_data_device_set_selection(term->wl->data_device, clipboard->data_source, serial); wl_data_source_set_user_data(clipboard->data_source, clipboard); /* Needed when sending the selection to other client */ @@ -482,7 +482,7 @@ text_from_clipboard(struct terminal *term, uint32_t serial, void (*cb)(const char *data, size_t size, void *user), void *user) { - struct wl_clipboard *clipboard = &term->wl.clipboard; + struct wl_clipboard *clipboard = &term->wl->clipboard; if (clipboard->data_offer == NULL) return; @@ -499,7 +499,7 @@ text_from_clipboard(struct terminal *term, uint32_t serial, /* Give write-end of pipe to other client */ wl_data_offer_receive( clipboard->data_offer, "text/plain;charset=utf-8", write_fd); - wl_display_roundtrip(term->wl.display); + wl_display_roundtrip(term->wl->display); /* Don't keep our copy of the write-end open (or we'll never get EOF) */ close(write_fd); @@ -539,7 +539,7 @@ from_clipboard_cb(const char *data, size_t size, void *user) void selection_from_clipboard(struct terminal *term, uint32_t serial) { - struct wl_clipboard *clipboard = &term->wl.clipboard; + struct wl_clipboard *clipboard = &term->wl->clipboard; if (clipboard->data_offer == NULL) return; @@ -555,18 +555,18 @@ selection_from_clipboard(struct terminal *term, uint32_t serial) bool text_to_primary(struct terminal *term, char *text, uint32_t serial) { - if (term->wl.primary_selection_device_manager == NULL) + if (term->wl->primary_selection_device_manager == NULL) return false; - struct wl_primary *primary = &term->wl.primary; + struct wl_primary *primary = &term->wl->primary; /* TODO: somehow share code with the clipboard equivalent */ - if (term->wl.primary.data_source != NULL) { + if (term->wl->primary.data_source != NULL) { /* Kill previous data source */ assert(primary->serial != 0); zwp_primary_selection_device_v1_set_selection( - term->wl.primary_selection_device, NULL, primary->serial); + term->wl->primary_selection_device, NULL, primary->serial); zwp_primary_selection_source_v1_destroy(primary->data_source); free(primary->text); @@ -576,7 +576,7 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial) primary->data_source = zwp_primary_selection_device_manager_v1_create_source( - term->wl.primary_selection_device_manager); + term->wl->primary_selection_device_manager); if (primary->data_source == NULL) { LOG_ERR("failed to create clipboard data source"); @@ -589,7 +589,7 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial) /* Configure source */ zwp_primary_selection_source_v1_offer(primary->data_source, "text/plain;charset=utf-8"); zwp_primary_selection_source_v1_add_listener(primary->data_source, &primary_selection_source_listener, term); - zwp_primary_selection_device_v1_set_selection(term->wl.primary_selection_device, primary->data_source, serial); + zwp_primary_selection_device_v1_set_selection(term->wl->primary_selection_device, primary->data_source, serial); zwp_primary_selection_source_v1_set_user_data(primary->data_source, primary); /* Needed when sending the selection to other client */ @@ -600,7 +600,7 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial) void selection_to_primary(struct terminal *term, uint32_t serial) { - if (term->wl.primary_selection_device_manager == NULL) + if (term->wl->primary_selection_device_manager == NULL) return; /* Get selection as a string */ @@ -614,10 +614,10 @@ text_from_primary( struct terminal *term, void (*cb)(const char *data, size_t size, void *user), void *user) { - if (term->wl.primary_selection_device_manager == NULL) + if (term->wl->primary_selection_device_manager == NULL) return; - struct wl_primary *primary = &term->wl.primary; + struct wl_primary *primary = &term->wl->primary; if (primary->data_offer == NULL) return; @@ -634,7 +634,7 @@ text_from_primary( /* Give write-end of pipe to other client */ zwp_primary_selection_offer_v1_receive( primary->data_offer, "text/plain;charset=utf-8", write_fd); - wl_display_roundtrip(term->wl.display); + wl_display_roundtrip(term->wl->display); /* Don't keep our copy of the write-end open (or we'll never get EOF) */ close(write_fd); @@ -667,10 +667,10 @@ text_from_primary( void selection_from_primary(struct terminal *term) { - if (term->wl.primary_selection_device_manager == NULL) + if (term->wl->primary_selection_device_manager == NULL) return; - struct wl_clipboard *clipboard = &term->wl.clipboard; + struct wl_clipboard *clipboard = &term->wl->clipboard; if (clipboard->data_offer == NULL) return; diff --git a/terminal.c b/terminal.c index ada701cc..684f4c30 100644 --- a/terminal.c +++ b/terminal.c @@ -480,7 +480,7 @@ void term_mouse_down(struct terminal *term, int button, int row, int col, bool shift, bool alt, bool ctrl) { - if (term->wl.kbd.shift) { + if (term->wl->kbd.shift) { /* "raw" mouse mode */ return; } @@ -513,7 +513,7 @@ void term_mouse_up(struct terminal *term, int button, int row, int col, bool shift, bool alt, bool ctrl) { - if (term->wl.kbd.shift) { + if (term->wl->kbd.shift) { /* "raw" mouse mode */ return; } @@ -551,7 +551,7 @@ void term_mouse_motion(struct terminal *term, int button, int row, int col, bool shift, bool alt, bool ctrl) { - if (term->wl.kbd.shift) { + if (term->wl->kbd.shift) { /* "raw" mouse mode */ return; } diff --git a/terminal.h b/terminal.h index 7d7d1fcd..724bc6f1 100644 --- a/terminal.h +++ b/terminal.h @@ -246,7 +246,7 @@ struct terminal { int max_x_advance; } fextents; - struct wayland wl; + struct wayland *wl; struct wl_window window; struct { diff --git a/wayland.h b/wayland.h index f82d2630..e0f7cd2f 100644 --- a/wayland.h +++ b/wayland.h @@ -12,7 +12,6 @@ #include "tllist.h" struct monitor { - //struct terminal *term; struct wayland *wayl; struct wl_output *output; struct zxdg_output_v1 *xdg; 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 15/30] 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); From 1e75b8955212ad081d95606575db7809ea7ee9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:11:35 +0100 Subject: [PATCH 16/30] wayland: integrate directly with the FDM --- main.c | 16 ---------------- wayland.c | 24 +++++++++++++++++++++++- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/main.c b/main.c index eb91ce5b..fc9c2e8f 100644 --- a/main.c +++ b/main.c @@ -44,20 +44,6 @@ #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) -static bool -fdm_wayl(struct fdm *fdm, int fd, int events, void *data) -{ - struct wayland *wayl = data; - int event_count = wl_display_dispatch(wayl->display); - - if (events & EPOLLHUP) { - LOG_ERR("disconnected from Wayland"); - return false; - } - - return event_count != -1 && !wayl->term->quit; -} - static bool fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) { @@ -650,7 +636,6 @@ main(int argc, char *const *argv) } } - 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); fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); @@ -669,7 +654,6 @@ main(int argc, char *const *argv) out: if (fdm != NULL) { - fdm_del(fdm, wl_display_get_fd(term.wl->display)); fdm_del(fdm, term.ptmx); fdm_del(fdm, term.wl->kbd.repeat.fd); fdm_del(fdm, term.flash.fd); diff --git a/wayland.c b/wayland.c index 227f9fc4..e6b6ad7d 100644 --- a/wayland.c +++ b/wayland.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -378,6 +379,20 @@ static const struct wl_registry_listener registry_listener = { .global_remove = &handle_global_remove, }; +static bool +fdm_wayl(struct fdm *fdm, int fd, int events, void *data) +{ + struct wayland *wayl = data; + int event_count = wl_display_dispatch(wayl->display); + + if (events & EPOLLHUP) { + LOG_ERR("disconnected from Wayland"); + return false; + } + + return event_count != -1 && !wayl->term->quit; +} + struct wayland * wayl_init(struct fdm *fdm) { @@ -476,6 +491,11 @@ wayl_init(struct fdm *fdm) goto out; } + if (!fdm_add(fdm, wl_display_get_fd(wayl->display), EPOLLIN, &fdm_wayl, wayl)) { + LOG_ERR("failed to register Wayland connection with the FDM"); + goto out; + } + return wayl; out: @@ -553,8 +573,10 @@ wayl_destroy(struct wayland *wayl) wl_compositor_destroy(wayl->compositor); if (wayl->registry != NULL) wl_registry_destroy(wayl->registry); - if (wayl->display != NULL) + if (wayl->display != NULL) { + fdm_del(wayl->fdm, wl_display_get_fd(wayl->display)); wl_display_disconnect(wayl->display); + } } struct wl_window * From 251cf9838803b57b630249859514fe837bd11f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:16:12 +0100 Subject: [PATCH 17/30] wayland: move keyboard repeat handling into wayland --- main.c | 31 ------------------------------- wayland.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/main.c b/main.c index fc9c2e8f..1334f5e6 100644 --- a/main.c +++ b/main.c @@ -123,33 +123,6 @@ 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) -{ - if (events & EPOLLHUP) - return false; - - struct wayland *wayl = data; - uint64_t expiration_count; - ssize_t ret = read( - wayl->kbd.repeat.fd, &expiration_count, sizeof(expiration_count)); - - if (ret < 0) { - if (errno == EAGAIN) - return true; - - LOG_ERRNO("failed to read repeat key from repeat timer fd"); - return false; - } - - wayl->kbd.repeat.dont_re_repeat = true; - for (size_t i = 0; i < expiration_count; i++) - input_repeat(wayl, wayl->kbd.repeat.key); - wayl->kbd.repeat.dont_re_repeat = false; - return true; -} - static bool fdm_flash(struct fdm *fdm, int fd, int events, void *data) { @@ -637,7 +610,6 @@ main(int argc, char *const *argv) } fdm_add(fdm, term.ptmx, EPOLLIN, &fdm_ptmx, &term); - fdm_add(fdm, term.wl->kbd.repeat.fd, EPOLLIN, &fdm_repeat, term.wl); fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); fdm_add(fdm, term.blink.fd, EPOLLIN, &fdm_blink, &term); fdm_add(fdm, term.delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, &term); @@ -655,7 +627,6 @@ main(int argc, char *const *argv) out: if (fdm != NULL) { fdm_del(fdm, term.ptmx); - fdm_del(fdm, term.wl->kbd.repeat.fd); fdm_del(fdm, term.flash.fd); fdm_del(fdm, term.blink.fd); fdm_del(fdm, term.delayed_render_timer.lower_fd); @@ -701,8 +672,6 @@ out: close(term.flash.fd); if (term.blink.fd != -1) close(term.blink.fd); - if (term.wl->kbd.repeat.fd != -1) - close(term.wl->kbd.repeat.fd); if (term.ptmx != -1) close(term.ptmx); diff --git a/wayland.c b/wayland.c index e6b6ad7d..faf7cd6c 100644 --- a/wayland.c +++ b/wayland.c @@ -1,6 +1,9 @@ #include "wayland.h" #include +#include +#include + #include #include @@ -393,6 +396,32 @@ fdm_wayl(struct fdm *fdm, int fd, int events, void *data) return event_count != -1 && !wayl->term->quit; } +static bool +fdm_repeat(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct wayland *wayl = data; + uint64_t expiration_count; + ssize_t ret = read( + wayl->kbd.repeat.fd, &expiration_count, sizeof(expiration_count)); + + if (ret < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read repeat key from repeat timer fd"); + return false; + } + + wayl->kbd.repeat.dont_re_repeat = true; + for (size_t i = 0; i < expiration_count; i++) + input_repeat(wayl, wayl->kbd.repeat.key); + wayl->kbd.repeat.dont_re_repeat = false; + return true; +} + struct wayland * wayl_init(struct fdm *fdm) { @@ -496,6 +525,11 @@ wayl_init(struct fdm *fdm) goto out; } + if (!fdm_add(fdm, wayl->kbd.repeat.fd, EPOLLIN, &fdm_repeat, wayl)) { + LOG_ERR("failed to register keyboard repeat timer with the FDM"); + goto out; + } + return wayl; out: @@ -507,6 +541,11 @@ out: void wayl_destroy(struct wayland *wayl) { + if (wayl->kbd.repeat.fd != 0) { + fdm_del(wayl->fdm, wayl->kbd.repeat.fd); + close(wayl->kbd.repeat.fd); + } + tll_foreach(wayl->monitors, it) { free(it->item.name); if (it->item.xdg != NULL) From 6314de554d9b5c3fc5b8141548f3813c1c364c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:16:25 +0100 Subject: [PATCH 18/30] wayl_destroy: early return on NULL --- wayland.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wayland.c b/wayland.c index faf7cd6c..16442303 100644 --- a/wayland.c +++ b/wayland.c @@ -541,6 +541,9 @@ out: void wayl_destroy(struct wayland *wayl) { + if (wayl == NULL) + return; + if (wayl->kbd.repeat.fd != 0) { fdm_del(wayl->fdm, wayl->kbd.repeat.fd); close(wayl->kbd.repeat.fd); From 9a31c1ed964bcf282770d9a2d5bedafc4f6cda5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:16:42 +0100 Subject: [PATCH 19/30] wayland: free wayland/wl_window instances --- wayland.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wayland.c b/wayland.c index 16442303..447534fb 100644 --- a/wayland.c +++ b/wayland.c @@ -619,6 +619,8 @@ wayl_destroy(struct wayland *wayl) fdm_del(wayl->fdm, wl_display_get_fd(wayl->display)); wl_display_disconnect(wayl->display); } + + free(wayl); } struct wl_window * @@ -685,6 +687,7 @@ wayl_win_destroy(struct wl_window *win) xdg_surface_destroy(win->xdg_surface); if (win->surface != NULL) wl_surface_destroy(win->surface); + free(win); } struct terminal * From 2eaa258e11ffdd2412138479c3939563a8ee3e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:21:16 +0100 Subject: [PATCH 20/30] main: cleanup --- main.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/main.c b/main.c index 1334f5e6..c419e4c9 100644 --- a/main.c +++ b/main.c @@ -276,7 +276,6 @@ main(int argc, char *const *argv) case 'f': tll_free_and_free(conf.fonts, free); - //tll_push_back(conf.fonts, strdup(optarg)); for (char *font = strtok(optarg, ","); font != NULL; font = strtok(NULL, ",")) { /* Strip leading spaces */ @@ -349,10 +348,6 @@ main(int argc, char *const *argv) }, .vt = { .state = 1, /* STATE_GROUND */ - .attrs = { - //.foreground = conf.colors.fg, - //.background = conf.colors.bg - }, }, .colors = { .default_fg = conf.colors.fg, @@ -521,8 +516,6 @@ main(int argc, char *const *argv) conf.height = max(conf.height, term.cell_height); render_resize(&term, conf.width, conf.height); - wl_display_dispatch_pending(term.wl->display); - { int fork_pipe[2]; if (pipe2(fork_pipe, O_CLOEXEC) < 0) { From 4a63defeb16cbf41054196440532b35cc7c97a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:21:36 +0100 Subject: [PATCH 21/30] wayland: backend now configures FD as non-blocking, not main --- main.c | 17 ++--------------- wayland.c | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/main.c b/main.c index c419e4c9..b1e2227d 100644 --- a/main.c +++ b/main.c @@ -588,20 +588,6 @@ main(int argc, char *const *argv) } } - - { - int fd = wl_display_get_fd(term.wl->display); - int fd_flags = fcntl(fd, F_GETFL); - if (fd_flags == -1) { - LOG_ERRNO("failed to set non blocking mode on Wayland display connection"); - goto out; - } - if (fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK) == -1) { - LOG_ERRNO("failed to set non blocking mode on Wayland display connection"); - goto out; - } - } - fdm_add(fdm, term.ptmx, EPOLLIN, &fdm_ptmx, &term); fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); fdm_add(fdm, term.blink.fd, EPOLLIN, &fdm_blink, &term); @@ -609,7 +595,8 @@ main(int argc, char *const *argv) fdm_add(fdm, term.delayed_render_timer.upper_fd, EPOLLIN, &fdm_delayed_render, &term); while (true) { - wl_display_flush(term.wl->display); + wl_display_flush(term.wl->display); /* TODO: figure out how to get rid of this */ + if (!fdm_poll(fdm)) break; } diff --git a/wayland.c b/wayland.c index 447534fb..a331190b 100644 --- a/wayland.c +++ b/wayland.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -520,7 +521,18 @@ wayl_init(struct fdm *fdm) goto out; } - if (!fdm_add(fdm, wl_display_get_fd(wayl->display), EPOLLIN, &fdm_wayl, wayl)) { + int wl_fd = wl_display_get_fd(wayl->display); + int fd_flags = fcntl(wl_fd, F_GETFL); + if (fd_flags == -1) { + LOG_ERRNO("failed to set non blocking mode on Wayland display connection"); + goto out; + } + if (fcntl(wl_fd, F_SETFL, fd_flags | O_NONBLOCK) == -1) { + LOG_ERRNO("failed to set non blocking mode on Wayland display connection"); + goto out; + } + + if (!fdm_add(fdm, wl_fd, EPOLLIN, &fdm_wayl, wayl)) { LOG_ERR("failed to register Wayland connection with the FDM"); goto out; } @@ -530,6 +542,8 @@ wayl_init(struct fdm *fdm) goto out; } + //wl_display_dispatch_pending(wayl->display); + //wl_display_flush(wayl->display); return wayl; out: From 3684547fd7c8367d38e0f086395204c269516606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:23:22 +0100 Subject: [PATCH 22/30] main: kbd repeat FD is handled by the wayland backend --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index b1e2227d..f28f7517 100644 --- a/main.c +++ b/main.c @@ -447,7 +447,7 @@ main(int argc, char *const *argv) goto out; } - if (term.flash.fd == -1 || term.blink.fd == -1 || term.wl->kbd.repeat.fd == -1) { + if (term.flash.fd == -1 || term.blink.fd == -1) { LOG_ERR("failed to create timers"); goto out; } From 664641104c47c51979088dd01244f954aab1f841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:28:23 +0100 Subject: [PATCH 23/30] main: remove a couple of includes --- main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/main.c b/main.c index f28f7517..b4e550fa 100644 --- a/main.c +++ b/main.c @@ -13,9 +13,7 @@ #include #include -#include #include -#include #include #include @@ -33,12 +31,12 @@ #include "fdm.h" #include "font.h" #include "grid.h" +#include "render.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)) From 957fb255594d634fdd1c2458546babd2b08114a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Oct 2019 19:36:45 +0100 Subject: [PATCH 24/30] wayland: move reload_cursor_theme() and update_cursor_surface() to wayland --- input.c | 2 +- render.c | 55 ------------------------------------------------ render.h | 2 -- wayland.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- wayland.h | 4 ++++ 5 files changed, 64 insertions(+), 61 deletions(-) diff --git a/input.c b/input.c index b91fea2f..bee51244 100644 --- a/input.c +++ b/input.c @@ -397,7 +397,7 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, wayl->mouse.col = x / term->cell_width; wayl->mouse.row = y / term->cell_height; - render_update_cursor_surface(term); + wayl_update_cursor_surface(wayl, term); } static void diff --git a/render.c b/render.c index 09e4d7d0..f1eaa9fa 100644 --- a/render.c +++ b/render.c @@ -942,61 +942,6 @@ render_set_title(struct terminal *term, const char *title) xdg_toplevel_set_title(term->window->xdg_toplevel, title); } -bool -render_reload_cursor_theme(struct terminal *term) -{ - if (term->wl->pointer.size == 0) - return true; - - if (term->wl->pointer.theme != NULL) { - wl_cursor_theme_destroy(term->wl->pointer.theme); - term->wl->pointer.theme = NULL; - term->wl->pointer.cursor = NULL; - } - - LOG_DBG("reloading cursor theme: %s@%d", - term->wl->pointer.theme_name, term->wl->pointer.size); - - term->wl->pointer.theme = wl_cursor_theme_load( - term->wl->pointer.theme_name, term->wl->pointer.size * term->scale, term->wl->shm); - if (term->wl->pointer.theme == NULL) { - LOG_ERR("failed to load cursor theme"); - return false; - } - - term->wl->pointer.cursor = wl_cursor_theme_get_cursor( - term->wl->pointer.theme, "left_ptr"); - assert(term->wl->pointer.cursor != NULL); - render_update_cursor_surface(term); - - return true; -} - -void -render_update_cursor_surface(struct terminal *term) -{ - if (term->wl->pointer.cursor == NULL) - return; - - const int scale = term->scale; - wl_surface_set_buffer_scale(term->wl->pointer.surface, scale); - - struct wl_cursor_image *image = term->wl->pointer.cursor->images[0]; - - wl_surface_attach( - term->wl->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); - - wl_pointer_set_cursor( - term->wl->pointer.pointer, term->wl->pointer.serial, - term->wl->pointer.surface, - image->hotspot_x / scale, image->hotspot_y / scale); - - wl_surface_damage_buffer( - term->wl->pointer.surface, 0, 0, INT32_MAX, INT32_MAX); - - wl_surface_commit(term->wl->pointer.surface); -} - void render_refresh(struct terminal *term) { diff --git a/render.h b/render.h index c2fa489e..1c6bbd7f 100644 --- a/render.h +++ b/render.h @@ -11,8 +11,6 @@ void render_set_title(struct terminal *term, const char *title); void render_refresh(struct terminal *term); void render_search_box(struct terminal *term); -bool render_reload_cursor_theme(struct terminal *term); -void render_update_cursor_surface(struct terminal *term); struct render_worker_context { int my_id; diff --git a/wayland.c b/wayland.c index a331190b..2fcf2340 100644 --- a/wayland.c +++ b/wayland.c @@ -126,7 +126,7 @@ output_scale(void *data, struct wl_output *wl_output, int32_t 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); + wayl_reload_cursor_theme(mon->wayl, term); } } @@ -269,7 +269,7 @@ surface_enter(void *data, struct wl_surface *wl_surface, /* Resize, since scale-to-use may have changed */ render_resize(term, term->width / term->scale, term->height / term->scale); - render_reload_cursor_theme(term); + wayl_reload_cursor_theme(wayl, term); return; } } @@ -293,7 +293,7 @@ surface_leave(void *data, struct wl_surface *wl_surface, /* Resize, since scale-to-use may have changed */ render_resize(term, term->width / term->scale, term->height / term->scale); - render_reload_cursor_theme(term); + wayl_reload_cursor_theme(wayl, term); return; } @@ -704,6 +704,62 @@ wayl_win_destroy(struct wl_window *win) free(win); } +bool +wayl_reload_cursor_theme(struct wayland *wayl, struct terminal *term) +{ + if (wayl->pointer.size == 0) + return true; + + if (wayl->pointer.theme != NULL) { + wl_cursor_theme_destroy(wayl->pointer.theme); + wayl->pointer.theme = NULL; + wayl->pointer.cursor = NULL; + } + + LOG_DBG("reloading cursor theme: %s@%d", + wayl->pointer.theme_name, wayl->pointer.size); + + wayl->pointer.theme = wl_cursor_theme_load( + wayl->pointer.theme_name, wayl->pointer.size * term->scale, wayl->shm); + if (wayl->pointer.theme == NULL) { + LOG_ERR("failed to load cursor theme"); + return false; + } + + wayl->pointer.cursor = wl_cursor_theme_get_cursor( + wayl->pointer.theme, "left_ptr"); + assert(wayl->pointer.cursor != NULL); + wayl_update_cursor_surface(wayl, term); + + return true; +} + +void +wayl_update_cursor_surface(struct wayland *wayl, struct terminal *term) +{ + if (wayl->pointer.cursor == NULL) + return; + + const int scale = term->scale; + wl_surface_set_buffer_scale(wayl->pointer.surface, scale); + + struct wl_cursor_image *image = wayl->pointer.cursor->images[0]; + + wl_surface_attach( + wayl->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); + + wl_pointer_set_cursor( + wayl->pointer.pointer, wayl->pointer.serial, + wayl->pointer.surface, + image->hotspot_x / scale, image->hotspot_y / scale); + + wl_surface_damage_buffer( + wayl->pointer.surface, 0, 0, INT32_MAX, INT32_MAX); + + wl_surface_commit(wayl->pointer.surface); +} + + struct terminal * wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface) { diff --git a/wayland.h b/wayland.h index 29c30518..a8aba13c 100644 --- a/wayland.h +++ b/wayland.h @@ -161,5 +161,9 @@ struct terminal *wayl_terminal_from_xdg_surface( struct terminal *wayl_terminal_from_xdg_toplevel( struct wayland *wayl, struct xdg_toplevel *toplevel); +/* TODO: pass something other than 'term'? Need scale... */ +bool wayl_reload_cursor_theme(struct wayland *wayl, struct terminal *term); +void wayl_update_cursor_surface(struct wayland *wayl, struct terminal *term); + struct wl_window *wayl_win_init(struct wayland *wayl); void wayl_win_destroy(struct wl_window *win); From 0979a0e2e56f196684260e96e2554ebf0b828667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 28 Oct 2019 18:25:19 +0100 Subject: [PATCH 25/30] terminal: implement term_init() and term_destroy() --- main.c | 345 +++------------------------------------------------- terminal.c | 348 +++++++++++++++++++++++++++++++++++++++++++++++++++++ terminal.h | 10 ++ 3 files changed, 375 insertions(+), 328 deletions(-) diff --git a/main.c b/main.c index b4e550fa..3d951b67 100644 --- a/main.c +++ b/main.c @@ -330,106 +330,10 @@ main(int argc, char *const *argv) setlocale(LC_ALL, ""); setenv("TERM", conf.term, 1); - struct terminal term = { - .quit = false, - .ptmx = posix_openpt(O_RDWR | O_NOCTTY), - .cursor_keys_mode = CURSOR_KEYS_NORMAL, - .keypad_keys_mode = KEYPAD_NUMERICAL, - .auto_margin = true, - .window_title_stack = tll_init(), - .scale = 1, - .flash = { - .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), - }, - .blink = { - .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), - }, - .vt = { - .state = 1, /* STATE_GROUND */ - }, - .colors = { - .default_fg = conf.colors.fg, - .default_bg = conf.colors.bg, - .default_table = { - conf.colors.regular[0], - conf.colors.regular[1], - conf.colors.regular[2], - conf.colors.regular[3], - conf.colors.regular[4], - conf.colors.regular[5], - conf.colors.regular[6], - conf.colors.regular[7], - - conf.colors.bright[0], - conf.colors.bright[1], - conf.colors.bright[2], - conf.colors.bright[3], - conf.colors.bright[4], - conf.colors.bright[5], - conf.colors.bright[6], - conf.colors.bright[7], - }, - .alpha = conf.colors.alpha, - }, - .default_cursor_style = conf.cursor.style, - .cursor_style = conf.cursor.style, - .default_cursor_color = { - .text = conf.cursor.color.text, - .cursor = conf.cursor.color.cursor, - }, - .cursor_color = { - .text = conf.cursor.color.text, - .cursor = conf.cursor.color.cursor, - }, - .selection = { - .start = {-1, -1}, - .end = {-1, -1}, - }, - .normal = {.damage = tll_init(), .scroll_damage = tll_init()}, - .alt = {.damage = tll_init(), .scroll_damage = tll_init()}, - .grid = &term.normal, - .render = { - .scrollback_lines = conf.scrollback_lines, - .workers = { - .count = conf.render_worker_count, - .queue = tll_init(), - }, - }, - .delayed_render_timer = { - .is_armed = false, - .lower_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), - .upper_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), - }, - }; - - LOG_INFO("using %zu rendering threads", term.render.workers.count); - - struct render_worker_context worker_context[term.render.workers.count]; - - /* Initialize 'current' colors from the default colors */ - term.colors.fg = term.colors.default_fg; - term.colors.bg = term.colors.default_bg; - - /* Initialize the 256 gray-scale color cube */ - { - /* First 16 entries have already been initialized from conf */ - for (size_t r = 0; r < 6; r++) { - for (size_t g = 0; g < 6; g++) { - for (size_t b = 0; b < 6; b++) { - term.colors.default_table[16 + r * 6 * 6 + g * 6 + b] - = r * 51 << 16 | g * 51 << 8 | b * 51; - } - } - } - - for (size_t i = 0; i < 24; i++) - term.colors.default_table[232 + i] = i * 11 << 16 | i * 11 << 8 | i * 11; - - memcpy(term.colors.table, term.colors.default_table, sizeof(term.colors.table)); - } struct fdm *fdm = NULL; struct wayland *wayl = NULL; + struct terminal *term = NULL; if ((fdm = fdm_init()) == NULL) goto out; @@ -437,256 +341,41 @@ main(int argc, char *const *argv) 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; - } - - if (term.flash.fd == -1 || term.blink.fd == -1) { - LOG_ERR("failed to create timers"); - goto out; - } - - sem_init(&term.render.workers.start, 0, 0); - sem_init(&term.render.workers.done, 0, 0); - mtx_init(&term.render.workers.lock, mtx_plain); - cnd_init(&term.render.workers.cond); - - term.render.workers.threads = calloc(term.render.workers.count, sizeof(term.render.workers.threads[0])); - for (size_t i = 0; i < term.render.workers.count; i++) { - worker_context[i].term = &term; - worker_context[i].my_id = 1 + i; - thrd_create(&term.render.workers.threads[i], &render_worker_thread, &worker_context[i]); - } - - font_list_t font_names = tll_init(); - tll_foreach(conf.fonts, it) - tll_push_back(font_names, it->item); - - if ((term.fonts[0] = font_from_name(font_names, "")) == NULL) { - tll_free(font_names); - goto out; - } - - term.fonts[1] = font_from_name(font_names, "style=bold"); - term.fonts[2] = font_from_name(font_names, "style=italic"); - term.fonts[3] = font_from_name(font_names, "style=bold italic"); - - tll_free(font_names); - - { - FT_Face ft_face = term.fonts[0]->face; - int max_x_advance = ft_face->size->metrics.max_advance / 64; - int height = ft_face->size->metrics.height / 64; - int descent = ft_face->size->metrics.descender / 64; - int ascent = ft_face->size->metrics.ascender / 64; - - term.fextents.height = height * term.fonts[0]->pixel_size_fixup; - term.fextents.descent = -descent * term.fonts[0]->pixel_size_fixup; - term.fextents.ascent = ascent * term.fonts[0]->pixel_size_fixup; - term.fextents.max_x_advance = max_x_advance * term.fonts[0]->pixel_size_fixup; - - LOG_DBG("metrics: height: %d, descent: %d, ascent: %d, x-advance: %d", - height, descent, ascent, max_x_advance); - } - - term.cell_width = (int)ceil(term.fextents.max_x_advance); - term.cell_height = (int)ceil(term.fextents.height); - LOG_INFO("cell width=%d, height=%d", term.cell_width, term.cell_height); - - /* Main window */ - term.window = wayl_win_init(wayl); - if (term.window == NULL) + if ((term = term_init(&conf, fdm, wayl, argc, argv)) == NULL) goto out; - term_set_window_title(&term, "foot"); - - 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); - - { - int fork_pipe[2]; - if (pipe2(fork_pipe, O_CLOEXEC) < 0) { - LOG_ERRNO("failed to create pipe"); - goto out; - } - - term.slave = fork(); - switch (term.slave) { - case -1: - LOG_ERRNO("failed to fork"); - close(fork_pipe[0]); - close(fork_pipe[1]); - goto out; - - case 0: - /* Child */ - close(fork_pipe[0]); /* Close read end */ - - char **_shell_argv = NULL; - char *const *shell_argv = argv; - - if (argc == 0) { - if (!tokenize_cmdline(conf.shell, &_shell_argv)) { - (void)!write(fork_pipe[1], &errno, sizeof(errno)); - _exit(0); - } - shell_argv = _shell_argv; - } - - slave_spawn(term.ptmx, shell_argv, fork_pipe[1]); - assert(false); - break; - - default: { - close(fork_pipe[1]); /* Close write end */ - LOG_DBG("slave has PID %d", term.slave); - - int _errno; - static_assert(sizeof(errno) == sizeof(_errno), "errno size mismatch"); - - ssize_t ret = read(fork_pipe[0], &_errno, sizeof(_errno)); - close(fork_pipe[0]); - - if (ret < 0) { - LOG_ERRNO("failed to read from pipe"); - goto out; - } else if (ret == sizeof(_errno)) { - LOG_ERRNO( - "%s: failed to execute", argc == 0 ? conf.shell : argv[0]); - goto out; - } else - LOG_DBG("%s: successfully started", conf.shell); - break; - } - } - } - - /* Read logic requires non-blocking mode */ - { - int fd_flags = fcntl(term.ptmx, F_GETFL); - if (fd_flags == -1) { - LOG_ERRNO("failed to set non blocking mode on PTY master"); - goto out; - } - - if (fcntl(term.ptmx, F_SETFL, fd_flags | O_NONBLOCK) == -1) { - LOG_ERRNO("failed to set non blocking mode on PTY master"); - goto out; - } - } - - fdm_add(fdm, term.ptmx, EPOLLIN, &fdm_ptmx, &term); - fdm_add(fdm, term.flash.fd, EPOLLIN, &fdm_flash, &term); - fdm_add(fdm, term.blink.fd, EPOLLIN, &fdm_blink, &term); - fdm_add(fdm, term.delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, &term); - fdm_add(fdm, term.delayed_render_timer.upper_fd, EPOLLIN, &fdm_delayed_render, &term); + fdm_add(fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term); + fdm_add(fdm, term->flash.fd, EPOLLIN, &fdm_flash, term); + fdm_add(fdm, term->blink.fd, EPOLLIN, &fdm_blink, term); + fdm_add(fdm, term->delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, term); + fdm_add(fdm, term->delayed_render_timer.upper_fd, EPOLLIN, &fdm_delayed_render, term); while (true) { - wl_display_flush(term.wl->display); /* TODO: figure out how to get rid of this */ + wl_display_flush(term->wl->display); /* TODO: figure out how to get rid of this */ if (!fdm_poll(fdm)) break; } - if (term.quit) + if (term->quit) ret = EXIT_SUCCESS; out: if (fdm != NULL) { - fdm_del(fdm, term.ptmx); - fdm_del(fdm, term.flash.fd); - fdm_del(fdm, term.blink.fd); - fdm_del(fdm, term.delayed_render_timer.lower_fd); - fdm_del(fdm, term.delayed_render_timer.upper_fd); + fdm_del(fdm, term->ptmx); + fdm_del(fdm, term->flash.fd); + fdm_del(fdm, term->blink.fd); + fdm_del(fdm, term->delayed_render_timer.lower_fd); + fdm_del(fdm, term->delayed_render_timer.upper_fd); } - if (term.delayed_render_timer.lower_fd != -1) - close(term.delayed_render_timer.lower_fd); - if (term.delayed_render_timer.upper_fd != -1) - close(term.delayed_render_timer.upper_fd); - - mtx_lock(&term.render.workers.lock); - assert(tll_length(term.render.workers.queue) == 0); - for (size_t i = 0; i < term.render.workers.count; i++) { - sem_post(&term.render.workers.start); - tll_push_back(term.render.workers.queue, -2); - } - cnd_broadcast(&term.render.workers.cond); - mtx_unlock(&term.render.workers.lock); - shm_fini(); - wayl_win_destroy(term.window); + int child_ret = term_destroy(term); wayl_destroy(wayl); - - free(term.vt.osc.data); - for (int row = 0; row < term.normal.num_rows; row++) - grid_row_free(term.normal.rows[row]); - free(term.normal.rows); - for (int row = 0; row < term.alt.num_rows; row++) - grid_row_free(term.alt.rows[row]); - free(term.alt.rows); - - free(term.window_title); - tll_free_and_free(term.window_title_stack, free); - - for (size_t i = 0; i < sizeof(term.fonts) / sizeof(term.fonts[0]); i++) - font_destroy(term.fonts[i]); - - free(term.search.buf); - - if (term.flash.fd != -1) - close(term.flash.fd); - if (term.blink.fd != -1) - close(term.blink.fd); - - if (term.ptmx != -1) - close(term.ptmx); - - for (size_t i = 0; i < term.render.workers.count; i++) - thrd_join(term.render.workers.threads[i], NULL); - free(term.render.workers.threads); - cnd_destroy(&term.render.workers.cond); - mtx_destroy(&term.render.workers.lock); - sem_destroy(&term.render.workers.start); - sem_destroy(&term.render.workers.done); - assert(tll_length(term.render.workers.queue) == 0); - tll_free(term.render.workers.queue); - - if (term.slave > 0) { - /* Note: we've closed ptmx, so the slave *should* exit... */ - int status; - waitpid(term.slave, &status, 0); - - int child_ret = EXIT_FAILURE; - if (WIFEXITED(status)) { - child_ret = WEXITSTATUS(status); - LOG_DBG("slave exited with code %d", child_ret); - } else if (WIFSIGNALED(status)) { - child_ret = WTERMSIG(status); - LOG_WARN("slave exited with signal %d", child_ret); - } else { - LOG_WARN("slave exited for unknown reason (status = 0x%08x)", status); - } - - if (ret == EXIT_SUCCESS) - ret = child_ret; - } - fdm_destroy(fdm); - config_free(conf); - return ret; + + return ret == EXIT_SUCCESS ? child_ret : ret; } diff --git a/terminal.c b/terminal.c index 684f4c30..023390d9 100644 --- a/terminal.c +++ b/terminal.c @@ -3,8 +3,12 @@ #include #include #include +#include +#include +#include #include +#include #include #define LOG_MODULE "terminal" @@ -14,10 +18,354 @@ #include "render.h" #include "vt.h" #include "selection.h" +#include "config.h" +#include "tokenize.h" +#include "slave.h" #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) +struct terminal * +term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, + int argc, char *const *argv) +{ + struct terminal *term = malloc(sizeof(*term)); + *term = (struct terminal) { + .fdm = fdm, + .quit = false, + .ptmx = posix_openpt(O_RDWR | O_NOCTTY), + .cursor_keys_mode = CURSOR_KEYS_NORMAL, + .keypad_keys_mode = KEYPAD_NUMERICAL, + .auto_margin = true, + .window_title_stack = tll_init(), + .scale = 1, + .flash = { + .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), + }, + .blink = { + .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), + }, + .vt = { + .state = 1, /* STATE_GROUND */ + }, + .colors = { + .default_fg = conf->colors.fg, + .default_bg = conf->colors.bg, + .default_table = { + conf->colors.regular[0], + conf->colors.regular[1], + conf->colors.regular[2], + conf->colors.regular[3], + conf->colors.regular[4], + conf->colors.regular[5], + conf->colors.regular[6], + conf->colors.regular[7], + + conf->colors.bright[0], + conf->colors.bright[1], + conf->colors.bright[2], + conf->colors.bright[3], + conf->colors.bright[4], + conf->colors.bright[5], + conf->colors.bright[6], + conf->colors.bright[7], + }, + .alpha = conf->colors.alpha, + }, + .default_cursor_style = conf->cursor.style, + .cursor_style = conf->cursor.style, + .default_cursor_color = { + .text = conf->cursor.color.text, + .cursor = conf->cursor.color.cursor, + }, + .cursor_color = { + .text = conf->cursor.color.text, + .cursor = conf->cursor.color.cursor, + }, + .selection = { + .start = {-1, -1}, + .end = {-1, -1}, + }, + .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 = { + .count = conf->render_worker_count, + .queue = tll_init(), + }, + }, + .delayed_render_timer = { + .is_armed = false, + .lower_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), + .upper_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), + }, + }; + + LOG_INFO("using %zu rendering threads", term->render.workers.count); + + struct render_worker_context worker_context[term->render.workers.count]; + + /* Initialize 'current' colors from the default colors */ + term->colors.fg = term->colors.default_fg; + term->colors.bg = term->colors.default_bg; + + /* Initialize the 256 gray-scale color cube */ + { + /* First 16 entries have already been initialized from conf */ + for (size_t r = 0; r < 6; r++) { + for (size_t g = 0; g < 6; g++) { + for (size_t b = 0; b < 6; b++) { + term->colors.default_table[16 + r * 6 * 6 + g * 6 + b] + = r * 51 << 16 | g * 51 << 8 | b * 51; + } + } + } + + for (size_t i = 0; i < 24; i++) + term->colors.default_table[232 + i] = i * 11 << 16 | i * 11 << 8 | i * 11; + + memcpy(term->colors.table, term->colors.default_table, sizeof(term->colors.table)); + } + if (term->ptmx == -1) { + LOG_ERR("failed to open pseudo terminal"); + goto out; + } + + if (term->flash.fd == -1 || term->blink.fd == -1) { + LOG_ERR("failed to create timers"); + goto out; + } + + sem_init(&term->render.workers.start, 0, 0); + sem_init(&term->render.workers.done, 0, 0); + mtx_init(&term->render.workers.lock, mtx_plain); + cnd_init(&term->render.workers.cond); + + term->render.workers.threads = calloc(term->render.workers.count, sizeof(term->render.workers.threads[0])); + for (size_t i = 0; i < term->render.workers.count; i++) { + worker_context[i].term = term; + worker_context[i].my_id = 1 + i; + thrd_create(&term->render.workers.threads[i], &render_worker_thread, &worker_context[i]); + } + + font_list_t font_names = tll_init(); + tll_foreach(conf->fonts, it) + tll_push_back(font_names, it->item); + + if ((term->fonts[0] = font_from_name(font_names, "")) == NULL) { + tll_free(font_names); + goto out; + } + + term->fonts[1] = font_from_name(font_names, "style=bold"); + term->fonts[2] = font_from_name(font_names, "style=italic"); + term->fonts[3] = font_from_name(font_names, "style=bold italic"); + + tll_free(font_names); + + { + FT_Face ft_face = term->fonts[0]->face; + int max_x_advance = ft_face->size->metrics.max_advance / 64; + int height = ft_face->size->metrics.height / 64; + int descent = ft_face->size->metrics.descender / 64; + int ascent = ft_face->size->metrics.ascender / 64; + + term->fextents.height = height * term->fonts[0]->pixel_size_fixup; + term->fextents.descent = -descent * term->fonts[0]->pixel_size_fixup; + term->fextents.ascent = ascent * term->fonts[0]->pixel_size_fixup; + term->fextents.max_x_advance = max_x_advance * term->fonts[0]->pixel_size_fixup; + + LOG_DBG("metrics: height: %d, descent: %d, ascent: %d, x-advance: %d", + height, descent, ascent, max_x_advance); + } + + term->cell_width = (int)ceil(term->fextents.max_x_advance); + term->cell_height = (int)ceil(term->fextents.height); + LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); + + /* Main window */ + term->window = wayl_win_init(wayl); + if (term->window == NULL) + goto out; + + term_set_window_title(term, "foot"); + + unsigned width = conf->width; + unsigned height = conf->height; + + if (width == -1) { + assert(height == -1); + width = 80 * term->cell_width; + height = 24 * term->cell_height; + } + + width = max(width, term->cell_width); + height = max(height, term->cell_height); + render_resize(term, width, height); + + { + int fork_pipe[2]; + if (pipe2(fork_pipe, O_CLOEXEC) < 0) { + LOG_ERRNO("failed to create pipe"); + goto out; + } + + term->slave = fork(); + switch (term->slave) { + case -1: + LOG_ERRNO("failed to fork"); + close(fork_pipe[0]); + close(fork_pipe[1]); + goto out; + + case 0: + /* Child */ + close(fork_pipe[0]); /* Close read end */ + + char **_shell_argv = NULL; + char *const *shell_argv = argv; + + if (argc == 0) { + if (!tokenize_cmdline(conf->shell, &_shell_argv)) { + (void)!write(fork_pipe[1], &errno, sizeof(errno)); + _exit(0); + } + shell_argv = _shell_argv; + } + + slave_spawn(term->ptmx, shell_argv, fork_pipe[1]); + assert(false); + break; + + default: { + close(fork_pipe[1]); /* Close write end */ + LOG_DBG("slave has PID %d", term->slave); + + int _errno; + static_assert(sizeof(errno) == sizeof(_errno), "errno size mismatch"); + + ssize_t ret = read(fork_pipe[0], &_errno, sizeof(_errno)); + close(fork_pipe[0]); + + if (ret < 0) { + LOG_ERRNO("failed to read from pipe"); + goto out; + } else if (ret == sizeof(_errno)) { + LOG_ERRNO( + "%s: failed to execute", argc == 0 ? conf->shell : argv[0]); + goto out; + } else + LOG_DBG("%s: successfully started", conf->shell); + break; + } + } + } + + /* Read logic requires non-blocking mode */ + { + int fd_flags = fcntl(term->ptmx, F_GETFL); + if (fd_flags == -1) { + LOG_ERRNO("failed to set non blocking mode on PTY master"); + goto out; + } + + if (fcntl(term->ptmx, F_SETFL, fd_flags | O_NONBLOCK) == -1) { + LOG_ERRNO("failed to set non blocking mode on PTY master"); + goto out; + } + } + + wayl->term = term; + return term; + +out: + term_destroy(term); + return NULL; +} + +int +term_destroy(struct terminal *term) +{ + if (term == NULL) + return 0; + + wayl_win_destroy(term->window); + + if (term->delayed_render_timer.lower_fd != -1) + close(term->delayed_render_timer.lower_fd); + if (term->delayed_render_timer.upper_fd != -1) + close(term->delayed_render_timer.upper_fd); + + mtx_lock(&term->render.workers.lock); + assert(tll_length(term->render.workers.queue) == 0); + for (size_t i = 0; i < term->render.workers.count; i++) { + sem_post(&term->render.workers.start); + tll_push_back(term->render.workers.queue, -2); + } + cnd_broadcast(&term->render.workers.cond); + mtx_unlock(&term->render.workers.lock); + free(term->vt.osc.data); + for (int row = 0; row < term->normal.num_rows; row++) + grid_row_free(term->normal.rows[row]); + free(term->normal.rows); + for (int row = 0; row < term->alt.num_rows; row++) + grid_row_free(term->alt.rows[row]); + free(term->alt.rows); + + free(term->window_title); + tll_free_and_free(term->window_title_stack, free); + + for (size_t i = 0; i < sizeof(term->fonts) / sizeof(term->fonts[0]); i++) + font_destroy(term->fonts[i]); + + free(term->search.buf); + + if (term->flash.fd != -1) + close(term->flash.fd); + if (term->blink.fd != -1) + close(term->blink.fd); + + if (term->ptmx != -1) + close(term->ptmx); + + for (size_t i = 0; i < term->render.workers.count; i++) + thrd_join(term->render.workers.threads[i], NULL); + free(term->render.workers.threads); + cnd_destroy(&term->render.workers.cond); + mtx_destroy(&term->render.workers.lock); + sem_destroy(&term->render.workers.start); + sem_destroy(&term->render.workers.done); + assert(tll_length(term->render.workers.queue) == 0); + tll_free(term->render.workers.queue); + + int ret = EXIT_SUCCESS; + + if (term->slave > 0) { + /* Note: we've closed ptmx, so the slave *should* exit... */ + int status; + waitpid(term->slave, &status, 0); + + int child_ret = EXIT_FAILURE; + if (WIFEXITED(status)) { + child_ret = WEXITSTATUS(status); + LOG_DBG("slave exited with code %d", child_ret); + } else if (WIFSIGNALED(status)) { + child_ret = WTERMSIG(status); + LOG_WARN("slave exited with signal %d", child_ret); + } else { + LOG_WARN("slave exited for unknown reason (status = 0x%08x)", status); + } + + ret = child_ret; + } + + free(term); + return ret; +} + void term_reset(struct terminal *term, bool hard) { diff --git a/terminal.h b/terminal.h index 2f3fcdc5..7303f503 100644 --- a/terminal.h +++ b/terminal.h @@ -8,6 +8,8 @@ #include #include +//#include "config.h" +#include "fdm.h" #include "font.h" #include "tllist.h" #include "wayland.h" @@ -142,6 +144,8 @@ enum mouse_reporting { enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR }; struct terminal { + struct fdm *fdm; + pid_t slave; int ptmx; bool quit; @@ -283,6 +287,12 @@ struct terminal { } delayed_render_timer; }; +struct config; +struct terminal *term_init( + const struct config *conf, struct fdm *fdm, struct wayland *wayl, + int argc, char *const *argv); +int term_destroy(struct terminal *term); + void term_reset(struct terminal *term, bool hard); void term_damage_rows(struct terminal *term, int start, int end); From fe974956b045de20df3f39f6804c5ae934ea8e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 28 Oct 2019 18:35:16 +0100 Subject: [PATCH 26/30] term: integrate directly with FDM --- main.c | 218 +------------------------------------------------- terminal.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 224 insertions(+), 223 deletions(-) diff --git a/main.c b/main.c index 3d951b67..64dea222 100644 --- a/main.c +++ b/main.c @@ -3,227 +3,26 @@ #include #include #include -#include -#include -#include #include #include -#include #include -#include #include -#include -#include - -#include -#include -#include - -#include -#include #define LOG_MODULE "main" -#define LOG_ENABLE_DBG 1 +#define LOG_ENABLE_DBG 0 #include "log.h" #include "config.h" #include "fdm.h" #include "font.h" -#include "grid.h" -#include "render.h" #include "shm.h" -#include "slave.h" #include "terminal.h" -#include "tokenize.h" #include "version.h" -#include "vt.h" #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) -static bool -fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) -{ - struct terminal *term = data; - - if (events & EPOLLHUP) { - term->quit = true; - - if (!(events & EPOLLIN)) - return false; - } - - assert(events & EPOLLIN); - - uint8_t buf[24 * 1024]; - ssize_t count = read(term->ptmx, buf, sizeof(buf)); - - if (count < 0) { - if (errno == EAGAIN) - return true; - - LOG_ERRNO("failed to read from pseudo terminal"); - return false; - } - - vt_from_slave(term, buf, count); - - /* - * We likely need to re-render. But, we don't want to - * do it immediately. Often, a single client operation - * is done through multiple writes. Many times, we're - * so fast that we render mid-operation frames. - * - * For example, we might end up rendering a frame - * where the client just erased a line, while in the - * next frame, the client wrote to the same line. This - * causes screen "flashes". - * - * Mitigate by always incuring a small delay before - * rendering the next frame. This gives the client - * some time to finish the operation (and thus gives - * us time to receive the last writes before doing any - * actual rendering). - * - * We incur this delay *every* time we receive - * input. To ensure we don't delay rendering - * indefinitely, we start a second timer that is only - * reset when we render. - * - * Note that when the client is producing data at a - * very high pace, we're rate limited by the wayland - * compositor anyway. The delay we introduce here only - * has any effect when the renderer is idle. - * - * TODO: this adds input latency. Can we somehow hint - * ourselves we just received keyboard input, and in - * this case *not* delay rendering? - */ - if (term->window->frame_callback == NULL) { - /* First timeout - reset each time we receive input. */ - timerfd_settime( - term->delayed_render_timer.lower_fd, 0, - &(struct itimerspec){.it_value = {.tv_nsec = 1000000}}, - NULL); - - /* Second timeout - only reset when we render. Set to one - * frame (assuming 60Hz) */ - if (!term->delayed_render_timer.is_armed) { - timerfd_settime( - term->delayed_render_timer.upper_fd, 0, - &(struct itimerspec){.it_value = {.tv_nsec = 16666666}}, - NULL); - term->delayed_render_timer.is_armed = true; - } - } - - return !(events & EPOLLHUP); -} - -static bool -fdm_flash(struct fdm *fdm, int fd, int events, void *data) -{ - if (events & EPOLLHUP) - return false; - - struct terminal *term = data; - uint64_t expiration_count; - ssize_t ret = read( - term->flash.fd, &expiration_count, sizeof(expiration_count)); - - if (ret < 0) { - if (errno == EAGAIN) - return true; - - LOG_ERRNO("failed to read flash timer"); - return false; - } - - LOG_DBG("flash timer expired %llu times", - (unsigned long long)expiration_count); - - term->flash.active = false; - term_damage_view(term); - render_refresh(term); - return true; -} - -static bool -fdm_blink(struct fdm *fdm, int fd, int events, void *data) -{ - if (events & EPOLLHUP) - return false; - - struct terminal *term = data; - uint64_t expiration_count; - ssize_t ret = read( - term->blink.fd, &expiration_count, sizeof(expiration_count)); - - if (ret < 0) { - if (errno == EAGAIN) - return true; - - LOG_ERRNO("failed to read blink timer"); - return false; - } - - LOG_DBG("blink timer expired %llu times", - (unsigned long long)expiration_count); - - term->blink.state = term->blink.state == BLINK_ON - ? BLINK_OFF : BLINK_ON; - - /* Scan all visible cells and mark rows with blinking cells dirty */ - for (int r = 0; r < term->rows; r++) { - struct row *row = grid_row_in_view(term->grid, r); - for (int col = 0; col < term->cols; col++) { - struct cell *cell = &row->cells[col]; - - if (cell->attrs.blink) { - cell->attrs.clean = 0; - row->dirty = true; - } - } - } - - render_refresh(term); - return true; -} - -static bool -fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data) -{ - if (events & EPOLLHUP) - return false; - - struct terminal *term = data; - assert(term->delayed_render_timer.is_armed); - - uint64_t unused; - ssize_t ret1 = 0; - ssize_t ret2 = 0; - - if (fd == term->delayed_render_timer.lower_fd) - ret1 = read(term->delayed_render_timer.lower_fd, &unused, sizeof(unused)); - if (fd == term->delayed_render_timer.upper_fd) - ret2 = read(term->delayed_render_timer.upper_fd, &unused, sizeof(unused)); - - if ((ret1 < 0 || ret2 < 0) && errno != EAGAIN) - LOG_ERRNO("failed to read timeout timer"); - else if (ret1 > 0 || ret2 > 0) { - render_refresh(term); - - /* Reset timers */ - term->delayed_render_timer.is_armed = false; - timerfd_settime(term->delayed_render_timer.lower_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL); - timerfd_settime(term->delayed_render_timer.upper_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL); - } else - assert(false); - - return true; -} - static void print_usage(const char *prog_name) { @@ -330,7 +129,6 @@ main(int argc, char *const *argv) setlocale(LC_ALL, ""); setenv("TERM", conf.term, 1); - struct fdm *fdm = NULL; struct wayland *wayl = NULL; struct terminal *term = NULL; @@ -344,12 +142,6 @@ main(int argc, char *const *argv) if ((term = term_init(&conf, fdm, wayl, argc, argv)) == NULL) goto out; - fdm_add(fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term); - fdm_add(fdm, term->flash.fd, EPOLLIN, &fdm_flash, term); - fdm_add(fdm, term->blink.fd, EPOLLIN, &fdm_blink, term); - fdm_add(fdm, term->delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, term); - fdm_add(fdm, term->delayed_render_timer.upper_fd, EPOLLIN, &fdm_delayed_render, term); - while (true) { wl_display_flush(term->wl->display); /* TODO: figure out how to get rid of this */ @@ -361,14 +153,6 @@ main(int argc, char *const *argv) ret = EXIT_SUCCESS; out: - if (fdm != NULL) { - fdm_del(fdm, term->ptmx); - fdm_del(fdm, term->flash.fd); - fdm_del(fdm, term->blink.fd); - fdm_del(fdm, term->delayed_render_timer.lower_fd); - fdm_del(fdm, term->delayed_render_timer.upper_fd); - } - shm_fini(); int child_ret = term_destroy(term); diff --git a/terminal.c b/terminal.c index 023390d9..2cf87fa6 100644 --- a/terminal.c +++ b/terminal.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,188 @@ #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) +static bool +fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) +{ + struct terminal *term = data; + + if (events & EPOLLHUP) { + term->quit = true; + + if (!(events & EPOLLIN)) + return false; + } + + assert(events & EPOLLIN); + + uint8_t buf[24 * 1024]; + ssize_t count = read(term->ptmx, buf, sizeof(buf)); + + if (count < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read from pseudo terminal"); + return false; + } + + vt_from_slave(term, buf, count); + + /* + * We likely need to re-render. But, we don't want to + * do it immediately. Often, a single client operation + * is done through multiple writes. Many times, we're + * so fast that we render mid-operation frames. + * + * For example, we might end up rendering a frame + * where the client just erased a line, while in the + * next frame, the client wrote to the same line. This + * causes screen "flashes". + * + * Mitigate by always incuring a small delay before + * rendering the next frame. This gives the client + * some time to finish the operation (and thus gives + * us time to receive the last writes before doing any + * actual rendering). + * + * We incur this delay *every* time we receive + * input. To ensure we don't delay rendering + * indefinitely, we start a second timer that is only + * reset when we render. + * + * Note that when the client is producing data at a + * very high pace, we're rate limited by the wayland + * compositor anyway. The delay we introduce here only + * has any effect when the renderer is idle. + * + * TODO: this adds input latency. Can we somehow hint + * ourselves we just received keyboard input, and in + * this case *not* delay rendering? + */ + if (term->window->frame_callback == NULL) { + /* First timeout - reset each time we receive input. */ + timerfd_settime( + term->delayed_render_timer.lower_fd, 0, + &(struct itimerspec){.it_value = {.tv_nsec = 1000000}}, + NULL); + + /* Second timeout - only reset when we render. Set to one + * frame (assuming 60Hz) */ + if (!term->delayed_render_timer.is_armed) { + timerfd_settime( + term->delayed_render_timer.upper_fd, 0, + &(struct itimerspec){.it_value = {.tv_nsec = 16666666}}, + NULL); + term->delayed_render_timer.is_armed = true; + } + } + + return !(events & EPOLLHUP); +} + +static bool +fdm_flash(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct terminal *term = data; + uint64_t expiration_count; + ssize_t ret = read( + term->flash.fd, &expiration_count, sizeof(expiration_count)); + + if (ret < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read flash timer"); + return false; + } + + LOG_DBG("flash timer expired %llu times", + (unsigned long long)expiration_count); + + term->flash.active = false; + term_damage_view(term); + render_refresh(term); + return true; +} + +static bool +fdm_blink(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct terminal *term = data; + uint64_t expiration_count; + ssize_t ret = read( + term->blink.fd, &expiration_count, sizeof(expiration_count)); + + if (ret < 0) { + if (errno == EAGAIN) + return true; + + LOG_ERRNO("failed to read blink timer"); + return false; + } + + LOG_DBG("blink timer expired %llu times", + (unsigned long long)expiration_count); + + term->blink.state = term->blink.state == BLINK_ON + ? BLINK_OFF : BLINK_ON; + + /* Scan all visible cells and mark rows with blinking cells dirty */ + for (int r = 0; r < term->rows; r++) { + struct row *row = grid_row_in_view(term->grid, r); + for (int col = 0; col < term->cols; col++) { + struct cell *cell = &row->cells[col]; + + if (cell->attrs.blink) { + cell->attrs.clean = 0; + row->dirty = true; + } + } + } + + render_refresh(term); + return true; +} + +static bool +fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data) +{ + if (events & EPOLLHUP) + return false; + + struct terminal *term = data; + assert(term->delayed_render_timer.is_armed); + + uint64_t unused; + ssize_t ret1 = 0; + ssize_t ret2 = 0; + + if (fd == term->delayed_render_timer.lower_fd) + ret1 = read(term->delayed_render_timer.lower_fd, &unused, sizeof(unused)); + if (fd == term->delayed_render_timer.upper_fd) + ret2 = read(term->delayed_render_timer.upper_fd, &unused, sizeof(unused)); + + if ((ret1 < 0 || ret2 < 0) && errno != EAGAIN) + LOG_ERRNO("failed to read timeout timer"); + else if (ret1 > 0 || ret2 > 0) { + render_refresh(term); + + /* Reset timers */ + term->delayed_render_timer.is_armed = false; + timerfd_settime(term->delayed_render_timer.lower_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL); + timerfd_settime(term->delayed_render_timer.upper_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL); + } else + assert(false); + + return true; +} + struct terminal * term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, int argc, char *const *argv) @@ -278,6 +461,28 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, } } + if (!fdm_add(fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term)) { + LOG_ERR("failed to add ptmx to FDM"); + goto out; + } + + if (!fdm_add(fdm, term->flash.fd, EPOLLIN, &fdm_flash, term)) { + LOG_ERR("failed to add flash timer FD to FDM"); + goto out; + } + + if (!fdm_add(fdm, term->blink.fd, EPOLLIN, &fdm_blink, term)) { + LOG_ERR("failed to add blink tiemr FD to FDM"); + goto out; + } + + if (!fdm_add(fdm, term->delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, term) || + !fdm_add(fdm, term->delayed_render_timer.upper_fd, EPOLLIN, &fdm_delayed_render, term)) + { + LOG_ERR("failed to add delayed rendering timer FDs to FDM"); + goto out; + } + wayl->term = term; return term; @@ -294,10 +499,15 @@ term_destroy(struct terminal *term) wayl_win_destroy(term->window); - if (term->delayed_render_timer.lower_fd != -1) + if (term->delayed_render_timer.lower_fd != -1) { + fdm_del(term->fdm, term->delayed_render_timer.lower_fd); close(term->delayed_render_timer.lower_fd); - if (term->delayed_render_timer.upper_fd != -1) + } + + if (term->delayed_render_timer.upper_fd != -1) { + fdm_del(term->fdm, term->delayed_render_timer.upper_fd); close(term->delayed_render_timer.upper_fd); + } mtx_lock(&term->render.workers.lock); assert(tll_length(term->render.workers.queue) == 0); @@ -323,13 +533,20 @@ term_destroy(struct terminal *term) free(term->search.buf); - if (term->flash.fd != -1) + if (term->flash.fd != -1) { + fdm_del(term->fdm, term->flash.fd); close(term->flash.fd); - if (term->blink.fd != -1) - close(term->blink.fd); + } - if (term->ptmx != -1) + if (term->blink.fd != -1) { + fdm_del(term->fdm, term->blink.fd); + close(term->blink.fd); + } + + if (term->ptmx != -1) { + fdm_del(term->fdm, term->ptmx); close(term->ptmx); + } for (size_t i = 0; i < term->render.workers.count; i++) thrd_join(term->render.workers.threads[i], NULL); From 720d0df067d0e8add7793f25398e21ca0486bce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 28 Oct 2019 18:46:03 +0100 Subject: [PATCH 27/30] term: open FDs before allocating terminal struct --- terminal.c | 111 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/terminal.c b/terminal.c index 2cf87fa6..cd8d0ed9 100644 --- a/terminal.c +++ b/terminal.c @@ -212,22 +212,46 @@ struct terminal * term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, int argc, char *const *argv) { - struct terminal *term = malloc(sizeof(*term)); + int ptmx = -1; + int flash_fd = -1; + int blink_fd = -1; + int delay_lower_fd = -1; + int delay_upper_fd = -1; + + struct terminal *term = NULL; + struct render_worker_context worker_context[conf->render_worker_count]; + + if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) == -1) { + LOG_ERRNO("failed to open PTY"); + goto close_fds; + } + if ((flash_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) { + LOG_ERRNO("failed to create flash timer FD"); + goto close_fds; + } + if ((blink_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) { + LOG_ERRNO("failed to create blink timer FD"); + goto close_fds; + } + if ((delay_lower_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1 || + (delay_upper_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) + { + LOG_ERRNO("failed to create delayed rendering timer FDs"); + goto close_fds; + } + + term = malloc(sizeof(*term)); *term = (struct terminal) { .fdm = fdm, .quit = false, - .ptmx = posix_openpt(O_RDWR | O_NOCTTY), + .ptmx = ptmx, .cursor_keys_mode = CURSOR_KEYS_NORMAL, .keypad_keys_mode = KEYPAD_NUMERICAL, .auto_margin = true, .window_title_stack = tll_init(), .scale = 1, - .flash = { - .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), - }, - .blink = { - .fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK), - }, + .flash = {.fd = flash_fd}, + .blink = {.fd = blink_fd}, .vt = { .state = 1, /* STATE_GROUND */ }, @@ -282,14 +306,13 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, }, .delayed_render_timer = { .is_armed = false, - .lower_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), - .upper_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC), + .lower_fd = delay_lower_fd, + .upper_fd = delay_upper_fd, }, }; LOG_INFO("using %zu rendering threads", term->render.workers.count); - struct render_worker_context worker_context[term->render.workers.count]; /* Initialize 'current' colors from the default colors */ term->colors.fg = term->colors.default_fg; @@ -312,15 +335,6 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, memcpy(term->colors.table, term->colors.default_table, sizeof(term->colors.table)); } - if (term->ptmx == -1) { - LOG_ERR("failed to open pseudo terminal"); - goto out; - } - - if (term->flash.fd == -1 || term->blink.fd == -1) { - LOG_ERR("failed to create timers"); - goto out; - } sem_init(&term->render.workers.start, 0, 0); sem_init(&term->render.workers.done, 0, 0); @@ -340,7 +354,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, if ((term->fonts[0] = font_from_name(font_names, "")) == NULL) { tll_free(font_names); - goto out; + goto err; } term->fonts[1] = font_from_name(font_names, "style=bold"); @@ -369,10 +383,9 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, term->cell_height = (int)ceil(term->fextents.height); LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); - /* Main window */ term->window = wayl_win_init(wayl); if (term->window == NULL) - goto out; + goto err; term_set_window_title(term, "foot"); @@ -393,7 +406,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, int fork_pipe[2]; if (pipe2(fork_pipe, O_CLOEXEC) < 0) { LOG_ERRNO("failed to create pipe"); - goto out; + goto err; } term->slave = fork(); @@ -402,7 +415,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, LOG_ERRNO("failed to fork"); close(fork_pipe[0]); close(fork_pipe[1]); - goto out; + goto err; case 0: /* Child */ @@ -419,7 +432,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, shell_argv = _shell_argv; } - slave_spawn(term->ptmx, shell_argv, fork_pipe[1]); + slave_spawn(ptmx, shell_argv, fork_pipe[1]); assert(false); break; @@ -435,11 +448,11 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, if (ret < 0) { LOG_ERRNO("failed to read from pipe"); - goto out; + goto err; } else if (ret == sizeof(_errno)) { LOG_ERRNO( "%s: failed to execute", argc == 0 ? conf->shell : argv[0]); - goto out; + goto err; } else LOG_DBG("%s: successfully started", conf->shell); break; @@ -449,46 +462,60 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, /* Read logic requires non-blocking mode */ { - int fd_flags = fcntl(term->ptmx, F_GETFL); + int fd_flags = fcntl(ptmx, F_GETFL); if (fd_flags == -1) { LOG_ERRNO("failed to set non blocking mode on PTY master"); - goto out; + goto err; } - if (fcntl(term->ptmx, F_SETFL, fd_flags | O_NONBLOCK) == -1) { + if (fcntl(ptmx, F_SETFL, fd_flags | O_NONBLOCK) == -1) { LOG_ERRNO("failed to set non blocking mode on PTY master"); - goto out; + goto err; } } - if (!fdm_add(fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term)) { + if (!fdm_add(fdm, ptmx, EPOLLIN, &fdm_ptmx, term)) { LOG_ERR("failed to add ptmx to FDM"); - goto out; + goto err; } - if (!fdm_add(fdm, term->flash.fd, EPOLLIN, &fdm_flash, term)) { + if (!fdm_add(fdm, flash_fd, EPOLLIN, &fdm_flash, term)) { LOG_ERR("failed to add flash timer FD to FDM"); - goto out; + goto err; } - if (!fdm_add(fdm, term->blink.fd, EPOLLIN, &fdm_blink, term)) { + if (!fdm_add(fdm, blink_fd, EPOLLIN, &fdm_blink, term)) { LOG_ERR("failed to add blink tiemr FD to FDM"); - goto out; + goto err; } - if (!fdm_add(fdm, term->delayed_render_timer.lower_fd, EPOLLIN, &fdm_delayed_render, term) || - !fdm_add(fdm, term->delayed_render_timer.upper_fd, EPOLLIN, &fdm_delayed_render, term)) + if (!fdm_add(fdm, delay_lower_fd, EPOLLIN, &fdm_delayed_render, term) || + !fdm_add(fdm, delay_upper_fd, EPOLLIN, &fdm_delayed_render, term)) { LOG_ERR("failed to add delayed rendering timer FDs to FDM"); - goto out; + goto err; } wayl->term = term; return term; -out: +err: term_destroy(term); return NULL; + +close_fds: + if (ptmx != -1) + close(ptmx); + if (flash_fd != -1) + close(flash_fd); + if (blink_fd != -1) + close(blink_fd); + if (delay_lower_fd != -1) + close(delay_lower_fd); + if (delay_upper_fd != -1) + close(delay_upper_fd); + assert(term == NULL); + return NULL; } int From 8e6f87eb17b562fd3d1def7a65b5dc8346642f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 28 Oct 2019 18:48:43 +0100 Subject: [PATCH 28/30] render worker context: allocate, and let worker threads free Since we now initialize the worker threads from term_init(), which returns before the threads terminate, we can no longer use stack-allocated worker contexts. We _could_ put them in the terminal struct. But a simpler solution is to allocate them in term_init(), and let the threads free them when they don't need them anymore. --- render.c | 1 + terminal.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/render.c b/render.c index f1eaa9fa..0c559ce0 100644 --- a/render.c +++ b/render.c @@ -374,6 +374,7 @@ render_worker_thread(void *_ctx) struct render_worker_context *ctx = _ctx; struct terminal *term = ctx->term; const int my_id = ctx->my_id; + free(ctx); char proc_title[16]; snprintf(proc_title, sizeof(proc_title), "foot:render:%d", my_id); diff --git a/terminal.c b/terminal.c index cd8d0ed9..5c9c7b37 100644 --- a/terminal.c +++ b/terminal.c @@ -219,7 +219,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, int delay_upper_fd = -1; struct terminal *term = NULL; - struct render_worker_context worker_context[conf->render_worker_count]; + struct render_worker_context *worker_context[conf->render_worker_count]; if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) == -1) { LOG_ERRNO("failed to open PTY"); @@ -343,9 +343,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, term->render.workers.threads = calloc(term->render.workers.count, sizeof(term->render.workers.threads[0])); for (size_t i = 0; i < term->render.workers.count; i++) { - worker_context[i].term = term; - worker_context[i].my_id = 1 + i; - thrd_create(&term->render.workers.threads[i], &render_worker_thread, &worker_context[i]); + worker_context[i] = malloc(sizeof(*worker_context[i])); + worker_context[i]->term = term; + worker_context[i]->my_id = 1 + i; + thrd_create(&term->render.workers.threads[i], &render_worker_thread, worker_context[i]); } font_list_t font_names = tll_init(); From 4ec9db8e186675793f4d2a71cbeaf70882d65efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 28 Oct 2019 19:23:41 +0100 Subject: [PATCH 29/30] term: no need to stack-allocate an array of worker contexts --- terminal.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/terminal.c b/terminal.c index 5c9c7b37..9aadd549 100644 --- a/terminal.c +++ b/terminal.c @@ -219,7 +219,6 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, int delay_upper_fd = -1; struct terminal *term = NULL; - struct render_worker_context *worker_context[conf->render_worker_count]; if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) == -1) { LOG_ERRNO("failed to open PTY"); @@ -341,12 +340,16 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, mtx_init(&term->render.workers.lock, mtx_plain); cnd_init(&term->render.workers.cond); - term->render.workers.threads = calloc(term->render.workers.count, sizeof(term->render.workers.threads[0])); + term->render.workers.threads = calloc( + term->render.workers.count, sizeof(term->render.workers.threads[0])); + for (size_t i = 0; i < term->render.workers.count; i++) { - worker_context[i] = malloc(sizeof(*worker_context[i])); - worker_context[i]->term = term; - worker_context[i]->my_id = 1 + i; - thrd_create(&term->render.workers.threads[i], &render_worker_thread, worker_context[i]); + struct render_worker_context *ctx = malloc(sizeof(*ctx)); + *ctx = (struct render_worker_context) { + .term = term, + .my_id = 1 + i, + }; + thrd_create(&term->render.workers.threads[i], &render_worker_thread, ctx); } font_list_t font_names = tll_init(); From ce5f5e4d51d693933cb7193ccce2c19d4feac79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 28 Oct 2019 18:53:41 +0100 Subject: [PATCH 30/30] slave: rename slave_spawn() -> slave_exec() --- slave.c | 2 +- slave.h | 2 +- terminal.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/slave.c b/slave.c index 18fcf3ad..92571fde 100644 --- a/slave.c +++ b/slave.c @@ -13,7 +13,7 @@ #include "log.h" void -slave_spawn(int ptmx, char *const argv[], int err_fd) +slave_exec(int ptmx, char *const argv[], int err_fd) { int pts = -1; const char *pts_name = ptsname(ptmx); diff --git a/slave.h b/slave.h index 4161dbda..5ec59149 100644 --- a/slave.h +++ b/slave.h @@ -1,4 +1,4 @@ #pragma once #include -void slave_spawn(int ptmx, char *const argv[], int err_fd); +void slave_exec(int ptmx, char *const argv[], int err_fd); diff --git a/terminal.c b/terminal.c index 9aadd549..9259cfd5 100644 --- a/terminal.c +++ b/terminal.c @@ -436,7 +436,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, shell_argv = _shell_argv; } - slave_spawn(ptmx, shell_argv, fork_pipe[1]); + slave_exec(ptmx, shell_argv, fork_pipe[1]); assert(false); break;