selection clicks, shift+arrow keys, fast(er) redraw, key mask in config.h (thx Magnus Leuthner) - st - Personal fork of st
 (HTM) git clone git://git.drkhsh.at/st.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 9d5ea14b9dffac863b41432e4ce12b5624592000
 (DIR) parent d3c5aba2d9b0f6046e4cc72fb056cbd56c6314e6
 (HTM) Author: Aurélien Aptel <aurelien.aptel@gmail.com>
       Date:   Fri, 22 Apr 2011 00:18:53 +0200
       
       selection clicks, shift+arrow keys, fast(er) redraw, key mask in config.h (thx Magnus Leuthner)
       
       Diffstat:
         M config.def.h                        |      47 +++++++++++++++++--------------
         M st.c                                |     136 ++++++++++++++++++++++---------
         M st.info                             |       4 ++++
       
       3 files changed, 126 insertions(+), 61 deletions(-)
       ---
 (DIR) diff --git a/config.def.h b/config.def.h
       @@ -26,32 +26,33 @@ static const char *colorname[] = {
        };
        
        /* Default colors (colorname index) */
       -/* foreground, background, cursor */
       +/* foreground, background, cursor   */
        #define DefaultFG 7
        #define DefaultBG 0
        #define DefaultCS 1
        
       -/* Special keys */
       +/* Special keys (change & recompile st.info accordingly) */
       +/*    key,        mask,  output */
        static Key key[] = {
       -        { XK_BackSpace, "\177" },
       -        { XK_Insert,    "\033[2~" },
       -        { XK_Delete,    "\033[3~" },
       -        { XK_Home,      "\033[1~" },
       -        { XK_End,       "\033[4~" },
       -        { XK_Prior,     "\033[5~" },
       -        { XK_Next,      "\033[6~" },
       -        { XK_F1,        "\033OP"   },
       -        { XK_F2,        "\033OQ"   },
       -        { XK_F3,        "\033OR"   },
       -        { XK_F4,        "\033OS"   },
       -        { XK_F5,        "\033[15~" },
       -        { XK_F6,        "\033[17~" },
       -        { XK_F7,        "\033[18~" },
       -        { XK_F8,        "\033[19~" },
       -        { XK_F9,        "\033[20~" },
       -        { XK_F10,       "\033[21~" },
       -        { XK_F11,       "\033[23~" },
       -        { XK_F12,       "\033[24~" },
       +        { XK_BackSpace, 0, "\177" },
       +        { XK_Insert,    0, "\033[2~" },
       +        { XK_Delete,    0, "\033[3~" },
       +        { XK_Home,      0, "\033[1~" },
       +        { XK_End,       0, "\033[4~" },
       +        { XK_Prior,     0, "\033[5~" },
       +        { XK_Next,      0, "\033[6~" },
       +        { XK_F1,        0, "\033OP"   },
       +        { XK_F2,        0, "\033OQ"   },
       +        { XK_F3,        0, "\033OR"   },
       +        { XK_F4,        0, "\033OS"   },
       +        { XK_F5,        0, "\033[15~" },
       +        { XK_F6,        0, "\033[17~" },
       +        { XK_F7,        0, "\033[18~" },
       +        { XK_F8,        0, "\033[19~" },
       +        { XK_F9,        0, "\033[20~" },
       +        { XK_F10,       0, "\033[21~" },
       +        { XK_F11,       0, "\033[23~" },
       +        { XK_F12,       0, "\033[24~" },
        };
        
        /* Line drawing characters (sometime specific to each font...) */
       @@ -61,3 +62,7 @@ static char gfx[] = {
                ['i'] = '#',
                [255] = 0,
        };
       +
       +/* double-click timeout (in milliseconds) between clicks for selection */
       +#define DOUBLECLICK_TIMEOUT 300
       +#define TRIPLECLICK_TIMEOUT (2*DOUBLECLICK_TIMEOUT)
 (DIR) diff --git a/st.c b/st.c
       @@ -22,6 +22,9 @@
        #include <X11/cursorfont.h>
        #include <X11/keysym.h>
        
       +#include <sys/time.h>
       +#include <time.h>
       +
        #if   defined(__linux)
         #include <pty.h>
        #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
       @@ -45,11 +48,12 @@
        #define MIN(a, b)  ((a) < (b) ? (a) : (b))
        #define MAX(a, b)  ((a) < (b) ? (b) : (a))
        #define LEN(a)     (sizeof(a) / sizeof(a[0]))
       -#define DEFAULT(a, b)     (a) = (a) ? (a) : (b)    
       +#define DEFAULT(a, b)     (a) = (a) ? (a) : (b)
        #define BETWEEN(x, a, b)  ((a) <= (x) && (x) <= (b))
        #define LIMIT(x, a, b)    (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
        #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
        #define IS_SET(flag) (term.mode & (flag))
       +#define TIMEDIFFERENCE(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_usec-t2.tv_usec)/1000)
        
        /* Attribute, Cursor, Character state, Terminal mode, Screen draw mode */
        enum { ATTR_NULL=0 , ATTR_REVERSE=1 , ATTR_UNDERLINE=2, ATTR_BOLD=4, ATTR_GFX=8 };
       @@ -57,10 +61,9 @@ enum { CURSOR_UP, CURSOR_DOWN, CURSOR_LEFT, CURSOR_RIGHT,
               CURSOR_SAVE, CURSOR_LOAD };
        enum { CURSOR_DEFAULT = 0, CURSOR_HIDE = 1, CURSOR_WRAPNEXT = 2 };
        enum { GLYPH_SET=1, GLYPH_DIRTY=2 };
       -enum { MODE_WRAP=1, MODE_INSERT=2, MODE_APPKEYPAD=4, MODE_ALTSCREEN=8, 
       +enum { MODE_WRAP=1, MODE_INSERT=2, MODE_APPKEYPAD=4, MODE_ALTSCREEN=8,
               MODE_CRLF=16 };
        enum { ESC_START=1, ESC_CSI=2, ESC_OSC=4, ESC_TITLE=8, ESC_ALTCHARSET=16 };
       -enum { SCREEN_UPDATE, SCREEN_REDRAW };
        enum { WIN_VISIBLE=1, WIN_REDRAW=2, WIN_FOCUSED=4 };
        
        #undef B0
       @@ -87,21 +90,21 @@ typedef struct {
        /* ESC '[' [[ [<priv>] <arg> [;]] <mode>] */
        typedef struct {
                char buf[ESC_BUF_SIZ]; /* raw string */
       -        int len;                           /* raw string length */
       +        int len;               /* raw string length */
                char priv;
                int arg[ESC_ARG_SIZ];
       -        int narg;                           /* nb of args */
       +        int narg;              /* nb of args */
                char mode;
        } CSIEscape;
        
        /* Internal representation of the screen */
        typedef struct {
       -        int row;        /* nb row */  
       +        int row;        /* nb row */
                int col;        /* nb col */
                Line* line;        /* screen */
                Line* alt;        /* alternate screen */
                TCursor c;        /* cursor */
       -        int top;        /* top          scroll limit */
       +        int top;        /* top    scroll limit */
                int bot;        /* bottom scroll limit */
                int mode;        /* terminal mode flags */
                int esc;        /* escape state flags */
       @@ -118,17 +121,18 @@ typedef struct {
                XIM xim;
                XIC xic;
                int scr;
       -        int w;        /* window width         */
       +        int w;        /* window width */
                int h;        /* window height */
                int bufw; /* pixmap width  */
                int bufh; /* pixmap height */
                int ch; /* char height */
                int cw; /* char width  */
                char state; /* focus, redraw, visible */
       -} XWindow; 
       +} XWindow;
        
        typedef struct {
                KeySym k;
       +        unsigned int mask;
                char s[ESC_BUF_SIZ];
        } Key;
        
       @@ -150,15 +154,18 @@ typedef struct {
                int mode;
                int bx, by;
                int ex, ey;
       -        struct {int x, y;}  b, e;
       +        struct {int x, y;} b, e;
                char *clip;
                Atom xtarget;
       +        struct timeval tclick1;
       +        struct timeval tclick2;
        } Selection;
        
        #include "config.h"
        
        static void die(const char *errstr, ...);
       -static void draw(int);
       +static void draw();
       +static void drawregion(int, int, int, int);
        static void execsh(void);
        static void sigchld(int);
        static void run(void);
       @@ -206,7 +213,7 @@ static void xresize(int, int);
        static void expose(XEvent *);
        static void visibility(XEvent *);
        static void unmap(XEvent *);
       -static char* kmap(KeySym);
       +static char* kmap(KeySym, unsigned int state);
        static void kpress(XEvent *);
        static void resize(XEvent *);
        static void focus(XEvent *);
       @@ -219,7 +226,7 @@ static void selrequest(XEvent *);
        static void selinit(void);
        static inline int selected(int, int);
        static void selcopy(void);
       -static void selpaste(void);
       +static void selpaste();
        
        static int utf8decode(char *, long *);
        static int utf8encode(long *, char *);
       @@ -340,7 +347,7 @@ isfullutf8(char *s, int b) {
                else if((*c1&(B7|B6|B5)) == (B7|B6) && b == 1)
                        return 0;
                else if((*c1&(B7|B6|B5|B4)) == (B7|B6|B5) &&
       -            ((b == 1) || 
       +            ((b == 1) ||
                    ((b == 2) && (*c2&(B7|B6)) == B7)))
                        return 0;
                else if((*c1&(B7|B6|B5|B4|B3)) == (B7|B6|B5|B4) &&
       @@ -362,12 +369,14 @@ utf8size(char *s) {
                        return 2;
                else if ((c&(B7|B6|B5|B4)) == (B7|B6|B5))
                        return 3;
       -        else 
       +        else
                        return 4;
        }
        
        void
        selinit(void) {
       +        sel.tclick1.tv_sec = 0;
       +        sel.tclick1.tv_usec = 0;
                sel.mode = 0;
                sel.bx = -1;
                sel.clip = NULL;
       @@ -376,14 +385,14 @@ selinit(void) {
                        sel.xtarget = XA_STRING;
        }
        
       -static inline int 
       +static inline int
        selected(int x, int y) {
                if(sel.ey == y && sel.by == y) {
                        int bx = MIN(sel.bx, sel.ex);
                        int ex = MAX(sel.bx, sel.ex);
                        return BETWEEN(x, bx, ex);
                }
       -        return ((sel.b.y < y&&y < sel.e.y) || (y==sel.e.y && x<=sel.e.x)) 
       +        return ((sel.b.y < y&&y < sel.e.y) || (y==sel.e.y && x<=sel.e.x))
                        || (y==sel.b.y && x>=sel.b.x && (x<=sel.e.x || sel.b.y!=sel.e.y));
        }
        
       @@ -511,30 +520,63 @@ xsetsel(char *str) {
                XFlush(xw.dpy);
        }
        
       -/* TODO: doubleclick to select word */
        void
        brelease(XEvent *e) {
                int b;
                sel.mode = 0;
                getbuttoninfo(e, &b, &sel.ex, &sel.ey);
       -        if(sel.bx==sel.ex && sel.by==sel.ey) {
       +        
       +        if(sel.bx == sel.ex && sel.by == sel.ey) {
                        sel.bx = -1;
       -                if(b==2)
       +                if(b == 2)
                                selpaste();
       +
       +                else if(b == 1) {
       +                        /* double click to select word */
       +                        struct timeval now;
       +                        gettimeofday(&now, NULL);
       +
       +                        if(TIMEDIFFERENCE(now, sel.tclick1) <= DOUBLECLICK_TIMEOUT) {
       +                                sel.bx = sel.ex;
       +                                while(term.line[sel.ey][sel.bx-1].state & GLYPH_SET && 
       +                                          term.line[sel.ey][sel.bx-1].c[0] != ' ') sel.bx--;
       +                                sel.b.x = sel.bx;
       +                                while(term.line[sel.ey][sel.ex+1].state & GLYPH_SET && 
       +                                          term.line[sel.ey][sel.ex+1].c[0] != ' ') sel.ex++;
       +                                sel.e.x = sel.ex;
       +                                sel.b.y = sel.e.y = sel.ey;
       +                                selcopy();
       +                        }
       +
       +                        /* triple click on the line */
       +                        if(TIMEDIFFERENCE(now, sel.tclick2) <= TRIPLECLICK_TIMEOUT) {
       +                                sel.b.x = sel.bx = 0;
       +                                sel.e.x = sel.ex = term.col;
       +                                sel.b.y = sel.e.y = sel.ey;
       +                                selcopy();
       +                        }
       +                }
                } else {
       -                if(b==1)
       +                if(b == 1) 
                                selcopy();
                }
       -        draw(1);
       +        memcpy(&sel.tclick2, &sel.tclick1, sizeof(struct timeval));
       +        gettimeofday(&sel.tclick1, NULL);
       +        draw();
        }
        
        void
        bmotion(XEvent *e) {
       -        if (sel.mode) {
       +        if(sel.mode) {
       +                int oldey = sel.ey, 
       +                        oldex = sel.ex;
                        getbuttoninfo(e, NULL, &sel.ex, &sel.ey);
       -                /* XXX: draw() can't keep up, disabled for now.
       -                   selection is visible on button release.
       -                   draw(1); */
       +
       +                if(oldey != sel.ey || oldex != sel.ex) {
       +                        int starty = MIN(oldey, sel.ey);
       +                        int endy = MAX(oldey, sel.ey);
       +                        drawregion(0, (starty > 0 ? starty : 0), term.col, (sel.ey < term.row ? endy+1 : term.row));
       +                }
                }
        }
        
       @@ -641,6 +683,10 @@ ttyread(void) {
        
        void
        ttywrite(const char *s, size_t n) {
       +        {size_t nn;
       +                for(nn = 0; nn < n; nn++)
       +                        dump(s[nn]);
       +        }
                if(write(cmdfd, s, n) == -1)
                        die("write error on tty: %s\n", SERRNO);
        }
       @@ -1640,8 +1686,13 @@ xdrawc(int x, int y, Glyph g) {
                    dc.gc, r.x, r.y+dc.font.ascent, g.c, sl);
        }
        
       +void 
       +drawregion(int x0, int x1, int y0, int y1) {
       +        draw();
       +}
       +
        void
       -draw(int dummy) {
       +draw() {
                int x, y;
        
                xclear(0, 0, term.col-1, term.row-1);
       @@ -1657,8 +1708,13 @@ draw(int dummy) {
        
        #else
        /* optimized drawing routine */
       +void 
       +draw() {
       +        drawregion(0, 0, term.col, term.row);
       +}
       +
        void
       -draw(int redraw_all) {
       +drawregion(int x1, int y1, int x2, int y2) {
                int ic, ib, x, y, ox, sl;
                Glyph base, new;
                char buf[DRAW_BUF_SIZ];
       @@ -1666,13 +1722,13 @@ draw(int redraw_all) {
                if(!(xw.state & WIN_VISIBLE))
                        return;
        
       -        xclear(0, 0, term.col-1, term.row-1);
       -        for(y = 0; y < term.row; y++) {
       +        xclear(x1, y1, x2-1, y2-1);
       +        for(y = y1; y < y2; y++) {
                        base = term.line[y][0];
                        ic = ib = ox = 0;
       -                for(x = 0; x < term.col; x++) {
       +                for(x = x1; x < x2; x++) {
                                new = term.line[y][x];
       -                        if(sel.bx!=-1 && *(new.c) && selected(x, y))
       +                        if(sel.bx != -1 && *(new.c) && selected(x, y))
                                        new.mode ^= ATTR_REVERSE;
                                if(ib > 0 && (!(new.state & GLYPH_SET) || ATTRCMP(base, new) ||
                                                ib >= DRAW_BUF_SIZ-UTF_SIZ)) {
       @@ -1705,7 +1761,7 @@ expose(XEvent *ev) {
                if(xw.state & WIN_REDRAW) {
                        if(!e->count) {
                                xw.state &= ~WIN_REDRAW;
       -                        draw(SCREEN_REDRAW);
       +                        draw();
                        }
                } else
                        XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, e->x-BORDER, e->y-BORDER,
       @@ -1742,14 +1798,14 @@ focus(XEvent *ev) {
                        xseturgency(0);
                } else
                        xw.state &= ~WIN_FOCUSED;
       -        draw(SCREEN_UPDATE);
       +        draw();
        }
        
        char*
       -kmap(KeySym k) {
       +kmap(KeySym k, unsigned int state) {
                int i;
                for(i = 0; i < LEN(key); i++)
       -                if(key[i].k == k)
       +                if(key[i].k == k && (key[i].mask == 0 || key[i].mask & state))
                                return (char*)key[i].s;
                return NULL;
        }
       @@ -1770,7 +1826,7 @@ kpress(XEvent *ev) {
                len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status);
                
                /* 1. custom keys from config.h */
       -        if((customkey = kmap(ksym)))
       +        if((customkey = kmap(ksym, e->state)))
                        ttywrite(customkey, strlen(customkey));
                /* 2. hardcoded (overrides X lookup) */
                else
       @@ -1779,7 +1835,7 @@ kpress(XEvent *ev) {
                        case XK_Down:
                        case XK_Left:
                        case XK_Right:
       -                        sprintf(buf, "\033%c%c", IS_SET(MODE_APPKEYPAD) ? 'O' : '[', "DACB"[ksym - XK_Left]);
       +                        sprintf(buf, "\033%c%c", IS_SET(MODE_APPKEYPAD) ? 'O' : '[', (shift ? "dacb":"DACB")[ksym - XK_Left]);
                                ttywrite(buf, 3);
                                break;
                        case XK_Insert:
       @@ -1817,7 +1873,7 @@ resize(XEvent *e) {
                if(col == term.col && row == term.row)
                        return;
                if(tresize(col, row))
       -                draw(SCREEN_REDRAW);
       +                draw();
                ttyresize(col, row);
                xresize(col, row);
        }
       @@ -1839,7 +1895,7 @@ run(void) {
                        }
                        if(FD_ISSET(cmdfd, &rfd)) {
                                ttyread();
       -                        draw(SCREEN_UPDATE); 
       +                        draw(); 
                        }
                        while(XPending(xw.dpy)) {
                                XNextEvent(xw.dpy, &ev);
 (DIR) diff --git a/st.info b/st.info
       @@ -49,6 +49,10 @@ st| simpleterm,
                kcud1=\E[B,
                kcuf1=\E[C,
                kcuu1=\E[A,
       +        kLFT=\E[d,
       +        kRIT=\E[c,
       +        kind=\E[a,
       +        kri=\E[b,
                kdch1=\E[3~,
                kich1=\E[2~,
                kend=\E[4~,