JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
People, learn to keep to styles. Thanks.
[st.git] / st.c
diff --git a/st.c b/st.c
index 0fa0c86..84f9abb 100644 (file)
--- a/st.c
+++ b/st.c
@@ -27,6 +27,7 @@
 #include <X11/keysym.h>
 #include <X11/Xft/Xft.h>
 #include <fontconfig/fontconfig.h>
+#include <wchar.h>
 
 #include "arg.h"
 
@@ -96,6 +97,8 @@ enum glyph_attribute {
        ATTR_ITALIC    = 16,
        ATTR_BLINK     = 32,
        ATTR_WRAP      = 64,
+       ATTR_WIDE      = 128,
+       ATTR_WDUMMY    = 256,
 };
 
 enum cursor_movement {
@@ -129,6 +132,7 @@ enum term_mode {
        MODE_FOCUS       = 65536,
        MODE_MOUSEX10    = 131072,
        MODE_MOUSEMANY   = 262144,
+       MODE_BRCKTPASTE  = 524288,
        MODE_MOUSE       = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
                          |MODE_MOUSEMANY,
 };
@@ -165,7 +169,7 @@ typedef unsigned short ushort;
 
 typedef struct {
        char c[UTF_SIZ]; /* character code */
-       uchar mode;      /* attribute flags */
+       ushort mode;      /* attribute flags */
        ulong fg;        /* foreground  */
        ulong bg;        /* background  */
 } Glyph;
@@ -719,8 +723,13 @@ selsnap(int mode, int *x, int *y, int direction) {
                                }
                        }
 
+                       if(term.line[*y][*x+direction].mode & ATTR_WDUMMY) {
+                               *x += direction;
+                               continue;
+                       }
+
                        if(strchr(worddelimiters,
-                                       term.line[*y][*x + direction].c[0])) {
+                                       term.line[*y][*x+direction].c[0])) {
                                break;
                        }
 
@@ -815,18 +824,23 @@ mousereport(XEvent *e) {
                button = oldbutton + 32;
                ox = x;
                oy = y;
-       } else if(!IS_SET(MODE_MOUSESGR)
-                       && (e->xbutton.type == ButtonRelease
-                               || button == AnyButton)) {
-               button = 3;
        } else {
-               button -= Button1;
-               if(button >= 3)
-                       button += 64 - 3;
+               if(!IS_SET(MODE_MOUSESGR) && e->xbutton.type == ButtonRelease) {
+                       button = 3;
+               } else {
+                       button -= Button1;
+                       if(button >= 3)
+                               button += 64 - 3;
+               }
                if(e->xbutton.type == ButtonPress) {
                        oldbutton = button;
                        ox = x;
                        oy = y;
+               } else if(e->xbutton.type == ButtonRelease) {
+                       oldbutton = 3;
+                       /* MODE_MOUSEX10: no button release reporting */
+                       if(IS_SET(MODE_MOUSEX10))
+                               return;
                }
        }
 
@@ -843,8 +857,7 @@ mousereport(XEvent *e) {
                                e->xbutton.type == ButtonRelease ? 'm' : 'M');
        } else if(x < 223 && y < 223) {
                len = snprintf(buf, sizeof(buf), "\033[M%c%c%c",
-                               IS_SET(MODE_MOUSEX10)? button-1 : 32+button,
-                               32+x+1, 32+y+1);
+                               32+button, 32+x+1, 32+y+1);
        } else {
                return;
        }
@@ -932,7 +945,7 @@ selcopy(void) {
                                /* nothing */;
 
                        for(x = 0; gp <= last; x++, ++gp) {
-                               if(!selected(x, y))
+                               if(!selected(x, y) || (gp->mode & ATTR_WDUMMY))
                                        continue;
 
                                size = utf8size(gp->c);
@@ -949,7 +962,7 @@ selcopy(void) {
                         * st.
                         * FIXME: Fix the computer world.
                         */
-                       if(y < sel.ne.y && !((gp-1)->mode & ATTR_WRAP))
+                       if(y < sel.ne.y && x > 0 && !((gp-1)->mode & ATTR_WRAP))
                                *ptr++ = '\n';
 
                        /*
@@ -1001,7 +1014,11 @@ selnotify(XEvent *e) {
                        *repl++ = '\r';
                }
 
+               if(IS_SET(MODE_BRCKTPASTE))
+                       ttywrite("\033[200~", 6);
                ttywrite((const char *)data, nitems * format / 8);
+               if(IS_SET(MODE_BRCKTPASTE))
+                       ttywrite("\033[201~", 6);
                XFree(data);
                /* number of 32-bit chunks returned */
                ofs += nitems * format / 32;
@@ -1325,13 +1342,14 @@ tfulldirt(void) {
 
 void
 tcursor(int mode) {
-       static TCursor c;
+       static TCursor c[2];
+       bool alt = IS_SET(MODE_ALTSCREEN);
 
        if(mode == CURSOR_SAVE) {
-               c = term.c;
+               c[alt] = term.c;
        } else if(mode == CURSOR_LOAD) {
-               term.c = c;
-               tmoveto(c.x, c.y);
+               term.c = c[alt];
+               tmoveto(c[alt].x, c[alt].y);
        }
 }
 
@@ -1533,6 +1551,16 @@ tsetchar(char *c, Glyph *attr, int x, int y) {
                }
        }
 
+       if(term.line[y][x].mode & ATTR_WIDE) {
+               if(x+1 < term.col) {
+                       term.line[y][x+1].c[0] = ' ';
+                       term.line[y][x+1].mode &= ~ATTR_WDUMMY;
+               }
+       } else if(term.line[y][x].mode & ATTR_WDUMMY) {
+               term.line[y][x-1].c[0] = ' ';
+               term.line[y][x-1].mode &= ~ATTR_WIDE;
+       }
+
        term.dirty[y] = 1;
        term.line[y][x] = *attr;
        memcpy(term.line[y][x].c, c, UTF_SIZ);
@@ -1827,12 +1855,12 @@ tsetmode(bool priv, bool set, int *args, int narg) {
                        case 1034:
                                MODBIT(term.mode, set, MODE_8BIT);
                                break;
-                       case 1049: /* = 1047 and 1048 */
-                       case 47:
+                       case 1049: /* swap screen & set/restore cursor as xterm */
+                               tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
+                       case 47: /* swap screen */
                        case 1047:
                                if (!allowaltscreen)
                                        break;
-
                                alt = IS_SET(MODE_ALTSCREEN);
                                if(alt) {
                                        tclearregion(0, 0, term.col-1,
@@ -1846,6 +1874,9 @@ tsetmode(bool priv, bool set, int *args, int narg) {
                        case 1048:
                                tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
                                break;
+                       case 2004: /* 2004: bracketed paste mode */
+                               MODBIT(term.mode, set, MODE_BRCKTPASTE);
+                               break;
                        /* Not implemented mouse modes. See comments there. */
                        case 1001: /* mouse highlight mode; can hang the
                                      terminal by design when implemented. */
@@ -1889,6 +1920,9 @@ tsetmode(bool priv, bool set, int *args, int narg) {
 
 void
 csihandle(void) {
+       char buf[40];
+       int len;
+
        switch(csiescseq.mode) {
        default:
        unknown:
@@ -2037,6 +2071,13 @@ csihandle(void) {
        case 'm': /* SGR -- Terminal attribute (color) */
                tsetattr(csiescseq.arg, csiescseq.narg);
                break;
+       case 'n': /* DSR – Device Status Report (cursor position) */
+               if (csiescseq.arg[0] == 6) {
+                       len = snprintf(buf, sizeof(buf),"\033[%i;%iR",
+                                       term.c.y+1, term.c.x+1);
+                       ttywrite(buf, len);
+                       break;
+               }
        case 'r': /* DECSTBM -- Set Scrolling Region */
                if(csiescseq.priv) {
                        goto unknown;
@@ -2222,6 +2263,15 @@ void
 tputc(char *c, int len) {
        uchar ascii = *c;
        bool control = ascii < '\x20' || ascii == 0177;
+       long u8char;
+       int width;
+
+       if(len == 1) {
+               width = 1;
+       } else {
+               utf8decode(c, &u8char);
+               width = wcwidth(u8char);
+       }
 
        if(iofd != -1) {
                if(xwrite(iofd, c, len) < 0) {
@@ -2293,6 +2343,8 @@ tputc(char *c, int len) {
                case '\a':   /* BEL */
                        if(!(xw.state & WIN_FOCUSED))
                                xseturgency(1);
+                       if (bellvolume)
+                               XBell(xw.dpy, bellvolume);
                        return;
                case '\033': /* ESC */
                        csireset();
@@ -2469,9 +2521,20 @@ tputc(char *c, int len) {
                        (term.col - term.c.x - 1) * sizeof(Glyph));
        }
 
+       if(term.c.x+width > term.col)
+               tnewline(1);
+
        tsetchar(c, &term.c.attr, term.c.x, term.c.y);
-       if(term.c.x+1 < term.col) {
-               tmoveto(term.c.x+1, term.c.y);
+
+       if(width == 2) {
+               term.line[term.c.y][term.c.x].mode |= ATTR_WIDE;
+               if(term.c.x+1 < term.col) {
+                       term.line[term.c.y][term.c.x+1].c[0] = '\0';
+                       term.line[term.c.y][term.c.x+1].mode = ATTR_WDUMMY;
+               }
+       }
+       if(term.c.x+width < term.col) {
+               tmoveto(term.c.x+width, term.c.y);
        } else {
                term.c.state |= CURSOR_WRAPNEXT;
        }
@@ -3173,7 +3236,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                                xp, winy + frc[i].font->ascent,
                                (FcChar8 *)u8c, u8cblen);
 
-               xp += xw.cw;
+               xp += xw.cw * wcwidth(u8char);
        }
 
        /*
@@ -3193,18 +3256,27 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 void
 xdrawcursor(void) {
        static int oldx = 0, oldy = 0;
-       int sl;
+       int sl, width, curx;
        Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs};
 
        LIMIT(oldx, 0, term.col-1);
        LIMIT(oldy, 0, term.row-1);
 
+       curx = term.c.x;
+
+       /* adjust position if in dummy */
+       if(term.line[oldy][oldx].mode & ATTR_WDUMMY)
+               oldx--;
+       if(term.line[term.c.y][curx].mode & ATTR_WDUMMY)
+               curx--;
+
        memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ);
 
        /* remove the old cursor */
        sl = utf8size(term.line[oldy][oldx].c);
+       width = (term.line[oldy][oldx].mode & ATTR_WIDE)? 2 : 1;
        xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx,
-                       oldy, 1, sl);
+                       oldy, width, sl);
 
        /* draw the new one */
        if(!(IS_SET(MODE_HIDE))) {
@@ -3216,26 +3288,28 @@ xdrawcursor(void) {
                        }
 
                        sl = utf8size(g.c);
-                       xdraws(g.c, g, term.c.x, term.c.y, 1, sl);
+                       width = (term.line[term.c.y][curx].mode & ATTR_WIDE)\
+                               ? 2 : 1;
+                       xdraws(g.c, g, term.c.x, term.c.y, width, sl);
                } else {
                        XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                       borderpx + term.c.x * xw.cw,
+                                       borderpx + curx * xw.cw,
                                        borderpx + term.c.y * xw.ch,
                                        xw.cw - 1, 1);
                        XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                       borderpx + term.c.x * xw.cw,
+                                       borderpx + curx * xw.cw,
                                        borderpx + term.c.y * xw.ch,
                                        1, xw.ch - 1);
                        XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                       borderpx + (term.c.x + 1) * xw.cw - 1,
+                                       borderpx + (curx + 1) * xw.cw - 1,
                                        borderpx + term.c.y * xw.ch,
                                        1, xw.ch - 1);
                        XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                       borderpx + term.c.x * xw.cw,
+                                       borderpx + curx * xw.cw,
                                        borderpx + (term.c.y + 1) * xw.ch - 1,
                                        xw.cw, 1);
                }
-               oldx = term.c.x, oldy = term.c.y;
+               oldx = curx, oldy = term.c.y;
        }
 }
 
@@ -3284,6 +3358,7 @@ drawregion(int x1, int y1, int x2, int y2) {
        Glyph base, new;
        char buf[DRAW_BUF_SIZ];
        bool ena_sel = sel.ob.x != -1;
+       long u8char;
 
        if(sel.alt ^ IS_SET(MODE_ALTSCREEN))
                ena_sel = 0;
@@ -3301,6 +3376,8 @@ drawregion(int x1, int y1, int x2, int y2) {
                ic = ib = ox = 0;
                for(x = x1; x < x2; x++) {
                        new = term.line[y][x];
+                       if(new.mode == ATTR_WDUMMY)
+                               continue;
                        if(ena_sel && selected(x, y))
                                new.mode ^= ATTR_REVERSE;
                        if(ib > 0 && (ATTRCMP(base, new)
@@ -3313,10 +3390,10 @@ drawregion(int x1, int y1, int x2, int y2) {
                                base = new;
                        }
 
-                       sl = utf8size(new.c);
+                       sl = utf8decode(new.c, &u8char);
                        memcpy(buf+ib, new.c, sl);
                        ib += sl;
-                       ++ic;
+                       ic += (new.mode & ATTR_WIDE)? 2 : 1;
                }
                if(ib > 0)
                        xdraws(buf, base, ox, y, ic, ib);