xF: add basic configuration
Alpine 3.24 Success
Arch Linux AUR Success
OpenBSD 7.8 Success

This commit is contained in:
2026-08-08 01:46:54 +02:00
parent 3275cd1fa2
commit 8e2e7be56f
3 changed files with 78 additions and 28 deletions
+8 -8
View File
@@ -37,7 +37,7 @@ image::xP.webp[align="center"]
xF
--
The hybrid terminal/X11 frontend for 'xC'. It has simplified word wrapping,
limited colour support, and X11 doesn't support text selection (but in 'xC'
limited colour support, and X11 doesn't support text selection (though in 'xC'
tradition, it will happily launch embedded gVim on both the input and the log).
This frontend is perfect for testing.
@@ -96,7 +96,7 @@ Building
Build-only dependencies: CMake, pkg-config, awk, liberty (included),
asciidoctor or asciidoc (recommended but optional), rsvg-convert (for 'xF') +
Common runtime dependencies: openssl +
Additionally for 'xC': curses, lua >= 5.3 (optional), termo (included) +
Additionally for 'xC': any curses, lua >= 5.3 (optional), termo (included) +
Additionally for 'xF': ncursesw, libunistring, termo (included),
x11 + xft + libpng (X11)
@@ -125,12 +125,14 @@ For the rest you might want to generate a configuration file:
$ xB --write-default-config
$ xD --write-default-config
$ xF --write-default-config
After making any necessary edits to the file (there are comments to aid you in
doing that), simply run the appropriate program with no arguments:
$ xB
$ xD
$ xF
'xB' stays running in the foreground, therefore I recommend launching it inside
a Screen or tmux session.
@@ -139,12 +141,14 @@ a Screen or tmux session.
file or something like `killall` if you want to terminate it. You can run it
as a `forking` type systemd user service.
xP
~~
Frontends
~~~~~~~~~
The precondition for running 'xC' frontends is enabling its relay interface:
/set general.relay_bind = "127.0.0.1:9000"
xP
~~
To build the web server, install the Go compiler, and run `make`
from the _xP_ directory. Then start the resulting binary, and navigate to
the adress you give it as its first argument--in the following example,
@@ -156,10 +160,6 @@ For remote use, it's recommended to put 'xP' behind a reverse proxy, with TLS,
and some form of HTTP authentication. Pass the external URL of the WebSocket
endpoint as the third command line argument in this case.
xF
~~
The terminal/X11 frontend needs the relay address on its command line.
xA
~~
The Fyne frontend supports all of Linux, FreeBSD, Windows, macOS, Android, and
+8 -1
View File
@@ -10,7 +10,7 @@ xF - Terminal/X11 frontend for xC
Synopsis
--------
*xF* [_OPTION_]... RELAY-ADDRESS
*xF* [_OPTION_]... [HOST:PORT]
Description
-----------
@@ -18,6 +18,8 @@ Description
It normally runs in the terminal, and can also use a graphical interface
when built with support for one.
When an explicit relay address is given, it takes priority over configuration.
Options
-------
*-g*, *--gui*::
@@ -29,6 +31,11 @@ Options
*-V*, *--version*::
Output version information and exit.
*--write-default-cfg*[**=**__PATH__]::
Write a configuration file with defaults, show its path and exit.
+
The file will be appropriately commented.
Reporting bugs
--------------
Use https://git.janouch.name/p/xK to report bugs, request features,
+62 -19
View File
@@ -32,6 +32,16 @@
#endif // HAVE_X11
#include "liberty/liberty-xui.c"
// --- Configuration (application-specific) ------------------------------------
static struct simple_config_item g_config_table[] =
{
{ "relay", NULL, "Default relay HOST:PORT" },
{ "editor", NULL, "TUI editor; $1 filename" },
{ "editor_x11", NULL, "GUI editor; $1 Window ID, $2 filename" },
{ NULL, NULL, NULL }
};
// --- Relay model -------------------------------------------------------------
// ~~~ Buffer lines ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -449,6 +459,7 @@ struct relay_client
static struct
{
struct str_map config; ///< Frontend configuration
struct poller poller; ///< Poller
bool polling; ///< The event loop is running
struct poller_fd signal_event; ///< There are signals to process
@@ -3604,16 +3615,25 @@ app_external_editor_launch (struct error **e)
bool run_in_terminal = g_xui.ui == &tui_ui;
if (!child)
{
#define LAUNCH(x, ...) execlp (x, x, __VA_ARGS__, g.editor_filename, NULL)
if (run_in_terminal)
{
hard_assert (setpgid (0, 0) != -1);
hard_assert (tcsetpgrp (STDOUT_FILENO, getpgrp ()) != -1);
const char *command = str_map_find (&g.config, "editor");
if (command)
LAUNCH ("sh", "-c", command, "editor");
execlp (editor, editor, g.editor_filename, NULL);
}
#ifdef HAVE_X11
#define LAUNCH(x, ...) execlp (x, x, __VA_ARGS__, g.editor_filename, NULL)
else
{
const char *command = str_map_find (&g.config, "editor_x11");
if (command)
LAUNCH ("sh", "-c", command, "editor_x11", xid);
// This list is nearly exhaustive, so it's about priorities
// and option customisation.
LAUNCH ("gvim", "-f", "--socketid", xid);
@@ -3625,8 +3645,8 @@ app_external_editor_launch (struct error **e)
// XTerm will work better with "XTerm*allowSendEvents: true".
LAUNCH ("xterm", "-into", xid, "-e", editor);
}
#undef LAUNCH
#endif // HAVE_X11
#undef LAUNCH
// AppKit offers no similar mechanism.
@@ -4231,8 +4251,11 @@ static const char g_link_pattern[] =
"([^][(){}<>\"'[:space:],.:]|\\([^][(){}<>\"'[:space:]]*\\))";
static void
app_init_context (const char *host, const char *port)
app_init_context (void)
{
g.config = str_map_make (free);
simple_config_load_defaults (&g.config, g_config_table);
poller_init (&g.poller);
app_editor_init ();
@@ -4244,8 +4267,6 @@ app_init_context (const char *host, const char *port)
if (!(g.link_re = regex_compile (g_link_pattern, REG_EXTENDED, &e)))
exit_fatal ("Failed to compile link regex: %s", e->message);
g.relay.host = xstrdup (host);
g.relay.port = xstrdup (port);
g.relay.reconnect_event = poller_timer_make (&g.poller);
g.relay.reconnect_event.dispatcher = relay_on_reconnect_timer;
@@ -4283,6 +4304,8 @@ app_free_context (void)
free (g.editor_filename);
free (g.editor_running_for);
poller_free (&g.poller);
str_map_free (&g.config);
}
// --- Main --------------------------------------------------------------------
@@ -4301,11 +4324,14 @@ app_parse_options (int argc, char *argv[], bool *requested_gui)
#endif // HAVE_X11
{ 'h', "help", NULL, 0, "display this help and exit" },
{ 'V', "version", NULL, 0, "output version information and exit" },
{ 'w', "write-default-cfg", "FILENAME",
OPT_OPTIONAL_ARG | OPT_LONG_ONLY,
"write a default configuration file and exit" },
{ 0, NULL, NULL, 0, NULL }
};
struct opt_handler oh = opt_handler_make (argc, argv, opts,
"HOST:PORT", "Terminal/X11 frontend for xC.");
"[HOST:PORT]", "Terminal/X11 frontend for xC.");
int c;
while ((c = opt_handler_get (&oh)) != -1)
switch (c)
@@ -4321,6 +4347,9 @@ app_parse_options (int argc, char *argv[], bool *requested_gui)
case 'V':
printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
exit (EXIT_SUCCESS);
case 'w':
call_simple_config_write_default (optarg, g_config_table);
exit (EXIT_SUCCESS);
default:
print_error ("wrong options");
opt_handler_usage (&oh, stderr);
@@ -4329,31 +4358,45 @@ app_parse_options (int argc, char *argv[], bool *requested_gui)
argc -= optind;
argv += optind;
if (argc != 1)
{
opt_handler_usage (&oh, stderr);
exit (EXIT_FAILURE);
}
opt_handler_free (&oh);
return argv[0];
}
static void
app_parse_address (const char *address)
{
char *copy = xstrdup (address);
const char *port = NULL, *host = tokenize_host_port (copy, &port);
if (!port)
exit_fatal ("missing port number/service name");
g.relay.host = xstrdup (host);
g.relay.port = xstrdup (port);
free (copy);
}
int
main (int argc, char *argv[])
{
bool requested_gui = false;
char *address = xstrdup (app_parse_options (argc, argv, &requested_gui));
const char *port = NULL, *host = tokenize_host_port (address, &port);
if (!port)
exit_fatal ("missing port number/service name");
if (!setlocale (LC_CTYPE, ""))
print_warning ("failed to set the character locale");
if (!setlocale (LC_TIME, ""))
print_warning ("failed to set the time locale");
app_init_context (host, port);
free (address);
bool requested_gui = false;
const char *address = app_parse_options (argc, argv, &requested_gui);
app_init_context ();
struct error *e = NULL;
if (!simple_config_update_from_file (&g.config, &e))
exit_fatal ("error loading configuration: %s", e->message);
if (!address)
address = str_map_find (&g.config, "relay");
if (!address)
exit_fatal ("no relay address given and none configured");
app_parse_address (address);
signals_init ();
g.signal_event = poller_fd_make (&g.poller, g_signal_pipe[0]);