]> git.armaanb.net Git - st.git/blobdiff - st.c
Merge branch 'master' of ssh://suckless.org/gitrepos/st
[st.git] / st.c
diff --git a/st.c b/st.c
index 39a6836b0b0558f7e1249c427ae750b75a2d5d59..40840ce58f515b6fd115d4f01f1a4116ce2df1e8 100644 (file)
--- a/st.c
+++ b/st.c
@@ -1,4 +1,4 @@
-/* See LICENSE for licence details. */
+/* See LICENSE for license details. */
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -197,14 +197,14 @@ typedef struct {
 } TCursor;
 
 /* CSI Escape sequence structs */
-/* ESC '[' [[ [<priv>] <arg> [;]] <mode>] */
+/* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
 typedef struct {
        char buf[ESC_BUF_SIZ]; /* raw string */
        int len;               /* raw string length */
        char priv;
        int arg[ESC_ARG_SIZ];
        int narg;              /* nb of args */
-       char mode;
+       char mode[2];
 } CSIEscape;
 
 /* STR Escape sequence structs */
@@ -257,6 +257,7 @@ typedef struct {
        int ch; /* char height */
        int cw; /* char width  */
        char state; /* focus, redraw, visible */
+       int cursor; /* cursor style */
 } XWindow;
 
 typedef struct {
@@ -290,7 +291,7 @@ typedef struct {
                int x, y;
        } nb, ne, ob, oe;
 
-       char *clip;
+       char *primary, *clipboard;
        Atom xtarget;
        bool alt;
        struct timespec tclick1;
@@ -312,6 +313,7 @@ typedef struct {
 } Shortcut;
 
 /* function definitions used in config.h */
+static void clipcopy(const Arg *);
 static void clippaste(const Arg *);
 static void numlock(const Arg *);
 static void selpaste(const Arg *);
@@ -357,7 +359,7 @@ static void csidump(void);
 static void csihandle(void);
 static void csiparse(void);
 static void csireset(void);
-static int eschandle(uchar ascii);
+static int eschandle(uchar);
 static void strdump(void);
 static void strhandle(void);
 static void strparse(void);
@@ -404,8 +406,9 @@ static void ttyread(void);
 static void ttyresize(void);
 static void ttysend(char *, size_t);
 static void ttywrite(const char *, size_t);
-static void tstrsequence(uchar c);
+static void tstrsequence(uchar);
 
+static inline ushort sixd_to_16bit(int);
 static void xdraws(char *, Glyph, int, int, int, int);
 static void xhints(void);
 static void xclear(int, int, int, int);
@@ -416,12 +419,11 @@ static int xsetcolorname(int, const char *);
 static int xgeommasktogravity(int);
 static int xloadfont(Font *, FcPattern *);
 static void xloadfonts(char *, double);
-static int xloadfontset(Font *);
 static void xsettitle(char *);
 static void xresettitle(void);
 static void xsetpointermotion(int);
 static void xseturgency(int);
-static void xsetsel(char *);
+static void xsetsel(char *, Time);
 static void xtermclear(int, int, int, int);
 static void xunloadfont(Font *);
 static void xunloadfonts(void);
@@ -447,9 +449,11 @@ static void selinit(void);
 static void selnormalize(void);
 static inline bool selected(int, int);
 static char *getsel(void);
-static void selcopy(void);
+static void selcopy(Time);
 static void selscroll(int, int);
 static void selsnap(int, int *, int *, int);
+static int x2col(int);
+static int y2row(int);
 static void getbuttoninfo(XEvent *);
 static void mousereport(XEvent *);
 
@@ -479,7 +483,11 @@ static void (*handler[LASTEvent])(XEvent *) = {
        [MotionNotify] = bmotion,
        [ButtonPress] = bpress,
        [ButtonRelease] = brelease,
-       [SelectionClear] = selclear,
+/*
+ * Uncomment if you want the selection to disappear when you select something
+ * different in another window.
+ */
+/*     [SelectionClear] = selclear, */
        [SelectionNotify] = selnotify,
        [SelectionRequest] = selrequest,
 };
@@ -634,19 +642,20 @@ utf8validate(long *u, size_t i) {
        return i;
 }
 
-static void
+void
 selinit(void) {
        memset(&sel.tclick1, 0, sizeof(sel.tclick1));
        memset(&sel.tclick2, 0, sizeof(sel.tclick2));
        sel.mode = 0;
        sel.ob.x = -1;
-       sel.clip = NULL;
+       sel.primary = NULL;
+       sel.clipboard = NULL;
        sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
        if(sel.xtarget == None)
                sel.xtarget = XA_STRING;
 }
 
-static int
+int
 x2col(int x) {
        x -= borderpx;
        x /= xw.cw;
@@ -654,7 +663,7 @@ x2col(int x) {
        return LIMIT(x, 0, term.col-1);
 }
 
-static int
+int
 y2row(int y) {
        y -= borderpx;
        y /= xw.ch;
@@ -662,7 +671,7 @@ y2row(int y) {
        return LIMIT(y, 0, term.row-1);
 }
 
-static int tlinelen(int y) {
+int tlinelen(int y) {
        int i = term.col;
 
        if(term.line[y][i - 1].mode & ATTR_WRAP)
@@ -674,7 +683,7 @@ static int tlinelen(int y) {
        return i;
 }
 
-static void
+void
 selnormalize(void) {
        int i;
 
@@ -701,7 +710,7 @@ selnormalize(void) {
                sel.ne.x = term.col - 1;
 }
 
-static inline bool
+bool
 selected(int x, int y) {
        if(sel.type == SEL_RECTANGULAR)
                return BETWEEN(y, sel.nb.y, sel.ne.y)
@@ -935,7 +944,7 @@ getsel(void) {
        ptr = str = xmalloc(bufsize);
 
        /* append every set & selected glyph to the selection */
-       for(y = sel.nb.y; y < sel.ne.y + 1; y++) {
+       for(y = sel.nb.y; y <= sel.ne.y; y++) {
                linelen = tlinelen(y);
 
                if(sel.type == SEL_RECTANGULAR) {
@@ -975,8 +984,8 @@ getsel(void) {
 }
 
 void
-selcopy(void) {
-       xsetsel(getsel());
+selcopy(Time t) {
+       xsetsel(getsel(), t);
 }
 
 void
@@ -985,12 +994,17 @@ selnotify(XEvent *e) {
        int format;
        uchar *data, *last, *repl;
        Atom type;
+       XSelectionEvent *xsev;
 
        ofs = 0;
+       xsev = &e->xselection;
+       if (xsev->property == None)
+           return;
        do {
-               if(XGetWindowProperty(xw.dpy, xw.win, XA_PRIMARY, ofs, BUFSIZ/4,
-                                       False, AnyPropertyType, &type, &format,
-                                       &nitems, &rem, &data)) {
+               if(XGetWindowProperty(xw.dpy, xw.win, xsev->property, ofs,
+                                       BUFSIZ/4, False, AnyPropertyType,
+                                       &type, &format, &nitems, &rem,
+                                       &data)) {
                        fprintf(stderr, "Clipboard allocation failed\n");
                        return;
                }
@@ -1025,12 +1039,26 @@ selpaste(const Arg *dummy) {
                        xw.win, CurrentTime);
 }
 
+void
+clipcopy(const Arg *dummy) {
+       Atom clipboard;
+
+       if(sel.clipboard != NULL)
+               free(sel.clipboard);
+
+       if(sel.primary != NULL) {
+               sel.clipboard = xstrdup(sel.primary);
+               clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+               XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
+       }
+}
+
 void
 clippaste(const Arg *dummy) {
        Atom clipboard;
 
        clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
-       XConvertSelection(xw.dpy, clipboard, sel.xtarget, XA_PRIMARY,
+       XConvertSelection(xw.dpy, clipboard, sel.xtarget, clipboard,
                        xw.win, CurrentTime);
 }
 
@@ -1046,7 +1074,8 @@ void
 selrequest(XEvent *e) {
        XSelectionRequestEvent *xsre;
        XSelectionEvent xev;
-       Atom xa_targets, string;
+       Atom xa_targets, string, clipboard;
+       char *seltext;
 
        xsre = (XSelectionRequestEvent *) e;
        xev.type = SelectionNotify;
@@ -1054,6 +1083,9 @@ selrequest(XEvent *e) {
        xev.selection = xsre->selection;
        xev.target = xsre->target;
        xev.time = xsre->time;
+        if (xsre->property == None)
+            xsre->property = xsre->target;
+
        /* reject */
        xev.property = None;
 
@@ -1065,11 +1097,29 @@ selrequest(XEvent *e) {
                                XA_ATOM, 32, PropModeReplace,
                                (uchar *) &string, 1);
                xev.property = xsre->property;
-       } else if(xsre->target == sel.xtarget && sel.clip != NULL) {
-               XChangeProperty(xsre->display, xsre->requestor, xsre->property,
-                               xsre->target, 8, PropModeReplace,
-                               (uchar *) sel.clip, strlen(sel.clip));
-               xev.property = xsre->property;
+       } else if(xsre->target == sel.xtarget || xsre->target == XA_STRING) {
+               /*
+                * xith XA_STRING non ascii characters may be incorrect in the
+                * requestor. It is not our problem, use utf8.
+                */
+               clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+               if(xsre->selection == XA_PRIMARY) {
+                       seltext = sel.primary;
+               } else if(xsre->selection == clipboard) {
+                       seltext = sel.clipboard;
+               } else {
+                       fprintf(stderr,
+                               "Unhandled clipboard selection 0x%lx\n",
+                               xsre->selection);
+                       return;
+               }
+               if(seltext != NULL) {
+                       XChangeProperty(xsre->display, xsre->requestor,
+                                       xsre->property, xsre->target,
+                                       8, PropModeReplace,
+                                       (uchar *)seltext, strlen(seltext));
+                       xev.property = xsre->property;
+               }
        }
 
        /* all done, send a notification to the listener */
@@ -1078,10 +1128,13 @@ selrequest(XEvent *e) {
 }
 
 void
-xsetsel(char *str) {
-       free(sel.clip);
-       sel.clip = str;
-       XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, CurrentTime);
+xsetsel(char *str, Time t) {
+       free(sel.primary);
+       sel.primary = str;
+
+       XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);
+        if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)
+            selclear(0);
 }
 
 void
@@ -1098,7 +1151,7 @@ brelease(XEvent *e) {
                        selclear(NULL);
                } else {
                        getbuttoninfo(e);
-                       selcopy();
+                       selcopy(e->xbutton.time);
                }
                sel.mode = 0;
                tsetdirt(sel.nb.y, sel.ne.y);
@@ -1502,7 +1555,8 @@ csiparse(void) {
                        break;
                p++;
        }
-       csiescseq.mode = *p;
+       csiescseq.mode[0] = *p++;
+       csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0';
 }
 
 /* for absolute user moves, when decom is set */
@@ -1522,11 +1576,9 @@ tmoveto(int x, int y) {
                miny = 0;
                maxy = term.row - 1;
        }
-       LIMIT(x, 0, term.col-1);
-       LIMIT(y, miny, maxy);
        term.c.state &= ~CURSOR_WRAPNEXT;
-       term.c.x = x;
-       term.c.y = y;
+       term.c.x = LIMIT(x, 0, term.col-1);
+       term.c.y = LIMIT(y, miny, maxy);
 }
 
 void
@@ -1940,7 +1992,7 @@ csihandle(void) {
        char buf[40];
        int len;
 
-       switch(csiescseq.mode) {
+       switch(csiescseq.mode[0]) {
        default:
        unknown:
                fprintf(stderr, "erresc: unknown csi ");
@@ -2128,6 +2180,19 @@ csihandle(void) {
        case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */
                tcursor(CURSOR_LOAD);
                break;
+       case ' ':
+               switch (csiescseq.mode[1]) {
+                       case 'q': /* DECSCUSR -- Set Cursor Style */
+                               DEFAULT(csiescseq.arg[0], 1);
+                               if (!BETWEEN(csiescseq.arg[0], 0, 6)) {
+                                       goto unknown;
+                               }
+                               xw.cursor = csiescseq.arg[0];
+                               break;
+                       default:
+                               goto unknown;
+               }
+               break;
        }
 }
 
@@ -2212,12 +2277,23 @@ strhandle(void) {
 
 void
 strparse(void) {
+       int c;
        char *p = strescseq.buf;
 
        strescseq.narg = 0;
        strescseq.buf[strescseq.len] = '\0';
-       while(p && strescseq.narg < STR_ARG_SIZ)
-               strescseq.args[strescseq.narg++] = strsep(&p, ";");
+
+       if(*p == '\0')
+               return;
+
+       while(strescseq.narg < STR_ARG_SIZ) {
+               strescseq.args[strescseq.narg++] = p;
+               while((c = *p) != ';' && c != '\0')
+                       ++p;
+               if(c == '\0')
+                       return;
+               *p++ = '\0';
+       }
 }
 
 void
@@ -2319,7 +2395,7 @@ tputtab(int n) {
                        for(--x; x > 0 && !term.tabs[x]; --x)
                                /* nothing */ ;
        }
-       tmoveto(x, term.c.y);
+       term.c.x = LIMIT(x, 0, term.col-1);
 }
 
 void
@@ -2373,21 +2449,19 @@ tdectest(char c) {
 
 void
 tstrsequence(uchar c) {
-       if (c & 0x80) {
-               switch (c) {
-               case 0x90:   /* DCS -- Device Control String */
-                       c = 'P';
-                       break;
-               case 0x9f:   /* APC -- Application Program Command */
-                       c = '_';
-                       break;
-               case 0x9e:   /* PM -- Privacy Message */
-                       c = '^';
-                       break;
-               case 0x9d:   /* OSC -- Operating System Command */
-                       c = ']';
-                       break;
-               }
+       switch (c) {
+       case 0x90:   /* DCS -- Device Control String */
+               c = 'P';
+               break;
+       case 0x9f:   /* APC -- Application Program Command */
+               c = '_';
+               break;
+       case 0x9e:   /* PM -- Privacy Message */
+               c = '^';
+               break;
+       case 0x9d:   /* OSC -- Operating System Command */
+               c = ']';
+               break;
        }
        strreset();
        strescseq.type = c;
@@ -2577,7 +2651,6 @@ tputc(char *c, int len) {
                        c = "\357\277\275";     /* UTF_INVALID */
                        width = 1;
                }
-               control = ISCONTROLC1(unicodep);
                ascii = unicodep;
        }
 
@@ -2592,7 +2665,7 @@ tputc(char *c, int len) {
         * character.
         */
        if(term.esc & ESC_STR) {
-               if(width == 1 &&
+               if(len == 1 &&
                   (ascii == '\a' || ascii == 030 ||
                    ascii == 032  || ascii == 033 ||
                    ISCONTROLC1(unicodep))) {
@@ -2697,7 +2770,6 @@ tresize(int col, int row) {
        int i;
        int minrow = MIN(row, term.row);
        int mincol = MIN(col, term.col);
-       int slide = term.c.y - row + 1;
        bool *bp;
        TCursor c;
 
@@ -2707,21 +2779,17 @@ tresize(int col, int row) {
                return;
        }
 
-       /* free unneeded rows */
-       i = 0;
-       if(slide > 0) {
-               /*
-                * slide screen to keep cursor where we expect it -
-                * tscrollup would work here, but we can optimize to
-                * memmove because we're freeing the earlier lines
-                */
-               for(/* i = 0 */; i < slide; i++) {
-                       free(term.line[i]);
-                       free(term.alt[i]);
-               }
-               memmove(term.line, term.line + slide, row * sizeof(Line));
-               memmove(term.alt, term.alt + slide, row * sizeof(Line));
+       /*
+        * slide screen to keep cursor where we expect it -
+        * tscrollup would work here, but we can optimize to
+        * memmove because we're freeing the earlier lines
+        */
+       for(i = 0; i <= term.c.y - row; i++) {
+               free(term.line[i]);
+               free(term.alt[i]);
        }
+       memmove(term.line, term.line + i, row * sizeof(Line));
+       memmove(term.alt, term.alt + i, row * sizeof(Line));
        for(i += row; i < term.row; i++) {
                free(term.line[i]);
                free(term.alt[i]);
@@ -2787,7 +2855,7 @@ xresize(int col, int row) {
        xclear(0, 0, xw.w, xw.h);
 }
 
-static inline ushort
+ushort
 sixd_to_16bit(int x) {
        return x == 0 ? 0 : 0x3737 + 0x2828 * x;
 }
@@ -3043,15 +3111,6 @@ xloadfonts(char *fontstr, double fontsize) {
        FcPatternDestroy(pattern);
 }
 
-int
-xloadfontset(Font *f) {
-       FcResult result;
-
-       if(!(f->set = FcFontSort(0, f->pattern, FcTrue, 0, &result)))
-               return 1;
-       return 0;
-}
-
 void
 xunloadfont(Font *f) {
        XftFontClose(xw.dpy, f->match);
@@ -3399,7 +3458,8 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                /* Nothing was found. */
                if(i >= frclen) {
                        if(!font->set)
-                               xloadfontset(font);
+                               font->set = FcFontSort(0, font->pattern,
+                                                      FcTrue, 0, &fcres);
                        fcsets[0] = font->set;
 
                        /*
@@ -3508,16 +3568,36 @@ xdrawcursor(void) {
 
        /* draw the new one */
        if(xw.state & WIN_FOCUSED) {
-               if(IS_SET(MODE_REVERSE)) {
-                       g.mode |= ATTR_REVERSE;
-                       g.fg = defaultcs;
-                       g.bg = defaultfg;
-               }
+               switch (xw.cursor) {
+                       case 0: /* Blinking Block */
+                       case 1: /* Blinking Block (Default) */
+                       case 2: /* Steady Block */
+                               if(IS_SET(MODE_REVERSE)) {
+                                               g.mode |= ATTR_REVERSE;
+                                               g.fg = defaultcs;
+                                               g.bg = defaultfg;
+                                       }
 
-               sl = utf8len(g.c);
-               width = (term.line[term.c.y][curx].mode & ATTR_WIDE)\
-                       ? 2 : 1;
-               xdraws(g.c, g, term.c.x, term.c.y, width, sl);
+                               sl = utf8len(g.c);
+                               width = (term.line[term.c.y][curx].mode & ATTR_WIDE)\
+                                       ? 2 : 1;
+                               xdraws(g.c, g, term.c.x, term.c.y, width, sl);
+                               break;
+                       case 3: /* Blinking Underline */
+                       case 4: /* Steady Underline */
+                               XftDrawRect(xw.draw, &dc.col[defaultcs],
+                                               borderpx + curx * xw.cw,
+                                               borderpx + (term.c.y + 1) * xw.ch - cursorthickness,
+                                               xw.cw, cursorthickness);
+                               break;
+                       case 5: /* Blinking bar */
+                       case 6: /* Steady bar */
+                               XftDrawRect(xw.draw, &dc.col[defaultcs],
+                                               borderpx + curx * xw.cw,
+                                               borderpx + term.c.y * xw.ch,
+                                               cursorthickness, xw.ch);
+                               break;
+               }
        } else {
                XftDrawRect(xw.draw, &dc.col[defaultcs],
                                borderpx + curx * xw.cw,
@@ -3682,7 +3762,7 @@ focus(XEvent *ev) {
        }
 }
 
-static inline bool
+bool
 match(uint mask, uint state) {
        return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
 }
@@ -3833,17 +3913,13 @@ run(void) {
        long deltatime;
 
        /* Waiting for window mapping */
-       while(1) {
+       do {
                XNextEvent(xw.dpy, &ev);
-               if(XFilterEvent(&ev, None))
-                       continue;
                if(ev.type == ConfigureNotify) {
                        w = ev.xconfigure.width;
                        h = ev.xconfigure.height;
-               } else if(ev.type == MapNotify) {
-                       break;
                }
-       }
+       } while(ev.type != MapNotify);
 
        ttynew();
        cresize(w, h);
@@ -3937,11 +4013,11 @@ usage(void) {
 
 int
 main(int argc, char *argv[]) {
-       char *titles;
        uint cols = 80, rows = 24;
 
        xw.l = xw.t = 0;
        xw.isfixed = False;
+       xw.cursor = 0;
 
        ARGBEGIN {
        case 'a':
@@ -3954,10 +4030,8 @@ main(int argc, char *argv[]) {
                /* eat all remaining arguments */
                if(argc > 1) {
                        opt_cmd = &argv[1];
-                       if(argv[1] != NULL && opt_title == NULL) {
-                               titles = xstrdup(argv[1]);
-                               opt_title = basename(titles);
-                       }
+                       if(argv[1] != NULL && opt_title == NULL)
+                               opt_title = basename(xstrdup(argv[1]));
                }
                goto run;
        case 'f':
@@ -3987,7 +4061,7 @@ main(int argc, char *argv[]) {
 run:
        setlocale(LC_CTYPE, "");
        XSetLocaleModifiers("");
-       tnew(cols? cols : 1, rows? rows : 1);
+       tnew(MAX(cols, 1), MAX(rows, 1));
        xinit();
        selinit();
        run();