AmendHub

Download:

jcs

/

subtext

/

amendments

/

2

console+session: Work on functioning console, integrate ANSI parser


jcs made amendment 2 over 2 years ago
--- console.c Wed Nov 17 15:31:31 2021 +++ console.c Sun Nov 28 21:12:14 2021 @@ -21,13 +21,18 @@ #include "session.h" #include "util.h" +/* for monaco 9 */ +#define FONT_WIDTH 6 +#define FONT_HEIGHT 10 + struct node_funcs console_node_funcs = { console_output, - console_read, + console_input, }; void console_redraw(struct console *console); void console_cursor(struct console *console, short line, short column); +void console_parse_csi(struct console *console); struct console * console_init(void) @@ -69,7 +74,7 @@ console_init(void) console->session->cookie = (void *)console; console->ncolumns = 80; console->nlines = 24; - console->old_cursor_line = -1; + console->attrs[0] |= (ATTR_CURSOR | ATTR_DIRTY); return console; } @@ -77,6 +82,7 @@ console_init(void) void console_idle(struct console *console) { + console_output(console->session); } void @@ -92,7 +98,6 @@ console_resume(struct console *console) void console_update(struct console *console, EventRecord *event) { - Str255 buf; Rect r; short what = -1; @@ -117,14 +122,33 @@ console_mouse_down(struct console *console, EventRecor { } +void +console_key_down(struct console *console, EventRecord *event) +{ + char k; + + if (console->session->ibuflen >= sizeof(console->session->ibuf)) + return; + + k = (event->message & charCodeMask); + console->session->ibuf[console->session->ibuflen++] = k; + if (k == '\r') + console->session->ibuf[console->session->ibuflen++] = '\n'; +} + short console_output(struct session *session) { - struct console *console = (struct console *)(session->cookie); - short n, len, cursor; + struct console *console = (struct console *)session->cookie; + short n, len, cursor, iac = 0; if (session->obuflen == 0) return 0; + + cursor = (console->cursor_line * console->ncolumns) + + console->cursor_column; + console->attrs[cursor] |= ATTR_DIRTY; + console->attrs[cursor] &= ~ATTR_CURSOR; for (n = 0; n < session->obuflen; n++) { if (console->cursor_column >= console->ncolumns && @@ -133,6 +157,22 @@ console_output(struct session *session) console->cursor_line++; } + if (console->in_csi) { + if (session->obuf[n] == '\e' || + console->csilen >= sizeof(console->csi) - 1) { + console_parse_csi(console); + console->in_csi = 0; + console->csi[0] = '\0'; + console->csilen = 0; + } else { + console->csi[console->csilen] = session->obuf[n]; + console->csilen++; + console_parse_csi(console); + } + + continue; + } + switch (session->obuf[n]) { case '\r': console->cursor_column = 0; @@ -140,6 +180,19 @@ console_output(struct session *session) case '\n': console->cursor_line++; break; + case '\e': + if (session->obuflen <= n + 1) { + /* lone \e at end of buffer, keep it until we see next */ + session->obuflen = 1; + session->obuf[0] = '\e'; + goto output_done; + } + if (session->obuf[n + 1] == '[') { + console->in_csi = 1; + n++; + continue; + } + /* escape but not CSI, fall through */ default: cursor = (console->cursor_line * console->ncolumns) + console->cursor_column; @@ -150,57 +203,444 @@ console_output(struct session *session) } } + len = session->obuflen; session->obuflen = 0; +output_done: + cursor = (console->cursor_line * console->ncolumns) + + console->cursor_column; + console->attrs[cursor] |= (ATTR_DIRTY | ATTR_CURSOR); console_redraw(console); return len; } +short +console_input(struct session *session) +{ + /* nothing to do, input is fed from main loop */ + uthread_yield(); + return 0; +} + void console_redraw(struct console *console) { + Rect cursor; short n, line = 0, column = 0; TextFont(monaco); TextSize(9); - if (console->old_cursor_line != -1) - console_cursor(console, console->old_cursor_line, - console->old_cursor_column); - for (n = 0; n < sizeof(console->chars); n++) { if (!(console->attrs[n] & ATTR_DIRTY)) continue; line = ((n + 1) / console->ncolumns); column = n - (line * console->ncolumns); - MoveTo(6 + ((console->win->portRect.left + column) * 6), - 6 + ((console->win->portRect.top + line + 1) * 10)); + cursor.left = FONT_WIDTH + ((console->win->portRect.left + + column) * FONT_WIDTH); + cursor.top = FONT_WIDTH + ((console->win->portRect.top + line) * + FONT_HEIGHT); + cursor.right = cursor.left + FONT_WIDTH; + cursor.bottom = cursor.top + FONT_HEIGHT; + + FillRect(&cursor, white); + + MoveTo(cursor.left, cursor.top + FONT_HEIGHT - 2); DrawChar(console->chars[n]); console->attrs[n] &= ~ATTR_DIRTY; + + if (console->attrs[n] & ATTR_CURSOR) + InvertRect(&cursor); } - - console_cursor(console, console->cursor_line, console->cursor_column); - console->old_cursor_line = console->cursor_line; - console->old_cursor_column = console->cursor_column; } +#if 0 void console_cursor(struct console *console, short line, short column) { Rect cursor; cursor.left = 6 + ((console->win->portRect.left + column) * 6); - cursor.top = 6 + ((console->win->portRect.top + line + 1) * 10); + cursor.top = 6 + ((console->win->portRect.top + line) * 10) + 2; cursor.right = cursor.left + 6; cursor.bottom = cursor.top + 10; InvertRect(&cursor); } +#endif -short -console_read(struct session *session) +void +console_parse_csi(struct console *console) { - return 0; + short x, y, cursor; + short serviced = 0; + short param1 = -1, param2 = -1; + short parambuflen; + char parambuf[4]; + char c; + + if (console->csilen == 0) + return; + + c = console->csi[console->csilen - 1]; + + if (c < 'A' || (c > 'Z' && c < 'a') || c > 'z') + return; + + switch (c) { + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'J': + case 'K': + case 'S': + case 'T': + case 'd': + case 'g': + case 's': + case 'u': + case '7': + case '8': + /* optional multiplier */ + if (c == 'J' || c == 'K') + param1 = 0; + else + param1 = 1; + + if (console->csilen > 1) { + for (x = 0; x < console->csilen - 1, x < sizeof(parambuf); x++) { + parambuf[x] = console->csi[x]; + parambuf[x + 1] = '\0'; + } + param1 = atoi(parambuf); + } + break; + case 'H': + case 'f': + /* two optional parameters separated by ; each defaulting to 1 */ + param1 = 1; + param2 = 1; + + y = -1; + for (x = 0; x < console->csilen; x++) { + if (console->csi[x] == ';') { + y = x; + break; + } + } + if (y == -1) + /* CSI 17H -> CSI 17; */ + y = console->csilen - 1; + + if (y > 0) { + for (x = 0; x < y && x < 4; x++) { + parambuf[x] = console->csi[x]; + parambuf[x + 1] = '\0'; + } + param1 = atoi(parambuf); + + if (y < console->csilen - 2) { + parambuf[0] = '\0'; + for (x = 0; x < (console->csilen - 1 - y) && x < 4; x++) { + parambuf[x] = console->csi[y + 1 + x]; + parambuf[x + 1] = '\0'; + } + param2 = atoi(parambuf); + } + } + break; + } + + serviced = 1; + + /* uncursor */ + cursor = (console->cursor_line * console->ncolumns) + + console->cursor_column; + console->attrs[cursor] &= ~ATTR_CURSOR; + console->attrs[cursor] |= ATTR_DIRTY; + + switch (c) { + case 'A': /* CUU - cursor up, stop at top of screen */ + for (x = 0; x < param1 && console->cursor_line > 0; x++) + console->cursor_line--; + break; + case 'B': /* CUD - cursor down, stop at bottom of screen */ + for (x = 0; x < param1 && + console->cursor_line < console->nlines - 1; x++) + console->cursor_line++; + break; + case 'C': /* CUF - cursor forward, stop at screen edge */ + for (x = 0; x < param1 && + console->cursor_column < console->ncolumns - 1; x++) + console->cursor_column++; + break; + case 'D': /* CUB - cursor back, stop at left edge of screen */ + for (x = 0; x < param1 && console->cursor_column > 0; x++) + console->cursor_column--; + break; + case 'E': /* CNL - cursor next line */ + console->cursor_column = 0; + for (x = 0; x < param1 && + console->cursor_line < console->nlines - 1; x++) + console->cursor_line++; + break; + case 'F': /* CPL - cursor previous line */ + console->cursor_column = 0; + for (x = 0; x < param1 && console->cursor_line > 0; x++) + console->cursor_line--; + break; + case 'G': /* CHA - cursor horizontal absolute */ + if (param1 > console->ncolumns) + param1 = console->ncolumns; + console->cursor_column = param1; + break; + case 'H': /* CUP - cursor absolute position */ + case 'f': /* HVP - horizontal vertical position */ + if (param1 - 1 < 0) + console->cursor_line = 0; + else if (param1 > console->nlines) + console->cursor_line = console->nlines - 1; + else + console->cursor_line = param1 - 1; + + if (param2 - 1 < 0) + console->cursor_column = 0; + else if (param2 > console->ncolumns) + console->cursor_column = console->ncolumns - 1; + else + console->cursor_column = param2 - 1; + break; + case 'J': /* ED - erase in display */ + switch (param1) { + case 0: + /* clear from cursor to end of screen */ +#if 0 + for (y = console->cursor_line; y < console->nlines; y++) { + for (x = 0; x < console->ncolumns; x++) { + if (y == console->cursor_line && + x < console->cursor_column) + continue; + + update_char(x, y, ' ', cur_attr, cur_color); + } + } +#endif + break; + case 1: + /* clear from cursor to beginning of the screen */ +#if 0 + for (y = console->cursor_line; y >= 0; y--) { + for (x = console->ncolumns; x >= 0; x--) { + if (y == console->cursor_line && + x > console->cursor_column) + continue; + + update_char(x, y, ' ', cur_attr, cur_color); + } + } +#endif + break; + case 2: + /* clear entire screen */ +#if 0 + for (y = 0; y < console->nlines; y++) { + for (x = 0; x < console->ncolumns; x++) + update_char(x, y, ' ', cur_attr, cur_color); + } +#endif + break; + } + break; + case 'K': /* EL - erase in line */ + switch (param1) { + case 0: + /* clear from cursor to end of line */ + if (console->cursor_column >= console->ncolumns) + break; +#if 0 + for (x = console->cursor_column; x < console->ncolumns; x++) + update_char(x, console->cursor_line, ' ', cur_attr, + cur_color); +#endif + break; + case 1: + /* clear from cursor to beginning of line */ + if (console->cursor_column == 0) + break; +#if 0 + for (x = console->cursor_column; x >= 0; x--) + update_char(x, console->cursor_line, ' ', cur_attr, + cur_color); +#endif + break; + case 2: + /* clear entire line */ +#if 0 + for (x = 0; x < console->ncolumns - 1; x++) + update_char(x, console->cursor_line, ' ', cur_attr, + cur_color); +#endif + break; + } + break; + case 'S': /* SU - scroll up */ + /* TODO */ + break; + case 'T': /* SD - scroll down */ + /* TODO */ + break; + case 'd': /* absolute line number */ + if (param1 < 1) + console->cursor_line = 0; + else if (param1 > console->nlines) + console->cursor_line = console->nlines; + else + console->cursor_line = param1 - 1; + break; + case 'g': /* clear tabs, ignore */ + break; + case 'h': /* reset, ignore */ + break; + case 'm': /* graphic changes */ + parambuf[0] = '\0'; + parambuflen = 0; + param2 = console->cur_attr; + + for (x = 0; x < console->csilen; x++) { + /* all the way to console->csilen to catch 'm' */ + if (console->csi[x] == ';' || console->csi[x] == 'm') { + param1 = atoi(parambuf); + + switch (param1) { + case 0: /* reset */ +#ifdef COLOR + cur_color = FG_WHITE | BG_BLACK; +#endif + param2 = 0; + break; + case 1: /* bold */ + param2 |= ATTR_BOLD; + break; + case 4: /* underline */ + param2 |= ATTR_UNDERLINE; + break; + case 7: /* reverse */ + param2 |= ATTR_REVERSE; + break; + case 22: /* normal color */ + param2 = 0; + break; + case 21: /* bold off */ + param2 &= ~ATTR_BOLD; + break; + case 24: /* underline off */ + param2 &= ~ATTR_UNDERLINE; + break; + case 27: /* inverse off */ + param2 &= ~ATTR_REVERSE; + break; + case 30: /* fg black */ + case 31: /* fg red */ + case 32: /* fg green */ + case 33: /* fg yellow */ + case 34: /* fg blue */ + case 35: /* fg magenta */ + case 36: /* fg cyan */ + case 37: /* fg white */ +#ifdef COLOR + cur_color &= ~0xff; + cur_color |= (1 << (param1 - 30)); +#endif + break; + case 40: /* bg black */ + case 41: /* bg red */ + case 42: /* bg green */ + case 43: /* bg yellow */ + case 44: /* bg blue */ + case 45: /* bg magenta */ + case 46: /* bg cyan */ + case 47: /* bg white */ +#ifdef COLOR + cur_color = (1 << (8 + param1 - 40)) | + (cur_color & 0xff); +#endif + break; + } + + parambuf[0] = '\0'; + parambuflen = 0; + } else if (parambuflen < 4) { + parambuf[parambuflen] = console->csi[x]; + parambuflen++; + parambuf[parambuflen] = '\0'; + } + } + + console->cur_attr = param2; + break; + case 'n': /* DSR - device status report */ + switch (param1) { + case 5: /* terminal is ready */ + session_output(console->session, "\e[0n"); + break; + case 6: /* CPR - report cursor position */ + session_output(console->session, "\e["); + + sprintf(parambuf, "%d", console->cursor_line + 1); + for (x = 0; x < sizeof(parambuf); x++) { + if (parambuf[x] == '\0') + break; + session_output_char(console->session, parambuf[x]); + } + session_output_char(console->session, ';'); + + sprintf(parambuf, "%d", console->cursor_column + 1); + for (x = 0; x < sizeof(parambuf); x++) { + if (parambuf[x] == '\0') + break; + session_output_char(console->session, parambuf[x]); + } + + session_output_char(console->session, 'R'); + break; + } + break; + case '7': /* DECSC - save cursor */ + case 's': /* from ANSI.SYS */ +#if 0 + saved_cursorx = cursorx; + saved_cursory = cursory; +#endif + break; + case '8': /* DECRC - restore cursor */ + case 'u': /* from ANSI.SYS */ +#if 0 + cursorx = saved_cursorx; + cursory = saved_cursory; +#endif + break; + default: + /* + * if the last character is a letter and we haven't serviced + * it, assume it's a sequence we don't support and should just + * suppress + */ + if (c < 65 || (c > 90 && c < 97) || c > 122) + serviced = 0; + } + + if (serviced) { + cursor = (console->cursor_line * console->ncolumns) + + console->cursor_column; + console->attrs[cursor] |= (ATTR_CURSOR | ATTR_DIRTY); + console->csilen = 0; + console->csi[0] = '\0'; + console->in_csi = 0; + } } --- console.h Wed Nov 17 15:26:21 2021 +++ console.h Sun Nov 28 13:22:53 2021 @@ -25,14 +25,19 @@ struct console { ControlHandle scroller; char chars[80 * 24]; char attrs[80 * 24]; -#define ATTR_DIRTY (1 << 0) -#define ATTR_BOLD (1 << 1) + char cur_attr; +#define ATTR_BOLD (1 << 0) +#define ATTR_REVERSE (1 << 1) +#define ATTR_UNDERLINE (1 << 2) +#define ATTR_CURSOR (1 << 6) +#define ATTR_DIRTY (1 << 7) short nlines; short ncolumns; short cursor_line; short cursor_column; - short old_cursor_line; - short old_cursor_column; + short in_csi; + short csilen; + char csi[32]; struct session *session; }; @@ -42,8 +47,10 @@ void console_suspend(struct console *console); void console_resume(struct console *console); void console_update(struct console *console, EventRecord *event); void console_mouse_down(struct console *console, EventRecord *event); +void console_key_down(struct console *console, EventRecord *event); short console_output(struct session *session); +short console_input(struct session *session); short console_read(struct session *session); #endif /* __CONSOLE_H__ */ --- main.c Wed Nov 17 15:36:46 2021 +++ main.c Sun Nov 28 21:17:44 2021 @@ -20,6 +20,7 @@ #include "subtext.h" #include "console.h" #include "session.h" +#include "uthread.h" #include "util.h" MenuHandle file_menu; @@ -41,6 +42,8 @@ main(void) short event_in, n; char key; + uthread_init(); + InitGraf(&thePort); InitFonts(); FlushEvents(everyEvent, 0); @@ -65,23 +68,25 @@ main(void) memset(&config, 0, sizeof(config)); strcpy(config.name, "Kludge BBS"); strcpy(config.hostname, "klud.ge"); + + cur_console = console_init(); while (!quitting) { - WaitNextEvent(everyEvent, &event, 5L, 0L); + uthread_coordinate(); + WaitNextEvent(everyEvent, &event, 1L, 0L); switch (event.what) { case nullEvent: - if (!cur_console) - cur_console = console_init(); - console_idle(cur_console); - for (n = 0; n < nsessions; n++) - session_idle(sessions[n]); + if (cur_console) + console_idle(cur_console); break; case keyDown: case autoKey: key = event.message & charCodeMask; if ((event.modifiers & cmdKey) != 0) handle_menu(MenuKey(key)); + else if (cur_console) + console_key_down(cur_console, &event); break; case mouseDown: event_in = FindWindow(event.where, &event_win); --- session.c Wed Nov 17 15:39:45 2021 +++ session.c Sun Nov 28 21:25:13 2021 @@ -21,45 +21,35 @@ #include "subtext.h" #include "session.h" +#include "uthread.h" #include "util.h" struct session **sessions = NULL; short nsessions = 0; char session_tbuf[512]; -void -session_idle(struct session *s) -{ - if (s->obuflen) { - s->node_funcs->output(s); - if (s->obuflen) - return; - } +void session_run(struct uthread *uthread, void *arg); - switch (s->state) { - case SESSION_STATE_INIT: - session_output(s, "\r\nWelcome to %s (%s)\r\n", config.name, - s->node); - s->state = SESSION_STATE_LOGIN; - break; - } -} - struct session * session_create(char *node, struct node_funcs *node_funcs) { struct session *session; - - nsessions++; - sessions = xreallocarray(sessions, nsessions, sizeof(struct session)); + session = xmalloczero(sizeof(struct session)); - sessions[nsessions - 1] = session; + session->uthread = uthread_add(session_run, session); + if (!session->uthread) { + free(session); + return NULL; + } session->state = SESSION_STATE_INIT; - memcpy(session->node, node, sizeof(session->node)); - session->node_funcs = node_funcs; + memcpy(session->node, node, sizeof(session->node)); + nsessions++; + sessions = xreallocarray(sessions, nsessions, sizeof(struct session)); + sessions[nsessions - 1] = session; + return session; } @@ -82,6 +72,114 @@ session_output(struct session *session, const char *fo memcpy(session->obuf + session->obuflen, session_tbuf, len); session->obuflen += len; - + session->node_funcs->output(session); + uthread_yield(); + return len; +} + +short +session_output_char(struct session *session, const char c) +{ + session->obuf[session->obuflen++] = c; + session->node_funcs->output(session); + uthread_yield(); + + return 1; +} + +char +session_input_char(struct session *session) +{ + char ret; + + while (session->ibuflen == 0) + session->node_funcs->input(session); + + ret = session->ibuf[0]; + if (session->ibuflen == 1) + session->ibuflen = 0; + else { + memmove(session->ibuf, session->ibuf + 1, session->ibuflen - 1); + session->ibuflen--; + } + + return ret; +} + +char * +session_field_input(struct session *session, unsigned short len) +{ + short ilen = 0, ipos = 0, lastlen = 0; + char *field; + char c, redraw = 0; + + field = xmalloczero(len); + + for (;;) { + c = session_input_char(session); + switch (c) { + case 8: + if (ipos == 0) + continue; + + if (ipos < ilen) { + /* l:3 p:2 f:ab[cursor]c -> l:2 p:2 f:a[cursor]c */ + redraw = 1; + BlockMove((Ptr)(field + ipos), (Ptr)(field + ipos - 1), + ilen - 1); + } + + ipos--; + ilen--; + field[ipos] = '\0'; + + if (redraw) { + /* TODO */ + } else + /* back one, space, back one */ + session_output(session, "\e[D \e[D"); + + break; + case '\n': + return field; + default: + if (c < 32 || c > 127) + break; + if (ilen >= len) + break; + if (ipos <= ilen) + /* TODO: move */ + ; + + field[ipos] = c; + ipos++; + ilen++; + field[ipos] = '\0'; + session_output_char(session, c); + } + } + + free(field); + return NULL; +} + +void +session_run(struct uthread *uthread, void *arg) +{ + struct session *s = (struct session *)arg; + char *line; + + session_output(s, "\r\n" + "Welcome to %s (%s)\r\n" + "\r\n", config.name, s->node); + + session_output(s, "login: "); + line = session_field_input(s, 50); + session_output(s, "\r\nHi, %s\r\n", line); + free(line); + + for (;;) { + uthread_yield(); + } } --- session.h Wed Nov 17 13:31:32 2021 +++ session.h Fri Nov 26 13:41:08 2021 @@ -17,26 +17,37 @@ #ifndef __SESSION_H__ #define __SESSION_H__ +#include "uthread.h" + enum session_state { SESSION_STATE_INIT, SESSION_STATE_LOGIN, SESSION_STATE_WELCOME }; +enum session_input_state { + SESSION_INPUT_NONE, + SESSION_INPUT_LINE, + SESSION_INPUT_CHAR +}; + +/* TODO: move these to node struct to be session->node->output(..) */ struct node_funcs { short (*output)(struct session *session); - short (*read)(struct session *session); + short (*input)(struct session *session); }; struct session { char node[32]; char obuf[64]; char ibuf[64]; - short state; + enum session_state state; + enum session_input_state input_state; short obuflen; short ibuflen; void *cookie; struct node_funcs *node_funcs; + struct uthread *uthread; }; extern struct session **sessions; @@ -44,6 +55,9 @@ extern short nsessions; struct session *session_create(char *node, struct node_funcs *node_funcs); void session_idle(struct session *session); +char session_input_char(struct session *session); short session_output(struct session *session, const char *format, ...); +short session_output_char(struct session *session, const char c); +char *session_field_input(struct session *session, unsigned short len); #endif /* __SESSION_H__ */