view ui.c @ 503:bac3f9af112c

More work on curses cleanup. Almost working now.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 02 Jun 2012 20:03:08 +0300
parents ca88945d8eda
children 60e04709ce0f
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) th_vputch(&(win->line->buf), &(win->line->bufsize), &(win->line->len), ch)

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);
                QPUTCH(*s);
                win->line->chlen++;
            }
            else
            {
                if (!nnwin_get_color(&s, &col))
                    return -1;
                
                QPUTCH('½');

                if (!th_growbuf(&(win->line->buf), &(win->line->bufsize), &(win->line->len), sizeof(int)))
                    return -2;

                memcpy(win->line->buf + win->line->len, &col, sizeof(int));
                win->line->len += sizeof(int);
            }
        }
        else if (*s == '\n')
        {
            QPUTCH(0);
            th_ringbuf_add(win->data, win->line);
            win->line = NULL;
            win->dirty = TRUE;
        }
        else if (*s != '\r')
        {
            QPUTCH((unsigned char) *s == 255 ? ' ' : *s);
            win->line->chlen++;
        }

        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) >= getmaxx(win))
            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 char *s = line->buf;
                int col = 0;
                y -= 1 + (line->chlen / scrWidth);
                wmove(stdscr, y, 0);

                while (*s)
                {
                    if (*s == '½')
                    {
                        s++;
                        if (*s == '½')
                        {
                            waddch(stdscr, ((unsigned char) *s) | col);
                            s++;
                        }
                        else
                        {
                            memcpy(&col, s, sizeof(int));
                            s += sizeof(int);
                        }
                    }
                    else
                    {
                        waddch(stdscr, ((unsigned char) *s) | col);
                        s++;
                    }
                }
            }
        }

        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);
}