Mercurial > hg > nnchat
view ui.c @ 642:c8e5949a8961
Use th-libs functions.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 07 Jan 2015 16:56:54 +0200 |
parents | 5269a8cdbd96 |
children | fc19ab0334b1 |
line wrap: on
line source
/* * NNChat - Custom chat client for NewbieNudes.com chatrooms * Written by Matti 'ccr' Hämäläinen * (C) Copyright 2008-2013 Tecnic Software productions (TNSP) */ #include "util.h" #include "ui.h" nn_window_t *chatWindows[SET_MAX_WINDOWS], *currWin = NULL; BOOL appCursesInit = FALSE, appQuitFlag = FALSE; int cursorVisible = ERR, scrWidth, scrHeight; static void nn_line_free(void *ptr) { nn_line_t *line = (nn_line_t *) ptr; if (line != NULL) { th_free(line->buf); th_free(line); } } static nn_window_t *nn_window_new(const char *id) { nn_window_t *res = th_malloc0(sizeof(nn_window_t)); if (res == NULL) return NULL; res->data = th_ringbuf_new(SET_BACKBUF_LEN, nn_line_free); if (res->data == NULL) { th_free(res); return NULL; } res->id = th_strdup(id); return res; } static void nn_window_free(nn_window_t *win) { if (win != NULL) { th_ringbuf_free(win->data); th_free(win->id); th_free(win); } } nn_window_t *nnwin_main_window() { return chatWindows[0]; } nn_window_t *nnwin_get(const int index) { if (index >= 1 && index <= SET_MAX_WINDOWS) return chatWindows[index - 1]; else return NULL; } BOOL nnwin_init(int delay) { // Sanity check the terminal size if (LINES < 0 || LINES > 1000) LINES = 24; if (COLS < 0 || COLS > 1000) COLS = 80; // Initialize (n)curses library and terminal settings initscr(); raw(); keypad(stdscr, TRUE); noecho(); meta(stdscr, TRUE); timeout(delay); scrollok(stdscr, FALSE); cursorVisible = curs_set(1); if (has_colors()) { start_color(); init_pair( 1, COLOR_RED, COLOR_BLACK); init_pair( 2, COLOR_GREEN, COLOR_BLACK); init_pair( 3, COLOR_YELLOW, COLOR_BLACK); init_pair( 4, COLOR_BLUE, COLOR_BLACK); init_pair( 5, COLOR_MAGENTA, COLOR_BLACK); init_pair( 6, COLOR_CYAN, COLOR_BLACK); init_pair( 7, COLOR_WHITE, COLOR_BLACK); init_pair( 8, COLOR_BLACK, COLOR_BLACK); init_pair(10, COLOR_BLACK, COLOR_RED); init_pair(11, COLOR_WHITE, COLOR_RED); init_pair(12, COLOR_GREEN, COLOR_RED); init_pair(13, COLOR_YELLOW, COLOR_RED); init_pair(14, COLOR_BLUE, COLOR_RED); init_pair(15, COLOR_MAGENTA, COLOR_RED); init_pair(16, COLOR_CYAN, COLOR_RED); } appCursesInit = TRUE; nnwin_reset(); #ifdef PDCURSES PDC_set_title("NNChat v" NN_VERSION); #endif memset(chatWindows, 0, sizeof(chatWindows)); chatWindows[0] = nn_window_new(NULL); nn_log_open(chatWindows[0]); currWin = chatWindows[0]; return TRUE; } void nnwin_shutdown() { int i; for (i = 0; i < SET_MAX_WINDOWS; i++) nn_window_free(chatWindows[i]); if (appCursesInit) { if (cursorVisible != ERR) curs_set(cursorVisible); endwin(); THMSG(1, "NCurses deinitialized.\n"); } } void nnwin_reset(void) { getmaxyx(stdscr, scrHeight, scrWidth); } nn_window_t *nnwin_find(const char *id) { int i; for (i = 0; i < SET_MAX_WINDOWS; i++) { if (chatWindows[i] != NULL && chatWindows[i]->id != NULL && th_strcasecmp(id, chatWindows[i]->id) == 0) return chatWindows[i]; } return NULL; } BOOL nnwin_open(const char *name, BOOL curwin) { nn_window_t *res; int i; if (name == NULL) return FALSE; if ((res = nn_window_new(name)) == NULL) return FALSE; nn_log_open(res); for (i = 1; i < SET_MAX_WINDOWS; i++) if (chatWindows[i] == NULL) { res->num = i; chatWindows[i] = res; if (curwin) currWin = res; return TRUE; } return FALSE; } void nnwin_close(nn_window_t *win) { int i; if (win == NULL) return; for (i = 1; i < SET_MAX_WINDOWS; i++) if (chatWindows[i] == win) { chatWindows[i] = NULL; nn_log_close(win); nn_window_free(win); return; } } static BOOL nnwin_get_color(char const **s, int *col) { int val = 0; while (**s >= '0' && **s <= '9') { val *= 10; val += (**s - '0'); (*s)++; } if (**s != '½') return FALSE; if (val < 9) *col = A_DIM | COLOR_PAIR(val); else if (val < 30) *col = A_BOLD | COLOR_PAIR(val - 9); return TRUE; } #define QPUTCH(ch) nnwin_putch(&(win->line->buf), &(win->line->bufsize), &(win->line->len), col, ch) static BOOL nnwin_putch(int **buf, size_t *bufsize, size_t *len, int color, char ch) { if (*buf == NULL || *len + 1 >= *bufsize) { *bufsize += TH_BUFGROW; *buf = (int *) th_realloc(*buf, *bufsize * sizeof(int)); if (*buf == NULL) return FALSE; } (*buf)[*len] = ((unsigned char) ch) | color; (*len)++; return TRUE; } int nnwin_print(nn_window_t *win, const char *fmt) { const char *s = fmt; int col = 0; if (win == NULL) return -16; while (*s) { if (win->line == NULL) { win->line = th_malloc0(sizeof(nn_line_t)); if (win->line == NULL) return -15; } if (*s == '½') { s++; if (*s == '½') { QPUTCH(*s); } else { if (!nnwin_get_color(&s, &col)) return -1; } } else if (*s == '\n') { th_ringbuf_add(win->data, win->line); win->line = NULL; win->dirty = TRUE; } else if (*s != '\r') { QPUTCH((unsigned char) *s == 255 ? ' ' : *s); } s++; } return 0; } static void nnwin_print_str(WINDOW *win, const char *fmt, BOOL clip) { const char *s = fmt; int col = 0; while (*s) { if (clip && getcurx(win) >= scrWidth) return; if (*s == '½') { s++; if (*s == '½') { waddch(win, ((unsigned char) *s) | col); s++; } else { if (!nnwin_get_color(&s, &col)) return; } } else { waddch(win, ((unsigned char) *s) | col); s++; } } } void nnwin_update(BOOL force, BOOL mask, nn_editbuf_t *ebuf, char *username, int usercolor) { int sx, sy; BOOL changed = FALSE; // Clear screen if forced or main or editbuf are dirty if (force || (currWin != NULL && currWin->dirty) || (ebuf != NULL && ebuf->dirty)) { // Save cursor position getyx(stdscr, sy, sx); wattrset(stdscr, A_NORMAL); wbkgdset(stdscr, COLOR_PAIR(0)); werase(stdscr); force = TRUE; changed = TRUE; } else { // Save cursor position getyx(stdscr, sy, sx); } // Check if update is forced or if the window is dirty if (currWin != NULL && (force || currWin->dirty)) { int y, offs; qringbuf_t *buf = currWin->data; changed = TRUE; for (y = scrHeight - 4, offs = buf->size - 1 - currWin->pos; offs >= 0 && y > 0; offs--) { nn_line_t *line = (nn_line_t *) buf->data[offs]; if (line != NULL) { const int *s = line->buf; size_t pos; y -= (line->len / scrWidth); if (line->len % scrWidth != 0) y -= 1; if (y < 0) { size_t r; int x; for (r = -y, x = 0, pos = 0; r && pos < line->len; pos++) { if (++x >= scrWidth) { x = 0; r++; } } y = 0; } wmove(stdscr, y, 0); for (pos = 0; pos < line->len; pos++) waddch(stdscr, s[pos]); } } currWin->dirty = FALSE; } // Update statusline if (changed || force) { char tmpStamp[32], tmpStr[128]; int i; str_get_timestamp(tmpStamp, sizeof(tmpStamp), "%H:%M:%S"); #if 0 snprintf(tmpStr, sizeof(tmpStr), " ½10½%s½13½ | ½16½%s½13½ | ½11½#%06x½13½ | WIN: %d: %s / %d | ½11½", tmpStamp, username, usercolor, currWin->num + 1, currWin->id != NULL ? currWin->id : "MAIN", currWin->pos); #else snprintf(tmpStr, sizeof(tmpStr), " %s | %s | #%06x | WIN: %d: %s / %d | ", tmpStamp, username != NULL ? username : "-", usercolor, currWin != NULL ? currWin->num + 1 : 0, (currWin != NULL && currWin->id != NULL) ? currWin->id : "MAIN", currWin != NULL ? currWin->pos : 0); #endif wmove(stdscr, scrHeight - 4, 0); wbkgdset(stdscr, COLOR_PAIR(10)); wclrtoeol(stdscr); nnwin_print_str(stdscr, tmpStr, TRUE); for (i = 0; i < SET_MAX_WINDOWS; i++) { if (chatWindows[i] != NULL && chatWindows[i]->dirty) { snprintf(tmpStr, sizeof(tmpStr), "%d ", i + 1); waddstr(stdscr, tmpStr); } } // Restore cursor position wmove(stdscr, sy, sx); } // Update editbuf if needed if (ebuf != NULL && (force || ebuf->dirty)) { int yoffs = ebuf->pos / scrWidth, xoffs = ebuf->pos % scrWidth; wmove(stdscr, scrHeight - 3, 0); wattrset(stdscr, A_NORMAL); wbkgdset(stdscr, COLOR_PAIR(0)); ebuf->dirty = FALSE; if (mask) { size_t i; for (i = 0; i < ebuf->len; i++) waddch(stdscr, '*'); } else { char *tmp; ebuf->data[ebuf->len] = 0; tmp = nn_username_decode(th_strdup(ebuf->data)); waddnstr(stdscr, tmp, ebuf->len); th_free(tmp); } wmove(stdscr, scrHeight - 3 + yoffs, xoffs); changed = TRUE; } if (changed) wrefresh(stdscr); } void nnwin_input_process(nn_editbuf_t *editBuf, nn_editstate_t *editState, BOOL (*callback)(int, nn_editbuf_t *, nn_editstate_t *)) { int c, cnt = 0; // Handle several buffered keypresses at once do { c = wgetch(stdscr); /* Handle various problematic cases where terminal * keycodes do not get properly translated by curses */ if (c == 10 || c == 13) c = KEY_ENTER; else if (c == 0x1b) { // ^[O c = wgetch(stdscr); if (c == 'O') { c = wgetch(stdscr); switch (c) { case 'd': c = 0x204; break; case 'c': c = 0x206; break; default: editState->debugMsg("Unhandled ESC-O key sequence 0x%02x\n", c); break; } } // ^[[ else if (c == '[') { c = wgetch(stdscr); switch (c) { case 0x31: c = wgetch(stdscr); if (c >= 0x31 && c <= 0x39) c = KEY_F(c - 0x30); else c = ERR; break; case 0x32: c = KEY_IC; break; case 0x33: c = KEY_DC; break; case 0x35: c = KEY_PPAGE; break; case 0x36: c = KEY_NPAGE; break; case 0x37: c = KEY_HOME; break; case 0x38: c = KEY_END; break; default: editState->debugMsg("Unhandled ESC-[*~ key sequence 0x%02x\n", c); c = ERR; break; } // Get the trailing ~ if (c != ERR) wgetch(stdscr); } else if (c >= 0x31 && c <= 0x39) { c = c - 0x30 + 0x5000; } else { editState->debugMsg("Unhandled ESC key sequence 0x%02x\n", c); } } #if defined(__WIN32) && defined(PDCURSES) else if (c >= 0x198 && c <= 0x1a0) { c = c - 0x198 + 0x5000; } #endif switch (c) { #ifdef KEY_RESIZE case KEY_RESIZE: resize_term(0, 0); erase(); timeout(SET_DELAY); nnwin_reset(); editState->update = TRUE; break; #endif case 0x204: // ctrl+left arrow = Skip words left case 0x20b: while (editBuf->pos > 0 && isspace((int) editBuf->data[editBuf->pos - 1])) editBuf->pos--; while (editBuf->pos > 0 && !isspace((int) editBuf->data[editBuf->pos - 1])) editBuf->pos--; editBuf->dirty = TRUE; break; case 0x206: // ctrl+right arrow = Skip words right case 0x210: while (editBuf->pos < editBuf->len && isspace((int) editBuf->data[editBuf->pos])) editBuf->pos++; while (editBuf->pos < editBuf->len && !isspace((int) editBuf->data[editBuf->pos])) editBuf->pos++; editBuf->dirty = TRUE; break; case KEY_HOME: nn_editbuf_setpos(editBuf, 0); break; case KEY_END: nn_editbuf_setpos(editBuf, editBuf->len); break; case KEY_LEFT: nn_editbuf_setpos(editBuf, editBuf->pos - 1); break; case KEY_RIGHT: nn_editbuf_setpos(editBuf, editBuf->pos + 1); break; case KEY_BACKSPACE: case 0x08: case 0x7f: if (editBuf->pos > 0) { nn_editbuf_delete(editBuf, editBuf->pos - 1); nn_editbuf_setpos(editBuf, editBuf->pos - 1); } break; case KEY_DC: // Delete character nn_editbuf_delete(editBuf, editBuf->pos); break; case KEY_IC: // Ins = Toggle insert / overwrite mode editState->insertMode = !editState->insertMode; break; case KEY_F(2): // F2 = Clear editbuffer nn_editbuf_clear(editBuf); break; case 0x0c: // Ctrl + L editState->update = TRUE; break; case ERR: // Ignore break; default: if (!callback(c, editBuf, editState)) { if (isprint(c) || c == 0xe4 || c == 0xf6 || c == 0xc4 || c == 0xd6) { if (editState->insertMode) nn_editbuf_insert(editBuf, editBuf->pos, c); else nn_editbuf_write(editBuf, editBuf->pos, c); nn_editbuf_setpos(editBuf, editBuf->pos + 1); } else editState->debugMsg("Unhandled key: 0x%02x\n", c); } break; } } while (c != ERR && ++cnt < 10); } char *nnwin_prompt_requester(BOOL allowEmpty, nn_editstate_t *editState, BOOL (*callback)(int, nn_editbuf_t *, nn_editstate_t *), void (*update)(nn_editbuf_t *, nn_editstate_t *)) { nn_editbuf_t *editBuf = nn_editbuf_new(NN_TMPBUF_SIZE); char *res; editState->done = FALSE; while (!editState->isError && !appQuitFlag && !editState->done) { nnwin_input_process(editBuf, editState, callback); update(editBuf, editState); } if (allowEmpty || editBuf->len > 0) res = nn_editbuf_get_string(editBuf, 0, editBuf->len); else res = NULL; nn_editbuf_free(editBuf); return res; }