/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -Wpedantic -Wno-format-truncation $@ -o $* \
- -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lncurses -ltinfo $+
- * Written by Emil.
- * Licensed under the GPLv3 only.
- *
- * TODO Add proper editing facilities
- */
+ -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lncurses -ltinfo $+
+ * Written by Emil.
+ * Licensed under the GPLv3 only.
+ */
#include <stdio.h>
#include <stdlib.h>
#define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
#define PORT "50000"
+#define NAME "anonymous"
+
+#define streq(a,b) (!strcmp(a,b))
+#define strneq(a,b,c) (!memcmp(a,b,c))
+
+int g_sockfd;
+
+#define g_y LINES
+#define g_x COLS
+
+#define HELP \
+ "%s [options ...]\n" \
+ "\n-serv SERVER Sets the server to connect to [default: " SERV "]" \
+ "\n-port PORT Sets the port [default: " PORT "]" \
+ "\n-name NAME Sets your display name [default: " NAME "]\n" \
+ "\nControls\n" \
+ "\nC-l Refreshes the screen" \
+ "\nC-w Delete the previous word" \
+ "\nC-c Close the client" \
+ "\nUp/Down Scrolls all the way up/Scrolls down by a line\n"
+
+
+/* I know, and I don't care */
#define TAB 9 /* */
#define BACKSPACE 263 /* ^G */
#define C_L 12 /* REFRESH */
#define C_U 21 /* CLR TO BOL */
#define C_W 23 /* DELETE PREVWORD */
-#define C_L 12 /* Signal SIGWINCH */
-#define C_R 18 /* hah not going to implement that */
-
-#define C_T 20 /* scroll up */
-#define C_N 14 /* scroll down */
-
-#define UP 258
-#define DOWN 259
-#define LEFT 260
-#define RIGHT 261
-
-#define TIMESTR "<%Y/%m/%d %H:%M:%S "
-#define TIMESTRMAX 21
-
-#define SENDMAX (1 << 8) - 1
-#define RECVMAX 17663 /* (1 << 11) */
-
-#define streq(a,b) (!strcmp(a,b))
-#define strneq(a,b,c) (!memcmp(a,b,c))
-
-#define g_y COLS
-#define g_x LINES
-
-int g_sockfd = -1;
-
-void fillline(int y, int xstart, char c) {
- int i = xstart, x = g_x;
- for (; i < x; ++i)
- { mvaddch(y,i,c); }
-}
+#define C_L 12 /* Signal full refresh */
-#define TITLE(str) do { mvprintw(0,0,str); fillline(0,strlen(str),'-'); } while (0)
+/****/
-void decoration(void) {
- /* mvprintw(g_y - 2, 0, "max screen %d; max ours %d", (g_y - 3) * SENDMAX, RECVMAX); */
- TITLE("-- MOONTALK ");
- fillline(g_y - 3, 0, '-');
-}
-
-#undef TITLE
-
-int g_hardrefresh = 0;
-
-void init_screen(int x) {
- (void)x;
- WINDOW * header, * body, * input;
- signal(SIGWINCH, SIG_IGN);
-
- endwin();
- struct winsize w;
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
- initscr();
- raw();
- noecho();
- keypad(stdscr, ERR);
- nodelay(stdscr, TRUE);
- ESCDELAY = 0;
- clear();
- decoration();
- g_hardrefresh = 1;
-
- signal(SIGWINCH, init_screen);
-}
-
-void stop(void) {
+void disconnect(void) {
int sockfd = g_sockfd;
- endwin();
- printf("stopping now.\n");
if (sockfd > -1) { close(sockfd); }
g_sockfd = -1;
}
struct addrinfo hints, * res;
memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC;
+ hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
__builtin_unreachable();
}
-#if 0
-int getanonval(char * buf, size_t len) {
- return 0;
-}
-#endif
+//
-int countlines(char * buf, size_t len) {
- size_t i = 0;
- for (buf += len - 1; *buf; --buf)
- { i += (*buf == '\n'); }
- return i;
+void fillline(WINDOW * w, int y, int xstart, char c) {
+ int i = xstart, x = g_x;
+ for (; i < x; ++i)
+ { mvwaddch(w,y,i,c); }
}
-size_t getoff(char * buf, size_t len, size_t count, char c) {
- size_t i;
- for (i = 0; count && i < len; ++i) {
- count -= (buf[i] == c) * 1;
- ++buf;
- }
- return i;
+void clearline(WINDOW * w, int y) {
+ wmove(w, y, 0);
+ wclrtoeol(w);
}
-size_t lastline(char * buf, size_t len) {
- size_t ret;
- char * start = buf;
- for (buf += len - 1; *buf; --buf) {
- ret = (*buf == '\n') ? buf - start : 0;
- if (ret) { return ret; }
+void sanitize(char * buf, size_t rem) {
+ char * base = buf;
+ buf += rem;
+ while (buf - base) {
+ if (*buf < ' ' || *buf > '~') {
+ if (*buf != '\n')
+ { *buf = '!'; }
+ }
+ --buf;
}
- return 0;
-}
-
-void clearline(int y) {
- int oldy, oldx;
- getyx(stdscr, oldy, oldx);
- move(y, 0);
- clrtoeol();
- move(oldy, oldx);
}
-#define UPDATE_TIME() do { \
- t = time(NULL); \
- tm = gmtime(&t); \
- strftime(sendbuf, TIMESTRMAX, TIMESTR, tm); \
- sendbuf[TIMESTRMAX - 1] = ' '; } while (0)
-
-
int main (int argc, char ** argv) {
- char * argv0 = argv[0];
- char * serv = SERV, * port = PORT, name[32] = "anonymous";
-
+ char * serv = SERV, * port = PORT, name[32] = NAME;
int sockfd;
+ {
+ char * argv0 = argv[0];
- while (++argv, --argc) {
- if (streq(*argv, "-help")) {
- printf("%s: HELP\n", argv0);
- return 1;
- }
- if (argc - 1)
- { --argc; ++argv; }
- else {
- printf("%s: %s requires argument\n", argv0, *argv);
- return 1;
- }
- if (streq(*(argv-1), "-serv")) {
- serv = *argv;
- }
- else if (streq(*(argv-1), "-port")) {
- port = *argv;
- }
- else if (streq(*(argv-1), "-name")) {
- memset(name, 0, 31);
- strncpy(name, *argv, 31);
+ while (++argv, --argc) {
+ if (streq(*argv, "-help")) {
+ printf(HELP, argv0);
+ exit(1);
+ }
+ if (argc - 1)
+ { --argc; ++argv; }
+ else {
+ printf("%s: %s requires argument\n", argv0, *argv);
+ return 1;
+ }
+ if (streq(*(argv-1), "-serv")) {
+ serv = *argv;
+ } else if (streq(*(argv-1), "-port")) {
+ port = *argv;
+ } else if (streq(*(argv-1), "-name")) {
+ memset(name, 0, 31);
+ strncpy(name, *argv, 31);
+ }
}
+
+ printf("Connecting to %s:%s as %s\n", serv, port, name);
+
+ atexit(disconnect);
+ sockfd = init_connection(serv, port);
}
- init_screen(-1);
- move(1,0);
- printw("Connecting to %s:%s as %s\n", serv, port, name);
- printw("g_y: %d; g_x: %d\n", g_y, g_x);
- refresh();
+ initscr();
+ noecho();
+ keypad(stdscr, ERR);
+ nodelay(stdscr, TRUE);
+ ESCDELAY = 0;
+ curs_set(0);
clear();
- atexit(stop);
- sockfd = init_connection(serv, port);
+ #define WINCOUNT 3
+ WINDOW * w[WINCOUNT];
+ #define header w[0]
+ #define body w[1]
+ #define input w[2]
+
+ #define SENDMAX (1 << 8) - 1
+ #define RECVMAX 17663 /* (1 << 11) */
char raw[SENDMAX + RECVMAX];
char * sendbuf = raw, * recvbuf = raw + SENDMAX, * off = recvbuf;
- size_t sendminlen;
- size_t sendlen = sendminlen, recvlen = 1, offlen = (g_y - 4) * 255;
+ size_t sendminlen, sendlen, recvlen = 0, offlen = recvlen;
+ size_t edit;
+
+ #define TIMESTR "<%Y/%m/%d %H:%M:%S "
+ #define TIMESTRMAX 21
+
+
+ #define UPDATE_TIME()\
+ do { \
+ t = time(NULL); \
+ tm = gmtime(&t); \
+ strftime(sendbuf, TIMESTRMAX, TIMESTR, tm); \
+ sendbuf[TIMESTRMAX - 1] = ' '; \
+ } while (0)
time_t t;
struct tm * tm;
+
useconds_t frame = 30;
- useconds_t interval = 1000000 / frame;
- int32_t ct = 0;
- /* int lc, off; */
- int hardrefresh = 0, inputrefresh = 1;
- int ch;
- int ret;
+ useconds_t interval = 1000000. / frame;
+ int32_t ct;
- sendminlen = TIMESTRMAX;
+ int ch, ret;
+ size_t i;
- memcpy(sendbuf + sendminlen, name, strlen(name));
- sendminlen += strlen(name);
- memcpy(sendbuf + sendminlen, "> ", 2);
- sendminlen += 2;
- sendlen = sendminlen;
+ size_t namelen = strlen(name);
- decoration();
- mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
+ edit = sendlen = sendminlen = TIMESTRMAX + namelen + 2;
- while (1) {
- nextloop:
- hardrefresh |= g_hardrefresh;
- /* update */
- if (ct % (frame) == 0 || inputrefresh) {
- UPDATE_TIME();
- mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
- }
- /* recv */
- if (ct % (frame * 2) == 0) {
- ret = recv(sockfd, recvbuf + recvlen - 1, RECVMAX - recvlen + 1, MSG_DONTWAIT);
- mvprintw(g_y - 2, 150, "ct:%d:%d:called", ct, ret);
- if (ret > -1) {
- recvlen += ret;
+ /* fill in the name */
+ memcpy(sendbuf + TIMESTRMAX, name, namelen);
+ memcpy(sendbuf + TIMESTRMAX + namelen, "> ", 2);
- mvprintw(g_y - 2, 50, "rendstat ct:%d:%d:%ld", ct, ret, recvlen);
- if (ret > 0 && recvbuf[recvlen-1] == '\n') {
- mvprintw(g_y - 2, 0, "newline at %ld", recvlen-1);
- }
- mvprintw(g_y - 2, 130, "ct:%d:rendered", ct);
- hardrefresh = 1;
- }
- }
- if (hardrefresh) {
- mvprintw(g_y - 2, 100, "ct:%d:REFRESH INVOKED", ct);
- mvaddnstr(1, 0, off, offlen);
- hardrefresh = 0;
- g_hardrefresh = 0;
- }
- /* send */
+ int inputrefresh, bodyrefresh;
+
+hardrefresh:
+ ct = 0;
+ inputrefresh = bodyrefresh = 1;
+
+ header = newwin(1, g_x, 0, 0);
+ body = newwin(g_y - 4, g_x, 1, 0);
+ input = newwin(3, g_x, g_y - 3, 0);
+
+ fillline(header, 0, 0, '-');
+ mvwprintw(header, 0, 2, " moontalk ");
+ fillline(input, 0, 0, '-');
+
+ while (1) {
+ /* input */
while ((ch = getch()) != -1) {
- if (ch == '\n') {
- if (sendlen == sendminlen) {
- continue;
- }
+ if (ch == KEY_RESIZE || ch == C_L) {
+ for (i = 0; i < WINCOUNT; ++i)
+ { delwin(w[i]); }
+ endwin();
+ erase();
+ refresh();
+ clear();
+ flushinp();
+ goto hardrefresh;
+ }
+ else if ((ch > 31 && ch < 127)) {
+ if (sendlen + 1 < SENDMAX)
+ { sendbuf[edit++] = ch; ++sendlen; }
+ /* mvwchgat(input, 2, sendlen - 1, 1, A_REVERSE, 0, NULL); */
+ mvwaddnstr(input, 2, 0, sendbuf, sendlen);
+ }
+ else if (ch == '\n') {
+ if (sendlen == sendminlen)
+ { continue; }
if (sendlen + 1 < SENDMAX)
{ sendbuf[sendlen++] = '\n'; }
if (send(sockfd, sendbuf, sendlen, 0) > 0) {
- memcpy(recvbuf + recvlen - 1, sendbuf, (sendlen + recvlen - 1 < RECVMAX) * sendlen);
+ memcpy(recvbuf + recvlen, sendbuf, (sendlen + recvlen < RECVMAX) * sendlen);
recvlen += sendlen;
- hardrefresh = 1;
+ offlen += sendlen;
} else {
- mvprintw(g_y - 2, 0, "message failed: %s", strerror(errno));
+ mvwprintw(input, 1, 0, "message failed: %s", strerror(errno));
}
- mvaddch(g_y - 1, sendminlen, ' ');
- inputrefresh = 1;
- clearline(g_y - 1);
- sendlen = sendminlen;
- }
- else if ((ch > 31 && ch < 127)) {
- if (sendlen + 1 < SENDMAX)
- { sendbuf[sendlen++] = ch; }
- mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
+ /* mvwaddch(0, sendminlen, ' '); */
+ /* mvwchgat(input, 2, 0, 1, A_STANDOUT, 0, NULL); */
+ bodyrefresh = inputrefresh = 1;
+ clearline(input, 2);
+ edit = sendlen = sendminlen;
}
else if (ch == BACKSPACE || ch == C_H) {
inputrefresh = 1;
- clearline(g_y - 1);
+ clearline(input, 2);
if (sendlen - 1 >= sendminlen)
- { mvaddch(g_y - 1, --sendlen, ' '); }
- mvaddnstr(g_y - 1, 0, sendbuf, sendlen);
- move(g_y - 1, sendlen);
- goto nextloop;
+ { mvwaddch(input, 2, --sendlen, ' '); --edit; }
+ mvwaddnstr(input, 2, 0, sendbuf, sendlen);
+ wmove(input, 2, sendlen);
}
- else if (ch == C_T) {
- while (*off != '\n') { --off; }
- if (*off == '\n') { ++off; }
- hardrefresh = 1;
+ else if (ch == KEY_LEFT) {
+ /* if (edit > sendminlen) { --edit; } */
+ }
+ else if (ch == KEY_RIGHT) {
+ /* if (edit - 1 < sendlen) { ++edit; } */
}
- else if (ch == C_N) {
- while (*off != '\n') { ++off; }
+ else if (ch == KEY_DOWN) {
+ mvwprintw(input, 1, 150, "scroll down %ld", offlen);
+ while (off - recvbuf < RECVMAX && *off != '\n') { ++off; }
if (*off == '\n') { ++off; }
- hardrefresh = 1;
+ wclear(body);
+ bodyrefresh = 1;
+ }
+ else if (ch == KEY_UP) {
+ mvwprintw(input, 1, 150, "scroll up %ld", offlen);
+ while (off - recvbuf > 0) { --off; }
+ /* wclear(body); */
+ bodyrefresh = 1;
}
else if (ch == C_W) {
while (sendlen > sendminlen && ispunct(sendbuf[sendlen - 1])) { --sendlen; }
while (sendlen > sendminlen && isspace(sendbuf[sendlen - 1])) { --sendlen; }
while (sendlen > sendminlen && isalnum(sendbuf[sendlen - 1])) { --sendlen; }
inputrefresh = 1;
- clearline(g_y - 1);
- goto nextloop;
+ clearline(input, 2);
}
- else if (ch == C_L) {
- clear();
- decoration();
- hardrefresh = 1;
- ct = 0;
- goto nextloop;
+
+ }
+ /* update and rendering */
+ if (ct % frame == 0 || inputrefresh || bodyrefresh) {
+ UPDATE_TIME();
+ /* wclear(input); */
+ mvwaddnstr(input, 2, 0, sendbuf, sendlen);
+
+ ret = recv(sockfd, recvbuf + recvlen, RECVMAX - recvlen, MSG_DONTWAIT);
+ if (errno != EAGAIN)
+ { mvwaddstr(input, 1, 0, strerror(errno)); }
+ if (bodyrefresh) {
+ bodyrefresh = 0;
+ if (!(ret > -1))
+ goto _bodyrefresh;
}
- else if (ch == C_D) {
- exit(0);
+ if (ret > -1) {
+ sanitize(recvbuf + recvlen, ret);
+ if (ret + recvlen < RECVMAX)
+ {
+ recvlen += ret;
+ offlen += ret;
+ }
+ _bodyrefresh:
+ mvwprintw(input, 1, 50, "render ct:%d ret:%d", ct, ret);
+ mvwaddnstr(body, 0,0, off, offlen);
}
}
- /* frame update */
- move(g_y - 1, sendlen);
- ++ct;
refresh();
+ for (i = 0; i < WINCOUNT; ++i)
+ { wnoutrefresh(w[i]); }
+ doupdate();
usleep(interval);
+ ++ct;
}
- return 0;
+
+ endwin();
}