/* * cl_split -- * Split a screen. * * PUBLIC: int cl_split __P((SCR *, SCR *)); */ int cl_split(SCR *origp, SCR *newp) { CL_PRIVATE *clp; clp = CLP(origp); F_SET(clp, CL_LAYOUT); if (CLSP(origp)) delwin(CLSP(origp)); origp->cl_private = subwin(stdscr, origp->rows, origp->cols, origp->roff, origp->coff); newp->cl_private = subwin(stdscr, newp->rows, newp->cols, newp->roff, newp->coff); /* origp is the original screen, giving up space to newp. */ return (0); }
/* * cl_cursor -- * Return the current cursor position. * * PUBLIC: int cl_cursor __P((SCR *, size_t *, size_t *)); */ int cl_cursor(SCR *sp, size_t *yp, size_t *xp) { WINDOW *win; win = CLSP(sp) ? CLSP(sp) : stdscr; /* * The curses screen support splits a single underlying curses screen * into multiple screens to support split screen semantics. For this * reason the returned value must be adjusted to be relative to the * current screen, and not absolute. Screens that implement the split * using physically distinct screens won't need this hack. */ getyx(win, *yp, *xp); /* *yp -= sp->roff; *xp -= sp->coff; */ return (0); }
/* * cl_clrtoeol -- * Clear from the current cursor to the end of the line. * * PUBLIC: int cl_clrtoeol __P((SCR *)); */ int cl_clrtoeol(SCR *sp) { WINDOW *win; size_t spcnt, y, x; win = CLSP(sp) ? CLSP(sp) : stdscr; #if 0 if (IS_VSPLIT(sp)) { /* The cursor must be returned to its original position. */ getyx(win, y, x); for (spcnt = (sp->coff + sp->cols) - x; spcnt > 0; --spcnt) (void)waddch(win, ' '); (void)wmove(win, y, x); return (0); } else #endif return (wclrtoeol(win) == ERR); }
static int addstr4(SCR *sp, void *str, size_t len, int wide) { CL_PRIVATE *clp; WINDOW *win; size_t y, x; int iv; clp = CLP(sp); win = CLSP(sp) ? CLSP(sp) : stdscr; /* * If ex isn't in control, it's the last line of the screen and * it's a split screen, use inverse video. */ iv = 0; getyx(win, y, x); if (!F_ISSET(sp, SC_SCR_EXWROTE) && y == RLNO(sp, LASTLINE(sp)) && IS_SPLIT(sp)) { iv = 1; (void)wstandout(win); } #ifdef USE_WIDECHAR if (wide) { if (waddnwstr(win, str, len) == ERR) return (1); } else #endif if (waddnstr(win, str, len) == ERR) return (1); if (iv) (void)wstandend(win); return (0); }
/* * cl_suspend -- * Suspend a screen. * * PUBLIC: int cl_suspend __P((SCR *, int *)); */ int cl_suspend(SCR *sp, int *allowedp) { struct termios t; CL_PRIVATE *clp; WINDOW *win; size_t y, x; int changed; clp = CLP(sp); win = CLSP(sp) ? CLSP(sp) : stdscr; *allowedp = 1; /* * The ex implementation of this function isn't needed by screens not * supporting ex commands that require full terminal canonical mode * (e.g. :suspend). * * The vi implementation of this function isn't needed by screens not * supporting vi process suspension, i.e. any screen that isn't backed * by a UNIX shell. * * Setting allowedp to 0 will cause the editor to reject the command. */ if (F_ISSET(sp, SC_EX)) { /* Save the terminal settings, and restore the original ones. */ if (F_ISSET(clp, CL_STDIN_TTY)) { (void)tcgetattr(STDIN_FILENO, &t); (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &clp->orig); } /* Stop the process group. */ (void)kill(0, SIGTSTP); /* Time passes ... */ /* Restore terminal settings. */ if (F_ISSET(clp, CL_STDIN_TTY)) (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); return (0); } /* * Move to the lower left-hand corner of the screen. * * XXX * Not sure this is necessary in System V implementations, but it * shouldn't hurt. */ getyx(win, y, x); (void)wmove(win, LINES - 1, 0); (void)wrefresh(win); /* * Temporarily end the screen. System V introduced a semantic where * endwin() could be restarted. We use it because restarting curses * from scratch often fails in System V. 4BSD curses didn't support * restarting after endwin(), so we have to do what clean up we can * without calling it. */ /* Save the terminal settings. */ (void)tcgetattr(STDIN_FILENO, &t); /* Restore the cursor keys to normal mode. */ (void)keypad(stdscr, FALSE); /* Restore the window name. */ (void)cl_rename(sp, NULL, 0); #ifdef HAVE_BSD_CURSES (void)cl_attr(sp, SA_ALTERNATE, 0); #else (void)endwin(); #endif /* * XXX * Restore the original terminal settings. This is bad -- the * reset can cause character loss from the tty queue. However, * we can't call endwin() in BSD curses implementations, and too * many System V curses implementations don't get it right. */ (void)tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &clp->orig); /* Stop the process group. */ (void)kill(0, SIGTSTP); /* Time passes ... */ /* * If we received a killer signal, we're done. Leave everything * unchanged. In addition, the terminal has already been reset * correctly, so leave it alone. */ if (clp->killersig) { F_CLR(clp, CL_SCR_EX_INIT | CL_SCR_VI_INIT); return (0); } /* Restore terminal settings. */ wrefresh(win); /* Needed on SunOs/Solaris ? */ if (F_ISSET(clp, CL_STDIN_TTY)) (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t); #ifdef HAVE_BSD_CURSES (void)cl_attr(sp, SA_ALTERNATE, 1); #endif /* Set the window name. */ (void)cl_rename(sp, sp->frp->name, 1); /* Put the cursor keys into application mode. */ (void)keypad(stdscr, TRUE); /* Refresh and repaint the screen. */ (void)wmove(win, y, x); (void)cl_refresh(sp, 1); /* If the screen changed size, set the SIGWINCH bit. */ if (cl_ssize(sp, 1, NULL, NULL, &changed)) return (1); if (changed) F_SET(CLP(sp), CL_SIGWINCH); return (0); }
/* * cl_attr -- * Toggle a screen attribute on/off. * * PUBLIC: int cl_attr __P((SCR *, scr_attr_t, int)); */ int cl_attr(SCR *sp, scr_attr_t attribute, int on) { CL_PRIVATE *clp; WINDOW *win; clp = CLP(sp); win = CLSP(sp) ? CLSP(sp) : stdscr; switch (attribute) { case SA_ALTERNATE: /* * !!! * There's a major layering violation here. The problem is that the * X11 xterm screen has what's known as an "alternate" screen. Some * xterm termcap/terminfo entries include sequences to switch to/from * that alternate screen as part of the ti/te (smcup/rmcup) strings. * Vi runs in the alternate screen, so that you are returned to the * same screen contents on exit from vi that you had when you entered * vi. Further, when you run :shell, or :!date or similar ex commands, * you also see the original screen contents. This wasn't deliberate * on vi's part, it's just that it historically sent terminal init/end * sequences at those times, and the addition of the alternate screen * sequences to the strings changed the behavior of vi. The problem * caused by this is that we don't want to switch back to the alternate * screen while getting a new command from the user, when the user is * continuing to enter ex commands, e.g.: * * :!date <<< switch to original screen * [Hit return to continue] <<< prompt user to continue * :command <<< get command from user * * Note that the :command input is a true vi input mode, e.g., input * maps and abbreviations are being done. So, we need to be able to * switch back into the vi screen mode, without flashing the screen. * * To make matters worse, the curses initscr() and endwin() calls will * do this automatically -- so, this attribute isn't as controlled by * the higher level screen as closely as one might like. */ if (on) { if (clp->ti_te != TI_SENT) { clp->ti_te = TI_SENT; if (clp->smcup == NULL) (void)cl_getcap(sp, "smcup", &clp->smcup); if (clp->smcup != NULL) (void)tputs(clp->smcup, 1, cl_putchar); } } else if (clp->ti_te != TE_SENT) { clp->ti_te = TE_SENT; if (clp->rmcup == NULL) (void)cl_getcap(sp, "rmcup", &clp->rmcup); if (clp->rmcup != NULL) (void)tputs(clp->rmcup, 1, cl_putchar); (void)fflush(stdout); } (void)fflush(stdout); break; case SA_INVERSE: if (F_ISSET(sp, SC_EX | SC_SCR_EXWROTE)) { if (clp->smso == NULL) return (1); if (on) (void)tputs(clp->smso, 1, cl_putchar); else (void)tputs(clp->rmso, 1, cl_putchar); (void)fflush(stdout); } else { if (on) (void)wstandout(win); else (void)wstandend(win); } break; default: abort(); } return (0); }
/* * cl_screen -- * Switch screen types. * * PUBLIC: int cl_screen __P((SCR *, u_int32_t)); */ int cl_screen(SCR *sp, u_int32_t flags) { CL_PRIVATE *clp; WINDOW *win; GS *gp; int ret, error; sigset_t oset; gp = sp->gp; clp = CLP(sp); win = CLSP(sp) ? CLSP(sp) : stdscr; ret = 0; /* * During initialization of the screen, block signals to make sure that * curses/terminfo routines are not interrupted. */ error = sigprocmask(SIG_BLOCK, &__sigblockset, &oset); /* See if the current information is incorrect. */ if (F_ISSET(gp, G_SRESTART)) { if (CLSP(sp)) { delwin(CLSP(sp)); sp->cl_private = NULL; } if (cl_quit(gp)) { ret = 1; goto end; } F_CLR(gp, G_SRESTART); } /* See if we're already in the right mode. */ if ((LF_ISSET(SC_EX) && F_ISSET(sp, SC_SCR_EX)) || (LF_ISSET(SC_VI) && F_ISSET(sp, SC_SCR_VI))) goto end; /* * Fake leaving ex mode. * * We don't actually exit ex or vi mode unless forced (e.g. by a window * size change). This is because many curses implementations can't be * called twice in a single program. Plus, it's faster. If the editor * "leaves" vi to enter ex, when it exits ex we'll just fall back into * vi. */ if (F_ISSET(sp, SC_SCR_EX)) F_CLR(sp, SC_SCR_EX); /* * Fake leaving vi mode. * * Clear out the rest of the screen if we're in the middle of a split * screen. Move to the last line in the current screen -- this makes * terminal scrolling happen naturally. Note: *don't* move past the * end of the screen, as there are ex commands (e.g., :read ! cat file) * that don't want to. Don't clear the info line, its contents may be * valid, e.g. :file|append. */ if (F_ISSET(sp, SC_SCR_VI)) { F_CLR(sp, SC_SCR_VI); if (TAILQ_NEXT(sp, q) != NULL) { (void)wmove(win, RLNO(sp, sp->rows), 0); wclrtobot(win); } (void)wmove(win, RLNO(sp, sp->rows) - 1, 0); wrefresh(win); } /* Enter the requested mode. */ if (LF_ISSET(SC_EX)) { if (cl_ex_init(sp)) { ret = 1; goto end; } F_SET(clp, CL_IN_EX | CL_SCR_EX_INIT); /* * If doing an ex screen for ex mode, move to the last line * on the screen. */ if (F_ISSET(sp, SC_EX) && clp->cup != NULL) tputs(tgoto(clp->cup, 0, O_VAL(sp, O_LINES) - 1), 1, cl_putchar); } else { if (cl_vi_init(sp)) { ret = 1; goto end; } F_CLR(clp, CL_IN_EX); F_SET(clp, CL_SCR_VI_INIT); } end: /* Unblock signals. */ if (error == 0) (void)sigprocmask(SIG_SETMASK, &oset, NULL); return ret; }
/* * cl_refresh -- * Refresh the screen. * * PUBLIC: int cl_refresh __P((SCR *, int)); */ int cl_refresh(SCR *sp, int repaint) { GS *gp; CL_PRIVATE *clp; WINDOW *win; SCR *psp, *tsp; size_t y, x; gp = sp->gp; clp = CLP(sp); win = CLSP(sp) ? CLSP(sp) : stdscr; /* * If we received a killer signal, we're done, there's no point * in refreshing the screen. */ if (clp->killersig) return (0); /* * If repaint is set, the editor is telling us that we don't know * what's on the screen, so we have to repaint from scratch. * * If repaint set or the screen layout changed, we need to redraw * any lines separating vertically split screens. If the horizontal * offsets are the same, then the split was vertical, and need to * draw a dividing line. */ if (repaint || F_ISSET(clp, CL_LAYOUT)) { getyx(stdscr, y, x); for (psp = sp; psp != (void *)&sp->wp->scrq; psp = psp->q.cqe_next) for (tsp = psp->q.cqe_next; tsp != (void *)&sp->wp->scrq; tsp = tsp->q.cqe_next) if (psp->roff == tsp->roff) { if (psp->coff + psp->cols + 1 == tsp->coff) cl_rdiv(psp); else if (tsp->coff + tsp->cols + 1 == psp->coff) cl_rdiv(tsp); } (void)wmove(stdscr, y, x); F_CLR(clp, CL_LAYOUT); } /* * In the curses library, doing wrefresh(curscr) is okay, but the * screen flashes when we then apply the refresh() to bring it up * to date. So, use clearok(). */ if (repaint) clearok(curscr, 1); /* * Only do an actual refresh, when this is the focus window, * i.e. the one holding the cursor. This assumes that refresh * is called for that window after refreshing the others. * This prevents the cursor being drawn in the other windows. */ return (wnoutrefresh(stdscr) == ERR || wnoutrefresh(win) == ERR || (sp == clp->focus && doupdate() == ERR)); }