1 /*
2 * The functions in this file handle redisplay. There are two halves, the
3 * ones that update the virtual display screen, and the ones that make the
4 * physical display screen the same as the virtual display screen. These
5 * functions use hints that are left in the windows by the commands.
6 *
7 * $Id: display.c,v 1.580 2021/05/09 21:32:16 Lois.Mansot Exp $
8 */
9
10 #include "estruct.h"
11 #include "edef.h"
12 #include "pscreen.h"
13 #include "nefsms.h"
14
15 #define vMAXINT ((int)((unsigned)(~0)>>1)) /* 0x7fffffff */
16 #define vMAXNEG (-vMAXINT) /* 0x80000001 */
17
18 #define NU_WIDTH 8
19 #define NU_GUTTER 1
20
21 #if DISP_X11
22 #define FontHasGlyph(ch) gui_isprint(ch)
23 #define UseCellWidth(wp, ch) \
24 (!w_val(wp, WMDUNICODE_AS_HEX) && FontHasGlyph(ch))
25 #else
26 #define FontHasGlyph(ch) TRUE
27 #define UseCellWidth(wp, ch) \
28 (!w_val(wp, WMDUNICODE_AS_HEX))
29 #endif
30
31 #define UseCellWidth2(wp, lp, off) \
32 UseCellWidth(wp, get_char2(lp, off))
33
34 #define reset_term_attrs() term.rev(0)
35
36 #ifdef GVAL_VIDEO
37 #define VIDEOATTRS (VABOLD|VAUL|VAITAL)
38 #define set_term_attrs(a) term.rev(a ^ ((VIDEO_ATTR) global_g_val(GVAL_VIDEO) & VIDEOATTRS))
39 #else
40 #define set_term_attrs(a) term.rev(a)
41 #endif
42
43 #ifdef WMDLINEWRAP /* overrides left/right scrolling */
44 #define if_LINEWRAP(wp, t, f) w_val(wp, WMDLINEWRAP) ? (t) : (f)
45 #else
46 #define if_LINEWRAP(wp, t, f) (f)
47 #endif
48
49 #define line_has_newline(lp, bp) \
50 (b_val(bp, MDNEWLINE) || (lforw(lp) != buf_head(bp)))
51
52 #define MRK_EMPTY "~"
53 #define MRK_EXTEND_LEFT "<"
54 #define MRK_EXTEND_RIGHT ">"
55
56 VIDEO **vscreen; /* Virtual screen. */
57 VIDEO **pscreen; /* Physical screen. */
58
59 typedef struct {
60 LINE *lp; /* the line itself */
61 int map; /* map to line-number in window */
62 int left; /* offset of leftmost character on line */
63 int right; /* offset of rightmost character on line */
64 } LMAP;
65 static LMAP *lmap0; /* where to _not_ alter attributes */
66 static LMAP *lmap; /* workspace for computing attributes */
67
68 #if OPT_SCROLLCODE
69 #define CAN_SCROLL 1
70 #else
71 #define CAN_SCROLL 0
72 #endif
73
74 static int i_displayed; /* false until we're in screen-mode */
75 static int mpresf; /* zero if message-line empty */
76 #if OPT_WORKING
77 static int im_timing;
78 #endif
79
80 /*
81 * MARK2COL may be greater than mark2col if the mark does not point to the end
82 * of the line, and if it points to a nonprinting character. We use that value
83 * when setting visible attributes, to keep tabs and other nonprinting
84 * characters looking 'right'.
85 */
86 #define MARK2COL(wp, mk) offs2col(wp, mk.l, mk.o + BytesAt(mk.l, mk.o)) - 1
87 #define mark2col(wp, mk) offs2col(wp, mk.l, mk.o)
88
89 #ifdef WMDLINEWRAP
90 #define TopRow(wp) (wp)->w_toprow + (wp)->w_line.o
91 static int allow_wrap;
92 #else
93 #define TopRow(wp) (wp)->w_toprow
94 #endif
95
96 /* for window size changes */
97 static int chg_width, chg_height;
98
99 /******************************************************************************/
100
101 typedef int (*OutFunc) (int c);
102
103 static OutFunc dfoutfn;
104
105 static int vtlistc(WINDOW *wp, const char *src, unsigned limit);
106
107 /*--------------------------------------------------------------------------*/
108
109 /*
110 * Format a number, right-justified, returning a pointer to the formatted
111 * buffer.
112 */
113 static char *
right_num(char * buffer,int len,long value)114 right_num(char *buffer, int len, long value)
115 {
116 char temp[NSTRING];
117 char *p = lsprintf(temp, "%ld", value);
118 char *q = buffer + len;
119
120 *q = EOS;
121 while (q != buffer)
122 *(--q) = (char) ((p != temp) ? *(--p) : ' ');
123 return buffer;
124 }
125
126 /*
127 * Do format a string. Return the number of bytes by which 'width' exceeds
128 * that actually written to the output.
129 */
130 static int
dfputsn(OutFunc outfunc,const char * s,int width,int limit)131 dfputsn(OutFunc outfunc, const char *s, int width, int limit)
132 {
133 int l = 0;
134
135 if (s != 0) {
136 int length = (int) strlen(s);
137
138 if (width < 0)
139 width = length;
140 if (limit > 0 && width > limit)
141 width = limit;
142 if (width > length)
143 width = length;
144
145 TRACE2(("...str=%s\n", visible_buff(s, width, TRUE)));
146 while (width-- > 0) {
147 (*outfunc) (*s++);
148 l++;
149 }
150 }
151 return l;
152 }
153
154 /* as above, but uses null-terminated string's length */
155 static int
dfputs(OutFunc outfunc,const char * s)156 dfputs(OutFunc outfunc, const char *s)
157 {
158 return dfputsn(outfunc, s, -1, -1);
159 }
160
161 /*
162 * Do format an integer, in the specified radix.
163 */
164 static int
dfputi(OutFunc outfunc,UINT i,UINT r)165 dfputi(OutFunc outfunc, UINT i, UINT r)
166 {
167 int q;
168
169 TRACE2(("...int=%d\n", i));
170
171 q = (i >= r) ? dfputi(outfunc, i / r, r) : 0;
172
173 (*outfunc) (hexdigits[i % r]);
174 return q + 1; /* number of digits printed */
175 }
176
177 /*
178 * do the same except as a long integer.
179 */
180 static int
dfputli(OutFunc outfunc,ULONG l,UINT r)181 dfputli(OutFunc outfunc, ULONG l, UINT r)
182 {
183 int q;
184
185 TRACE2(("...long=%ld\n", l));
186
187 q = (l >= r) ? dfputli(outfunc, (l / r), r) : 0;
188
189 return q + dfputi(outfunc, (UINT) (l % r), r);
190 }
191
192 /*
193 * format a floating value with two decimal places
194 */
195 static int
dfputf(OutFunc outfunc,double s)196 dfputf(OutFunc outfunc, double s)
197 {
198 int n = 3;
199 UINT i; /* integer portion of number */
200 UINT f; /* fractional portion of number */
201
202 if (s < 0) {
203 (*outfunc) ('.');
204 s = -s;
205 n++;
206 }
207
208 /* break it up */
209 i = (UINT) s;
210 f = (UINT) ((s - (double) i) * 100);
211
212 /* send out the integer portion */
213 n += dfputi(outfunc, i, 10);
214 (*outfunc) ('.');
215 (*outfunc) ((int) (f / 10) + '0');
216 (*outfunc) ((int) (f % 10) + '0');
217 return n;
218 }
219
220 /*
221 * On entry, *fmt may point to either '*' or a digit. If either, decode the
222 * appropriate width or limit.
223 */
224 static void
decode_length(const char ** fmt,int * result)225 decode_length(const char **fmt, int *result)
226 {
227 int c = **fmt;
228 int value = 0;
229 int found = FALSE;
230
231 while (isDigit(c)) {
232 value = (value * 10) + c - '0';
233 found = TRUE;
234 (*fmt)++;
235 c = **fmt;
236 }
237 if (found)
238 *result = value;
239 }
240
241 #define DecodeLength(fmt, app, result) \
242 if (*fmt == '*') { \
243 result = va_arg(app, int); \
244 ++fmt; \
245 } else { \
246 decode_length(&fmt, &result); \
247 }
248
249 /*
250 * Generic string formatter. Takes printf-like args, and calls
251 * the global function (*dfoutfn)(c) for each c
252 */
253 static void
dofmt(const char * fmt,va_list app2)254 dofmt(const char *fmt, va_list app2)
255 {
256 int c; /* current char in format string */
257 int the_width;
258 int the_limit = 0;
259 int n;
260 int islong;
261 UINT uint_value;
262 ULONG ulong_value;
263 int int_value;
264 long long_value;
265 va_list app;
266 OutFunc outfunc = dfoutfn; /* local copy, for recursion */
267
268 TRACE2(("dofmt fmt='%s'\n", visible_buff(fmt, strlen(fmt), FALSE)));
269 begin_va_copy(app, app2);
270 while ((c = *fmt++) != 0) {
271 if (c != '%') {
272 (*outfunc) (c);
273 continue;
274 }
275 the_width = -1;
276 islong = FALSE;
277 DecodeLength(fmt, app, the_width);
278 if (*fmt == '.') {
279 ++fmt;
280 DecodeLength(fmt, app, the_limit);
281 if (the_width < 0)
282 the_width = the_limit;
283 } else {
284 the_limit = the_width;
285 }
286 c = *fmt++;
287 if (c == 'l') {
288 islong = TRUE;
289 c = *fmt++;
290 }
291 TRACE2(("... fmt='%%%d.%d%c%s'\n",
292 the_width, the_limit,
293 c, islong ? "L" : ""));
294 switch (c) {
295 case EOS:
296 n = 0;
297 break;
298 case 'c':
299 (*outfunc) (va_arg(app, int));
300 n = 1;
301 break;
302
303 case 'd':
304 if (!islong) {
305 int_value = va_arg(app, int);
306 if (int_value < 0) {
307 if (int_value < vMAXNEG) {
308 n = dfputs(outfunc, "OVFL");
309 break;
310 }
311 int_value = -int_value;
312 (*outfunc) ('-');
313 }
314 n = dfputi(outfunc, (UINT) int_value, 10);
315 break;
316 }
317 long_value = va_arg(app, long);
318 if (long_value < 0) {
319 long_value = -long_value;
320 (*outfunc) ('-');
321 }
322 n = dfputli(outfunc, (ULONG) long_value, 10);
323 break;
324
325 case 'u':
326 if (!islong) {
327 uint_value = va_arg(app, UINT);
328 n = dfputi(outfunc, uint_value, 10);
329 break;
330 }
331 ulong_value = va_arg(app, ULONG);
332 n = dfputli(outfunc, ulong_value, 10);
333 break;
334
335 case 'o':
336 n = dfputi(outfunc, va_arg(app, UINT), 8);
337 break;
338
339 case 'x':
340 if (!islong) {
341 n = dfputi(outfunc, va_arg(app, UINT), 16);
342 break;
343 }
344 /* FALLTHROUGH */
345 case 'X':
346 n = dfputli(outfunc, va_arg(app, ULONG), 16);
347 break;
348
349 case 's':
350 n = dfputsn(outfunc, va_arg(app, char *), the_width, the_limit);
351 break;
352
353 case 'f':
354 n = dfputf(outfunc, va_arg(app, double));
355 break;
356
357 default:
358 (*outfunc) (c);
359 n = 1;
360 break;
361 }
362 the_width -= n;
363 while (the_width-- > 0) {
364 (*outfunc) (' ');
365 }
366 }
367 end_va_copy(app);
368 }
369
370 /******************************************************************************/
371
372 /*
373 * Line-number mode
374 */
375 int
nu_width(WINDOW * wp)376 nu_width(WINDOW *wp)
377 {
378 return w_val(wp, WMDNUMBER) ? NU_WIDTH : 0;
379 }
380
381 int
col_limit(WINDOW * wp)382 col_limit(WINDOW *wp)
383 {
384 return if_LINEWRAP(wp, curcol + 1, term.cols - 1 - nu_width(wp));
385 }
386
387 #if OPT_VIDEO_ATTRS
388 static void
set_vattrs(int row,int col,unsigned attr,size_t len)389 set_vattrs(int row, int col, unsigned attr, size_t len)
390 {
391 if ((long) len > 0) {
392 while (len--)
393 vscreen[row]->v_attrs[col++] = (VIDEO_ATTR) attr;
394 }
395 }
396
397 static void
preset_lmap0(void)398 preset_lmap0(void)
399 {
400 int row;
401 for (row = 0; row <= term.rows; ++row) {
402 lmap0[row].left = 0;
403 lmap0[row].right = term.cols;
404 }
405 }
406
407 /* use this to markup line-numbers and line-break padding, which conveniently
408 * are both on the extreme left/right of the line on the display.
409 */
410 #if OPT_EXTRA_COLOR
411 static void
preset_vattrs(int row,int col,int attr,size_t len)412 preset_vattrs(int row, int col, int attr, size_t len)
413 {
414 set_vattrs(row, col, (unsigned) attr, len);
415 if (col == 0) {
416 lmap0[row].left = (int) len;
417 } else {
418 lmap0[row].right = col;
419 }
420 }
421 #endif
422
423 #else
424
425 #define preset_lmap0() /* nothing */
426 #define set_vattrs(row, col, attr, len) /*nothing */
427 #define preset_vattrs(row, col, attr, len) /*nothing */
428
429 #endif
430
431 static void
freeVIDEO(VIDEO * vp)432 freeVIDEO(VIDEO * vp)
433 {
434 if (vp != 0) {
435 #if OPT_VIDEO_ATTRS
436 FreeIfNeeded(VideoAttr(vp));
437 #endif
438 free((char *) vp);
439 }
440 }
441
442 int
video_alloc(VIDEO ** vpp)443 video_alloc(VIDEO ** vpp)
444 {
445 VIDEO *vp;
446 size_t have = (size_t) ((term.maxcols > 0) ? term.maxcols : 1);
447 size_t need = sizeof(VIDEO_TEXT) * (have - 1);
448
449 if ((vp = typeallocplus(VIDEO, need)) != 0) {
450 (void) memset((char *) vp, 0, sizeof(VIDEO) + need);
451
452 #if OPT_VIDEO_ATTRS
453 VideoAttr(vp) = typecallocn(VIDEO_ATTR, have);
454 if (VideoAttr(vp) == 0) {
455 FreeAndNull(vp);
456 }
457 #endif
458 if (vp != 0) {
459 freeVIDEO(*vpp);
460 *vpp = vp;
461 }
462 }
463 return (vp != 0);
464 }
465
466 static int
vtalloc(void)467 vtalloc(void)
468 {
469 int i, first;
470 static int vcols, vrows;
471
472 if (term.maxrows > vrows) {
473 GROW(vscreen, VIDEO *, vrows, term.maxrows);
474 GROW(pscreen, VIDEO *, vrows, term.maxrows);
475 GROW(lmap0, LMAP, vrows, ((size_t) term.maxrows + 1));
476 GROW(lmap, LMAP, vrows, ((size_t) term.maxrows + 1));
477 } else {
478 for (i = term.maxrows; i < vrows; i++) {
479 freeVIDEO(vscreen[i]);
480 freeVIDEO(pscreen[i]);
481 }
482 }
483
484 first = (term.maxcols > vcols) ? 0 : vrows;
485
486 for (i = first; i < term.maxrows; ++i) {
487 if (!video_alloc(&vscreen[i]))
488 return FALSE;
489 if (!video_alloc(&pscreen[i]))
490 return FALSE;
491 }
492 vcols = term.maxcols;
493 vrows = term.maxrows;
494
495 return TRUE;
496 }
497
498 /* free all video memory, in anticipation of a (growing) resize */
499 #if NO_LEAKS
500 static void
vtfree(void)501 vtfree(void)
502 {
503 int i;
504
505 if (vscreen) {
506 for (i = 0; i < term.maxrows; ++i) {
507 freeVIDEO(vscreen[i]);
508 }
509 free((char *) vscreen);
510 vscreen = 0;
511 }
512 if (pscreen) {
513 for (i = 0; i < term.maxrows; ++i) {
514 freeVIDEO(pscreen[i]);
515 }
516 free((char *) pscreen);
517 pscreen = 0;
518 }
519 FreeIfNeeded(lmap0);
520 FreeIfNeeded(lmap);
521 }
522 #endif
523
524 /*
525 * Returns true if the virtual screen has been allocated and initialized,
526 * so we're able to create/update windows.
527 */
528 int
is_vtinit(void)529 is_vtinit(void)
530 {
531 return (vscreen != 0);
532 }
533
534 /*
535 * Initialize the data structures used by the display code. The edge vectors
536 * used to access the screens are set up.
537 */
538 int
vtinit(void)539 vtinit(void)
540 {
541 int rc = TRUE;
542 int i;
543 VIDEO *vp;
544
545 beginDisplay();
546 /* allocate new display memory */
547 if (vtalloc() == FALSE) { /* if we fail, only serious if not a realloc */
548 rc = is_vtinit();
549 } else {
550 for (i = 0; i < term.maxrows; ++i) {
551 vp = vscreen[i];
552 vp->v_flag = 0;
553 #if OPT_COLOR
554 ReqFcolor(vp) = gfcolor;
555 ReqBcolor(vp) = gbcolor;
556 #endif
557 }
558 #if OPT_WORKING
559 if (!i_displayed && !im_displaying)
560 imworking(0);
561 #endif
562 }
563 endofDisplay();
564 return rc;
565 }
566
567 /*
568 * Set the virtual cursor to the specified row and column on the virtual
569 * screen. There is no checking for nonsense values.
570 */
571 static void
vtmove(int row,int col)572 vtmove(int row, int col)
573 {
574 vtrow = row;
575 vtcol = col;
576 }
577
578 static VIDEO *
current_video(void)579 current_video(void)
580 {
581 VIDEO *vp;
582
583 #ifdef WMDLINEWRAP
584 if (vtrow < 0) {
585 static VIDEO *fake_line;
586 static int length;
587 if (length != term.maxcols || fake_line == 0) {
588 if (video_alloc(&fake_line)) {
589 length = term.maxcols;
590 } else {
591 freeVIDEO(fake_line);
592 fake_line = 0;
593 }
594 }
595 vp = fake_line;
596 } else
597 #endif
598 vp = vscreen[vtrow];
599
600 return vp;
601 }
602
603 static void
mark_extent(int row,int col,int ch)604 mark_extent(int row, int col, int ch)
605 {
606 if (row >= 0 && row < term.maxrows && col >= 0 && col < term.maxcols) {
607 #if OPT_EXTRA_COLOR && OPT_VIDEO_ATTRS
608 int *attrp = lookup_extra_color(XCOLOR_LINEBREAK);
609 if (!isEmpty(attrp)) {
610 set_vattrs(row, col, (unsigned) *attrp, 1);
611 if (ch == MRK_EXTEND_LEFT[0]) {
612 lmap0[row].left = col + 1;
613 } else {
614 lmap0[row].right = col;
615 }
616 }
617 #endif /* OPT_EXTRA_COLOR */
618 vscreen[row]->v_text[col] = (VIDEO_TEXT) ch;
619 }
620 }
621
622 static int
current_limit(void)623 current_limit(void)
624 {
625 return ((vtrow == term.rows - 1)
626 ? (term.cols - 1)
627 : term.cols);
628 }
629
630 /*
631 * Given an offset limit and a string, find the offset of the first blank
632 * within that limit. Compute the corresponding number of columns.
633 */
634 #ifdef WMDLINEWRAP
635 static unsigned
cols_until(WINDOW * wp,const char * src,unsigned limit)636 cols_until(WINDOW *wp, const char *src, unsigned limit)
637 {
638 unsigned n;
639 unsigned cols = 0;
640 int used;
641 int nxt;
642 int adj;
643
644 for (n = 0; n < limit; ++n) {
645 if (isBlank(src[n])) {
646 break;
647 }
648 used = 1;
649 nxt = (int) (limit - n);
650 adj = column_sizes(wp, src + n, (unsigned) nxt, &used);
651
652 #if OPT_MULTIBYTE
653 if (adj == COLS_UTF8
654 && b_is_utfXX(wp->w_bufp)
655 && !w_val(wp, WMDUNICODE_AS_HEX)) {
656 UINT target;
657 int ch2;
658
659 if (vl_conv_to_utf32(&target,
660 src + n,
661 (B_COUNT) (limit - n)) > 0) {
662 ch2 = (int) target;
663 } else {
664 ch2 = CharOf(src[n]);
665 }
666 (void) ch2;
667 if (FontHasGlyph(ch2)) {
668 adj = mb_cellwidth(wp, src + n, nxt);
669 }
670 }
671 #endif
672 cols += (unsigned) adj;
673 n += (unsigned) (used - 1);
674 }
675 return cols;
676 }
677 #endif
678
679 /*
680 * Write a character to the virtual screen. The virtual row and column are
681 * updated. Only print characters if they would be "visible". If the line is
682 * too long put a ">" in the last column. This routine only puts printing
683 * characters into the virtual terminal buffers. Only column overflow is
684 * checked.
685 *
686 * Returns the number of bytes eaten from the source.
687 */
688 static int
vtputc(WINDOW * wp,const char * src,unsigned limit)689 vtputc(WINDOW *wp, const char *src, unsigned limit)
690 {
691 /* since we don't allow wrapping on the message line, we only need
692 * to evaluate this once. */
693 int lastcol = current_limit();
694 int rc = 1;
695 UINT ch = (UCHAR) (*src);
696 VIDEO *vp = current_video();
697
698 if (vp != 0) {
699 if (isPrint(ch) && vtcol >= 0 && vtcol < lastcol) {
700 #if OPT_MULTIBYTE
701 if (!b_is_utfXX(wp->w_bufp) && (vl_encoding >= enc_UTF8)) {
702 int ch2;
703 if (vl_8bit_to_ucs(&ch2, (int) ch)) {
704 ch = (UINT) ch2;
705 }
706 }
707 #endif
708 #ifdef WMDLINEWRAP
709 if ((allow_wrap != 0)
710 && w_val(wp, WMDLINEBREAK)
711 && w_val(wp, WMDLINEWRAP)
712 && vtcol > 0
713 && VideoText(vp)[vtcol - 1] == ' '
714 && !isBlank(ch)) {
715 unsigned n = cols_until(wp, src, limit);
716 int have = term.cols - vtcol;
717
718 if (n < limit)
719 ++n;
720 if ((int) n > have) {
721 #if OPT_EXTRA_COLOR
722 int *attrp = lookup_extra_color(XCOLOR_LINEBREAK);
723 if (!isEmpty(attrp)) {
724 preset_vattrs(vtrow, vtcol, *attrp,
725 (size_t) term.cols - (size_t) vtcol);
726 }
727 #endif /* OPT_EXTRA_COLOR */
728 while (vtcol < term.cols)
729 VideoText(vp)[vtcol++] = ' ';
730 vtcol = 0;
731 ++vtrow;
732 vp = current_video();
733 vp->v_flag |= VFCHG;
734 horscroll += lastcol;
735 }
736 }
737 #endif
738 VideoText(vp)[vtcol++] = (VIDEO_TEXT) ch;
739 #ifdef WMDLINEWRAP
740 if ((allow_wrap != 0)
741 && (vtcol == lastcol)
742 && (vtrow < allow_wrap)) {
743 vtcol = 0;
744 if (++vtrow >= 0)
745 vscreen[vtrow]->v_flag |= VFCHG;
746 horscroll += lastcol;
747 }
748 #endif
749 } else if (vtcol >= lastcol) {
750 mark_extent(vtrow, lastcol - 1, MRK_EXTEND_RIGHT[0]);
751 } else if (isTab(ch)) {
752 do {
753 vtputc(wp, " ", 1);
754 } while (((vtcol + horscroll) % wp->w_tabstop) != 0
755 && vtcol < lastcol);
756 } else if (!is_record_sep(wp->w_bufp, (int) ch)) {
757 if (isPrint(ch)) {
758 ++vtcol;
759 } else {
760 rc = vtlistc(wp, src, limit);
761 }
762 }
763 }
764 return rc;
765 }
766
767 static void
vtputsn(WINDOW * wp,const char * src,size_t n)768 vtputsn(WINDOW *wp, const char *src, size_t n)
769 {
770 if (src != 0) {
771 while (n != 0 && *src != EOS) {
772 vtputc(wp, src++, (unsigned) (n--));
773 }
774 }
775 }
776
777 /*
778 * Shows non-printing character. Returns the number of bytes eaten from the
779 * source, which will always be one except when decoding UTF-8.
780 */
781 static int
vtlistc(WINDOW * wp,const char * src,unsigned limit)782 vtlistc(WINDOW *wp, const char *src, unsigned limit)
783 {
784 int rc = 1;
785 char temp[10];
786 unsigned n = 0;
787
788 #if OPT_MULTIBYTE
789 if (b_is_utfXX(wp->w_bufp)) {
790 UINT value;
791 int need = column_sizes(wp, src, limit, &rc);
792 int cells;
793
794 switch (need) {
795 case COLS_UTF8:
796 rc = vl_conv_to_utf32(&value, src, (B_COUNT) limit);
797 cells = vl_wcwidth((int) value);
798
799 if (!w_val(wp, WMDUNICODE_AS_HEX)
800 && cells == 1
801 && isPrint(value)
802 && FoldTo8bits((int) value)) {
803 temp[n++] = (char) value;
804 } else if (cells > 0
805 && term_is_utfXX()
806 && UseCellWidth(wp, (int) value)) {
807 /*
808 * It does not fit into the "8bit" mapping, but is printable
809 * (since the number of cells is nonzero). Write the Unicode
810 * value directly to the grid (do not try to recur back here
811 * again).
812 */
813 VIDEO *vp = current_video();
814 int lastcol = current_limit();
815
816 if (vtcol >= 0 && vtcol + cells <= lastcol) {
817 /* FIXME - adapt the wrapping logic and margin marker too */
818 VideoText(vp)[vtcol++] = (VIDEO_TEXT) value;
819 /*
820 * Pad multicell values with 0's which the terminal driver
821 * has to remove. The padding lets us update 'vtcol' in a
822 * simple way.
823 */
824 while (--cells > 0)
825 VideoText(vp)[vtcol++] = 0;
826 #ifdef WMDLINEWRAP
827 if ((allow_wrap != 0)
828 && (vtcol == lastcol)
829 && (vtrow < allow_wrap)) {
830 vtcol = 0;
831 if (++vtrow >= 0)
832 vscreen[vtrow]->v_flag |= VFCHG;
833 horscroll += lastcol;
834 }
835 #endif
836 } else if (vtcol >= lastcol) {
837 if (VideoText(vp)[lastcol - 1] == 0)
838 mark_extent(vtrow, lastcol - 2, MRK_EXTEND_RIGHT[0]);
839 mark_extent(vtrow, lastcol - 1, MRK_EXTEND_RIGHT[0]);
840 } else {
841 int nulls = cells;
842 /* have to account for split-characters... */
843 while (cells-- > 0 && vtcol <= lastcol) {
844 mark_extent(vtrow, vtcol++, MRK_EXTEND_RIGHT[0]);
845 }
846 #ifdef WMDLINEWRAP
847 if (allow_wrap != 0
848 && (vtrow < allow_wrap)) {
849 vtcol = 0;
850 if (++vtrow >= 0) {
851 vscreen[vtrow]->v_flag |= VFCHG;
852 vp = current_video();
853 VideoText(vp)[vtcol++] = (VIDEO_TEXT) value;
854 while (--nulls > 0)
855 VideoText(vp)[vtcol++] = 0;
856 }
857 }
858 #endif
859 }
860 return rc;
861 } else {
862 /* FIXME - some of recode's UTF-8/UTF-16 gives 6-digit codes
863 * rather than 4 - but the low 4 digits are the same.
864 */
865 sprintf(temp, "\\u%04X", value & ((1 << MaxCBits) - 1));
866 n = (unsigned) need;
867 }
868 break;
869 case COLS_CTRL:
870 if (isPrint(*src)) {
871 temp[n++] = *src;
872 } else {
873 temp[n++] = '^';
874 temp[n++] = (char) toalpha(*src);
875 }
876 break;
877 case COLS_8BIT:
878 /*
879 * Recover from illegal bytes...
880 */
881 TRACE2(("vtlistc illegal:%#x\n", CharOf(*src)));
882 sprintf(temp, "\\?%02X", CharOf(*src));
883 n = (unsigned) need;
884 break;
885 default:
886 temp[n++] = *src;
887 break;
888 }
889 } else
890 #endif
891 if (isPrint(*src)) {
892 temp[n++] = *src;
893 } else {
894 int need = column_sizes(wp, src, limit, &rc);
895
896 switch (need) {
897 case COLS_8BIT:
898 temp[n++] = BACKSLASH;
899 if (w_val(wp, WMDNONPRINTOCTAL)) {
900 temp[n++] = (char) (((*src >> 6) & 3) + '0');
901 temp[n++] = (char) (((*src >> 3) & 7) + '0');
902 temp[n++] = (char) (((*src) & 7) + '0');
903 } else {
904 temp[n++] = 'x';
905 temp[n++] = hexdigits[(*src >> 4) & 0xf];
906 temp[n++] = hexdigits[(*src) & 0xf];
907 }
908 break;
909 case COLS_CTRL:
910 temp[n++] = '^';
911 temp[n++] = (char) toalpha(*src);
912 break;
913 default:
914 temp[n++] = *src;
915 break;
916 }
917 }
918 vtputsn(wp, temp, (size_t) n);
919 return rc;
920 }
921
922 #if OPT_MULTIBYTE
923 static int
vtset_put(WINDOW * wp,const char * src,unsigned limit)924 vtset_put(WINDOW *wp, const char *src, unsigned limit)
925 {
926 int rc;
927
928 if (w_val(wp, WMDLIST)) {
929 rc = vtlistc(wp, src, limit);
930 } else if (b_is_utfXX(wp->w_bufp)
931 && column_sizes(wp, src, limit, &rc) >= COLS_8BIT) {
932 rc = vtlistc(wp, src, limit);
933 } else {
934 rc = vtputc(wp, src, limit);
935 }
936 return rc;
937 }
938 #else
939 #define vtset_put(wp, src, n) (w_val(wp, WMDLIST) \
940 ? vtlistc(wp, src, n) \
941 : vtputc(wp, src, n))
942 #endif
943 /*
944 * Write a line to the screen at the current video coordinates, allowing for
945 * line-wrap or right-shifting.
946 */
947 static void
vtset(LINE * lp,WINDOW * wp)948 vtset(LINE *lp, WINDOW *wp)
949 {
950 #if OPT_TRACE > 1
951 int save_vtrow = vtrow;
952 #endif
953 char *from;
954 int n = llength(lp);
955 BUFFER *bp = wp->w_bufp;
956 int skip = -vtcol;
957 int list = w_val(wp, WMDLIST);
958
959 TRACE2(("vtset line %d (top %d.%d), row %d \n",
960 line_no(wp->w_bufp, lp),
961 line_no(wp->w_bufp, wp->w_line.l),
962 wp->w_line.o,
963 vtrow));
964 wp->w_tabstop = tabstop_val(wp->w_bufp);
965
966 #ifdef WMDLINEWRAP
967 /*
968 * If the window's offset is negative, we've got a case of linewrap
969 * where the line's beginning is forced before the beginning of the
970 * window.
971 */
972 if (wp->w_line.o < 0) {
973 vtrow -= wp->w_line.o;
974 skip = col2offs(wp, lp, -(wp->w_line.o * term.cols));
975 n -= skip;
976 } else
977 #endif
978 if (w_val(wp, WMDNUMBER)) {
979 int j, k, jk;
980 L_NUM line = line_no(bp, lp);
981 char temp[NU_WIDTH + 2];
982
983 vtcol = 0; /* make sure we always see line numbers */
984 vtputsn(wp,
985 right_num(temp, NU_WIDTH - NU_GUTTER, (long) line),
986 (size_t) (NU_WIDTH - NU_GUTTER));
987 vtputsn(wp, " ", (size_t) NU_GUTTER);
988 #if OPT_EXTRA_COLOR
989 {
990 int *attrp = lookup_extra_color(XCOLOR_LINENUMBER);
991 if (!isEmpty(attrp)) {
992 preset_vattrs(vtrow, 0, *attrp, (NU_WIDTH - NU_GUTTER));
993 }
994 }
995 #endif /* OPT_EXTRA_COLOR */
996 horscroll = skip - vtcol;
997
998 /* account for leading fill; this repeats logic in vtputc so
999 * I don't have to introduce a global variable... */
1000 from = lvalue(lp);
1001 for (j = k = jk = 0; (j < n) && (k < skip);) {
1002 int c = from[j];
1003 int used = 1;
1004 if ((list || !isTab(c)) && !isPrint(c)) {
1005 k += column_sizes(wp, from + j, (UINT) (n - j), &used);
1006 } else {
1007 if (isTab(c)) {
1008 k += (wp->w_tabstop - (k % wp->w_tabstop));
1009 } else if (isPrint(c)) {
1010 k++;
1011 }
1012 }
1013 j += used;
1014 jk = j;
1015 }
1016 while (k-- > skip)
1017 vtputc(wp, " ", 1);
1018 if ((skip = jk) < 0)
1019 skip = 0;
1020 n -= skip;
1021 } else
1022 skip = 0;
1023
1024 #if OPT_B_LIMITS
1025 horscroll -= w_left_margin(wp);
1026 #endif
1027 from = lvalue(lp) + skip;
1028 #ifdef WMDLINEWRAP
1029 allow_wrap = w_val(wp, WMDLINEWRAP) ? mode_row(wp) - 1 : 0;
1030 #endif
1031
1032 /*
1033 * The loops below are split up for a reason - there's a hidden side effect
1034 * which makes the final increment of vtcol inconsistent, so it won't
1035 * terminate until the end of the line. For very long lines, that's a
1036 * performance hit.
1037 */
1038 #define VTSET_PUT() \
1039 int used = vtset_put(wp, from, (UINT) n); \
1040 from += used; n -= used
1041
1042 #define VTSET_LOOP(condition) \
1043 while ((vtcol < term.cols) && (condition)) { \
1044 VTSET_PUT(); \
1045 } \
1046 if ((vtcol <= term.cols) && (condition)) { \
1047 VTSET_PUT(); \
1048 }
1049
1050 #ifdef WMDLINEWRAP
1051 if (w_val(wp, WMDLINEWRAP)) {
1052 VTSET_LOOP(((vtrow == term.rows - 1) || (vtrow < mode_row(wp)))
1053 && (n > 0));
1054 } else
1055 #endif
1056 {
1057 VTSET_LOOP((n > 0));
1058 }
1059
1060 /* Display a "^J" if 'list' mode is active, unless we've suppressed
1061 * it for some reason.
1062 */
1063 if (list && (n >= 0)) {
1064 if (b_is_scratch(bp) && listrimmed(lp))
1065 /*EMPTY */ ;
1066 else if (line_has_newline(lp, bp)) {
1067 const char *s = get_record_sep(bp);
1068 UINT len = (UINT) len_record_sep(bp);
1069 while (len != 0)
1070 vtlistc(wp, s++, len--);
1071 }
1072 }
1073 #ifdef WMDLINEWRAP
1074 allow_wrap = 0;
1075 #endif
1076 #if OPT_TRACE > 1
1077 while (save_vtrow <= vtrow) {
1078 /*
1079 * Display the row(s) which we just wrote to the virtual screen.
1080 *
1081 * FIXME - need to compute length from the number of columns.
1082 * This is only assuming columns==cells.
1083 */
1084 TRACE2(("TEXT %4d:%s\n",
1085 save_vtrow,
1086 visible_video_text(vscreen[save_vtrow]->v_text,
1087 (save_vtrow == vtrow)
1088 ? vtcol
1089 : term.cols)));
1090 TRACE3(("ATTR :%s\n",
1091 visible_video_attr(vscreen[save_vtrow]->v_attrs,
1092 (save_vtrow == vtrow)
1093 ? vtcol
1094 : term.cols)));
1095 ++save_vtrow;
1096 }
1097 #endif
1098 }
1099
1100 /*
1101 * Erase from the end of the software cursor to the end of the line on which
1102 * the software cursor is located.
1103 */
1104 static void
vteeol(void)1105 vteeol(void)
1106 {
1107 if (vtcol < term.cols) {
1108 int n = (vtcol >= 0) ? vtcol : 0;
1109 #ifdef WMDLINEWRAP
1110 if (vtrow >= 0)
1111 #endif
1112 while (n < term.cols) {
1113 vscreen[vtrow]->v_text[n++] = ' ';
1114 }
1115 vtcol = term.cols;
1116 }
1117 }
1118
1119 /* upscreen: user routine to force a screen update
1120 always finishes complete update */
1121 #if !SMALLER
1122 /* ARGSUSED */
1123 int
upscreen(int f GCC_UNUSED,int n GCC_UNUSED)1124 upscreen(int f GCC_UNUSED, int n GCC_UNUSED)
1125 {
1126 return update(TRUE);
1127 }
1128 #endif
1129
1130 static UINT scrflags;
1131
1132 /*
1133 * Convert MARK's offset to virtual column. The virtual column counts the left
1134 * margin, the sideways shift of the window, etc., up to the given MARK.
1135 *
1136 * The buffer may have a left-margin, e.g., [Registers]. Use the 'bp'
1137 * parameter to get that value.
1138 *
1139 * To allow this to be used in the OPT_CACHE_VCOL logic, it accepts a 'column'
1140 * parameter which in that case may be nonzero, e.g., the nominal column of the
1141 * left margin of the display (the column at which the sideways-shift
1142 * coincides).
1143 *
1144 * Finally, use 'adjust' to adjust for cases where the sideways-shift does
1145 * not exactly coincide with the 'column', where nonprinting data is displayed
1146 * in hex/octal/uparrow format and is split across the display's left margin.
1147 * After adding the left margin, that gives the index into the line for
1148 * reading bytes whose width is added til we read the mark's offset.
1149 */
1150 int
mk_to_vcol(WINDOW * wp,MARK mark,int expanded,int column,int adjust)1151 mk_to_vcol(WINDOW *wp, MARK mark, int expanded, int column, int adjust)
1152 {
1153 BUFFER *bp = wp->w_bufp;
1154 int i = b_left_margin(bp) + adjust;
1155 int ch, c0;
1156 int limit;
1157 int t = tabstop_val(bp);
1158 LINE *lp;
1159 int extra = ((!global_g_val(GMDALTTABPOS) && !insertmode) ? 1 : 0);
1160 const char *text;
1161 int prev_col = column;
1162 int my_bytes = BytesAt(mark.l, mark.o);
1163 #ifdef WMDLINEWRAP
1164 int nu_adj = nu_width(wp);
1165 #endif
1166
1167 TRACE2((T_CALLED "mk_to_vcol(mark.o=%d, col=%d, adjust=%d) extra %d\n",
1168 mark.o, column, adjust, extra));
1169
1170 if (i < 0) {
1171 i = 0;
1172 column = 0;
1173 }
1174 lp = mark.l;
1175 limit = mark.o + (extra ? my_bytes : 0);
1176 if (limit > llength(lp))
1177 limit = llength(lp);
1178
1179 text = lvalue(lp);
1180 if (column == 0 || !mark.o) {
1181 ch = EOF;
1182 } else if (mark.o) {
1183 ch = text[mark.o - 1];
1184 } else {
1185 ch = EOF;
1186 }
1187 while (i < limit) {
1188 int used = 1;
1189
1190 prev_col = column;
1191 c0 = ch;
1192 ch = text[i];
1193 #ifdef WMDLINEWRAP
1194 if (column != 0
1195 && w_val(wp, WMDLINEBREAK)
1196 && w_val(wp, WMDLINEWRAP)
1197 && isBlank(c0)) {
1198 int nu_col = (column + nu_adj);
1199 int k = (int) cols_until(wp, text + i, (unsigned) (llength(lp) - i));
1200 int wide = term.cols;
1201 int have = (nu_col % wide) + k;
1202
1203 if (have >= wide) {
1204 int wrap = (nu_col / wide);
1205 int need = (wrap + 1) * wide;
1206 nu_col = need;
1207 column = nu_col - nu_adj;
1208 prev_col = column;
1209 }
1210 }
1211 #endif
1212 if (isTab(ch) && !expanded) {
1213 column += t - (column % t);
1214 }
1215 #if OPT_MULTIBYTE
1216 else if (b_is_utfXX(wp->w_bufp)) {
1217 int nxt = llength(mark.l) - i;
1218 int adj = column_sizes(wp, text + i, (unsigned) nxt, &used);
1219
1220 if (adj == COLS_UTF8 && UseCellWidth2(wp, lp, i)) {
1221 adj = mb_cellwidth(wp, text + i, nxt);
1222 }
1223 column += adj;
1224 }
1225 #endif
1226 else if (!isPrint(ch)) {
1227 column += column_sizes(wp,
1228 text + i,
1229 (UINT) (llength(mark.l) - i),
1230 &used);
1231 } else {
1232 ++column;
1233 }
1234 i += used;
1235 }
1236 if (extra && (column != 0) && (mark.o < llength(lp))) {
1237 column = (my_bytes == 1 && b_is_utfXX(bp)) ? (column - 1) : prev_col;
1238 }
1239 return2Code(column);
1240 }
1241
1242 /*
1243 * Convert the window's DOT into a virtual column. This is used both for the
1244 * ruler, as well as for updating the cursor position, and for very long lines
1245 * can be slow. So we try to cache a good starting point to allow rapid
1246 * movement along the line.
1247 */
1248 static int
dot_to_vcol(WINDOW * wp)1249 dot_to_vcol(WINDOW *wp)
1250 {
1251 BUFFER *bp = wp->w_bufp;
1252 int result;
1253 #if OPT_CACHE_VCOL
1254 W_TRAITS *wt = &(wp->w_traits);
1255 int need_col = FALSE;
1256 int shift = (w_val(wp, WVAL_SIDEWAYS)
1257 ? w_val(wp, WVAL_SIDEWAYS) - 1
1258 : 0);
1259 int use_off = -b_left_margin(bp);
1260 #if OPT_TRACE
1261 int check;
1262 #endif
1263
1264 TRACE2((T_CALLED "dot_to_vcol(%s)\n", bp->b_bname));
1265
1266 #if OPT_TRACE && OPT_DEBUG
1267 TRACE(("HAVE: dot.o %d, left_dot %d+%d, left_col %d\n",
1268 wp->w_dot.o,
1269 wt->w_left_dot.o, use_off,
1270 wt->w_left_col));
1271 TRACE(("%s\n", lp_visible(wp->w_dot.l)));
1272 TRACE(("DOT:%s\n", visible_buff(lvalue(wp->w_dot.l) + wp->w_dot.o,
1273 llength(wp->w_dot.l) - wp->w_dot.o,
1274 FALSE)));
1275 #endif
1276
1277 /*
1278 * If we've moved to a new line, or if the 'list' mode is changed,
1279 * we'll need to recompute the whole state.
1280 */
1281 if (wt->w_left_dot.l != wp->w_dot.l
1282 || wt->w_list_opt != w_val(wp, WMDLIST)
1283 || wt->w_num_cols != term.cols) {
1284 wt->w_left_dot.l = wp->w_dot.l;
1285 wt->w_left_dot.o = 0;
1286 wt->w_left_col = 0;
1287 wt->w_left_adj = 0;
1288 wt->w_list_opt = w_val(wp, WMDLIST);
1289 wt->w_num_cols = term.cols;
1290 need_col = TRUE;
1291 }
1292 #ifdef WMDLINEWRAP
1293 if (w_val(wp, WMDLINEWRAP)) {
1294 int on_1st = (nu_width(wp) + w_left_margin(wp));
1295 int lo = wt->w_left_dot.o;
1296 int hi = lo + term.cols; /* estimate (may be lower) */
1297 int inside;
1298 int row;
1299 int col = wt->w_left_col;
1300
1301 /*
1302 * Normally 'hi' is a good estimate since each column in the display
1303 * corresponds to one or more bytes of the displayed data. But if we
1304 * are in linebreak mode, then we can get some extra columns on each
1305 * row. That can make the estimate too high, causing the chunk of
1306 * code tested by 'inside' to not be executed.
1307 *
1308 * FIXME - it would be nice to cache the offset corresponding to the
1309 * right-limit of the row containing dot, e.g., wt->w_right_dot.o
1310 */
1311 if (w_val(wp, WMDLINEBREAK)) {
1312 int chk = hi;
1313 int tst;
1314 char *text = lvalue(wp->w_dot.l);
1315
1316 while (chk < llength(wp->w_dot.l) && !isBlank(text[chk])) {
1317 ++chk;
1318 }
1319 tst = offs2col(wp, wp->w_dot.l, chk);
1320 if (tst > hi) {
1321 chk = hi;
1322 do {
1323 while (chk >= 0 && !isBlank(text[chk])) {
1324 --chk;
1325 }
1326 if (chk >= 0) {
1327 tst = offs2col(wp, wp->w_dot.l, chk);
1328 } else {
1329 break;
1330 }
1331 } while (--chk >= 0 && tst > hi);
1332 }
1333 hi = tst;
1334 }
1335
1336 inside = (wp->w_dot.o >= lo && wp->w_dot.o < hi);
1337 TRACE(("dot.o %d is %s [%d..%d]\n",
1338 wp->w_dot.o,
1339 inside ? "inside" : "NOT inside",
1340 lo, hi - 1));
1341
1342 if (!inside) {
1343 col = offs2col(wp, wt->w_left_dot.l, wp->w_dot.o);
1344 TRACE(("w_dot offs2col(%d) = %d\n", wp->w_dot.o, col));
1345 row = col / term.cols;
1346 TRACE(("...in row %d\n", row));
1347 col = row * term.cols - on_1st;
1348 if (col < 0)
1349 col = 0;
1350 TRACE(("...row %d begins with col %d\n", row, col));
1351 }
1352
1353 if (wt->w_left_col != col) {
1354 TRACE(("left_col %d vs %d\n", wt->w_left_col, col));
1355 wt->w_left_col = col;
1356
1357 wt->w_left_dot.o = col2offs(wp, wt->w_left_dot.l, col + on_1st);
1358 TRACE(("...left_dot col2offs(%d + %d) = %d\n",
1359 col, on_1st, wt->w_left_dot.o));
1360 }
1361 } else
1362 #endif
1363 if ((wt->w_left_col + wt->w_left_adj < shift)
1364 || (wt->w_left_col > shift)
1365 || (wp->w_dot.o < wt->w_left_dot.o)) {
1366 int base = nu_width(wp) + w_left_margin(wp) - 1;
1367 int col;
1368
1369 TRACE(("...change dot %d(%d) w_left_col from [%d..%d] to match shift %d, base %d\n",
1370 wp->w_dot.o,
1371 wt->w_left_dot.o,
1372 wt->w_left_col,
1373 wt->w_left_col + wt->w_left_adj,
1374 shift, base));
1375
1376 col = offs2col(wp, wt->w_left_dot.l, wp->w_dot.o) + shift - base;
1377 TRACE(("...compare col %d to shift %d\n", col, shift));
1378 if (col < shift) {
1379 wt->w_left_col = col;
1380 } else {
1381 wt->w_left_col = shift;
1382 }
1383 wt->w_left_adj = 0;
1384
1385 wt->w_left_dot.o = col2offs(wp,
1386 wt->w_left_dot.l,
1387 wt->w_left_col + base - shift);
1388 TRACE(("...col2offs(%d) = %d\n", wt->w_left_col, wt->w_left_dot.o));
1389
1390 TRACE(("...compare w_left_dot.o %d to w_dot.o %d\n",
1391 wt->w_left_dot.o, wp->w_dot.o));
1392 if (wt->w_left_dot.o > wp->w_dot.o) {
1393 need_col = TRUE; /* dot is inconsistent with shift */
1394 wt->w_left_dot.o = wp->w_dot.o;
1395 } else {
1396 col = offs2col(wp, wt->w_left_dot.l, wt->w_left_dot.o) + shift - base;
1397 TRACE(("...offs2col(%d) = %d\n", wt->w_left_dot.o, col));
1398
1399 TRACE(("...compare col %d to w_left_col %d\n",
1400 col, wt->w_left_col));
1401 if (col < wt->w_left_col) {
1402 /* if there was a multi-column character at the left
1403 * side of the shifted screen, adjust */
1404 wt->w_left_adj = wt->w_left_col - col;
1405 TRACE(("...adjust for multi-column character %d..%d -> %d\n",
1406 col,
1407 wt->w_left_col,
1408 wt->w_left_adj));
1409 wt->w_left_col = col;
1410 }
1411 need_col = FALSE;
1412 }
1413 }
1414
1415 if (need_col) {
1416 wt->w_left_col = 0;
1417 if (wt->w_left_dot.o > 0) {
1418 wt->w_left_col = mk_to_vcol(wp,
1419 wt->w_left_dot,
1420 w_val(wp, WMDLIST),
1421 0,
1422 0);
1423 TRACE(("...cache w_left_col %d\n", wt->w_left_col));
1424 }
1425 }
1426 if (use_off != 0) {
1427 if (wt->w_left_col == 0)
1428 use_off = -wt->w_left_dot.o;
1429 }
1430 result = mk_to_vcol(wp,
1431 wp->w_dot,
1432 w_val(wp, WMDLIST),
1433 wt->w_left_col,
1434 wt->w_left_dot.o + use_off);
1435 #if OPT_TRACE
1436 check = mk_to_vcol(wp, wp->w_dot, w_val(wp, WMDLIST), 0, 0);
1437 if (check != result) {
1438 TRACE(("MISMATCH result %d check %d\n", result, check));
1439 kbd_alarm();
1440 TRACE(("-> OOPS: dot.o %d, left_dot %d+%d, left_col %d\n",
1441 wp->w_dot.o,
1442 wt->w_left_dot.o, use_off,
1443 wt->w_left_col));
1444 result = check;
1445 }
1446 #endif
1447 #else
1448 (void) bp;
1449 result = mk_to_vcol(wp, wp->w_dot, w_val(wp, WMDLIST), 0, 0);
1450 #endif
1451 return2Code(result);
1452 }
1453
1454 /*
1455 * Update a single line. This does not know how to use insert or delete
1456 * character sequences; we are using VT52 functionality. Update the physical
1457 * row and column variables. It does try an exploit erase to end of line.
1458 *
1459 * row - row of screen to update
1460 * colfrom - col to start updating from
1461 * colto - col to go to
1462 */
1463
1464 #if OPT_PSCREEN
1465 /*
1466 * Update a single row on the physical screen.
1467 */
1468 static void
update_line(int row,int colfrom,int colto)1469 update_line(int row, int colfrom, int colto)
1470 {
1471 VIDEO_TEXT *vc, *pc, *evc;
1472 VIDEO_ATTR *va, *pa, xx;
1473 int nchanges = 0;
1474
1475 if ((vscreen[row]->v_flag & VFCHG) == 0)
1476 return;
1477
1478 vc = &vscreen[row]->v_text[colfrom];
1479 evc = &vscreen[row]->v_text[colto];
1480 va = &vscreen[row]->v_attrs[colfrom];
1481 pc = &CELL_TEXT(row, colfrom);
1482 pa = &CELL_ATTR(row, colfrom);
1483
1484 while (vc < evc) {
1485 xx = *va;
1486 #ifdef GVAL_VIDEO
1487 xx ^= (VIDEO_ATTR) global_g_val(GVAL_VIDEO);
1488 #endif
1489 if (*vc != *pc || VATTRIB(xx) != VATTRIB(*pa)) {
1490 *pc = *vc;
1491 *pa = xx | VADIRTY;
1492 nchanges++;
1493 }
1494 vc++;
1495 pc++;
1496 va++;
1497 pa++;
1498 }
1499
1500 if (nchanges > 0)
1501 MARK_LINE_DIRTY(row);
1502 vscreen[row]->v_flag &= ~(VFCHG | VFCOL); /* mark virtual line updated */
1503 }
1504
1505 #else /* !OPT_PSCREEN */
1506
1507 /*
1508 * UPDATELINE code for all other versions
1509 */
1510 static void
update_line(int row,int colfrom,int colto)1511 update_line(int row, int colfrom, int colto)
1512 {
1513 struct VIDEO *vp1 = vscreen[row]; /* virtual screen image */
1514 struct VIDEO *vp2 = pscreen[row]; /* physical screen image */
1515 int xl = colfrom;
1516 int xr = colto;
1517 int xx;
1518
1519 VIDEO_TEXT *cp1 = VideoText(vp1);
1520 VIDEO_TEXT *cp2 = VideoText(vp2);
1521 int nbflag; /* non-blanks to the right flag? */
1522
1523 #if OPT_VIDEO_ATTRS
1524 VIDEO_ATTR *ap1 = VideoAttr(vp1);
1525 VIDEO_ATTR *ap2 = VideoAttr(vp2);
1526 VIDEO_ATTR Blank = 0; /* FIXME: Color? */
1527 #else
1528 UINT rev; /* reverse video flag */
1529 UINT req; /* reverse video request flag */
1530 #endif
1531
1532 #if !OPT_VIDEO_ATTRS
1533 #if OPT_COLOR
1534 term.setfore(ReqFcolor(vp1));
1535 term.setback(ReqBcolor(vp1));
1536 #endif
1537
1538 #if OPT_REVSTA || OPT_COLOR
1539 /* if we need to change the reverse video status of the
1540 current line, we need to re-write the entire line */
1541 rev = (vp1->v_flag & VFREV) == VFREV;
1542 req = (vp1->v_flag & VFREQ) == VFREQ;
1543 if ((rev != req)
1544 #if OPT_COLOR
1545 || (CurFcolor(vp1) != ReqFcolor(vp1))
1546 || (CurBcolor(vp1) != ReqBcolor(vp1))
1547 #endif
1548 ) {
1549 movecursor(row, colfrom); /* Go to start of line. */
1550 /* set rev video if needed */
1551 if (req)
1552 set_term_attrs(req);
1553
1554 /* scan through the line and dump it to the screen and
1555 the virtual screen array */
1556 for (; xl < colto; xl++) {
1557 term.putch(cp1[xl]);
1558 ++ttcol;
1559 cp2[xl] = cp1[xl];
1560 }
1561 /* turn rev video off */
1562 if (req)
1563 reset_term_attrs();
1564
1565 /* update the needed flags */
1566 vp1->v_flag &= ~(VFCHG | VFCOL);
1567 if (req)
1568 vp1->v_flag |= VFREV;
1569 else
1570 vp1->v_flag &= ~VFREV;
1571 #if OPT_COLOR
1572 CurFcolor(vp1) = ReqFcolor(vp1);
1573 CurBcolor(vp1) = ReqBcolor(vp1);
1574 #endif
1575 return;
1576 }
1577 #else
1578 rev = FALSE;
1579 #endif /* OPT_REVSTA || OPT_COLOR */
1580 #endif /* !OPT_VIDEO_ATTRS */
1581
1582 /* advance past any common chars at the left */
1583 #if !OPT_VIDEO_ATTRS
1584 if (!rev)
1585 #endif /* !OPT_VIDEO_ATTRS */
1586 while (xl != colto
1587 && cp1[xl] == cp2[xl]
1588 #if OPT_VIDEO_ATTRS
1589 && VATTRIB(ap1[xl]) == VATTRIB(ap2[xl])
1590 #endif /* OPT_VIDEO_ATTRS */
1591 ) {
1592 ++xl;
1593 }
1594
1595 /* This can still happen, even though we only call this routine on changed
1596 * lines. A hard update is always done when a line splits, a massive
1597 * change is done, or a buffer is displayed twice. This optimizes out most
1598 * of the excess updating. A lot of computes are used, but these tend to
1599 * be hard operations that do a lot of update, so I don't really care.
1600 */
1601
1602 /* if both lines are the same, no update needs to be done */
1603 if (xl == colto) {
1604 vp1->v_flag &= ~VFCHG;
1605 return;
1606 }
1607
1608 /* find out if there is a match on the right */
1609 nbflag = FALSE;
1610
1611 #if !OPT_VIDEO_ATTRS
1612 if (!rev)
1613 #endif
1614 while (cp1[xr - 1] == cp2[xr - 1]
1615 #if OPT_VIDEO_ATTRS
1616 && VATTRIB(ap1[xr - 1]) == VATTRIB(ap2[xr - 1])
1617 #endif
1618 ) {
1619 --xr;
1620 /* Note if any nonblank in right match */
1621 if (cp1[xr] != ' '
1622 #if OPT_VIDEO_ATTRS
1623 || VATTRIB(ap1[xr] != Blank)
1624 #endif
1625 )
1626 nbflag = TRUE;
1627 }
1628
1629 xx = xr;
1630
1631 /* Erase to EOL ? */
1632 if (nbflag == FALSE
1633 && eolexist == TRUE
1634 #if OPT_REVSTA && !OPT_VIDEO_ATTRS
1635 && (req != TRUE)
1636 #endif
1637 ) {
1638 while ((xx != xl)
1639 && cp1[xx - 1] == ' '
1640 #if OPT_VIDEO_ATTRS
1641 && VATTRIB(ap1[xx - 1]) == Blank
1642 #endif
1643 ) {
1644 xx--;
1645 }
1646
1647 if ((xr - xx) <= 3) /* Use only if erase is */
1648 xx = xr; /* fewer characters. */
1649 }
1650
1651 movecursor(row, xl - colfrom); /* Go to start of line. */
1652 #if OPT_VIDEO_ATTRS
1653 while (xl < xx) {
1654 int j = xl;
1655 VIDEO_ATTR attr = VATTRIB(ap1[j]);
1656 while ((j < xx) && (attr == VATTRIB(ap1[j])))
1657 j++;
1658 set_term_attrs(attr);
1659 for (; xl < j; xl++) {
1660 term.putch(cp1[xl]);
1661 ++ttcol;
1662 cp2[xl] = cp1[xl];
1663 ap2[xl] = ap1[xl];
1664 }
1665 }
1666 reset_term_attrs();
1667
1668 if (xx != xr) { /* Erase. */
1669 term.eeol();
1670 for (; xl < xr; xl++) {
1671 if (cp2[xl] != cp1[xl]
1672 || VATTRIB(ap2[xl]) != VATTRIB(ap1[xl]))
1673 ap2[xl] = ap1[xl];
1674 cp2[xl] = cp1[xl];
1675 }
1676 }
1677 #else /* OPT_VIDEO_ATTRS */
1678 #if OPT_REVSTA
1679 set_term_attrs(rev);
1680 #endif
1681
1682 for (; xl < xr; xl++) { /* Ordinary. */
1683 term.putch(cp1[xl]);
1684 ++ttcol;
1685 cp2[xl] = cp1[xl];
1686 }
1687
1688 if (xx != xr) { /* Erase. */
1689 term.eeol();
1690 for (; xl < xr; xl++) {
1691 cp2[xl] = cp1[xl];
1692 }
1693 }
1694 #if OPT_REVSTA
1695 reset_term_attrs();
1696 #endif
1697 #endif /* OPT_VIDEO_ATTRS */
1698 vp1->v_flag &= ~(VFCHG | VFCOL);
1699 return;
1700 }
1701 #endif /* OPT_PSCREEN(update_line) */
1702
1703 void
kbd_openup(void)1704 kbd_openup(void)
1705 {
1706 kbd_flush();
1707 bottomleft();
1708 term.putch('\n');
1709 term.putch('\r');
1710 term.flush();
1711
1712 #if !OPT_PSCREEN
1713 if (pscreen != 0) {
1714 int i, j;
1715 for (i = 0; i < term.rows - 1; ++i) {
1716 for (j = 0; j < term.cols; ++j) {
1717 CELL_TEXT(i, j) = CELL_TEXT(i + 1, j);
1718 #if OPT_VIDEO_ATTRS
1719 CELL_ATTR(i, j) = CELL_ATTR(i + 1, j);
1720 #endif
1721 }
1722 }
1723 for (j = 0; j < term.cols; ++j) {
1724 CELL_TEXT(i, j) = ' ';
1725 #if OPT_VIDEO_ATTRS
1726 CELL_ATTR(i, j) = VADIRTY;
1727 #endif
1728 }
1729 }
1730 #endif
1731 }
1732
1733 /* cannot be allocated since it's used by OPT_HEAPSIZE */
1734 static char my_overlay[20];
1735
1736 /* save/erase text for the overlay on the message line */
1737 void
kbd_overlay(const char * s)1738 kbd_overlay(const char *s)
1739 {
1740 my_overlay[0] = EOS;
1741 if ((mpresf = (s != 0 && *s != EOS)) != 0) {
1742 vl_strncpy(my_overlay, s, sizeof(my_overlay));
1743 }
1744 }
1745
1746 void
kbd_flush(void)1747 kbd_flush(void)
1748 {
1749 int ok;
1750
1751 beginDisplay();
1752 if (is_vtinit()) {
1753 int row = term.rows - 1;
1754
1755 vtmove(row, -w_val(wminip, WVAL_SIDEWAYS));
1756
1757 ok = (wminip != 0
1758 && wminip->w_dot.l != 0
1759 && lvalue(wminip->w_dot.l) != 0);
1760 if (ok) {
1761 TRACE(("SHOW:%2d:%s\n",
1762 llength(wminip->w_dot.l),
1763 lp_visible(wminip->w_dot.l)));
1764 lsettrimmed(wminip->w_dot.l);
1765 vtset(wminip->w_dot.l, wminip);
1766 }
1767
1768 vteeol();
1769 set_vattrs(row, 0,
1770 (VIDEO_ATTR) (miniedit
1771 ? global_g_val(GVAL_MINI_HILITE)
1772 : 0),
1773 (size_t) term.cols);
1774 if (my_overlay[0] != EOS) {
1775 int j;
1776 int n = term.cols - (int) strlen(my_overlay) - 1;
1777 if (n > 0) {
1778 for (j = 0; my_overlay[j] != EOS; ++j)
1779 vscreen[row]->v_text[n + j] = (VIDEO_TEXT) my_overlay[j];
1780 }
1781 }
1782 vscreen[row]->v_flag |= VFCHG;
1783 update_line(row, 0, term.cols);
1784 if (ok)
1785 movecursor(row,
1786 offs2col(wminip, wminip->w_dot.l, wminip->w_dot.o));
1787 }
1788 term.flush();
1789 endofDisplay();
1790 }
1791
1792 /*
1793 * Translate offset (into a line's text) into the display-column, taking into
1794 * account the tabstop, sideways, number- and list-modes.
1795 *
1796 * Note: we intentionally compare against '\n' rather than the record separator,
1797 * since it is simpler than handling ^M^J in one case, and we really only want
1798 * to know if we're in the record-separator.
1799 */
1800 static int
offs2col0(WINDOW * wp,LINE * lp,C_NUM offset,C_NUM * cache_offset,int * cache_column)1801 offs2col0(WINDOW *wp,
1802 LINE *lp,
1803 C_NUM offset,
1804 C_NUM * cache_offset,
1805 int *cache_column)
1806 {
1807 int column;
1808
1809 /* this makes the how-much-to-select calculation easier above */
1810 if (offset < 0) {
1811 column = offset;
1812 } else if ((lp == win_head(wp)) || !lisreal(lp)) {
1813 column = 0;
1814 } else {
1815 BUFFER *bp = wp->w_bufp;
1816 int rs = use_record_sep(bp);
1817 int length = llength(lp);
1818 int nums = nu_width(wp);
1819 int tabs = tabstop_val(wp->w_bufp);
1820 int list = w_val(wp, WMDLIST);
1821 int last = (list ? (N_chars * 2) : rs);
1822 int left = if_LINEWRAP(wp, 0, w_val(wp, WVAL_SIDEWAYS));
1823 int ch = EOF, c0;
1824 C_NUM n;
1825 C_NUM start_offset;
1826 const char *text = lvalue(lp);
1827
1828 if (offset > length + 1)
1829 offset = length + 1;
1830
1831 if (cache_offset && cache_column && *cache_offset < offset) {
1832 start_offset = *cache_offset;
1833 column = *cache_column;
1834 } else {
1835 start_offset = w_left_margin(wp);
1836 column = 0;
1837 }
1838
1839 for (n = start_offset; n < offset;) {
1840 int used = 1;
1841
1842 c0 = ch;
1843 ch = (n == length) ? rs : CharOf(text[n]);
1844 #if OPT_MULTIBYTE
1845 if ((n < length) && b_is_utfXX(bp) && !isTab(ch)) {
1846 int nxt = offset - n;
1847 int adj = column_sizes(wp, text + n, (UINT) nxt, &used);
1848
1849 if (adj == COLS_UTF8 && UseCellWidth2(wp, lp, n)) {
1850 adj = mb_cellwidth(wp, text + n, nxt);
1851 }
1852 column += adj;
1853 } else
1854 #endif
1855 if (isPrint(ch)) {
1856 #ifdef WMDLINEWRAP
1857 if (column != 0
1858 && w_val(wp, WMDLINEBREAK)
1859 && w_val(wp, WMDLINEWRAP)
1860 && isBlank(c0)) {
1861 int k = (int) cols_until(wp,
1862 text + n,
1863 (unsigned) (length - n));
1864 int col2 = column + nums;
1865 int wide = term.cols;
1866 int have;
1867
1868 have = (col2 % wide) + k;
1869 if (have >= wide) {
1870 int wrap = (col2 / wide);
1871 int need = (wrap + 1) * wide;
1872 column = need - nums;
1873 }
1874 }
1875 #endif
1876 column++;
1877 } else if (ch == last) {
1878 column++;
1879 } else if (list || !isTab(ch)) {
1880 column += column_sizes(wp,
1881 (n >= length) ? "" : (text + n),
1882 (UINT) (offset - n), &used);
1883 } else if (isTab(ch)) {
1884 column = ((column / tabs) + 1) * tabs;
1885 }
1886 n += used;
1887 }
1888
1889 if (cache_offset && cache_column) {
1890 *cache_offset = offset;
1891 *cache_column = column;
1892 }
1893
1894 column += (nums + w_left_margin(wp) - left);
1895 }
1896 return column;
1897 }
1898
1899 int
offs2col(WINDOW * wp,LINE * lp,C_NUM offset)1900 offs2col(WINDOW *wp, LINE *lp, C_NUM offset)
1901 {
1902 return offs2col0(wp, lp, offset, 0, 0);
1903 }
1904
1905 /*
1906 * Translate a display-column (assuming an infinitely-wide display) into the
1907 * line's offset, taking into account the tabstop, sideways, number and list
1908 * modes.
1909 */
1910 #if OPT_MOUSE || defined(WMDLINEWRAP)
1911 int
col2offs(WINDOW * wp,LINE * lp,C_NUM col)1912 col2offs(WINDOW *wp, LINE *lp, C_NUM col)
1913 {
1914 int nums = nu_width(wp);
1915 int tabs = tabstop_val(wp->w_bufp);
1916 int list = w_val(wp, WMDLIST);
1917 int left = if_LINEWRAP(wp, 0, w_val(wp, WVAL_SIDEWAYS));
1918 int goal = col + left - nums - w_left_margin(wp);
1919
1920 C_NUM n;
1921 C_NUM offset;
1922 C_NUM len = llength(lp);
1923 char *text = lvalue(lp);
1924
1925 if (lp == win_head(wp)) {
1926 offset = 0;
1927 } else {
1928 for (offset = w_left_margin(wp), n = 0;
1929 (offset < len) && (n < goal);
1930 ) {
1931 int c = text[offset];
1932 int used = 1;
1933
1934 #ifdef WMDLINEWRAP
1935 /*
1936 * A linebreak can happen after whitespace. To avoid a confusing
1937 * display, we avoid doing the linebreak at the beginning or in the
1938 * middle of whitespace. Also, we disallow linebreak at the
1939 * beginning of a line.
1940 */
1941 if (offset != 0
1942 && w_val(wp, WMDLINEBREAK)
1943 && w_val(wp, WMDLINEWRAP)
1944 && isBlank(text[offset - 1])
1945 && !isBlank(text[offset])) {
1946 int n2 = (int) cols_until(wp,
1947 text + offset,
1948 (unsigned) (len - offset));
1949 int col1 = ((n + nums) % term.cols);
1950 int col2 = col1 + n2;
1951
1952 if (col2 >= term.cols) {
1953 n += (term.cols - col1);
1954 }
1955 }
1956 #endif
1957
1958 #if OPT_MULTIBYTE
1959 if (b_is_utfXX(wp->w_bufp) && !isTab(c)) {
1960 int nxt = len - offset;
1961 int adj = column_sizes(wp, text + offset, (UINT) nxt, &used);
1962
1963 if (adj == COLS_UTF8 && UseCellWidth2(wp, lp, offset)) {
1964 adj = mb_cellwidth(wp, text + offset, nxt);
1965 }
1966 n += adj;
1967 } else
1968 #endif
1969 if (isPrint(c)) {
1970 n++;
1971 } else if (list || !isTab(c)) {
1972 n += column_sizes(wp, text + offset, (UINT) (len - offset), &used);
1973 } else if (isTab(c)) {
1974 n = ((n / tabs) + 1) * tabs;
1975 }
1976 if (n > goal)
1977 break;
1978 offset += used;
1979 }
1980 }
1981 return offset;
1982 }
1983 #endif
1984
1985 /*
1986 * Compute the number of rows required for displaying a line.
1987 */
1988 #ifdef WMDLINEWRAP
1989 int
line_height(WINDOW * wp,LINE * lp)1990 line_height(WINDOW *wp, LINE *lp)
1991 {
1992 int hi = 1;
1993
1994 if (w_val(wp, WMDLINEWRAP)) {
1995 int len = llength(lp);
1996 if (len > 0) {
1997 int col = offs2col(wp, lp, len) - 1;
1998 int rsl = ((w_val(wp, WMDLIST))
1999 ? ((b_val(wp->w_bufp, VAL_RECORD_SEP) == RS_CRLF) ? 4 : 2)
2000 : 1);
2001 if (ins_mode(wp) != FALSE
2002 && lp == DOT.l
2003 && len <= DOT.o) {
2004 col += rsl - 1; /* wrapping treats one column specially */
2005 } else if (w_val(wp, WMDLIST)) {
2006 col += rsl;
2007 }
2008 hi = (col / term.cols) + 1;
2009 }
2010 }
2011 return hi;
2012 }
2013 #endif
2014
2015 /*
2016 * Given a row on the screen, determines which window it belongs to. Returns
2017 * null only for the message line.
2018 */
2019 #if defined(WMDLINEWRAP) || OPT_MOUSE
2020 WINDOW *
row2window(int row)2021 row2window(int row)
2022 {
2023 WINDOW *wp;
2024
2025 for_each_visible_window(wp) {
2026 if (row >= wp->w_toprow && row <= mode_row(wp))
2027 return wp;
2028 }
2029 return 0;
2030 }
2031 #endif
2032
2033 #if OPT_UPBUFF
2034 typedef struct {
2035 WINDOW *wp;
2036 struct VAL w_vals[MAX_W_VALUES];
2037 int top;
2038 int line;
2039 int col;
2040 } SAVEWIN;
2041
2042 static SAVEWIN *recomp_tbl;
2043 static size_t recomp_len;
2044 static WINDOW *recomp_win;
2045
2046 /*
2047 * Return true if the current window (or its associated buffer) is being
2048 * recomputed.
2049 */
2050 int
is_recomputing(WINDOW * wp)2051 is_recomputing(WINDOW *wp)
2052 {
2053 int result = FALSE;
2054
2055 if (recomp_win && wp) {
2056 if (recomp_win == wp) {
2057 result = TRUE;
2058 } else if (recomp_win->w_bufp == wp->w_bufp) {
2059 result = TRUE;
2060 }
2061 }
2062
2063 return result;
2064 }
2065
2066 BUFFER *
recomputing_buf(void)2067 recomputing_buf(void)
2068 {
2069 BUFFER *result = 0;
2070
2071 if (recomp_win != 0)
2072 result = recomp_win->w_bufp;
2073
2074 return result;
2075 }
2076
2077 WINDOW *
recomputing_win(void)2078 recomputing_win(void)
2079 {
2080 return recomp_win;
2081 }
2082
2083 /*
2084 * Recompute the given buffer. Save/restore its modes and position information
2085 * so that a redisplay will show as little change as possible.
2086 */
2087 static void
recompute_buffer(BUFFER * bp)2088 recompute_buffer(BUFFER *bp)
2089 {
2090 if (b_val(bp, MDUPBUFF)) {
2091 WINDOW *wp;
2092 SAVEWIN *tbl;
2093
2094 struct VAL b_vals[MAX_B_VALUES];
2095 size_t num = 0;
2096 BUFFER *savebp = curbp;
2097 WINDOW *savewp = curwp;
2098 int mygoal = curgoal;
2099
2100 recomp_win = curwp;
2101
2102 if (recomp_len < bp->b_nwnd) {
2103 recomp_len = (size_t) bp->b_nwnd + 1;
2104 recomp_tbl = (recomp_tbl != 0)
2105 ? typereallocn(SAVEWIN, recomp_tbl, recomp_len)
2106 : typeallocn(SAVEWIN, recomp_len);
2107 if (recomp_tbl == 0) {
2108 recomp_len = 0;
2109 return;
2110 }
2111 }
2112 tbl = recomp_tbl;
2113
2114 /* remember where we are, to reposition */
2115 /* ...in case line is deleted from buffer-list */
2116 relisting_b_vals = 0;
2117 relisting_w_vals = 0;
2118 if (curbp == bp) {
2119 relisting_b_vals = b_vals;
2120 } else {
2121 curbp = bp;
2122 curwp = bp2any_wp(bp);
2123 }
2124 for_each_visible_window(wp) {
2125 if (wp->w_bufp == bp) {
2126 if (wp == savewp)
2127 relisting_w_vals = tbl[num].w_vals;
2128 curwp = wp; /* to make 'getccol()' work */
2129 curbp = curwp->w_bufp;
2130 tbl[num].wp = wp;
2131 tbl[num].top = line_no(bp, wp->w_line.l);
2132 tbl[num].line = line_no(bp, wp->w_dot.l);
2133 tbl[num].col = getccol(FALSE);
2134 save_vals(NUM_W_VALUES, global_w_values.wv,
2135 tbl[num].w_vals, wp->w_values.wv);
2136 if (++num >= recomp_len)
2137 break;
2138 }
2139 }
2140 curwp = savewp;
2141 curbp = savebp;
2142
2143 save_vals(NUM_B_VALUES, global_b_values.bv, b_vals, bp->b_values.bv);
2144 (bp->b_upbuff) (bp);
2145 copy_mvals(NUM_B_VALUES, bp->b_values.bv, b_vals);
2146
2147 /* reposition and restore */
2148 while (num-- != 0) {
2149 curwp = wp = tbl[num].wp;
2150 curbp = curwp->w_bufp;
2151 (void) vl_gotoline(tbl[num].top);
2152 wp->w_line.l = wp->w_dot.l;
2153 wp->w_line.o = 0;
2154 if (tbl[num].line != tbl[num].top)
2155 (void) vl_gotoline(tbl[num].line);
2156 (void) gocol(tbl[num].col);
2157 wp->w_flag |= WFMOVE | WFSBAR;
2158 copy_mvals(NUM_W_VALUES, wp->w_values.wv, tbl[num].w_vals);
2159 }
2160 curwp = savewp;
2161 curbp = savebp;
2162 curgoal = mygoal;
2163 relisting_b_vals = 0;
2164 relisting_w_vals = 0;
2165
2166 recomp_win = 0;
2167 }
2168 b_clr_obsolete(bp);
2169
2170 return;
2171 }
2172 #endif /* OPT_UPBUFF */
2173
2174 static int
TypeAhead(int force)2175 TypeAhead(int force)
2176 {
2177 if (force != TRUE) {
2178 if ((force == FALSE && !global_g_val(GMDSMOOTH_SCROLL))
2179 || (force == SORTOFTRUE))
2180 return (keystroke_avail());
2181 }
2182 return FALSE;
2183 }
2184
2185 /*
2186 * Update virtual screen line, given a LINE pointer.
2187 */
2188 static void
update_screen_line(WINDOW * wp,LINE * lp,int sline)2189 update_screen_line(WINDOW *wp, LINE *lp, int sline)
2190 {
2191 C_NUM left;
2192
2193 /*
2194 * Mark the screen lines changed, resetting the requests for reverse
2195 * video. Set the global 'horscroll' to the amount of horizontal
2196 * scrolling.
2197 */
2198 #ifdef WMDLINEWRAP
2199 if (w_val(wp, WMDLINEWRAP)) {
2200 int top_line = sline - wp->w_line.o;
2201 int m = (top_line >= 0) ? top_line : 0;
2202 int n = top_line + line_height(wp, lp);
2203 while (n > m)
2204 if (--n < mode_row(wp)) {
2205 vscreen[n]->v_flag |= VFCHG;
2206 vscreen[n]->v_flag &= ~VFREQ;
2207 }
2208 horscroll = 0;
2209 } else
2210 #endif
2211 if (sline >= 0) {
2212 vscreen[sline]->v_flag |= VFCHG;
2213 vscreen[sline]->v_flag &= ~VFREQ;
2214 if (w_val(wp, WVAL_SIDEWAYS))
2215 horscroll = w_val(wp, WVAL_SIDEWAYS);
2216 }
2217 left = horscroll;
2218
2219 if (lp != win_head(wp)) {
2220 vtmove(sline, -left);
2221 vtset(lp, wp);
2222 if (left && sline >= 0) {
2223 int zero = nu_width(wp);
2224 mark_extent(sline, zero, MRK_EXTEND_LEFT[0]);
2225 if (vtcol <= zero)
2226 vtcol = zero + 1;
2227 }
2228 } else {
2229 vtmove(sline, 0);
2230 vtputc(wp, MRK_EMPTY, 1);
2231 }
2232 horscroll = 0;
2233 #if OPT_COLOR
2234 if (sline >= 0) {
2235 ReqFcolor(vscreen[sline]) = gfcolor;
2236 ReqBcolor(vscreen[sline]) = gbcolor;
2237 }
2238 #endif
2239 }
2240
2241 /*
2242 * Update the current line to the virtual screen.
2243 *
2244 * 'wp' points to the window to update current line in.
2245 */
2246 static void
update_oneline(WINDOW * wp)2247 update_oneline(WINDOW *wp)
2248 {
2249 LINE *lp; /* line to update */
2250 int sline; /* physical screen line to update */
2251
2252 /* search down the line we want */
2253 lp = wp->w_line.l;
2254 sline = TopRow(wp);
2255 while (lp != wp->w_dot.l) {
2256 sline += line_height(wp, lp);
2257 lp = lforw(lp);
2258 }
2259
2260 update_screen_line(wp, lp, sline);
2261 vteeol();
2262 }
2263
2264 /*
2265 * Update all the lines in a window on the virtual screen.
2266 *
2267 * 'wp' points to the window to update current line in.
2268 */
2269 static void
update_all(WINDOW * wp)2270 update_all(WINDOW *wp)
2271 {
2272 LINE *lp; /* line to update */
2273 int sline; /* physical screen line to update */
2274
2275 /* search down the lines, updating them */
2276 lp = wp->w_line.l;
2277 sline = TopRow(wp);
2278 while (sline < mode_row(wp)) {
2279 update_screen_line(wp, lp, sline);
2280 vteeol();
2281 sline += line_height(wp, lp);
2282 if (lp != win_head(wp))
2283 lp = lforw(lp);
2284 }
2285 }
2286
2287 #if OPT_VIDEO_ATTRS
2288 /* Merge (or combine) the specified attribute into the vscreen structure */
2289 static void
mergeattr(WINDOW * wp,int row,int start_col,int end_col,unsigned attr)2290 mergeattr(WINDOW *wp, int row, int start_col, int end_col, unsigned attr)
2291 {
2292 int col;
2293
2294 if (start_col < 0)
2295 start_col = 0;
2296
2297 #ifdef WMDLINEWRAP
2298 if (w_val(wp, WMDLINEWRAP)) {
2299 for (col = start_col; col <= end_col; col++) {
2300 int x = row + col / term.cols;
2301 if (x < 0)
2302 continue;
2303 if (x < mode_row(wp)) {
2304 int y = col % term.cols;
2305 vscreen[x]->v_attrs[y] =
2306 (VIDEO_ATTR) ((vscreen[x]->v_attrs[y] | (attr & ~VAREV))
2307 ^ (attr & VAREV));
2308 } else {
2309 break;
2310 }
2311 }
2312 } else
2313 #endif
2314 {
2315 if (end_col >= term.cols)
2316 end_col = term.cols - 1;
2317 for (col = start_col; col <= end_col; col++)
2318 vscreen[row]->v_attrs[col] =
2319 (VIDEO_ATTR) ((vscreen[row]->v_attrs[col] | (attr & ~VAREV))
2320 ^ (attr & VAREV));
2321 }
2322 }
2323
2324 #if OPT_LINE_ATTRS
2325 static void
update_line_attrs(WINDOW * wp)2326 update_line_attrs(WINDOW *wp)
2327 {
2328 int row;
2329 LINE *lp;
2330 int linewrap;
2331
2332 #ifdef WMDLINEWRAP
2333 linewrap = w_val(wp, WMDLINEWRAP);
2334 #else
2335 linewrap = 0;
2336 #endif
2337
2338 row = TopRow(wp);
2339 lp = wp->w_line.l;
2340 while (row < wp->w_toprow + wp->w_ntrows && lp != win_head(wp)) {
2341 int save_offset = w_left_margin(wp);
2342 int save_column = 0;
2343 if (lp->l_attrs) {
2344 C_NUM start_col, end_col;
2345 int i, a;
2346 /* Attempt to set starting point intelligently. */
2347 if (wp->w_line.o < 0)
2348 i = col2offs(wp, lp, -(wp->w_line.o * term.cols));
2349 else {
2350 if (w_val(wp, WVAL_SIDEWAYS))
2351 i = col2offs(wp, lp, 0);
2352 else
2353 i = 0;
2354 }
2355 if (!linewrap && w_val(wp, WMDNUMBER) && w_val(wp, WVAL_SIDEWAYS)) {
2356 i += NU_WIDTH;
2357 }
2358 /* If i > 0, make sure it indexes a valid attribute. */
2359 if (i > 0) {
2360 int ii;
2361 for (ii = 0; ii < i; ii++)
2362 if (lp->l_attrs[ii] == 0)
2363 break;
2364 i = ii;
2365 }
2366 /* Scan attributes... */
2367 for (;;) {
2368 if (lp->l_attrs[i] == 0)
2369 break; /* get out if at end */
2370 while (lp->l_attrs[i] == 1)
2371 i++; /* skip normals */
2372 a = lp->l_attrs[i];
2373 if (a == 0)
2374 break; /* get out if at end */
2375 start_col = offs2col0(wp, lp, i, &save_offset, &save_column);
2376 i++;
2377 while (lp->l_attrs[i] == a)
2378 i++; /* find run of same attr */
2379 if (start_col < w_left_margin(wp))
2380 start_col = w_left_margin(wp);
2381 end_col = offs2col0(wp, lp, i, &save_offset, &save_column) - 1;
2382 if (!linewrap) {
2383 if (start_col > term.cols)
2384 break;
2385 mergeattr(wp, row, start_col, end_col,
2386 line_attr_tbl[a].vattr);
2387 if (end_col > term.cols)
2388 break;
2389 } else {
2390 /* mergeattr() handles wrapping of attributes for us. */
2391 mergeattr(wp, row, start_col, end_col,
2392 line_attr_tbl[a].vattr);
2393 if (row + end_col / term.cols >= mode_row(wp))
2394 break; /* Get out if no more to do. */
2395 }
2396 }
2397 }
2398 row += line_height(wp, lp);
2399 lp = lforw(lp);
2400 }
2401 }
2402 #endif /* OPT_LINE_ATTRS */
2403
2404 static void
update_window_attrs(WINDOW * wp)2405 update_window_attrs(WINDOW *wp)
2406 {
2407 BUFFER *bp = wp->w_bufp;
2408 AREGION **rpp;
2409 AREGION *ap;
2410 int i, lmax;
2411
2412 L_NUM start_wlnum, end_wlnum;
2413 LINE *lp;
2414 int rows;
2415 C_NUM col;
2416 #if OPT_TRACE
2417 int total_regions = 0;
2418 int visible_regions = 0;
2419 #endif
2420
2421 /*
2422 * Clear portion of virtual screen associated with window pointer
2423 * of all attributes.
2424 */
2425 /* FIXME: color; need to set to value indicating fg and bg for window */
2426 for (i = wp->w_toprow + wp->w_ntrows - 1; i >= wp->w_toprow; i--) {
2427 set_vattrs(i,
2428 lmap0[i].left, 0,
2429 (size_t) lmap0[i].right - (size_t) lmap0[i].left);
2430 }
2431
2432 #if OPT_LINE_ATTRS
2433 update_line_attrs(wp);
2434 #endif /* OPT_LINE_ATTRS */
2435
2436 /*
2437 * No need to do any more work on this window if there are no
2438 * attributes.
2439 */
2440 if (bp->b_attribs == NULL)
2441 return;
2442
2443 /*
2444 * Compute starting and ending line numbers for the window. We
2445 * also fill in lmap which is used for mapping line numbers to
2446 * screen row numbers.
2447 */
2448 lp = wp->w_line.l;
2449 start_wlnum =
2450 end_wlnum = line_no(bp, lp);
2451 rows = wp->w_ntrows;
2452
2453 lmax = end_wlnum - start_wlnum;
2454 lmap[lmax].lp = lp;
2455 lmap[lmax].map = TopRow(wp);
2456 lmap[lmax].left = -1;
2457 lmap[lmax].right = -1;
2458
2459 while ((rows -= line_height(wp, lp)) > 0) {
2460 lp = lforw(lp);
2461 end_wlnum++, lmax++;
2462 lmap[lmax].lp = lp;
2463 lmap[lmax].map = TopRow(wp) + wp->w_ntrows - rows;
2464 lmap[lmax].left = -1;
2465 lmap[lmax].right = -1;
2466 }
2467
2468 ++lmax;
2469 lmap[lmax].lp = 0;
2470 lmap[lmax].map = TopRow(wp) + wp->w_ntrows;
2471 lmap[lmax].left = -1;
2472 lmap[lmax].right = -1;
2473
2474 /*
2475 * Set current attributes in virtual screen associated with window
2476 * pointer.
2477 */
2478 rpp = &(bp->b_attribs);
2479 while ((ap = *rpp) != NULL) {
2480 AREGION **next = &(ap->ar_next);
2481 VIDEO_ATTR attr;
2482 C_NUM start_col, end_col;
2483 C_NUM rect_start_col = 0, rect_end_col = 0;
2484 L_NUM start_rlnum, end_rlnum, lnum, start_lnum, end_lnum;
2485 int visible = TRUE;
2486
2487 start_rlnum = line_no(bp, ap->ar_region.r_orig.l);
2488 end_rlnum = line_no(bp, ap->ar_region.r_end.l);
2489
2490 /* Garbage collect empty visible regions */
2491 if (start_rlnum == end_rlnum
2492 && VATTRIB(ap->ar_vattr) != 0
2493 && ap->ar_region.r_orig.o >= ap->ar_region.r_end.o) {
2494 free_attrib2(bp, rpp);
2495 continue;
2496 }
2497
2498 /* compute starting and ending line-numbers, given the region's */
2499 if (start_rlnum > start_wlnum) {
2500 start_lnum = start_rlnum;
2501 lp = ap->ar_region.r_orig.l;
2502 } else {
2503 start_lnum = start_wlnum;
2504 lp = wp->w_line.l;
2505 }
2506 end_lnum = (end_rlnum < end_wlnum) ? end_rlnum : end_wlnum;
2507
2508 /*
2509 * Filter out attribute regions that will not be on the screen.
2510 * Most of the regions generated by syntax filters start/end on
2511 * the same line, which allows us to make a simple/fast check.
2512 */
2513 if (start_rlnum == end_rlnum) {
2514 i = (start_rlnum - start_wlnum);
2515
2516 /*
2517 * Skip lines which don't fall into the vertical range.
2518 */
2519 if (i < 0 || i >= lmax) {
2520 visible = FALSE;
2521 } else {
2522
2523 /*
2524 * Compute the left/right offsets for the visible part of the
2525 * line the first time we need to compare it.
2526 */
2527 if (lmap[i].left < 0 && lmap[i].right < 0) {
2528 #ifdef WMDLINEWRAP
2529 if (w_val(wp, WMDLINEWRAP)) {
2530 int lines_remaining = (lmap[i + 1].map - lmap[i].map);
2531
2532 col = -((wp->w_line.o * term.cols) + nu_width(wp));
2533 lmap[i].left = col2offs(wp, lmap[i].lp, col);
2534
2535 col += (lines_remaining * term.cols);
2536 lmap[i].right = col2offs(wp, lmap[i].lp, col);
2537 } else
2538 #endif
2539 {
2540 col = -((wp->w_line.o * term.cols) + nu_width(wp));
2541 lmap[i].left = col2offs(wp, lmap[i].lp, col);
2542
2543 col += term.cols;
2544 lmap[i].right = col2offs(wp, lmap[i].lp, col);
2545 }
2546 TRACE2(("...update_window_attrs row %d [%d..%d] (%d)\n",
2547 lmap[i].map,
2548 lmap[i].left,
2549 lmap[i].right,
2550 lmap[i].right - lmap[i].left));
2551 }
2552
2553 if (ap->ar_region.r_orig.o >= lmap[i].right) {
2554 visible = FALSE;
2555 } else if (ap->ar_region.r_end.o < lmap[i].left) {
2556 visible = FALSE;
2557 }
2558 }
2559 }
2560 #if OPT_TRACE
2561 total_regions++;
2562 #endif
2563 if (visible) {
2564 #if OPT_TRACE
2565 visible_regions++;
2566 #endif
2567
2568 attr = ap->ar_vattr;
2569
2570 /* if it's a rectangle, precompute the start/end columns */
2571 if (ap->ar_shape == rgn_RECTANGLE) {
2572 int n;
2573 rect_start_col = mark2col(wp, ap->ar_region.r_orig);
2574 rect_end_col = mark2col(wp, ap->ar_region.r_end);
2575 if (rect_end_col < rect_start_col) {
2576 col = rect_end_col;
2577 rect_end_col = rect_start_col;
2578 rect_start_col = col;
2579 n = MARK2COL(wp, ap->ar_region.r_orig);
2580 } else {
2581 n = MARK2COL(wp, ap->ar_region.r_end);
2582 }
2583 if (rect_end_col < n)
2584 rect_end_col = n;
2585 }
2586 for (lnum = start_lnum; lnum <= end_lnum; lnum++, lp = lforw(lp)) {
2587 int row;
2588 if (ap->ar_shape == rgn_RECTANGLE) {
2589 start_col = rect_start_col;
2590 } else if (lnum == start_rlnum) {
2591 start_col = mark2col(wp, ap->ar_region.r_orig);
2592 } else {
2593 start_col = w_left_margin(wp) + nu_width(wp);
2594 }
2595
2596 if (start_col < w_left_margin(wp))
2597 start_col = (lnum == start_rlnum)
2598 ? w_left_margin(wp) + nu_width(wp)
2599 : w_left_margin(wp);
2600
2601 if (ap->ar_shape == rgn_RECTANGLE) {
2602 end_col = rect_end_col;
2603 } else if (lnum == end_rlnum) {
2604 end_col = mark2col(wp, ap->ar_region.r_end) - 1;
2605 } else {
2606 end_col = offs2col(wp, lp, llength(lp) + 1) - 1;
2607 #ifdef WMDLINEWRAP
2608 if (w_val(wp, WMDLINEWRAP)
2609 && (end_col % term.cols) == 0)
2610 end_col--; /* cannot highlight the newline */
2611 #endif
2612 }
2613 row = lmap[lnum - start_wlnum].map;
2614 mergeattr(wp, row, start_col, end_col, attr);
2615 }
2616 }
2617 rpp = next;
2618 }
2619 TRACE2(("update_window_attrs visible %d / %d\n",
2620 visible_regions,
2621 total_regions));
2622 }
2623 #endif /* OPT_VIDEO_ATTRS */
2624
2625 /*
2626 * If the screen is garbage, clear the physical screen and the virtual screen
2627 * and force a full update.
2628 */
2629 static void
update_garbaged_screen(void)2630 update_garbaged_screen(void)
2631 {
2632 #if !OPT_PSCREEN
2633 int j;
2634 #endif
2635 int i;
2636
2637 TRACE((T_CALLED "update_garbaged_screen\n"));
2638
2639 for (i = 0; i < term.rows; ++i) {
2640 vscreen[i]->v_flag |= VFCHG;
2641 #if OPT_REVSTA
2642 vscreen[i]->v_flag &= ~VFREV;
2643 #endif
2644 #if OPT_COLOR
2645 CurFcolor(vscreen[i]) = -1;
2646 CurBcolor(vscreen[i]) = -1;
2647 #endif
2648 #if !OPT_PSCREEN
2649 for (j = 0; j < term.cols; ++j) {
2650 CELL_TEXT(i, j) = ' ';
2651 #if OPT_VIDEO_ATTRS
2652 CELL_ATTR(i, j) = 0;
2653 #endif /* OPT_VIDEO_ATTRS */
2654 }
2655 #endif
2656 }
2657 #if !OPT_PSCREEN
2658 #if OPT_COLOR
2659 term.setfore(gfcolor); /* Need to set before erasing. */
2660 term.setback(gbcolor);
2661 #endif
2662 movecursor(0, 0); /* Erase the screen. */
2663 reset_term_attrs();
2664 term.eeop();
2665 #else
2666 kbd_erase_to_end(0);
2667 #endif
2668 sgarbf = FALSE; /* Erase-page clears */
2669 need_update = FALSE;
2670 kbd_flush();
2671
2672 returnVoid();
2673 }
2674
2675 /*
2676 * Update the extended line which the cursor is currently on at a column less
2677 * than the terminal width. The line will be scrolled right or left to let the
2678 * user see where the cursor is.
2679 */
2680 static int
update_extended_line(int col,int excess,int use_excess)2681 update_extended_line(int col, int excess, int use_excess)
2682 {
2683 int rcursor;
2684 int zero;
2685 int scrollsiz;
2686
2687 /* calculate what column the real cursor will end up in */
2688 if (!use_excess) {
2689 curcol = col;
2690 rcursor = (col % (term.cols - term.cols / 10));
2691 } else {
2692 scrollsiz = term.rows - ((term.rows / 10) * 2);
2693 rcursor = ((excess - 1) % scrollsiz) + term.cols / 10;
2694 }
2695 horscroll = col - rcursor;
2696
2697 /* Scan through the line outputting characters to the virtual screen
2698 * once we reach the left edge. */
2699 vtmove(currow, -horscroll); /* start scanning offscreen */
2700 vtset(DOT.l, curwp);
2701
2702 /* truncate the virtual line */
2703 vteeol();
2704
2705 horscroll = 0;
2706
2707 if ((use_excess || col != rcursor)
2708 && (zero = nu_width(curwp)) < term.cols) {
2709 /* ... put a marker in column 1 */
2710 vscreen[currow]->v_text[zero] = (VIDEO_TEXT) MRK_EXTEND_LEFT[0];
2711 }
2712 vscreen[currow]->v_flag |= (VFEXT | VFCHG);
2713 return rcursor;
2714 }
2715
2716 /*
2717 * Update the position of the hardware cursor and handle extended lines. This
2718 * is the only update for simple moves. Returns the screen location for the
2719 * cursor, and a boolean indicating if full sideways scroll was necessary
2720 */
2721 static int
update_cursor_position(int * screenrowp,int * screencolp)2722 update_cursor_position(int *screenrowp, int *screencolp)
2723 {
2724 LINE *lp;
2725 #ifdef WMDLINEWRAP
2726 int i;
2727 #endif
2728 int col, excess;
2729 int collimit;
2730 int moved = FALSE;
2731 int nuadj;
2732 int liadj;
2733
2734 if (curwp == 0) {
2735 *screenrowp = 0;
2736 *screencolp = 0;
2737 return FALSE;
2738 }
2739 nuadj = is_empty_buf(curwp->w_bufp) ? 0 : nu_width(curwp);
2740 liadj = (w_val(curwp, WMDLIST)) ? 1 : 0;
2741
2742 TRACE2((T_CALLED "update_cursor_position\n"));
2743 /* find the current row */
2744 lp = curwp->w_line.l;
2745 currow = TopRow(curwp);
2746 while (lp != DOT.l) {
2747 currow += line_height(curwp, lp);
2748 lp = lforw(lp);
2749 if (lp == curwp->w_line.l
2750 || currow > mode_row(curwp)) {
2751 mlforce("BUG: lost dot updpos(). setting at top");
2752 DOT.l = lforw(buf_head(curbp));
2753 DOT.o = b_left_margin(curbp);
2754 lp = curwp->w_line.l = DOT.l;
2755 currow = TopRow(curwp);
2756 }
2757 }
2758
2759 /*
2760 * Find the current column. Reuse the ruler column-value if we
2761 * computed it.
2762 */
2763 #ifdef WMDRULER
2764 if (w_val(curwp, WMDRULER))
2765 col = curwp->w_ruler_col + w_left_margin(curwp) - 1;
2766 else
2767 #endif
2768 col = dot_to_vcol(curwp) + w_left_margin(curwp);
2769
2770 #ifdef WMDLINEWRAP
2771 if (w_val(curwp, WMDLINEWRAP)) {
2772 curcol = col;
2773 collimit = term.cols - nuadj;
2774 *screenrowp = currow;
2775 if (col >= collimit) {
2776 col -= collimit;
2777 *screenrowp += 1;
2778 if (col >= term.cols)
2779 *screenrowp += (col / term.cols);
2780 *screencolp = col % term.cols;
2781 } else {
2782 *screencolp = col + nuadj;
2783 }
2784 /* kludge to keep the cursor within the window */
2785 i = mode_row(curwp) - 1;
2786 if (*screenrowp > i) {
2787 *screenrowp = i;
2788 *screencolp = term.cols - 1;
2789 }
2790 TRACE2(("... (%d,%d)\n", *screenrowp, *screencolp));
2791 return2Code(FALSE);
2792 } else
2793 #endif
2794 *screenrowp = currow;
2795
2796 /* ...adjust to offset from shift-margin */
2797 curcol = col - w_val(curwp, WVAL_SIDEWAYS);
2798 *screencolp = curcol;
2799
2800 /* if extended, flag so and update the virtual line image */
2801 collimit = col_limit(curwp);
2802 excess = curcol - collimit + liadj;
2803 if ((excess > 0) || (excess == 0 &&
2804 (DOT.o < llength(DOT.l) - 1))) {
2805 if (w_val(curwp, WMDHORSCROLL)) {
2806 (void) mvrightwind(TRUE, excess + collimit / 2);
2807 moved = TRUE;
2808 preset_lmap0();
2809 } else {
2810 *screencolp = update_extended_line(col, excess, TRUE);
2811 }
2812 } else if (w_val(curwp, WVAL_SIDEWAYS) && (curcol < 1)) {
2813 if (w_val(curwp, WMDHORSCROLL)) {
2814 (void) mvleftwind(TRUE, -curcol + collimit / 2 + 1);
2815 moved = TRUE;
2816 preset_lmap0();
2817 } else {
2818 *screencolp = update_extended_line(col, 0, FALSE);
2819 }
2820 } else {
2821 if (vscreen[currow]->v_flag & VFEXT) {
2822 update_screen_line(curwp, lp, currow);
2823 vteeol();
2824 /* this line no longer is extended */
2825 vscreen[currow]->v_flag &= ~VFEXT;
2826 }
2827 }
2828 if (!moved)
2829 *screencolp += nuadj;
2830 TRACE2(("... (%d,%d)\n", *screenrowp, *screencolp));
2831 return2Code(moved);
2832 }
2833
2834 #if CAN_SCROLL
2835
2836 static int
same_video_text(const VIDEO_TEXT * virtual_text,const VIDEO_TEXT * physical_text,int ncols)2837 same_video_text(const VIDEO_TEXT * virtual_text, const VIDEO_TEXT *
2838 physical_text, int ncols)
2839 {
2840 int rc = TRUE;
2841 int j;
2842
2843 for (j = 0; j < ncols; ++j) {
2844 if (virtual_text[j] != physical_text[j]) {
2845 rc = FALSE;
2846 break;
2847 }
2848 }
2849
2850 return rc;
2851 }
2852
2853 /*
2854 * return TRUE on text match
2855 *
2856 * vrow - virtual row
2857 * prow - physical row
2858 */
2859 static int
same_row_text(int vrow,int prow)2860 same_row_text(int vrow, int prow)
2861 {
2862 return same_video_text(vscreen[vrow]->v_text,
2863 pscreen[prow]->v_text,
2864 term.cols);
2865 }
2866
2867 /*
2868 * return the index of the first trailing whitespace character
2869 */
2870 static int
endofline(const VIDEO_TEXT * s,int n)2871 endofline(const VIDEO_TEXT * s, int n)
2872 {
2873 int i;
2874 for (i = n - 1; i >= 0; i--)
2875 if (s[i] != ' ')
2876 return (i + 1);
2877 return (0);
2878 }
2879
2880 /* move the "count" lines starting at "from" to "to" */
2881 static void
scroll_screen(int from,int to,int count)2882 scroll_screen(int from, int to, int count)
2883 {
2884 beginDisplay();
2885 ttrow = ttcol = -1;
2886 term.scroll(from, to, count);
2887 endofDisplay();
2888 }
2889
2890 /*
2891 * Optimize simple scrolled screen region movements. Argument chooses between
2892 * looking for inserts or deletes. Original code by Roger Ove, for an early
2893 * version of MicroEMACS. used by permission.
2894 *
2895 * inserts - returns true if it does an optimization
2896 */
2897 static int
simple_scroll(int inserts)2898 simple_scroll(int inserts)
2899 {
2900 struct VIDEO *vpv; /* virtual screen image */
2901 struct VIDEO *vpp; /* physical screen image */
2902 int i, j, k;
2903 int rows, cols;
2904 int first, match, count, ptarget = 0, vtarget = 0;
2905 size_t end;
2906 int longmatch, longcount;
2907 int longinplace, inplace; /* count of lines which are already
2908 in the right place */
2909 int from, to;
2910
2911 if (term.scroll == nullterm_scroll) /* can't scroll */
2912 return FALSE;
2913
2914 rows = term.rows - 1;
2915 cols = term.cols;
2916
2917 /* find first line that doesn't match */
2918 first = -1;
2919 for (i = 0; i < rows; i++) {
2920 if (!same_row_text(i, i)) {
2921 first = i;
2922 break;
2923 }
2924 }
2925 if (first < 0)
2926 return FALSE; /* there isn't one */
2927
2928 vpv = vscreen[first];
2929 vpp = pscreen[first];
2930
2931 if (inserts) {
2932 /* determine types of potential scrolls */
2933 end = (size_t) endofline(vpv->v_text, cols);
2934 if (end == 0)
2935 ptarget = first; /* newlines */
2936 else if (same_video_text(vpp->v_text, vpv->v_text, (int) end))
2937 ptarget = first + 1; /* broken line newlines */
2938 else
2939 ptarget = first;
2940 from = ptarget;
2941 } else {
2942 from = vtarget = first + 1;
2943 }
2944
2945 /* can we find that text elsewhere ? */
2946 longmatch = -1;
2947 longcount = 0;
2948 longinplace = 0;
2949 for (i = from + 1; i < rows; i++) {
2950 if (inserts ? same_row_text(i, from) : same_row_text(from, i)) {
2951 match = i;
2952 count = 1;
2953 inplace = same_row_text(match, match) ? 1 : 0;
2954 for (j = match + 1, k = from + 1; j < rows && k < rows; j++, k++) {
2955 if (inserts ? same_row_text(j, k) : same_row_text(k, j)) {
2956 count++;
2957 if (same_row_text(j, j))
2958 inplace++;
2959 } else
2960 break;
2961 }
2962 if (longcount - longinplace < count - inplace) {
2963 longcount = count;
2964 longmatch = match;
2965 longinplace = inplace;
2966 }
2967 }
2968 }
2969 match = longmatch;
2970 count = longcount;
2971
2972 if (!inserts) {
2973 /* full kill case? */
2974 if (match > 0 && same_row_text(first, match - 1)) {
2975 vtarget--;
2976 match--;
2977 count++;
2978 }
2979 }
2980
2981 if (match > 0 && count > 2) { /* got a scroll */
2982 /* move the count lines starting at ptarget to match */
2983 /* mlwrite("scrolls: move the %d lines starting at %d to %d",
2984 count,ptarget,match);
2985 */
2986 if (inserts) {
2987 from = ptarget;
2988 to = match;
2989 } else {
2990 from = match;
2991 to = vtarget;
2992 }
2993 #if OPT_PSCREEN
2994 /*
2995 * Update lines _before_ the scroll so that they will be available for
2996 * any updates which need to be done (due to a GraphicsExpose event in
2997 * X11...these occur when scrolling a partially obscured window). Note
2998 * that in the typical case of scrolling a line or two that very few
2999 * memory accesses are performed. We mostly shuffle pointers around.
3000 */
3001 #define SWAP_PLINE(a, b) do { VIDEO *temp = pscreen[a]; \
3002 pscreen[a] = pscreen[b]; \
3003 pscreen[b] = temp; } one_time
3004 #define CLEAR_PLINE(a) do { \
3005 MARK_LINE_DIRTY(a); \
3006 for (j = 0; j < term.cols; j++) { \
3007 CELL_TEXT(a,j) = ' '; \
3008 CELL_ATTR(a,j) = 0; \
3009 } \
3010 } one_time
3011 if (from < to) {
3012 /* FIXME: color */
3013 for (i = from; i < to; i++)
3014 CLEAR_PLINE(i + count);
3015 for (i = count - 1; i >= 0; i--)
3016 SWAP_PLINE(from + i, to + i);
3017 } else {
3018 /* FIXME: color */
3019 for (i = to; i < from; i++)
3020 CLEAR_PLINE(i);
3021 for (i = 0; i < count; i++)
3022 SWAP_PLINE(from + i, to + i);
3023 }
3024 #endif /* OPT_PSCREEN */
3025 scroll_screen(from, to, count);
3026 #if !OPT_PSCREEN
3027 for (i = 0; i < count; i++) {
3028 vpp = pscreen[to + i];
3029 vpv = vscreen[to + i];
3030 for (j = 0; j < cols; ++j)
3031 vpp->v_text[j] = vpv->v_text[j];
3032 }
3033 #if OPT_VIDEO_ATTRS
3034 #define SWAP_ATTR_PTR(a, b) do { VIDEO_ATTR *temp = pscreen[a]->v_attrs; \
3035 pscreen[a]->v_attrs = pscreen[b]->v_attrs; \
3036 pscreen[b]->v_attrs = temp; } one_time
3037 if (from < to) {
3038 /* FIXME: color */
3039 for (i = from; i < to; i++)
3040 for (j = 0; j < term.cols; j++)
3041 CELL_ATTR(i + count, j) = 0;
3042 for (i = count - 1; i >= 0; i--)
3043 SWAP_ATTR_PTR(from + i, to + i);
3044
3045 } else {
3046 /* FIXME: color */
3047 for (i = to; i < from; i++)
3048 for (j = 0; j < term.cols; j++)
3049 CELL_ATTR(i, j) = 0;
3050 for (i = 0; i < count; i++)
3051 SWAP_ATTR_PTR(from + i, to + i);
3052 }
3053 #undef SWAP_ATTR_PTR
3054 #endif /* OPT_VIDEO_ATTRS */
3055 if (inserts) {
3056 from = ptarget;
3057 to = match;
3058 } else {
3059 from = vtarget + count;
3060 to = match + count;
3061 }
3062 for (i = from; i < to; i++) {
3063 VIDEO_TEXT *txt;
3064 txt = pscreen[i]->v_text;
3065 for (j = 0; j < term.cols; ++j)
3066 txt[j] = ' ';
3067 vscreen[i]->v_flag |= VFCHG;
3068 }
3069 #endif /* !OPT_PSCREEN */
3070 return (TRUE);
3071 }
3072 return (FALSE);
3073 }
3074
3075 #endif /* CAN_SCROLL */
3076
3077 /*
3078 * update the physical screen from the virtual screen. The 'force' flag
3079 * forces an update even if there is type-ahead.
3080 */
3081 static void
update_physical_screen(int force GCC_UNUSED)3082 update_physical_screen(int force GCC_UNUSED)
3083 {
3084 int i;
3085
3086 TRACE((T_CALLED "update_physical_screen(%d)\n", force));
3087 #if CAN_SCROLL
3088 if (scrflags & WFKILLS)
3089 (void) simple_scroll(FALSE);
3090 if (scrflags & WFINS)
3091 (void) simple_scroll(TRUE);
3092 scrflags = 0;
3093 #endif
3094
3095 for (i = 0; i < term.rows; ++i) {
3096 /* for each line that needs to be updated */
3097 if ((vscreen[i]->v_flag & (VFCHG | VFCOL)) != 0) {
3098 #if !DISP_X11
3099 if (TypeAhead(force))
3100 returnVoid();
3101 #endif
3102 update_line(i, 0, term.cols);
3103 }
3104 }
3105 returnVoid();
3106 }
3107
3108 #if OPT_MLFORMAT || OPT_POSFORMAT || OPT_TITLE
3109 static void
mlfs_prefix(const char ** fsp,char ** msp,int lchar)3110 mlfs_prefix(const char **fsp, char **msp, int lchar)
3111 {
3112 const char *fs = *fsp;
3113 char *ms = *msp;
3114
3115 if (*fs == ':') {
3116 fs++;
3117 while (*fs && *fs != ':') {
3118 if (*fs != '%')
3119 *ms++ = *fs++;
3120 else {
3121 fs++;
3122 switch (*fs++) {
3123 case EOS:
3124 fs--;
3125 break;
3126 case '%':
3127 *ms++ = '%';
3128 break;
3129 case ':':
3130 *ms++ = ':';
3131 break;
3132 case '-':
3133 *ms++ = (char) lchar;
3134 break;
3135 default:
3136 *ms++ = '%';
3137 *ms++ = *(fs - 1);
3138 break;
3139 }
3140 }
3141 }
3142 }
3143 *fsp = fs;
3144 *msp = ms;
3145 }
3146
3147 static void
mlfs_suffix(const char ** fsp,char ** msp,int lchar)3148 mlfs_suffix(const char **fsp, char **msp, int lchar)
3149 {
3150 mlfs_prefix(fsp, msp, lchar);
3151 if (**fsp == ':')
3152 (*fsp)++;
3153 }
3154
3155 static void
mlfs_skipfix(const char ** fsp)3156 mlfs_skipfix(const char **fsp)
3157 {
3158 const char *fs = *fsp;
3159 if (*fs == ':') {
3160 for (fs++; *fs && *fs != ':'; fs++) ;
3161 if (*fs == ':')
3162 fs++;
3163 for (; *fs && *fs != ':'; fs++) ;
3164 if (*fs == ':')
3165 fs++;
3166 }
3167 *fsp = fs;
3168 }
3169 #endif /* OPT_MLFORMAT */
3170
3171 #define PutModename(format, name) { \
3172 if (ms != 0) { \
3173 ms = lsprintf(ms, format, \
3174 mcnt ? ' ' : '[', \
3175 name); \
3176 } \
3177 mcnt++; \
3178 }
3179
3180 #define PutMode(mode,name) \
3181 if (b_val(bp, mode)) PutModename("%c%s", name)
3182
3183 #if OPT_MAJORMODE
3184 #define PutMajormode(bp) \
3185 if (bp->majr != 0) \
3186 PutModename("%c%s", \
3187 brief \
3188 ? bp->majr->shortname \
3189 : bp->majr->longname)
3190 #else
3191 #define PutMajormode(bp) /*nothing */
3192 #endif
3193
3194 static int
modeline_modes(BUFFER * bp,char ** msptr,int brief)3195 modeline_modes(BUFFER *bp, char **msptr, int brief)
3196 {
3197 char *ms = msptr ? *msptr : 0;
3198 size_t mcnt = 0;
3199
3200 PutMajormode(bp);
3201 #if COMPLETE_FILES || COMPLETE_DIRS
3202 if (b_is_directory(bp))
3203 PutModename("%c%s", brief ? "dir" : "directory");
3204 #endif
3205 #if !OPT_MAJORMODE
3206 PutMode(MDCMOD, "cmode");
3207 #endif
3208 #if OPT_ENCRYPT
3209 PutMode(MDCRYPT, "crypt");
3210 #endif
3211 {
3212 #if OPT_RECORDSEP_CHOICES
3213 int show = FALSE;
3214
3215 switch (b_val(bp, VAL_SHOW_FORMAT)) {
3216 case SF_ALWAYS:
3217 show = TRUE;
3218 break;
3219 case SF_NEVER:
3220 show = FALSE;
3221 break;
3222 case SF_DIFFERS:
3223 show = b_val(bp, VAL_RECORD_SEP) != global_b_val(VAL_RECORD_SEP);
3224 break;
3225 case SF_FOREIGN:
3226 show = b_val(bp, VAL_RECORD_SEP) != RS_SYS_DEFAULT;
3227 break;
3228 case SF_LOCAL:
3229 show = is_local_b_val(bp, VAL_RECORD_SEP);
3230 break;
3231 }
3232 if (show)
3233 PutModename("%c%s",
3234 choice_to_name(&fsm_recordsep_blist,
3235 b_val(bp, VAL_RECORD_SEP)));
3236 #else
3237 PutMode(MDDOS, brief ? "dos" : "dos-style");
3238 #endif
3239 }
3240 PutMode(MDREADONLY, brief ? "ro" : "read-only");
3241 PutMode(MDVIEW, brief ? "view" : "view-only");
3242 PutMode(MDLOADING, "loading");
3243 #if OPT_LCKFILES
3244 PutMode(MDLOCKED, "locked by");
3245 if (ms != 0 && b_val(bp, MDLOCKED))
3246 ms = lsprintf(ms, " %s", b_val_ptr(bp, VAL_LOCKER));
3247 #endif
3248 if (mcnt && ms)
3249 *ms++ = ']';
3250 if (b_is_changed(bp)) {
3251 if (ms != 0)
3252 ms = lsprintf(ms, "%s[modified]", mcnt ? " " : "");
3253 mcnt++;
3254 }
3255 if (kbd_mac_recording()) {
3256 if (ms != 0)
3257 ms = lsprintf(ms, "%s[recording]", mcnt ? " " : "");
3258 mcnt++;
3259 }
3260 if (ms != 0)
3261 *msptr = ms;
3262 return (mcnt != 0);
3263 }
3264
3265 static char
modeline_show(WINDOW * wp,int lchar)3266 modeline_show(WINDOW *wp, int lchar)
3267 {
3268 char ic = (char) lchar;
3269 BUFFER *bp = wp->w_bufp;
3270
3271 if (b_val(bp, MDSHOWMODE)) {
3272 #ifdef insertmode /* insert mode is a trait for each window */
3273 if (wp->w_traits.insmode == INSMODE_INS)
3274 ic = 'I';
3275 else if (wp->w_traits.insmode == INSMODE_RPL)
3276 ic = 'R';
3277 else if (wp->w_traits.insmode == INSMODE_OVR)
3278 ic = 'O';
3279 #else /* insertmode is a variable global to all windows */
3280 if (wp == curwp) {
3281 if (insertmode == INSMODE_INS)
3282 ic = 'I';
3283 else if (insertmode == INSMODE_RPL)
3284 ic = 'R';
3285 else if (insertmode == INSMODE_OVR)
3286 ic = 'O';
3287 }
3288 #endif /* !defined(insertmode) */
3289 }
3290 return ic;
3291 }
3292
3293 static const char *
rough_position(WINDOW * wp)3294 rough_position(WINDOW *wp)
3295 {
3296 LINE *lp = wp->w_line.l;
3297 int rows = wp->w_ntrows;
3298 const char *msg = 0;
3299
3300 while (rows-- > 0) {
3301 lp = lforw(lp);
3302 if (lp == win_head(wp)) {
3303 msg = "bot";
3304 break;
3305 }
3306 }
3307 if (lback(wp->w_line.l) == win_head(wp)) {
3308 if (msg) {
3309 if (wp->w_line.l == win_head(wp))
3310 msg = "emp";
3311 else
3312 msg = "all";
3313 } else {
3314 msg = "top";
3315 }
3316 }
3317 if (!msg)
3318 msg = "mid";
3319 return msg;
3320 }
3321
3322 #define MAX_FORMAT ((NFILEN + 1) * 2 * COLS_UTF8)
3323
3324 #define MARK_COL 80
3325
3326 #if OPT_MLFORMAT || OPT_POSFORMAT || OPT_TITLE
3327
3328 #define L_CURL '{'
3329 #define R_CURL '}'
3330
3331 static int
percentage(WINDOW * wp)3332 percentage(WINDOW *wp)
3333 {
3334 BUFFER *bp = wp->w_bufp;
3335 L_NUM val;
3336 L_NUM denom = vl_line_count(bp);
3337
3338 #ifdef WMDRULER
3339 if (w_val(wp, WMDRULER) && !is_empty_buf(bp))
3340 val = wp->w_ruler_line;
3341 else
3342 #endif
3343 val = line_no(bp, wp->w_dot.l);
3344
3345 return PERCENT(val, denom);
3346 }
3347
3348 #if OPT_MULTIBYTE
3349 static int
str_col2offs(WINDOW * wp,int target_col,const char * source,int length)3350 str_col2offs(WINDOW *wp, int target_col, const char *source, int length)
3351 {
3352 const char *base = source;
3353 int result = 0;
3354 int have_col = 0;
3355
3356 while (have_col < target_col) {
3357 int rc;
3358 UINT value;
3359 int need = column_sizes(wp, source, (UINT) length, &rc);
3360 int cells = need;
3361
3362 switch (need) {
3363 case COLS_UTF8:
3364 rc = vl_conv_to_utf32(&value, source, (B_COUNT) length);
3365 cells = vl_wcwidth((int) value);
3366 source += rc;
3367 length -= rc;
3368 break;
3369 case COLS_CTRL:
3370 source += 1;
3371 length -= 1;
3372 break;
3373 case COLS_8BIT:
3374 source += 1;
3375 length -= 1;
3376 break;
3377 default:
3378 source += 1;
3379 length -= 1;
3380 break;
3381 }
3382 have_col += cells;
3383
3384 if (have_col < target_col)
3385 result = (int) (source - base);
3386 }
3387 return result;
3388 }
3389 #else
3390 #define str_col2offs(wp,target_col,value,length) target_col
3391 #endif
3392
3393 #define HaveEnough(string) ((ms + strlen(string)) - base < MAX_FORMAT)
3394
3395 /*
3396 * Format special single-use messages, i.e., the modeline format, which has
3397 * a number of special variables that we would like to output quickly.
3398 */
3399 void
special_formatter(TBUFF ** result,const char * fs,WINDOW * wp)3400 special_formatter(TBUFF **result, const char *fs, WINDOW *wp)
3401 {
3402 BUFFER *bp;
3403 char *ms;
3404 char *base;
3405 const char *want;
3406 const char *save_fs;
3407 char left_ms[MAX_FORMAT];
3408 char right_ms[MAX_FORMAT];
3409 char temp[MAX_FORMAT / 2];
3410 int have_cols;
3411 int col;
3412 int fc;
3413 char lchar;
3414 int need_eighty_column_indicator;
3415 int right_cols;
3416 int right_offs;
3417 int n;
3418 int skip = TRUE;
3419
3420 if (fs == 0)
3421 return;
3422
3423 if (wp == wnullp)
3424 return;
3425
3426 TRACE((T_CALLED "special_formatter %s\n", fs));
3427 tb_init(result, EOS);
3428
3429 left_ms[0] = right_ms[0] = EOS;
3430 ms = base = left_ms;
3431 need_eighty_column_indicator = FALSE;
3432
3433 bp = wp->w_bufp;
3434
3435 if (wp == curwp) { /* mark the current buffer */
3436 lchar = '=';
3437 } else {
3438 #if OPT_REVSTA
3439 if (revexist)
3440 lchar = ' ';
3441 else
3442 #endif
3443 lchar = '-';
3444 }
3445
3446 while (*fs) {
3447 /* check for single-character buffer overflow */
3448 if ((ms + (COLS_UTF8 + 2)) - base >= MAX_FORMAT) {
3449 if (base == right_ms)
3450 break;
3451 if (strncmp(fs, "%=", (size_t) 2)) {
3452 ++fs;
3453 continue;
3454 }
3455 }
3456 if (*fs != '%') {
3457 *ms++ = *fs++;
3458 } else {
3459 fs++;
3460 switch ((fc = *fs++)) {
3461 case EOS: /* Null character ! */
3462 fs--;
3463 break;
3464 case '%':
3465 case ':':
3466 *ms++ = *(fs - 1);
3467 break;
3468 case '|':
3469 need_eighty_column_indicator = TRUE;
3470 break;
3471 case '-':
3472 *ms++ = lchar;
3473 break;
3474 case '=':
3475 *ms = EOS;
3476 ms = base = right_ms;
3477 break;
3478 case 'i': /* insert mode indicator */
3479 *ms++ = modeline_show(wp, lchar);
3480 break;
3481 case 'b':
3482 want = (bp ? bp->b_bname : UNNAMED_BufName);
3483 if (!HaveEnough(want))
3484 continue;
3485 ms = lsprintf(ms, "%s", want);
3486 break;
3487 case 'M':
3488 case 'm':
3489 if (bp != 0 && modeline_modes(bp, (char **) 0, (fc == 'M'))) {
3490 mlfs_prefix(&fs, &ms, lchar);
3491 (void) modeline_modes(bp, &ms, (fc == 'M'));
3492 mlfs_suffix(&fs, &ms, lchar);
3493 } else
3494 mlfs_skipfix(&fs);
3495 break;
3496 case 'f':
3497 case 'F':
3498 skip = TRUE; /* assumption */
3499
3500 if (bp != 0 && bp->b_fname != 0) {
3501 char *p;
3502
3503 /*
3504 * when b_fname is a pipe cmd, it can be
3505 * arbitrarily long
3506 */
3507 vl_strncpy(temp, bp->b_fname, sizeof(temp));
3508
3509 if ((p = shorten_path(temp, FALSE)) != 0
3510 && *(p = skip_space_tab(p)) != '\0'
3511 && !eql_bname(bp, p)
3512 && ((fc == 'f')
3513 ? !is_internalname(p)
3514 : is_internalname(p))) {
3515 mlfs_prefix(&fs, &ms, lchar);
3516 if (!HaveEnough(p))
3517 continue;
3518 ms = lsprintf(ms, "%s", p);
3519 mlfs_suffix(&fs, &ms, lchar);
3520 skip = FALSE;
3521 }
3522 }
3523 if (skip)
3524 mlfs_skipfix(&fs);
3525 break;
3526 case 'r':
3527 case 'n':
3528 case 'N':
3529 mlfs_prefix(&fs, &ms, lchar);
3530 if (bp != 0) {
3531 if (bp->b_fname != 0 && !is_internalname(bp->b_bname)) {
3532
3533 vl_strncpy(temp, bp->b_fname, sizeof(temp));
3534
3535 switch (fc) {
3536 case 'r':
3537 want = shorten_path(temp, FALSE);
3538 if (want == 0)
3539 want = temp;
3540 break;
3541 case 'n':
3542 want = pathleaf(temp);
3543 break;
3544 default:
3545 want = temp;
3546 break;
3547 }
3548 } else {
3549 want = bp->b_bname;
3550 }
3551 } else {
3552 want = UNNAMED_BufName;
3553 }
3554 if (!HaveEnough(want))
3555 continue;
3556 ms = lsprintf(ms, "%s", want);
3557 mlfs_suffix(&fs, &ms, lchar);
3558 break;
3559 #ifdef WMDRULER
3560 case 'l': /* line number */
3561 case 'c': /* column number */
3562 case 'p': /* percentage */
3563 case 'L': /* number of lines in buffer */
3564
3565 if (w_val(wp, WMDRULER) && (bp != 0 && !is_empty_buf(bp))) {
3566 int val = 0;
3567 switch (fc) {
3568 case 'l':
3569 val = wp->w_ruler_line;
3570 break;
3571 case 'L':
3572 val = vl_line_count(bp);
3573 break;
3574 case 'c':
3575 val = wp->w_ruler_col;
3576 break;
3577 case 'p':
3578 val = percentage(wp);
3579 break;
3580 }
3581 mlfs_prefix(&fs, &ms, lchar);
3582 ms = lsprintf(ms, "%d", val);
3583 mlfs_suffix(&fs, &ms, lchar);
3584 } else
3585 mlfs_skipfix(&fs);
3586 break;
3587
3588 #endif
3589 #ifdef WMDSHOWCHAR
3590 case 'C':
3591 if (w_val(wp, WMDSHOWCHAR)
3592 && (bp != 0 && !is_empty_buf(bp))
3593 && (wp->w_dot.o < llength(wp->w_dot.l)
3594 || line_has_newline(wp->w_dot.l, bp))) {
3595 sprintf(temp, "%02X", char_at_mark(wp->w_dot));
3596 mlfs_prefix(&fs, &ms, lchar);
3597 ms = lsprintf(ms, "%s", temp);
3598 mlfs_suffix(&fs, &ms, lchar);
3599 } else {
3600 mlfs_skipfix(&fs);
3601 }
3602 break;
3603 #endif
3604 case 'P':
3605 ms = lsprintf(ms, "%d", percentage(wp));
3606 break;
3607
3608 case 'S':
3609 if (
3610 #ifdef WMDRULER
3611 !w_val(wp, WMDRULER) ||
3612 #endif
3613 ((bp == 0) || is_empty_buf(bp))) {
3614 mlfs_prefix(&fs, &ms, lchar);
3615 ms = lsprintf(ms, " %s ", rough_position(wp));
3616 mlfs_suffix(&fs, &ms, lchar);
3617 } else
3618 mlfs_skipfix(&fs);
3619 break;
3620 case L_CURL:
3621 save_fs = fs;
3622 while (*fs != EOS && *fs != R_CURL)
3623 fs++;
3624
3625 if (fs != save_fs) {
3626 int flag = clexec;
3627 char *save_execstr;
3628 TBUFF *tok = 0;
3629
3630 save_execstr = execstr;
3631 clexec = TRUE;
3632 execstr = temp;
3633
3634 strncpy0(temp, save_fs, (size_t) (fs + 1 - save_fs));
3635 execstr = get_token(execstr,
3636 &tok,
3637 eol_null,
3638 EOS,
3639 (int *) 0);
3640 vl_strncpy(temp, tokval(tb_values(tok)), sizeof(temp));
3641
3642 tb_free(&tok);
3643 execstr = save_execstr;
3644 clexec = flag;
3645 } else {
3646 *temp = EOS;
3647 }
3648 if (*fs != EOS)
3649 fs++;
3650 save_fs = fs;
3651 /*
3652 * Allow an optional <number><format> on the end of the
3653 * token, to reformat it. Don't bother reformatting if
3654 * it is just a 'd' added to make the string unambiguous.
3655 */
3656 fs = skip_cnumber(fs);
3657 if (isAlpha(*fs)) {
3658 if ((*fs != 'd' || fs != save_fs)) {
3659 char format[NSTRING];
3660
3661 format[0] = '%';
3662 strncpy0(format + 1, save_fs, (size_t) (fs + 2 - save_fs));
3663 if (strchr("dDxXo", *fs)) {
3664 int value = scan_int(temp);
3665 ms = lsprintf(ms, format, value);
3666 } else {
3667 ms = lsprintf(ms, format, temp);
3668 }
3669 } else {
3670 ms = lsprintf(ms, "%s", temp);
3671 }
3672 fs++;
3673 } else {
3674 ms = lsprintf(ms, "%s", temp);
3675 }
3676 break;
3677 default:
3678 *ms++ = '%';
3679 *ms++ = *(fs - 1);
3680 break;
3681 }
3682 }
3683 }
3684 *ms++ = EOS;
3685
3686 tb_bappend(result, left_ms, strlen(left_ms));
3687
3688 if (tb_values(*result) != 0) {
3689 have_cols = tb_columns(*result);
3690
3691 if ((have_cols < term.cols)
3692 && (right_cols = str_columns(right_ms)) != 0) {
3693 for (n = term.cols - have_cols - right_cols; n > 0; n--)
3694 tb_append(result, lchar);
3695 if ((n = term.cols - right_cols) < 0) {
3696 col = right_cols + n;
3697 n = -n;
3698 } else {
3699 col = right_cols;
3700 n = 0;
3701 }
3702 if ((col < term.cols)
3703 && (n < right_cols)) {
3704 right_offs = str_col2offs(wp, n, right_ms, (int) strlen(right_ms));
3705 if ((tb_columns(*result) + col) > term.cols) {
3706 tb_setlen(result,
3707 str_col2offs(wp, term.cols - col,
3708 tb_values(*result),
3709 (int) tb_length(*result)));
3710 have_cols = tb_columns(*result);
3711 for (n = term.cols - have_cols - right_cols; n > 0; n--)
3712 tb_append(result, lchar);
3713 }
3714 tb_bappend(result, right_ms + right_offs, (size_t) col);
3715 }
3716 }
3717 }
3718
3719 /* mark column 80 */
3720 if (tb_values(*result) != 0) {
3721 have_cols = tb_columns(*result);
3722
3723 if (need_eighty_column_indicator) {
3724 int left = -nu_width(wp);
3725 char *ss;
3726 #ifdef WMDLINEWRAP
3727 if (!w_val(wp, WMDLINEWRAP))
3728 #endif
3729 left += w_val(wp, WVAL_SIDEWAYS);
3730 n = term.cols + left;
3731 col = MARK_COL - left;
3732
3733 if ((n > MARK_COL) && (col >= 0)) {
3734 for (n = have_cols; n < col; n++)
3735 tb_append(result, lchar);
3736 right_offs = str_col2offs(wp, col,
3737 tb_values(*result),
3738 (int) tb_length(*result));
3739 if ((ss = tb_values(*result)) != 0
3740 && ss[right_offs] == lchar) {
3741 ss[right_offs] = '|';
3742 }
3743 }
3744 }
3745 }
3746 tb_append(result, EOS);
3747 returnVoid();
3748 }
3749 #endif
3750
3751 /*
3752 * Redisplay the mode line for the window pointed to by the "wp".
3753 * This is the only routine that has any idea of how the modeline is
3754 * formatted. You can change the modeline format by hacking at this
3755 * routine. Called by "update" any time there is a dirty window.
3756 */
3757 static void
update_modeline(WINDOW * wp)3758 update_modeline(WINDOW *wp)
3759 {
3760 #if OPT_MLFORMAT
3761 static TBUFF *result;
3762 #else
3763 static char lchar_pad[4];
3764 BUFFER *bp;
3765 char temp[MAX_FORMAT / 2];
3766 int col;
3767 char lchar[2];
3768 int right_cols;
3769 char left_ms[MAX_FORMAT];
3770 char *ms;
3771 char right_ms[MAX_FORMAT];
3772 int my_col;
3773 #endif
3774 int my_row;
3775
3776 TRACE((T_CALLED "update_modeline\n"));
3777 if (is_vtinit()) {
3778 term.cursorvis(FALSE);
3779
3780 my_row = mode_row(wp); /* Location. */
3781 #if OPT_VIDEO_ATTRS
3782 {
3783 VIDEO_ATTR attr;
3784 if (wp == curwp)
3785 attr = VAMLFOC;
3786 else
3787 attr = VAML;
3788 #if OPT_REVSTA
3789 #ifdef GVAL_MCOLOR
3790 attr = (VIDEO_ATTR) (attr | (global_g_val(GVAL_MCOLOR) & ~VASPCOL));
3791 #else
3792 attr |= VAREV;
3793 #endif
3794 #endif
3795 vscreen[my_row]->v_flag |= VFCHG;
3796 set_vattrs(my_row, 0, attr, (size_t) term.cols);
3797 }
3798 #else
3799 vscreen[my_row]->v_flag |= VFCHG | VFREQ | VFCOL; /* Redraw next time. */
3800 #endif
3801 #if OPT_COLOR
3802 ReqFcolor(vscreen[my_row]) = gfcolor;
3803 ReqBcolor(vscreen[my_row]) = gbcolor;
3804 #endif
3805 vtmove(my_row, 0); /* Seek to right line. */
3806
3807 #if OPT_MLFORMAT
3808 special_formatter(&result, modeline_format, wp);
3809 #if OPT_MULTIBYTE
3810 {
3811 char *from = tb_values(result);
3812 int n = (int) strlen(from);
3813 VTSET_LOOP(n > 0);
3814 }
3815 #else
3816 vtputsn(wp, tb_values(result), tb_length(result));
3817 #endif
3818 #else /* hard-coded format */
3819 bp = wp->w_bufp;
3820 if (bp == 0)
3821 bp = curbp;
3822 if (bp == 0)
3823 bp = bminip;
3824 if (wp == curwp) { /* current buffer shows as = */
3825 lchar[0] = '=';
3826 } else {
3827 #if OPT_REVSTA
3828 if (revexist)
3829 lchar[0] = ' ';
3830 else
3831 #endif
3832 lchar[0] = '-';
3833 }
3834 left_ms[0] = right_ms[0] = EOS;
3835 ms = left_ms;
3836 ms = lsprintf(ms, "%c%c%c %s ",
3837 lchar[0], modeline_show(wp, lchar[0]), lchar[0], bp->b_bname);
3838 if (modeline_modes(bp, &ms, FALSE))
3839 *ms++ = ' ';
3840 if (bp->b_fname != 0
3841 && (shorten_path(vl_strncpy(temp, bp->b_fname, sizeof(temp)), FALSE))
3842 && !eql_bname(bp, temp)) {
3843 if (is_internalname(temp)) {
3844 for (my_col = term.cols - (13 + strlen(temp));
3845 my_col > 0; my_col--)
3846 *ms++ = lchar[0];
3847 } else {
3848 ms = lsprintf(ms, "is");
3849 }
3850 ms = lsprintf(ms, " %s ", temp);
3851 }
3852 memset(lchar_pad, lchar[0], sizeof(lchar_pad) - 1);
3853 #ifdef WMDRULER
3854 if (w_val(wp, WMDRULER))
3855 (void) lsprintf(right_ms, " (%d,%d) %s",
3856 wp->w_ruler_line, wp->w_ruler_col, lchar_pad);
3857 else
3858 #endif
3859 (void) lsprintf(right_ms, " %s %s", rough_position(wp), lchar_pad);
3860
3861 *ms++ = EOS;
3862 right_cols = str_columns(right_ms);
3863 vtputsn(wp, left_ms, term.cols);
3864 for (my_col = term.cols - str_columns(left_ms) - right_cols;
3865 my_col > 0;
3866 my_col--)
3867 vtputc(wp, lchar, 1);
3868
3869 vtcol = term.cols - right_cols;
3870 if (vtcol < 0) {
3871 my_col = -vtcol;
3872 vtcol = 0;
3873 } else {
3874 my_col = 0;
3875 }
3876
3877 if (term.cols > vtcol)
3878 vtputsn(wp, right_ms + my_col, term.cols - vtcol);
3879
3880 col = -nu_width(wp);
3881 #ifdef WMDLINEWRAP
3882 if (!w_val(wp, WMDLINEWRAP))
3883 #endif
3884 col += w_val(wp, WVAL_SIDEWAYS);
3885 my_col = term.cols + col;
3886 col = MARK_COL - col;
3887
3888 if ((my_col > MARK_COL)
3889 && (col >= 0)
3890 && (vscreen[vtrow]->v_text[col] == lchar[0])) {
3891 vtcol = col;
3892 vtputc(wp, "|", 1);
3893 }
3894 #endif /* OPT_MLFORMAT */
3895 term.cursorvis(TRUE);
3896 TRACE2(("MODE %4d:%s\n",
3897 vtrow,
3898 visible_video_text(vscreen[vtrow]->v_text, vtcol)));
3899 }
3900 returnVoid();
3901 }
3902
3903 /*
3904 * Update all the mode lines.
3905 */
3906 void
upmode(void)3907 upmode(void)
3908 {
3909 WINDOW *wp;
3910
3911 for_each_window(wp) {
3912 wp->w_flag |= WFMODE;
3913 }
3914 }
3915
3916 /*
3917 * Check to see if the cursor is on in the window and re-frame it if needed or
3918 * wanted.
3919 */
3920 static void
reframe_cursor_position(WINDOW * wp)3921 reframe_cursor_position(WINDOW *wp)
3922 {
3923 LINE *dlp;
3924 LINE *lp;
3925 int i = 0;
3926 int rows;
3927 int founddot = FALSE; /* set to true iff we find dot */
3928 int tildecount;
3929
3930 /* if not a requested reframe, check for a needed one */
3931 if ((wp->w_flag & WFFORCE) == 0) {
3932 /* initial update in main.c may not set these first... */
3933 if (!valid_line_wp(wp->w_dot.l, wp)) {
3934 wp->w_dot.l = lforw(win_head(wp));
3935 wp->w_dot.o = 0;
3936 }
3937 if (!valid_line_wp(wp->w_line.l, wp)) {
3938 wp->w_line.l = wp->w_dot.l;
3939 wp->w_line.o = 0;
3940 }
3941 #if CAN_SCROLL
3942 /* loop from one line above the window to one line after */
3943 lp = lback(wp->w_line.l);
3944 i = -line_height(wp, lp);
3945 #else
3946 /* loop through the window */
3947 lp = wp->w_line.l;
3948 i = 0;
3949 #endif
3950 for_ever {
3951 /* if the line is in the window, no reframe */
3952 if (lp == wp->w_dot.l) {
3953 founddot = TRUE;
3954 #if CAN_SCROLL
3955 /* if not _quite_ in, we'll reframe gently */
3956 if (i < 0 || i >= wp->w_ntrows) {
3957 /* if the terminal can't help, then
3958 we're simply outside */
3959 if (term.scroll == nullterm_scroll)
3960 i = wp->w_force;
3961 break;
3962 }
3963 #endif
3964 #ifdef WMDLINEWRAP
3965 if (w_val(wp, WMDLINEWRAP)
3966 && i > 0
3967 && i + line_height(wp, lp) > wp->w_ntrows) {
3968 i = wp->w_ntrows;
3969 break;
3970 }
3971 #endif
3972 lp = wp->w_line.l;
3973 goto kill_tildes;
3974 }
3975
3976 /* if we are at the end of the file, reframe */
3977 if (i >= 0 && lp == win_head(wp))
3978 break;
3979
3980 /* on to the next line */
3981 if (i >= wp->w_ntrows) {
3982 i = 0; /* dot-not-found */
3983 break;
3984 }
3985 i += line_height(wp, lp);
3986 lp = lforw(lp);
3987 }
3988 }
3989 #if CAN_SCROLL
3990 if (i < 0) { /* we're just above the window */
3991 i = 1; /* put dot at first line */
3992 scrflags |= WFINS;
3993 } else if (founddot && (i >= wp->w_ntrows)) {
3994 /* we're just below the window */
3995 i = -1; /* put dot at last line */
3996 scrflags |= WFKILLS;
3997 } else /* put dot where requested */
3998 #endif
3999 i = wp->w_force; /* (is 0, unless reposition() was called) */
4000
4001 wp->w_flag |= WFMODE;
4002 wp->w_line.o = 0;
4003
4004 /* w_force specifies which line of the window dot should end up on */
4005 /* positive --> lines from the top */
4006 /* negative --> lines from the bottom */
4007 /* zero --> middle of window */
4008
4009 lp = wp->w_dot.l;
4010
4011 #ifdef WMDLINEWRAP
4012 /*
4013 * Center dot in middle of screen with line-wrapping
4014 */
4015 if (i == 0 && w_val(wp, WMDLINEWRAP)) {
4016 rows = (wp->w_ntrows - line_height(wp, lp) + 2) / 2;
4017 while (rows > 0) {
4018 dlp = lback(lp);
4019 if (dlp == win_head(wp))
4020 break;
4021 if ((rows -= line_height(wp, dlp)) < 0)
4022 break;
4023 lp = dlp;
4024 }
4025 } else
4026 #endif
4027 {
4028 rows = (i != 0)
4029 ? wp->w_ntrows
4030 : wp->w_ntrows / 2;
4031 while (rows > 0) {
4032 if ((i > 0)
4033 && (--i <= 0))
4034 break;
4035 dlp = lback(lp);
4036 if (dlp == win_head(wp))
4037 break;
4038 if ((rows -= line_height(wp, lp)) < 0)
4039 break;
4040 lp = dlp;
4041 }
4042 if (rows < line_height(wp, lp)
4043 && (lp != wp->w_dot.l)) {
4044 while (i++ < 0) {
4045 dlp = lforw(lp);
4046 if (dlp == win_head(wp))
4047 break;
4048 else
4049 lp = dlp;
4050 }
4051 }
4052 }
4053 kill_tildes:
4054 /* Eliminate as many tildes as possible from bottom */
4055 dlp = lp;
4056 rows = wp->w_ntrows;
4057 while (rows > 0 && (dlp != win_head(wp))) {
4058 rows -= line_height(wp, dlp);
4059 dlp = lforw(dlp);
4060 }
4061 dlp = lback(lp);
4062
4063 tildecount = (wp->w_ntrows * ntildes) / 100;
4064 if (tildecount == wp->w_ntrows)
4065 tildecount--;
4066
4067 while (dlp != win_head(wp)
4068 && (rows -= line_height(wp, dlp)) >= tildecount) {
4069 lp = dlp;
4070 dlp = lback(lp);
4071 }
4072
4073 /* and reset the current line-at-top-of-window */
4074 if (lp != win_head(wp) /* mouse click could be past end */
4075 &&lp != wp->w_line.l) { /* no need to set it if already there */
4076 wp->w_line.l = lp;
4077 wp->w_flag |= WFHARD;
4078 clr_typed_flags(wp->w_flag, USHORT, WFFORCE);
4079 }
4080 #ifdef WMDLINEWRAP
4081 /*
4082 * Ensure that dot will be visible, by adjusting the w_line.o value if
4083 * necessary. That's used to start the beginning of the first line in
4084 * a window "before" the start of the window.
4085 */
4086 if (w_val(wp, WMDLINEWRAP)
4087 && sameline(wp->w_line, wp->w_dot)) {
4088 int want = mark2col(wp, wp->w_dot) / term.cols;
4089 if (want + wp->w_line.o >= wp->w_ntrows) {
4090 wp->w_line.o = wp->w_ntrows - want - 1;
4091 wp->w_flag |= WFHARD;
4092 clr_typed_flags(wp->w_flag, USHORT, WFFORCE);
4093 } else if (want + wp->w_line.o < 0) {
4094 wp->w_line.o = -want;
4095 wp->w_flag |= WFHARD;
4096 clr_typed_flags(wp->w_flag, USHORT, WFFORCE);
4097 }
4098 } else if (!w_val(wp, WMDLINEWRAP)) {
4099 if (wp->w_line.o < 0) {
4100 wp->w_line.o = 0;
4101 wp->w_flag |= WFHARD;
4102 clr_typed_flags(wp->w_flag, USHORT, WFFORCE);
4103 }
4104 }
4105 #endif
4106 }
4107
4108 /*
4109 * De-extend any line that deserves it.
4110 */
4111 static void
de_extend_lines(void)4112 de_extend_lines(void)
4113 {
4114 WINDOW *wp;
4115 LINE *lp;
4116 int i;
4117
4118 for_each_visible_window(wp) {
4119 if ((lp = wp->w_line.l) == NULL) {
4120 /* this should not happen */
4121 mlforce("BUG: lost w_line for %s", wp->w_bufp->b_bname);
4122 lp = win_head(wp);
4123 wp->w_toprow = 0;
4124 }
4125 if (valid_line_wp(lp, wp)) {
4126 i = TopRow(wp);
4127
4128 while (i < mode_row(wp)) {
4129 if (i >= 0
4130 && (vscreen[i]->v_flag & VFEXT) != 0) {
4131 if ((wp != curwp)
4132 || (lp != wp->w_dot.l)
4133 || ((i != currow)
4134 && (curcol < col_limit(wp)))) {
4135 update_screen_line(wp, lp, i);
4136 vteeol();
4137 /* this line no longer is extended */
4138 vscreen[i]->v_flag &= ~VFEXT;
4139 }
4140 }
4141 i += line_height(wp, lp);
4142 lp = lforw(lp);
4143 }
4144 }
4145 }
4146 }
4147
4148 /*
4149 * Make sure that the display is right. This is a three part process. First,
4150 * scan through all of the windows looking for dirty ones. Check the framing,
4151 * and refresh the screen. Second, make sure that "currow" and "curcol" are
4152 * correct for the current window. Third, make the virtual and physical
4153 * screens the same.
4154 */
4155 int
update(int force)4156 update(int force /* force update past type ahead? */ )
4157 {
4158 WINDOW *wp;
4159 int origrow, origcol;
4160 int screenrow, screencol;
4161 int updated = FALSE;
4162
4163 TRACE((T_CALLED "update(%d)\n", force));
4164
4165 /* Get row and column prior to doing the update in case we are
4166 * reading the message line.
4167 */
4168 origrow = ttrow;
4169 origcol = ttcol;
4170
4171 if (clhide || (valid_window(curwp) && !is_visible_window(curwp)))
4172 returnCode(TRUE);
4173
4174 /*
4175 * If not initialized, just return.
4176 */
4177 if (!valid_buffer(curbp)
4178 || !vscreen
4179 || !valid_window(curwp))
4180 returnCode(FALSE);
4181
4182 /*
4183 * Don't try to update if we got called via a read-hook on a window
4184 * that isn't complete.
4185 */
4186 if (!valid_buffer(curwp->w_bufp)
4187 || curwp->w_bufp->b_nwnd == 0
4188 || curwp->w_ntrows < 1)
4189 returnCode(FALSE);
4190 if (TypeAhead(force))
4191 returnCode(SORTOFTRUE);
4192
4193 /* don't display during keystroke replay */
4194 if (!force && kbd_replaying(TRUE) && (get_recorded_char(FALSE) != -1))
4195 returnCode(SORTOFTRUE);
4196
4197 beginDisplay();
4198
4199 preset_lmap0();
4200 #if OPT_TITLE
4201 /*
4202 * Only update the title when we have nothing better to do. The
4203 * auto_set_title logic is otherwise likely to set the title frequently if
4204 * [Buffer List] is visible.
4205 */
4206 if (tb_values(request_title) != 0
4207 && (tb_values(current_title) == 0
4208 || strcmp(tb_values(current_title), tb_values(request_title)))) {
4209 tb_copy(¤t_title, request_title);
4210 term.set_title(tb_values(request_title));
4211 }
4212 #endif
4213
4214 /* propagate mode line changes to all instances of
4215 a buffer displayed in more than one window */
4216 for_each_visible_window(wp) {
4217 if (wp->w_flag & WFMODE) {
4218 if (wp->w_bufp->b_nwnd > 1) {
4219 /* make sure all previous windows have this */
4220 WINDOW *owp;
4221 for_each_visible_window(owp) {
4222 if (owp->w_bufp == wp->w_bufp)
4223 owp->w_flag |= WFMODE;
4224 }
4225 }
4226 }
4227 #ifdef WMDLINEWRAP
4228 /* Make sure that movements in very long wrapped lines
4229 get updated properly. */
4230 if (w_val(wp, WMDLINEWRAP)
4231 && line_height(wp, wp->w_dot.l) > wp->w_ntrows)
4232 wp->w_flag |= WFMOVE;
4233 #endif
4234 #if OPT_CACHE_VCOL
4235 if (wp->w_flag & (WFEDIT | WFHARD | WFMODE | WFKILLS | WFINS))
4236 wp->w_traits.w_left_dot = nullmark;
4237 #endif
4238 }
4239 #ifdef WMDSHOWVARS
4240 if (curwp != 0
4241 && w_val(curwp, WMDSHOWVARS)) {
4242 updatelistvariables();
4243 }
4244 #endif
4245
4246 /* look for scratch-buffers that should be recomputed. */
4247 #if OPT_UPBUFF
4248 for_each_visible_window(wp) {
4249 if (wp->w_flag) {
4250 reframe_cursor_position(wp); /* check the framing */
4251 }
4252 if (b_is_obsolete(wp->w_bufp))
4253 recompute_buffer(wp->w_bufp);
4254 }
4255 #endif
4256
4257 /* look for windows that need the ruler updated */
4258 #ifdef WMDRULER
4259 for_each_visible_window(wp) {
4260 #ifdef WMDSHOWCHAR
4261 if (w_val(wp, WMDSHOWCHAR)) {
4262 wp->w_flag |= WFMODE;
4263 update_char_classes();
4264 }
4265 #endif
4266 if (w_val(wp, WMDRULER)) {
4267 int line = line_no(wp->w_bufp, wp->w_dot.l);
4268 int col = dot_to_vcol(wp) + 1;
4269
4270 if (line != wp->w_ruler_line
4271 || col != wp->w_ruler_col) {
4272 wp->w_ruler_line = line;
4273 wp->w_ruler_col = col;
4274 wp->w_flag |= WFMODE;
4275 }
4276 } else if (wp->w_flag & WFSTAT) {
4277 wp->w_flag |= WFMODE;
4278 }
4279 clr_typed_flags(wp->w_flag, USHORT, WFSTAT);
4280 }
4281 #endif
4282
4283 do {
4284 /* update any windows that need refreshing */
4285 for_each_visible_window(wp) {
4286 if (wp->w_flag) {
4287 if ((wp->w_flag & ~(WFMOVE)) && !updated++)
4288 term.cursorvis(FALSE);
4289 /* if the window has changed, service it */
4290 if (wp->w_flag & (WFKILLS | WFINS)) {
4291 scrflags |= (wp->w_flag & (WFINS | WFKILLS));
4292 clr_typed_flags(wp->w_flag, USHORT, WFKILLS | WFINS);
4293 }
4294 if ((wp->w_flag & ~(WFMODE)) == WFEDIT) {
4295 update_oneline(wp); /* update EDITed line */
4296 } else if (wp->w_flag & ~(WFMOVE | WFMODE)) {
4297 update_all(wp); /* update all lines */
4298 }
4299 #if OPT_SCROLLBARS
4300 if (wp->w_flag & (WFHARD | WFMOVE | WFSBAR))
4301 gui_update_scrollbar(wp);
4302 #endif /* OPT_SCROLLBARS */
4303
4304 #if OPT_VIDEO_ATTRS
4305 if (wp->w_flag & (WFHARD | WFEDIT))
4306 update_window_attrs(wp);
4307 #endif
4308 if (scrflags || (wp->w_flag & (WFMODE | WFCOLR)))
4309 update_modeline(wp); /* update modeline */
4310 wp->w_flag = 0;
4311 wp->w_force = 0;
4312 }
4313 }
4314
4315 /* Recalculate the current hardware cursor location. If true, we've
4316 * done a horizontal scroll.
4317 */
4318 } while (update_cursor_position(&screenrow, &screencol));
4319
4320 /* check for lines to de-extend */
4321 de_extend_lines();
4322
4323 /* if screen is garbage, re-plot it */
4324 if (sgarbf || need_update)
4325 update_garbaged_screen();
4326
4327 /* update the virtual screen to the physical screen */
4328 update_physical_screen(force);
4329
4330 /* update the cursor and flush the buffers */
4331 if (reading_msg_line)
4332 movecursor(origrow, origcol);
4333 else
4334 movecursor(screenrow, screencol);
4335
4336 if (updated)
4337 term.cursorvis(TRUE);
4338 #if OPT_ICURSOR
4339 if (curwp != 0) {
4340 static int old_insertmode = -1;
4341
4342 if (insertmode != old_insertmode) {
4343 old_insertmode = insertmode;
4344 term.icursor(insertmode);
4345 }
4346 }
4347 #endif
4348 term.flush();
4349 endofDisplay();
4350 i_displayed = TRUE;
4351
4352 while (allow_working_msg() && (chg_width && chg_height))
4353 newscreensize(chg_height, chg_width);
4354 returnCode(TRUE);
4355 }
4356
4357 /*
4358 * Highlight the requested portion of the screen. We're mucking with the video
4359 * attributes on the line here, so this is NOT good code - it would be better
4360 * if there was an individual colour attribute per character, rather than per
4361 * row, but I didn't write the original code. Anyway, hilite is called only
4362 * once so far, so it's not that big a deal.
4363 *
4364 * row - row to start highlighting
4365 * colfrom - column to start highlighting
4366 * colto - column to end highlighting
4367 * on - start highlighting
4368 */
4369 void
hilite(int row,int colfrom,int colto,int on)4370 hilite(int row, int colfrom, int colto, int on)
4371 {
4372 #if !OPT_VIDEO_ATTRS
4373 VIDEO *vp1 = vscreen[row];
4374 #endif
4375 #ifdef WMDLINEWRAP
4376 WINDOW *wp = row2window(row);
4377 if (w_val(wp, WMDLINEWRAP)) {
4378 if (colfrom < 0)
4379 colfrom = 0;
4380 if (colfrom > term.cols) {
4381 do {
4382 row++;
4383 colfrom -= term.cols;
4384 colto -= term.cols;
4385 hilite(row, colfrom, colto, on);
4386 } while (colto > term.cols);
4387 return;
4388 }
4389 }
4390 #endif
4391 if (row < term.rows - 1 && (colfrom >= 0 || colto <= term.cols)) {
4392 if (colfrom < 0)
4393 colfrom = 0;
4394 if (colto > term.cols)
4395 colto = term.cols;
4396 #if OPT_VIDEO_ATTRS
4397 if (on) {
4398 int col;
4399 for (col = colfrom; col < colto; col++)
4400 vscreen[row]->v_attrs[col] |= VAREV;
4401 } else {
4402 int col;
4403 for (col = colfrom; col < colto; col++) {
4404 clr_typed_flags(vscreen[row]->v_attrs[col], VIDEO_ATTR, VAREV);
4405 }
4406 }
4407 vscreen[row]->v_flag |= VFCHG;
4408 update_line(row, 0, term.cols);
4409 #else /* OPT_VIDEO_ATTRS */
4410 if (on) {
4411 vp1->v_flag |= VFREQ;
4412 } else {
4413 vp1->v_flag &= ~VFREQ;
4414 }
4415 update_line(row, colfrom, colto);
4416 #endif /* OPT_VIDEO_ATTRS */
4417 }
4418 }
4419
4420 /*
4421 * Send a command to the terminal to move the hardware cursor to row "row"
4422 * and column "col". The row and column arguments are origin 0. Optimize out
4423 * random calls. Update "ttrow" and "ttcol".
4424 */
4425 void
movecursor(int row,int col)4426 movecursor(int row, int col)
4427 {
4428 beginDisplay();
4429 if ((row != ttrow || col != ttcol)
4430 && (row >= 0 && row < term.rows)
4431 && (col >= 0 && col < term.cols)) {
4432 ttrow = row;
4433 ttcol = col;
4434 term.curmove(row, col);
4435 }
4436 endofDisplay();
4437 }
4438
4439 void
bottomleft(void)4440 bottomleft(void)
4441 {
4442 movecursor(term.rows - 1, 0);
4443 }
4444
4445 /*
4446 * Erase the message line. This is a special routine because the message line
4447 * is not considered to be part of the virtual screen. It always works
4448 * immediately; the terminal buffer is flushed via a call to the flusher.
4449 */
4450 void
mlerase(void)4451 mlerase(void)
4452 {
4453 if (!clhide) {
4454 beginDisplay();
4455 kbd_erase_to_end(0);
4456 kbd_flush();
4457 endofDisplay();
4458 }
4459 }
4460
4461 int
mlsavec(int c)4462 mlsavec(int c)
4463 {
4464 tb_append(&mlsave, c);
4465 return TRUE;
4466 }
4467
4468 /*
4469 * Do the real message-line work. Keep track of the physical cursor position.
4470 * A small class of printf like format items is handled. Set the "message
4471 * line" flag TRUE.
4472 */
4473 static void
mlmsg(const char * fmt,va_list app2)4474 mlmsg(const char *fmt, va_list app2)
4475 {
4476 static int recur;
4477
4478 va_list app;
4479 int end_at;
4480 #if DISP_NTWIN
4481 int cursor_state = 0;
4482 #endif
4483 int do_crlf = (strchr(fmt, '\n') != 0
4484 || strchr(fmt, '\r') != 0);
4485
4486 if (no_minimsgs) {
4487 kbd_alarm();
4488 return;
4489 }
4490 if (quiet)
4491 return;
4492
4493 #if DISP_NTWIN
4494 if (recur == 0) {
4495 /*
4496 * Winvile internally manages its own cursor and this usually works
4497 * fine. However, if the user activates functionality that triggers
4498 * windows message(s) (e.g., invoke the Win32 common open dialog) and
4499 * if this functionality writes message line status text as a side
4500 * effect, then winvile may inadvertently enable its cursor during the
4501 * display update. If this happens, the GDI drops a cursor glyph in
4502 * the message line. Fix this problem by forcing the winvile cursor
4503 * off now.
4504 */
4505 cursor_state = winvile_cursor_state(FALSE, FALSE);
4506 }
4507 #endif
4508 if (recur++) {
4509 /*EMPTY */ ;
4510 } else if (sgarbf) {
4511 /* then we'll lose the message on the next update(),
4512 * so save it now */
4513 tb_init(&mlsave, EOS);
4514 #if OPT_POPUP_MSGS
4515 if (global_g_val(GMDPOPUP_MSGS) || !valid_window(curwp)) {
4516 TRACE(("mlmsg popup_msgs #1 for '%s'\n", fmt));
4517 popup_msgs();
4518 msg_putc('\n');
4519 dfoutfn = msg_putc;
4520 } else
4521 #endif
4522 dfoutfn = mlsavec;
4523
4524 begin_va_copy(app, app2);
4525 dofmt(fmt, app);
4526 end_va_copy(app);
4527 } else {
4528 beginDisplay();
4529
4530 kbd_expand = -1;
4531 #if OPT_POPUP_MSGS
4532 if (global_g_val(GMDPOPUP_MSGS)) {
4533 TRACE(("mlmsg popup_msgs #2 for '%s'\n", fmt));
4534 popup_msgs();
4535 if (tb_length(mlsave) == 0) {
4536 msg_putc('\n');
4537 dfoutfn = msg_putc;
4538 } else {
4539 dfoutfn = kbd_putc;
4540 }
4541 } else
4542 #endif
4543 dfoutfn = kbd_putc;
4544
4545 if (*fmt != '\n') {
4546
4547 kbd_erase_to_end(0);
4548
4549 begin_va_copy(app, app2);
4550 dofmt(fmt, app);
4551 end_va_copy(app);
4552
4553 kbd_expand = 0;
4554
4555 /* if we can, erase to the end of screen */
4556 end_at = wminip->w_dot.o;
4557 kbd_erase_to_end(end_at);
4558 tb_init(&mlsave, EOS);
4559 kbd_flush();
4560 }
4561 if (do_crlf) {
4562 term.openup();
4563 }
4564 endofDisplay();
4565 }
4566 recur--;
4567 #if DISP_NTWIN
4568 if (recur == 0) {
4569
4570 /* restore previous cursor state if it was ON. */
4571
4572 if (cursor_state)
4573 winvile_cursor_state(TRUE, TRUE);
4574 }
4575 #endif
4576 }
4577
4578 /*
4579 * Format a string onto the message line, but only if it's appropriate.
4580 * Keyboard macro replay, "dot" command replay, command buffer execution,
4581 * "terse" mode, and the user-accessible "discmd" state variable can all
4582 * suppress this.
4583 */
4584 /* VARARGS1 */
4585 void
mlwrite(const char * fmt,...)4586 mlwrite(const char *fmt,...)
4587 {
4588 if (fmt != 0) {
4589 va_list ap;
4590 if (global_b_val(MDTERSE) || kbd_replaying(FALSE) || !vl_msgs) {
4591 if (!clhide)
4592 bottomleft();
4593 return;
4594 }
4595 va_start(ap, fmt);
4596 mlmsg(fmt, ap);
4597 va_end(ap);
4598 }
4599 }
4600
4601 /*
4602 * Unconditionally format a string out onto the message line.
4603 */
4604 /* VARARGS1 */
4605 void
mlforce(const char * fmt,...)4606 mlforce(const char *fmt,...)
4607 {
4608 if (fmt != 0) {
4609 va_list ap;
4610 va_start(ap, fmt);
4611 mlmsg(fmt, ap);
4612 va_end(ap);
4613 }
4614 }
4615
4616 /* VARARGS1 */
4617 void
mlprompt(const char * fmt,...)4618 mlprompt(const char *fmt,...)
4619 {
4620 if (fmt != 0) {
4621 va_list ap;
4622 int osgarbf = sgarbf;
4623 if (!vl_msgs) {
4624 bottomleft();
4625 return;
4626 }
4627 sgarbf = FALSE;
4628 va_start(ap, fmt);
4629 mlmsg(fmt, ap);
4630 va_end(ap);
4631 sgarbf = osgarbf;
4632 }
4633 }
4634
4635 /* VARARGS */
4636 void
dbgwrite(const char * fmt,...)4637 dbgwrite(const char *fmt,...)
4638 {
4639 if (fmt != 0) {
4640 char temp[80];
4641
4642 va_list ap; /* ptr to current data field */
4643 va_start(ap, fmt);
4644 lsprintf(temp, "[press ^G to continue] %s", fmt);
4645 mlmsg(temp, ap);
4646 va_end(ap);
4647 beginDisplay();
4648 while (term.getch() != '\007') {
4649 ;
4650 }
4651 endofDisplay();
4652 }
4653 }
4654
4655 /*
4656 * Do the equivalent of 'perror()' on the message line
4657 */
4658 void
mlerror(const char * str)4659 mlerror(const char *str)
4660 {
4661 int save_err = errno;
4662 const char *t = 0;
4663 #ifdef HAVE_STRERROR
4664 #if SYS_VMS
4665 if (save_err == EVMSERR)
4666 t = strerror(save_err, vaxc$errno);
4667 else
4668 #endif /* SYS_VMS */
4669 if (save_err > 0)
4670 t = strerror(save_err);
4671 #else
4672 #ifdef HAVE_SYS_ERRLIST
4673 if (save_err > 0 && save_err < sys_nerr)
4674 t = sys_errlist[save_err];
4675 #endif /* HAVE_SYS_ERRLIST */
4676 #endif /* HAVE_STRERROR */
4677 if (t != 0) {
4678 /* Borland's strerror() returns newlines on the end of the strings */
4679 static TBUFF *tt;
4680 if (tb_scopy(&tt, t) != 0) {
4681 t = mktrimmed(tb_values(tt));
4682 }
4683 mlwarn("[Error %s: %s]", str, t);
4684 } else {
4685 mlwarn("[Error %s: unknown system error %d]", str, save_err);
4686 }
4687 }
4688
4689 /*
4690 * Emit a warning message (with alarm)
4691 */
4692 /* VARARGS1 */
4693 void
mlwarn(const char * fmt,...)4694 mlwarn(const char *fmt,...)
4695 {
4696 va_list ap;
4697 va_start(ap, fmt);
4698 mlmsg(fmt, ap);
4699 va_end(ap);
4700 kbd_alarm();
4701 }
4702
4703 /*
4704 * Local sprintf -- similar to standard libc, but
4705 * returns pointer to null character at end of buffer, so it can
4706 * be called repeatedly, as in:
4707 * cp = lsprintf(cp, fmt, args...);
4708 *
4709 */
4710
4711 static char *lsp;
4712
4713 static int
lspputc(int c)4714 lspputc(int c)
4715 {
4716 *lsp++ = (char) c;
4717 return TRUE;
4718 }
4719
4720 /* VARARGS1 */
4721 char *
lsprintf(char * buf,const char * fmt,...)4722 lsprintf(char *buf, const char *fmt,...)
4723 {
4724 if ((lsp = buf) != 0) {
4725 va_list ap;
4726 va_start(ap, fmt);
4727
4728 dfoutfn = lspputc;
4729
4730 dofmt(fmt, ap);
4731 va_end(ap);
4732
4733 *lsp = EOS;
4734 }
4735 return lsp;
4736 }
4737
4738 int
format_int(char * buf,UINT number,UINT radix)4739 format_int(char *buf, UINT number, UINT radix)
4740 {
4741 if ((lsp = buf) != 0) {
4742 dfputi(lspputc, number, radix);
4743 *lsp = EOS;
4744 }
4745 return (int) (lsp - buf);
4746 }
4747
4748 /*
4749 * Buffer printf -- like regular printf, but puts characters
4750 * into the BUFFER.
4751 */
4752 int
bputc(int c)4753 bputc(int c)
4754 {
4755 int status;
4756
4757 if (is_record_sep(curbp, c)) {
4758 status = lnewline();
4759 } else {
4760 status = lins_bytes(1, c);
4761 }
4762
4763 return status;
4764 }
4765
4766 int
bputsn(const char * src,int len)4767 bputsn(const char *src, int len)
4768 {
4769 int rc = TRUE;
4770
4771 if (len < 0) {
4772 rc = bputsn(src, (int) strlen(src));
4773 } else {
4774 while ((len > 0) && (rc == TRUE)) {
4775 int n;
4776 int chunk;
4777 int rs = use_record_sep(curbp);
4778
4779 if (*src == rs) {
4780 rc = lnewline();
4781 chunk = 1;
4782 } else {
4783 for (chunk = 0; chunk < len; ++chunk) {
4784 if (src[chunk] == rs)
4785 break;
4786 }
4787 if ((rc = lins_bytes(chunk, ' ')) == TRUE) {
4788 LINE *lp = DOT.l;
4789 int offs = DOT.o - chunk;
4790 for (n = 0; n < chunk; ++n) {
4791 lvalue(lp)[n + offs] = src[n];
4792 }
4793 }
4794 }
4795 len -= chunk;
4796 src += chunk;
4797 }
4798 }
4799 return rc;
4800 }
4801
4802 #if OPT_EXTRA_COLOR
4803 int
bputsn_attr(const char * src,int len,int attr)4804 bputsn_attr(const char *src, int len, int attr)
4805 {
4806 int rc;
4807
4808 if (attr) {
4809 REGIONSHAPE save_regn = regionshape;
4810 VIDEO_ATTR save_attr = videoattribute;
4811
4812 regionshape = rgn_EXACT;
4813 videoattribute = (VIDEO_ATTR) attr;
4814
4815 if (len < 0)
4816 len = (int) strlen(src);
4817 rc = bputsn(src, len);
4818
4819 /*
4820 * We cannot simply save MK before writing, since writing to an empty
4821 * buffer for instance may split the original line, leaving MK pointing
4822 * to the fragment _after_ the newline. But limiting this call to
4823 * strings without embedded newlines, we can reliably compute MK based
4824 * on the string length.
4825 */
4826 MK = DOT;
4827 MK.o -= len;
4828 (void) attributeregion();
4829
4830 regionshape = save_regn;
4831 videoattribute = save_attr;
4832 MK = DOT;
4833 } else {
4834 rc = bputsn(src, len);
4835 }
4836 return rc;
4837 }
4838
4839 int
bputsn_xcolor(const char * src,int len,XCOLOR_CODES code)4840 bputsn_xcolor(const char *src, int len, XCOLOR_CODES code)
4841 {
4842 int rc;
4843 int *valuep = lookup_extra_color(code);
4844
4845 if (isEmpty(valuep)) {
4846 rc = bputsn(src, len);
4847 } else {
4848 rc = bputsn_attr(src, len, *valuep);
4849 }
4850 return rc;
4851 }
4852 #endif /* OPT_EXTRA_COLOR */
4853
4854 void
bpadc(int c,int count)4855 bpadc(int c, int count)
4856 {
4857 while (count-- > 0)
4858 bputc(c);
4859 }
4860
4861 /* printf into curbp, at DOT */
4862 /* VARARGS */
4863 void
bprintf(const char * fmt,...)4864 bprintf(const char *fmt,...)
4865 {
4866 va_list ap;
4867
4868 dfoutfn = bputc;
4869
4870 va_start(ap, fmt);
4871 dofmt(fmt, ap);
4872 va_end(ap);
4873 }
4874
4875 /*
4876 * Print into the given internal buffer, returning a pointer to the line
4877 * written.
4878 */
4879 LINE *
b2vprintf(BUFFER * bp,const char * fmt,va_list app2)4880 b2vprintf(BUFFER *bp, const char *fmt, va_list app2)
4881 {
4882 va_list app;
4883 LINE *result = 0;
4884 WINDOW *save_wp;
4885 BUFFER *save_bp = curbp;
4886 W_TRAITS save_w_traits;
4887 OutFunc save_outfn;
4888
4889 if ((save_wp = push_fake_win(bp)) != 0) {
4890 int save_curgoal = curgoal;
4891
4892 save_outfn = dfoutfn;
4893 dfoutfn = bputc;
4894
4895 save_w_traits = curwp->w_traits;
4896
4897 (void) gotoeob(FALSE, 1);
4898 (void) gotoeol(FALSE, 1);
4899
4900 begin_va_copy(app, app2);
4901 dofmt(fmt, app);
4902 end_va_copy(app);
4903
4904 result = lback(DOT.l);
4905
4906 b_clr_changed(bp);
4907 curwp->w_traits = save_w_traits;
4908
4909 pop_fake_win(save_wp, save_bp);
4910 dfoutfn = save_outfn;
4911 curgoal = save_curgoal;
4912 }
4913 return result;
4914 }
4915
4916 /*
4917 * Write text (not necessarily a whole line) to the given buffer.
4918 */
4919 LINE *
b2printf(BUFFER * bp,const char * fmt,...)4920 b2printf(BUFFER *bp, const char *fmt,...)
4921 {
4922 LINE *result;
4923 va_list ap;
4924
4925 va_start(ap, fmt);
4926 result = b2vprintf(bp, fmt, ap);
4927 va_end(ap);
4928 return result;
4929 }
4930
4931 #if OPT_EVAL || OPT_DEBUGMACROS
4932 /* printf into [Trace] */
4933 void
tprintf(const char * fmt,...)4934 tprintf(const char *fmt,...)
4935 {
4936 static int nested;
4937
4938 BUFFER *bp;
4939 va_list ap;
4940 #if OPT_TRACE
4941 LINE *line;
4942 #endif
4943
4944 if (!nested
4945 && (bp = make_ro_bp(TRACE_BufName, BFINVS)) != 0) {
4946 nested = TRUE;
4947
4948 va_start(ap, fmt);
4949 #if OPT_TRACE
4950 line =
4951 #endif
4952 b2vprintf(bp, fmt, ap);
4953 va_end(ap);
4954
4955 #if OPT_TRACE
4956 if (line)
4957 TRACE(("tprintf {%.*s}\n", llength(line), lvalue(line)));
4958 #endif
4959
4960 nested = FALSE;
4961 }
4962 }
4963 #endif
4964
4965 #if defined(SIGWINCH) && ! DISP_X11
4966 /* ARGSUSED */
4967 SIGT
sizesignal(int ACTUAL_SIG_ARGS GCC_UNUSED)4968 sizesignal(int ACTUAL_SIG_ARGS GCC_UNUSED)
4969 {
4970 int w, h;
4971 int old_errno = errno;
4972
4973 getscreensize(&w, &h);
4974
4975 if ((h > 1 && h != term.rows) || (w > 1 && w != term.cols))
4976 newscreensize(h, w);
4977
4978 setup_handler(SIGWINCH, sizesignal);
4979 errno = old_errno;
4980 SIGRET;
4981 }
4982 #endif
4983
4984 void
newscreensize(int h,int w)4985 newscreensize(int h, int w)
4986 {
4987 TRACE(("newscreensize(h=%d, w=%d)\n", h, w));
4988 /* do the change later */
4989 if (im_displaying
4990 #if OPT_WORKING
4991 || !i_displayed
4992 #endif
4993 ) {
4994 chg_width = w;
4995 chg_height = h;
4996 } else {
4997 beginDisplay();
4998 chg_width = chg_height = 0;
4999 if ((h > term.maxrows) || (w > term.maxcols)) {
5000 int old_r, old_c;
5001 old_r = term.maxrows;
5002 old_c = term.maxcols;
5003 term.maxrows = h;
5004 term.maxcols = w;
5005 if (!vtinit()) { /* allocation failure */
5006 term.maxrows = old_r;
5007 term.maxcols = old_c;
5008 endofDisplay();
5009 return;
5010 }
5011 }
5012 endofDisplay();
5013
5014 if (newlength(TRUE, h) && newwidth(TRUE, w))
5015 (void) update(TRUE);
5016 }
5017 }
5018
5019 #if OPT_WORKING
5020 /*
5021 * Start the timer that controls the "working..." message.
5022 */
5023 static void
start_working(void)5024 start_working(void)
5025 {
5026 TRACE2(("start_working\n"));
5027 setup_handler(SIGALRM, imworking);
5028 (void) alarm(1);
5029 im_timing = TRUE;
5030 }
5031
5032 /*
5033 * When we stop the timer, we should cleanup the "working..." message.
5034 */
5035 static void
stop_working(void)5036 stop_working(void)
5037 {
5038 TRACE2(("stop_working\n"));
5039 if (mpresf) { /* erase leftover working-message */
5040 int save_row = ttrow;
5041 int save_col = ttcol;
5042 kbd_overlay(0);
5043 kbd_flush();
5044 movecursor(save_row, save_col);
5045 term.flush();
5046 }
5047 }
5048
5049 /*
5050 * Displays alternate
5051 * "working..." and
5052 * "...working"
5053 * at the end of the message line if it has been at least a second since
5054 * displaying anything or waiting for keyboard input. The cur_working and
5055 * max_working values are used in 'slowreadf()' to show the progress of reading
5056 * large files.
5057 */
5058
5059 /*ARGSUSED*/
5060 SIGT
imworking(int ACTUAL_SIG_ARGS GCC_UNUSED)5061 imworking(int ACTUAL_SIG_ARGS GCC_UNUSED)
5062 {
5063 static int flip;
5064 static int skip;
5065
5066 TRACE2(("imworking(%d)\n", signo));
5067 /* (if GMDWORKING is _not_ set, or MDTERSE is set, we're allowed to erase,
5068 * but not to write. If we do erase, we don't reschedule the alarm, since
5069 * setting the mode will call us again to start things up)
5070 */
5071 if (allow_working_msg()) {
5072 TRACE2(("...allow_working_msg\n"));
5073 if (im_waiting(-1)) {
5074 TRACE2(("...im_waiting(-1)\n"));
5075 im_timing = FALSE;
5076 stop_working();
5077 } else if (ShowWorking()) {
5078 TRACE2(("...ShowWorking()\n"));
5079 if (skip) {
5080 TRACE2(("...skipped()\n"));
5081 skip = FALSE;
5082 } else {
5083 #if DISP_X11
5084 x_working();
5085 #else
5086 static const char *const msg[] =
5087 {"working", "..."};
5088 char result[20];
5089
5090 TRACE2(("...FINALLY!()\n"));
5091 result[0] = EOS;
5092 if (cur_working != 0
5093 && cur_working != old_working) {
5094 char temp[20];
5095 int len = cur_working > 999999L ? 10 : 6;
5096
5097 old_working = cur_working;
5098 vl_strncat(result,
5099 right_num(temp, len, (long) cur_working),
5100 sizeof(result));
5101 if (len == 10) {
5102 /*EMPTY */ ;
5103 } else if (max_working != 0) {
5104 vl_strncat(result, " ", sizeof(result));
5105 vl_strncat(result,
5106 right_num(temp, 2,
5107 (long) ((100 * cur_working)
5108 / max_working)),
5109 sizeof(result));
5110 vl_strncat(result, "%", sizeof(result));
5111 } else {
5112 vl_strncat(result, " ...", sizeof(result));
5113 }
5114 } else {
5115 vl_strncat(result, msg[flip], sizeof(result));
5116 vl_strncat(result, msg[!flip], sizeof(result));
5117 }
5118 kbd_overlay(result);
5119 kbd_flush();
5120 #endif
5121 }
5122 start_working();
5123 flip = !flip;
5124 } else {
5125 TRACE2(("...NOT ShowWorking()\n"));
5126 stop_working();
5127 skip = TRUE;
5128 }
5129 } else {
5130 TRACE2(("... NOT allow_working_msg(%d/%d/%d)%s ShowWorking\n",
5131 vile_is_busy, im_displaying, !i_displayed,
5132 ShowWorking()? "" : " NOT"));
5133 if (ShowWorking()) { /* keep the timer running */
5134 start_working();
5135 flip = !flip;
5136 }
5137 }
5138 }
5139 #endif /* OPT_WORKING */
5140
5141 /*
5142 * Returns true if we should/could show a "working..." message or other busy
5143 * indicator, assuming that 'working' mode is set.
5144 */
5145 int
allow_working_msg(void)5146 allow_working_msg(void)
5147 {
5148 return !(vile_is_busy || im_displaying || !i_displayed);
5149 }
5150
5151 /*
5152 * Maintain a flag that records whether we're waiting for keyboard input. As a
5153 * side-effect, restart the 'working' timer if we see that it's been made
5154 * inactive while we were waiting.
5155 */
5156 int
im_waiting(int flag)5157 im_waiting(int flag)
5158 {
5159 static int waiting;
5160 if (flag >= 0) { /* TRUE or FALSE set, negative used to query */
5161 waiting = flag;
5162 #if OPT_WORKING
5163 if (!waiting && !im_timing && ShowWorking())
5164 start_working();
5165 #endif
5166 }
5167 return waiting;
5168 }
5169
5170 #if OPT_PSCREEN
5171 /* Most of the code in this section is for making the message line work
5172 * right...it shouldn't be called to display the rest of the screen.
5173 */
5174 static int psc_row;
5175 static int psc_col;
5176
5177 #define SWAP_INT(x,y) \
5178 do { (x) = (x)+(y); (y) = (x)-(y); (x) = (x)-(y); } one_time
5179 #define SWAP_VT_PSC \
5180 do { SWAP_INT(vtcol, psc_col); SWAP_INT(vtrow, psc_row); } one_time
5181
5182 OUTC_DCL
psc_putchar(int c)5183 psc_putchar(int c)
5184 {
5185 if (c == '\b') {
5186 if (psc_col > 0)
5187 psc_col--;
5188 } else {
5189 char temp[2];
5190
5191 SWAP_VT_PSC;
5192
5193 temp[0] = (char) c;
5194 vtputc(wminip, temp, 1);
5195 vscreen[vtrow]->v_flag |= VFCHG;
5196
5197 SWAP_VT_PSC;
5198 }
5199 OUTC_RET c;
5200 }
5201
5202 void
psc_flush(void)5203 psc_flush(void)
5204 {
5205 update_line(term.rows - 1, 0, term.cols);
5206 term.pflush();
5207 }
5208
5209 void
psc_move(int row,int col)5210 psc_move(int row, int col)
5211 {
5212 psc_row = row;
5213 psc_col = col;
5214 }
5215
5216 void
psc_eeol(void)5217 psc_eeol(void)
5218 {
5219 if (ttrow >= 0 && ttrow < term.rows && ttcol >= 0) {
5220 VIDEO_ATTR *vp = &vscreen[ttrow]->v_attrs[ttcol];
5221 VIDEO_TEXT *cp = &vscreen[ttrow]->v_text[ttcol];
5222 VIDEO_TEXT *cplim = &vscreen[ttrow]->v_text[term.cols];
5223 vscreen[ttrow]->v_flag |= VFCHG;
5224 while (cp < cplim) {
5225 *vp++ = 0;
5226 *cp++ = ' ';
5227 }
5228 }
5229 }
5230
5231 void
psc_eeop(void)5232 psc_eeop(void)
5233 {
5234 int saverow = ttrow;
5235 int savecol = ttcol;
5236 while (ttrow < term.rows) {
5237 psc_eeol();
5238 ttrow++;
5239 ttcol = 0;
5240 }
5241 ttrow = saverow;
5242 ttcol = savecol;
5243 }
5244
5245 /* ARGSUSED */
5246 void
psc_rev(UINT huh GCC_UNUSED)5247 psc_rev(UINT huh GCC_UNUSED)
5248 {
5249 /* do nothing */
5250 }
5251
5252 #endif /* OPT_PSCREEN */
5253
5254 /* For memory-leak testing (only!), releases all display storage. */
5255 #if NO_LEAKS
5256 void
vt_leaks(void)5257 vt_leaks(void)
5258 {
5259 vtfree();
5260 #if OPT_UPBUFF
5261 FreeIfNeeded(recomp_tbl);
5262 #endif
5263 }
5264 #endif
5265