forked from th-otto/gulam
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdisplay.c
720 lines (625 loc) · 15.2 KB
/
display.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
/*
display.c of ue/Gulam
The functions in this file handle redisplay. There are two halves,
the ones that update the virtual display screen, and the ones that
make the physical display screen the same as the virtual display
screen. These functions use hints that are left in the windows by the
commands.
prabhaker mateti, 11-14-86, many improvements
prabhaker mateti, 1-1-86, ST520 changes
Steve Wilhite, 1-Dec-85 - massive cleanup on code.
*/
#include "ue.h"
#include <stdarg.h>
#define WFDEBUG 0 /* Window flag debug. */
#define VFCHG 0x0001 /* Changed. */
#define MDLIN 0x0002 /* Mode line indicator. */
TERM term;
int mpresf; /* TRUE if message in last line */
int sgarbf; /* TRUE if screen is garbage */
int currow; /* Working cursor row */
int curcol; /* Working cursor column */
int screenwidth;
static int vtrow; /* Row location of SW cursor */
static int vtcol; /* Column location of SW cursor */
static int ttrow; /* Row location of HW cursor */
static int ttcol; /* Column location of HW cursor */
static uchar *mcp = NULL;
static ELINE *mdlp; /* spare ELINE for mode line */
static uchar *ms; /* area used by mlwrite */
static uchar *mlstr; /* holds the mesg line message */
static int mloff; /* == strlen(mlstr) */
static VIDEO **vscreen; /* Virtual screen */
static VIDEO **pscreen; /* Physical screen */
/* Initialize the data structures used by the display code. The edge
vectors used to access the screens are set up. pm: (1+term.t_nrow)
because message line is now a window for minibuffer. Gets called
initially, and then every time we change nrows, or ncols. */
void vtinit(void) /* redone by pm */
{
int nc;
int j;
int nr;
uchar *p;
if (mcp)
free(mcp);
sgarbf = TRUE;
mpresf = FALSE;
vtrow = vtcol = 0;
ttrow = ttcol = HUGE;
topen();
term.t_nrow = nr = getnrow() - 1;
screenwidth = term.t_ncol = nc = getncol();
j = 2 * (1 + nr) * (((uint) sizeof(VIDEO *)) + ((uint) sizeof(VIDEO)) + nc);
p = mcp = malloc(((uint) sizeof(ELINE)) + 6 * nc + 2 + 2 + nc + j);
if (p == NULL)
outofroom(); /* which exits */
mdlp = (ELINE *) (p);
p += ((uint) sizeof(ELINE)) + 3 * nc + 2;
ms = p;
p += 3 * nc;
mlstr = p;
*p = '\0';
p += nc + 2;
mloff = 0;
vscreen = (VIDEO **) p;
p += (1 + nr) * ((uint) sizeof(VIDEO *));
pscreen = (VIDEO **) p;
p += (1 + nr) * ((uint) sizeof(VIDEO *));
for (j = 0; j <= nr; ++j)
{
vscreen[j] = (VIDEO *) p;
p += (((uint) sizeof(VIDEO)) + nc);
pscreen[j] = (VIDEO *) p;
p += (((uint) sizeof(VIDEO)) + nc);
}
}
void uefreeall(void)
{
freeall();
mcp = NULL;
}
/* Write a line to the virtual screen. The virtual row and column are
updated. If the line is too long put a "$" in the last column. This
routine only puts printing characters into the virtual terminal
buffers. Only column overflow is checked. pm. */
/* row# on the virtual screen */
/* show lp->l_text on the above row */
static void vtputln(int n, ELINE *lp)
{
uchar *p;
uchar *q;
uchar *r;
unsigned int c;
q = vscreen[vtrow = n]->v_text;
r = q + term.t_ncol;
p = lp->l_text;
n = llength(lp);
while (n-- > 0) /* invariant: q <= r */
{
c = *p++;
isqLTr:if (q == r)
{
r[-1] = '$';
break;
}
if (c == '\t')
{
do
{
*q++ = ' '; /* not nec to test q < r */
} while ((q - r + term.t_ncol) & 0x07);
continue;
} /* display a control char */
if (c < 0x20)
{
*q++ = '^';
c += '@';
goto isqLTr;
}
*q++ = c;
}
vtcol = (int)(q - r) + term.t_ncol;
}
/* Erase from the end of the software cursor to the end of the line on
which the software cursor is located. */
static void vteeol(void)
{
char *p;
char c;
int i;
int j;
i = vtcol;
j = vtcol = term.t_ncol;
c = ' ';
p = &vscreen[vtrow]->v_text[i];
while (i++ < j)
*p++ = c;
}
/* Erase the physical screen line #i */
static void erasepscrln(int i)
{
uchar *p;
uchar c;
p = pscreen[i]->v_text;
c = '\0';
for (i = term.t_ncol; --i >= 0;)
*p++ = c;
}
/* Refresh the screen. With no argument, it just does the refresh.
With an argument it recenters "." in the current window. Bound to
"C-L". */
int refresh(int f, int n)
{
UNUSED(n);
if (f == FALSE)
sgarbf = TRUE;
else
{
curwp->w_force = 0; /* Center dot. */
curwp->w_flag |= WFFORCE;
}
keysetup();
return TRUE;
}
/* Winodw framing is not acceptable, compute a new value for the line
at the top of the window. Then set the "WFHARD" flag to force full
redraw. */
static void reframe(WINDOW *wp)
{
ELINE *lp;
int i;
i = wp->w_force;
if (i > 0)
{
--i;
if (i >= wp->w_ntrows)
i = wp->w_ntrows - 1;
} else if (i < 0)
{
i += wp->w_ntrows;
if (i < 0)
i = 0;
} else
i = wp->w_ntrows / 2;
lp = wp->w_dotp;
while (i > 0 && lback(lp) != wp->w_bufp->b_linep)
{
--i;
lp = lback(lp);
}
wp->w_linep = lp;
wp->w_flag |= WFHARD; /* Force full. */
}
static void curlnupdate(WINDOW *wp)
{
ELINE *lp;
int i;
i = wp->w_toprow;
for (lp = wp->w_linep; lp != wp->w_dotp; lp = lforw(lp))
++i;
vscreen[i]->v_flag |= VFCHG;
vtputln(i, lp);
vteeol();
}
static void hardupdate(WINDOW *wp)
{
ELINE *lp;
int i, j;
lp = wp->w_linep;
j = wp->w_toprow + wp->w_ntrows;
for (i = wp->w_toprow; i < j; i++)
{
vscreen[i]->v_flag |= VFCHG;
vscreen[i]->v_flag &= ~MDLIN;
vtrow = i;
vtcol = 0; /* vtmove(i, 0); */
if (lp != wp->w_bufp->b_linep)
{
vtputln(i, lp);
lp = lforw(lp);
}
vteeol();
}
}
/* Always recompute the row and column number of the hardware cursor.
This is the only update for simple moves. */
static void computerowcol(void)
{
ELINE *lp;
uchar *p;
uchar *q;
int c;
currow = curwp->w_toprow;
for (lp = curwp->w_linep; lp != curwp->w_dotp; lp = lforw(lp))
++currow;
curcol = 0;
if (currow == term.t_nrow)
curcol = mloff;
p = lp->l_text;
q = (uchar *) ((long) p + (long) curwp->w_doto);
while (p < q)
{
c = *p++;
if (c == '\t')
curcol |= 0x07;
else if (c < 0x20)
++curcol;
++curcol;
}
if (curcol >= term.t_ncol)
curcol = term.t_ncol - 1;
}
/* Special hacking if the screen is garbage. Clear the hardware
screen, and update your copy to agree with it. Set all the virtual
screen change bits, to force a full update. */
static void scrgarb(void)
{
int i;
for (i = 0; i <= term.t_nrow; ++i)
{
vscreen[i]->v_flag |= VFCHG;
erasepscrln(i);
}
sgarbf = FALSE; /* Erase-page clears */
mpresf = FALSE; /* the message area. */
screenerase();
}
/* Send a command to the terminal to move the hardware cursor to row
"row" and column "col". The row and column arguments are origin 0.
Optimize out random calls. Update "ttrow" and "ttcol". */
static void movecursor(int row, int col)
{
if (row != ttrow || col != ttcol)
{
mvcursor(row, col);
ttrow = row;
ttcol = col;
}
}
/* Update a single line. This does not know how to use insert or
delete character sequences; we are using VT52 functionality. Update
the physical row and column variables. It does try and exploit erase
to end of line. */
static void updateline(int row, uchar *vline, uchar *pline)
{
uchar *vp;
uchar *pp;
uchar *vq;
uchar *pq;
uchar *vr;
uchar *vs;
uchar c;
int nbflag;
int ncol;
if (vscreen[row]->v_flag & MDLIN)
onreversevideo();
ncol = term.t_ncol;
if (row == term.t_nrow)
{
if (mpresf)
goto ret; /* let the mesg stay on screen */
else
{
vq = ms;
cpymem(vq, mlstr, mloff);
vq += mloff;
cpymem(vq, vline, ncol);
vline = ms;
}
}
vp = &vline[0];
vs = vp + ncol;
pp = &pline[0];
/* Compute left match. */
while (vp < vs && vp[0] == pp[0])
{
++vp;
++pp;
}
/*
This can still happen, even though we only call this routine on changed
lines. A hard update is always done when a line splits, a massive
change is done, or a buffer is displayed twice. This optimizes out most
of the excess updating. A lot of computes are used, but these tend to
be hard operations that do a lot of update, so I don't really care.
*/
if (vp == vs)
goto ret; /* All equal. */
nbflag = FALSE;
vq = vs; /* Compute right match. */
pq = &pline[ncol];
/* Note if any nonblank in right match. */
while (vq[-1] == pq[-1])
{
--vq;
--pq;
if (vq[0] != ' ')
nbflag = TRUE;
}
vr = vq;
if (nbflag == FALSE)
{
while (vr != vp && vr[-1] == ' ')
--vr;
if ((int) (vq - vr) <= 3)
vr = vq;
/* Use only if erase is fewer characters. */
}
movecursor(row, (int) (vp - &vline[0]));
ttcol += (int) (vr - vp);
while (vp < vr)
{
*pp++ = c = *vp++;
gputchar(c);
}
if (vr != vq)
{
toeolerase();
while (vp++ != vq)
*pp++ = ' ';
}
ret:
if (vscreen[row]->v_flag & MDLIN)
offreversevideo();
}
/* Make a modeline for a regular/gulam buffer */
static void makemodeline(uchar *md, BUFFER *bp)
{
uchar *cp;
uchar *mp;
uchar patc;
uchar *p1;
uchar *p2;
uchar *tp;
uchar *pmdncol;
if (bp == setgulambp(FALSE))
{
tp = GulamLogo; /* looks: >>gulam<< */
p1 = " cwd: ";
p2 = gfgetcwd();
} else
{
tp = uE;
p1 = " buf: ";
p2 = bp->b_bname;
}
patc = bp->b_modec;
pmdncol = &md[term.t_ncol];
for (mp = md; mp < pmdncol;)
*mp++ = patc;
if ((bp->b_flag & BFCHG) != 0)
md[1] = md[2] = '*';
mp = &md[4];
*mp++ = ' ';
while ((*mp++ = *tp++) != 0)
; /* title */
*--mp = ' ';
mp += 5;
if ((bp->b_flag & BFTEMP) != 0)
*mp++ = 't';
if ((bp->b_flag & BFRDO) != 0)
*mp++ = 'r';
for (mp += 2; (*mp++ = *p1++) != 0;)
; /* cwd/buf */
for (mp--; (*mp++ = *p2++) != 0;) /* cwd/buf */
;
*--mp = ' ';
if (bp->b_fname[0]) /* File name */
{
cp = &bp->b_fname[0];
for (mp += 4; (*mp++ = *cp++) != 0;)
;
mp[-1] = ' ';
}
}
/* Redisplay the mode line for the window pointed to by the "wp".
This is the only routine that has any idea of how the modeline is
formatted. You can change the modeline format by hacking at this
routine. Called by "update" any time there is a dirty window. */
static void modeline(WINDOW *wp)
{
uchar *md;
int n;
ELINE *lp;
uchar mdbuf[sizeof(ELINE) + 2 * 150];
if (wp == mlwp)
return;
/* tired of range checks! */
lp = (ELINE *) mdbuf;
if (lp == NULL)
return;
md = lp->l_text;
lp->l_used = term.t_ncol;
makemodeline(md, wp->w_bufp);
n = wp->w_toprow + wp->w_ntrows; /* Location. */
vscreen[n]->v_flag |= VFCHG | MDLIN; /* Redraw next time. */
vtputln(n, lp);
}
/* Make sure that the display is right. This is a three part process.
First, scan through all of the windows looking for dirty ones. Check
the framing, and refresh the screen. Second, make sure that "currow"
and "curcol" are correct for the current window. Third, make the
virtual and physical screens the same. */
void update(void)
{
WINDOW *wp;
ELINE *lp;
VIDEO *vp1;
VIDEO *vp2;
int i;
for (wp = wheadp; wp; wp = wp->w_wndp)
{
if (wp->w_flag)
{ /* If not force reframe, check the framing. */
if ((wp->w_flag & WFFORCE) == 0)
{
lp = wp->w_linep;
for (i = 0; i < wp->w_ntrows; ++i)
{
if (lp == wp->w_dotp)
goto out;
if (lp == wp->w_bufp->b_linep)
break;
lp = lforw(lp);
}
}
reframe(wp);
out:if ((wp->w_flag & ~WFMODE) == WFEDIT)
curlnupdate(wp);
else if (wp->w_flag & (WFEDIT | WFHARD))
hardupdate(wp);
if (wp->w_flag & WFMODE)
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
}
}
computerowcol();
if (sgarbf != FALSE)
scrgarb();
/* Make sure that the physical and virtual displays agree. */
invisiblecursor();
i = (exitue == -1 ? term.t_nrow : 0); /* kludge, for now! */
for (; i <= term.t_nrow; ++i)
{
vp1 = vscreen[i];
if (vp1->v_flag & VFCHG)
{
vp1->v_flag &= ~VFCHG;
vp2 = pscreen[i];
updateline(i, vp1->v_text, vp2->v_text);
}
}
/* Finally, update the hardware cursor and flush out buffers. */
visiblecursor();
movecursor(currow, curcol);
}
/* Erase the message line. */
void mlerase(void)
{
movecursor(term.t_nrow, 0);
toeolerase();
mpresf = FALSE;
vscreen[term.t_nrow]->v_flag |= VFCHG;
erasepscrln(term.t_nrow);
}
/* Ask a yes or no question in the message line. Return either TRUE,
FALSE, or ABORT. The ABORT status is returned if the user bumps out
of the question with a ^G. Used any time a confirmation is required.
*/
int mlyesno(uchar *prompt)
{
int s;
uchar buf[4];
for (;;)
{
buf[0] = '\0';
s = mlreply(prompt, buf, ((uint) sizeof(buf)));
if (s == '\007')
return ABORT;
return buf[0] == 'y' || buf[0] == 'Y';
}
}
/* Write a prompt into the message line, then read back a response.
Keep track of the physical position of the cursor. If we are in a
keyboard macro throw the prompt away, and return the remembered
response. This lets macros run at full speed. The reply is always
terminated by a carriage return. Handle erase, kill, and abort keys.
Returns the terminating char. */
int mlreply(uchar *prompt, uchar *buf, int nbuf)
{
uchar c;
mlmesg(prompt);
c = getuserinput(buf, nbuf);
mlmesg(ES);
if (c == '\007')
ctrlg(FALSE, 0);
return c;
}
/* Write a message into the message line. Keep track of the physical
cursor position. A small class of printf like format items is
handled. Set the "message line" flag TRUE. Should merge with sprintp()
of util.c */
void mlwrite(const char *fmt, ...) /* rewritten by pm */
{
int c;
int r;
uchar *p;
va_list ap;
long v;
va_start(ap, fmt);
p = ms;
while ((c = *fmt++) != 0)
{
if (c != '%')
{
*p++ = c;
continue;
}
c = *fmt++;
r = 0;
switch (c)
{
case 'x':
r += 6;
case 'd':
r += 2;
case 'o':
r += 8;
v = va_arg(ap, int);
strcpy(p, itoar(v, r));
p += strlen(p);
break;
case 'X':
r += 6;
case 'D':
r += 2;
case 'O':
r += 8;
v = va_arg(ap, long);
strcpy(p, itoar(v, r));
p += strlen(p);
break;
case 's':
strcpy(p, va_arg(ap, char *));
p += strlen(p);
break;
default:
*p++ = c;
break;
}
}
*p = '\0';
va_end(ap);
ttcol = HUGE;
mlerase();
offreversevideo(); /* to be sure!! */
gputs(ms);
mpresf = TRUE;
}
/* Display in the message line area the string pted by p. p can == NULL,
which means display the old mlstr again. The new mesg actually gets
seen by the user because there is an update() happening soon after
calling this rtn. */
void mlmesg(uchar *p)
{
if (p)
strcpy(mlstr, p);
mloff = (int)strlen(mlstr);
mlerase();
}
/* As above, but with no erase. We explicitly update() because otherwise
it does not get seen by the user. */
void mlmesgne(uchar *p)
{
int n;
if (p)
strcpy(mlstr, p);
mloff = (int)strlen(mlstr);
vscreen[term.t_nrow]->v_flag |= VFCHG;
n = exitue;
exitue = -1;
update();
exitue = n;
}