119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13281373Sbaptstatic const char sccsid[] = "$Id: vs_refresh.c,v 10.54 2015/04/08 16:32:49 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <ctype.h> 2219304Speter#include <limits.h> 2319304Speter#include <stdio.h> 2419304Speter#include <stdlib.h> 2519304Speter#include <string.h> 2619304Speter 2719304Speter#include "../common/common.h" 2819304Speter#include "vi.h" 2919304Speter 3019304Speter#define UPDATE_CURSOR 0x01 /* Update the cursor. */ 3119304Speter#define UPDATE_SCREEN 0x02 /* Flush to screen. */ 3219304Speter 33281373Sbaptstatic void vs_modeline(SCR *); 34281373Sbaptstatic int vs_paint(SCR *, u_int); 3519304Speter 3619304Speter/* 3719304Speter * v_repaint -- 3819304Speter * Repaint selected lines from the screen. 3919304Speter * 40281373Sbapt * PUBLIC: int vs_repaint(SCR *, EVENT *); 4119304Speter */ 4219304Speterint 43254225Spetervs_repaint( 44254225Speter SCR *sp, 45254225Speter EVENT *evp) 4619304Speter{ 4719304Speter SMAP *smp; 4819304Speter 4919304Speter for (; evp->e_flno <= evp->e_tlno; ++evp->e_flno) { 5019304Speter smp = HMAP + evp->e_flno - 1; 5119304Speter SMAP_FLUSH(smp); 5219304Speter if (vs_line(sp, smp, NULL, NULL)) 5319304Speter return (1); 5419304Speter } 5519304Speter return (0); 5619304Speter} 5719304Speter 5819304Speter/* 5919304Speter * vs_refresh -- 6019304Speter * Refresh all screens. 6119304Speter * 62281373Sbapt * PUBLIC: int vs_refresh(SCR *, int); 6319304Speter */ 6419304Speterint 65254225Spetervs_refresh( 66254225Speter SCR *sp, 67254225Speter int forcepaint) 6819304Speter{ 6919304Speter GS *gp; 7019304Speter SCR *tsp; 71254225Speter int need_refresh = 0; 7219304Speter u_int priv_paint, pub_paint; 7319304Speter 7419304Speter gp = sp->gp; 7519304Speter 7619304Speter /* 7719304Speter * 1: Refresh the screen. 7819304Speter * 7919304Speter * If SC_SCR_REDRAW is set in the current screen, repaint everything 8019304Speter * that we can find, including status lines. 8119304Speter */ 8219304Speter if (F_ISSET(sp, SC_SCR_REDRAW)) 83254225Speter TAILQ_FOREACH(tsp, gp->dq, q) 8419304Speter if (tsp != sp) 8519304Speter F_SET(tsp, SC_SCR_REDRAW | SC_STATUS); 8619304Speter 8719304Speter /* 8819304Speter * 2: Related or dirtied screens, or screens with messages. 8919304Speter * 9019304Speter * If related screens share a view into a file, they may have been 9119304Speter * modified as well. Refresh any screens that aren't exiting that 9219304Speter * have paint or dirty bits set. Always update their screens, we 9319304Speter * are not likely to get another chance. Finally, if we refresh any 9419304Speter * screens other than the current one, the cursor will be trashed. 9519304Speter */ 9619304Speter pub_paint = SC_SCR_REFORMAT | SC_SCR_REDRAW; 9719304Speter priv_paint = VIP_CUR_INVALID | VIP_N_REFRESH; 9819304Speter if (O_ISSET(sp, O_NUMBER)) 9919304Speter priv_paint |= VIP_N_RENUMBER; 100254225Speter TAILQ_FOREACH(tsp, gp->dq, q) 10119304Speter if (tsp != sp && !F_ISSET(tsp, SC_EXIT | SC_EXIT_FORCE) && 10219304Speter (F_ISSET(tsp, pub_paint) || 10319304Speter F_ISSET(VIP(tsp), priv_paint))) { 10419304Speter (void)vs_paint(tsp, 10519304Speter (F_ISSET(VIP(tsp), VIP_CUR_INVALID) ? 10619304Speter UPDATE_CURSOR : 0) | UPDATE_SCREEN); 10719304Speter F_SET(VIP(sp), VIP_CUR_INVALID); 10819304Speter } 10919304Speter 11019304Speter /* 11119304Speter * 3: Refresh the current screen. 11219304Speter * 11319304Speter * Always refresh the current screen, it may be a cursor movement. 11419304Speter * Also, always do it last -- that way, SC_SCR_REDRAW can be set 11519304Speter * in the current screen only, and the screen won't flash. 11619304Speter */ 11719304Speter if (vs_paint(sp, UPDATE_CURSOR | (!forcepaint && 11819304Speter F_ISSET(sp, SC_SCR_VI) && KEYS_WAITING(sp) ? 0 : UPDATE_SCREEN))) 11919304Speter return (1); 12019304Speter 12119304Speter /* 12219304Speter * 4: Paint any missing status lines. 12319304Speter * 12419304Speter * XXX 12519304Speter * This is fairly evil. Status lines are written using the vi message 12619304Speter * mechanism, since we have no idea how long they are. Since we may be 12719304Speter * painting screens other than the current one, we don't want to make 12819304Speter * the user wait. We depend heavily on there not being any other lines 12919304Speter * currently waiting to be displayed and the message truncation code in 13019304Speter * the msgq_status routine working. 13119304Speter * 13219304Speter * And, finally, if we updated any status lines, make sure the cursor 13319304Speter * gets back to where it belongs. 13419304Speter */ 135254225Speter TAILQ_FOREACH(tsp, gp->dq, q) 13619304Speter if (F_ISSET(tsp, SC_STATUS)) { 13719304Speter need_refresh = 1; 13819304Speter vs_resolve(tsp, sp, 0); 13919304Speter } 14019304Speter if (need_refresh) 14119304Speter (void)gp->scr_refresh(sp, 0); 14219304Speter 14319304Speter /* 14419304Speter * A side-effect of refreshing the screen is that it's now ready 14519304Speter * for everything else, i.e. messages. 14619304Speter */ 14719304Speter F_SET(sp, SC_SCR_VI); 14819304Speter return (0); 14919304Speter} 15019304Speter 15119304Speter/* 15219304Speter * vs_paint -- 15319304Speter * This is the guts of the vi curses screen code. The idea is that 15419304Speter * the SCR structure passed in contains the new coordinates of the 15519304Speter * screen. What makes this hard is that we don't know how big 15619304Speter * characters are, doing input can put the cursor in illegal places, 15719304Speter * and we're frantically trying to avoid repainting unless it's 15819304Speter * absolutely necessary. If you change this code, you'd better know 15919304Speter * what you're doing. It's subtle and quick to anger. 16019304Speter */ 16119304Speterstatic int 162254225Spetervs_paint( 163254225Speter SCR *sp, 164254225Speter u_int flags) 16519304Speter{ 16619304Speter GS *gp; 16719304Speter SMAP *smp, tmp; 16819304Speter VI_PRIVATE *vip; 16919304Speter recno_t lastline, lcnt; 17019304Speter size_t cwtotal, cnt, len, notused, off, y; 171254225Speter int ch = 0, didpaint, isempty, leftright_warp; 172254225Speter CHAR_T *p; 17319304Speter 17419304Speter#define LNO sp->lno /* Current file line. */ 17519304Speter#define OLNO vip->olno /* Remembered file line. */ 17619304Speter#define CNO sp->cno /* Current file column. */ 17719304Speter#define OCNO vip->ocno /* Remembered file column. */ 17819304Speter#define SCNO vip->sc_col /* Current screen column. */ 17919304Speter 18019304Speter gp = sp->gp; 18119304Speter vip = VIP(sp); 18219304Speter didpaint = leftright_warp = 0; 18319304Speter 18419304Speter /* 18519304Speter * 5: Reformat the lines. 18619304Speter * 18719304Speter * If the lines themselves have changed (:set list, for example), 18819304Speter * fill in the map from scratch. Adjust the screen that's being 18919304Speter * displayed if the leftright flag is set. 19019304Speter */ 19119304Speter if (F_ISSET(sp, SC_SCR_REFORMAT)) { 19219304Speter /* Invalidate the line size cache. */ 19319304Speter VI_SCR_CFLUSH(vip); 19419304Speter 19519304Speter /* Toss vs_line() cached information. */ 19619304Speter if (F_ISSET(sp, SC_SCR_TOP)) { 19719304Speter if (vs_sm_fill(sp, LNO, P_TOP)) 19819304Speter return (1); 19919304Speter } 20019304Speter else if (F_ISSET(sp, SC_SCR_CENTER)) { 20119304Speter if (vs_sm_fill(sp, LNO, P_MIDDLE)) 20219304Speter return (1); 20319304Speter } else 20419304Speter if (vs_sm_fill(sp, OOBLNO, P_TOP)) 20519304Speter return (1); 20619304Speter F_SET(sp, SC_SCR_REDRAW); 20719304Speter } 20819304Speter 20919304Speter /* 21019304Speter * 6: Line movement. 21119304Speter * 21219304Speter * Line changes can cause the top line to change as well. As 21319304Speter * before, if the movement is large, the screen is repainted. 21419304Speter * 21519304Speter * 6a: Small screens. 21619304Speter * 21719304Speter * Users can use the window, w300, w1200 and w9600 options to make 21819304Speter * the screen artificially small. The behavior of these options 21919304Speter * in the historic vi wasn't all that consistent, and, in fact, it 22019304Speter * was never documented how various screen movements affected the 22119304Speter * screen size. Generally, one of three things would happen: 22219304Speter * 1: The screen would expand in size, showing the line 22319304Speter * 2: The screen would scroll, showing the line 22419304Speter * 3: The screen would compress to its smallest size and 22519304Speter * repaint. 22619304Speter * In general, scrolling didn't cause compression (200^D was handled 22719304Speter * the same as ^D), movement to a specific line would (:N where N 22819304Speter * was 1 line below the screen caused a screen compress), and cursor 22919304Speter * movement would scroll if it was 11 lines or less, and compress if 23019304Speter * it was more than 11 lines. (And, no, I have no idea where the 11 23119304Speter * comes from.) 23219304Speter * 23319304Speter * What we do is try and figure out if the line is less than half of 23419304Speter * a full screen away. If it is, we expand the screen if there's 23519304Speter * room, and then scroll as necessary. The alternative is to compress 23619304Speter * and repaint. 23719304Speter * 23819304Speter * !!! 23919304Speter * This code is a special case from beginning to end. Unfortunately, 24019304Speter * home modems are still slow enough that it's worth having. 24119304Speter * 24219304Speter * XXX 24319304Speter * If the line a really long one, i.e. part of the line is on the 24419304Speter * screen but the column offset is not, we'll end up in the adjust 24519304Speter * code, when we should probably have compressed the screen. 24619304Speter */ 24719304Speter if (IS_SMALL(sp)) 24819304Speter if (LNO < HMAP->lno) { 24919304Speter lcnt = vs_sm_nlines(sp, HMAP, LNO, sp->t_maxrows); 25019304Speter if (lcnt <= HALFSCREEN(sp)) 25119304Speter for (; lcnt && sp->t_rows != sp->t_maxrows; 25219304Speter --lcnt, ++sp->t_rows) { 25319304Speter ++TMAP; 25419304Speter if (vs_sm_1down(sp)) 25519304Speter return (1); 25619304Speter } 25719304Speter else 25819304Speter goto small_fill; 25919304Speter } else if (LNO > TMAP->lno) { 26019304Speter lcnt = vs_sm_nlines(sp, TMAP, LNO, sp->t_maxrows); 26119304Speter if (lcnt <= HALFSCREEN(sp)) 26219304Speter for (; lcnt && sp->t_rows != sp->t_maxrows; 26319304Speter --lcnt, ++sp->t_rows) { 26419304Speter if (vs_sm_next(sp, TMAP, TMAP + 1)) 26519304Speter return (1); 26619304Speter ++TMAP; 26719304Speter if (vs_line(sp, TMAP, NULL, NULL)) 26819304Speter return (1); 26919304Speter } 27019304Speter else { 27119304Spetersmall_fill: (void)gp->scr_move(sp, LASTLINE(sp), 0); 27219304Speter (void)gp->scr_clrtoeol(sp); 27319304Speter for (; sp->t_rows > sp->t_minrows; 27419304Speter --sp->t_rows, --TMAP) { 27519304Speter (void)gp->scr_move(sp, TMAP - HMAP, 0); 27619304Speter (void)gp->scr_clrtoeol(sp); 27719304Speter } 27819304Speter if (vs_sm_fill(sp, LNO, P_FILL)) 27919304Speter return (1); 28019304Speter F_SET(sp, SC_SCR_REDRAW); 28119304Speter goto adjust; 28219304Speter } 28319304Speter } 28419304Speter 28519304Speter /* 28619304Speter * 6b: Line down, or current screen. 28719304Speter */ 28819304Speter if (LNO >= HMAP->lno) { 28919304Speter /* Current screen. */ 29019304Speter if (LNO <= TMAP->lno) 29119304Speter goto adjust; 29219304Speter if (F_ISSET(sp, SC_SCR_TOP)) 29319304Speter goto top; 29419304Speter if (F_ISSET(sp, SC_SCR_CENTER)) 29519304Speter goto middle; 29619304Speter 29719304Speter /* 29819304Speter * If less than half a screen above the line, scroll down 29919304Speter * until the line is on the screen. 30019304Speter */ 30119304Speter lcnt = vs_sm_nlines(sp, TMAP, LNO, HALFTEXT(sp)); 30219304Speter if (lcnt < HALFTEXT(sp)) { 30319304Speter while (lcnt--) 30419304Speter if (vs_sm_1up(sp)) 30519304Speter return (1); 30619304Speter goto adjust; 30719304Speter } 30819304Speter goto bottom; 30919304Speter } 31019304Speter 31119304Speter /* 31219304Speter * 6c: If not on the current screen, may request center or top. 31319304Speter */ 31419304Speter if (F_ISSET(sp, SC_SCR_TOP)) 31519304Speter goto top; 31619304Speter if (F_ISSET(sp, SC_SCR_CENTER)) 31719304Speter goto middle; 31819304Speter 31919304Speter /* 32019304Speter * 6d: Line up. 32119304Speter */ 32219304Speter lcnt = vs_sm_nlines(sp, HMAP, LNO, HALFTEXT(sp)); 32319304Speter if (lcnt < HALFTEXT(sp)) { 32419304Speter /* 32519304Speter * If less than half a screen below the line, scroll up until 32619304Speter * the line is the first line on the screen. Special check so 32719304Speter * that if the screen has been emptied, we refill it. 32819304Speter */ 32919304Speter if (db_exist(sp, HMAP->lno)) { 33019304Speter while (lcnt--) 33119304Speter if (vs_sm_1down(sp)) 33219304Speter return (1); 33319304Speter goto adjust; 334257999Speter } else 335257999Speter goto top; /* XXX No such line. */ 33619304Speter 33719304Speter /* 33819304Speter * If less than a half screen from the bottom of the file, 33919304Speter * put the last line of the file on the bottom of the screen. 34019304Speter */ 34119304Speterbottom: if (db_last(sp, &lastline)) 34219304Speter return (1); 34319304Speter tmp.lno = LNO; 34419304Speter tmp.coff = HMAP->coff; 34519304Speter tmp.soff = 1; 34619304Speter lcnt = vs_sm_nlines(sp, &tmp, lastline, sp->t_rows); 34719304Speter if (lcnt < HALFTEXT(sp)) { 34819304Speter if (vs_sm_fill(sp, lastline, P_BOTTOM)) 34919304Speter return (1); 35019304Speter F_SET(sp, SC_SCR_REDRAW); 35119304Speter goto adjust; 35219304Speter } 35319304Speter /* It's not close, just put the line in the middle. */ 35419304Speter goto middle; 35519304Speter } 35619304Speter 35719304Speter /* 35819304Speter * If less than half a screen from the top of the file, put the first 35919304Speter * line of the file at the top of the screen. Otherwise, put the line 36019304Speter * in the middle of the screen. 36119304Speter */ 36219304Speter tmp.lno = 1; 36319304Speter tmp.coff = HMAP->coff; 36419304Speter tmp.soff = 1; 36519304Speter lcnt = vs_sm_nlines(sp, &tmp, LNO, HALFTEXT(sp)); 36619304Speter if (lcnt < HALFTEXT(sp)) { 36719304Speter if (vs_sm_fill(sp, 1, P_TOP)) 36819304Speter return (1); 36919304Speter } else 37019304Spetermiddle: if (vs_sm_fill(sp, LNO, P_MIDDLE)) 37119304Speter return (1); 37219304Speter if (0) { 37319304Spetertop: if (vs_sm_fill(sp, LNO, P_TOP)) 37419304Speter return (1); 37519304Speter } 37619304Speter F_SET(sp, SC_SCR_REDRAW); 37719304Speter 37819304Speter /* 37919304Speter * At this point we know part of the line is on the screen. Since 38019304Speter * scrolling is done using logical lines, not physical, all of the 38119304Speter * line may not be on the screen. While that's not necessarily bad, 38219304Speter * if the part the cursor is on isn't there, we're going to lose. 38319304Speter * This can be tricky; if the line covers the entire screen, lno 38419304Speter * may be the same as both ends of the map, that's why we test BOTH 38519304Speter * the top and the bottom of the map. This isn't a problem for 38619304Speter * left-right scrolling, the cursor movement code handles the problem. 38719304Speter * 38819304Speter * There's a performance issue here if editing *really* long lines. 38919304Speter * This gets to the right spot by scrolling, and, in a binary, by 39019304Speter * scrolling hundreds of lines. If the adjustment looks like it's 39119304Speter * going to be a serious problem, refill the screen and repaint. 39219304Speter */ 39319304Speteradjust: if (!O_ISSET(sp, O_LEFTRIGHT) && 39419304Speter (LNO == HMAP->lno || LNO == TMAP->lno)) { 39519304Speter cnt = vs_screens(sp, LNO, &CNO); 39619304Speter if (LNO == HMAP->lno && cnt < HMAP->soff) 39719304Speter if ((HMAP->soff - cnt) > HALFTEXT(sp)) { 39819304Speter HMAP->soff = cnt; 39919304Speter vs_sm_fill(sp, OOBLNO, P_TOP); 40019304Speter F_SET(sp, SC_SCR_REDRAW); 40119304Speter } else 40219304Speter while (cnt < HMAP->soff) 40319304Speter if (vs_sm_1down(sp)) 40419304Speter return (1); 40519304Speter if (LNO == TMAP->lno && cnt > TMAP->soff) 40619304Speter if ((cnt - TMAP->soff) > HALFTEXT(sp)) { 40719304Speter TMAP->soff = cnt; 40819304Speter vs_sm_fill(sp, OOBLNO, P_BOTTOM); 40919304Speter F_SET(sp, SC_SCR_REDRAW); 41019304Speter } else 41119304Speter while (cnt > TMAP->soff) 41219304Speter if (vs_sm_1up(sp)) 41319304Speter return (1); 41419304Speter } 41519304Speter 41619304Speter /* 41719304Speter * If the screen needs to be repainted, skip cursor optimization. 41819304Speter * However, in the code above we skipped leftright scrolling on 41919304Speter * the grounds that the cursor code would handle it. Make sure 42019304Speter * the right screen is up. 42119304Speter */ 42219304Speter if (F_ISSET(sp, SC_SCR_REDRAW)) { 42319304Speter if (O_ISSET(sp, O_LEFTRIGHT)) 42419304Speter goto slow; 42519304Speter goto paint; 42619304Speter } 42719304Speter 42819304Speter /* 42919304Speter * 7: Cursor movements (current screen only). 43019304Speter */ 43119304Speter if (!LF_ISSET(UPDATE_CURSOR)) 43219304Speter goto number; 43319304Speter 43419304Speter /* 43519304Speter * Decide cursor position. If the line has changed, the cursor has 43619304Speter * moved over a tab, or don't know where the cursor was, reparse the 43719304Speter * line. Otherwise, we've just moved over fixed-width characters, 43819304Speter * and can calculate the left/right scrolling and cursor movement 43919304Speter * without reparsing the line. Note that we don't know which (if any) 44019304Speter * of the characters between the old and new cursor positions changed. 44119304Speter * 44219304Speter * XXX 44319304Speter * With some work, it should be possible to handle tabs quickly, at 44419304Speter * least in obvious situations, like moving right and encountering 44519304Speter * a tab, without reparsing the whole line. 44619304Speter * 44719304Speter * If the line we're working with has changed, reread it.. 44819304Speter */ 44919304Speter if (F_ISSET(vip, VIP_CUR_INVALID) || LNO != OLNO) 45019304Speter goto slow; 45119304Speter 45219304Speter /* Otherwise, if nothing's changed, ignore the cursor. */ 45319304Speter if (CNO == OCNO) 45419304Speter goto fast; 45519304Speter 45619304Speter /* 45719304Speter * Get the current line. If this fails, we either have an empty 45819304Speter * file and can just repaint, or there's a real problem. This 45919304Speter * isn't a performance issue because there aren't any ways to get 46019304Speter * here repeatedly. 46119304Speter */ 46219304Speter if (db_eget(sp, LNO, &p, &len, &isempty)) { 46319304Speter if (isempty) 46419304Speter goto slow; 46519304Speter return (1); 46619304Speter } 46719304Speter 46819304Speter#ifdef DEBUG 46919304Speter /* Sanity checking. */ 47019304Speter if (CNO >= len && len != 0) { 471254225Speter msgq(sp, M_ERR, "Error: %s/%d: cno (%zu) >= len (%zu)", 47219304Speter tail(__FILE__), __LINE__, CNO, len); 47319304Speter return (1); 47419304Speter } 47519304Speter#endif 47619304Speter /* 47719304Speter * The basic scheme here is to look at the characters in between 47819304Speter * the old and new positions and decide how big they are on the 47919304Speter * screen, and therefore, how many screen positions to move. 48019304Speter */ 48119304Speter if (CNO < OCNO) { 48219304Speter /* 48319304Speter * 7a: Cursor moved left. 48419304Speter * 48519304Speter * Point to the old character. The old cursor position can 48619304Speter * be past EOL if, for example, we just deleted the rest of 48719304Speter * the line. In this case, since we don't know the width of 48819304Speter * the characters we traversed, we have to do it slowly. 48919304Speter */ 49019304Speter p += OCNO; 49119304Speter cnt = (OCNO - CNO) + 1; 49219304Speter if (OCNO >= len) 49319304Speter goto slow; 49419304Speter 49519304Speter /* 49619304Speter * Quick sanity check -- it's hard to figure out exactly when 49719304Speter * we cross a screen boundary as we do in the cursor right 49819304Speter * movement. If cnt is so large that we're going to cross the 49919304Speter * boundary no matter what, stop now. 50019304Speter */ 50119304Speter if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt) 50219304Speter goto slow; 50319304Speter 50419304Speter /* 50519304Speter * Count up the widths of the characters. If it's a tab 50619304Speter * character, go do it the the slow way. 50719304Speter */ 508254225Speter for (cwtotal = 0; cnt--; cwtotal += KEY_COL(sp, ch)) 509254225Speter if ((ch = *(UCHAR_T *)p--) == '\t') 51019304Speter goto slow; 51119304Speter 51219304Speter /* 51319304Speter * Decrement the screen cursor by the total width of the 51419304Speter * characters minus 1. 51519304Speter */ 51619304Speter cwtotal -= 1; 51719304Speter 51819304Speter /* 51919304Speter * If we're moving left, and there's a wide character in the 52019304Speter * current position, go to the end of the character. 52119304Speter */ 522254225Speter if (KEY_COL(sp, ch) > 1) 523254225Speter cwtotal -= KEY_COL(sp, ch) - 1; 52419304Speter 52519304Speter /* 52619304Speter * If the new column moved us off of the current logical line, 52719304Speter * calculate a new one. If doing leftright scrolling, we've 52819304Speter * moved off of the current screen, as well. 52919304Speter */ 53019304Speter if (SCNO < cwtotal) 53119304Speter goto slow; 53219304Speter SCNO -= cwtotal; 53319304Speter } else { 53419304Speter /* 53519304Speter * 7b: Cursor moved right. 53619304Speter * 53719304Speter * Point to the first character to the right. 53819304Speter */ 53919304Speter p += OCNO + 1; 54019304Speter cnt = CNO - OCNO; 54119304Speter 54219304Speter /* 54319304Speter * Count up the widths of the characters. If it's a tab 54419304Speter * character, go do it the the slow way. If we cross a 54519304Speter * screen boundary, we can quit. 54619304Speter */ 54719304Speter for (cwtotal = SCNO; cnt--;) { 548254225Speter if ((ch = *(UCHAR_T *)p++) == '\t') 54919304Speter goto slow; 550254225Speter if ((cwtotal += KEY_COL(sp, ch)) >= SCREEN_COLS(sp)) 55119304Speter break; 55219304Speter } 55319304Speter 55419304Speter /* 55519304Speter * Increment the screen cursor by the total width of the 55619304Speter * characters. 55719304Speter */ 55819304Speter SCNO = cwtotal; 55919304Speter 56019304Speter /* See screen change comment in section 6a. */ 56119304Speter if (SCNO >= SCREEN_COLS(sp)) 56219304Speter goto slow; 56319304Speter } 56419304Speter 56519304Speter /* 56619304Speter * 7c: Fast cursor update. 56719304Speter * 56819304Speter * We have the current column, retrieve the current row. 56919304Speter */ 57019304Speterfast: (void)gp->scr_cursor(sp, &y, ¬used); 57119304Speter goto done_cursor; 57219304Speter 57319304Speter /* 57419304Speter * 7d: Slow cursor update. 57519304Speter * 57619304Speter * Walk through the map and find the current line. 57719304Speter */ 57819304Speterslow: for (smp = HMAP; smp->lno != LNO; ++smp); 57919304Speter 58019304Speter /* 58119304Speter * 7e: Leftright scrolling adjustment. 58219304Speter * 58319304Speter * If doing left-right scrolling and the cursor movement has changed 58419304Speter * the displayed screen, scroll the screen left or right, unless we're 58519304Speter * updating the info line in which case we just scroll that one line. 58619304Speter * We adjust the offset up or down until we have a window that covers 58719304Speter * the current column, making sure that we adjust differently for the 58819304Speter * first screen as compared to subsequent ones. 58919304Speter */ 59019304Speter if (O_ISSET(sp, O_LEFTRIGHT)) { 59119304Speter /* 59219304Speter * Get the screen column for this character, and correct 59319304Speter * for the number option offset. 59419304Speter */ 59519304Speter cnt = vs_columns(sp, NULL, LNO, &CNO, NULL); 596281373Sbapt if (O_ISSET(sp, O_NUMBER)) 59719304Speter cnt -= O_NUMBER_LENGTH; 59819304Speter 59919304Speter /* Adjust the window towards the beginning of the line. */ 60019304Speter off = smp->coff; 60119304Speter if (off >= cnt) { 60219304Speter do { 60319304Speter if (off >= O_VAL(sp, O_SIDESCROLL)) 60419304Speter off -= O_VAL(sp, O_SIDESCROLL); 60519304Speter else { 60619304Speter off = 0; 60719304Speter break; 60819304Speter } 60919304Speter } while (off >= cnt); 61019304Speter goto shifted; 61119304Speter } 61219304Speter 61319304Speter /* Adjust the window towards the end of the line. */ 614254225Speter if ((off == 0 && off + SCREEN_COLS(sp) < cnt) || 615254225Speter (off != 0 && off + sp->cols < cnt)) { 61619304Speter do { 61719304Speter off += O_VAL(sp, O_SIDESCROLL); 61819304Speter } while (off + sp->cols < cnt); 61919304Speter 62019304Spetershifted: /* Fill in screen map with the new offset. */ 62119304Speter if (F_ISSET(sp, SC_TINPUT_INFO)) 62219304Speter smp->coff = off; 62319304Speter else { 62419304Speter for (smp = HMAP; smp <= TMAP; ++smp) 62519304Speter smp->coff = off; 62619304Speter leftright_warp = 1; 62719304Speter } 62819304Speter goto paint; 62919304Speter } 63019304Speter 63119304Speter /* 63219304Speter * We may have jumped here to adjust a leftright screen because 63319304Speter * redraw was set. If so, we have to paint the entire screen. 63419304Speter */ 63519304Speter if (F_ISSET(sp, SC_SCR_REDRAW)) 63619304Speter goto paint; 63719304Speter } 63819304Speter 63919304Speter /* 64019304Speter * Update the screen lines for this particular file line until we 64119304Speter * have a new screen cursor position. 64219304Speter */ 64319304Speter for (y = -1, 64419304Speter vip->sc_smap = NULL; smp <= TMAP && smp->lno == LNO; ++smp) { 64519304Speter if (vs_line(sp, smp, &y, &SCNO)) 64619304Speter return (1); 64719304Speter if (y != -1) { 64819304Speter vip->sc_smap = smp; 64919304Speter break; 65019304Speter } 65119304Speter } 65219304Speter goto done_cursor; 65319304Speter 65419304Speter /* 65519304Speter * 8: Repaint the entire screen. 65619304Speter * 65719304Speter * Lost big, do what you have to do. We flush the cache, since 65819304Speter * SC_SCR_REDRAW gets set when the screen isn't worth fixing, and 65919304Speter * it's simpler to repaint. So, don't trust anything that we 66019304Speter * think we know about it. 66119304Speter */ 66219304Speterpaint: for (smp = HMAP; smp <= TMAP; ++smp) 66319304Speter SMAP_FLUSH(smp); 66419304Speter for (y = -1, vip->sc_smap = NULL, smp = HMAP; smp <= TMAP; ++smp) { 66519304Speter if (vs_line(sp, smp, &y, &SCNO)) 66619304Speter return (1); 66719304Speter if (y != -1 && vip->sc_smap == NULL) 66819304Speter vip->sc_smap = smp; 66919304Speter } 67019304Speter /* 67119304Speter * If it's a small screen and we're redrawing, clear the unused lines, 67219304Speter * ex may have overwritten them. 67319304Speter */ 67419304Speter if (F_ISSET(sp, SC_SCR_REDRAW) && IS_SMALL(sp)) 67519304Speter for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { 67619304Speter (void)gp->scr_move(sp, cnt, 0); 67719304Speter (void)gp->scr_clrtoeol(sp); 67819304Speter } 67919304Speter 68019304Speter didpaint = 1; 68119304Speter 68219304Speterdone_cursor: 68319304Speter /* 68419304Speter * Sanity checking. When the repainting code messes up, the usual 68519304Speter * result is we don't repaint the cursor and so sc_smap will be 68619304Speter * NULL. If we're debugging, die, otherwise restart from scratch. 68719304Speter */ 68819304Speter#ifdef DEBUG 68919304Speter if (vip->sc_smap == NULL) 69019304Speter abort(); 69119304Speter#else 69219304Speter if (vip->sc_smap == NULL) { 69319304Speter F_SET(sp, SC_SCR_REFORMAT); 69419304Speter return (vs_paint(sp, flags)); 69519304Speter } 69619304Speter#endif 69719304Speter 69819304Speter /* 69919304Speter * 9: Set the remembered cursor values. 70019304Speter */ 70119304Speter OCNO = CNO; 70219304Speter OLNO = LNO; 70319304Speter 70419304Speter /* 70519304Speter * 10: Repaint the line numbers. 70619304Speter * 70719304Speter * If O_NUMBER is set and the VIP_N_RENUMBER bit is set, and we 70819304Speter * didn't repaint the screen, repaint all of the line numbers, 70919304Speter * they've changed. 71019304Speter */ 71119304Speternumber: if (O_ISSET(sp, O_NUMBER) && 71219304Speter F_ISSET(vip, VIP_N_RENUMBER) && !didpaint && vs_number(sp)) 71319304Speter return (1); 71419304Speter 71519304Speter /* 71619304Speter * 11: Update the mode line, position the cursor, and flush changes. 71719304Speter * 71819304Speter * If we warped the screen, we have to refresh everything. 71919304Speter */ 72019304Speter if (leftright_warp) 72119304Speter LF_SET(UPDATE_CURSOR | UPDATE_SCREEN); 72219304Speter 72319304Speter if (LF_ISSET(UPDATE_SCREEN) && !IS_ONELINE(sp) && 72419304Speter !F_ISSET(vip, VIP_S_MODELINE) && !F_ISSET(sp, SC_TINPUT_INFO)) 72519304Speter vs_modeline(sp); 72619304Speter 72719304Speter if (LF_ISSET(UPDATE_CURSOR)) { 72819304Speter (void)gp->scr_move(sp, y, SCNO); 72919304Speter 73019304Speter /* 73119304Speter * XXX 73219304Speter * If the screen shifted, we recalculate the "most favorite" 73319304Speter * cursor position. Vi won't know that we've warped the 73419304Speter * screen, so it's going to have a wrong idea about where the 73519304Speter * cursor should be. This is vi's problem, and fixing it here 73619304Speter * is a gross layering violation. 73719304Speter */ 73819304Speter if (leftright_warp) 73919304Speter (void)vs_column(sp, &sp->rcm); 74019304Speter } 74119304Speter 74219304Speter if (LF_ISSET(UPDATE_SCREEN)) 74319304Speter (void)gp->scr_refresh(sp, F_ISSET(vip, VIP_N_EX_PAINT)); 74419304Speter 74519304Speter /* 12: Clear the flags that are handled by this routine. */ 74619304Speter F_CLR(sp, SC_SCR_CENTER | SC_SCR_REDRAW | SC_SCR_REFORMAT | SC_SCR_TOP); 74719304Speter F_CLR(vip, VIP_CUR_INVALID | 74819304Speter VIP_N_EX_PAINT | VIP_N_REFRESH | VIP_N_RENUMBER | VIP_S_MODELINE); 74919304Speter 75019304Speter return (0); 75119304Speter 75219304Speter#undef LNO 75319304Speter#undef OLNO 75419304Speter#undef CNO 75519304Speter#undef OCNO 75619304Speter#undef SCNO 75719304Speter} 75819304Speter 75919304Speter/* 76019304Speter * vs_modeline -- 76119304Speter * Update the mode line. 76219304Speter */ 76319304Speterstatic void 764254225Spetervs_modeline(SCR *sp) 76519304Speter{ 76619304Speter static char * const modes[] = { 76719304Speter "215|Append", /* SM_APPEND */ 76819304Speter "216|Change", /* SM_CHANGE */ 76919304Speter "217|Command", /* SM_COMMAND */ 77019304Speter "218|Insert", /* SM_INSERT */ 77119304Speter "219|Replace", /* SM_REPLACE */ 77219304Speter }; 77319304Speter GS *gp; 77419304Speter size_t cols, curcol, curlen, endpoint, len, midpoint; 775254225Speter const char *t = NULL; 77619304Speter int ellipsis; 777254225Speter char buf[20]; 77819304Speter 77919304Speter gp = sp->gp; 78019304Speter 78119304Speter /* 78219304Speter * We put down the file name, the ruler, the mode and the dirty flag. 78319304Speter * If there's not enough room, there's not enough room, we don't play 78419304Speter * any special games. We try to put the ruler in the middle and the 78519304Speter * mode and dirty flag at the end. 78619304Speter * 78719304Speter * !!! 78819304Speter * Leave the last character blank, in case it's a really dumb terminal 78919304Speter * with hardware scroll. Second, don't paint the last character in the 79019304Speter * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you. 79119304Speter * 79219304Speter * Move to the last line on the screen. 79319304Speter */ 79419304Speter (void)gp->scr_move(sp, LASTLINE(sp), 0); 79519304Speter 79619304Speter /* If more than one screen in the display, show the file name. */ 79719304Speter curlen = 0; 79819304Speter if (IS_SPLIT(sp)) { 799254225Speter CHAR_T *wp, *p; 800254225Speter size_t l; 801254225Speter 802254225Speter CHAR2INT(sp, sp->frp->name, strlen(sp->frp->name) + 1, wp, l); 803254225Speter p = wp + l; 804254225Speter for (ellipsis = 0, cols = sp->cols / 2; --p > wp;) { 80519304Speter if (*p == '/') { 80619304Speter ++p; 80719304Speter break; 80819304Speter } 809254225Speter if ((curlen += KEY_COL(sp, *p)) > cols) { 81019304Speter ellipsis = 3; 81119304Speter curlen += 81219304Speter KEY_LEN(sp, '.') * 3 + KEY_LEN(sp, ' '); 81319304Speter while (curlen > cols) { 81419304Speter ++p; 815254225Speter curlen -= KEY_COL(sp, *p); 81619304Speter } 81719304Speter break; 81819304Speter } 81919304Speter } 82019304Speter if (ellipsis) { 82119304Speter while (ellipsis--) 82219304Speter (void)gp->scr_addstr(sp, 82319304Speter KEY_NAME(sp, '.'), KEY_LEN(sp, '.')); 82419304Speter (void)gp->scr_addstr(sp, 82519304Speter KEY_NAME(sp, ' '), KEY_LEN(sp, ' ')); 82619304Speter } 82719304Speter for (; *p != '\0'; ++p) 82819304Speter (void)gp->scr_addstr(sp, 829254225Speter KEY_NAME(sp, *p), KEY_COL(sp, *p)); 83019304Speter } 83119304Speter 83219304Speter /* Clear the rest of the line. */ 83319304Speter (void)gp->scr_clrtoeol(sp); 83419304Speter 83519304Speter /* 83619304Speter * Display the ruler. If we're not at the midpoint yet, move there. 83719304Speter * Otherwise, add in two extra spaces. 83819304Speter * 83919304Speter * Adjust the current column for the fact that the editor uses it as 84019304Speter * a zero-based number. 84119304Speter * 84219304Speter * XXX 84319304Speter * Assume that numbers, commas, and spaces only take up a single 84419304Speter * column on the screen. 84519304Speter */ 84619304Speter cols = sp->cols - 1; 84719304Speter if (O_ISSET(sp, O_RULER)) { 84819304Speter vs_column(sp, &curcol); 84938022Sbde len = snprintf(buf, sizeof(buf), "%lu,%lu", 85038022Sbde (u_long)sp->lno, (u_long)(curcol + 1)); 85119304Speter 85219304Speter midpoint = (cols - ((len + 1) / 2)) / 2; 85319304Speter if (curlen < midpoint) { 85419304Speter (void)gp->scr_move(sp, LASTLINE(sp), midpoint); 85519304Speter curlen += len; 85619304Speter } else if (curlen + 2 + len < cols) { 85719304Speter (void)gp->scr_addstr(sp, " ", 2); 85819304Speter curlen += 2 + len; 85919304Speter } 86019304Speter (void)gp->scr_addstr(sp, buf, len); 86119304Speter } 86219304Speter 86319304Speter /* 86419304Speter * Display the mode and the modified flag, as close to the end of the 86519304Speter * line as possible, but guaranteeing at least two spaces between the 86619304Speter * ruler and the modified flag. 86719304Speter */ 86819304Speter#define MODESIZE 9 86919304Speter endpoint = cols; 87019304Speter if (O_ISSET(sp, O_SHOWMODE)) { 87119304Speter if (F_ISSET(sp->ep, F_MODIFIED)) 87219304Speter --endpoint; 87319304Speter t = msg_cat(sp, modes[sp->showmode], &len); 87419304Speter endpoint -= len; 87519304Speter } 87619304Speter 87719304Speter if (endpoint > curlen + 2) { 87819304Speter (void)gp->scr_move(sp, LASTLINE(sp), endpoint); 87919304Speter if (O_ISSET(sp, O_SHOWMODE)) { 88019304Speter if (F_ISSET(sp->ep, F_MODIFIED)) 88119304Speter (void)gp->scr_addstr(sp, 88219304Speter KEY_NAME(sp, '*'), KEY_LEN(sp, '*')); 88319304Speter (void)gp->scr_addstr(sp, t, len); 88419304Speter } 88519304Speter } 88619304Speter} 887