mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
term: integrate directly with FDM
This commit is contained in:
parent
0979a0e2e5
commit
fe974956b0
2 changed files with 224 additions and 223 deletions
218
main.c
218
main.c
|
|
@ -3,227 +3,26 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <poll.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <sys/timerfd.h>
|
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-cursor.h>
|
|
||||||
#include <xdg-shell.h>
|
|
||||||
|
|
||||||
#include <xdg-output-unstable-v1.h>
|
|
||||||
#include <xdg-decoration-unstable-v1.h>
|
|
||||||
|
|
||||||
#define LOG_MODULE "main"
|
#define LOG_MODULE "main"
|
||||||
#define LOG_ENABLE_DBG 1
|
#define LOG_ENABLE_DBG 0
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "fdm.h"
|
#include "fdm.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "grid.h"
|
|
||||||
#include "render.h"
|
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
#include "slave.h"
|
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "tokenize.h"
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "vt.h"
|
|
||||||
|
|
||||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||||
#define max(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
|
static void
|
||||||
print_usage(const char *prog_name)
|
print_usage(const char *prog_name)
|
||||||
{
|
{
|
||||||
|
|
@ -330,7 +129,6 @@ main(int argc, char *const *argv)
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
setenv("TERM", conf.term, 1);
|
setenv("TERM", conf.term, 1);
|
||||||
|
|
||||||
|
|
||||||
struct fdm *fdm = NULL;
|
struct fdm *fdm = NULL;
|
||||||
struct wayland *wayl = NULL;
|
struct wayland *wayl = NULL;
|
||||||
struct terminal *term = 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)
|
if ((term = term_init(&conf, fdm, wayl, argc, argv)) == NULL)
|
||||||
goto out;
|
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) {
|
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 */
|
||||||
|
|
||||||
|
|
@ -361,14 +153,6 @@ main(int argc, char *const *argv)
|
||||||
ret = EXIT_SUCCESS;
|
ret = EXIT_SUCCESS;
|
||||||
|
|
||||||
out:
|
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();
|
shm_fini();
|
||||||
|
|
||||||
int child_ret = term_destroy(term);
|
int child_ret = term_destroy(term);
|
||||||
|
|
|
||||||
229
terminal.c
229
terminal.c
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
#include <sys/timerfd.h>
|
#include <sys/timerfd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/input-event-codes.h>
|
#include <linux/input-event-codes.h>
|
||||||
|
|
@ -25,6 +26,188 @@
|
||||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||||
#define max(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 *
|
struct terminal *
|
||||||
term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
int argc, char *const *argv)
|
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;
|
wayl->term = term;
|
||||||
return term;
|
return term;
|
||||||
|
|
||||||
|
|
@ -294,10 +499,15 @@ term_destroy(struct terminal *term)
|
||||||
|
|
||||||
wayl_win_destroy(term->window);
|
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);
|
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);
|
close(term->delayed_render_timer.upper_fd);
|
||||||
|
}
|
||||||
|
|
||||||
mtx_lock(&term->render.workers.lock);
|
mtx_lock(&term->render.workers.lock);
|
||||||
assert(tll_length(term->render.workers.queue) == 0);
|
assert(tll_length(term->render.workers.queue) == 0);
|
||||||
|
|
@ -323,13 +533,20 @@ term_destroy(struct terminal *term)
|
||||||
|
|
||||||
free(term->search.buf);
|
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);
|
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);
|
close(term->ptmx);
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < term->render.workers.count; i++)
|
for (size_t i = 0; i < term->render.workers.count; i++)
|
||||||
thrd_join(term->render.workers.threads[i], NULL);
|
thrd_join(term->render.workers.threads[i], NULL);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue