X-Git-Url: https://git.xinqibao.xyz/st.git/blobdiff_plain/f1307d91e2ec351a4a8b7352be8b5f6e4cb24294..dc33d1d66e89ee974cc6d7cfa2efb693ce67aa86:/st.c

diff --git a/st.c b/st.c
index 274ac5d..0db3d58 100644
--- 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,57 @@ 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.
+				 */
+				ttyread();
+				n -= r;
+				s += r;
+			} else {
+				/* All bytes have been written. */
+				break;
+			}
+		}
+	}
 }
 
 void
@@ -3012,6 +3134,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 +3174,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 +3244,7 @@ xgeommasktogravity(int mask)
 	case YNegative:
 		return SouthWestGravity;
 	}
+
 	return SouthEastGravity;
 }
 
@@ -4035,9 +4160,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 +4259,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 +4304,7 @@ main(int argc, char *argv[])
 		opt_line = EARGF(usage());
 		break;
 	case 't':
+	case 'T':
 		opt_title = EARGF(usage());
 		break;
 	case 'w':
@@ -4203,3 +4331,4 @@ run:
 
 	return 0;
 }
+