void xxflush(int intr) { struct xx *xp, *xq; for (xp = xx_head; xp != 0 && !(intr && wwinterrupt()); xp = xq) { switch (xp->cmd) { case xc_move: if (xp->link == 0) (*tt.tt_move)(xp->arg0, xp->arg1); break; case xc_scroll: xxflush_scroll(xp); break; case xc_inschar: (*tt.tt_move)(xp->arg0, xp->arg1); tt.tt_nmodes = xp->arg3; (*tt.tt_inschar)(xp->arg2); break; case xc_insspace: (*tt.tt_move)(xp->arg0, xp->arg1); (*tt.tt_insspace)(xp->arg2); break; case xc_delchar: (*tt.tt_move)(xp->arg0, xp->arg1); (*tt.tt_delchar)(xp->arg2); break; case xc_clear: (*tt.tt_clear)(); break; case xc_clreos: (*tt.tt_move)(xp->arg0, xp->arg1); (*tt.tt_clreos)(); break; case xc_clreol: (*tt.tt_move)(xp->arg0, xp->arg1); (*tt.tt_clreol)(); break; case xc_write: (*tt.tt_move)(xp->arg0, xp->arg1); tt.tt_nmodes = xp->arg3; (*tt.tt_write)(xp->buf, xp->arg2); break; } xq = xp->link; xxfree(xp); } if ((xx_head = xp) == 0) { xx_tail = 0; xxbufp = xxbuf; } ttflush(); }
/* * Tty input interrupt handler. * (1) Read input into buffer (wwib*). * (2) If the flag wwsetjmp is true, do longjmp(wwjmpbuf) for asyncronous * actions, and to avoid race conditions, clear wwsetjmp. * Currently, the last is used to get out of the blocking * select() in wwiomux(). * To avoid race conditions, we only modify wwibq in here, except * when the buffer is empty; and everywhere else, we only change wwibp. * It should be completely safe. */ wwrint() { register n; if (wwibp == wwibq) wwibp = wwibq = wwib; wwnread++; (void) fcntl(0, F_SETFL, FNDELAY|wwnewtty.ww_fflags); n = read(0, wwibq, wwibe - wwibq); (void) fcntl(0, F_SETFL, wwnewtty.ww_fflags); if (n > 0) { wwibq += n; wwnreadc += n; } else if (n == 0) wwnreadz++; else wwnreade++; if (wwinterrupt() && wwsetjmp) { wwsetjmp = 0; (void) sigsetmask(sigblock(0) & ~sigmask(SIGIO)); longjmp(wwjmpbuf, 1); } }
wwupdate1(top, bot) { int i; register j; char *touched; struct ww_update *upd; char check_clreos = 0; int scan_top, scan_bot; wwnupdate++; { register char *t1 = wwtouched + top, *t2 = wwtouched + bot; register n; while (!*t1++) if (t1 == t2) return; while (!*--t2) ; scan_top = top = t1 - wwtouched - 1; scan_bot = bot = t2 - wwtouched + 1; if (scan_bot - scan_top > 1 && (tt.tt_clreos != 0 || tt.tt_clear != 0)) { int st = tt.tt_clreos != 0 ? scan_top : 0; /* * t1 is one past the first touched row, * t2 is on the last touched row. */ for (t1--, n = 1; t1 < t2;) if (*t1++) n++; /* * If we can't clreos then we try for clearing * the whole screen. */ if (check_clreos = n * 10 > (wwnrow - st) * 9) { scan_top = st; scan_bot = wwnrow; } } } if (tt.tt_clreol == 0 && !check_clreos) goto simple; for (i = scan_top, touched = &wwtouched[i], upd = &wwupd[i]; i < scan_bot; i++, touched++, upd++) { register gain = 0; register best_gain = 0; register best_col; register union ww_char *ns, *os; if (wwinterrupt()) return; if (!check_clreos && !*touched) continue; wwnupdscan++; j = wwncol; ns = &wwns[i][j]; os = &wwos[i][j]; while (--j >= 0) { /* * The cost of clearing is: * ncol - nblank + X * The cost of straight update is, more or less: * ncol - nsame * We clear if nblank - nsame > X * X is the clreol overhead. * So we make gain = nblank - nsame. */ if ((--ns)->c_w == (--os)->c_w) gain--; else best_gain--; if (ns->c_w == ' ') gain++; if (gain > best_gain) { best_col = j; best_gain = gain; } } upd->best_gain = best_gain; upd->best_col = best_col; upd->gain = gain; } if (check_clreos) { register struct ww_update *u; register gain = 0; register best_gain = 0; int best_row; register simple_gain = 0; char didit = 0; /* * gain is the advantage of clearing all the lines. * best_gain is the advantage of clearing to eos * at best_row and u->best_col. * simple_gain is the advantage of using only clreol. * We use g > best_gain because u->best_col can be * undefined when u->best_gain is 0 so we can't use it. */ for (j = scan_bot - 1, u = wwupd + j; j >= top; j--, u--) { register g = gain + u->best_gain; if (g > best_gain) { best_gain = g; best_row = j; } gain += u->gain; if (tt.tt_clreol != 0 && u->best_gain > 4) simple_gain += u->best_gain - 4; } if (tt.tt_clreos == 0) { if (gain > simple_gain && gain > 4) { xxclear(); i = top = scan_top; bot = scan_bot; j = 0; didit = 1; } } else if (best_gain > simple_gain && best_gain > 4) { i = best_row; xxclreos(i, j = wwupd[i].best_col); bot = scan_bot; didit = 1; } if (didit) { wwnupdclreos++; wwnupdclreosline += wwnrow - i; u = wwupd + i; while (i < scan_bot) { register union ww_char *os = &wwos[i][j]; for (j = wwncol - j; --j >= 0;) os++->c_w = ' '; wwtouched[i++] |= WWU_TOUCHED; u++->best_gain = 0; j = 0; } } else wwnupdclreosmiss++; } simple: for (i = top, touched = &wwtouched[i], upd = &wwupd[i]; i < bot; i++, touched++, upd++) { register union ww_char *os, *ns; char didit; if (!*touched) continue; *touched = 0; wwnupdline++; didit = 0; if (tt.tt_clreol != 0 && upd->best_gain > 4) { wwnupdclreol++; xxclreol(i, j = upd->best_col); for (os = &wwos[i][j], j = wwncol - j; --j >= 0;) os++->c_w = ' '; didit = 1; } ns = wwns[i]; os = wwos[i]; for (j = 0; j < wwncol;) { register char *p, *q; char m; int c; register n; char buf[512]; /* > wwncol */ union ww_char lastc; for (; j++ < wwncol && ns++->c_w == os++->c_w;) ; if (j > wwncol) break; p = buf; m = ns[-1].c_m; c = j - 1; os[-1] = ns[-1]; *p++ = ns[-1].c_c; n = 5; q = p; while (j < wwncol && ns->c_m == m) { *p++ = ns->c_c; if (ns->c_w == os->c_w) { if (--n <= 0) break; os++; ns++; } else { n = 5; q = p; lastc = *os; *os++ = *ns++; } j++; } n = q - buf; if (!wwwrap || i != wwnrow - 1 || c + n != wwncol) xxwrite(i, c, buf, n, m); else if (tt.tt_inschar || tt.tt_insspace) { if (n > 1) { q[-2] = q[-1]; n--; } else c--; xxwrite(i, c, buf, n, m); c += n - 1; if (tt.tt_inschar) xxinschar(i, c, ns[-2].c_c, ns[-2].c_m); else { xxinsspace(i, c); xxwrite(i, c, &ns[-2].c_c, 1, ns[-2].c_m); } } else { if (--n) xxwrite(i, c, buf, n, m); os[-1] = lastc; *touched = WWU_TOUCHED; } didit = 1; } if (!didit) wwnupdmiss++; } }
/* * Multiple window output handler. * The idea is to copy window outputs to the terminal, via the * display package. We try to give wwcurwin highest priority. * The only return conditions are when there is keyboard input * and when a child process dies. * When there's nothing to do, we sleep in a select(). * The history of this routine is interesting. */ void wwiomux(void) { struct ww *w; nfds_t nfd; int i; int volatile dostdin; /* avoid longjmp clobbering */ char volatile c; /* avoid longjmp clobbering */ char *p; int millis; char noblock = 0; static struct pollfd *pfd = NULL; static nfds_t maxfds = 0; c = 0; /* XXXGCC -Wuninitialized */ for (;;) { if (wwinterrupt()) { wwclrintr(); return; } nfd = 0; for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) { if (w->ww_pty < 0 || w->ww_obq >= w->ww_obe) continue; nfd++; } if (maxfds <= ++nfd) { /* One more for the fd=0 case below */ struct pollfd *npfd = pfd == NULL ? malloc(sizeof(*pfd) * nfd) : realloc(pfd, sizeof(*pfd) * nfd); if (npfd == NULL) { warn("will retry"); if (pfd) free(pfd); pfd = NULL; maxfds = 0; return; } pfd = npfd; maxfds = nfd; } nfd = 0; for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) { if (w->ww_pty < 0) continue; if (w->ww_obq < w->ww_obe) { pfd[nfd].fd = w->ww_pty; pfd[nfd++].events = POLLIN; } if (w->ww_obq > w->ww_obp && !ISSET(w->ww_pflags, WWP_STOPPED)) noblock = 1; } if (wwibq < wwibe) { dostdin = nfd; pfd[nfd].fd = 0; pfd[nfd++].events = POLLIN; } else { dostdin = -1; } if (!noblock) { if (wwcurwin != 0) wwcurtowin(wwcurwin); wwupdate(); wwflush(); (void) setjmp(wwjmpbuf); wwsetjmp = 1; if (wwinterrupt()) { wwsetjmp = 0; wwclrintr(); return; } /* XXXX */ millis = 30000; } else { millis = 10; } wwnselect++; i = poll(pfd, nfd, millis); wwsetjmp = 0; noblock = 0; if (i < 0) wwnselecte++; else if (i == 0) wwnselectz++; else { if (dostdin != -1 && (pfd[dostdin].revents & POLLIN) != 0) wwrint(); nfd = 0; for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) { int n; if (w->ww_pty < 0) continue; if (w->ww_pty != pfd[nfd].fd) continue; if ((pfd[nfd++].revents & POLLIN) == 0) continue; wwnwread++; p = w->ww_obq; if (w->ww_type == WWT_PTY) { if (p == w->ww_ob) { w->ww_obp++; w->ww_obq++; } else p--; c = *p; } n = read(w->ww_pty, p, w->ww_obe - p); if (n < 0) { wwnwreade++; (void) close(w->ww_pty); w->ww_pty = -1; } else if (n == 0) { wwnwreadz++; (void) close(w->ww_pty); w->ww_pty = -1; } else if (w->ww_type != WWT_PTY) { wwnwreadd++; wwnwreadc += n; w->ww_obq += n; } else if (*p == TIOCPKT_DATA) { n--; wwnwreadd++; wwnwreadc += n; w->ww_obq += n; } else { wwnwreadp++; if (*p & TIOCPKT_STOP) SET(w->ww_pflags, WWP_STOPPED); if (*p & TIOCPKT_START) CLR(w->ww_pflags, WWP_STOPPED); if (*p & TIOCPKT_FLUSHWRITE) { CLR(w->ww_pflags, WWP_STOPPED); w->ww_obq = w->ww_obp = w->ww_ob; } } if (w->ww_type == WWT_PTY) *p = c; } } /* * Try the current window first, if there is output * then process it and go back to the top to try again. * This can lead to starvation of the other windows, * but presumably that what we want. * Update will eventually happen when output from wwcurwin * dies down. */ if ((w = wwcurwin) != NULL && w->ww_pty >= 0 && w->ww_obq > w->ww_obp && !ISSET(w->ww_pflags, WWP_STOPPED)) { int n = wwwrite(w, w->ww_obp, w->ww_obq - w->ww_obp); if ((w->ww_obp += n) == w->ww_obq) w->ww_obq = w->ww_obp = w->ww_ob; noblock = 1; continue; } for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) if (w->ww_pty >= 0 && w->ww_obq > w->ww_obp && !ISSET(w->ww_pflags, WWP_STOPPED)) { int n = wwwrite(w, w->ww_obp, w->ww_obq - w->ww_obp); if ((w->ww_obp += n) == w->ww_obq) w->ww_obq = w->ww_obp = w->ww_ob; if (wwinterrupt()) break; } } }
/* * Multiple window output handler. * The idea is to copy window outputs to the terminal, via the * display package. We try to give the top most window highest * priority. The only return condition is when there is keyboard * input, which is serviced asynchronously by wwrint(). * When there's nothing to do, we sleep in a select(). * This can be done better with interrupt driven io. But that's * not supported on ptys, yet. * The history of this routine is interesting. */ wwiomux() { register struct ww *w; fd_set imask; register n; register char *p; char c; static struct timeval tv = { 0, 0 }; char noblock; loop: if (wwinterrupt()) return; FD_ZERO(&imask); noblock = 0; for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) { if (w->ww_pty < 0) continue; if (w->ww_obq < w->ww_obe) FD_SET(w->ww_pty, &imask); if (w->ww_obq > w->ww_obp && !w->ww_stopped) noblock = 1; } if (!noblock) { if (wwcurwin != 0) wwcurtowin(wwcurwin); wwupdate(); wwflush(); if (setjmp(wwjmpbuf)) return; wwsetjmp = 1; if (wwinterrupt()) { wwsetjmp = 0; return; } } wwnselect++; n = select(wwdtablesize, &imask, (fd_set *)0, (fd_set *)0, noblock ? &tv : (struct timeval *)0); wwsetjmp = 0; if (n < 0) wwnselecte++; else if (n == 0) wwnselectz++; else for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) { if (w->ww_pty < 0 || !FD_ISSET(w->ww_pty, &imask)) continue; wwnwread++; p = w->ww_obq; if (w->ww_ispty) { if (p == w->ww_ob) { w->ww_obp++; w->ww_obq++; } else p--; c = *p; } n = read(w->ww_pty, p, w->ww_obe - p); if (n < 0) { wwnwreade++; (void) close(w->ww_pty); w->ww_pty = -1; } else if (n == 0) { wwnwreadz++; (void) close(w->ww_pty); w->ww_pty = -1; } else if (!w->ww_ispty) { wwnwreadd++; wwnwreadc += n; w->ww_obq += n; } else if (*p == TIOCPKT_DATA) { n--; wwnwreadd++; wwnwreadc += n; w->ww_obq += n; } else { wwnwreadp++; if (*p & TIOCPKT_STOP) w->ww_stopped = 1; if (*p & TIOCPKT_START) w->ww_stopped = 0; if (*p & TIOCPKT_FLUSHWRITE) { w->ww_stopped = 0; w->ww_obq = w->ww_obp = w->ww_ob; } } if (w->ww_ispty) *p = c; } for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) if (w->ww_pty >= 0 && w->ww_obq > w->ww_obp && !w->ww_stopped) { n = wwwrite(w, w->ww_obp, w->ww_obq - w->ww_obp); if ((w->ww_obp += n) == w->ww_obq) w->ww_obq = w->ww_obp = w->ww_ob; if (wwinterrupt()) return; break; } goto loop; }