diff options
Diffstat (limited to 'pkgs/applications/terminal-emulators/foot')
-rw-r--r-- | pkgs/applications/terminal-emulators/foot/Add-support-for-opening-an-existing-PTY.patch | 211 | ||||
-rw-r--r-- | pkgs/applications/terminal-emulators/foot/default.nix | 228 |
2 files changed, 439 insertions, 0 deletions
diff --git a/pkgs/applications/terminal-emulators/foot/Add-support-for-opening-an-existing-PTY.patch b/pkgs/applications/terminal-emulators/foot/Add-support-for-opening-an-existing-PTY.patch new file mode 100644 index 00000000000..f07a26bad6f --- /dev/null +++ b/pkgs/applications/terminal-emulators/foot/Add-support-for-opening-an-existing-PTY.patch @@ -0,0 +1,211 @@ +From 6cb6fe265eb8c066fce8ce12473d6b8135f3b3cb Mon Sep 17 00:00:00 2001 +From: Alyssa Ross <hi@alyssa.is> +Date: Fri, 10 Dec 2021 17:40:59 +0000 +Subject: [PATCH] Add support for opening an existing PTY + +--- + main.c | 12 +++++++++- + server.c | 2 +- + terminal.c | 65 +++++++++++++++++++++++++++++++----------------------- + terminal.h | 2 +- + 4 files changed, 51 insertions(+), 30 deletions(-) + +diff --git a/main.c b/main.c +index 4f2dc7b6..d1307a84 100644 +--- a/main.c ++++ b/main.c +@@ -152,6 +152,10 @@ print_pid(const char *pid_file, bool *unlink_at_exit) + return false; + } + ++enum { ++ PTY_OPTION = CHAR_MAX + 1, ++}; ++ + int + main(int argc, char *const *argv) + { +@@ -187,6 +191,7 @@ main(int argc, char *const *argv) + {"maximized", no_argument, NULL, 'm'}, + {"fullscreen", no_argument, NULL, 'F'}, + {"presentation-timings", no_argument, NULL, 'P'}, /* Undocumented */ ++ {"pty", required_argument, NULL, PTY_OPTION}, + {"print-pid", required_argument, NULL, 'p'}, + {"log-level", required_argument, NULL, 'd'}, + {"log-colorize", optional_argument, NULL, 'l'}, +@@ -202,6 +207,7 @@ main(int argc, char *const *argv) + const char *conf_title = NULL; + const char *conf_app_id = NULL; + const char *custom_cwd = NULL; ++ const char *pty_path = NULL; + bool login_shell = false; + tll(char *) conf_fonts = tll_init(); + enum conf_size_type conf_size_type = CONF_SIZE_PX; +@@ -320,6 +326,10 @@ main(int argc, char *const *argv) + conf_server_socket_path = optarg; + break; + ++ case PTY_OPTION: ++ pty_path = optarg; ++ break; ++ + case 'P': + presentation_timings = true; + break; +@@ -538,7 +548,7 @@ main(int argc, char *const *argv) + goto out; + + if (!as_server && (term = term_init( +- &conf, fdm, reaper, wayl, "foot", cwd, token, ++ &conf, fdm, reaper, wayl, "foot", cwd, token, pty_path, + argc, argv, + &term_shutdown_cb, &shutdown_ctx)) == NULL) { + goto out; +diff --git a/server.c b/server.c +index 1d31abc6..190d224d 100644 +--- a/server.c ++++ b/server.c +@@ -315,7 +315,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) + instance->terminal = term_init( + conf != NULL ? conf : server->conf, + server->fdm, server->reaper, server->wayl, "footclient", cwd, token, +- cdata.argc, argv, &term_shutdown_handler, instance); ++ NULL, cdata.argc, argv, &term_shutdown_handler, instance); + + if (instance->terminal == NULL) { + LOG_ERR("failed to instantiate new terminal"); +diff --git a/terminal.c b/terminal.c +index ac887412..e11ef0d6 100644 +--- a/terminal.c ++++ b/terminal.c +@@ -327,6 +327,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) + if (hup) { + fdm_del(fdm, fd); + term->ptmx = -1; ++ term_shutdown(term); + } + + return true; +@@ -1035,10 +1036,12 @@ load_fonts_from_conf(struct terminal *term) + static void fdm_client_terminated( + struct reaper *reaper, pid_t pid, int status, void *data); + ++static const int PTY_OPEN_FLAGS = O_RDWR | O_NOCTTY; ++ + struct terminal * + term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, + struct wayland *wayl, const char *foot_exe, const char *cwd, +- const char *token, int argc, char *const *argv, ++ const char *token, const char *pty_path, int argc, char *const *argv, + void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data) + { + int ptmx = -1; +@@ -1054,7 +1057,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, + return NULL; + } + +- if ((ptmx = posix_openpt(O_RDWR | O_NOCTTY)) < 0) { ++ ptmx = pty_path ? open(pty_path, PTY_OPEN_FLAGS) : posix_openpt(PTY_OPEN_FLAGS); ++ if (ptmx < 0) { + LOG_ERRNO("failed to open PTY"); + goto close_fds; + } +@@ -1116,6 +1120,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, + .fdm = fdm, + .reaper = reaper, + .conf = conf, ++ .slave = -1, + .ptmx = ptmx, + .ptmx_buffers = tll_init(), + .ptmx_paste_buffers = tll_init(), +@@ -1237,16 +1242,18 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, + } + term->font_line_height = conf->line_height; + +- /* Start the slave/client */ +- if ((term->slave = slave_spawn( +- term->ptmx, argc, term->cwd, argv, +- conf->term, conf->shell, conf->login_shell, +- &conf->notifications)) == -1) +- { +- goto err; +- } ++ if (!pty_path) { ++ /* Start the slave/client */ ++ if ((term->slave = slave_spawn( ++ term->ptmx, argc, term->cwd, argv, ++ conf->term, conf->shell, conf->login_shell, ++ &conf->notifications)) == -1) ++ { ++ goto err; ++ } + +- reaper_add(term->reaper, term->slave, &fdm_client_terminated, term); ++ reaper_add(term->reaper, term->slave, &fdm_client_terminated, term); ++ } + + /* Guess scale; we're not mapped yet, so we don't know on which + * output we'll be. Pick highest scale we find for now */ +@@ -1506,26 +1513,30 @@ term_shutdown(struct terminal *term) + close(term->ptmx); + + if (!term->shutdown.client_has_terminated) { +- LOG_DBG("initiating asynchronous terminate of slave; " +- "sending SIGTERM to PID=%u", term->slave); ++ if (term->slave <= 0) { ++ term->shutdown.client_has_terminated = true; ++ } else { ++ LOG_DBG("initiating asynchronous terminate of slave; " ++ "sending SIGTERM to PID=%u", term->slave); + +- kill(-term->slave, SIGTERM); ++ kill(-term->slave, SIGTERM); + +- const struct itimerspec timeout = {.it_value = {.tv_sec = 60}}; ++ const struct itimerspec timeout = {.it_value = {.tv_sec = 60}}; + +- int timeout_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); +- if (timeout_fd < 0 || +- timerfd_settime(timeout_fd, 0, &timeout, NULL) < 0 || +- !fdm_add(term->fdm, timeout_fd, EPOLLIN, &fdm_terminate_timeout, term)) +- { +- if (timeout_fd >= 0) +- close(timeout_fd); +- LOG_ERRNO("failed to create slave terminate timeout FD"); +- return false; ++ int timeout_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); ++ if (timeout_fd < 0 || ++ timerfd_settime(timeout_fd, 0, &timeout, NULL) < 0 || ++ !fdm_add(term->fdm, timeout_fd, EPOLLIN, &fdm_terminate_timeout, term)) ++ { ++ if (timeout_fd >= 0) ++ close(timeout_fd); ++ LOG_ERRNO("failed to create slave terminate timeout FD"); ++ return false; ++ } ++ ++ xassert(term->shutdown.terminate_timeout_fd < 0); ++ term->shutdown.terminate_timeout_fd = timeout_fd; + } +- +- xassert(term->shutdown.terminate_timeout_fd < 0); +- term->shutdown.terminate_timeout_fd = timeout_fd; + } + + term->selection.auto_scroll.fd = -1; +diff --git a/terminal.h b/terminal.h +index 09b04614..3beb3e80 100644 +--- a/terminal.h ++++ b/terminal.h +@@ -671,7 +671,7 @@ struct config; + struct terminal *term_init( + const struct config *conf, struct fdm *fdm, struct reaper *reaper, + struct wayland *wayl, const char *foot_exe, const char *cwd, +- const char *token, int argc, char *const *argv, ++ const char *token, const char *pty_path, int argc, char *const *argv, + void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data); + + bool term_shutdown(struct terminal *term); +-- +2.33.0 + diff --git a/pkgs/applications/terminal-emulators/foot/default.nix b/pkgs/applications/terminal-emulators/foot/default.nix new file mode 100644 index 00000000000..a58e45e50dc --- /dev/null +++ b/pkgs/applications/terminal-emulators/foot/default.nix @@ -0,0 +1,228 @@ +{ stdenv +, lib +, fetchFromGitea +, fetchurl +, runCommand +, fcft +, freetype +, pixman +, libxkbcommon +, fontconfig +, wayland +, meson +, ninja +, ncurses +, scdoc +, tllist +, wayland-protocols +, wayland-scanner +, pkg-config +, utf8proc +, allowPgo ? true +, python3 # for PGO +# for clang stdenv check +, foot +, llvmPackages +, llvmPackages_latest +}: + +let + version = "1.11.0"; + + # build stimuli file for PGO build and the script to generate it + # independently of the foot's build, so we can cache the result + # and avoid unnecessary rebuilds as it can take relatively long + # to generate + # + # For every bump, make sure that the hash is still accurate. + stimulusGenerator = stdenv.mkDerivation { + name = "foot-generate-alt-random-writes"; + + src = fetchurl { + url = "https://codeberg.org/dnkl/foot/raw/tag/${version}/scripts/generate-alt-random-writes.py"; + sha256 = "0w4d0rxi54p8lvbynypcywqqwbbzmyyzc0svjab27ngmdj1034ii"; + }; + + dontUnpack = true; + + buildInputs = [ python3 ]; + + installPhase = '' + install -Dm755 $src $out + ''; + }; + + stimuliFile = runCommand "pgo-stimulus-file" { } '' + ${stimulusGenerator} \ + --rows=67 --cols=135 \ + --scroll --scroll-region \ + --colors-regular --colors-bright --colors-256 --colors-rgb \ + --attr-bold --attr-italic --attr-underline \ + --sixel \ + --seed=2305843009213693951 \ + $out + ''; + + compilerName = + if stdenv.cc.isClang + then "clang" + else if stdenv.cc.isGNU + then "gcc" + else "unknown"; + + # https://codeberg.org/dnkl/foot/src/branch/master/INSTALL.md#performance-optimized-pgo + pgoCflags = { + "clang" = "-O3 -Wno-ignored-optimization-argument"; + "gcc" = "-O3"; + }."${compilerName}"; + + # ar with lto support + ar = stdenv.cc.bintools.targetPrefix + { + "clang" = "llvm-ar"; + "gcc" = "gcc-ar"; + "unknown" = "ar"; + }."${compilerName}"; + + # PGO only makes sense if we are not cross compiling and + # using a compiler which foot's PGO build supports (clang or gcc) + doPgo = allowPgo && (stdenv.hostPlatform == stdenv.buildPlatform) + && compilerName != "unknown"; + + terminfoDir = "${placeholder "terminfo"}/share/terminfo"; +in +stdenv.mkDerivation rec { + pname = "foot"; + inherit version; + + src = fetchFromGitea { + domain = "codeberg.org"; + owner = "dnkl"; + repo = pname; + rev = version; + sha256 = "1d9bk8lhmw5lc8k0mw80g0vbwgxyh3gw5c7ppy3sir07s9y0y0fn"; + }; + + patches = [ + ./Add-support-for-opening-an-existing-PTY.patch + ]; + + depsBuildBuild = [ + pkg-config + ]; + + nativeBuildInputs = [ + wayland-scanner + meson + ninja + ncurses + scdoc + pkg-config + ] ++ lib.optionals (compilerName == "clang") [ + stdenv.cc.cc.libllvm.out + ]; + + buildInputs = [ + tllist + wayland-protocols + fontconfig + freetype + pixman + wayland + libxkbcommon + fcft + utf8proc + ]; + + # recommended build flags for performance optimized foot builds + # https://codeberg.org/dnkl/foot/src/branch/master/INSTALL.md#release-build + CFLAGS = + if !doPgo + then "-O3 -fno-plt" + else pgoCflags; + + # ar with gcc plugins for lto objects + preConfigure = '' + export AR="${ar}" + ''; + + mesonBuildType = "release"; + + # See https://codeberg.org/dnkl/foot/src/tag/1.9.2/INSTALL.md#options + mesonFlags = [ + # Use lto + "-Db_lto=true" + # “Build” and install terminfo db + "-Dterminfo=enabled" + # Ensure TERM=foot is used + "-Ddefault-terminfo=foot" + # Tell foot to set TERMINFO and where to install the terminfo files + "-Dcustom-terminfo-install-location=${terminfoDir}" + ]; + + # build and run binary generating PGO profiles, + # then reconfigure to build the normal foot binary utilizing PGO + preBuild = lib.optionalString doPgo '' + meson configure -Db_pgo=generate + ninja + # make sure there is _some_ profiling data on all binaries + ./footclient --version + ./foot --version + ./tests/test-config + # generate pgo data of wayland independent code + ./pgo ${stimuliFile} ${stimuliFile} ${stimuliFile} + meson configure -Db_pgo=use + '' + lib.optionalString (doPgo && compilerName == "clang") '' + llvm-profdata merge default_*profraw --output=default.profdata + ''; + + # Install example themes which can be added to foot.ini via the include + # directive to a separate output to save a bit of space + postInstall = '' + moveToOutput share/foot/themes "$themes" + ''; + + outputs = [ "out" "terminfo" "themes" ]; + + passthru.tests = { + clang-default-compilation = foot.override { + inherit (llvmPackages) stdenv; + }; + + clang-latest-compilation = foot.override { + inherit (llvmPackages_latest) stdenv; + }; + + noPgo = foot.override { + allowPgo = false; + }; + + # By changing name, this will get rebuilt everytime we change version, + # even if the hash stays the same. Consequently it'll fail if we introduce + # a hash mismatch when updating. + stimulus-script-is-current = stimulusGenerator.src.overrideAttrs (_: { + name = "generate-alt-random-writes-${version}.py"; + }); + }; + + meta = with lib; { + homepage = "https://codeberg.org/dnkl/foot/"; + changelog = "https://codeberg.org/dnkl/foot/releases/tag/${version}"; + description = "A fast, lightweight and minimalistic Wayland terminal emulator"; + license = licenses.mit; + maintainers = [ maintainers.sternenseemann ]; + platforms = platforms.linux; + # From (presumably) ncurses version 6.3, it will ship a foot + # terminfo file. This however won't include some non-standard + # capabilities foot's bundled terminfo file contains. Unless we + # want to have some features in e. g. vim or tmux stop working, + # we need to make sure that the foot terminfo overwrites ncurses' + # one. Due to <nixpkgs/nixos/modules/config/system-path.nix> + # ncurses is always added to environment.systemPackages on + # NixOS with its priority increased by 3, so we need to go + # one bigger. + # This doesn't matter a lot for local use since foot sets + # TERMINFO to a store path, but allows installing foot.terminfo + # on remote systems for proper foot terminfo support. + priority = (ncurses.meta.priority or 5) + 3 + 1; + }; +} |