Přemysl Eric Janouch
1639235a48
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.
173 lines
4.4 KiB
C
173 lines
4.4 KiB
C
/*
|
|
* 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;
|
|
}
|