void set_notifmode(int type, KeySym ksym) {
    static char *lib[] = { " MOVE ", " SEL  "};
    static Glyph *g, *deb, *fin;
    static int col, bot;

    if ( ksym == -1 ) {
        free(g);
        col = term.col, bot = term.bot;
        g = xmalloc(col * sizeof(Glyph));
        memcpy(g, term.line[bot], col * sizeof(Glyph));

    }
    else if ( ksym == -2 )
        memcpy(term.line[bot], g, col * sizeof(Glyph));

    if ( type < 2 ) {
        char *z = lib[type];
        for (deb = &term.line[bot][col - 6], fin = &term.line[bot][col]; deb < fin; z++, deb++)
            deb->mode = ATTR_REVERSE,
            deb->u = *z,
            deb->fg = defaultfg, deb->bg = defaultbg;
    }
    else if ( type < 5 )
        memcpy(term.line[bot], g, col * sizeof(Glyph));
    else {
        for (deb = &term.line[bot][0], fin = &term.line[bot][col]; deb < fin; deb++)
            deb->mode = ATTR_REVERSE,
            deb->u = ' ',
            deb->fg = defaultfg, deb->bg = defaultbg;
        term.line[bot][0].u = ksym;
    }

    term.dirty[bot] = 1;
    drawregion(0, bot, col, bot + 1);
}

void select_or_drawcursor(int selectsearch_mode, int type) {
    int done = 0;

    if ( selectsearch_mode & 1 ) {
        selextend(term.c.x, term.c.y, type, done);
        xsetsel(getsel());
    }
    else
        xdrawcursor(term.c.x, term.c.y, term.line[term.c.y][term.c.x],
                    term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
}

void search(int selectsearch_mode, Rune *target, int ptarget, int incr, int type, TCursor *cu) {
    Rune *r;
    int i, bound = (term.col * cu->y + cu->x) * (incr > 0) + incr;

    for (i = term.col * term.c.y + term.c.x + incr; i != bound; i += incr) {
        for (r = target; r - target < ptarget; r++) {
            if ( *r == term.line[(i + r - target) / term.col][(i + r - target) % term.col].u ) {
                if ( r - target == ptarget - 1 )     break;
            } else {
                r = NULL;
                break;
            }
        }
        if ( r != NULL )    break;
    }

    if ( i != bound ) {
        term.c.y = i / term.col, term.c.x = i % term.col;
        select_or_drawcursor(selectsearch_mode, type);
    }
}

int trt_kbdselect(KeySym ksym, char *buf, int len) {
    static TCursor cu;
    static Rune target[64];
    static int type = 1, ptarget, in_use;
    static int sens, quant;
    static char selectsearch_mode;
    int i, bound, *xy;


    if ( selectsearch_mode & 2 ) {
		if ( ksym == XK_Return ) {
			selectsearch_mode ^= 2;
			set_notifmode(selectsearch_mode, -2);
            if ( ksym == XK_Escape )    ptarget = 0;
			return 0;
		}
        else if ( ksym == XK_BackSpace ) {
            if ( !ptarget )     return 0;
            term.line[term.bot][ptarget--].u = ' ';
		}
        else if ( len < 1 ) {
			return 0;
		}
        else if ( ptarget == term.col  || ksym == XK_Escape ) {
            return 0;
        }
		else {
            utf8decode(buf, &target[ptarget++], len);
            term.line[term.bot][ptarget].u = target[ptarget - 1];
		}

        if ( ksym != XK_BackSpace )
            search(selectsearch_mode, &target[0], ptarget, sens, type, &cu);

        term.dirty[term.bot] = 1;
        drawregion(0, term.bot, term.col, term.bot + 1);
        return 0;
    }

    switch ( ksym ) {
    case -1 :
        in_use = 1;
        cu.x = term.c.x, cu.y = term.c.y;
        set_notifmode(0, ksym);
        return MODE_KBDSELECT;
    case XK_s :
        if ( selectsearch_mode & 1 )
            selclear();
        else
            selstart(term.c.x, term.c.y, 0);
        set_notifmode(selectsearch_mode ^= 1, ksym);
        break;
    case XK_t :
        selextend(term.c.x, term.c.y, type ^= 3, i = 0);  /* 2 fois */
        selextend(term.c.x, term.c.y, type, i = 0);
        break;
    case XK_slash :
    case XK_KP_Divide :
    case XK_question :
        ksym &= XK_question;                /* Divide to slash */
        sens = (ksym == XK_slash) ? -1 : 1;
        ptarget = 0;
        set_notifmode(15, ksym);
        selectsearch_mode ^= 2;
        break;
    case XK_Escape :
        if ( !in_use )  break;
        selclear();
    case XK_Return :
        set_notifmode(4, ksym);
        term.c.x = cu.x, term.c.y = cu.y;
        select_or_drawcursor(selectsearch_mode = 0, type);
        in_use = quant = 0;
        return MODE_KBDSELECT;
    case XK_n :
    case XK_N :
        if ( ptarget )
            search(selectsearch_mode, &target[0], ptarget, (ksym == XK_n) ? -1 : 1, type, &cu);
        break;
    case XK_BackSpace :
        term.c.x = 0;
        select_or_drawcursor(selectsearch_mode, type);
        break;
    case XK_dollar :
        term.c.x = term.col - 1;
        select_or_drawcursor(selectsearch_mode, type);
        break;
    case XK_Home :
        term.c.x = 0, term.c.y = 0;
        select_or_drawcursor(selectsearch_mode, type);
        break;
    case XK_End :
        term.c.x = cu.x, term.c.y = cu.y;
        select_or_drawcursor(selectsearch_mode, type);
        break;
    case XK_Page_Up :
    case XK_Page_Down :
        term.c.y = (ksym == XK_Prior ) ? 0 : cu.y;
        select_or_drawcursor(selectsearch_mode, type);
        break;
    case XK_exclam :
        term.c.x = term.col >> 1;
        select_or_drawcursor(selectsearch_mode, type);
        break;
    case XK_asterisk :
    case XK_KP_Multiply :
        term.c.x = term.col >> 1;
    case XK_underscore :
        term.c.y = cu.y >> 1;
        select_or_drawcursor(selectsearch_mode, type);
        break;
    default :
        if ( ksym >= XK_0 && ksym <= XK_9 ) {               /* 0-9 keyboard */
            quant = (quant * 10) + (ksym ^ XK_0);
            return 0;
        }
        else if ( ksym >= XK_KP_0 && ksym <= XK_KP_9 ) {    /* 0-9 numpad */
            quant = (quant * 10) + (ksym ^ XK_KP_0);
            return 0;
        }
        else if ( ksym == XK_k || ksym == XK_h )
            i = ksym & 1;
        else if ( ksym == XK_l || ksym == XK_j )
            i = ((ksym & 6) | 4) >> 1;
        else if ( (XK_Home & ksym) != XK_Home || (i = (ksym ^ XK_Home) - 1) > 3 )
            break;

        xy = (i & 1) ? &term.c.y : &term.c.x;
        sens = (i & 2) ? 1 : -1;
        bound = (i >> 1 ^ 1) ? 0 : (i ^ 3) ? term.col - 1 : term.bot;

        if ( quant == 0 )
            quant++;

        if ( *xy == bound && ((sens < 0 && bound == 0) || (sens > 0 && bound > 0)) )
            break;

        *xy += quant * sens;
        if ( *xy < 0 || ( bound > 0 && *xy > bound) )
            *xy = bound;

        select_or_drawcursor(selectsearch_mode, type);
    }
    quant = 0;
    return 0;
}