summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-09-10 15:55:13 +0200
committerPřemysl Eric Janouch <p@janouch.name>2022-09-11 01:20:18 +0200
commit4bc2f736f2ef34464c1afa1296850c3af014b933 (patch)
tree1074ee992fe810248133f47acabe941cd3ae8e03
parentadd670212f71c3b8e11661eff6c5ea1ae430ce95 (diff)
downloadxK-4bc2f736f2ef34464c1afa1296850c3af014b933.tar.gz
xK-4bc2f736f2ef34464c1afa1296850c3af014b933.tar.xz
xK-4bc2f736f2ef34464c1afa1296850c3af014b933.zip
xC: make terminal attributes abstract
And translate them for frontends. This is very long overdue, and a rather significant cleanup. Bump liberty.
-rw-r--r--NEWS6
-rw-r--r--README.adoc32
m---------liberty0
-rw-r--r--xC.c499
4 files changed, 312 insertions, 225 deletions
diff --git a/NEWS b/NEWS
index 59fb4f8..dcf8c54 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Unreleased
+2.0.0 (Unreleased)
* xD: implemented WALLOPS, choosing to make it target even non-operators
@@ -8,6 +8,10 @@ Unreleased
with the exception of editor_command/editor, backlog_helper/pager,
and backlog_helper_strip_formatting/pager_strip_formatting
+ * xC: all attributes.* configuration options have been made abstract in
+ a subset of the git-config(1) format, and renamed to theme.*,
+ with the exception of attributes.reset, which has no replacement
+
* xC: replaced behaviour.save_on_quit with general.autosave
* xC: improved pager integration capabilities
diff --git a/README.adoc b/README.adoc
index 6d61bcc..10b18cd 100644
--- a/README.adoc
+++ b/README.adoc
@@ -26,12 +26,13 @@ As a unique bonus, you can launch a full text editor from within.
xP
--
The web frontend for 'xC', making use of its networked relay interface.
-So far it's somewhat basic, yet usable.
+So far it's a bit rough around the edges, yet fully usable.
xF
--
The X11 frontend for 'xC', making use of its networked relay interface.
-It's currently in development, and hidden behind a CMake option.
+This subproject has been put on hold, partly because of its massive overlap
+with 'xP', and is hidden behind a CMake option.
xD
--
@@ -117,8 +118,19 @@ as a `forking` type systemd user service.
xP
~~
-Install the Go compiler, and build the server using `make` in its directory,
-then run it from within the _public_ subdirectory.
+The precondition for running 'xC' frontends is enabling its relay interface:
+
+ /set general.relay_bind = "127.0.0.1:9000"
+
+To build the web server, you'll need to install the Go compiler, and run `make`
+from the _xP_ directory. Then start it from the _public_ subdirectory,
+and navigate to the adress you gave it as its first argument--in the following
+example, that would be http://localhost:8080[]:
+
+ $ ../xP 127.0.0.1:8080 127.0.0.1:9000
+
+For remote use, it's recommended to put 'xP' behind a reverse proxy, with TLS,
+and some form of HTTP authentication.
Client Certificates
-------------------
@@ -165,12 +177,12 @@ properly set-up terminal emulator, it suffices to run:
/set general.pager = Press Tab here and change +Gb to +Gb1d
/set general.date_change_line = "%a %e %b %Y"
/set general.plugin_autoload += "fancy-prompt.lua"
- /set attributes.userhost = "\x1b[38;5;109m"
- /set attributes.join = "\x1b[38;5;108m"
- /set attributes.part = "\x1b[38;5;138m"
- /set attributes.external = "\x1b[38;5;248m"
- /set attributes.timestamp = "\x1b[48;5;255m\x1b[38;5;250m"
- /set attributes.read_marker = "\x1b[38;5;202m"
+ /set theme.userhost = "109"
+ /set theme.join = "108"
+ /set theme.part = "138"
+ /set theme.external = "248"
+ /set theme.timestamp = "250 255"
+ /set theme.read_marker = "202"
Configuration profiles
----------------------
diff --git a/liberty b/liberty
-Subproject f545be725df9195a5b5897ad95a0220acf10f14
+Subproject 22a121383f73fa7739f324021b6ad0ba6ed3cdb
diff --git a/xC.c b/xC.c
index 1399744..c35c158 100644
--- a/xC.c
+++ b/xC.c
@@ -19,7 +19,6 @@
// A table of all attributes we use for output
#define ATTR_TABLE(XX) \
XX( PROMPT, prompt, Terminal attrs for the prompt ) \
- XX( RESET, reset, String to reset terminal attributes ) \
XX( DATE_CHANGE, date_change, Terminal attrs for date change ) \
XX( READ_MARKER, read_marker, Terminal attrs for the read marker ) \
XX( WARNING, warning, Terminal attrs for warnings ) \
@@ -34,6 +33,7 @@
enum
{
+ ATTR_RESET,
#define XX(x, y, z) ATTR_ ## x,
ATTR_TABLE (XX)
#undef XX
@@ -48,6 +48,9 @@ enum
#include "config.h"
#define PROGRAM_NAME "xC"
+// fmemopen
+#define _POSIX_C_SOURCE 200809L
+
#include "common.c"
#include "xD-replies.c"
#include "xC-proto.c"
@@ -1452,6 +1455,58 @@ channel_destroy (struct channel *self)
REF_COUNTABLE_METHODS (channel)
+// ~~~ Attribute utilities ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+enum
+{
+ TEXT_BOLD = 1 << 0,
+ TEXT_ITALIC = 1 << 1,
+ TEXT_UNDERLINE = 1 << 2,
+ TEXT_INVERSE = 1 << 3,
+ TEXT_BLINK = 1 << 4,
+ TEXT_CROSSED_OUT = 1 << 5,
+ TEXT_MONOSPACE = 1 << 6
+};
+
+// Similar to code in liberty-tui.c.
+struct attrs
+{
+ short fg; ///< Foreground (256-colour cube or -1)
+ short bg; ///< Background (256-colour cube or -1)
+ unsigned attrs; ///< TEXT_* mask
+};
+
+/// Decode attributes in the value using a subset of the git config format,
+/// ignoring all errors since it doesn't affect functionality
+static struct attrs
+attrs_decode (const char *value)
+{
+ struct strv v = strv_make ();
+ cstr_split (value, " ", true, &v);
+
+ int colors = 0;
+ struct attrs attrs = { -1, -1, 0 };
+ for (char **it = v.vector; *it; it++)
+ {
+ char *end = NULL;
+ long n = strtol (*it, &end, 10);
+ if (*it != end && !*end && n >= SHRT_MIN && n <= SHRT_MAX)
+ {
+ if (colors == 0) attrs.fg = n;
+ if (colors == 1) attrs.bg = n;
+ colors++;
+ }
+ else if (!strcmp (*it, "bold")) attrs.attrs |= TEXT_BOLD;
+ else if (!strcmp (*it, "italic")) attrs.attrs |= TEXT_ITALIC;
+ else if (!strcmp (*it, "ul")) attrs.attrs |= TEXT_UNDERLINE;
+ else if (!strcmp (*it, "reverse")) attrs.attrs |= TEXT_INVERSE;
+ else if (!strcmp (*it, "blink")) attrs.attrs |= TEXT_BLINK;
+ else if (!strcmp (*it, "strike")) attrs.attrs |= TEXT_CROSSED_OUT;
+ }
+ strv_free (&v);
+ return attrs;
+}
+
// ~~~ Buffers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum formatter_item_type
@@ -1465,17 +1520,6 @@ enum formatter_item_type
FORMATTER_ITEM_IGNORE_ATTR ///< Un/set attribute ignoration
};
-enum
-{
- TEXT_BOLD = 1 << 0,
- TEXT_ITALIC = 1 << 1,
- TEXT_UNDERLINE = 1 << 2,
- TEXT_INVERSE = 1 << 3,
- TEXT_BLINK = 1 << 4,
- TEXT_CROSSED_OUT = 1 << 5,
- TEXT_MONOSPACE = 1 << 6
-};
-
struct formatter_item
{
enum formatter_item_type type : 16; ///< Type of this item
@@ -2116,12 +2160,13 @@ struct completion_hook
struct app_context
{
- char *attrs_defaults[ATTR_COUNT]; ///< Default terminal attributes
+ /// Default terminal attributes
+ struct attrs theme_defaults[ATTR_COUNT];
// Configuration:
struct config config; ///< Program configuration
- char *attrs[ATTR_COUNT]; ///< Terminal attributes
+ struct attrs theme[ATTR_COUNT]; ///< Terminal attributes
bool isolate_buffers; ///< Isolate global/server buffers
bool beep_on_highlight; ///< Beep on highlight
bool logging; ///< Logging to file enabled
@@ -2305,11 +2350,6 @@ app_context_free (struct app_context *self)
plugin_destroy (iter);
config_free (&self->config);
- for (size_t i = 0; i < ATTR_COUNT; i++)
- {
- free (self->attrs_defaults[i]);
- free (self->attrs[i]);
- }
LIST_FOR_EACH (struct buffer, iter, self->buffers)
{
@@ -2367,7 +2407,7 @@ on_config_show_all_prefixes_change (struct config_item *item)
static void on_config_relay_bind_change (struct config_item *item);
static void on_config_backlog_limit_change (struct config_item *item);
-static void on_config_attribute_change (struct config_item *item);
+static void on_config_theme_change (struct config_item *item);
static void on_config_logging_change (struct config_item *item);
#define TRIVIAL_BOOLEAN_ON_CHANGE(name) \
@@ -2648,10 +2688,10 @@ static struct config_schema g_config_general[] =
{}
};
-static struct config_schema g_config_attributes[] =
+static struct config_schema g_config_theme[] =
{
#define XX(x, y, z) { .name = #y, .comment = #z, .type = CONFIG_ITEM_STRING, \
- .on_change = on_config_attribute_change },
+ .on_change = on_config_theme_change },
ATTR_TABLE (XX)
#undef XX
{}
@@ -2666,9 +2706,9 @@ load_config_general (struct config_item *subtree, void *user_data)
}
static void
-load_config_attributes (struct config_item *subtree, void *user_data)
+load_config_theme (struct config_item *subtree, void *user_data)
{
- config_schema_apply_to_object (g_config_attributes, subtree, user_data);
+ config_schema_apply_to_object (g_config_theme, subtree, user_data);
}
static void
@@ -2676,11 +2716,11 @@ register_config_modules (struct app_context *ctx)
{
struct config *config = &ctx->config;
// The servers are loaded later when we can create buffers for them
- config_register_module (config, "servers", NULL, NULL);
- config_register_module (config, "aliases", NULL, NULL);
- config_register_module (config, "plugins", NULL, NULL);
- config_register_module (config, "general", load_config_general, ctx);
- config_register_module (config, "attributes", load_config_attributes, ctx);
+ config_register_module (config, "servers", NULL, NULL);
+ config_register_module (config, "aliases", NULL, NULL);
+ config_register_module (config, "plugins", NULL, NULL);
+ config_register_module (config, "general", load_config_general, ctx);
+ config_register_module (config, "theme", load_config_theme, ctx);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -3114,6 +3154,64 @@ relay_prepare_buffer_activate (struct app_context *ctx, struct buffer *buffer)
e->buffer_name = str_from_cstr (buffer->name);
}
+static union relay_item_data *
+relay_translate_formatter (struct app_context *ctx, union relay_item_data *p,
+ struct formatter_item *i)
+{
+ // XXX: See attr_printer_decode_color(), this is a footgun.
+ int16_t c16 = i->color;
+ int16_t c256 = i->color >> 16;
+
+ unsigned attrs = i->attribute;
+ switch (i->type)
+ {
+ case FORMATTER_ITEM_TEXT:
+ p->text.text = str_from_cstr (i->text);
+ (p++)->kind = RELAY_ITEM_TEXT;
+ break;
+ case FORMATTER_ITEM_FG_COLOR:
+ p->fg_color.color = c256 <= 0 ? c16 : c256;
+ (p++)->kind = RELAY_ITEM_FG_COLOR;
+ break;
+ case FORMATTER_ITEM_BG_COLOR:
+ p->bg_color.color = c256 <= 0 ? c16 : c256;
+ (p++)->kind = RELAY_ITEM_BG_COLOR;
+ break;
+ case FORMATTER_ITEM_ATTR:
+ (p++)->kind = RELAY_ITEM_RESET;
+ if ((c256 = ctx->theme[i->attribute].fg) >= 0)
+ {
+ p->fg_color.color = c256;
+ (p++)->kind = RELAY_ITEM_FG_COLOR;
+ }
+ if ((c256 = ctx->theme[i->attribute].bg) >= 0)
+ {
+ p->bg_color.color = c256;
+ (p++)->kind = RELAY_ITEM_BG_COLOR;
+ }
+
+ attrs = ctx->theme[i->attribute].attrs;
+ // Fall-through
+ case FORMATTER_ITEM_SIMPLE:
+ if (attrs & TEXT_BOLD)
+ (p++)->kind = RELAY_ITEM_FLIP_BOLD;
+ if (attrs & TEXT_ITALIC)
+ (p++)->kind = RELAY_ITEM_FLIP_ITALIC;
+ if (attrs & TEXT_UNDERLINE)
+ (p++)->kind = RELAY_ITEM_FLIP_UNDERLINE;
+ if (attrs & TEXT_INVERSE)
+ (p++)->kind = RELAY_ITEM_FLIP_INVERSE;
+ if (attrs & TEXT_CROSSED_OUT)
+ (p++)->kind = RELAY_ITEM_FLIP_CROSSED_OUT;
+ if (attrs & TEXT_MONOSPACE)
+ (p++)->kind = RELAY_ITEM_FLIP_MONOSPACE;
+ break;
+ default:
+ break;
+ }
+ return p;
+}
+
static void
relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer,
struct buffer_line *line, bool leak_to_active)
@@ -3132,49 +3230,10 @@ relay_prepare_buffer_line (struct app_context *ctx, struct buffer *buffer,
for (size_t i = 0; line->items[i].type; i++)
len++;
- // XXX: This way helps xP's JSON conversion, but is super annoying for us.
- union relay_item_data *p = e->items = xcalloc (len * 6, sizeof *e->items);
+ // Beware of the upper bound, currently dominated by FORMATTER_ITEM_ATTR.
+ union relay_item_data *p = e->items = xcalloc (len * 9, sizeof *e->items);
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)
- {
- case FORMATTER_ITEM_TEXT:
- p->text.text = str_from_cstr (i->text);
- (p++)->kind = RELAY_ITEM_TEXT;
- break;
- case FORMATTER_ITEM_ATTR:
- // For future consideration.
- (p++)->kind = RELAY_ITEM_RESET;
- break;
- case FORMATTER_ITEM_FG_COLOR:
- p->fg_color.color = c256 <= 0 ? c16 : c256;
- (p++)->kind = RELAY_ITEM_FG_COLOR;
- break;
- case FORMATTER_ITEM_BG_COLOR:
- p->bg_color.color = c256 <= 0 ? c16 : c256;
- (p++)->kind = RELAY_ITEM_BG_COLOR;
- break;
- case FORMATTER_ITEM_SIMPLE:
- if (i->attribute & TEXT_BOLD)
- (p++)->kind = RELAY_ITEM_FLIP_BOLD;
- if (i->attribute & TEXT_ITALIC)
- (p++)->kind = RELAY_ITEM_FLIP_ITALIC;
- if (i->attribute & TEXT_UNDERLINE)
- (p++)->kind = RELAY_ITEM_FLIP_UNDERLINE;
- if (i->attribute & TEXT_INVERSE)
- (p++)->kind = RELAY_ITEM_FLIP_INVERSE;
- if (i->attribute & TEXT_CROSSED_OUT)
- (p++)->kind = RELAY_ITEM_FLIP_CROSSED_OUT;
- if (i->attribute & TEXT_MONOSPACE)
- (p++)->kind = RELAY_ITEM_FLIP_MONOSPACE;
- break;
- default:
- break;
- }
- }
+ p = relay_translate_formatter (ctx, p, i);
e->items_len = p - e->items;
}
@@ -3239,123 +3298,6 @@ get_attribute_printer (FILE *stream)
return NULL;
}
-static void
-vprint_attributed (struct app_context *ctx,
- FILE *stream, intptr_t attribute, const char *fmt, va_list ap)
-{
- terminal_printer_fn printer = get_attribute_printer (stream);
- if (!attribute)
- printer = NULL;
-
- if (printer)
- tputs (ctx->attrs[attribute], 1, printer);
-
- vfprintf (stream, fmt, ap);
-
- if (printer)
- tputs (ctx->attrs[ATTR_RESET], 1, printer);
-}
-
-static void
-print_attributed (struct app_context *ctx,
- FILE *stream, intptr_t attribute, const char *fmt, ...)
-{
- va_list ap;
- va_start (ap, fmt);
- vprint_attributed (ctx, stream, attribute, fmt, ap);
- va_end (ap);
-}
-
-static void
-log_message_attributed (void *user_data, const char *quote, const char *fmt,
- va_list ap)
-{
- FILE *stream = stderr;
- struct app_context *ctx = g_ctx;
-
- CALL (ctx->input, hide);
-
- print_attributed (ctx, stream, (intptr_t) user_data, "%s", quote);
- vprint_attributed (ctx, stream, (intptr_t) user_data, fmt, ap);
- fputs ("\n", stream);
-
- CALL (ctx->input, show);
-}
-
-static ssize_t
-attr_by_name (const char *name)
-{
- static const char *table[ATTR_COUNT] =
- {
-#define XX(x, y, z) [ATTR_ ## x] = #y,
- ATTR_TABLE (XX)
-#undef XX
- };
-
- for (size_t i = 0; i < N_ELEMENTS (table); i++)
- if (!strcmp (name, table[i]))
- return i;
- return -1;
-}
-
-static void
-on_config_attribute_change (struct config_item *item)
-{
- struct app_context *ctx = item->user_data;
- ssize_t id = attr_by_name (item->schema->name);
- if (id != -1)
- {
- cstr_set (&ctx->attrs[id], xstrdup (item->type == CONFIG_ITEM_NULL
- ? ctx->attrs_defaults[id]
- : item->value.string.str));
- }
-}
-
-static void
-init_colors (struct app_context *ctx)
-{
- bool have_ti = init_terminal ();
- char **defaults = ctx->attrs_defaults;
-
-#define INIT_ATTR(id, ti) defaults[ATTR_ ## id] = xstrdup (have_ti ? (ti) : "")
-
- INIT_ATTR (PROMPT, enter_bold_mode);
- INIT_ATTR (RESET, exit_attribute_mode);
- INIT_ATTR (DATE_CHANGE, enter_bold_mode);
- INIT_ATTR (READ_MARKER, g_terminal.color_set_fg[COLOR_MAGENTA]);
- INIT_ATTR (WARNING, g_terminal.color_set_fg[COLOR_YELLOW]);
- INIT_ATTR (ERROR, g_terminal.color_set_fg[COLOR_RED]);
-
- INIT_ATTR (EXTERNAL, g_terminal.color_set_fg[COLOR_WHITE]);
- INIT_ATTR (TIMESTAMP, g_terminal.color_set_fg[COLOR_WHITE]);
- INIT_ATTR (ACTION, g_terminal.color_set_fg[COLOR_RED]);
- INIT_ATTR (USERHOST, g_terminal.color_set_fg[COLOR_CYAN]);
- INIT_ATTR (JOIN, g_terminal.color_set_fg[COLOR_GREEN]);
- INIT_ATTR (PART, g_terminal.color_set_fg[COLOR_RED]);
-
- char *highlight = have_ti ? xstrdup_printf ("%s%s%s",
- g_terminal.color_set_fg[COLOR_YELLOW],
- g_terminal.color_set_bg[COLOR_MAGENTA],
- enter_bold_mode) : NULL;
- INIT_ATTR (HIGHLIGHT, highlight);
- free (highlight);
-
-#undef INIT_ATTR
-
- // This prevents formatters from obtaining an attribute printer function
- if (!have_ti)
- {
- g_terminal.stdout_is_tty = false;
- g_terminal.stderr_is_tty = false;
- }
-
- g_log_message_real = log_message_attributed;
-
- // Apply the default values so that we start with any formatting at all
- config_schema_call_changed
- (config_item_get (ctx->config.root, "attributes", NULL));
-}
-
// ~~~ Attribute printer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// A little tool that tries to make the most of the terminal's capabilities
@@ -3365,12 +3307,12 @@ init_colors (struct app_context *ctx)
struct attr_printer
{
- char **attrs; ///< Named attributes
+ struct attrs *attrs; ///< Named attributes
FILE *stream; ///< Output stream
bool dirty; ///< Attributes are set
};
-#define ATTR_PRINTER_INIT(ctx, stream) { ctx->attrs, stream, true }
+#define ATTR_PRINTER_INIT(attrs, stream) { attrs, stream, true }
static void
attr_printer_filtered_puts (FILE *stream, const char *attr)
@@ -3408,22 +3350,11 @@ static void
attr_printer_reset (struct attr_printer *self)
{
if (self->dirty)
- attr_printer_tputs (self, self->attrs[ATTR_RESET]);
+ attr_printer_tputs (self, exit_attribute_mode);
self->dirty = false;
}
-static void
-attr_printer_apply_named (struct attr_printer *self, int attribute)
-{
- attr_printer_reset (self);
- if (attribute != ATTR_RESET)
- {
- attr_printer_tputs (self, self->attrs[attribute]);
- self->dirty = true;
- }
-}
-
// NOTE: commonly terminals have:
// 8 colours (worst, bright fg often with BOLD, bg sometimes with BLINK)
// 16 colours (okayish, we have the full basic range guaranteed)
@@ -3551,6 +3482,135 @@ attr_printer_apply (struct attr_printer *self,
self->dirty = true;
}
+static void
+attr_printer_apply_named (struct attr_printer *self, int attribute)
+{
+ attr_printer_reset (self);
+ if (attribute == ATTR_RESET)
+ return;
+
+ // See the COLOR_256 macro or attr_printer_decode_color().
+ struct attrs *a = &self->attrs[attribute];
+ attr_printer_apply (self, a->attrs,
+ a->fg < 16 ? a->fg : (a->fg << 16 | (-1 & 0xFFFF)),
+ a->bg < 16 ? a->bg : (a->bg << 16 | (-1 & 0xFFFF)));
+ self->dirty = true;
+}
+
+// ~~~ Logging redirect ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+static void
+vprint_attributed (struct app_context *ctx,
+ FILE *stream, intptr_t attribute, const char *fmt, va_list ap)
+{
+ terminal_printer_fn printer = get_attribute_printer (stream);
+ if (!attribute)
+ printer = NULL;
+
+ struct attr_printer state = ATTR_PRINTER_INIT (ctx->theme, stream);
+ if (printer)
+ attr_printer_apply_named (&state, attribute);
+
+ vfprintf (stream, fmt, ap);
+
+ if (printer)
+ attr_printer_reset (&state);
+}
+
+static void
+print_attributed (struct app_context *ctx,
+ FILE *stream, intptr_t attribute, const char *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vprint_attributed (ctx, stream, attribute, fmt, ap);
+ va_end (ap);
+}
+
+static void
+log_message_attributed (void *user_data, const char *quote, const char *fmt,
+ va_list ap)
+{
+ FILE *stream = stderr;
+ struct app_context *ctx = g_ctx;
+
+ CALL (ctx->input, hide);
+
+ print_attributed (ctx, stream, (intptr_t) user_data, "%s", quote);
+ vprint_attributed (ctx, stream, (intptr_t) user_data, fmt, ap);
+ fputs ("\n", stream);
+
+ CALL (ctx->input, show);
+}
+
+// ~~~ Theme ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+static ssize_t
+attr_by_name (const char *name)
+{
+ static const char *table[ATTR_COUNT] =
+ {
+ NULL,
+#define XX(x, y, z) [ATTR_ ## x] = #y,
+ ATTR_TABLE (XX)
+#undef XX
+ };
+
+ for (size_t i = 1; i < N_ELEMENTS (table); i++)
+ if (!strcmp (name, table[i]))
+ return i;
+ return -1;
+}
+
+static void
+on_config_theme_change (struct config_item *item)
+{
+ struct app_context *ctx = item->user_data;
+ ssize_t id = attr_by_name (item->schema->name);
+ if (id != -1)
+ {
+ // TODO: There should be a validator.
+ ctx->theme[id] = item->type == CONFIG_ITEM_NULL
+ ? ctx->theme_defaults[id]
+ : attrs_decode (item->value.string.str);
+ }
+}
+
+static void
+init_colors (struct app_context *ctx)
+{
+ bool have_ti = init_terminal ();
+
+#define INIT_ATTR(id, ...) ctx->theme[ATTR_ ## id] = \
+ ctx->theme_defaults[ATTR_ ## id] = (struct attrs) { __VA_ARGS__ }
+
+ INIT_ATTR (PROMPT, -1, -1, TEXT_BOLD);
+ INIT_ATTR (RESET, -1, -1);
+ INIT_ATTR (DATE_CHANGE, -1, -1, TEXT_BOLD);
+ INIT_ATTR (READ_MARKER, COLOR_MAGENTA, -1);
+ INIT_ATTR (WARNING, COLOR_YELLOW, -1);
+ INIT_ATTR (ERROR, COLOR_RED, -1);
+
+ INIT_ATTR (EXTERNAL, COLOR_WHITE, -1);
+ INIT_ATTR (TIMESTAMP, COLOR_WHITE, -1);
+ INIT_ATTR (HIGHLIGHT, COLOR_BRIGHT (YELLOW), COLOR_MAGENTA, TEXT_BOLD);
+ INIT_ATTR (ACTION, COLOR_RED, -1);
+ INIT_ATTR (USERHOST, COLOR_CYAN, -1);
+ INIT_ATTR (JOIN, COLOR_GREEN, -1);
+ INIT_ATTR (PART, COLOR_RED, -1);
+
+#undef INIT_ATTR
+
+ // This prevents formatters from obtaining an attribute printer function
+ if (!have_ti)
+ {
+ g_terminal.stdout_is_tty = false;
+ g_terminal.stderr_is_tty = false;
+ }
+
+ g_log_message_real = log_message_attributed;
+}
+
// --- Helpers -----------------------------------------------------------------
static int
@@ -4413,7 +4473,7 @@ formatter_flush (struct formatter *self, FILE *stream, int flush_opts)
if (self->ctx->word_wrapping && !(flush_opts & FLUSH_OPT_NOWRAP))
line = line_wrap (line, g_terminal.columns);
- struct attr_printer state = ATTR_PRINTER_INIT (self->ctx, stream);
+ struct attr_printer state = ATTR_PRINTER_INIT (self->ctx->theme, stream);
struct line_char_attrs attrs = {}; // Won't compare equal to anything
LIST_FOR_EACH (struct line_char, c, line)
{
@@ -4425,10 +4485,10 @@ formatter_flush (struct formatter *self, FILE *stream, int flush_opts)
formatter_putc (NULL, stream);
attrs = c->attrs;
- if (attrs.named != -1)
- attr_printer_apply_named (&state, attrs.named);
- else
+ if (attrs.named == -1)
attr_printer_apply (&state, attrs.text, attrs.fg, attrs.bg);
+ else
+ attr_printer_apply_named (&state, attrs.named);
}
formatter_putc (c, stream);
@@ -6820,17 +6880,28 @@ on_refresh_prompt (struct app_context *ctx)
char *localized = iconv_xstrdup (ctx->term_from_utf8, prompt.str, -1, NULL);
str_free (&prompt);
+ // XXX: to be completely correct, we should use tputs, but we cannot
if (have_attributes)
{
- // XXX: to be completely correct, we should use tputs, but we cannot
- input_maybe_set_prompt (ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c%s",
- INPUT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
- INPUT_END_IGNORE,
- localized,
- INPUT_START_IGNORE, ctx->attrs[ATTR_RESET],
- INPUT_END_IGNORE,
- attributed_suffix));
+ char buf[16384] = "";
+ FILE *memfp = fmemopen (buf, sizeof buf - 1, "wb");
+ struct attr_printer state = { ctx->theme, memfp, false };
+
+ fputc (INPUT_START_IGNORE, memfp);
+ attr_printer_apply_named (&state, ATTR_PROMPT);
+ fputc (INPUT_END_IGNORE, memfp);
+
+ fputs (localized, memfp);
free (localized);
+
+ fputc (INPUT_START_IGNORE, memfp);
+ attr_printer_reset (&state);
+ fputc (INPUT_END_IGNORE, memfp);
+
+ fputs (attributed_suffix, memfp);
+
+ fclose (memfp);
+ input_maybe_set_prompt (ctx->input, xstrdup (buf));
}
else
input_maybe_set_prompt (ctx->input, localized);