IRC colour support, /colors toggle, persistent config, ESC timeout fix, Ctrl-L redraw
- mIRC colour/bold/underline/italic codes converted to ANSI (or stripped) - /colors toggles display vs strip, saved to ~/.hircrc - /trans state saved to ~/.hircrc on toggle - ESC key stays pending indefinitely (no 50ms timeout) - Ctrl-L redraws screen - /mode * expands to current channel - PM tab: incoming PMs prioritized, sent targets don't reorder list
This commit is contained in:
@@ -79,6 +79,7 @@ static struct {
|
||||
static char query_target[64] = "";
|
||||
static int translate_public = 0; /* /trans toggle: echo translations to channel */
|
||||
static int translate_enabled = 1; /* master toggle for translation */
|
||||
static int irc_colors = 1; /* display IRC colour codes as ANSI */
|
||||
|
||||
/* Track nicks who sent us private messages */
|
||||
#define MAX_PM_NICKS 32
|
||||
@@ -146,6 +147,8 @@ static void ai_config_load(void)
|
||||
fprintf(f, "ai_model=\n");
|
||||
fprintf(f, "ai_target_lang=\n");
|
||||
fprintf(f, "ai_skip_langs=\n");
|
||||
fprintf(f, "translate=on\n");
|
||||
fprintf(f, "irc_colors=1\n");
|
||||
fclose(f);
|
||||
}
|
||||
return;
|
||||
@@ -178,6 +181,13 @@ static void ai_config_load(void)
|
||||
snprintf(ai_cfg.target_lang, sizeof(ai_cfg.target_lang), "%s", eq);
|
||||
else if (strcmp(line, "ai_skip_langs") == 0)
|
||||
snprintf(ai_cfg.skip_langs, sizeof(ai_cfg.skip_langs), "%s", eq);
|
||||
else if (strcmp(line, "translate") == 0) {
|
||||
if (strcmp(eq, "off") == 0) { translate_enabled = 0; translate_public = 0; }
|
||||
else if (strcmp(eq, "public") == 0) { translate_enabled = 1; translate_public = 1; }
|
||||
else { translate_enabled = 1; translate_public = 0; }
|
||||
}
|
||||
else if (strcmp(line, "irc_colors") == 0)
|
||||
irc_colors = atoi(eq);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
@@ -186,6 +196,28 @@ static void ai_config_load(void)
|
||||
ai_cfg.enabled = 1;
|
||||
}
|
||||
|
||||
static void config_save(void)
|
||||
{
|
||||
char path[512];
|
||||
snprintf(path, sizeof(path), "%s/.hircrc", getenv("HOME"));
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f) return;
|
||||
fprintf(f, "# AI translation settings\n");
|
||||
fprintf(f, "ai_type=%s\n", ai_cfg.type);
|
||||
if (ai_cfg.port)
|
||||
fprintf(f, "ai_host=%s:%d\n", ai_cfg.host, ai_cfg.port);
|
||||
else
|
||||
fprintf(f, "ai_host=%s\n", ai_cfg.host);
|
||||
fprintf(f, "ai_key=%s\n", ai_cfg.key);
|
||||
fprintf(f, "ai_model=%s\n", ai_cfg.model);
|
||||
fprintf(f, "ai_target_lang=%s\n", ai_cfg.target_lang);
|
||||
fprintf(f, "ai_skip_langs=%s\n", ai_cfg.skip_langs);
|
||||
fprintf(f, "translate=%s\n",
|
||||
!translate_enabled ? "off" : translate_public ? "public" : "on");
|
||||
fprintf(f, "irc_colors=%d\n", irc_colors);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static int needs_translation(const char *text)
|
||||
{
|
||||
if (!ai_cfg.enabled || !translate_enabled) return 0;
|
||||
@@ -438,9 +470,101 @@ static void draw_statusbar(void)
|
||||
}
|
||||
|
||||
/* Store a line in a window's scrollback */
|
||||
/* mIRC colour code to ANSI */
|
||||
static const char *mirc_to_ansi[] = {
|
||||
"\033[97m", /* 0: white */
|
||||
"\033[30m", /* 1: black */
|
||||
"\033[34m", /* 2: blue */
|
||||
"\033[32m", /* 3: green */
|
||||
"\033[31m", /* 4: red */
|
||||
"\033[33m", /* 5: brown */
|
||||
"\033[35m", /* 6: purple */
|
||||
"\033[33m", /* 7: orange */
|
||||
"\033[93m", /* 8: yellow */
|
||||
"\033[92m", /* 9: light green */
|
||||
"\033[36m", /* 10: cyan */
|
||||
"\033[96m", /* 11: light cyan */
|
||||
"\033[94m", /* 12: light blue */
|
||||
"\033[95m", /* 13: pink */
|
||||
"\033[90m", /* 14: grey */
|
||||
"\033[37m", /* 15: light grey */
|
||||
};
|
||||
|
||||
static size_t irc_color_process(char *out, size_t outsize, const char *in)
|
||||
{
|
||||
size_t o = 0;
|
||||
for (const char *p = in; *p && o < outsize - 10; p++) {
|
||||
if (*p == '\x03') {
|
||||
/* mIRC colour: ^C[fg[,bg]] */
|
||||
p++;
|
||||
if (irc_colors && *p >= '0' && *p <= '9') {
|
||||
int fg = *p - '0';
|
||||
p++;
|
||||
if (*p >= '0' && *p <= '9') { fg = fg * 10 + (*p - '0'); p++; }
|
||||
if (*p == ',') {
|
||||
p++;
|
||||
if (*p >= '0' && *p <= '9') p++;
|
||||
if (*p >= '0' && *p <= '9') p++;
|
||||
}
|
||||
if (fg < 16) {
|
||||
const char *a = mirc_to_ansi[fg];
|
||||
size_t alen = strlen(a);
|
||||
if (o + alen < outsize) { memcpy(out + o, a, alen); o += alen; }
|
||||
}
|
||||
} else if (!irc_colors) {
|
||||
/* Strip: skip digits and comma */
|
||||
if (*p >= '0' && *p <= '9') p++;
|
||||
if (*p >= '0' && *p <= '9') p++;
|
||||
if (*p == ',') {
|
||||
p++;
|
||||
if (*p >= '0' && *p <= '9') p++;
|
||||
if (*p >= '0' && *p <= '9') p++;
|
||||
}
|
||||
}
|
||||
p--; /* loop will p++ */
|
||||
} else if (*p == '\x02') {
|
||||
/* Bold */
|
||||
if (irc_colors) {
|
||||
const char *a = "\033[1m";
|
||||
memcpy(out + o, a, 4); o += 4;
|
||||
}
|
||||
} else if (*p == '\x1F') {
|
||||
/* Underline */
|
||||
if (irc_colors) {
|
||||
const char *a = "\033[4m";
|
||||
memcpy(out + o, a, 4); o += 4;
|
||||
}
|
||||
} else if (*p == '\x0F') {
|
||||
/* Reset */
|
||||
if (irc_colors) {
|
||||
const char *a = "\033[0m";
|
||||
memcpy(out + o, a, 4); o += 4;
|
||||
}
|
||||
} else if (*p == '\x1D') {
|
||||
/* Italic */
|
||||
if (irc_colors) {
|
||||
const char *a = "\033[3m";
|
||||
memcpy(out + o, a, 4); o += 4;
|
||||
}
|
||||
} else {
|
||||
out[o++] = *p;
|
||||
}
|
||||
}
|
||||
/* Reset at end if colours were used */
|
||||
if (irc_colors && o > 0) {
|
||||
const char *a = "\033[0m";
|
||||
size_t alen = strlen(a);
|
||||
if (o + alen < outsize) { memcpy(out + o, a, alen); o += alen; }
|
||||
}
|
||||
out[o] = '\0';
|
||||
return o;
|
||||
}
|
||||
|
||||
static void buf_store(int level, const char *line){
|
||||
if (level < 0 || level >= WL_MAX) return;
|
||||
snprintf(win_buf[level].lines[win_buf[level].head], 512, "%s", line);
|
||||
char processed[512];
|
||||
irc_color_process(processed, sizeof(processed), line);
|
||||
snprintf(win_buf[level].lines[win_buf[level].head], 512, "%s", processed);
|
||||
win_buf[level].head = (win_buf[level].head + 1) % SCROLLBACK;
|
||||
if (win_buf[level].count < SCROLLBACK)
|
||||
win_buf[level].count++;
|
||||
@@ -1213,6 +1337,12 @@ static void handle_input(char *line)
|
||||
(unsigned char *)text,
|
||||
strlen(text));
|
||||
wprintf(WL_MSG, "-> %s: %s\n", target, text);
|
||||
/* Ensure target is in PM list (but don't reorder) */
|
||||
int found = 0;
|
||||
for (int i = 0; i < pm_nick_count; i++)
|
||||
if (strcasecmp(pm_nicks[i], target) == 0) { found = 1; break; }
|
||||
if (!found && pm_nick_count < MAX_PM_NICKS)
|
||||
snprintf(pm_nicks[pm_nick_count++], NICK_LEN, "%s", target);
|
||||
}
|
||||
} else if (strcasecmp(cmd, "q") == 0) {
|
||||
if (args && args[0]) {
|
||||
@@ -1263,6 +1393,12 @@ static void handle_input(char *line)
|
||||
translate_public = 0;
|
||||
wprintf(current_level, "* Translation OFF\n");
|
||||
}
|
||||
config_save();
|
||||
} else if (strcasecmp(cmd, "colors") == 0) {
|
||||
irc_colors = !irc_colors;
|
||||
wprintf(current_level, "* IRC colours %s\n",
|
||||
irc_colors ? "ON" : "OFF (stripped)");
|
||||
config_save();
|
||||
} else if (strcasecmp(cmd, "ctcp") == 0 && args) {
|
||||
char *target = args;
|
||||
char *ctcp_cmd = strchr(args, ' ');
|
||||
@@ -1277,7 +1413,17 @@ static void handle_input(char *line)
|
||||
wprintf(current_level, "Usage: /ctcp <nick> <command>\n");
|
||||
}
|
||||
} else if (strcasecmp(cmd, "mode") == 0 && args) {
|
||||
/* Replace * with current channel */
|
||||
if (args[0] == '*') {
|
||||
const char *chan = current_channel();
|
||||
if (chan[0]) {
|
||||
char modebuf[IRC_MAX];
|
||||
snprintf(modebuf, sizeof(modebuf), "%s%s", chan, args + 1);
|
||||
irc_send_raw("MODE %s", modebuf);
|
||||
}
|
||||
} else {
|
||||
irc_send_raw("MODE %s", args);
|
||||
}
|
||||
} else if (strcasecmp(cmd, "whois") == 0 && args) {
|
||||
if (strchr(args, ' '))
|
||||
irc_send_raw("WHOIS %s", args);
|
||||
@@ -1528,7 +1674,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = esc_pending ? 50000 : 500000;
|
||||
tv.tv_usec = 500000;
|
||||
|
||||
int ret = select(maxfd + 1, &fds, NULL, NULL, &tv);
|
||||
if (ret < 0) {
|
||||
@@ -1647,10 +1793,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0 && esc_pending) {
|
||||
esc_pending = 0;
|
||||
}
|
||||
|
||||
if (got_sigint) {
|
||||
got_sigint = 0;
|
||||
printf("\033[%d;1H\033[KWanna quit? [y/N] ",
|
||||
@@ -1834,6 +1976,13 @@ int main(int argc, char *argv[])
|
||||
input_pos += yank_len;
|
||||
redraw_input(input_line, input_len, input_pos);
|
||||
}
|
||||
} else if (ch == 0x0C) {
|
||||
/* Ctrl-L: redraw screen */
|
||||
get_term_size();
|
||||
printf("\033[2J\033[H");
|
||||
printf("\033[1;%dr", term_rows - 2);
|
||||
redraw_window();
|
||||
redraw_input(input_line, input_len, input_pos);
|
||||
} else if (ch == 0x0B) {
|
||||
/* Ctrl-K: kill to end */
|
||||
yank_len = input_len - input_pos;
|
||||
|
||||
Reference in New Issue
Block a user