sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit bab7d06386deefc481785911e5a9eb240dce9a51
parent 4060b0312c8894bfa8c2ce8ef66e5bbca01d995e
Author: Tonton Couillon <la.luge@free.fr>
Date:   Tue, 19 Jun 2018 14:56:11 +0200

This patch allows you to select and copy text to primary buffer with
keyboard shortcuts like the perl extension keyboard-select for urxvt.

Diffstat:
Ast.suckless.org/patches/keyboard_select/git-st-keyboard_select-20180619-937e367.diff | 319+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ast.suckless.org/patches/keyboard_select/index.md | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ast.suckless.org/patches/keyboard_select/st-keyboard_select-0.8.1.diff | 319+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 703 insertions(+), 0 deletions(-)

diff --git a/st.suckless.org/patches/keyboard_select/git-st-keyboard_select-20180619-937e367.diff b/st.suckless.org/patches/keyboard_select/git-st-keyboard_select-20180619-937e367.diff @@ -0,0 +1,319 @@ +diff --git a/a/config.def.h b/b/config.def.h +index 82b1b09..cdfbdc9 100644 +--- a/a/config.def.h ++++ b/b/config.def.h +@@ -178,6 +178,7 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { TERMMOD, XK_I, iso14755, {.i = 0} }, ++ { TERMMOD, XK_Escape, keyboard_select,{ 0 } }, + }; + + /* +diff --git a/a/st.c b/b/st.c +index 76bb3ea..8a0d448 100644 +--- a/a/st.c ++++ b/b/st.c +@@ -16,6 +16,8 @@ + #include <termios.h> + #include <unistd.h> + #include <wchar.h> ++#include <X11/keysym.h> ++#include <X11/X.h> + + #include "st.h" + #include "win.h" +@@ -2504,6 +2506,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); +@@ -2625,3 +2630,220 @@ redraw(void) + tfulldirt(); + draw(); + } ++ ++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; ++} +diff --git a/a/st.h b/b/st.h +index dac64d8..d6693bf 100644 +--- a/a/st.h ++++ b/b/st.h +@@ -110,6 +110,7 @@ size_t utf8encode(Rune, char *); + void *xmalloc(size_t); + void *xrealloc(void *, size_t); + char *xstrdup(char *); ++int trt_kbdselect(KeySym, char *, int); + + /* config.h globals */ + extern char *utmp; +diff --git a/a/win.h b/b/win.h +index 31f327d..75a2756 100644 +--- a/a/win.h ++++ b/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); +@@ -36,3 +37,5 @@ void xsetmode(int, unsigned int); + void xsetpointermotion(int); + void xsetsel(char *); + int xstartdraw(void); ++void toggle_winmode(int); ++void keyboard_select(const Arg *); +diff --git a/a/x.c b/b/x.c +index c0bd890..39e2984 100644 +--- a/a/x.c ++++ b/b/x.c +@@ -1695,6 +1695,13 @@ kpress(XEvent *ev) + return; + + len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); ++ 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)) { +@@ -1879,6 +1886,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[]) + { diff --git a/st.suckless.org/patches/keyboard_select/index.md b/st.suckless.org/patches/keyboard_select/index.md @@ -0,0 +1,65 @@ +keyboard_select +=============== + +Description +----------- + +This patch allows you to select and copy text to primary buffer with keyboard +shortcuts like the perl extension &ldquo;keyboard-select&rdquo; for urxvt. + +Instructions +------------ +The patch changes the &ldquo;config.def.h&rdquo;. Delete your +&ldquo;config.h&rdquo; or add the shortcut below if you use a custom one. + + Shortcut shortcuts[] = { + ... + { TERMMOD, XK_Escape, keyboard_select, { 0 } }, + }; + + +Notes +----- + +When you run "keyboard_select", you have 3 modes available : + +* move mode : to set the start of the selection; +* select mode : to activate and set the end of the selection; +* input mode : to enter the search criteria. + +<br> + +Shortcuts for move and select modes : + + h, j, k, l: move cursor left/down/up/right (also with arrow keys) + !, _, *: move cursor to the middle of the line/column/screen + Backspace, $: move cursor to the beginning/end of the line + PgUp, PgDown : move cursor to the beginning/end of the column + Home, End: move cursor to the top/bottom left corner of the screen + /, ?: activate input mode and search up/down + n, N: repeat last search, up/down + s: toggle move/selection mode + t: toggle regular/rectangular selection type + Return: quit keyboard_select, keeping the highlight of the selection + Escape: quit keyboard_select + +With h,j,k,l (also with arrow keys), you can use a quantifier. Enter a number before hitting the appropriate key. + +<br> + +Shortcuts for input mode : + + Return: Return to the previous mode + + + +Download +-------- + + * [git-st-keyboard_select-20180619-937e367.diff](git-st-keyboard_select-20180619-937e367.diff) + * [st-keyboard_select-0.8.1.diff](st-keyboard_select-0.8.1.diff) + + +Authors +------- + * Tonton Couillon - &lt;la dot luge at free dot fr> diff --git a/st.suckless.org/patches/keyboard_select/st-keyboard_select-0.8.1.diff b/st.suckless.org/patches/keyboard_select/st-keyboard_select-0.8.1.diff @@ -0,0 +1,319 @@ +diff --git a/a/config.def.h b/b/config.def.h +index 82b1b09..cdfbdc9 100644 +--- a/a/config.def.h ++++ b/b/config.def.h +@@ -178,6 +178,7 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { TERMMOD, XK_I, iso14755, {.i = 0} }, ++ { TERMMOD, XK_Escape, keyboard_select,{ 0 } }, + }; + + /* +diff --git a/a/st.c b/b/st.c +index 46c954b..1c07288 100644 +--- a/a/st.c ++++ b/b/st.c +@@ -16,6 +16,8 @@ + #include <termios.h> + #include <unistd.h> + #include <wchar.h> ++#include <X11/keysym.h> ++#include <X11/X.h> + + #include "st.h" + #include "win.h" +@@ -2495,6 +2497,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); +@@ -2616,3 +2621,220 @@ redraw(void) + tfulldirt(); + draw(); + } ++ ++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; ++} +diff --git a/a/st.h b/b/st.h +index dac64d8..d6693bf 100644 +--- a/a/st.h ++++ b/b/st.h +@@ -110,6 +110,7 @@ size_t utf8encode(Rune, char *); + void *xmalloc(size_t); + void *xrealloc(void *, size_t); + char *xstrdup(char *); ++int trt_kbdselect(KeySym, char *, int); + + /* config.h globals */ + extern char *utmp; +diff --git a/a/win.h b/b/win.h +index 31f327d..75a2756 100644 +--- a/a/win.h ++++ b/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); +@@ -36,3 +37,5 @@ void xsetmode(int, unsigned int); + void xsetpointermotion(int); + void xsetsel(char *); + int xstartdraw(void); ++void toggle_winmode(int); ++void keyboard_select(const Arg *); +diff --git a/a/x.c b/b/x.c +index c343ba2..14279de 100644 +--- a/a/x.c ++++ b/b/x.c +@@ -1696,6 +1696,13 @@ kpress(XEvent *ev) + return; + + len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &status); ++ 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)) { +@@ -1880,6 +1887,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[]) + {