terminfoメモ(libev化)

とりあえず、libevでメインループを置き換えてみる。node.jsで使っているのは、memcacheのlibeventじゃなくてlibevの方だった。

// g++ -o cursor -g cursor.cpp -g -lcurses -lreadline -lev

#include <stdlib.h>
#include <term.h>
#include <curses.h>
#include <errno.h>
#include <readline/readline.h>

#include <ev.h>
#include <fcntl.h>

class Termios
{
    struct termios save_term;
    struct termios temp_term;

    void update()
    {
        errno = 0;
        if(tcsetattr(fileno(stdin), TCSANOW, &temp_term) == -1){
            perror("tcsetattr failure");
            exit(EXIT_FAILURE);
        }
    }

    public:
    Termios()
    {
        // save term
        errno = 0;
        if(tcgetattr(fileno(stdin), &save_term) == -1){
            perror("tcgetattr failure");
            exit(EXIT_FAILURE);
        }
        temp_term=save_term;
    }

    ~Termios()
    {
        restore();
    }

    void no_canonical()
    {
        temp_term.c_iflag &= IGNCR;
        temp_term.c_oflag &= ONLRET;
        temp_term.c_lflag &= (~ISIG & ~ICANON & ~ECHO);
        temp_term.c_cc[VMIN] = 1;
        temp_term.c_cc[VTIME] = 5;
        update();
    }

    void restore()
    {
        tcsetattr(fileno(stdin), TCSANOW, &save_term);
        temp_term=save_term;
    }

    void echo(bool enable)
    {
        if(enable){
            temp_term.c_lflag |= ECHO;
        }
        else{
            temp_term.c_lflag &= ~ECHO;
        }
        update();
    }
};


class Terminfo
{
    public:
        static void initialize()
        {
            if(setupterm(NULL, fileno(stdout), (int *)0) == ERR){
                fprintf(stderr,"setupterm failure\n");
                exit(EXIT_FAILURE);
            }
            cmd("clear");
        }

        static void cmd(const char *cmd)
        {
            errno=0;
            char *parmcmd=tparm(tigetstr(cmd));
            if(parmcmd==NULL){
                perror("tparm NULL");
                exit(EXIT_FAILURE);
            }
            tputs(parmcmd, 1, putchar);
        }
};


Termios termios;


// all watcher callbacks have a similar signature
// this callback is called when data is readable on stdin
static void stdin_cb (EV_P_ ev_io *w, int revents)
{
    if(revents & EV_READ){
        int c;
        while((c=getchar())!=EOF){
            switch(c)
            {
                default:
                    break;
                case 'q':
                    ev_unloop(EV_A_ EVUNLOOP_ALL);
                    break;
                case 'h':
                    Terminfo::cmd("cub1");
                    break;
                case 'j':
                    Terminfo::cmd("cud1");
                    break;
                case 'k':
                    Terminfo::cmd("cuu1");
                    break;
                case 'l':
                    Terminfo::cmd("cuf1");
                    break;
                case ' ':
                    termios.echo(true);
                    readline("input:");
                    termios.echo(false);
                    break;
            }
        }
    }
}


int main(int argc, char **argv)
{
    termios.no_canonical();
    Terminfo::initialize();
    fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);

    // setup libev
    struct ev_loop *loop = EV_DEFAULT;
    ev_io stdin_watcher;
    ev_io_init (&stdin_watcher, stdin_cb, fileno(stdin), EV_READ);
    ev_io_start (loop, &stdin_watcher);

    // start libev
    ev_loop(loop, 0);

    return 0;
}