X-Git-Url: https://git.danieliu.xyz/?a=blobdiff_plain;f=st.c;h=97e1800d5196f160d777d0a06acdd7cdf213a59c;hb=684f0a0729442e18115f71bc594a159725418ce8;hp=274ac5d768fe727cbb15204aabdc20a5f983985f;hpb=f1307d91e2ec351a4a8b7352be8b5f6e4cb24294;p=st.git diff --git a/st.c b/st.c index 274ac5d..97e1800 100644 --- a/st.c +++ b/st.c @@ -452,6 +452,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 +501,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 +558,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 +1034,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 +1078,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 +1129,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 +1480,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 +4316,4 @@ run: return 0; } +