mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-13 04:27:47 -05:00
terminal: implement term_init() and term_destroy()
This commit is contained in:
parent
957fb25559
commit
0979a0e2e5
3 changed files with 375 additions and 328 deletions
345
main.c
345
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;
|
||||
|
||||
}
|
||||
|
|
|
|||
348
terminal.c
348
terminal.c
|
|
@ -3,8 +3,12 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
|
|
|
|||
10
terminal.h
10
terminal.h
|
|
@ -8,6 +8,8 @@
|
|||
#include <threads.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
//#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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue