mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
Since fonts are destroyed and removed from the font cache when the last instance is destroyed, closing the "last" terminal window in server mode would destroy the fonts, and their glyph caches. By instantiating the set in main, we ensure the fonts, and the glyph caches, remain. This makes launching new terminals (much) faster. Note that in "normal" (non-server) mode, this isn't really necessary, but also doesn't have any penalty.
245 lines
6.6 KiB
C
245 lines
6.6 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdbool.h>
|
|
#include <locale.h>
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
|
|
#include <sys/sysinfo.h>
|
|
|
|
#define LOG_MODULE "main"
|
|
#define LOG_ENABLE_DBG 0
|
|
#include "log.h"
|
|
|
|
#include "config.h"
|
|
#include "fdm.h"
|
|
#include "font.h"
|
|
#include "server.h"
|
|
#include "shm.h"
|
|
#include "terminal.h"
|
|
#include "version.h"
|
|
|
|
#define min(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
|
|
print_usage(const char *prog_name)
|
|
{
|
|
printf("Usage: %s [OPTIONS]...\n", prog_name);
|
|
printf("\n");
|
|
printf("Options:\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"
|
|
" -g,--geometry=WIDTHxHEIGHT set initial width and height\n"
|
|
" -s,--server run as a server (use 'footclient' to start terminals)\n"
|
|
" -v,--version show the version number and quit\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;
|
|
}
|
|
|
|
static bool
|
|
initialize_fonts(const struct config *conf, struct font *fonts[4])
|
|
{
|
|
font_list_t font_names = tll_init();
|
|
tll_foreach(conf->fonts, it)
|
|
tll_push_back(font_names, it->item);
|
|
|
|
if ((fonts[0] = font_from_name(font_names, "")) == NULL ||
|
|
(fonts[1] = font_from_name(font_names, "weight=bold")) == NULL ||
|
|
(fonts[2] = font_from_name(font_names, "slant=italic")) == NULL ||
|
|
(fonts[3] = font_from_name(font_names, "weight=bold:slant=italic")) == NULL)
|
|
{
|
|
tll_free(font_names);
|
|
return false;
|
|
}
|
|
|
|
tll_free(font_names);
|
|
return true;
|
|
}
|
|
|
|
int
|
|
main(int argc, char *const *argv)
|
|
{
|
|
int ret = EXIT_FAILURE;
|
|
|
|
/* Startup notifications; we don't support it, but must ensure we
|
|
* don't pass this on to programs launched by us */
|
|
unsetenv("DESKTOP_STARTUP_ID");
|
|
|
|
struct config conf = {NULL};
|
|
if (!config_load(&conf))
|
|
return ret;
|
|
|
|
const char *const prog_name = argv[0];
|
|
|
|
static const struct option longopts[] = {
|
|
{"term", required_argument, 0, 't'},
|
|
{"font", required_argument, 0, 'f'},
|
|
{"geometry", required_argument, 0, 'g'},
|
|
{"server", no_argument, 0, 's'},
|
|
{"version", no_argument, 0, 'v'},
|
|
{"help", no_argument, 0, 'h'},
|
|
{NULL, no_argument, 0, 0},
|
|
};
|
|
|
|
bool as_server = false;
|
|
|
|
while (true) {
|
|
int c = getopt_long(argc, argv, ":t:f:g:vh", longopts, NULL);
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 't':
|
|
free(conf.term);
|
|
conf.term = strdup(optarg);
|
|
break;
|
|
|
|
case 'f':
|
|
tll_free_and_free(conf.fonts, free);
|
|
for (char *font = strtok(optarg, ","); font != NULL; font = strtok(NULL, ",")) {
|
|
|
|
/* Strip leading spaces */
|
|
while (*font != '\0' && isspace(*font))
|
|
font++;
|
|
|
|
/* Strip trailing spaces */
|
|
char *end = font + strlen(font);
|
|
assert(*end == '\0');
|
|
end--;
|
|
while (end > font && isspace(*end))
|
|
*(end--) = '\0';
|
|
|
|
if (strlen(font) == 0)
|
|
continue;
|
|
|
|
tll_push_back(conf.fonts, strdup(font));
|
|
}
|
|
break;
|
|
|
|
case 'g': {
|
|
unsigned width, height;
|
|
if (sscanf(optarg, "%ux%u", &width, &height) != 2 || width == 0 || height == 0) {
|
|
fprintf(stderr, "error: invalid geometry: %s\n", optarg);
|
|
config_free(conf);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
conf.width = width;
|
|
conf.height = height;
|
|
break;
|
|
}
|
|
|
|
case 's':
|
|
as_server = true;
|
|
break;
|
|
|
|
case 'v':
|
|
printf("foot version %s\n", FOOT_VERSION);
|
|
config_free(conf);
|
|
return EXIT_SUCCESS;
|
|
|
|
case 'h':
|
|
print_usage(prog_name);
|
|
config_free(conf);
|
|
return EXIT_SUCCESS;
|
|
|
|
case ':':
|
|
fprintf(stderr, "error: -%c: missing required argument\n", optopt);
|
|
config_free(conf);
|
|
return EXIT_FAILURE;
|
|
|
|
case '?':
|
|
fprintf(stderr, "error: -%c: invalid option\n", optopt);
|
|
config_free(conf);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
struct font *fonts[4] = {NULL};
|
|
struct fdm *fdm = NULL;
|
|
struct wayland *wayl = NULL;
|
|
struct terminal *term = NULL;
|
|
struct server *server = NULL;
|
|
struct shutdown_context shutdown_ctx = {.term = &term, .exit_code = EXIT_FAILURE};
|
|
|
|
/* This ensures we keep a set of fonts in the cache */
|
|
if (!initialize_fonts(&conf, fonts))
|
|
goto out;
|
|
|
|
if ((fdm = fdm_init()) == NULL)
|
|
goto out;
|
|
|
|
if ((wayl = wayl_init(fdm)) == NULL)
|
|
goto out;
|
|
|
|
if (!as_server && (term = term_init(&conf, fdm, wayl, conf.term, argc, argv,
|
|
&term_shutdown_cb, &shutdown_ctx)) == NULL)
|
|
goto out;
|
|
|
|
if (as_server && (server = server_init(&conf, fdm, wayl)) == NULL)
|
|
goto out;
|
|
|
|
/* Remember to restore signals in slave */
|
|
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 (sigaction(SIGHUP, &(struct sigaction){.sa_handler = SIG_IGN}, NULL) < 0) {
|
|
LOG_ERRNO("failed to ignore SIGHUP");
|
|
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))
|
|
break;
|
|
}
|
|
|
|
ret = aborted || tll_length(wayl->terms) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
out:
|
|
server_destroy(server);
|
|
term_destroy(term);
|
|
|
|
shm_fini();
|
|
wayl_destroy(wayl);
|
|
fdm_destroy(fdm);
|
|
|
|
for (size_t i = 0; i < sizeof(fonts) / sizeof(fonts[0]); i++)
|
|
font_destroy(fonts[i]);
|
|
|
|
config_free(conf);
|
|
return ret == EXIT_SUCCESS && !as_server ? shutdown_ctx.exit_code : ret;
|
|
}
|