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 {
		#if LIGATURES_PATCH
		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],
					term.line[term.ocy], term.col);
		#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]);
		#endif // LIGATURES_PATCH
	}
}

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