summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--chat.c8
-rw-r--r--chat.h22
-rw-r--r--handle.c21
-rw-r--r--irc.c6
-rw-r--r--ui.c174
6 files changed, 228 insertions, 6 deletions
diff --git a/Makefile b/Makefile
index 999b491..4af59ee 100644
--- a/Makefile
+++ b/Makefile
@@ -3,12 +3,13 @@ CFLAGS += -I${LIBRESSL_PREFIX}/include
LDFLAGS += -L${LIBRESSL_PREFIX}/lib
CFLAGS += -std=c11 -Wall -Wextra -Wpedantic
-LDLIBS = -lcrypto -ltls
+LDLIBS = -lcurses -lcrypto -ltls
OBJS += chat.o
OBJS += handle.o
OBJS += irc.o
OBJS += term.o
+OBJS += ui.o
catgirl: ${OBJS}
${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@
diff --git a/chat.c b/chat.c
index 462faa0..47227d5 100644
--- a/chat.c
+++ b/chat.c
@@ -15,6 +15,7 @@
*/
#include <err.h>
+#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -33,6 +34,8 @@ size_t idNext = Network + 1;
struct Self self;
int main(int argc, char *argv[]) {
+ setlocale(LC_CTYPE, "");
+
bool insecure = false;
const char *host = NULL;
const char *port = "6697";
@@ -71,6 +74,10 @@ int main(int argc, char *argv[]) {
if (!real) real = nick;
ircConfig(insecure, cert, priv);
+
+ uiInit();
+ uiFormat(Network, Cold, NULL, "Traveling...");
+ uiDraw();
int irc = ircConnect(host, port);
if (pass) ircFormat("PASS :%s\r\n", pass);
@@ -80,6 +87,7 @@ int main(int argc, char *argv[]) {
ircFormat("USER %s 0 * :%s\r\n", user, real);
for (;;) {
+ uiDraw();
ircRecv();
}
}
diff --git a/chat.h b/chat.h
index 93014ef..be3952c 100644
--- a/chat.h
+++ b/chat.h
@@ -82,6 +82,18 @@ struct Message {
char *params[ParamCap];
};
+#define B "\2"
+#define C "\3"
+#define R "\17"
+#define V "\26"
+#define I "\35"
+#define U "\37"
+enum Color {
+ White, Black, Blue, Green, Red, Brown, Magenta, Orange,
+ Yellow, LightGreen, Cyan, LightCyan, LightBlue, Pink, Gray, LightGray,
+ Default = 99,
+};
+
void ircConfig(bool insecure, const char *cert, const char *priv);
int ircConnect(const char *host, const char *port);
void ircRecv(void);
@@ -91,6 +103,14 @@ void ircFormat(const char *format, ...)
void handle(struct Message msg);
+enum Heat { Cold, Warm, Hot };
+void uiInit(void);
+void uiDraw(void);
+void uiWrite(size_t id, enum Heat heat, const struct tm *time, const char *str);
+void uiFormat(
+ size_t id, enum Heat heat, const struct tm *time, const char *format, ...
+) __attribute__((format(printf, 4, 5)));
+
enum TermMode {
TermFocus,
TermPaste,
@@ -109,11 +129,9 @@ void termMode(enum TermMode mode, bool set);
enum TermEvent termEvent(char ch);
#define BASE64_SIZE(len) (1 + ((len) + 2) / 3 * 4)
-
static const char Base64[64] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
};
-
static inline void base64(char *dst, const byte *src, size_t len) {
size_t i = 0;
while (len > 2) {
diff --git a/handle.c b/handle.c
index 96bd2a2..b2b2b8d 100644
--- a/handle.c
+++ b/handle.c
@@ -136,11 +136,32 @@ static void handleReplyWelcome(struct Message *msg) {
if (self.join) ircFormat("JOIN :%s\r\n", self.join);
}
+static void handleReplyISupport(struct Message *msg) {
+ // TODO: Extract CHANTYPES and PREFIX for future use.
+ for (size_t i = 1; i < ParamCap; ++i) {
+ if (!msg->params[i]) break;
+ char *key = strsep(&msg->params[i], "=");
+ if (!msg->params[i]) continue;
+ if (!strcmp(key, "NETWORK")) {
+ uiFormat(Network, Cold, NULL, "You arrive in %s", msg->params[i]);
+ }
+ }
+}
+
+static void handleReplyMOTD(struct Message *msg) {
+ require(msg, false, 2);
+ char *line = msg->params[1];
+ if (!strncmp(line, "- ", 2)) line += 2;
+ uiFormat(Network, Cold, NULL, "%s", line);
+}
+
static const struct Handler {
const char *cmd;
Handler *fn;
} Handlers[] = {
{ "001", handleReplyWelcome },
+ { "005", handleReplyISupport },
+ { "372", handleReplyMOTD },
{ "900", handleReplyLoggedIn },
{ "904", handleErrorSASLFail },
{ "905", handleErrorSASLFail },
diff --git a/irc.c b/irc.c
index cf8aab7..f07fcc8 100644
--- a/irc.c
+++ b/irc.c
@@ -104,10 +104,10 @@ int ircConnect(const char *host, const char *port) {
static void debug(char dir, const char *line) {
if (!self.debug) return;
size_t len = strcspn(line, "\r\n");
- /*uiFormat(
- Debug, Cold, NULL, "\3%02d%c%c\3 %.*s",
+ uiFormat(
+ Debug, Cold, NULL, C"%d%c%c"C" %.*s",
Gray, dir, dir, (int)len, line
- );*/
+ );
if (!isatty(STDERR_FILENO)) {
fprintf(stderr, "%c%c %.*s\n", dir, dir, (int)len, line);
}
diff --git a/ui.c b/ui.c
new file mode 100644
index 0000000..0295c8d
--- /dev/null
+++ b/ui.c
@@ -0,0 +1,174 @@
+/* Copyright (C) 2020 C. McEnroe <june@causal.agency>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <curses.h>
+#include <err.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <time.h>
+
+#include "chat.h"
+
+#ifndef A_ITALIC
+#define A_ITALIC A_UNDERLINE
+#endif
+
+#define BOTTOM (LINES - 1)
+#define RIGHT (COLS - 1)
+#define WINDOW_LINES (LINES - 2)
+
+static short colorPairs;
+
+static void colorInit(void) {
+ start_color();
+ use_default_colors();
+ for (short pair = 0; pair < 16; ++pair) {
+ init_pair(1 + pair, pair % COLORS, -1);
+ }
+ colorPairs = 17;
+}
+
+static attr_t colorAttr(short fg) {
+ return (fg >= COLORS ? A_BOLD : A_NORMAL);
+}
+
+static short colorPair(short fg, short bg) {
+ if (bg == -1) return 1 + fg;
+ for (short pair = 17; pair < colorPairs; ++pair) {
+ short f, b;
+ pair_content(pair, &f, &b);
+ if (f == fg && b == bg) return pair;
+ }
+ init_pair(colorPairs, fg, bg);
+ return colorPairs++;
+}
+
+enum {
+ InputCols = 512,
+ PadLines = 512,
+};
+
+static WINDOW *status;
+static WINDOW *input;
+
+struct Window {
+ size_t id;
+ WINDOW *pad;
+ enum Heat heat;
+ int unread;
+ int scroll;
+ bool mark;
+ struct Window *prev;
+ struct Window *next;
+};
+
+static struct {
+ struct Window *active;
+ struct Window *other;
+ struct Window *head;
+ struct Window *tail;
+} windows;
+
+static void windowAdd(struct Window *window) {
+ if (windows.tail) windows.tail->next = window;
+ window->prev = windows.tail;
+ window->next = NULL;
+ windows.tail = window;
+ if (!windows.head) windows.head = window;
+}
+
+static void windowRemove(struct Window *window) {
+ if (window->prev) window->prev->next = window->next;
+ if (window->next) window->next->prev = window->prev;
+ if (windows.head == window) windows.head = window->next;
+ if (windows.tail == window) windows.tail = window->prev;
+}
+
+static struct Window *windowFor(size_t id) {
+ struct Window *window;
+ for (window = windows.head; window; window = window->next) {
+ if (window->id == id) return window;
+ }
+ window = malloc(sizeof(*window));
+ if (!window) err(EX_OSERR, "malloc");
+ window->id = id;
+ window->pad = newpad(PadLines, COLS);
+ wsetscrreg(window->pad, 0, PadLines - 1);
+ scrollok(window->pad, true);
+ wmove(window->pad, PadLines - 1, 0);
+ window->heat = Cold;
+ window->unread = 0;
+ window->scroll = PadLines;
+ window->mark = true;
+ windowAdd(window);
+ return window;
+}
+
+void uiInit(void) {
+ initscr();
+ cbreak();
+ noecho();
+ termInit();
+ termNoFlow();
+ def_prog_mode();
+ colorInit();
+ status = newwin(1, COLS, 0, 0);
+ input = newpad(1, InputCols);
+ keypad(input, true);
+ nodelay(input, true);
+ windows.active = windowFor(Network);
+}
+
+void uiDraw(void) {
+ wnoutrefresh(status);
+ pnoutrefresh(
+ windows.active->pad,
+ windows.active->scroll - WINDOW_LINES, 0,
+ 1, 0,
+ BOTTOM - 1, RIGHT
+ );
+ // TODO: Input scrolling.
+ pnoutrefresh(
+ input,
+ 0, 0,
+ BOTTOM, 0,
+ BOTTOM, RIGHT
+ );
+ doupdate();
+}
+
+void uiWrite(size_t id, enum Heat heat, const struct tm *time, const char *str) {
+ (void)time;
+ struct Window *window = windowFor(id);
+ waddch(window->pad, '\n');
+ waddstr(window->pad, str);
+}
+
+void uiFormat(
+ size_t id, enum Heat heat, const struct tm *time, const char *format, ...
+) {
+ char buf[1024];
+ va_list ap;
+ va_start(ap, format);
+ int len = vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+ assert((size_t)len < sizeof(buf));
+ uiWrite(id, heat, time, buf);
+}