Mercurial > hg > nnchat
view ui.c @ 504:60e04709ce0f
Refactor window backbuffer to use integer as internal storage to simplify
line handling and buffer drawing.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 02 Jun 2012 22:00:25 +0300 |
parents | bac3f9af112c |
children | 8734a02a86ec |
line wrap: on
line source
/* * NNChat - Custom chat client for NewbieNudes.com chatrooms * Written by Matti 'ccr' Hämäläinen * (C) Copyright 2008-2012 Tecnic Software productions (TNSP) */ #include "util.h" #include "ui.h" #define STATUS_YPOS 0 nn_window_t *chatWindows[SET_MAX_WINDOWS], *currWin = NULL; BOOL cursesInit = FALSE; int cursorVisible = ERR, scrWidth, scrHeight; static nn_window_t *nn_window_new(const char *id) { nn_window_t *res = th_calloc(1, sizeof(nn_window_t)); if (res == NULL) return NULL; res->data = th_ringbuf_new(NN_BACKBUF_LEN, th_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); } cursesInit = TRUE; nnwin_reset(); #ifdef PDCURSES PDC_set_title("NNChat v" NN_VERSION); #endif memset(chatWindows, 0, sizeof(chatWindows)); chatWindows[0] = nn_window_new(NULL); currWin = chatWindows[0]; return TRUE; } void nnwin_shutdown() { int i; for (i = 0; i < SET_MAX_WINDOWS; i++) nn_window_free(chatWindows[i]); if (cursesInit) { 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) { int i; nn_window_t *res; if (name == NULL) return FALSE; if ((res = nn_window_new(name)) == NULL) return FALSE; 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_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) *bufsize = *len = 0; 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; while (*s) { if (win->line == NULL) { win->line = th_calloc(1, 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; } char *nnwin_prompt_requester(const char *info, BOOL allowEmpty) { char tmpBuf[512], *ptr; int curSave = curs_set(1); echo(); waddstr(stdscr, info); wgetnstr(stdscr, tmpBuf, sizeof(tmpBuf) - 1); noecho(); if (curSave != ERR) curs_set(curSave); str_trim_right(tmpBuf); ptr = str_trim_left(tmpBuf); if (allowEmpty || ptr[0]) return th_strdup(ptr); else return NULL; } 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, nn_editbuf_t *ebuf, char *username, int usercolor) { int sx, sy; // Save cursor position getyx(stdscr, sy, sx); // Clear screen if forced or main or editbuf are dirty if (force || (currWin != NULL && currWin->dirty) || (ebuf != NULL && ebuf->dirty)) { wattrset(stdscr, A_NORMAL); wbkgdset(stdscr, COLOR_PAIR(0)); werase(stdscr); force = TRUE; } // 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; for (y = scrHeight - 4, offs = buf->size - 1 - currWin->pos; offs >= 0 && y > 0; offs--) { nn_line_t *line = 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 { 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, usercolor, currWin->num + 1, currWin->id != NULL ? currWin->id : "MAIN", currWin->pos); #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; char *tmp; ebuf->dirty = FALSE; ebuf->data[ebuf->len] = 0; tmp = nn_username_decode(th_strdup(ebuf->data)); wmove(stdscr, scrHeight - 3, 0); wattrset(stdscr, A_NORMAL); wbkgdset(stdscr, COLOR_PAIR(0)); waddnstr(stdscr, tmp, ebuf->len); wmove(stdscr, scrHeight - 3 + yoffs, xoffs); th_free(tmp); } wrefresh(stdscr); }