Compare commits

...

5 Commits

7 changed files with 93 additions and 28 deletions

View File

@ -23,15 +23,16 @@ command aliases, SOCKS proxying, SASL EXTERNAL authentication using TLS client
certificates, a remote relay interface, or basic support for Lua scripting. certificates, a remote relay interface, or basic support for Lua scripting.
As a unique bonus, you can launch a full text editor from within. As a unique bonus, you can launch a full text editor from within.
xF
--
The X11 frontend for 'xC', making use of its networked relay interface.
It's currently in development, and is hidden behind a CMake option.
xP xP
-- --
The web frontend for 'xC', making use of its networked relay interface. The web frontend for 'xC', making use of its networked relay interface.
It's currently rather elementary, and can be built from within its directory. So far it's quite basic, yet usable. Build it using `make` in its directory,
and run it from within the _public_ subdirectory.
xF
--
The X11 frontend for 'xC', making use of its networked relay interface.
It's currently in development, and hidden behind a CMake option.
xD xD
-- --
@ -72,15 +73,15 @@ Building
Build-only dependencies: Build-only dependencies:
CMake, pkg-config, asciidoctor or asciidoc, awk, liberty (included) + CMake, pkg-config, asciidoctor or asciidoc, awk, liberty (included) +
Common runtime dependencies: openssl + Common runtime dependencies: openssl +
Additionally for 'xC': curses, libffi, + Additionally for 'xC': curses, libffi, readline >= 6.0 or libedit >= 2013-07-12,
readline >= 6.0 or libedit >= 2013-07-12, lua >= 5.3 (optional) + lua >= 5.3 (optional) +
Additionally for 'xF': x11, xft Additionally for 'xF': x11, xft
$ git clone --recursive https://git.janouch.name/p/xK.git $ git clone --recursive https://git.janouch.name/p/xK.git
$ mkdir xK/build $ mkdir xK/build
$ cd xK/build $ cd xK/build
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo \ $ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DWANT_READLINE=ON -DWANT_LIBEDIT=OFF -DWANT_LUA=ON -DWANT_READLINE=ON -DWANT_LIBEDIT=OFF -DWITH_LUA=ON
$ make $ make
To install the application, you can do either the usual: To install the application, you can do either the usual:

View File

@ -69,6 +69,7 @@ struct EventMessage {
ERROR, ERROR,
JOIN, JOIN,
PART, PART,
ACTION,
} rendition; } rendition;
// Unix timestamp in seconds. // Unix timestamp in seconds.
u64 when; u64 when;

22
xC.c
View File

@ -1480,7 +1480,7 @@ struct formatter_item
{ {
enum formatter_item_type type : 16; ///< Type of this item enum formatter_item_type type : 16; ///< Type of this item
int attribute : 16; ///< Attribute ID or a TEXT_* mask int attribute : 16; ///< Attribute ID or a TEXT_* mask
int color; ///< Colour int color; ///< Colour ([256 << 16] | 16)
char *text; ///< String char *text; ///< String
}; };
@ -1536,6 +1536,7 @@ enum buffer_line_rendition
BUFFER_LINE_ERROR, ///< Error message BUFFER_LINE_ERROR, ///< Error message
BUFFER_LINE_JOIN, ///< Join arrow BUFFER_LINE_JOIN, ///< Join arrow
BUFFER_LINE_PART, ///< Part arrow BUFFER_LINE_PART, ///< Part arrow
BUFFER_LINE_ACTION, ///< Highlighted asterisk
}; };
struct buffer_line struct buffer_line
@ -3114,6 +3115,9 @@ relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer,
union relay_item_data *p = e->items = xcalloc (len * 6, sizeof *e->items); union relay_item_data *p = e->items = xcalloc (len * 6, sizeof *e->items);
for (struct formatter_item *i = line->items; len--; i++) for (struct formatter_item *i = line->items; len--; i++)
{ {
// XXX: See attr_printer_decode_color(), this is a footgun.
int16_t c16 = i->color;
int16_t c256 = i->color >> 16;
switch (i->type) switch (i->type)
{ {
case FORMATTER_ITEM_TEXT: case FORMATTER_ITEM_TEXT:
@ -3125,11 +3129,11 @@ relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer,
(p++)->kind = RELAY_ITEM_RESET; (p++)->kind = RELAY_ITEM_RESET;
break; break;
case FORMATTER_ITEM_FG_COLOR: case FORMATTER_ITEM_FG_COLOR:
p->fg_color.color = i->color; p->fg_color.color = c256 <= 0 ? c16 : c256;
(p++)->kind = RELAY_ITEM_FG_COLOR; (p++)->kind = RELAY_ITEM_FG_COLOR;
break; break;
case FORMATTER_ITEM_BG_COLOR: case FORMATTER_ITEM_BG_COLOR:
p->bg_color.color = i->color; p->bg_color.color = c256 <= 0 ? c16 : c256;
(p++)->kind = RELAY_ITEM_BG_COLOR; (p++)->kind = RELAY_ITEM_BG_COLOR;
break; break;
case FORMATTER_ITEM_SIMPLE: case FORMATTER_ITEM_SIMPLE:
@ -4475,6 +4479,7 @@ buffer_line_flush (struct buffer_line *line, struct formatter *f, FILE *output,
case BUFFER_LINE_ERROR: formatter_add (f, "#a=!=#r ", ATTR_ERROR); break; case BUFFER_LINE_ERROR: formatter_add (f, "#a=!=#r ", ATTR_ERROR); break;
case BUFFER_LINE_JOIN: formatter_add (f, "#a-->#r ", ATTR_JOIN); break; case BUFFER_LINE_JOIN: formatter_add (f, "#a-->#r ", ATTR_JOIN); break;
case BUFFER_LINE_PART: formatter_add (f, "#a<--#r ", ATTR_PART); break; case BUFFER_LINE_PART: formatter_add (f, "#a<--#r ", ATTR_PART); break;
case BUFFER_LINE_ACTION: formatter_add (f, " #a*#r ", ATTR_ACTION); break;
} }
for (struct formatter_item *iter = line->items; iter->type; iter++) for (struct formatter_item *iter = line->items; iter->type; iter++)
@ -4691,16 +4696,16 @@ log_full (struct app_context *ctx, struct server *s, struct buffer *buffer,
#define log_outcoming_privmsg(s, buffer, prefixes, who, text) \ #define log_outcoming_privmsg(s, buffer, prefixes, who, text) \
log_server ((s), (buffer), 0, 0, "<#s#n> #m", (prefixes), (who), (text)) log_server ((s), (buffer), 0, 0, "<#s#n> #m", (prefixes), (who), (text))
#define log_outcoming_action(s, buffer, who, text) \ #define log_outcoming_action(s, buffer, who, text) \
log_server ((s), (buffer), 0, 0, " #a*#r #n #m", \ log_server ((s), (buffer), 0, BUFFER_LINE_ACTION, "#n #m", (who), (text))
ATTR_ACTION, (who), (text))
#define log_outcoming_orphan_notice(s, target, text) \ #define log_outcoming_orphan_notice(s, target, text) \
log_server_status ((s), (s)->buffer, "Notice -> #n: #m", (target), (text)) log_server_status ((s), (s)->buffer, "Notice -> #n: #m", (target), (text))
#define log_outcoming_orphan_privmsg(s, target, text) \ #define log_outcoming_orphan_privmsg(s, target, text) \
log_server_status ((s), (s)->buffer, "MSG(#n): #m", (target), (text)) log_server ((s), (s)->buffer, 0, BUFFER_LINE_STATUS, \
"MSG(#n): #m", (target), (text))
#define log_outcoming_orphan_action(s, target, text) \ #define log_outcoming_orphan_action(s, target, text) \
log_server_status ((s), (s)->buffer, "MSG(#n): #a*#r #m", (target), \ log_server ((s), (s)->buffer, 0, BUFFER_LINE_ACTION, \
ATTR_ACTION, (text)) "MSG(#n): #m", (target), (text))
#define log_ctcp_query(s, target, tag) \ #define log_ctcp_query(s, target, tag) \
log_server_status ((s), (s)->buffer, "CTCP query to #S: #S", target, tag) log_server_status ((s), (s)->buffer, "CTCP query to #S: #S", target, tag)
@ -8763,6 +8768,7 @@ irc_process_numeric (struct server *s,
if (msg->params.len == 2) if (msg->params.len == 2)
irc_try_parse_welcome_for_userhost (s, msg->params.vector[1]); irc_try_parse_welcome_for_userhost (s, msg->params.vector[1]);
break; break;
case IRC_RPL_MOTDSTART:
case IRC_RPL_MOTD: case IRC_RPL_MOTD:
if (copy.len) if (copy.len)
irc_adjust_motd (&copy.vector[0]); irc_adjust_motd (&copy.vector[0]);

View File

@ -1,4 +1,4 @@
module janouch.name/xK module janouch.name/xK/xP
go 1.18 go 1.18

View File

@ -79,6 +79,9 @@ body {
.mark.part { .mark.part {
color: red; color: red;
} }
.mark.action {
color: darkred;
}
.content { .content {
padding: .1rem .3rem; padding: .1rem .3rem;
white-space: pre-wrap; white-space: pre-wrap;

View File

@ -1,3 +1,40 @@
// Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
// SPDX-License-Identifier: 0BSD
// --- Colours -----------------------------------------------------------------
let palette = [
'#000', '#800', '#080', '#880', '#008', '#808', '#088', '#ccc',
'#888', '#f00', '#0f0', '#ff0', '#00f', '#f0f', '#0ff', '#fff',
]
palette.length = 256
for (let i = 0; i < 216; i++) {
let r = i / 36 >> 0, g = (i / 6 >> 0) % 6, b = i % 6
r = !r ? '00' : (55 + 40 * r).toString(16)
g = !g ? '00' : (55 + 40 * g).toString(16)
b = !b ? '00' : (55 + 40 * b).toString(16)
palette[16 + i] = `#${r}${g}${b}`
}
for (let i = 0; i < 24; i++) {
let g = ('0' + (8 + i * 10).toString(16)).slice(-2)
palette[232 + i] = `#${g}${g}${g}`
}
function applyColor(fg, bg, inverse) {
if (inverse)
[fg, bg] = [bg >= 0 ? bg : 15, fg >= 0 ? fg : 0]
let style = ''
if (fg >= 0)
style += `color: ${palette[fg]};`
if (bg >= 0)
style += `background-color: ${palette[bg]};`
if (style)
return style
}
// ---- Event processing -------------------------------------------------------
// TODO: Probably reset state on disconnect, and indicate to user. // TODO: Probably reset state on disconnect, and indicate to user.
let socket = new WebSocket(proxy) let socket = new WebSocket(proxy)
@ -59,6 +96,8 @@ socket.onmessage = function(event) {
m.redraw() m.redraw()
} }
// ---- UI ---------------------------------------------------------------------
let BufferList = { let BufferList = {
view: vnode => { view: vnode => {
let items = [] let items = []
@ -86,6 +125,7 @@ let Content = {
case 'Error': content.push(m('span.mark.error', {}, '⚠')); break case 'Error': content.push(m('span.mark.error', {}, '⚠')); break
case 'Join': content.push(m('span.mark.join', {}, '→')); break case 'Join': content.push(m('span.mark.join', {}, '→')); break
case 'Part': content.push(m('span.mark.part', {}, '←')); break case 'Part': content.push(m('span.mark.part', {}, '←')); break
case 'Action': content.push(m('span.mark.action', {}, '✶')); break
} }
let classes = new Set() let classes = new Set()
@ -95,22 +135,33 @@ let Content = {
else else
classes.add(c) classes.add(c)
} }
let fg = -1, bg = -1, inverse = false
line.items.forEach(item => { line.items.forEach(item => {
// TODO: Colours.
switch (item.kind) { switch (item.kind) {
case 'Text': case 'Text':
// TODO: Detect and transform links. // TODO: Detect and transform links.
content.push(m('span', { content.push(m('span', {
class: Array.from(classes.keys()).join(' '), class: Array.from(classes.keys()).join(' '),
style: applyColor(fg, bg, inverse),
}, item.text)) }, item.text))
break break
case 'Reset': case 'Reset':
classes.clear() classes.clear()
fg = bg = -1
inverse = false
break
case 'FgColor':
fg = item.color
break
case 'BgColor':
bg = item.color
break
case 'FlipInverse':
inverse = !inverse
break break
case 'FlipBold': flip('b'); break case 'FlipBold': flip('b'); break
case 'FlipItalic': flip('i'); break case 'FlipItalic': flip('i'); break
case 'FlipUnderline': flip('u'); break case 'FlipUnderline': flip('u'); break
case 'FlipInverse': flip('i'); break
case 'FlipCrossedOut': flip('s'); break case 'FlipCrossedOut': flip('s'); break
case 'FlipMonospace': flip('m'); break case 'FlipMonospace': flip('m'); break
} }

View File

@ -1,3 +1,6 @@
// Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
// SPDX-License-Identifier: 0BSD
package main package main
import ( import (