/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2015  Ladislav Vaiz <ok1zia@nagano.cz>
    and authors of web browser Links 0.96

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include "header.h"

#include "kbd.h"
#include "language2.h"
#include "main.h"
#include "tsdl.h"
#include "terminal.h"

#define OUT_BUF_SIZE    16384
#define IN_BUF_SIZE 16

#define USE_TWIN_MOUSE  1
#define TW_BUTT_LEFT    1
#define TW_BUTT_MIDDLE  2
#define TW_BUTT_RIGHT   4

struct itrm {
    int std_in;
    int std_out;
    int sock_in;
    int sock_out;
    int ctl_in;
    int blocked;
#ifndef Z_MSC_MINGW
    struct termios t;
#endif
    int flags;
    char kqueue[IN_BUF_SIZE];
    int qlen;
    int tm;
    char *ev_queue;
    int eqlen;
    void *mouse_h;
    char *orig_title;
};

void free_trm(void *);
void in_kbd(void *);
void in_sock(void *);

static struct itrm *ditrm = NULL;

int is_blocked(void)
{
    return ditrm && ditrm->blocked;
}

void free_all_itrms(void)
{
	//progress(VTEXT(T_FREE_TERM));		
    if (ditrm) free_trm(ditrm);
}

static void write_ev_queue(struct itrm *itrm)
{
    int l;
    if (!itrm->eqlen) zinternal("event queue empty");
    if ((l = z_pipe_write(itrm->sock_out, itrm->ev_queue, itrm->eqlen > 128 ? 128 : itrm->eqlen)) == -1) {
        free_trm(itrm);
        return;
    }
#ifdef Z_HAVE_SDL    
    if (sdl){
        MUTEX_LOCK(sdl->eventpipestate);
        sdl->eventpipestate+=l;
/*        dbg("u %d\n", sdl->eventpipestate);*/
        MUTEX_UNLOCK(sdl->eventpipestate);
    }
#endif
    
    memmove(itrm->ev_queue, itrm->ev_queue + l, itrm->eqlen -= l);
    if (!itrm->eqlen) 
        zselect_set(zsel,
            itrm->sock_out, 
            (ZSELECT_HANDLER)zselect_get(zsel, itrm->sock_out, H_READ), 
            NULL, 
            (ZSELECT_HANDLER)zselect_get(zsel, itrm->sock_out, H_ERROR), 
            zselect_get(zsel, itrm->sock_out, H_DATA)
        );
}

static void queue_event(struct itrm *itrm, char *data, int len)
{
    int w = 0;
    if (!len) return;
    if (!itrm->eqlen && can_write(itrm->sock_out)){
#ifdef Z_MSC_MINGW
		w = send(itrm->sock_out, data, len, 0);
#else
		w = write(itrm->sock_out, data, len);
#endif
		if (w <= 0) {
			/*free_trm(itrm);*/
			zselect_bh_new(zsel, (void (*)(void *))free_trm, (void *)itrm);
			return;
		}
    }
    
#ifdef Z_HAVE_SDL    
    if (sdl){
        MUTEX_LOCK(sdl->eventpipestate);
        sdl->eventpipestate+=w;
/*        dbg("q %d\n", sdl->eventpipestate);    */
        MUTEX_UNLOCK(sdl->eventpipestate);
    }
#endif
        
    if (w < len) {
        char *c;
        if (!(c = (char *)g_realloc(itrm->ev_queue, itrm->eqlen + len - w))) {
            free_trm(itrm);
            return;
        }
        itrm->ev_queue = c;
        memcpy(itrm->ev_queue + itrm->eqlen, data + w, len - w);
        itrm->eqlen += len - w;
        zselect_set(zsel, itrm->sock_out, 
                (void (*)(void *))zselect_get(zsel, itrm->sock_out, H_READ), 
                (void (*)(void *))write_ev_queue, 
                (void (*)(void *))free_trm, 
                itrm);
    }
}

void kbd_ctrl_c(void)
{
    /*struct event ev = { EV_KBD, KBD_CTRL_C, 0, 0 };*/
    struct event ev = { EV_KBD, 'C', KBD_CTRL, 0 };
    if (ditrm) queue_event(ditrm, (char *)&ev, sizeof(struct event));
}

/*
char *init_seq = "\033[?1000h\033[?47h\0337";
char *term_seq = "\033[2J\033[?1000l\033[?47l\0338\b \b";
*/

static char *init_seq = "\033)0\0337";
static char *init_seq_x_mouse = "\033[?1000h";
static char *init_seq_tw_mouse = "\033[?9h";
static char *term_seq = "\033[2J\0338\r \b";
static char *term_seq_x_mouse = "\033[?1000l";
static char *term_seq_tw_mouse = "\033[?9l";

/*char *term_seq = "\033[2J\033[?1000l\0338\b \b";*/

static void send_init_sequence(int h,int flags)
{
    hard_write(h, init_seq, strlen(init_seq));
    if (flags & USE_TWIN_MOUSE) {
        hard_write(h, init_seq_tw_mouse, strlen(init_seq_tw_mouse));
    } else {
        hard_write(h, init_seq_x_mouse, strlen(init_seq_x_mouse));
    }
}

static void send_term_sequence(int h,int flags)
{
    hard_write(h, term_seq, strlen(term_seq));
    if (flags & USE_TWIN_MOUSE) {
        hard_write(h, term_seq_tw_mouse, strlen(term_seq_tw_mouse));
    } else {
        hard_write(h, term_seq_x_mouse, strlen(term_seq_x_mouse));
    }
}

void resize_terminal(void *arg)
{
	struct event ev = { EV_RESIZE, 0, 0, GPOINTER_TO_INT(arg) };
    int x, y;
#ifdef Z_HAVE_SDL    
    if (sdl){
        if (sdl_get_terminal_size(ditrm->std_out, &x, &y)) return;
    }
    else
#endif
    {
#ifndef Z_MSC_MINGW
        if (term_get_terminal_size(ditrm->std_out, &x, &y)) return;
#endif
    }
    ev.x = x;
    ev.y = y;
    queue_event(ditrm, (char *)&ev, sizeof(struct event));
}

#ifndef Z_MSC_MINGW
static int setraw(int fd, struct termios *p)
{
    struct termios t;
    memset(&t, 0, sizeof(struct termios));
    if (tcgetattr(fd, &t)) return -1;
    if (p) memcpy(p, &t, sizeof(struct termios));
    cfmakeraw(&t);
    t.c_lflag |= ISIG;
#ifdef TOSTOP
    t.c_lflag |= TOSTOP;
#endif
    t.c_oflag |= OPOST;
    if (tcsetattr(fd, TCSANOW, &t)) return -1;
    return 0;
}

#endif
void handle_trm(int std_in, int std_out, int sock_in, int sock_out, int ctl_in)
{
    int x, y;
    struct itrm *itrm;
    struct event ev = { EV_INIT, 80, 24, 0 };
    char *ts;
    int xwin, ret;

#ifdef Z_HAVE_SDL    
    if (sdl){
        ret=sdl_get_terminal_size(ctl_in, &x, &y);
    }
    else
#endif
    {
#ifndef Z_MSC_MINGW
        ret=term_get_terminal_size(ctl_in, &x, &y);
#endif
    }
    if (ret){
        error("ERROR: could not get terminal size");
        return;
    }
    if (!(itrm = (struct itrm*)g_malloc(sizeof(struct itrm)))) return;
#ifndef Z_MSC_MINGW
	memset(&itrm->t, 0, sizeof(itrm->t)); 
#endif
	ditrm = itrm;
    itrm->std_in = std_in;
    itrm->std_out = std_out;
    itrm->sock_in = sock_in;
    itrm->sock_out = sock_out;
    itrm->ctl_in = ctl_in;
    itrm->blocked = 0;
    itrm->qlen = 0;
    itrm->tm = -1;
    itrm->ev_queue = NULL;
    itrm->eqlen = 0;
    if (ctl_in >= 0) {
#ifndef Z_MSC_MINGW
        setraw(ctl_in, &itrm->t);
        zselect_set(zsel, std_in, in_kbd, NULL, free_trm, itrm);
#endif
    }
    // condidion always false
	// if (sock_in != std_out) zselect_set(zsel, sock_in, in_sock, NULL, free_trm, itrm);
    ev.x = x;
    ev.y = y;
#ifndef Z_MSC_MINGW
    handle_terminal_resize(ctl_in, resize_terminal);
#endif
    queue_event(itrm, (char *)&ev, sizeof(struct event));
    xwin = is_xterm() * ENV_XWIN + can_twterm() * ENV_TWIN + (!!getenv("STY")) * ENV_SCREEN;
    itrm->flags = 0;
    if (!(ts = getenv("TERM"))) ts = "";
    if ((xwin & ENV_TWIN) && !strcmp(ts,"linux")) itrm->flags |= USE_TWIN_MOUSE;
    
#ifdef Z_HAVE_SDL    
    if (!sdl){ 
        itrm->mouse_h = handle_mouse(0, (void (*)(void *, char *, int))queue_event, itrm);
    }
    else
#endif
    {
        itrm->mouse_h = NULL;
    }
    itrm->orig_title = get_window_title();
    if (std_out>=0){
        save_screen();
        send_init_sequence(std_out,itrm->flags);
    }
    set_window_title("Tucnak "PACKAGE_VERSION);
}
#ifndef Z_MSC_MINGW

void unblock_itrm_x(void *arg)
{
    close_handle(arg);
    if (!ditrm) return;
    unblock_itrm(0);
    resize_terminal(NULL);
}

int unblock_itrm(int fd)
{
    struct itrm *itrm = ditrm;
    if (!itrm) return -1;
    /*if (ditrm->sock_out != fd) {
        zinternal("unblock_itrm: bad fd: %d", fd);
        return -1;
    }*/
    if (itrm->ctl_in >= 0 && setraw(itrm->ctl_in, NULL)) return -1;
    itrm->blocked = 0;
    send_init_sequence(itrm->std_out,itrm->flags);
    zselect_set(zsel, itrm->std_in, in_kbd, NULL, free_trm, itrm);
    handle_terminal_resize(itrm->ctl_in, resize_terminal);
    //unblock_stdin();
    return 0;
}

void block_itrm(int fd)
{
    struct itrm *itrm = ditrm;
    if (!itrm) return;
    /*if (ditrm->sock_out != fd) {
        zinternal("block_itrm: bad fd: %d", fd);
        return;
    }*/
    itrm->blocked = 1;
    //block_stdin();
    unhandle_terminal_resize(itrm->ctl_in);
    send_term_sequence(itrm->std_out,itrm->flags);
    tcsetattr(itrm->ctl_in, TCSANOW, &itrm->t);
    zselect_set(zsel, itrm->std_in, NULL, NULL, free_trm, itrm);
}
#endif

void free_trm(void *arg)
{
    struct itrm *itrm;

    itrm=(struct itrm*)arg;
    if (!itrm) return;
    set_window_title(itrm->orig_title);
    if (itrm->orig_title) g_free(itrm->orig_title), itrm->orig_title = NULL;
#ifndef Z_MSC_MINGW
    unhandle_terminal_resize(itrm->ctl_in);
#endif
    if (itrm->std_out>=0)  send_term_sequence(itrm->std_out,itrm->flags);
#ifndef Z_MSC_MINGW
    tcsetattr(itrm->ctl_in, TCSANOW, &itrm->t);
#endif
    if (itrm->std_out>=0) restore_screen();
    if (itrm->mouse_h) unhandle_mouse(itrm->mouse_h);
    if (itrm->std_in>=0)   zselect_set(zsel, itrm->std_in, NULL, NULL, NULL, NULL);
    if (itrm->sock_in>=0)  zselect_set(zsel, itrm->sock_in, NULL, NULL, NULL, NULL);
    if (itrm->std_out>=0)  zselect_set(zsel, itrm->std_out, NULL, NULL, NULL, NULL);
    if (itrm->sock_out>=0) zselect_set(zsel, itrm->sock_out, NULL, NULL, NULL, NULL);
    if (itrm->tm != -1) zselect_timer_kill(zsel, itrm->tm);
    g_free(itrm->ev_queue);
    g_free(itrm);
    if (itrm == ditrm) ditrm = NULL;
    itrm=NULL;
}

#ifndef Z_MSC_MINGW
void itrm_safe_abort(void){
    struct itrm *itrm = ditrm;
    if (!itrm) return;
    tcsetattr(itrm->ctl_in, TCSANOW, &itrm->t);
}
#endif

static void resize_terminal_x(char *text)
{
    int x, y;
    char *p;
    if (!(p = strchr(text, ','))) return;
    *p++ = 0;
    x = atoi(text);
    y = atoi(p);
    resize_window(x, y);
    resize_terminal(NULL);
}

void dispatch_special(char *text)
{
    switch (text[0]) {
        case TERM_FN_TITLE:
            set_window_title(text + 1);
            break;
        case TERM_FN_RESIZE:
            resize_terminal_x(text + 1);
            break;
    }
}

//static char buf[OUT_BUF_SIZE];

/*#define RD ({ char cc; if (p < c) cc = buf[p++]; else if ((dl = hard_read(itrm->sock_in, &cc, 1)) <= 0) {debug("%d %d", dl, errno);goto fr;} cc; })*/
/*udefine RD ({ char cc; if (p < c) cc = buf[p++]; else if ((hard_read(itrm->sock_in, &cc, 1)) <= 0) goto fr; cc; })*/
#define RD(xx) { char cc; if (p < c) cc = buf[p++]; else if ((hard_read(itrm->sock_in, &cc, 1)) <= 0) goto fr; xx = cc; }

#if 0  // seems uncalled
void in_sock(void *arg)
{
    char ch;
    int fg;
    int c, i, p;
    struct itrm *itrm;
    GString *path, *delete1;

    itrm=(struct itrm *)arg;
    if ((c = z_pipe_read(itrm->sock_in, buf, OUT_BUF_SIZE)) <= 0) {
        fr:
        free_trm(itrm);
        return;
    }
    qwerty:
    for (i = 0; i < c; i++) if (!buf[i]) goto ex;
    if (!is_blocked()) {
        want_draw();
        hard_write(itrm->std_out, buf, c);
        done_draw();
    }
    return;
    ex:
    if (!is_blocked()) {
        want_draw();
        hard_write(itrm->std_out, buf, i);
        done_draw();
    }
    i++;
    memmove(buf, buf + i, OUT_BUF_SIZE - i);
    c -= i;
    p = 0;
    /*fg = RD;*/
    RD(fg);
    path = g_string_new("");
    delete1 = g_string_new("");
    while (1) {
        RD(ch);
        if (!ch) break;
        g_string_append_c(path, ch);
    }
    while (1) {
        RD(ch);
        if (!ch) break;
        g_string_append_c(delete1, ch);
    }
    if (!*path->str) {
        dispatch_special(delete1->str);
    } else {
        int blockh;
        char *param;
        if (is_blocked() && fg) {
            if (*delete1->str) unlink(delete1->str);
            goto to_je_ale_hnus;
        }
        if (!(param = g_malloc(strlen(path->str) + strlen(delete1->str) + 3))) goto to_je_ale_hnus;
        param[0] = fg;
        strcpy(param + 1, path->str);
        strcpy(param + 1 + strlen(path->str) + 1, delete1->str);
        if (fg == 1) block_itrm(itrm->ctl_in);
        if ((blockh = start_thread((void (*)(void *, int))exec_thread, param, strlen(path->str) + strlen(delete1->str) + 3)) == -1) {
            if (fg == 1) unblock_itrm(itrm->ctl_in);
            g_free(param);
            goto to_je_ale_hnus;
        }
        g_free(param);
        if (fg == 1) {
            zselect_set(zsel, blockh, unblock_itrm_x, NULL, unblock_itrm_x, GINT_TO_POINTER(blockh));
            /*block_itrm(itrm->ctl_in);*/
        } else {
            zselect_set(zsel, blockh, close_handle, NULL, close_handle, GINT_TO_POINTER(blockh));
        }
    }
    to_je_ale_hnus:
    g_string_free(path, TRUE);
    g_string_free(delete1, TRUE);
    memmove(buf, buf + p, OUT_BUF_SIZE - p);
    c -= p;
    goto qwerty;
}
#endif

int process_queue(struct itrm *);

static void kbd_timeout(struct itrm *itrm)
{
    struct event ev = {EV_KBD, KBD_ESC, 0, 0};
    itrm->tm = -1;
    /*if (can_read(itrm->std_in)) {
        in_kbd(itrm);
        return;
    }*/
    if (!itrm->qlen) {
        zinternal("timeout on empty queue");
        return;
    }
    queue_event(itrm, (char *)&ev, sizeof(struct event));
    if (--itrm->qlen) memmove(itrm->kqueue, itrm->kqueue+1, itrm->qlen);
    while (process_queue(itrm)) ;
}

static int get_esc_code(char *str, int len, char *code, int *num, int *el)
{
    int pos;
    *num = 0;
    for (pos = 2; pos < len; pos++) {
        if (str[pos] < '0' || str[pos] > '9' || pos > 7) {
            *el = pos + 1;
            *code = str[pos];
            return 0;
        }
        *num = *num * 10 + str[pos] - '0';
    }
    return -1;
}

struct key {
    int x, y;
};

static struct key os2xtd[256] = {
/* 0 */
{0,0},{0,0},{' ',KBD_CTRL},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{KBD_BS,KBD_ALT},{0,0},
/* 16 */
{'Q',KBD_ALT},{'W',KBD_ALT},{'E',KBD_ALT},{'R',KBD_ALT},{'T',KBD_ALT},{'Y',KBD_ALT},{'U',KBD_ALT},{'I',KBD_ALT},
/* 24 */
{'O',KBD_ALT},{'P',KBD_ALT},{'[',KBD_ALT},{']',KBD_ALT},{KBD_ENTER,KBD_ALT},{0,0},{'A',KBD_ALT},{'S',KBD_ALT},
/* 32 */
{'D',KBD_ALT},{'F',KBD_ALT},{'G',KBD_ALT},{'H',KBD_ALT},{'J',KBD_ALT},{'K',KBD_ALT},{'L',KBD_ALT},{';',KBD_ALT},
/* 40 */
{'\'',KBD_ALT},{'`',KBD_ALT},{0,0},{'\\',KBD_ALT},{'Z',KBD_ALT},{'X',KBD_ALT},{'C',KBD_ALT},{'V',KBD_ALT},
/* 48 */
{'B',KBD_ALT},{'N',KBD_ALT},{'M',KBD_ALT},{',',KBD_ALT},{'.',KBD_ALT},{'/',KBD_ALT},{0,0},{'*',KBD_ALT},
/* 56 */
{0,0},{' ',KBD_ALT},{0,0},{KBD_F1,0},{KBD_F2,0},{KBD_F3,0},{KBD_F4,0},{KBD_F5,0},
/* 64 */
{KBD_F6,0},{KBD_F7,0},{KBD_F8,0},{KBD_F9,0},{KBD_F10,0},{0,0},{0,0},{KBD_HOME,0},
/* 72 */
{KBD_UP,0},{KBD_PGUP,0},{'-',KBD_ALT},{KBD_LEFT,0},{'5',0},{KBD_RIGHT,0},{'+',KBD_ALT},{KBD_END,0},
/* 80 */
{KBD_DOWN,0},{KBD_PGDN,0},{KBD_INS,0},{KBD_DEL,0},{0,0},{0,0},{0,0},{0,0},
/* 88 */
{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{KBD_F1,KBD_CTRL},{KBD_F2,KBD_CTRL},
/* 96 */
{KBD_F3,KBD_CTRL},{KBD_F4,KBD_CTRL},{KBD_F5,KBD_CTRL},{KBD_F6,KBD_CTRL},{KBD_F7,KBD_CTRL},{KBD_F8,KBD_CTRL},{KBD_F9,KBD_CTRL},{KBD_F10,KBD_CTRL},
/* 104 */
{KBD_F1,KBD_ALT},{KBD_F2,KBD_ALT},{KBD_F3,KBD_ALT},{KBD_F4,KBD_ALT},{KBD_F5,KBD_ALT},{KBD_F6,KBD_ALT},{KBD_F7,KBD_ALT},{KBD_F8,KBD_ALT},
/* 112 */
{KBD_F9,KBD_ALT},{KBD_F10,KBD_ALT},{0,0},{KBD_LEFT,KBD_CTRL},{KBD_RIGHT,KBD_CTRL},{KBD_END,KBD_CTRL},{KBD_PGDN,KBD_CTRL},{KBD_HOME,KBD_CTRL},
/* 120 */
{'1',KBD_ALT},{'2',KBD_ALT},{'3',KBD_ALT},{'4',KBD_ALT},{'5',KBD_ALT},{'6',KBD_ALT},{'7',KBD_ALT},{'8',KBD_ALT},
/* 128 */
{'9',KBD_ALT},{'0',KBD_ALT},{'-',KBD_ALT},{'=',KBD_ALT},{KBD_PGUP,KBD_CTRL},{KBD_F11,0},{KBD_F12,0},{0,0},
/* 136 */
{0,0},{KBD_F11,KBD_CTRL},{KBD_F12,KBD_CTRL},{KBD_F11,KBD_ALT},{KBD_F12,KBD_ALT},{KBD_UP,KBD_CTRL},{'-',KBD_CTRL},{'5',KBD_CTRL},
/* 144 */
{'+',KBD_CTRL},{KBD_DOWN,KBD_CTRL},{KBD_INS,KBD_CTRL},{KBD_DEL,KBD_CTRL},{KBD_TAB,KBD_CTRL},{0,0},{0,0},{KBD_HOME,KBD_ALT},
/* 152 */
{KBD_UP,KBD_ALT},{KBD_PGUP,KBD_ALT},{0,0},{KBD_LEFT,KBD_ALT},{0,0},{KBD_RIGHT,KBD_ALT},{0,0},{KBD_END,KBD_ALT},
/* 160 */
{KBD_DOWN,KBD_ALT},{KBD_PGDN,KBD_ALT},{KBD_INS,KBD_ALT},{KBD_DEL,KBD_ALT},{0,0},{KBD_TAB,KBD_ALT},{KBD_ENTER,KBD_ALT},{0,0},
/* 168 */
{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
/* 176 */
{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
/* 192 */
{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
/* 208 */
{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
/* 224 */
{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
/* 240 */
{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
/* 256 */
};

static int xterm_button = -1;
static int xterm_mouse_x = -1;
static int xterm_mouse_y = -1;

int process_queue(struct itrm *itrm)
{
    struct event ev = {EV_KBD, -1, 0, 0};
    int el = 0;
    if (!itrm->qlen) goto end;
    if (itrm->kqueue[0] == '\033') {
        if (itrm->qlen < 2) goto ret;
        if (itrm->kqueue[1] == '[' || itrm->kqueue[1] == 'O') {
            char c;
            int v;
            if (get_esc_code(itrm->kqueue, itrm->qlen, &c, &v, &el)) {
                if (itrm->qlen < 4 || itrm->kqueue[2] != '[') goto ret;
                if (itrm->kqueue[2] != '[') goto ret;
                if (itrm->kqueue[3] < 'A' || itrm->kqueue[3] > 'L') goto ret;
                ev.x = KBD_F1 + itrm->kqueue[3] - 'A';
                el = 4;
            }
            else switch (c) {
                case 'A': ev.x = KBD_UP; break;
                case 'B': ev.x = KBD_DOWN; break;
                case 'C': ev.x = KBD_RIGHT; break;
                case 'D': ev.x = KBD_LEFT; break;
                case 'F':
                case 'e': ev.x = KBD_END; break;
                case 'H':
                case 0: ev.x = KBD_HOME; break;
                case 'I': ev.x = KBD_PGUP; break;
                case 'G': ev.x = KBD_PGDN; break;

                 /* BSD cons25 */         
 /*               case 'M': ev.x = KBD_F1; break;*/
                case 'N': ev.x = KBD_F2; break;
                case 'O': ev.x = KBD_F3; break;
/*                case 'P': ev.x = KBD_F4; break;*/
 /*               case 'Q': ev.x = KBD_F5; break;*/
 /*               case 'R': ev.x = KBD_F6; break;*/
 /*               case 'S': ev.x = KBD_F7; break;*/
                case 'T': ev.x = KBD_F8; break;
                case 'U': ev.x = KBD_F9; break;
                case 'V': ev.x = KBD_F10; break;
                case 'W': ev.x = KBD_F11; break;
                case 'X': ev.x = KBD_F12; break;

                          
                case 'z': switch (v) {
                    case 247: ev.x = KBD_INS; break;
                    case 214: ev.x = KBD_HOME; break;
                    case 220: ev.x = KBD_END; break;
                    case 216: ev.x = KBD_PGUP; break;
                    case 222: ev.x = KBD_PGDN; break;
                    case 249: ev.x = KBD_DEL; break;
                    } break;
                case '~': switch (v) {
                    case 1: ev.x = KBD_HOME; break;
                    case 2: ev.x = KBD_INS; break;
                    case 3: ev.x = KBD_DEL; break;
                    case 4: ev.x = KBD_END; break;
                    case 5: ev.x = KBD_PGUP; break;
                    case 6: ev.x = KBD_PGDN; break;
                    case 7: ev.x = KBD_HOME; break;
                    case 8: ev.x = KBD_END; break;
                    case 11: ev.x = KBD_F1; break;
                    case 12: ev.x = KBD_F2; break;
                    case 13: ev.x = KBD_F3; break;
                    case 14: ev.x = KBD_F4; break;
                    case 15: ev.x = KBD_F5; break;
                    case 17: ev.x = KBD_F6; break;
                    case 18: ev.x = KBD_F7; break;
                    case 19: ev.x = KBD_F8; break;
                    case 20: ev.x = KBD_F9; break;
                    case 21: ev.x = KBD_F10; break;
                    case 23: ev.x = KBD_F11; break;
                    case 24: ev.x = KBD_F12; break;
                    } break;
/*              case 'R':
                          resize_terminal (); break ;*/
                case 'M': if (itrm->qlen - el < 3) goto ret;
                    if (v == 5) {
                        if (xterm_button == -1) xterm_button = 0; /* */
                        if (itrm->qlen - el < 5) goto ret;
                        ev.x = (char)(itrm->kqueue[el+1]) - ' ' - 1 + ((int)((char)(itrm->kqueue[el+2]) - ' ' - 1) << 7);
                        if ( ev.x & (1 << 13)) ev.x = 0; /* ev.x |= ~0 << 14; */
                        ev.y = (char)(itrm->kqueue[el+3]) - ' ' - 1 + ((int)((char)(itrm->kqueue[el+4]) - ' ' - 1) << 7);
                        if ( ev.y & (1 << 13)) ev.y = 0; /* ev.y |= ~0 << 14; */
                        //dbg("process_queue: %d\n", itrm->kqueue[el]);
                        switch ((itrm->kqueue[el] - ' ') ^ xterm_button) { /* Every event changhes only one bit */
                            case TW_BUTT_LEFT:   ev.b = B_LEFT | ( (xterm_button & TW_BUTT_LEFT) ? B_UP : B_DOWN ); break; 
                            case TW_BUTT_MIDDLE: ev.b = B_MIDDLE | ( (xterm_button & TW_BUTT_MIDDLE) ? B_UP :  B_DOWN ); break;
                            case TW_BUTT_RIGHT:  ev.b = B_RIGHT | ( (xterm_button & TW_BUTT_RIGHT) ? B_UP : B_DOWN ); break;
                            case 0: ev.b = B_DRAG;
                            /* default : Twin protocol error */
                        }
                        xterm_button = itrm->kqueue[el] - ' ';
                        el += 5;
                    } else {
                        //dbg("process_queue: %d\n", (unsigned char)itrm->kqueue[el+1]);
                        dbg("process_queue: %02x %02x %02x\n", (unsigned char)itrm->kqueue[el], (unsigned char)itrm->kqueue[el+1], (unsigned char)itrm->kqueue[el+3]);
                        ev.x = (unsigned char)(itrm->kqueue[el+1]) - ' ' - 1;
                        ev.y = (unsigned char)(itrm->kqueue[el+2]) - ' ' - 1;
/*                        ev.b = B_DOWN;
                        if (itrm->kqueue[el] & 4) ev.b = B_DRAG;
                        ev.b |= (itrm->kqueue[el] & BM_BUTT) | B_DOWN;
                        if (itrm->kqueue[el] & 64) ev.b |= 4;
                        if (ev.b == 3) {
                            ev.b = B_UP;
                            if (xterm_button != -1) ev.b |= xterm_button;
                        }  
                        xterm_button = -1;
                        if ((ev.b & BM_ACT) == B_DOWN) xterm_button = ev.b & BM_BUTT;*/
                        ev.b = 0;
                        switch(itrm->kqueue[el]){
                            case 0x20: 
                                ev.b = B_LEFT | B_DOWN;
                                xterm_button = B_LEFT;
                                break;
                            case 0x21: 
                                ev.b = B_MIDDLE | B_DOWN; 
                                xterm_button = B_MIDDLE;
                                break;
                            case 0x22:
                                ev.b = B_RIGHT | B_DOWN;
                                xterm_button = B_RIGHT;
                                break;
                            case 0x23:
                                ev.b = B_UP;
                                if (xterm_button != -1) ev.b |= xterm_button;
                                if (ev.x == xterm_mouse_x && ev.y == xterm_mouse_y) ev.b |= B_CLICK;
                                xterm_button = -1;
                                break;
                            case 0x60:
                                ev.b = B_WHUP | B_DOWN;
                                xterm_button = B_WHUP;
                                break;
                            case 0x61:
                                ev.b = B_WHDOWN | B_DOWN;
                                xterm_button = B_WHDOWN;
                                break;

                        }
                        xterm_mouse_x = ev.x;
                        xterm_mouse_y = ev.y;
                        el += 3;
                    }
                    ev.ev = EV_MOUSE;
                    break;
                    
                case 'P': ev.x = KBD_F1; break;
                case 'Q': ev.x = KBD_F2; break;
                case 'R': ev.x = KBD_F3; break;
                case 'S': ev.x = KBD_F4; break;
                case '[':
                    if (itrm->qlen>=4){
                        el=4;      
                        switch(itrm->kqueue[3]){
                            case 'A': ev.x = KBD_F1; goto upbrk;
                            case 'B': ev.x = KBD_F2; goto upbrk;
                            case 'C': ev.x = KBD_F3; goto upbrk;
                            case 'D': ev.x = KBD_F4; goto upbrk;
                            case 'E': ev.x = KBD_F5; goto upbrk;
                        }        
                    }
                upbrk:        
                break;          
            }
        } else {
            el = 2;
            if (itrm->kqueue[1] >= ' ') {
                ev.x = itrm->kqueue[1];
                ev.y = KBD_ALT;
                goto l2;
            }
            if (itrm->kqueue[1] == '\033') {
                if (itrm->qlen >= 3 && (itrm->kqueue[2] == '[' || itrm->kqueue[2] == 'O')) el = 1;
                ev.x = KBD_ESC;
                goto l2;
            }
        }
        goto l1;
    } else if (itrm->kqueue[0] == 0) {
        if (itrm->qlen < 2) goto ret;
        if (!(ev.x = os2xtd[(unsigned int)itrm->kqueue[1]].x)) ev.x = -1;
        ev.y = os2xtd[(unsigned int)itrm->kqueue[1]].y;
        el = 2;
        /*printf("%02x - %02x %02x\n", (int)itrm->kqueue[1], ev.x, ev.y);*/
        goto l1;
    }
    el = 1;
    ev.x = itrm->kqueue[0];
    l2:
    /*if (ev.x == 1) ev.x = KBD_HOME;
    if (ev.x == 2) ev.x = KBD_PGUP;
    if (ev.x == 4) ev.x = KBD_DEL;
    if (ev.x == 5) ev.x = KBD_END;
    if (ev.x == 6) ev.x = KBD_PGDN;*/
    if (ev.x == 8) ev.x = KBD_BS;
    if (ev.x == 9) ev.x = KBD_TAB;
    if (ev.x == 10) ev.x = KBD_ENTER, ev.y = KBD_CTRL;
    if (ev.x == 13) ev.x = KBD_ENTER;
    if (ev.x == 127) ev.x = KBD_BS;
    if (ev.x < ' ') {
        ev.x += 'A' - 1;
        ev.y = KBD_CTRL;
    }
    l1:
    if (itrm->qlen < el) {
        zinternal("event queue underflow");
        itrm->qlen = el;
    }
    if (ev.x != -1) {
        queue_event(itrm, (char *)&ev, sizeof(struct event));
        memmove(itrm->kqueue, itrm->kqueue + el, itrm->qlen -= el);
    } else {
        /*printf("%d %d\n", itrm->qlen, el);fflush(stdout);*/
        memmove(itrm->kqueue, itrm->kqueue + el, itrm->qlen -= el);
    }
    end:
    if (itrm->qlen < IN_BUF_SIZE) zselect_set(zsel, itrm->std_in, in_kbd, NULL, free_trm, itrm);
    return el;
    ret:
    itrm->tm = zselect_timer_new(zsel, ESC_TIMEOUT, (void (*)(void *))kbd_timeout, itrm);
    return 0;
}

void in_kbd(void *arg)
{
#ifdef Z_MSC_MINGW
	zinternal("in_kbd unimplemented");
#else
    int r;
    struct itrm *itrm;

    itrm=(struct itrm *)arg;
    if (itrm->tm != -1) zselect_timer_kill(zsel, itrm->tm), itrm->tm = -1;
    if (itrm->qlen >= IN_BUF_SIZE) {
        zselect_set(zsel, itrm->std_in, NULL, NULL, free_trm, itrm);
        while (process_queue(itrm));
        return;
    }
    if ((r = read(itrm->std_in, itrm->kqueue + itrm->qlen, IN_BUF_SIZE - itrm->qlen)) <= 0) {
        free_trm(itrm);
        return;
    }
    if ((itrm->qlen += r) > IN_BUF_SIZE) {
        error("ERROR: too many bytes read");
        itrm->qlen = IN_BUF_SIZE;
    }
    while (process_queue(itrm));
#endif
}
