]> git.armaanb.net Git - st.git/blobdiff - st.c
Set default values of stty according to the man page
[st.git] / st.c
diff --git a/st.c b/st.c
index 274ac5d768fe727cbb15204aabdc20a5f983985f..256f8f5912026027715c6fe7494fefa7757c4e83 100644 (file)
--- a/st.c
+++ b/st.c
@@ -62,26 +62,28 @@ char *argv0;
 #define XK_SWITCH_MOD (1<<13)
 
 /* macros */
-#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 BETWEEN(x, a, b)  ((a) <= (x) && (x) <= (b))
-#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
-#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
-#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
-#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL)
-#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)) != 0)
-#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (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))
-#define TRUERED(x)       (((x) & 0xff0000) >> 8)
-#define TRUEGREEN(x)     (((x) & 0xff00))
-#define TRUEBLUE(x)      (((x) & 0xff) << 8)
+#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 BETWEEN(x, a, b)       ((a) <= (x) && (x) <= (b))
+#define ISCONTROLC0(c)         (BETWEEN(c, 0, 0x1f) || (c) == '\177')
+#define ISCONTROLC1(c)         (BETWEEN(c, 0x80, 0x9f))
+#define ISCONTROL(c)           (ISCONTROLC0(c) || ISCONTROLC1(c))
+#define ISDELIM(u)             (utf8strchr(worddelimiters, u) != NULL)
+#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)) != 0)
+#define TIMEDIFF(t1, t2)       ((t1.tv_sec-t2.tv_sec)*1000 + \
+                               (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))
+#define TRUERED(x)             (((x) & 0xff0000) >> 8)
+#define TRUEGREEN(x)           (((x) & 0xff00))
+#define TRUEBLUE(x)            (((x) & 0xff) << 8)
 
 
 enum glyph_attribute {
@@ -452,6 +454,7 @@ static void focus(XEvent *);
 static void brelease(XEvent *);
 static void bpress(XEvent *);
 static void bmotion(XEvent *);
+static void propnotify(XEvent *);
 static void selnotify(XEvent *);
 static void selclear(XEvent *);
 static void selrequest(XEvent *);
@@ -500,6 +503,11 @@ static void (*handler[LASTEvent])(XEvent *) = {
  */
 /*     [SelectionClear] = selclear, */
        [SelectionNotify] = selnotify,
+/*
+ * PropertyNotify is only turned on when there is some INCR transfer happening
+ * for the selection retrieval.
+ */
+       [PropertyNotify] = propnotify,
        [SelectionRequest] = selrequest,
 };
 
@@ -553,14 +561,16 @@ ssize_t
 xwrite(int fd, const char *s, size_t len)
 {
        size_t aux = len;
+       ssize_t r;
 
        while (len > 0) {
-               ssize_t r = write(fd, s, len);
+               r = write(fd, s, len);
                if (r < 0)
                        return r;
                len -= r;
                s += r;
        }
+
        return aux;
 }
 
@@ -614,6 +624,7 @@ utf8decode(char *c, Rune *u, size_t clen)
                return 0;
        *u = udecoded;
        utf8validate(u, len);
+
        return len;
 }
 
@@ -623,6 +634,7 @@ utf8decodebyte(char c, size_t *i)
        for (*i = 0; *i < LEN(utfmask); ++(*i))
                if (((uchar)c & utfmask[*i]) == utfbyte[*i])
                        return (uchar)c & ~utfmask[*i];
+
        return 0;
 }
 
@@ -634,11 +646,13 @@ utf8encode(Rune u, char *c)
        len = utf8validate(&u, 0);
        if (len > UTF_SIZ)
                return 0;
+
        for (i = len - 1; i != 0; --i) {
                c[i] = utf8encodebyte(u, 0);
                u >>= 6;
        }
        c[0] = utf8encodebyte(u, len);
+
        return len;
 }
 
@@ -661,6 +675,7 @@ utf8strchr(char *s, Rune u)
                if (r == u)
                        return &(s[i]);
        }
+
        return NULL;
 }
 
@@ -671,6 +686,7 @@ utf8validate(Rune *u, size_t i)
                *u = UTF_INVALID;
        for (i = 1; *u > utfmax[i]; ++i)
                ;
+
        return i;
 }
 
@@ -1028,21 +1044,43 @@ selcopy(Time t)
        xsetsel(getsel(), t);
 }
 
+void
+propnotify(XEvent *e)
+{
+       XPropertyEvent *xpev;
+       Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+
+       xpev = &e->xproperty;
+       if (xpev->state == PropertyNewValue &&
+                       (xpev->atom == XA_PRIMARY ||
+                        xpev->atom == clipboard)) {
+               selnotify(e);
+       }
+}
+
 void
 selnotify(XEvent *e)
 {
        ulong nitems, ofs, rem;
        int format;
        uchar *data, *last, *repl;
-       Atom type;
-       XSelectionEvent *xsev;
+       Atom type, incratom, property;
+
+       incratom = XInternAtom(xw.dpy, "INCR", 0);
 
        ofs = 0;
-       xsev = &e->xselection;
-       if (xsev->property == None)
-           return;
+       if (e->type == SelectionNotify) {
+               property = e->xselection.property;
+       } else if(e->type == PropertyNotify) {
+               property = e->xproperty.atom;
+       } else {
+               return;
+       }
+       if (property == None)
+               return;
+
        do {
-               if (XGetWindowProperty(xw.dpy, xw.win, xsev->property, ofs,
+               if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
                                        BUFSIZ/4, False, AnyPropertyType,
                                        &type, &format, &nitems, &rem,
                                        &data)) {
@@ -1050,6 +1088,35 @@ selnotify(XEvent *e)
                        return;
                }
 
+               if (e->type == PropertyNotify && nitems == 0 && rem == 0) {
+                       /*
+                        * If there is some PropertyNotify with no data, then
+                        * this is the signal of the selection owner that all
+                        * data has been transferred. We won't need to receive
+                        * PropertyNotify events anymore.
+                        */
+                       MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask);
+                       XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
+                                       &xw.attrs);
+               }
+
+               if (type == incratom) {
+                       /*
+                        * Activate the PropertyNotify events so we receive
+                        * when the selection owner does send us the next
+                        * chunk of data.
+                        */
+                       MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask);
+                       XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask,
+                                       &xw.attrs);
+
+                       /*
+                        * Deleting the property is the transfer start signal.
+                        */
+                       XDeleteProperty(xw.dpy, xw.win, (int)property);
+                       continue;
+               }
+
                /*
                 * As seen in getsel:
                 * Line endings are inconsistent in the terminal and GUI world
@@ -1072,6 +1139,13 @@ selnotify(XEvent *e)
                /* number of 32-bit chunks returned */
                ofs += nitems * format / 32;
        } while (rem > 0);
+
+       /*
+        * Deleting the property again tells the selection owner to send the
+        * next data chunk in the property.
+        */
+       if (e->type == PropertyNotify)
+               XDeleteProperty(xw.dpy, xw.win, (int)property);
 }
 
 void
@@ -1256,9 +1330,8 @@ execsh(void)
                        die("who are you?\n");
        }
 
-       if (!(sh = getenv("SHELL"))) {
+       if ((sh = getenv("SHELL")) == NULL)
                sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
-       }
 
        if (opt_cmd)
                prog = opt_cmd[0];
@@ -1416,8 +1489,58 @@ ttyread(void)
 void
 ttywrite(const char *s, size_t n)
 {
-       if (xwrite(cmdfd, s, n) == -1)
-               die("write error on tty: %s\n", strerror(errno));
+       fd_set wfd;
+       struct timespec tv;
+       ssize_t r;
+
+       /*
+        * Remember that we are using a pty, which might be a modem line.
+        * Writing too much will clog the line. That's why we are doing this
+        * dance.
+        * FIXME: Migrate the world to Plan 9.
+        */
+       while (n > 0) {
+               FD_ZERO(&wfd);
+               FD_SET(cmdfd, &wfd);
+               tv.tv_sec = 0;
+               tv.tv_nsec = 0;
+
+               /* Check if we can write. */
+               if (pselect(cmdfd+1, NULL, &wfd, NULL, &tv, NULL) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       die("select failed: %s\n", strerror(errno));
+               }
+               if(!FD_ISSET(cmdfd, &wfd)) {
+                       /* No, then free some buffer space. */
+                       ttyread();
+               } else {
+                       /*
+                        * Only write 256 bytes at maximum. This seems to be a
+                        * reasonable value for a serial line. Bigger values
+                        * might clog the I/O.
+                        */
+                       r = write(cmdfd, s, (n < 256)? n : 256);
+                       if (r < 0) {
+                               die("write error on tty: %s\n",
+                                               strerror(errno));
+                       }
+                       if (r < n) {
+                               /*
+                                * We weren't able to write out everything.
+                                * This means the buffer is getting full
+                                * again. Empty it.
+                                */
+                               if (n < 256)
+                                       ttyread();
+                               n -= r;
+                               s += r;
+                       } else {
+                               /* All bytes have been written. */
+                               break;
+                       }
+               }
+       }
 }
 
 void
@@ -2651,18 +2774,37 @@ tcontrolcode(uchar ascii)
        case '\023': /* XOFF (IGNORED) */
        case 0177:   /* DEL (IGNORED) */
                return;
+       case 0x80:   /* TODO: PAD */
+       case 0x81:   /* TODO: HOP */
+       case 0x82:   /* TODO: BPH */
+       case 0x83:   /* TODO: NBH */
        case 0x84:   /* TODO: IND */
                break;
        case 0x85:   /* NEL -- Next line */
                tnewline(1); /* always go to first col */
                break;
+       case 0x86:   /* TODO: SSA */
+       case 0x87:   /* TODO: ESA */
+               break;
        case 0x88:   /* HTS -- Horizontal tab stop */
                term.tabs[term.c.x] = 1;
                break;
+       case 0x89:   /* TODO: HTJ */
+       case 0x8a:   /* TODO: VTS */
+       case 0x8b:   /* TODO: PLD */
+       case 0x8c:   /* TODO: PLU */
        case 0x8d:   /* TODO: RI */
        case 0x8e:   /* TODO: SS2 */
        case 0x8f:   /* TODO: SS3 */
+       case 0x91:   /* TODO: PU1 */
+       case 0x92:   /* TODO: PU2 */
+       case 0x93:   /* TODO: STS */
+       case 0x94:   /* TODO: CCH */
+       case 0x95:   /* TODO: MW */
+       case 0x96:   /* TODO: SPA */
+       case 0x97:   /* TODO: EPA */
        case 0x98:   /* TODO: SOS */
+       case 0x99:   /* TODO: SGCI */
                break;
        case 0x9a:   /* DECID -- Identify Terminal */
                ttywrite(vtiden, sizeof(vtiden) - 1);
@@ -2671,9 +2813,9 @@ tcontrolcode(uchar ascii)
        case 0x9c:   /* TODO: ST */
                break;
        case 0x90:   /* DCS -- Device Control String */
-       case 0x9f:   /* APC -- Application Program Command */
-       case 0x9e:   /* PM -- Privacy Message */
        case 0x9d:   /* OSC -- Operating System Command */
+       case 0x9e:   /* PM -- Privacy Message */
+       case 0x9f:   /* APC -- Application Program Command */
                tstrsequence(ascii);
                return;
        }
@@ -2773,15 +2915,15 @@ tputc(Rune u)
        int width, len;
        Glyph *gp;
 
+       control = ISCONTROL(u);
        len = utf8encode(u, c);
-       if ((width = wcwidth(u)) == -1) {
+       if (!control && (width = wcwidth(u)) == -1) {
                memcpy(c, "\357\277\275", 4); /* UTF_INVALID */
                width = 1;
        }
 
        if (IS_SET(MODE_PRINT))
                tprinter(c, len);
-       control = ISCONTROL(u);
 
        /*
         * STR sequence must be checked before anything else
@@ -3012,6 +3154,7 @@ xloadcolor(int i, const char *name, Color *ncolor)
                } else
                        name = colorname[i];
        }
+
        return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor);
 }
 
@@ -3051,6 +3194,7 @@ xsetcolorname(int x, const char *name)
 
        XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]);
        dc.col[x] = ncolor;
+
        return 0;
 }
 
@@ -3120,6 +3264,7 @@ xgeommasktogravity(int mask)
        case YNegative:
                return SouthWestGravity;
        }
+
        return SouthEastGravity;
 }
 
@@ -3286,6 +3431,7 @@ xinit(void)
        Cursor cursor;
        Window parent;
        pid_t thispid = getpid();
+       XColor xmousefg, xmousebg;
 
        if (!(xw.dpy = XOpenDisplay(NULL)))
                die("Can't open display\n");
@@ -3358,11 +3504,22 @@ xinit(void)
                die("XCreateIC failed. Could not obtain input method.\n");
 
        /* white cursor, black outline */
-       cursor = XCreateFontCursor(xw.dpy, XC_xterm);
+       cursor = XCreateFontCursor(xw.dpy, mouseshape);
        XDefineCursor(xw.dpy, xw.win, cursor);
-       XRecolorCursor(xw.dpy, cursor,
-               &(XColor){.red = 0xffff, .green = 0xffff, .blue = 0xffff},
-               &(XColor){.red = 0x0000, .green = 0x0000, .blue = 0x0000});
+
+       if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) {
+               xmousefg.red   = 0xffff;
+               xmousefg.green = 0xffff;
+               xmousefg.blue  = 0xffff;
+       }
+
+       if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) {
+               xmousebg.red   = 0x0000;
+               xmousebg.green = 0x0000;
+               xmousebg.blue  = 0x0000;
+       }
+
+       XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg);
 
        xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False);
        xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False);
@@ -4035,9 +4192,9 @@ run(void)
        do {
                XNextEvent(xw.dpy, &ev);
                /*
-                * XFilterEvent is required to be called after you using XOpenIM,
-                * this is not unnecessary.It does not only filter the key event,
-                * but some clientmessage for input method as well.
+                * This XFilterEvent call is required because of XOpenIM. It
+                * does filter out the key event and some client message for
+                * the input method too.
                 */
                if (XFilterEvent(&ev, None))
                        continue;
@@ -4134,9 +4291,11 @@ usage(void)
 {
        die("%s " VERSION " (c) 2010-2015 st engineers\n"
        "usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
-       "          [-i] [-t title] [-w windowid] [-e command ...] [command ...]\n"
+       "          [-i] [-t title] [-T title] [-w windowid] [-e command ...]"
+       " [command ...]\n"
        "       st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
-       "          [-i] [-t title] [-w windowid] [-l line] [stty_args ...]\n",
+       "          [-i] [-t title] [-T title] [-w windowid] -l line"
+       " [stty_args ...]\n",
        argv0);
 }
 
@@ -4177,6 +4336,7 @@ main(int argc, char *argv[])
                opt_line = EARGF(usage());
                break;
        case 't':
+       case 'T':
                opt_title = EARGF(usage());
                break;
        case 'w':
@@ -4203,3 +4363,4 @@ run:
 
        return 0;
 }
+