diff --git a/config.def.h b/config.def.h index 7f0442d..a3c3785 100644 --- a/config.def.h +++ b/config.def.h @@ -255,6 +255,7 @@ static Shortcut shortcuts[] = { { MODKEY, XK_l, copyurl, {.i = 0} }, { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, + { TERMMOD, XK_Escape, keyboard_select,{.i = 0} }, }; /* diff --git a/st.c b/st.c index 4139d9a..6136b39 100644 --- a/st.c +++ b/st.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "st.h" #include "win.h" @@ -2609,6 +2611,9 @@ tresize(int col, int row) int *bp; TCursor c; + if ( row < term.row || col < term.col ) + toggle_winmode(trt_kbdselect(XK_Escape, NULL, 0)); + if (col < 1 || row < 1) { fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row); @@ -2832,3 +2837,221 @@ copyurl(const Arg *arg) { xclipcopy(); } } + +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], + term.line[term.ocy], term.col); +} + +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; +} diff --git a/st.h b/st.h index a409980..705ef37 100644 --- a/st.h +++ b/st.h @@ -12,45 +12,45 @@ #define DEFAULT(a, b) (a) = (a) ? (a) : (b) #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) #define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \ - (a).fg != (b).fg || \ - (a).bg != (b).bg) + (a).fg != (b).fg || \ + (a).bg != (b).bg) #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ - (t1.tv_nsec-t2.tv_nsec)/1E6) + (t1.tv_nsec-t2.tv_nsec)/1E6) #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) #define IS_TRUECOL(x) (1 << 24 & (x)) enum glyph_attribute { - ATTR_NULL = 0, - ATTR_BOLD = 1 << 0, - ATTR_FAINT = 1 << 1, - ATTR_ITALIC = 1 << 2, - ATTR_UNDERLINE = 1 << 3, - ATTR_BLINK = 1 << 4, - ATTR_REVERSE = 1 << 5, - ATTR_INVISIBLE = 1 << 6, - ATTR_STRUCK = 1 << 7, - ATTR_WRAP = 1 << 8, - ATTR_WIDE = 1 << 9, - ATTR_WDUMMY = 1 << 10, - ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +ATTR_NULL = 0, +ATTR_BOLD = 1 << 0, +ATTR_FAINT = 1 << 1, +ATTR_ITALIC = 1 << 2, +ATTR_UNDERLINE = 1 << 3, +ATTR_BLINK = 1 << 4, +ATTR_REVERSE = 1 << 5, +ATTR_INVISIBLE = 1 << 6, +ATTR_STRUCK = 1 << 7, +ATTR_WRAP = 1 << 8, +ATTR_WIDE = 1 << 9, +ATTR_WDUMMY = 1 << 10, +ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, }; enum selection_mode { - SEL_IDLE = 0, - SEL_EMPTY = 1, - SEL_READY = 2 +SEL_IDLE = 0, +SEL_EMPTY = 1, +SEL_READY = 2 }; enum selection_type { - SEL_REGULAR = 1, - SEL_RECTANGULAR = 2 +SEL_REGULAR = 1, +SEL_RECTANGULAR = 2 }; enum selection_snap { - SNAP_WORD = 1, - SNAP_LINE = 2 +SNAP_WORD = 1, +SNAP_LINE = 2 }; typedef unsigned char uchar; @@ -62,20 +62,20 @@ typedef uint_least32_t Rune; #define Glyph Glyph_ typedef struct { - Rune u; /* character code */ - ushort mode; /* attribute flags */ - uint32_t fg; /* foreground */ - uint32_t bg; /* background */ +Rune u; /* character code */ +ushort mode; /* attribute flags */ +uint32_t fg; /* foreground */ +uint32_t bg; /* background */ } Glyph; typedef Glyph *Line; typedef union { - int i; - uint ui; - float f; - const void *v; - const char *s; +int i; +uint ui; +float f; +const void *v; +const char *s; } Arg; void die(const char *, ...); @@ -114,6 +114,7 @@ size_t utf8encode(Rune, char *); void *xmalloc(size_t); void *xrealloc(void *, size_t); char *xstrdup(const char *); +int trt_kbdselect(KeySym, char *, int); /* config.h globals */ extern char *utmp; diff --git a/win.h b/win.h index 94679e4..173d5ea 100644 --- a/win.h +++ b/win.h @@ -21,6 +21,7 @@ enum win_mode { MODE_NUMLOCK = 1 << 17, MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ |MODE_MOUSEMANY, + MODE_KBDSELECT = 1 << 18, }; void xbell(void); @@ -38,4 +39,6 @@ void xsetmode(int, unsigned int); void xsetpointermotion(int); void xsetsel(char *); int xstartdraw(void); +void toggle_winmode(int); +void keyboard_select(const Arg *); void xximspot(int, int); diff --git a/x.c b/x.c index f832d1d..2a721c1 100644 --- a/x.c +++ b/x.c @@ -1951,6 +1951,12 @@ kpress(XEvent *ev) } else { len = XLookupString(e, buf, sizeof buf, &ksym, NULL); } + if ( IS_SET(MODE_KBDSELECT) ) { + if ( match(XK_NO_MOD, e->state) || + (XK_Shift_L | XK_Shift_R) & e->state ) + win.mode ^= trt_kbdselect(ksym, buf, len); + return; + } /* 1. shortcuts */ for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { if (ksym == bp->keysym && match(bp->mod, e->state)) { @@ -2181,6 +2187,14 @@ usage(void) " [stty_args ...]\n", argv0, argv0); } +void toggle_winmode(int flag) { + win.mode ^= flag; +} + +void keyboard_select(const Arg *dummy) { + win.mode ^= trt_kbdselect(-1, NULL, 0); +} + int main(int argc, char *argv[]) {