summaryrefslogtreecommitdiff
path: root/xF.c
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-08-08 04:39:20 +0200
committerPřemysl Eric Janouch <p@janouch.name>2022-09-05 14:26:00 +0200
commit1639235a48dbed75c2563c9a497b41c31a2a1bae (patch)
tree18193b72fa47e6bcac1358289ac9c36ed00c70ac /xF.c
parent2160d037943ef0a3adbf4c6e30a91ee0f205c3f3 (diff)
downloadxK-1639235a48dbed75c2563c9a497b41c31a2a1bae.tar.gz
xK-1639235a48dbed75c2563c9a497b41c31a2a1bae.tar.xz
xK-1639235a48dbed75c2563c9a497b41c31a2a1bae.zip
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.
Diffstat (limited to 'xF.c')
-rw-r--r--xF.c172
1 files changed, 172 insertions, 0 deletions
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 <p@janouch.name>
+ *
+ * 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 <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/XKBlib.h>
+#include <X11/Xft/Xft.h>
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+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;
+}