mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-16 05:34:00 -04:00
Merge branch 'multi-window'
This commit is contained in:
commit
25311fc4eb
19 changed files with 925 additions and 173 deletions
2
PKGBUILD
2
PKGBUILD
|
|
@ -22,6 +22,8 @@ build() {
|
||||||
./foot --term=xterm -- sh -c "../scripts/generate-alt-random-writes.py --scroll --scroll-region --colors-regular --colors-bright --colors-rgb ${tmp_file} && cat ${tmp_file}"
|
./foot --term=xterm -- sh -c "../scripts/generate-alt-random-writes.py --scroll --scroll-region --colors-regular --colors-bright --colors-rgb ${tmp_file} && cat ${tmp_file}"
|
||||||
rm "${tmp_file}"
|
rm "${tmp_file}"
|
||||||
|
|
||||||
|
./footclient --version
|
||||||
|
|
||||||
meson configure -Db_pgo=use
|
meson configure -Db_pgo=use
|
||||||
ninja
|
ninja
|
||||||
}
|
}
|
||||||
|
|
|
||||||
153
client.c
Normal file
153
client.c
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <linux/un.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "foot-client"
|
||||||
|
#include "log.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
static volatile sig_atomic_t aborted = 0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sig_handler(int signo)
|
||||||
|
{
|
||||||
|
aborted = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_usage(const char *prog_name)
|
||||||
|
{
|
||||||
|
printf("Usage: %s [OPTIONS]...\n", prog_name);
|
||||||
|
printf("\n");
|
||||||
|
printf("Options:\n");
|
||||||
|
printf(" -t,--term=TERM value to set the environment variable TERM to (foot)\n"
|
||||||
|
" -v,--version show the version number and quit\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *const *argv)
|
||||||
|
{
|
||||||
|
int ret = EXIT_FAILURE;
|
||||||
|
|
||||||
|
const char *const prog_name = argv[0];
|
||||||
|
|
||||||
|
static const struct option longopts[] = {
|
||||||
|
{"term", required_argument, 0, 't'},
|
||||||
|
{"version", no_argument, 0, 'v'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{NULL, no_argument, 0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *term = "";
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int c = getopt_long(argc, argv, ":t:hv", longopts, NULL);
|
||||||
|
if (c == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 't':
|
||||||
|
term = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'v':
|
||||||
|
printf("footclient version %s\n", FOOT_VERSION);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
print_usage(prog_name);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
fprintf(stderr, "error: -%c: missing required argument\n", optopt);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
fprintf(stderr, "error: -%c: invalid option\n", optopt);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd == -1) {
|
||||||
|
LOG_ERRNO("failed to create socket");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connected = false;
|
||||||
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||||
|
|
||||||
|
const char *xdg_runtime = getenv("XDG_RUNTIME_DIR");
|
||||||
|
if (xdg_runtime != NULL) {
|
||||||
|
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/foot.sock", xdg_runtime);
|
||||||
|
|
||||||
|
if (connect(fd, (const struct sockaddr *)&addr, sizeof(addr)) == 0)
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connected) {
|
||||||
|
strncpy(addr.sun_path, "/tmp/foot.sock", sizeof(addr.sun_path) - 1);
|
||||||
|
if (connect(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||||
|
LOG_ERRNO("failed to connect (is 'foot --server' running?)");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t term_len = strlen(term);
|
||||||
|
if (send(fd, &term_len, sizeof(term_len), 0) != sizeof(term_len) ||
|
||||||
|
send(fd, term, term_len, 0) != term_len)
|
||||||
|
{
|
||||||
|
LOG_ERRNO("failed to send TERM to server");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send(fd, &argc, sizeof(argc), 0) != sizeof(argc)) {
|
||||||
|
LOG_ERRNO("failed to send argc/argv to server");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
uint16_t len = strlen(argv[i]);
|
||||||
|
|
||||||
|
if (send(fd, &len, sizeof(len), 0) != sizeof(len) ||
|
||||||
|
send(fd, argv[i], len, 0) != sizeof(len))
|
||||||
|
{
|
||||||
|
LOG_ERRNO("failed to send argc/argv to server");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct sigaction sa = {.sa_handler = &sig_handler};
|
||||||
|
if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0) {
|
||||||
|
LOG_ERRNO("failed to register signal handlers");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exit_code;
|
||||||
|
ssize_t rcvd = recv(fd, &exit_code, sizeof(exit_code), 0);
|
||||||
|
|
||||||
|
if (rcvd == -1 && errno == EINTR)
|
||||||
|
assert(aborted);
|
||||||
|
else if (rcvd != sizeof(exit_code))
|
||||||
|
LOG_ERRNO("failed to read server response");
|
||||||
|
else
|
||||||
|
ret = exit_code;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (fd != -1)
|
||||||
|
close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
zsh_install_dir = join_paths(get_option('datadir'), 'zsh/site-functions')
|
zsh_install_dir = join_paths(get_option('datadir'), 'zsh/site-functions')
|
||||||
install_data('zsh/_foot', install_dir: zsh_install_dir)
|
install_data('zsh/_foot', install_dir: zsh_install_dir)
|
||||||
|
install_data('zsh/_footclient', install_dir: zsh_install_dir)
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,19 @@
|
||||||
|
|
||||||
_arguments \
|
_arguments \
|
||||||
-s \
|
-s \
|
||||||
'(-v --version)'{-v,--version}'[show the version number and quit]' \
|
|
||||||
'(-h --help)'{-h,--help}'[show help message and quit]' \
|
|
||||||
'(-f --font)'{-f,--font}'[font name and style in fontconfig format (monospace)]:font:->fonts' \
|
'(-f --font)'{-f,--font}'[font name and style in fontconfig format (monospace)]:font:->fonts' \
|
||||||
'(-g --geometry)'{-g,--geometry}'[window WIDTHxHEIGHT, in pixels (84x24 cells)]'
|
'(-t,--term)'{-t,--term}'[value to set the environment variable TERM to (foot)]:term:->terms' \
|
||||||
|
'(-g --geometry)'{-g,--geometry}'[window WIDTHxHEIGHT, in pixels (84x24 cells)]' \
|
||||||
|
'(-s --server)'{-s,--server}'[run as server; open terminals by running footclient]' \
|
||||||
|
'(-v --version)'{-v,--version}'[show the version number and quit]' \
|
||||||
|
'(-h --help)'{-h,--help}'[show help message and quit]'
|
||||||
|
|
||||||
case "${state}" in
|
case ${state} in
|
||||||
fonts)
|
fonts)
|
||||||
_values 'font families' $(fc-list : family | tr -d ' ')
|
_values 'font families' $(fc-list : family | tr -d ' ')
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
terms)
|
||||||
|
_values 'terminal definitions' $(find /usr/share/terminfo -type f -printf "%f\n")
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
13
completions/zsh/_footclient
Normal file
13
completions/zsh/_footclient
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#compdef footclient
|
||||||
|
|
||||||
|
_arguments \
|
||||||
|
-s \
|
||||||
|
'(-t,--term)'{-t,--term}'[value to set the environment variable TERM to (foot)]:term:->terms' \
|
||||||
|
'(-v --version)'{-v,--version}'[show the version number and quit]' \
|
||||||
|
'(-h --help)'{-h,--help}'[show help message and quit]'
|
||||||
|
|
||||||
|
case ${state} in
|
||||||
|
terms)
|
||||||
|
_values 'terminal definitions' $(find /usr/share/terminfo -type f -printf "%f\n")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
68
fdm.c
68
fdm.c
|
|
@ -1,6 +1,7 @@
|
||||||
#include "fdm.h"
|
#include "fdm.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
@ -15,11 +16,14 @@ struct fd {
|
||||||
int fd;
|
int fd;
|
||||||
void *data;
|
void *data;
|
||||||
fdm_handler_t handler;
|
fdm_handler_t handler;
|
||||||
|
bool deleted;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fdm {
|
struct fdm {
|
||||||
int epoll_fd;
|
int epoll_fd;
|
||||||
tll(struct fd) fds;
|
bool is_polling;
|
||||||
|
tll(struct fd *) fds;
|
||||||
|
tll(struct fd *) deferred_delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fdm *
|
struct fdm *
|
||||||
|
|
@ -34,7 +38,9 @@ fdm_init(void)
|
||||||
struct fdm *fdm = malloc(sizeof(*fdm));
|
struct fdm *fdm = malloc(sizeof(*fdm));
|
||||||
*fdm = (struct fdm){
|
*fdm = (struct fdm){
|
||||||
.epoll_fd = epoll_fd,
|
.epoll_fd = epoll_fd,
|
||||||
|
.is_polling = false,
|
||||||
.fds = tll_init(),
|
.fds = tll_init(),
|
||||||
|
.deferred_delete = tll_init(),
|
||||||
};
|
};
|
||||||
return fdm;
|
return fdm;
|
||||||
}
|
}
|
||||||
|
|
@ -60,23 +66,31 @@ fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data)
|
||||||
{
|
{
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
tll_foreach(fdm->fds, it) {
|
tll_foreach(fdm->fds, it) {
|
||||||
if (it->item.fd == fd) {
|
if (it->item->fd == fd) {
|
||||||
LOG_ERR("FD=%d already registered", fd);
|
LOG_ERR("FD=%d already registered", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tll_push_back(
|
struct fd *fd_data = malloc(sizeof(*fd_data));
|
||||||
fdm->fds, ((struct fd){.fd = fd, .data = data, .handler = handler}));
|
*fd_data = (struct fd) {
|
||||||
|
.fd = fd,
|
||||||
|
.data = data,
|
||||||
|
.handler = handler,
|
||||||
|
.deleted = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
tll_push_back(fdm->fds, fd_data);
|
||||||
|
|
||||||
struct epoll_event ev = {
|
struct epoll_event ev = {
|
||||||
.events = events,
|
.events = events,
|
||||||
.data = {.ptr = &tll_back(fdm->fds)},
|
.data = {.ptr = fd_data},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
|
if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
|
||||||
LOG_ERRNO("failed to register FD with epoll");
|
LOG_ERRNO("failed to register FD with epoll");
|
||||||
|
free(fd_data);
|
||||||
tll_pop_back(fdm->fds);
|
tll_pop_back(fdm->fds);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -84,14 +98,26 @@ fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
fdm_del(struct fdm *fdm, int fd)
|
fdm_del_internal(struct fdm *fdm, int fd, bool close_fd)
|
||||||
{
|
{
|
||||||
|
if (fd == -1)
|
||||||
|
return true;
|
||||||
|
|
||||||
tll_foreach(fdm->fds, it) {
|
tll_foreach(fdm->fds, it) {
|
||||||
if (it->item.fd == fd) {
|
if (it->item->fd == fd) {
|
||||||
if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0)
|
if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0)
|
||||||
LOG_ERRNO("failed to unregister FD=%d from epoll", fd);
|
LOG_ERRNO("failed to unregister FD=%d from epoll", fd);
|
||||||
|
|
||||||
|
if (close_fd)
|
||||||
|
close(it->item->fd);
|
||||||
|
|
||||||
|
it->item->deleted = true;
|
||||||
|
if (fdm->is_polling)
|
||||||
|
tll_push_back(fdm->deferred_delete, it->item);
|
||||||
|
else
|
||||||
|
free(it->item);
|
||||||
|
|
||||||
tll_remove(fdm->fds, it);
|
tll_remove(fdm->fds, it);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +127,18 @@ fdm_del(struct fdm *fdm, int fd)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
fdm_del(struct fdm *fdm, int fd)
|
||||||
|
{
|
||||||
|
return fdm_del_internal(fdm, fd, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
fdm_del_no_close(struct fdm *fdm, int fd)
|
||||||
|
{
|
||||||
|
return fdm_del_internal(fdm, fd, false);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
fdm_poll(struct fdm *fdm)
|
fdm_poll(struct fdm *fdm)
|
||||||
{
|
{
|
||||||
|
|
@ -114,10 +152,22 @@ fdm_poll(struct fdm *fdm)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fdm->is_polling = true;
|
||||||
for (int i = 0; i < ret; i++) {
|
for (int i = 0; i < ret; i++) {
|
||||||
struct fd *fd = events[i].data.ptr;
|
struct fd *fd = events[i].data.ptr;
|
||||||
if (!fd->handler(fdm, fd->fd, events[i].events, fd->data))
|
if (fd->deleted)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!fd->handler(fdm, fd->fd, events[i].events, fd->data)) {
|
||||||
|
fdm->is_polling = false;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fdm->is_polling = false;
|
||||||
|
|
||||||
|
tll_foreach(fdm->deferred_delete, it) {
|
||||||
|
free(it->item);
|
||||||
|
tll_remove(fdm->deferred_delete, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
1
fdm.h
1
fdm.h
|
|
@ -11,5 +11,6 @@ void fdm_destroy(struct fdm *fdm);
|
||||||
|
|
||||||
bool fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data);
|
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_del(struct fdm *fdm, int fd);
|
||||||
|
bool fdm_del_no_close(struct fdm *fdm, int fd);
|
||||||
|
|
||||||
bool fdm_poll(struct fdm *fdm);
|
bool fdm_poll(struct fdm *fdm);
|
||||||
|
|
|
||||||
52
font.c
52
font.c
|
|
@ -22,6 +22,13 @@ static mtx_t ft_lock;
|
||||||
|
|
||||||
static const size_t cache_size = 512;
|
static const size_t cache_size = 512;
|
||||||
|
|
||||||
|
struct font_cache_entry {
|
||||||
|
uint64_t hash;
|
||||||
|
struct font *font;
|
||||||
|
};
|
||||||
|
|
||||||
|
static tll(struct font_cache_entry) font_cache = tll_init();
|
||||||
|
|
||||||
static void __attribute__((constructor))
|
static void __attribute__((constructor))
|
||||||
init(void)
|
init(void)
|
||||||
{
|
{
|
||||||
|
|
@ -33,6 +40,9 @@ init(void)
|
||||||
static void __attribute__((destructor))
|
static void __attribute__((destructor))
|
||||||
fini(void)
|
fini(void)
|
||||||
{
|
{
|
||||||
|
while (tll_length(font_cache) > 0)
|
||||||
|
font_destroy(tll_front(font_cache).font);
|
||||||
|
|
||||||
mtx_destroy(&ft_lock);
|
mtx_destroy(&ft_lock);
|
||||||
FT_Done_FreeType(ft_lib);
|
FT_Done_FreeType(ft_lib);
|
||||||
FcFini();
|
FcFini();
|
||||||
|
|
@ -275,12 +285,46 @@ from_name(const char *name, bool is_fallback)
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
sdbm_hash(const char *s)
|
||||||
|
{
|
||||||
|
uint64_t hash = 0;
|
||||||
|
|
||||||
|
for (; *s != '\0'; s++) {
|
||||||
|
int c = *s;
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
font_hash(font_list_t names, const char *attributes)
|
||||||
|
{
|
||||||
|
uint64_t hash = 0;
|
||||||
|
tll_foreach(names, it)
|
||||||
|
hash ^= sdbm_hash(it->item);
|
||||||
|
|
||||||
|
if (attributes != NULL)
|
||||||
|
hash ^= sdbm_hash(attributes);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
struct font *
|
struct font *
|
||||||
font_from_name(font_list_t names, const char *attributes)
|
font_from_name(font_list_t names, const char *attributes)
|
||||||
{
|
{
|
||||||
if (tll_length(names) == 0)
|
if (tll_length(names) == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
uint64_t hash = font_hash(names, attributes);
|
||||||
|
tll_foreach(font_cache, it) {
|
||||||
|
if (it->item.hash == hash) {
|
||||||
|
it->item.font->ref_counter++;
|
||||||
|
return it->item.font;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct font *font = NULL;
|
struct font *font = NULL;
|
||||||
|
|
||||||
bool have_attrs = attributes != NULL && strlen(attributes) > 0;
|
bool have_attrs = attributes != NULL && strlen(attributes) > 0;
|
||||||
|
|
@ -312,6 +356,7 @@ font_from_name(font_list_t names, const char *attributes)
|
||||||
font->fallbacks, ((struct font_fallback){.pattern = strdup(name)}));
|
font->fallbacks, ((struct font_fallback){.pattern = strdup(name)}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tll_push_back(font_cache, ((struct font_cache_entry){.hash = hash, .font = font}));
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -605,6 +650,13 @@ font_destroy(struct font *font)
|
||||||
if (--font->ref_counter > 0)
|
if (--font->ref_counter > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
tll_foreach(font_cache, it) {
|
||||||
|
if (it->item.font == font) {
|
||||||
|
tll_remove(font_cache, it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
free(font->name);
|
free(font->name);
|
||||||
|
|
||||||
tll_foreach(font->fallbacks, it) {
|
tll_foreach(font->fallbacks, it) {
|
||||||
|
|
|
||||||
63
main.c
63
main.c
|
|
@ -5,6 +5,7 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
|
|
@ -16,6 +17,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "fdm.h"
|
#include "fdm.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
#include "server.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
@ -23,17 +25,38 @@
|
||||||
#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 volatile sig_atomic_t aborted = 0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sig_handler(int signo)
|
||||||
|
{
|
||||||
|
aborted = 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_usage(const char *prog_name)
|
print_usage(const char *prog_name)
|
||||||
{
|
{
|
||||||
printf("Usage: %s [OPTION]...\n", prog_name);
|
printf("Usage: %s [OPTIONS]...\n", prog_name);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -f,--font=FONT comma separated list of fonts in fontconfig format (monospace)\n"
|
printf(" -f,--font=FONT comma separated list of fonts in fontconfig format (monospace)\n"
|
||||||
" -t,--term=TERM value to set the environment variable TERM to (foot)\n"
|
" -t,--term=TERM value to set the environment variable TERM to (foot)\n"
|
||||||
" -g,--geometry=WIDTHxHEIGHT set initial width and height\n"
|
" -g,--geometry=WIDTHxHEIGHT set initial width and height\n"
|
||||||
|
" -s,--server run as a server\n"
|
||||||
" -v,--version show the version number and quit\n");
|
" -v,--version show the version number and quit\n");
|
||||||
printf("\n");
|
}
|
||||||
|
|
||||||
|
struct shutdown_context {
|
||||||
|
struct terminal **term;
|
||||||
|
int exit_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
term_shutdown_cb(void *data, int exit_code)
|
||||||
|
{
|
||||||
|
struct shutdown_context *ctx = data;
|
||||||
|
*ctx->term = NULL;
|
||||||
|
ctx->exit_code = exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
@ -55,11 +78,14 @@ main(int argc, char *const *argv)
|
||||||
{"term", required_argument, 0, 't'},
|
{"term", required_argument, 0, 't'},
|
||||||
{"font", required_argument, 0, 'f'},
|
{"font", required_argument, 0, 'f'},
|
||||||
{"geometry", required_argument, 0, 'g'},
|
{"geometry", required_argument, 0, 'g'},
|
||||||
|
{"server", no_argument, 0, 's'},
|
||||||
{"version", no_argument, 0, 'v'},
|
{"version", no_argument, 0, 'v'},
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
{NULL, no_argument, 0, 0},
|
{NULL, no_argument, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool as_server = false;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int c = getopt_long(argc, argv, ":t:f:g:vh", longopts, NULL);
|
int c = getopt_long(argc, argv, ":t:f:g:vh", longopts, NULL);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
|
|
@ -105,6 +131,10 @@ main(int argc, char *const *argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
as_server = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
printf("foot version %s\n", FOOT_VERSION);
|
printf("foot version %s\n", FOOT_VERSION);
|
||||||
config_free(conf);
|
config_free(conf);
|
||||||
|
|
@ -131,11 +161,12 @@ main(int argc, char *const *argv)
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
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;
|
||||||
|
struct server *server = NULL;
|
||||||
|
struct shutdown_context shutdown_ctx = {.term = &term, .exit_code = EXIT_FAILURE};
|
||||||
|
|
||||||
if ((fdm = fdm_init()) == NULL)
|
if ((fdm = fdm_init()) == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -143,27 +174,37 @@ main(int argc, char *const *argv)
|
||||||
if ((wayl = wayl_init(fdm)) == NULL)
|
if ((wayl = wayl_init(fdm)) == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if ((term = term_init(&conf, fdm, wayl, argc, argv)) == NULL)
|
if (!as_server && (term = term_init(&conf, fdm, wayl, conf.term, argc, argv,
|
||||||
|
&term_shutdown_cb, &shutdown_ctx)) == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
while (true) {
|
if (as_server && (server = server_init(&conf, fdm, wayl)) == NULL)
|
||||||
wl_display_flush(term->wl->display); /* TODO: figure out how to get rid of this */
|
goto out;
|
||||||
|
|
||||||
|
const struct sigaction sa = {.sa_handler = &sig_handler};
|
||||||
|
if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0) {
|
||||||
|
LOG_ERRNO("failed to register signal handlers");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (as_server)
|
||||||
|
LOG_INFO("running as server; launch terminals by running footclient");
|
||||||
|
|
||||||
|
while (!aborted && (as_server || tll_length(wayl->terms) > 0)) {
|
||||||
if (!fdm_poll(fdm))
|
if (!fdm_poll(fdm))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (term->quit)
|
ret = aborted || tll_length(wayl->terms) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
ret = EXIT_SUCCESS;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
shm_fini();
|
shm_fini();
|
||||||
|
|
||||||
int child_ret = term_destroy(term);
|
server_destroy(server);
|
||||||
|
term_destroy(term);
|
||||||
wayl_destroy(wayl);
|
wayl_destroy(wayl);
|
||||||
fdm_destroy(fdm);
|
fdm_destroy(fdm);
|
||||||
config_free(conf);
|
config_free(conf);
|
||||||
|
|
||||||
return ret == EXIT_SUCCESS ? child_ret : ret;
|
return ret == EXIT_SUCCESS && !as_server ? shutdown_ctx.exit_code : ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ executable(
|
||||||
'render.c', 'render.h',
|
'render.c', 'render.h',
|
||||||
'search.c', 'search.h',
|
'search.c', 'search.h',
|
||||||
'selection.c', 'selection.h',
|
'selection.c', 'selection.h',
|
||||||
|
'server.c', 'server.h',
|
||||||
'shm.c', 'shm.h',
|
'shm.c', 'shm.h',
|
||||||
'slave.c', 'slave.h',
|
'slave.c', 'slave.h',
|
||||||
'terminal.c', 'terminal.h',
|
'terminal.c', 'terminal.h',
|
||||||
|
|
@ -89,6 +90,12 @@ executable(
|
||||||
dependencies: [threads, math, freetype, fontconfig, pixman, wayland_client, wayland_cursor, xkb],
|
dependencies: [threads, math, freetype, fontconfig, pixman, wayland_client, wayland_cursor, xkb],
|
||||||
install: true)
|
install: true)
|
||||||
|
|
||||||
|
executable(
|
||||||
|
'footclient',
|
||||||
|
'client.c',
|
||||||
|
'log.c', 'log.h',
|
||||||
|
install: true)
|
||||||
|
|
||||||
custom_target(
|
custom_target(
|
||||||
'terminfo',
|
'terminfo',
|
||||||
output: 'f',
|
output: 'f',
|
||||||
|
|
|
||||||
16
render.c
16
render.c
|
|
@ -693,6 +693,7 @@ grid_render(struct terminal *term)
|
||||||
|
|
||||||
if (all_clean) {
|
if (all_clean) {
|
||||||
buf->busy = false;
|
buf->busy = false;
|
||||||
|
wl_display_flush(term->wl->display);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -727,6 +728,7 @@ grid_render(struct terminal *term)
|
||||||
LOG_INFO("frame rendered in %lds %ldus",
|
LOG_INFO("frame rendered in %lds %ldus",
|
||||||
render_time.tv_sec, render_time.tv_usec);
|
render_time.tv_sec, render_time.tv_usec);
|
||||||
#endif
|
#endif
|
||||||
|
wl_display_flush(term->wl->display);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -948,9 +950,21 @@ render_resize(struct terminal *term, int width, int height)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
render_set_title(struct terminal *term, const char *title)
|
render_set_title(struct terminal *term, const char *_title)
|
||||||
{
|
{
|
||||||
|
/* TODO: figure out what the limit actually is */
|
||||||
|
static const size_t max_len = 100;
|
||||||
|
|
||||||
|
const char *title = _title;
|
||||||
|
char *copy = NULL;
|
||||||
|
|
||||||
|
if (strlen(title) > max_len) {
|
||||||
|
copy = strndup(_title, max_len);
|
||||||
|
title = copy;
|
||||||
|
}
|
||||||
|
|
||||||
xdg_toplevel_set_title(term->window->xdg_toplevel, title);
|
xdg_toplevel_set_title(term->window->xdg_toplevel, title);
|
||||||
|
free(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
324
server.c
Normal file
324
server.c
Normal file
|
|
@ -0,0 +1,324 @@
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <linux/un.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "server"
|
||||||
|
#define LOG_ENABLE_DBG 1
|
||||||
|
#include "log.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
#include "tllist.h"
|
||||||
|
|
||||||
|
struct client;
|
||||||
|
struct server {
|
||||||
|
const struct config *conf;
|
||||||
|
struct fdm *fdm;
|
||||||
|
struct wayland *wayl;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
char *sock_path;
|
||||||
|
|
||||||
|
tll(struct client *) clients;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct client {
|
||||||
|
struct server *server;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
struct terminal *term;
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
client_destroy(struct client *client)
|
||||||
|
{
|
||||||
|
if (client == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (client->term != NULL) {
|
||||||
|
LOG_WARN("client FD=%d: terminal still alive", client->fd);
|
||||||
|
term_destroy(client->term);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->argv != NULL) {
|
||||||
|
for (int i = 0; i < client->argc; i++)
|
||||||
|
free(client->argv[i]);
|
||||||
|
free(client->argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->fd != -1) {
|
||||||
|
LOG_DBG("client FD=%d: disconnected", client->fd);
|
||||||
|
fdm_del(client->server->fdm, client->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
tll_foreach(client->server->clients, it) {
|
||||||
|
if (it->item == client) {
|
||||||
|
tll_remove(client->server->clients, it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
client_send_exit_code(struct client *client, int exit_code)
|
||||||
|
{
|
||||||
|
if (client->fd == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (write(client->fd, &exit_code, sizeof(exit_code)) != sizeof(exit_code))
|
||||||
|
LOG_ERRNO("failed to write slave exit code to client");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
term_shutdown_handler(void *data, int exit_code)
|
||||||
|
{
|
||||||
|
struct client *client = data;
|
||||||
|
|
||||||
|
client_send_exit_code(client, exit_code);
|
||||||
|
|
||||||
|
client->term = NULL;
|
||||||
|
client_destroy(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
{
|
||||||
|
struct client *client = data;
|
||||||
|
struct server *server = client->server;
|
||||||
|
char *term_env = NULL;
|
||||||
|
|
||||||
|
if (events & EPOLLHUP)
|
||||||
|
goto shutdown;
|
||||||
|
|
||||||
|
assert(events & EPOLLIN);
|
||||||
|
|
||||||
|
uint16_t term_env_len;
|
||||||
|
if (recv(fd, &term_env_len, sizeof(term_env_len), 0) != sizeof(term_env_len))
|
||||||
|
goto shutdown;
|
||||||
|
|
||||||
|
term_env = malloc(term_env_len + 1);
|
||||||
|
term_env[term_env_len] = '\0';
|
||||||
|
if (recv(fd, term_env, term_env_len, 0) != term_env_len)
|
||||||
|
goto shutdown;
|
||||||
|
|
||||||
|
if (recv(fd, &client->argc, sizeof(client->argc), 0) != sizeof(client->argc))
|
||||||
|
goto shutdown;
|
||||||
|
|
||||||
|
client->argv = calloc(client->argc + 1, sizeof(client->argv[0]));
|
||||||
|
for (int i = 0; i < client->argc; i++) {
|
||||||
|
uint16_t len;
|
||||||
|
if (recv(fd, &len, sizeof(len), 0) != sizeof(len))
|
||||||
|
goto shutdown;
|
||||||
|
|
||||||
|
client->argv[i] = malloc(len + 1);
|
||||||
|
client->argv[i][len] = '\0';
|
||||||
|
if (recv(fd, client->argv[i], len, 0) != len)
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(client->term == NULL);
|
||||||
|
client->term = term_init(
|
||||||
|
server->conf, server->fdm, server->wayl,
|
||||||
|
term_env_len > 0 ? term_env : server->conf->term,
|
||||||
|
client->argc, client->argv, &term_shutdown_handler, client);
|
||||||
|
|
||||||
|
if (client->term == NULL) {
|
||||||
|
LOG_ERR("failed to instantiate new terminal");
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(term_env);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
shutdown:
|
||||||
|
LOG_DBG("client FD=%d: disconnected", client->fd);
|
||||||
|
|
||||||
|
free(term_env);
|
||||||
|
|
||||||
|
fdm_del(fdm, fd);
|
||||||
|
client->fd = -1;
|
||||||
|
|
||||||
|
if (client->term != NULL && !client->term->is_shutting_down)
|
||||||
|
term_shutdown(client->term);
|
||||||
|
else
|
||||||
|
client_destroy(client);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
fdm_server(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
{
|
||||||
|
if (events & EPOLLHUP)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct server *server = data;
|
||||||
|
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
socklen_t addr_size = sizeof(addr);
|
||||||
|
int client_fd = accept4(
|
||||||
|
server->fd, (struct sockaddr *)&addr, &addr_size, SOCK_CLOEXEC);
|
||||||
|
|
||||||
|
if (client_fd == -1) {
|
||||||
|
LOG_ERRNO("failed to accept client connection");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct client *client = malloc(sizeof(*client));
|
||||||
|
*client = (struct client) {
|
||||||
|
.server = server,
|
||||||
|
.fd = client_fd,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!fdm_add(server->fdm, client_fd, EPOLLIN, &fdm_client, client)) {
|
||||||
|
LOG_ERR("client FD=%d: failed to add client to FDM", client_fd);
|
||||||
|
close(client_fd);
|
||||||
|
free(client);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("client FD=%d: connected", client_fd);
|
||||||
|
tll_push_back(server->clients, client);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
get_socket_path(void)
|
||||||
|
{
|
||||||
|
const char *xdg_runtime = getenv("XDG_RUNTIME_DIR");
|
||||||
|
if (xdg_runtime == NULL)
|
||||||
|
return strdup("/tmp/foot.sock");
|
||||||
|
|
||||||
|
char *path = malloc(strlen(xdg_runtime) + 1 + strlen("foot.sock") + 1);
|
||||||
|
sprintf(path, "%s/foot.sock", xdg_runtime);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum connect_status {CONNECT_ERR, CONNECT_FAIL, CONNECT_SUCCESS};
|
||||||
|
|
||||||
|
static enum connect_status
|
||||||
|
try_connect(const char *sock_path)
|
||||||
|
{
|
||||||
|
enum connect_status ret = CONNECT_ERR;
|
||||||
|
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd == -1) {
|
||||||
|
LOG_ERRNO("failed to create UNIX socket");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||||
|
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
ret = connect(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0
|
||||||
|
? CONNECT_FAIL : CONNECT_SUCCESS;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (fd != -1)
|
||||||
|
close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct server *
|
||||||
|
server_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl)
|
||||||
|
{
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd == -1) {
|
||||||
|
LOG_ERRNO("failed to create UNIX socket");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct server *server = NULL;
|
||||||
|
char *sock_path = NULL;
|
||||||
|
|
||||||
|
if ((sock_path = get_socket_path()) == NULL)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
switch (try_connect(sock_path)) {
|
||||||
|
case CONNECT_FAIL:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONNECT_SUCCESS:
|
||||||
|
LOG_ERR("foot --server already running");
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
|
case CONNECT_ERR:
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink(sock_path);
|
||||||
|
|
||||||
|
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FD_CLOEXEC) < 0) {
|
||||||
|
LOG_ERRNO("failed to set FD_CLOEXEC on socket");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||||
|
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||||
|
LOG_ERRNO("%s: failed to bind", addr.sun_path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listen(fd, 0) < 0) {
|
||||||
|
LOG_ERRNO("%s: failed to listen", addr.sun_path);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = malloc(sizeof(*server));
|
||||||
|
*server = (struct server) {
|
||||||
|
.conf = conf,
|
||||||
|
.fdm = fdm,
|
||||||
|
.wayl = wayl,
|
||||||
|
|
||||||
|
.fd = fd,
|
||||||
|
.sock_path = sock_path,
|
||||||
|
|
||||||
|
.clients = tll_init(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!fdm_add(fdm, fd, EPOLLIN, &fdm_server, server)) {
|
||||||
|
LOG_ERR("failed to add server FD to the FDM");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return server;
|
||||||
|
|
||||||
|
err:
|
||||||
|
free(server);
|
||||||
|
free(sock_path);
|
||||||
|
if (fd != -1)
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
server_destroy(struct server *server)
|
||||||
|
{
|
||||||
|
if (server == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_DBG("server destroy, %zu clients still alive",
|
||||||
|
tll_length(server->clients));
|
||||||
|
|
||||||
|
tll_foreach(server->clients, it) {
|
||||||
|
client_send_exit_code(it->item, 1);
|
||||||
|
client_destroy(it->item);
|
||||||
|
}
|
||||||
|
|
||||||
|
tll_free(server->clients);
|
||||||
|
|
||||||
|
fdm_del(server->fdm, server->fd);
|
||||||
|
free(server->sock_path);
|
||||||
|
free(server);
|
||||||
|
}
|
||||||
9
server.h
Normal file
9
server.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "fdm.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "wayland.h"
|
||||||
|
|
||||||
|
struct server;
|
||||||
|
struct server *server_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl);
|
||||||
|
void server_destroy(struct server *server);
|
||||||
13
slave.c
13
slave.c
|
|
@ -1,5 +1,6 @@
|
||||||
#define _XOPEN_SOURCE 500
|
#define _XOPEN_SOURCE 500
|
||||||
#include "slave.h"
|
#include "slave.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
@ -68,7 +69,7 @@ err:
|
||||||
|
|
||||||
pid_t
|
pid_t
|
||||||
slave_spawn(int ptmx, int argc, char *const *argv,
|
slave_spawn(int ptmx, int argc, char *const *argv,
|
||||||
const char *conf_shell)
|
const char *term_env, const char *conf_shell)
|
||||||
{
|
{
|
||||||
int fork_pipe[2];
|
int fork_pipe[2];
|
||||||
if (pipe2(fork_pipe, O_CLOEXEC) < 0) {
|
if (pipe2(fork_pipe, O_CLOEXEC) < 0) {
|
||||||
|
|
@ -88,6 +89,8 @@ slave_spawn(int ptmx, int argc, char *const *argv,
|
||||||
/* Child */
|
/* Child */
|
||||||
close(fork_pipe[0]); /* Close read end */
|
close(fork_pipe[0]); /* Close read end */
|
||||||
|
|
||||||
|
setenv("TERM", term_env, 1);
|
||||||
|
|
||||||
char **_shell_argv = NULL;
|
char **_shell_argv = NULL;
|
||||||
char *const *shell_argv = argv;
|
char *const *shell_argv = argv;
|
||||||
|
|
||||||
|
|
@ -107,7 +110,7 @@ slave_spawn(int ptmx, int argc, char *const *argv,
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
close(fork_pipe[1]); /* Close write end */
|
close(fork_pipe[1]); /* Close write end */
|
||||||
LOG_DBG("slave has PID %d", term->slave);
|
LOG_DBG("slave has PID %d", pid);
|
||||||
|
|
||||||
int _errno;
|
int _errno;
|
||||||
static_assert(sizeof(errno) == sizeof(_errno), "errno size mismatch");
|
static_assert(sizeof(errno) == sizeof(_errno), "errno size mismatch");
|
||||||
|
|
@ -119,11 +122,11 @@ slave_spawn(int ptmx, int argc, char *const *argv,
|
||||||
LOG_ERRNO("failed to read from pipe");
|
LOG_ERRNO("failed to read from pipe");
|
||||||
return -1;
|
return -1;
|
||||||
} else if (ret == sizeof(_errno)) {
|
} else if (ret == sizeof(_errno)) {
|
||||||
LOG_ERRNO(
|
LOG_ERRNO_P(
|
||||||
"%s: failed to execute", argc == 0 ? conf_shell : argv[0]);
|
"%s: failed to execute", _errno, argc == 0 ? conf_shell : argv[0]);
|
||||||
return -1;
|
return -1;
|
||||||
} else
|
} else
|
||||||
LOG_DBG("%s: successfully started", conf->shell);
|
LOG_DBG("%s: successfully started", conf_shell);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
slave.h
2
slave.h
|
|
@ -4,4 +4,4 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
pid_t slave_spawn(
|
pid_t slave_spawn(
|
||||||
int ptmx, int argc, char *const *argv, const char *conf_shell);
|
int ptmx, int argc, char *const *argv, const char *term_env, const char *conf_shell);
|
||||||
|
|
|
||||||
256
terminal.c
256
terminal.c
|
|
@ -8,6 +8,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/eventfd.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>
|
||||||
|
|
@ -30,12 +31,8 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
|
||||||
{
|
{
|
||||||
struct terminal *term = data;
|
struct terminal *term = data;
|
||||||
|
|
||||||
if (events & EPOLLHUP) {
|
if ((events & EPOLLHUP) && !(events & EPOLLIN))
|
||||||
term->quit = true;
|
return term_shutdown(term);
|
||||||
|
|
||||||
if (!(events & EPOLLIN))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(events & EPOLLIN);
|
assert(events & EPOLLIN);
|
||||||
|
|
||||||
|
|
@ -43,9 +40,6 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
|
||||||
ssize_t count = read(term->ptmx, buf, sizeof(buf));
|
ssize_t count = read(term->ptmx, buf, sizeof(buf));
|
||||||
|
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
if (errno == EAGAIN)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
LOG_ERRNO("failed to read from pseudo terminal");
|
LOG_ERRNO("failed to read from pseudo terminal");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +95,10 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return !(events & EPOLLHUP);
|
if (events & EPOLLHUP)
|
||||||
|
return term_shutdown(term);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -116,9 +113,6 @@ fdm_flash(struct fdm *fdm, int fd, int events, void *data)
|
||||||
term->flash.fd, &expiration_count, sizeof(expiration_count));
|
term->flash.fd, &expiration_count, sizeof(expiration_count));
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno == EAGAIN)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
LOG_ERRNO("failed to read flash timer");
|
LOG_ERRNO("failed to read flash timer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -144,9 +138,6 @@ fdm_blink(struct fdm *fdm, int fd, int events, void *data)
|
||||||
term->blink.fd, &expiration_count, sizeof(expiration_count));
|
term->blink.fd, &expiration_count, sizeof(expiration_count));
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno == EAGAIN)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
LOG_ERRNO("failed to read blink timer");
|
LOG_ERRNO("failed to read blink timer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +172,8 @@ fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
struct terminal *term = data;
|
struct terminal *term = data;
|
||||||
assert(term->delayed_render_timer.is_armed);
|
if (!term->delayed_render_timer.is_armed)
|
||||||
|
return true;
|
||||||
|
|
||||||
uint64_t unused;
|
uint64_t unused;
|
||||||
ssize_t ret1 = 0;
|
ssize_t ret1 = 0;
|
||||||
|
|
@ -192,17 +184,18 @@ fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data)
|
||||||
if (fd == term->delayed_render_timer.upper_fd)
|
if (fd == term->delayed_render_timer.upper_fd)
|
||||||
ret2 = read(term->delayed_render_timer.upper_fd, &unused, sizeof(unused));
|
ret2 = read(term->delayed_render_timer.upper_fd, &unused, sizeof(unused));
|
||||||
|
|
||||||
if ((ret1 < 0 || ret2 < 0) && errno != EAGAIN)
|
if ((ret1 < 0 || ret2 < 0)) {
|
||||||
LOG_ERRNO("failed to read timeout timer");
|
LOG_ERRNO("failed to read timeout timer");
|
||||||
else if (ret1 > 0 || ret2 > 0) {
|
return false;
|
||||||
render_refresh(term);
|
}
|
||||||
|
|
||||||
/* Reset timers */
|
render_refresh(term);
|
||||||
term->delayed_render_timer.is_armed = false;
|
|
||||||
timerfd_settime(term->delayed_render_timer.lower_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL);
|
/* Reset timers */
|
||||||
timerfd_settime(term->delayed_render_timer.upper_fd, 0, &(struct itimerspec){.it_value = {0}}, NULL);
|
struct itimerspec reset = {{0}};
|
||||||
} else
|
timerfd_settime(term->delayed_render_timer.lower_fd, 0, &reset, NULL);
|
||||||
assert(false);
|
timerfd_settime(term->delayed_render_timer.upper_fd, 0, &reset, NULL);
|
||||||
|
term->delayed_render_timer.is_armed = false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +289,8 @@ initialize_fonts(struct terminal *term, const struct config *conf)
|
||||||
|
|
||||||
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)
|
const char *term_env, int argc, char *const *argv,
|
||||||
|
void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data)
|
||||||
{
|
{
|
||||||
int ptmx = -1;
|
int ptmx = -1;
|
||||||
int flash_fd = -1;
|
int flash_fd = -1;
|
||||||
|
|
@ -304,29 +298,50 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
int delay_lower_fd = -1;
|
int delay_lower_fd = -1;
|
||||||
int delay_upper_fd = -1;
|
int delay_upper_fd = -1;
|
||||||
|
|
||||||
struct terminal *term = NULL;
|
struct terminal *term = malloc(sizeof(*term));
|
||||||
|
|
||||||
if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) == -1) {
|
if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) == -1) {
|
||||||
LOG_ERRNO("failed to open PTY");
|
LOG_ERRNO("failed to open PTY");
|
||||||
goto close_fds;
|
goto close_fds;
|
||||||
}
|
}
|
||||||
if ((flash_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) {
|
if ((flash_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC)) == -1) {
|
||||||
LOG_ERRNO("failed to create flash timer FD");
|
LOG_ERRNO("failed to create flash timer FD");
|
||||||
goto close_fds;
|
goto close_fds;
|
||||||
}
|
}
|
||||||
if ((blink_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) {
|
if ((blink_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC)) == -1) {
|
||||||
LOG_ERRNO("failed to create blink timer FD");
|
LOG_ERRNO("failed to create blink timer FD");
|
||||||
goto close_fds;
|
goto close_fds;
|
||||||
}
|
}
|
||||||
if ((delay_lower_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1 ||
|
if ((delay_lower_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC)) == -1 ||
|
||||||
(delay_upper_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1)
|
(delay_upper_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC)) == -1)
|
||||||
{
|
{
|
||||||
LOG_ERRNO("failed to create delayed rendering timer FDs");
|
LOG_ERRNO("failed to create delayed rendering timer FDs");
|
||||||
goto close_fds;
|
goto close_fds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fdm_add(fdm, ptmx, EPOLLIN, &fdm_ptmx, term)) {
|
||||||
|
LOG_ERR("failed to add ptmx to FDM");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fdm_add(fdm, flash_fd, EPOLLIN, &fdm_flash, term)) {
|
||||||
|
LOG_ERR("failed to add flash timer FD to FDM");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fdm_add(fdm, blink_fd, EPOLLIN, &fdm_blink, term)) {
|
||||||
|
LOG_ERR("failed to add blink tiemr FD to FDM");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize configure-based terminal attributes */
|
/* Initialize configure-based terminal attributes */
|
||||||
term = malloc(sizeof(*term));
|
|
||||||
*term = (struct terminal) {
|
*term = (struct terminal) {
|
||||||
.fdm = fdm,
|
.fdm = fdm,
|
||||||
.quit = false,
|
.quit = false,
|
||||||
|
|
@ -397,6 +412,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
.lower_fd = delay_lower_fd,
|
.lower_fd = delay_lower_fd,
|
||||||
.upper_fd = delay_upper_fd,
|
.upper_fd = delay_upper_fd,
|
||||||
},
|
},
|
||||||
|
.shutdown_cb = shutdown_cb,
|
||||||
|
.shutdown_data = shutdown_data,
|
||||||
};
|
};
|
||||||
|
|
||||||
initialize_color_cube(term);
|
initialize_color_cube(term);
|
||||||
|
|
@ -410,6 +427,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
term->cell_height = (int)ceil(term->fextents.height);
|
term->cell_height = (int)ceil(term->fextents.height);
|
||||||
LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height);
|
LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height);
|
||||||
|
|
||||||
|
/* Start the slave/client */
|
||||||
|
if ((term->slave = slave_spawn(term->ptmx, argc, argv, term_env, conf->shell)) == -1)
|
||||||
|
goto err;
|
||||||
|
|
||||||
/* Initiailze the Wayland window backend */
|
/* Initiailze the Wayland window backend */
|
||||||
if ((term->window = wayl_win_init(wayl)) == NULL)
|
if ((term->window = wayl_win_init(wayl)) == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
@ -432,47 +453,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
height = max(height, term->cell_height);
|
height = max(height, term->cell_height);
|
||||||
render_resize(term, width, height);
|
render_resize(term, width, height);
|
||||||
|
|
||||||
/* Start the slave/client */
|
tll_push_back(wayl->terms, term);
|
||||||
if (!slave_spawn(term->ptmx, argc, argv, conf->shell))
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
/* Read logic requires non-blocking mode */
|
|
||||||
{
|
|
||||||
int fd_flags = fcntl(ptmx, F_GETFL);
|
|
||||||
if (fd_flags == -1) {
|
|
||||||
LOG_ERRNO("failed to set non blocking mode on PTY master");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fcntl(ptmx, F_SETFL, fd_flags | O_NONBLOCK) == -1) {
|
|
||||||
LOG_ERRNO("failed to set non blocking mode on PTY master");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fdm_add(fdm, ptmx, EPOLLIN, &fdm_ptmx, term)) {
|
|
||||||
LOG_ERR("failed to add ptmx to FDM");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fdm_add(fdm, flash_fd, EPOLLIN, &fdm_flash, term)) {
|
|
||||||
LOG_ERR("failed to add flash timer FD to FDM");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fdm_add(fdm, blink_fd, EPOLLIN, &fdm_blink, term)) {
|
|
||||||
LOG_ERR("failed to add blink tiemr FD to FDM");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 err;
|
|
||||||
}
|
|
||||||
|
|
||||||
wayl->term = term;
|
|
||||||
return term;
|
return term;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
|
@ -480,37 +461,109 @@ err:
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
close_fds:
|
close_fds:
|
||||||
if (ptmx != -1)
|
fdm_del(fdm, ptmx);
|
||||||
close(ptmx);
|
fdm_del(fdm, flash_fd);
|
||||||
if (flash_fd != -1)
|
fdm_del(fdm, blink_fd);
|
||||||
close(flash_fd);
|
fdm_del(fdm, delay_lower_fd);
|
||||||
if (blink_fd != -1)
|
fdm_del(fdm, delay_upper_fd);
|
||||||
close(blink_fd);
|
|
||||||
if (delay_lower_fd != -1)
|
free(term);
|
||||||
close(delay_lower_fd);
|
|
||||||
if (delay_upper_fd != -1)
|
|
||||||
close(delay_upper_fd);
|
|
||||||
assert(term == NULL);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
fdm_shutdown(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
{
|
||||||
|
LOG_DBG("FDM shutdown");
|
||||||
|
struct terminal *term = data;
|
||||||
|
|
||||||
|
/* Kill the event FD */
|
||||||
|
fdm_del(term->fdm, fd);
|
||||||
|
|
||||||
|
wayl_win_destroy(term->window);
|
||||||
|
term->window = NULL;
|
||||||
|
|
||||||
|
struct wayland *wayl __attribute__((unused)) = term->wl;
|
||||||
|
assert(wayl->focused != term);
|
||||||
|
assert(wayl->moused != term);
|
||||||
|
|
||||||
|
void (*cb)(void *, int) = term->shutdown_cb;
|
||||||
|
void *cb_data = term->shutdown_data;
|
||||||
|
|
||||||
|
int exit_code = term_destroy(term);
|
||||||
|
if (cb != NULL)
|
||||||
|
cb(cb_data, exit_code);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
term_shutdown(struct terminal *term)
|
||||||
|
{
|
||||||
|
if (term->is_shutting_down)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
term->is_shutting_down = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close FDs then postpone self-destruction to the next poll
|
||||||
|
* iteration, by creating an event FD that we trigger immediately.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
||||||
|
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
||||||
|
fdm_del(term->fdm, term->blink.fd);
|
||||||
|
fdm_del(term->fdm, term->flash.fd);
|
||||||
|
fdm_del(term->fdm, term->ptmx);
|
||||||
|
|
||||||
|
term->delayed_render_timer.lower_fd = -1;
|
||||||
|
term->delayed_render_timer.upper_fd = -1;
|
||||||
|
term->blink.fd = -1;
|
||||||
|
term->flash.fd = -1;
|
||||||
|
term->ptmx = -1;
|
||||||
|
|
||||||
|
int event_fd = eventfd(0, EFD_CLOEXEC);
|
||||||
|
if (event_fd == -1) {
|
||||||
|
LOG_ERRNO("failed to create terminal shutdown event FD");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fdm_add(term->fdm, event_fd, EPOLLIN, &fdm_shutdown, term)) {
|
||||||
|
LOG_ERR("failed to add terminal shutdown event FD to the FDM");
|
||||||
|
close(event_fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(event_fd, &(uint64_t){1}, sizeof(uint64_t)) != sizeof(uint64_t)) {
|
||||||
|
LOG_ERRNO("failed to send terminal shutdown event");
|
||||||
|
fdm_del(term->fdm, event_fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
term_destroy(struct terminal *term)
|
term_destroy(struct terminal *term)
|
||||||
{
|
{
|
||||||
if (term == NULL)
|
if (term == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
wayl_win_destroy(term->window);
|
tll_foreach(term->wl->terms, it) {
|
||||||
|
if (it->item == term) {
|
||||||
if (term->delayed_render_timer.lower_fd != -1) {
|
tll_remove(term->wl->terms, it);
|
||||||
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
break;
|
||||||
close(term->delayed_render_timer.lower_fd);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (term->delayed_render_timer.upper_fd != -1) {
|
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
||||||
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
||||||
close(term->delayed_render_timer.upper_fd);
|
fdm_del(term->fdm, term->blink.fd);
|
||||||
}
|
fdm_del(term->fdm, term->flash.fd);
|
||||||
|
fdm_del(term->fdm, term->ptmx);
|
||||||
|
|
||||||
|
if (term->window != NULL)
|
||||||
|
wayl_win_destroy(term->window);
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -546,21 +599,6 @@ term_destroy(struct terminal *term)
|
||||||
|
|
||||||
free(term->search.buf);
|
free(term->search.buf);
|
||||||
|
|
||||||
if (term->flash.fd != -1) {
|
|
||||||
fdm_del(term->fdm, term->flash.fd);
|
|
||||||
close(term->flash.fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
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++) {
|
for (size_t i = 0; i < term->render.workers.count; i++) {
|
||||||
if (term->render.workers.threads[i] != 0)
|
if (term->render.workers.threads[i] != 0)
|
||||||
thrd_join(term->render.workers.threads[i], NULL);
|
thrd_join(term->render.workers.threads[i], NULL);
|
||||||
|
|
|
||||||
|
|
@ -287,12 +287,19 @@ struct terminal {
|
||||||
int lower_fd;
|
int lower_fd;
|
||||||
int upper_fd;
|
int upper_fd;
|
||||||
} delayed_render_timer;
|
} delayed_render_timer;
|
||||||
|
|
||||||
|
bool is_shutting_down;
|
||||||
|
void (*shutdown_cb)(void *data, int exit_code);
|
||||||
|
void *shutdown_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct config;
|
struct config;
|
||||||
struct terminal *term_init(
|
struct terminal *term_init(
|
||||||
const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
int argc, char *const *argv);
|
const char *term_env, int argc, char *const *argv,
|
||||||
|
void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data);
|
||||||
|
|
||||||
|
bool term_shutdown(struct terminal *term);
|
||||||
int term_destroy(struct terminal *term);
|
int term_destroy(struct terminal *term);
|
||||||
|
|
||||||
void term_reset(struct terminal *term, bool hard);
|
void term_reset(struct terminal *term, bool hard);
|
||||||
|
|
|
||||||
90
wayland.c
90
wayland.c
|
|
@ -123,8 +123,8 @@ output_scale(void *data, struct wl_output *wl_output, int32_t factor)
|
||||||
|
|
||||||
mon->scale = factor;
|
mon->scale = factor;
|
||||||
|
|
||||||
struct terminal *term = mon->wayl->term;
|
tll_foreach(mon->wayl->terms, it) {
|
||||||
if (term != NULL) {
|
struct terminal *term = it->item;
|
||||||
render_resize(term, term->width / term->scale, term->height / term->scale);
|
render_resize(term, term->width / term->scale, term->height / term->scale);
|
||||||
wayl_reload_cursor_theme(mon->wayl, term);
|
wayl_reload_cursor_theme(mon->wayl, term);
|
||||||
}
|
}
|
||||||
|
|
@ -325,9 +325,7 @@ xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
||||||
struct wayland *wayl = data;
|
struct wayland *wayl = data;
|
||||||
struct terminal *term = wayl_terminal_from_xdg_toplevel(wayl, xdg_toplevel);
|
struct terminal *term = wayl_terminal_from_xdg_toplevel(wayl, xdg_toplevel);
|
||||||
LOG_DBG("xdg-toplevel: close");
|
LOG_DBG("xdg-toplevel: close");
|
||||||
|
term_shutdown(term);
|
||||||
term->quit = true;
|
|
||||||
wl_display_roundtrip(wayl->display);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||||
|
|
@ -394,7 +392,7 @@ fdm_wayl(struct fdm *fdm, int fd, int events, void *data)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return event_count != -1 && !wayl->term->quit;
|
return event_count != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -409,9 +407,6 @@ fdm_repeat(struct fdm *fdm, int fd, int events, void *data)
|
||||||
wayl->kbd.repeat.fd, &expiration_count, sizeof(expiration_count));
|
wayl->kbd.repeat.fd, &expiration_count, sizeof(expiration_count));
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno == EAGAIN)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
LOG_ERRNO("failed to read repeat key from repeat timer fd");
|
LOG_ERRNO("failed to read repeat key from repeat timer fd");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -515,23 +510,16 @@ wayl_init(struct fdm *fdm)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
wayl->kbd.repeat.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK);
|
/* All wayland initialization done - make it so */
|
||||||
|
wl_display_roundtrip(wayl->display);
|
||||||
|
|
||||||
|
wayl->kbd.repeat.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC);
|
||||||
if (wayl->kbd.repeat.fd == -1) {
|
if (wayl->kbd.repeat.fd == -1) {
|
||||||
LOG_ERRNO("failed to create keyboard repeat timer FD");
|
LOG_ERRNO("failed to create keyboard repeat timer FD");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wl_fd = wl_display_get_fd(wayl->display);
|
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)) {
|
if (!fdm_add(fdm, wl_fd, EPOLLIN, &fdm_wayl, wayl)) {
|
||||||
LOG_ERR("failed to register Wayland connection with the FDM");
|
LOG_ERR("failed to register Wayland connection with the FDM");
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -542,8 +530,6 @@ wayl_init(struct fdm *fdm)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
//wl_display_dispatch_pending(wayl->display);
|
|
||||||
//wl_display_flush(wayl->display);
|
|
||||||
return wayl;
|
return wayl;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
@ -558,11 +544,19 @@ wayl_destroy(struct wayland *wayl)
|
||||||
if (wayl == NULL)
|
if (wayl == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (wayl->kbd.repeat.fd != 0) {
|
tll_foreach(wayl->terms, it) {
|
||||||
fdm_del(wayl->fdm, wayl->kbd.repeat.fd);
|
static bool have_warned = false;
|
||||||
close(wayl->kbd.repeat.fd);
|
if (!have_warned) {
|
||||||
|
have_warned = true;
|
||||||
|
LOG_WARN("there are terminals still running");
|
||||||
|
term_destroy(it->item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tll_free(wayl->terms);
|
||||||
|
|
||||||
|
fdm_del(wayl->fdm, wayl->kbd.repeat.fd);
|
||||||
|
|
||||||
tll_foreach(wayl->monitors, it) {
|
tll_foreach(wayl->monitors, it) {
|
||||||
free(it->item.name);
|
free(it->item.name);
|
||||||
if (it->item.xdg != NULL)
|
if (it->item.xdg != NULL)
|
||||||
|
|
@ -630,7 +624,7 @@ wayl_destroy(struct wayland *wayl)
|
||||||
if (wayl->registry != NULL)
|
if (wayl->registry != NULL)
|
||||||
wl_registry_destroy(wayl->registry);
|
wl_registry_destroy(wayl->registry);
|
||||||
if (wayl->display != NULL) {
|
if (wayl->display != NULL) {
|
||||||
fdm_del(wayl->fdm, wl_display_get_fd(wayl->display));
|
fdm_del_no_close(wayl->fdm, wl_display_get_fd(wayl->display));
|
||||||
wl_display_disconnect(wayl->display);
|
wl_display_disconnect(wayl->display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -641,6 +635,7 @@ struct wl_window *
|
||||||
wayl_win_init(struct wayland *wayl)
|
wayl_win_init(struct wayland *wayl)
|
||||||
{
|
{
|
||||||
struct wl_window *win = calloc(1, sizeof(*win));
|
struct wl_window *win = calloc(1, sizeof(*win));
|
||||||
|
win->wayl = wayl;
|
||||||
|
|
||||||
win->surface = wl_compositor_create_surface(wayl->compositor);
|
win->surface = wl_compositor_create_surface(wayl->compositor);
|
||||||
if (win->surface == NULL) {
|
if (win->surface == NULL) {
|
||||||
|
|
@ -686,6 +681,28 @@ out:
|
||||||
void
|
void
|
||||||
wayl_win_destroy(struct wl_window *win)
|
wayl_win_destroy(struct wl_window *win)
|
||||||
{
|
{
|
||||||
|
if (win == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, unmap all surfaces to trigger things like
|
||||||
|
* keyboard_leave() and wl_pointer_leave().
|
||||||
|
*
|
||||||
|
* This ensures we remove all references to *this* window from the
|
||||||
|
* global wayland struct (since it no longer has neither keyboard
|
||||||
|
* nor mouse focus).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Scrollback search */
|
||||||
|
wl_surface_attach(win->search_surface, NULL, 0, 0);
|
||||||
|
wl_surface_commit(win->search_surface);
|
||||||
|
wl_display_roundtrip(win->wayl->display);
|
||||||
|
|
||||||
|
/* Main window */
|
||||||
|
wl_surface_attach(win->surface, NULL, 0, 0);
|
||||||
|
wl_surface_commit(win->surface);
|
||||||
|
wl_display_roundtrip(win->wayl->display);
|
||||||
|
|
||||||
tll_free(win->on_outputs);
|
tll_free(win->on_outputs);
|
||||||
if (win->search_sub_surface != NULL)
|
if (win->search_sub_surface != NULL)
|
||||||
wl_subsurface_destroy(win->search_sub_surface);
|
wl_subsurface_destroy(win->search_sub_surface);
|
||||||
|
|
@ -701,6 +718,9 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
xdg_surface_destroy(win->xdg_surface);
|
xdg_surface_destroy(win->xdg_surface);
|
||||||
if (win->surface != NULL)
|
if (win->surface != NULL)
|
||||||
wl_surface_destroy(win->surface);
|
wl_surface_destroy(win->surface);
|
||||||
|
|
||||||
|
wl_display_roundtrip(win->wayl->display);
|
||||||
|
|
||||||
free(win);
|
free(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -763,14 +783,24 @@ wayl_update_cursor_surface(struct wayland *wayl, struct terminal *term)
|
||||||
struct terminal *
|
struct terminal *
|
||||||
wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface)
|
wayl_terminal_from_surface(struct wayland *wayl, struct wl_surface *surface)
|
||||||
{
|
{
|
||||||
assert(surface == wayl->term->window->surface);
|
tll_foreach(wayl->terms, it) {
|
||||||
return wayl->term;
|
if (it->item->window->surface == surface)
|
||||||
|
return it->item;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct terminal *
|
struct terminal *
|
||||||
wayl_terminal_from_xdg_toplevel(struct wayland *wayl,
|
wayl_terminal_from_xdg_toplevel(struct wayland *wayl,
|
||||||
struct xdg_toplevel *toplevel)
|
struct xdg_toplevel *toplevel)
|
||||||
{
|
{
|
||||||
assert(toplevel == wayl->term->window->xdg_toplevel);
|
tll_foreach(wayl->terms, it) {
|
||||||
return wayl->term;
|
if (it->item->window->xdg_toplevel == toplevel)
|
||||||
|
return it->item;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ struct wl_primary {
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wayland;
|
||||||
struct wl_window {
|
struct wl_window {
|
||||||
|
struct wayland *wayl;
|
||||||
struct wl_surface *surface;
|
struct wl_surface *surface;
|
||||||
struct xdg_surface *xdg_surface;
|
struct xdg_surface *xdg_surface;
|
||||||
struct xdg_toplevel *xdg_toplevel;
|
struct xdg_toplevel *xdg_toplevel;
|
||||||
|
|
@ -145,8 +147,7 @@ struct wayland {
|
||||||
bool have_argb8888;
|
bool have_argb8888;
|
||||||
tll(struct monitor) monitors; /* All available outputs */
|
tll(struct monitor) monitors; /* All available outputs */
|
||||||
|
|
||||||
/* TODO: turn into a list to support multiple windows */
|
tll(struct terminal *) terms;
|
||||||
struct terminal *term;
|
|
||||||
struct terminal *focused;
|
struct terminal *focused;
|
||||||
struct terminal *moused;
|
struct terminal *moused;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue