X-Git-Url: https://git.danieliu.xyz/?a=blobdiff_plain;f=st.c;h=4f958d866835caa42c1f84a61f4cb746423b3cff;hb=b823f57fa045bf8efa5648adbfeb0b6a7a9eb48b;hp=274ac5d768fe727cbb15204aabdc20a5f983985f;hpb=f1307d91e2ec351a4a8b7352be8b5f6e4cb24294;p=st.git diff --git a/st.c b/st.c index 274ac5d..4f958d8 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, }; @@ -552,10 +560,10 @@ static int frclen = 0; ssize_t xwrite(int fd, const char *s, size_t len) { - size_t aux = len; + size_t aux = len, r; while (len > 0) { - ssize_t r = write(fd, s, len); + r = write(fd, s, len); if (r < 0) return r; len -= r; @@ -1028,21 +1036,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 +1080,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 +1131,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 @@ -1416,8 +1482,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 @@ -4203,3 +4318,4 @@ run: return 0; } +