From 1639235a48dbed75c2563c9a497b41c31a2a1bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Mon, 8 Aug 2022 04:39:20 +0200 Subject: Start X11 and web frontends for xC For this, we needed a wire protocol. After surveying available options, it was decided to implement an XDR-like protocol code generator in portable AWK. It now has two backends, per each of: - xF, the X11 frontend, is in C, and is meant to be the primary user interface in the future. - xP, the web frontend, relies on a protocol proxy written in Go, and is meant for use on-the-go (no pun intended). They are very much work-in-progress proofs of concept right now, and the relay protocol is certain to change. --- xF.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 xF.c (limited to 'xF.c') diff --git a/xF.c b/xF.c new file mode 100644 index 0000000..054871d --- /dev/null +++ b/xF.c @@ -0,0 +1,172 @@ +/* + * xF.c: a toothless IRC client frontend + * + * Copyright (c) 2022, Přemysl Eric Janouch + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "config.h" +#define PROGRAM_NAME "xF" + +#include "common.c" +#include "xC-proto.c" + +#include +#include +#include +#include +#include + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static struct +{ + bool polling; + struct connector connector; + int socket; +} +g; + +static void +on_connector_connecting (void *user_data, const char *address) +{ + (void) user_data; + print_status ("connecting to %s...", address); +} + +static void +on_connector_error (void *user_data, const char *error) +{ + (void) user_data; + print_status ("connection failed: %s", error); +} + +static void +on_connector_failure (void *user_data) +{ + (void) user_data; + exit_fatal ("giving up"); +} + +static void +on_connector_connected (void *user_data, int socket, const char *hostname) +{ + (void) user_data; + (void) hostname; + g.polling = false; + g.socket = socket; +} + +static void +protocol_test (const char *host, const char *port) +{ + struct poller poller = {}; + poller_init (&poller); + + connector_init (&g.connector, &poller); + g.connector.on_connecting = on_connector_connecting; + g.connector.on_error = on_connector_error; + g.connector.on_connected = on_connector_connected; + g.connector.on_failure = on_connector_failure; + + connector_add_target (&g.connector, host, port); + + g.polling = true; + while (g.polling) + poller_run (&poller); + + connector_free (&g.connector); + + struct str s = str_make (); + str_pack_u32 (&s, 0); + struct relay_command_message m = {}; + m.data.hello.command = RELAY_COMMAND_HELLO; + m.data.hello.version = RELAY_VERSION; + if (!relay_command_message_serialize (&m, &s)) + exit_fatal ("serialization failed"); + + uint32_t len = htonl (s.len - sizeof len); + memcpy (s.str, &len, sizeof len); + if (errno = 0, write (g.socket, s.str, s.len) != (ssize_t) s.len) + exit_fatal ("short send or error: %s", strerror (errno)); + + char buf[1 << 20] = ""; + while (errno = 0, read (g.socket, &len, sizeof len) == sizeof len) + { + len = ntohl (len); + if (errno = 0, read (g.socket, buf, MIN (len, sizeof buf)) != len) + exit_fatal ("short read or error: %s", strerror (errno)); + + struct msg_unpacker r = msg_unpacker_make (buf, len); + struct relay_event_message m = {}; + if (!relay_event_message_deserialize (&m, &r)) + exit_fatal ("deserialization failed"); + if (msg_unpacker_get_available (&r)) + exit_fatal ("trailing data"); + + printf ("event: %d\n", m.data.event); + relay_event_message_free (&m); + } + exit_fatal ("short read or error: %s", strerror (errno)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +int +main (int argc, char *argv[]) +{ + static const struct opt opts[] = + { + { 'h', "help", NULL, 0, "display this help and exit" }, + { 'V', "version", NULL, 0, "output version information and exit" }, + { 0, NULL, NULL, 0, NULL } + }; + + struct opt_handler oh = opt_handler_make (argc, argv, opts, + "HOST:PORT", "X11 frontend for xC."); + + int c; + while ((c = opt_handler_get (&oh)) != -1) + switch (c) + { + case 'h': + opt_handler_usage (&oh, stdout); + exit (EXIT_SUCCESS); + case 'V': + printf (PROGRAM_NAME " " PROGRAM_VERSION "\n"); + exit (EXIT_SUCCESS); + default: + print_error ("wrong options"); + opt_handler_usage (&oh, stderr); + exit (EXIT_FAILURE); + } + + argc -= optind; + argv += optind; + if (argc != 1) + { + opt_handler_usage (&oh, stderr); + exit (EXIT_FAILURE); + } + opt_handler_free (&oh); + + char *address = xstrdup (argv[0]); + const char *port = NULL, *host = tokenize_host_port (address, &port); + if (!port) + exit_fatal ("missing port number/service name"); + + // TODO: Actually implement an X11-based user interface. + protocol_test (host, port); + return 0; +} -- cgit v1.2.3