From 96fc12bc4c852f1343a120126f1f46ac7cca447d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Eric=20Janouch?= Date: Sun, 11 Sep 2022 20:46:35 +0200 Subject: [PATCH] xC/xP: send buffer type and server state Also make PM highlighting behaviour consistent. --- xC-proto | 40 +++++++++++++- xC.c | 136 ++++++++++++++++++++++++++++++++++++++--------- xP/public/xP.css | 9 ++-- xP/public/xP.js | 51 ++++++++++++++++-- 4 files changed, 201 insertions(+), 35 deletions(-) diff --git a/xC-proto b/xC-proto index 52bab54..819eb24 100644 --- a/xC-proto +++ b/xC-proto @@ -19,8 +19,8 @@ struct CommandMessage { case HELLO: u32 version; // If the version check succeeds, the client will receive - // an initial stream of BUFFER_UPDATE, BUFFER_STATS, BUFFER_LINE, - // and finally a BUFFER_ACTIVATE message. + // an initial stream of SERVER_UPDATE, BUFFER_UPDATE, BUFFER_STATS, + // BUFFER_LINE, and finally a BUFFER_ACTIVATE message. case ACTIVE: void; case BUFFER_INPUT: @@ -56,14 +56,33 @@ struct EventMessage { BUFFER_ACTIVATE, BUFFER_LINE, BUFFER_CLEAR, + SERVER_UPDATE, + SERVER_RENAME, + SERVER_REMOVE, ERROR, RESPONSE, } event) { case PING: void; + case BUFFER_UPDATE: string buffer_name; bool hide_unimportant; + union BufferContext switch (enum BufferKind { + GLOBAL, + SERVER, + CHANNEL, + PRIVATE_MESSAGE, + } kind) { + case GLOBAL: + void; + case SERVER: + string server_name; + case CHANNEL: + string server_name; + case PRIVATE_MESSAGE: + string server_name; + } context; case BUFFER_STATS: string buffer_name; // These are cumulative, even for lines flushed out from buffers. @@ -130,6 +149,23 @@ struct EventMessage { case BUFFER_CLEAR: string buffer_name; + case SERVER_UPDATE: + string server_name; + enum ServerState { + DISCONNECTED, + CONNECTING, + CONNECTED, + REGISTERED, + DISCONNECTING, + } state; + case SERVER_RENAME: + // Buffers aren't sent updates for in this circumstance, + // as that wouldn't be sufficiently atomic anyway. + string server_name; + string new; + case SERVER_REMOVE: + string server_name; + // Restriction: command_seq strictly follows the sequence received // by the relay, across both of these replies. case ERROR: diff --git a/xC.c b/xC.c index a378421..0da1fea 100644 --- a/xC.c +++ b/xC.c @@ -3109,6 +3109,28 @@ relay_prepare_buffer_update (struct app_context *ctx, struct buffer *buffer) e->event = RELAY_EVENT_BUFFER_UPDATE; e->buffer_name = str_from_cstr (buffer->name); e->hide_unimportant = buffer->hide_unimportant; + + struct str *server_name = NULL; + switch (buffer->type) + { + case BUFFER_GLOBAL: + e->context.kind = RELAY_BUFFER_KIND_GLOBAL; + break; + case BUFFER_SERVER: + e->context.kind = RELAY_BUFFER_KIND_SERVER; + server_name = &e->context.server.server_name; + break; + case BUFFER_CHANNEL: + e->context.kind = RELAY_BUFFER_KIND_CHANNEL; + server_name = &e->context.channel.server_name; + break; + case BUFFER_PM: + e->context.kind = RELAY_BUFFER_KIND_PRIVATE_MESSAGE; + server_name = &e->context.private_message.server_name; + break; + } + if (server_name) + *server_name = str_from_cstr (buffer->server->name); } static void @@ -3248,6 +3270,51 @@ relay_prepare_buffer_clear (struct app_context *ctx, e->buffer_name = str_from_cstr (buffer->name); } +enum relay_server_state +relay_server_state_for_server (struct server *s) +{ + switch (s->state) + { + case IRC_DISCONNECTED: return RELAY_SERVER_STATE_DISCONNECTED; + case IRC_CONNECTING: return RELAY_SERVER_STATE_CONNECTING; + case IRC_CONNECTED: return RELAY_SERVER_STATE_CONNECTED; + case IRC_REGISTERED: return RELAY_SERVER_STATE_REGISTERED; + case IRC_CLOSING: + case IRC_HALF_CLOSED: return RELAY_SERVER_STATE_DISCONNECTING; + } + return 0; +} + +static void +relay_prepare_server_update (struct app_context *ctx, struct server *s) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_server_update *e = &m->data.server_update; + e->event = RELAY_EVENT_SERVER_UPDATE; + e->server_name = str_from_cstr (s->name); + e->state = relay_server_state_for_server (s); +} + +static void +relay_prepare_server_rename (struct app_context *ctx, struct server *s, + const char *new_name) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_server_rename *e = &m->data.server_rename; + e->event = RELAY_EVENT_SERVER_RENAME; + e->server_name = str_from_cstr (s->name); + e->new = str_from_cstr (new_name); +} + +static void +relay_prepare_server_remove (struct app_context *ctx, struct server *s) +{ + struct relay_event_message *m = relay_prepare (ctx); + struct relay_event_data_server_remove *e = &m->data.server_remove; + e->event = RELAY_EVENT_SERVER_REMOVE; + e->server_name = str_from_cstr (s->name); +} + static void relay_prepare_error (struct app_context *ctx, uint32_t seq, const char *message) { @@ -4890,14 +4957,14 @@ buffer_add (struct app_context *ctx, struct buffer *buffer) { hard_assert (!buffer_by_name (ctx, buffer->name)); + relay_prepare_buffer_update (ctx, buffer); + relay_broadcast (ctx); + str_map_set (&ctx->buffers_by_name, buffer->name, buffer); LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer); buffer_open_log_file (ctx, buffer); - relay_prepare_buffer_update (ctx, buffer); - relay_broadcast (ctx); - // Normally this doesn't cause changes in the prompt but a prompt hook // could decide to show some information for all buffers nonetheless refresh_prompt (ctx); @@ -4909,6 +4976,9 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer) hard_assert (buffer != ctx->current_buffer); hard_assert (buffer != ctx->global_buffer); + relay_prepare_buffer_remove (ctx, buffer); + relay_broadcast (ctx); + CALL_ (ctx->input, buffer_destroy, buffer->input_data); buffer->input_data = NULL; @@ -4924,9 +4994,6 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer) if (buffer->type == BUFFER_SERVER) buffer->server->buffer = NULL; - relay_prepare_buffer_remove (ctx, buffer); - relay_broadcast (ctx); - str_map_set (&ctx->buffers_by_name, buffer->name, NULL); LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer); buffer_unref (buffer); @@ -5040,6 +5107,9 @@ buffer_activate (struct app_context *ctx, struct buffer *buffer) if (ctx->current_buffer == buffer) return; + relay_prepare_buffer_activate (ctx, buffer); + relay_broadcast (ctx); + // This is the only place where the unread messages marker // and highlight indicator are reset if (ctx->current_buffer) @@ -5056,9 +5126,6 @@ buffer_activate (struct app_context *ctx, struct buffer *buffer) ctx->last_buffer = ctx->current_buffer; ctx->current_buffer = buffer; - relay_prepare_buffer_activate (ctx, buffer); - relay_broadcast (ctx); - refresh_prompt (ctx); } @@ -5146,14 +5213,14 @@ buffer_rename (struct app_context *ctx, static void buffer_clear (struct app_context *ctx, struct buffer *buffer) { + relay_prepare_buffer_clear (ctx, buffer); + relay_broadcast (ctx); + LIST_FOR_EACH (struct buffer_line, iter, buffer->lines) buffer_line_destroy (iter); buffer->lines = buffer->lines_tail = NULL; buffer->lines_count = 0; - - relay_prepare_buffer_clear (ctx, buffer); - relay_broadcast (ctx); } static struct buffer * @@ -5665,6 +5732,17 @@ irc_send (struct server *s, const char *format, ...) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +static void +irc_set_state (struct server *s, enum server_state state) +{ + s->state = state; + + relay_prepare_server_update (s->ctx, s); + relay_broadcast (s->ctx); + + refresh_prompt (s->ctx); +} + static void irc_real_shutdown (struct server *s) { @@ -5680,7 +5758,7 @@ irc_real_shutdown (struct server *s) if (!soft_assert (errno == EINTR)) break; - s->state = IRC_HALF_CLOSED; + irc_set_state (s, IRC_HALF_CLOSED); } static void @@ -5691,7 +5769,7 @@ irc_shutdown (struct server *s) return; // TODO: set a timer to cut the connection if we don't receive an EOF - s->state = IRC_CLOSING; + irc_set_state (s, IRC_CLOSING); // Either there's still some data in the write buffer and we wait // until they're sent, or we send an EOF to the server right away @@ -5713,7 +5791,7 @@ irc_destroy_connector (struct server *s) s->socks_conn = NULL; // Not connecting anymore - s->state = IRC_DISCONNECTED; + irc_set_state (s, IRC_DISCONNECTED); } static void @@ -5744,7 +5822,7 @@ irc_destroy_transport (struct server *s) poller_fd_reset (&s->socket_event); xclose (s->socket); s->socket = -1; - s->state = IRC_DISCONNECTED; + irc_set_state (s, IRC_DISCONNECTED); str_reset (&s->read_buffer); str_reset (&s->write_buffer); @@ -5805,8 +5883,6 @@ irc_disconnect (struct server *s) s->reconnect_attempt = 0; irc_queue_reconnect (s); } - - refresh_prompt (s->ctx); } static void @@ -6558,7 +6634,7 @@ irc_finish_connection (struct server *s, int socket, const char *hostname) } log_server_status (s, s->buffer, "Connection established"); - s->state = IRC_CONNECTED; + irc_set_state (s, IRC_CONNECTED); s->socket_event = poller_fd_make (&ctx->poller, s->socket); s->socket_event.dispatcher = (poller_fd_fn) on_irc_ready; @@ -6567,8 +6643,6 @@ irc_finish_connection (struct server *s, int socket, const char *hostname) irc_update_poller (s, NULL); irc_reset_connection_timeouts (s); irc_register (s); - - refresh_prompt (s->ctx); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6715,7 +6789,7 @@ irc_initiate_connect (struct server *s) irc_queue_reconnect (s); } else if (s->state != IRC_CONNECTED) - s->state = IRC_CONNECTING; + irc_set_state (s, IRC_CONNECTING); } // --- Input prompt ------------------------------------------------------------ @@ -8263,8 +8337,7 @@ irc_on_registered (struct server *s, const char *nickname) str_reset (&s->irc_user_mode); cstr_set (&s->irc_user_host, NULL); - s->state = IRC_REGISTERED; - refresh_prompt (s->ctx); + irc_set_state (s, IRC_REGISTERED); // XXX: we can also use WHOIS if it's not supported (optional by RFC 2812) // TODO: maybe rather always use RPL_ISUPPORT NICKLEN & USERLEN & HOSTLEN @@ -9422,6 +9495,9 @@ server_remove (struct app_context *ctx, struct server *s) if (s->buffer) buffer_remove_safe (ctx, s->buffer); + relay_prepare_server_remove (ctx, s); + relay_broadcast (ctx); + struct str_map_unset_iter iter = str_map_unset_iter_make (&s->irc_buffer_map); struct buffer *buffer; @@ -9445,6 +9521,10 @@ static void server_rename (struct app_context *ctx, struct server *s, const char *new_name) { hard_assert (!str_map_find (&ctx->servers, new_name)); + + relay_prepare_server_rename (ctx, s, new_name); + relay_broadcast (ctx); + str_map_set (&ctx->servers, new_name, str_map_steal (&ctx->servers, s->name)); @@ -15319,6 +15399,14 @@ init_poller_events (struct app_context *ctx) static void client_resync (struct client *c) { + struct str_map_iter iter = str_map_iter_make (&c->ctx->servers); + struct server *s; + while ((s = str_map_iter_next (&iter))) + { + relay_prepare_server_update (c->ctx, s); + relay_send (c); + } + LIST_FOR_EACH (struct buffer, buffer, c->ctx->buffers) { relay_prepare_buffer_update (c->ctx, buffer); diff --git a/xP/public/xP.css b/xP/public/xP.css index 71bf602..b4ce13c 100644 --- a/xP/public/xP.css +++ b/xP/public/xP.css @@ -17,6 +17,10 @@ body { padding: .05em .3em; background: #eee; + display: flex; + justify-content: space-between; + align-items: baseline; + position: relative; border-top: 3px solid #ccc; border-bottom: 2px solid #888; @@ -39,11 +43,6 @@ body { bottom: -1px; background: #ccc; } -.title { - display: flex; - justify-content: space-between; - align-items: baseline; -} .middle { flex: auto; diff --git a/xP/public/xP.js b/xP/public/xP.js index ea4b288..ced93e9 100644 --- a/xP/public/xP.js +++ b/xP/public/xP.js @@ -193,6 +193,8 @@ let bufferCurrent = undefined let bufferLog = undefined let bufferAutoscroll = true +let servers = new Map() + function bufferResetStats(b) { b.newMessages = 0 b.newUnimportantMessages = 0 @@ -234,6 +236,8 @@ rpc.connect().then(result => { bufferLog = undefined bufferAutoscroll = true + servers.clear() + rpc.send({command: 'Hello', version: 1}) connecting = false m.redraw() @@ -250,6 +254,8 @@ rpc.addEventListener('Ping', event => { rpc.send({command: 'PingResponse', eventSeq: event.detail.eventSeq}) }) +// ~~~ Buffer events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + rpc.addEventListener('BufferUpdate', event => { let e = event.detail, b = buffers.get(e.bufferName) if (b === undefined) { @@ -260,7 +266,10 @@ rpc.addEventListener('BufferUpdate', event => { })) bufferResetStats(b) } + b.hideUnimportant = e.hideUnimportant + b.kind = e.context.kind + b.server = servers.get(e.context.serverName) }) rpc.addEventListener('BufferStats', event => { @@ -354,8 +363,8 @@ rpc.addEventListener('BufferLine', event => { } } - // TODO: Also highlight on unseen private messages, like xC does. - if (line.isHighlight) { + if (line.isHighlight || + (!visible && b.kind === 'PrivateMessage' && !line.isUnimportant)) { beep() if (!visible) b.highlighted = true @@ -368,6 +377,26 @@ rpc.addEventListener('BufferClear', event => { b.lines.length = 0 }) +// ~~~ Server events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +rpc.addEventListener('ServerUpdate', event => { + let e = event.detail, s = servers.get(e.serverName) + if (s === undefined) + servers.set(e.serverName, (s = {})) + s.state = e.state +}) + +rpc.addEventListener('ServerRename', event => { + let e = event.detail + servers.set(e.new, servers.get(e.serverName)) + servers.delete(e.serverName) +}) + +rpc.addEventListener('ServerRemove', event => { + let e = event.detail + servers.delete(e.serverName) +}) + // --- Colours ----------------------------------------------------------------- let palette = [ @@ -614,6 +643,21 @@ let BufferContainer = { }, } +let Status = { + view: vnode => { + let b = buffers.get(bufferCurrent) + if (b === undefined) + return m('.status', {}, 'Synchronizing...') + + let status = `${bufferCurrent}` + if (b.hideUnimportant) + status += `` + if (b.server !== undefined) + status += ` (${b.server.state})` + return m('.status', {}, status) + }, +} + let Input = { counter: 0, stamp: textarea => { @@ -749,8 +793,7 @@ let Main = { return m('.xP', {}, [ m('.title', {}, [`xP (${state})`, m(Toolbar)]), m('.middle', {}, [m(BufferList), m(BufferContainer)]), - // TODO: Indicate hideUnimportant. - m('.status', {}, bufferCurrent), + m(Status), m(Input), ]) },