1 /*
2 * Device independent TTY interface for JOE
3 * Copyright
4 * (C) 1992 Joseph H. Allen
5 *
6 * This file is part of JOE (Joe's Own Editor)
7 */
8 #include "types.h"
9
10 int bg_text = 0; /* Background color for text */
11 int skiptop = 0;
12 int env_lines = 0;
13 int env_columns = 0;
14 int notite = 0;
15 int brpaste = 0;
16 int nolinefeeds = 0;
17 int opt_usetabs = 0;
18 int assume_color = 0;
19 int assume_256color = 0;
20
21 /* How to display characters (especially the control ones) */
22 /* here are characters ... */
23 char xlatc[256] = {
24 64, 65, 66, 67, 68, 69, 70, 71, /* 8 */
25 72, 73, 74, 75, 76, 77, 78, 79, /* 16 */
26 80, 81, 82, 83, 84, 85, 86, 87, /* 24 */
27 88, 89, 90, 91, 92, 93, 94, 95, /* 32 */
28 32, 33, 34, 35, 36, 37, 38, 39, /* 40 */
29 40, 41, 42, 43, 44, 45, 46, 47, /* 48 */
30 48, 49, 50, 51, 52, 53, 54, 55, /* 56 */
31 56, 57, 58, 59, 60, 61, 62, 63, /* 64 */
32
33 64, 65, 66, 67, 68, 69, 70, 71, /* 72 */
34 72, 73, 74, 75, 76, 77, 78, 79, /* 80 */
35 80, 81, 82, 83, 84, 85, 86, 87, /* 88 */
36 88, 89, 90, 91, 92, 93, 94, 95, /* 96 */
37 96, 97, 98, 99, 100, 101, 102, 103, /* 104 */
38 104, 105, 106, 107, 108, 109, 110, 111, /* 112 */
39 112, 113, 114, 115, 116, 117, 118, 119, /* 120 */
40 120, 121, 122, 123, 124, 125, 126, 63, /* 128 */
41
42 64, 65, 66, 67, 68, 69, 70, 71, /* 136 */
43 72, 73, 74, 75, 76, 77, 78, 79, /* 144 */
44 80, 81, 82, 83, 84, 85, 86, 87, /* 152 */
45 88, 89, 90, 91, 92, 93, 94, 95, /* 160 */
46 32, 33, 34, 35, 36, 37, 38, 39, /* 168 */
47 40, 41, 42, 43, 44, 45, 46, 47, /* 176 */
48 48, 49, 50, 51, 52, 53, 54, 55, /* 184 */
49 56, 57, 58, 59, 60, 61, 62, 63, /* 192 */
50
51 64, 65, 66, 67, 68, 69, 70, 71, /* 200 */
52 72, 73, 74, 75, 76, 77, 78, 79, /* 208 */
53 80, 81, 82, 83, 84, 85, 86, 87, /* 216 */
54 88, 89, 90, 91, 92, 93, 94, 95, /* 224 */
55 96, 97, 98, 99, 100, 101, 102, 103, /* 232 */
56 104, 105, 106, 107, 108, 109, 110, 111, /* 240 */
57 112, 113, 114, 115, 116, 117, 118, 119, /* 248 */
58 120, 121, 122, 123, 124, 125, 126, 63 /* 256 */
59 };
60 /* ... and here their attributes */
61 int xlata[256] = {
62 UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE, /* 4 */
63 UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE, /* 8 */
64 UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE, /* 12 */
65 UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE, /* 16 */
66 UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE, /* 20 */
67 UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE, /* 24 */
68 UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE, /* 28 */
69 UNDERLINE, UNDERLINE, UNDERLINE, UNDERLINE, /* 32 */
70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 48 */
71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 */
72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 */
73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 */
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112 */
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, UNDERLINE, /* 128 */
76
77 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 130 */
78 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 132 */
79 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 134 */
80 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 136 */
81 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 138 */
82 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 140 */
83 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 142 */
84 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 144 */
85 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 146 */
86 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 148 */
87 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 150 */
88 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 152 */
89 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 154 */
90 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 156 */
91 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 158 */
92 INVERSE + UNDERLINE, INVERSE + UNDERLINE, /* 160 */
93
94 INVERSE, INVERSE, INVERSE, INVERSE, /* 164 */
95 INVERSE, INVERSE, INVERSE, INVERSE, /* 168 */
96 INVERSE, INVERSE, INVERSE, INVERSE, /* 172 */
97 INVERSE, INVERSE, INVERSE, INVERSE, /* 176 */
98 INVERSE, INVERSE, INVERSE, INVERSE, /* 180 */
99 INVERSE, INVERSE, INVERSE, INVERSE, /* 184 */
100 INVERSE, INVERSE, INVERSE, INVERSE, /* 188 */
101 INVERSE, INVERSE, INVERSE, INVERSE, /* 192 */
102 INVERSE, INVERSE, INVERSE, INVERSE, /* 196 */
103 INVERSE, INVERSE, INVERSE, INVERSE, /* 200 */
104 INVERSE, INVERSE, INVERSE, INVERSE, /* 204 */
105 INVERSE, INVERSE, INVERSE, INVERSE, /* 208 */
106 INVERSE, INVERSE, INVERSE, INVERSE, /* 212 */
107 INVERSE, INVERSE, INVERSE, INVERSE, /* 216 */
108 INVERSE, INVERSE, INVERSE, INVERSE, /* 220 */
109 INVERSE, INVERSE, INVERSE, INVERSE, /* 224 */
110 INVERSE, INVERSE, INVERSE, INVERSE, /* 228 */
111 INVERSE, INVERSE, INVERSE, INVERSE, /* 232 */
112 INVERSE, INVERSE, INVERSE, INVERSE, /* 236 */
113 INVERSE, INVERSE, INVERSE, INVERSE, /* 240 */
114 INVERSE, INVERSE, INVERSE, INVERSE, /* 244 */
115 INVERSE, INVERSE, INVERSE, INVERSE, /* 248 */
116 INVERSE, INVERSE, INVERSE, INVERSE, /* 252 */
117 INVERSE, INVERSE, INVERSE, INVERSE + UNDERLINE /* 256 */
118 };
119
mfill(int (* dest)[COMPOSE],int val,ptrdiff_t count)120 static void mfill(int (*dest)[COMPOSE], int val, ptrdiff_t count)
121 {
122 ptrdiff_t x;
123 while (count--) {
124 (*dest)[0] = val;
125 for (x = 1; x != COMPOSE; ++x)
126 (*dest)[x] = 0;
127 ++dest;
128 }
129 }
130
131 /* For terminals with the "xn" capability, which includes all popular
132 * graphical terminal emulators: When a character is printed in the last
133 * (let's say 80th) column, the cursor enters a special state. If a letter is
134 * printed next (which shouldn't happen in joe) then it wraps to the next
135 * line. However, if the cursor is moved, it behaves as if it still stood in
136 * the 80th column, rather than in the invisible 81st as joe thinks. Some
137 * terminal emulators (e.g. xterm) display this cursor in the 80th (rightmost)
138 * column, some others (e.g. gnome-terminal) don't show it at all, yet some
139 * others (e.g. konsole) show it at the beginning of the next line. This is a
140 * state at which we never want to settle in joe, it's just a necessary
141 * transient state while printing in the rightmost column. This method makes
142 * sure that the cursor is not in this strange state, moving it out of there
143 * if necessary.
144 *
145 * For terminal without "xn": In nresize() just revert to not using the
146 * rightmost column. Another possibility would be to do a character insertion
147 * to push an already printed character to the right, as implemented in pts's
148 * fork of joe at http://pts-mini-gpl.googlecode.com/svn/trunk/joe-p37/ but
149 * it's quite complicated (especially with CJK) and probably not worth the
150 * trouble.
151 *
152 * More information:
153 * https://www.gnu.org/software/termutils/manual/termcap-1.3/html_chapter/termcap_4.html#SEC27
154 */
155
fixupcursor(register SCRN * t)156 static void fixupcursor(register SCRN *t)
157 {
158 if (t->x == t->co) {
159 texec(t->cap, t->cr, 1, 0, 0, 0, 0);
160 t->x = 0;
161 }
162 }
163
164 /* Set attributes */
165
set_attr(SCRN * t,int c)166 int set_attr(SCRN *t, int c)
167 {
168 int e;
169
170 /* Attributes which have gone off */
171 e = ((AT_MASK|FG_NOT_DEFAULT|BG_NOT_DEFAULT)&t->attrib & ~c);
172
173 if (e) { /* If any attribute go off, switch them all off: fixes bug on PCs */
174 if (t->me)
175 texec(t->cap, t->me, 1, 0, 0, 0, 0);
176 else {
177 if (t->ue)
178 texec(t->cap, t->ue, 1, 0, 0, 0, 0);
179 if (t->se)
180 texec(t->cap, t->se, 1, 0, 0, 0, 0);
181 if (t->ZR)
182 texec(t->cap, t->ZR, 1, 0, 0, 0, 0);
183 }
184 t->attrib = 0;
185 }
186
187 /* Attributes which have turned on */
188 e = (c & ~t->attrib);
189
190 if (e & INVERSE) {
191 if (t->mr)
192 texec(t->cap, t->mr, 1, 0, 0, 0, 0);
193 else if (t->so)
194 texec(t->cap, t->so, 1, 0, 0, 0, 0);
195 }
196
197 if (e & UNDERLINE)
198 if (t->us)
199 texec(t->cap, t->us, 1, 0, 0, 0, 0);
200
201 if (e & DOUBLE_UNDERLINE)
202 if (t->dunderline)
203 texec(t->cap, t->dunderline, 1, 0, 0, 0, 0);
204
205 if (e & CROSSED_OUT)
206 if (t->stricken)
207 texec(t->cap, t->stricken, 1, 0, 0, 0, 0);
208
209 if (e & BLINK)
210 if (t->mb)
211 texec(t->cap, t->mb, 1, 0, 0, 0, 0);
212 if (e & BOLD)
213 if (t->md)
214 texec(t->cap, t->md, 1, 0, 0, 0, 0);
215 if (e & DIM)
216 if (t->mh)
217 texec(t->cap, t->mh, 1, 0, 0, 0, 0);
218 if (e & ITALIC)
219 if (t->ZH)
220 texec(t->cap, t->ZH, 1, 0, 0, 0, 0);
221
222 if ((t->attrib & FG_MASK) != (c & FG_MASK)) {
223 if (t->Sf) {
224 int color = ((c & FG_VALUE) >> FG_SHIFT);
225 if (c & FG_TRUECOLOR) {
226 if (t->truecolor && t->palette && t->palette[color] >= 0) {
227 char bf[32];
228 int rgb = t->palette[color];
229 joe_snprintf_3(bf, SIZEOF(bf), "\033[38;2;%d;%d;%dm", (rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
230 ttputs(bf);
231 }
232 } else if (t->assume_256 && color >= t->Co) {
233 char bf[32];
234 joe_snprintf_1(bf, SIZEOF(bf), "\033[38;5;%dm", color);
235 ttputs(bf);
236 } else {
237 if (t->Co & (t->Co - 1))
238 texec(t->cap, t->Sf, 1, color % t->Co, 0, 0, 0);
239 else
240 texec(t->cap, t->Sf, 1, color & (t->Co - 1), 0, 0, 0);
241 }
242 }
243 }
244
245 if ((t->attrib & BG_MASK) != (c & BG_MASK)) {
246 if (t->Sb) {
247 int color = ((c & BG_VALUE) >> BG_SHIFT);
248 if (c & BG_TRUECOLOR) {
249 if (t->truecolor && t->palette && t->palette[color] >= 0) {
250 char bf[32];
251 int rgb = t->palette[color];
252 joe_snprintf_3(bf, SIZEOF(bf), "\033[48;2;%d;%d;%dm", (rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
253 ttputs(bf);
254 }
255 } else if (t->assume_256 && color >= t->Co) {
256 char bf[32];
257 joe_snprintf_1(bf,SIZEOF(bf),"\033[48;5;%dm",color);
258 ttputs(bf);
259 } else {
260 if (t->Co & (t->Co - 1))
261 texec(t->cap, t->Sb, 1, color % t->Co, 0, 0, 0);
262 else
263 texec(t->cap, t->Sb, 1, color & (t->Co - 1), 0, 0, 0);
264 }
265 }
266 }
267
268 t->attrib = c;
269
270 return 0;
271 }
272
273 /* Output character with attributes */
274
275 int outatr_state; /* 1 = we have a start character waiting to emit. 2 = we have combining chars we are ignoring */
276 int (*outatr_scrn)[COMPOSE];
277 int *outatr_attrf;
278 int outatr_build[COMPOSE];
279 ptrdiff_t outatr_xx;
280 ptrdiff_t outatr_yy;
281 ptrdiff_t outatr_ofst;
282 int outatr_wid;
283 int outatr_uni_ctrl;
284 int outatr_a;
285
286 /* Emit accumulated character (start character + compose characters) */
287
outatr_complete(SCRN * t)288 void outatr_complete(SCRN *t)
289 {
290 if (outatr_state == 1) {
291 ptrdiff_t x;
292 for (x = 0; x != COMPOSE; ++x)
293 if (outatr_scrn[0][x] != outatr_build[x])
294 break;
295 if (x != COMPOSE || *outatr_attrf != outatr_a) {
296 char buf[16];
297 for (x = 0; x != COMPOSE; ++x)
298 outatr_scrn[0][x] = outatr_build[x];
299 *outatr_attrf = outatr_a;
300 if (t->ins)
301 clrins(t);
302 if (t->x != outatr_xx || t->y != outatr_yy)
303 cpos(t, outatr_xx, outatr_yy);
304 if (t->attrib != outatr_a)
305 set_attr(t, outatr_a);
306 if (outatr_uni_ctrl) {
307 sprintf(buf, "<%X>", outatr_build[0]);
308 ttputs(buf);
309 } else {
310 for (x = 0; x != COMPOSE && outatr_build[x]; ++x) {
311 utf8_encode(buf, outatr_build[x]);
312 ttputs(buf);
313 }
314 }
315 t->x += outatr_wid;
316 while (outatr_wid > 1) {
317 (*++outatr_scrn)[0] = -1;
318 *++outatr_attrf = 0;
319 --outatr_wid;
320 }
321 }
322 }
323 outatr_state = 0;
324 }
325
outatr(struct charmap * map,SCRN * t,int (* scrn)[COMPOSE],int * attrf,ptrdiff_t xx,ptrdiff_t yy,int c,int a)326 void outatr(struct charmap *map,SCRN *t,int (*scrn)[COMPOSE],int *attrf,ptrdiff_t xx,ptrdiff_t yy,int c,int a)
327 {
328 if (c < 0)
329 c += 256;
330 if(map->type)
331 if(locale_map->type) {
332 if (cclass_lookup(cclass_combining, c)) { /* It's a combining character */
333 if (!outatr_state) /* No start character? */
334 outatr_state = 2; /* Ignore it... */
335 else if (outatr_state == 1) { /* We have a start character, add it */
336 if (outatr_ofst != COMPOSE)
337 outatr_build[outatr_ofst++] = c;
338 else { /* More combining chars than we buffer */
339 char buf[16];
340 outatr_scrn[0][0] = -1; /* Force outatr_complete to emit character */
341 outatr_complete(t);
342 utf8_encode(buf, c);
343 ttputs(buf);
344 outatr_state = 3;
345 }
346 } else if (outatr_state == 3) { /* We have alread emitted the start character, but we have more combining chars */
347 char buf[16];
348 utf8_encode(buf, c);
349 ttputs(buf);
350 }
351 } else {
352 ptrdiff_t x;
353 if (outatr_state) /* We already have a start character? */
354 outatr_complete(t);
355 outatr_uni_ctrl = 0;
356 /* Deal with control characters */
357 if (c < 32) {
358 c = c + '@';
359 a ^= UNDERLINE;
360 } else if (c == 127) {
361 c = '?';
362 a ^= UNDERLINE;
363 } else if (unictrl(c)) {
364 a ^= UNDERLINE;
365 outatr_uni_ctrl = 1;
366 }
367 outatr_wid = joe_wcwidth(1, c);
368 outatr_state = 1;
369 outatr_scrn = scrn;
370 outatr_attrf = attrf;
371 outatr_xx = xx;
372 outatr_yy = yy;
373 outatr_ofst = 1;
374 outatr_a = a;
375 outatr_build[0] = c;
376 for (x = 1; x != COMPOSE; ++x)
377 outatr_build[x] = 0;
378 }
379 } else {
380 /* UTF-8 char to non-UTF-8 terminal */
381 /* Don't convert control chars below 256 */
382 if ((c>=32 && c<=126) || c>=160) {
383 if (unictrl(c))
384 a ^= UNDERLINE;
385 c = from_uni(locale_map,c);
386 if (c==-1)
387 c = '?';
388 }
389
390 /* Deal with control characters */
391 if (!joe_isprint(locale_map,c) && !(dspasis && c>=128)) {
392 a ^= xlata[c];
393 c = xlatc[c];
394 }
395
396 if((*scrn)[0] == c && *attrf == a)
397 return;
398
399 (*scrn)[0] = c;
400 *attrf = a;
401 if(t->ins)
402 clrins(t);
403 if(t->x != xx || t->y != yy)
404 cpos(t,xx,yy);
405 if(t->attrib != a)
406 set_attr(t,a);
407 ttputc(TO_CHAR_OK(c));
408 t->x++;
409 }
410 else
411 if (!locale_map->type) {
412 /* Non UTF-8 char to non UTF-8 terminal */
413 /* Byte-byte Translate? */
414
415 /* Deal with control characters */
416 if (!joe_isprint(locale_map,c) && !(dspasis && c>=128)) {
417 a ^= xlata[c];
418 c = xlatc[c];
419 }
420
421 if ((*scrn)[0] == c && *attrf == a)
422 return;
423
424 (*scrn)[0] = c;
425 *attrf = a;
426
427 if(t->ins)
428 clrins(t);
429 if(t->x != xx || t->y != yy)
430 cpos(t,xx,yy);
431 if(t->attrib != a)
432 set_attr(t,a);
433 ttputc(TO_CHAR_OK(c));
434 t->x++;
435 } else {
436 /* Non UTF-8 char to UTF-8 terminal */
437 char buf[16];
438 int wid;
439
440 /* Deal with control characters */
441 if (!(dspasis && c>=128) && !joe_isprint(map,c)) {
442 a ^= xlata[c];
443 c = xlatc[c];
444 }
445
446 c = to_uni(map,c);
447 if (c == -1)
448 c = '?';
449 utf8_encode(buf,c);
450
451 if ((*scrn)[0] == c && *attrf == a)
452 return;
453
454 wid = joe_wcwidth(0,c);
455 (*scrn)[0] = c;
456 *attrf = a;
457 if(t->ins)
458 clrins(t);
459 if(t->x != xx || t->y != yy)
460 cpos(t, xx, yy);
461 if(t->attrib != a)
462 set_attr(t, a);
463 ttputs(buf);
464 t->x+=wid;
465 while(wid>1) {
466 (*++scrn)[0] = -1;
467 *++attrf= 0;
468 --wid;
469 }
470 }
471 /* At this point, t->x might equal t->co. Fixupcursor() will be called by next call
472 to cpos to deal with this. */
473 }
474
475 /* Set scrolling region */
476
setregn(SCRN * t,ptrdiff_t top,ptrdiff_t bot)477 static void setregn(SCRN *t, ptrdiff_t top, ptrdiff_t bot)
478 {
479 if (!t->cs) {
480 t->top = top;
481 t->bot = bot;
482 return;
483 }
484 if (t->top != top || t->bot != bot) {
485 t->top = top;
486 t->bot = bot;
487 texec(t->cap, t->cs, 1, top, bot - 1, 0, 0);
488 t->x = -1;
489 t->y = -1;
490 }
491 }
492
493 /* Exit insert mode */
494
clrins(SCRN * t)495 int clrins(SCRN *t)
496 {
497 if (t->ins != 0) {
498 texec(t->cap, t->ei, 1, 0, 0, 0, 0);
499 t->ins = 0;
500 }
501 return 0;
502 }
503
504 /* Erase from given screen coordinate to end of line */
505
506 /* Now we store a '\n' mark in the screen buffer. The idea is to emit ESC [
507 * K at the \n and remember that we did it. This way spaces at the end of
508 * the line are recorded in the terminal emulator so that they are preserved
509 * with cut/paste operations.
510 */
511
eraeol(SCRN * t,ptrdiff_t x,ptrdiff_t y,int atr)512 int eraeol(SCRN *t, ptrdiff_t x, ptrdiff_t y, int atr)
513 {
514 int (*s)[COMPOSE], (*ss)[COMPOSE], *a, *aa;
515 ptrdiff_t w = t->co - x;
516
517 if (w <= 0)
518 return 0;
519 s = t->scrn + y * t->co + x;
520 a = t->attr + y * t->co + x;
521 ss = s + w;
522 aa = a + w;
523 do {
524 --ss;
525 if ((*ss)[0] != (ss == s ? '\n' : ' ')) {
526 ++ss;
527 break;
528 } else if (*--aa != atr) {
529 ++ss;
530 ++aa;
531 break;
532 }
533 } while (ss != s);
534 if (s != ss) {
535 if (t->ce) {
536 cpos(t, x, y);
537 if(t->attrib != atr)
538 set_attr(t, atr);
539 texec(t->cap, t->ce, 1, 0, 0, 0, 0);
540 mfill(s, ' ', w);
541 msetI(a, atr, w);
542 (*s)[0] = '\n';
543 } else {
544 if (t->ins)
545 clrins(t);
546 if (t->x != x || t->y != y)
547 cpos(t, x, y);
548 if (t->attrib != atr)
549 set_attr(t, atr);
550 (*s)[0] = '\n';
551 *a = atr;
552 ttputc(' ');
553 ++t->x;
554 ++s;
555 ++a;
556 while (s != ss) {
557 (*s)[0] = ' ';
558 *a = atr;
559 ttputc(' ');
560 ++t->x;
561 ++s;
562 ++a;
563 }
564 }
565 }
566 return 0;
567 }
568
out(void * t,char c)569 static void out(void *t, char c)
570 {
571 ttputc(c);
572 }
573
nopen(CAP * cap)574 SCRN *nopen(CAP *cap)
575 {
576 SCRN *t = (SCRN *) joe_malloc(SIZEOF(SCRN));
577 ptrdiff_t x, y, co, li;
578 int ansiish;
579
580 ttopen();
581
582 t->cap = cap;
583 setcap(cap, tty_baud, out, NULL);
584
585 li = getnum(t->cap,"li");
586 if (li < 1)
587 li = 24;
588 co = getnum(t->cap,"co");
589 if (co < 2)
590 co = 80;
591 x = y = 0;
592 ttgtsz(&x, &y);
593 if (x > 7 && y > 3) {
594 li = y;
595 co = x;
596 }
597 t->co = t->li = -1; /* will be set by nresize() below */
598
599 t->haz = getflag(t->cap,"hz");
600 t->os = getflag(t->cap,"os");
601 t->eo = getflag(t->cap,"eo");
602 if (getflag(t->cap,"hc"))
603 t->os = 1;
604 if (t->os || getflag(t->cap,"ul"))
605 t->ul = 1;
606 else
607 t->ul = 0;
608
609 t->xn = getflag(t->cap,"xn");
610 t->am = getflag(t->cap,"am");
611
612 if (notite)
613 t->ti = 0;
614 else
615 t->ti = jgetstr(t->cap,"ti");
616 t->cl = jgetstr(t->cap,"cl");
617 t->cd = jgetstr(t->cap,"cd");
618
619 if (notite)
620 t->te = 0;
621 else
622 t->te = jgetstr(t->cap,"te");
623
624 t->ut = getflag(t->cap,"ut");
625 t->Sb = jgetstr(t->cap,"AB");
626 if (!t->Sb) t->Sb = jgetstr(t->cap,"Sb");
627 t->Sf = jgetstr(t->cap,"AF");
628 if (!t->Sf) t->Sf = jgetstr(t->cap,"Sf");
629 t->Co = getnum(t->cap,"Co");
630 if (t->Co == -1)
631 t->Co = 8;
632
633 t->mb = NULL;
634 t->md = NULL;
635 t->mh = NULL;
636 t->mr = NULL;
637 t->stricken = NULL;
638 t->dunderline = NULL;
639 t->avattr = 0;
640 if (!(t->me = jgetstr(t->cap,"me")))
641 goto oops;
642 if ((t->mb = jgetstr(t->cap,"mb")))
643 t->avattr |= BLINK;
644 if ((t->md = jgetstr(t->cap,"md")))
645 t->avattr |= BOLD;
646 if ((t->mh = jgetstr(t->cap,"mh")))
647 t->avattr |= DIM;
648 if ((t->mr = jgetstr(t->cap,"mr")))
649 t->avattr |= INVERSE;
650 oops:
651
652 /* Does it look like an ansi terminal? (it has bold which begins with ESC [) */
653 #ifndef TERMINFO
654 ansiish = t->md && t->md[0] == '\\' && t->md[1] == 'E' && t->md[2] == '[';
655 #else
656 ansiish = t->md && t->md[0] == '\033' && t->md[1] == '[';
657 #endif
658
659 /* Cheating here: there is no capability for stricken and double-underline, so we just assume it has it if terminal looks ansi */
660 if (ansiish) {
661 t->stricken = "\033[9m";
662 }
663
664 if (ansiish) {
665 t->dunderline = "\033[21m";
666 }
667
668 /* No termcap for bracketed paste. ANSI-looking terminals will either support bracketed paste
669 or this setting will cause no harm. */
670 if (ansiish && brpaste) {
671 #ifndef TERMINFO
672 t->brp = "\\E[?2004h";
673 t->bre = "\\E[?2004l";
674 #else
675 t->brp = "\033[?2004h";
676 t->bre = "\033[?2004l";
677 #endif
678 } else {
679 t->brp = t->bre = 0;
680 }
681
682 if (assume_color || assume_256color) {
683 /* Install 8 color support if it looks like an ansi terminal */
684 if (ansiish && !t->Sf) {
685 #ifndef TERMINFO
686 t->ut = 1;
687 t->Sf = "\\E[3%dm";
688 t->Sb = "\\E[4%dm";
689 t->Co = 8;
690 #else
691 t->ut = 1;
692 t->Sf = "\033[3%p1%dm";
693 t->Sb = "\033[4%p1%dm";
694 #endif
695 }
696 }
697
698 t->assume_256 = 0;
699 if (assume_256color && t->Co < 256) {
700 /* Force 256 color support */
701 if (ansiish) {
702 t->assume_256 = 1;
703 #ifndef TERMINFO
704 #ifdef junk
705 t->ut = 1;
706 t->Sf = "\\E[38;5;%dm";
707 t->Sb = "\\E[48;5;%dm";
708 #endif
709 #else
710 #ifdef junk
711 t->ut = 1;
712 t->Sf = "\033[38;5;%p1%dm";
713 t->Sb = "\033[48;5;%p1%dm";
714 #endif
715 #endif
716 }
717 }
718
719 {
720 char *s = getenv("COLORTERM");
721 t->truecolor = s && (!zicmp(s, "truecolor") || !zicmp(s, "24bit"));
722 t->palette = NULL;
723 }
724
725 t->so = NULL;
726 t->se = NULL;
727 if (getnum(t->cap,"sg") <= 0 && !t->mr && jgetstr(t->cap,"se")) {
728 if ((t->so = jgetstr(t->cap,"so")) != NULL)
729 t->avattr |= INVERSE;
730 t->se = jgetstr(t->cap,"se");
731 }
732 if (getflag(t->cap,"xs") || getflag(t->cap,"xt"))
733 t->so = NULL;
734
735 t->us = NULL;
736 t->ue = NULL;
737 if (getnum(t->cap,"ug") <= 0 && jgetstr(t->cap,"ue")) {
738 if ((t->us = jgetstr(t->cap,"us")) != NULL)
739 t->avattr |= UNDERLINE;
740 t->ue = jgetstr(t->cap,"ue");
741 }
742
743 t->ZH = NULL;
744 t->ZR = NULL;
745 if ((t->ZH = jgetstr(t->cap,"ZH")) != NULL)
746 t->avattr |= ITALIC;
747 t->ZR = jgetstr(t->cap,"ZR");
748
749 if (!(t->uc = jgetstr(t->cap,"uc")))
750 if (t->ul)
751 t->uc ="_";
752 if (t->uc)
753 t->avattr |= UNDERLINE;
754
755 t->ms = getflag(t->cap,"ms");
756
757 t->da = getflag(t->cap,"da");
758 t->db = getflag(t->cap,"db");
759 t->cs = jgetstr(t->cap,"cs");
760 t->rr = getflag(t->cap,"rr");
761 t->sf = jgetstr(t->cap,"sf");
762 t->sr = jgetstr(t->cap,"sr");
763 t->SF = jgetstr(t->cap,"SF");
764 t->SR = jgetstr(t->cap,"SR");
765 t->al = jgetstr(t->cap,"al");
766 t->dl = jgetstr(t->cap,"dl");
767 t->AL = jgetstr(t->cap,"AL");
768 t->DL = jgetstr(t->cap,"DL");
769 if (!getflag(t->cap,"ns") && !t->sf)
770 t->sf ="\12";
771
772 if (!getflag(t->cap,"in") && tty_baud < 38400) {
773 t->dc = jgetstr(t->cap,"dc");
774 t->DC = jgetstr(t->cap,"DC");
775 t->dm = jgetstr(t->cap,"dm");
776 t->ed = jgetstr(t->cap,"ed");
777
778 t->im = jgetstr(t->cap,"im");
779 t->ei = jgetstr(t->cap,"ei");
780 t->ic = jgetstr(t->cap,"ic");
781 t->IC = jgetstr(t->cap,"IC");
782 t->ip = jgetstr(t->cap,"ip");
783 t->mi = getflag(t->cap,"mi");
784 } else {
785 t->dm = NULL;
786 t->dc = NULL;
787 t->DC = NULL;
788 t->ed = NULL;
789 t->im = NULL;
790 t->ic = NULL;
791 t->IC = NULL;
792 t->ip = NULL;
793 t->ei = NULL;
794 t->mi = 1;
795 }
796
797 t->bs = NULL;
798 if (jgetstr(t->cap,"bc"))
799 t->bs = jgetstr(t->cap,"bc");
800 else if (jgetstr(t->cap,"le"))
801 t->bs = jgetstr(t->cap,"le");
802 if (getflag(t->cap,"bs"))
803 t->bs ="\10";
804
805 t->cbs = tcost(t->cap, t->bs, 1, 2, 2, 0, 0);
806
807 t->lf ="\12";
808 if (jgetstr(t->cap,"do"))
809 t->lf = jgetstr(t->cap,"do");
810 t->clf = tcost(t->cap, t->lf, 1, 2, 2, 0, 0);
811
812 t->up = jgetstr(t->cap,"up");
813 t->cup = tcost(t->cap, t->up, 1, 2, 2, 0, 0);
814
815 t->nd = jgetstr(t->cap,"nd");
816
817 t->tw = 8;
818 if (getnum(t->cap,"it") > 0)
819 t->tw = getnum(t->cap,"it");
820 else if (getnum(t->cap,"tw") > 0)
821 t->tw = getnum(t->cap,"tw");
822
823 if (!(t->ta = jgetstr(t->cap,"ta")))
824 if (getflag(t->cap,"pt"))
825 t->ta ="\11";
826 t->bt = jgetstr(t->cap,"bt");
827 if (getflag(t->cap,"xt")) {
828 t->ta = NULL;
829 t->bt = NULL;
830 }
831
832 if (!opt_usetabs) {
833 t->ta = NULL;
834 t->bt = NULL;
835 }
836
837 t->cta = tcost(t->cap, t->ta, 1, 2, 2, 0, 0);
838 t->cbt = tcost(t->cap, t->bt, 1, 2, 2, 0, 0);
839
840 t->ho = jgetstr(t->cap,"ho");
841 t->cho = tcost(t->cap, t->ho, 1, 2, 2, 0, 0);
842 t->ll = jgetstr(t->cap,"ll");
843 t->cll = tcost(t->cap, t->ll, 1, 2, 2, 0, 0);
844
845 t->cr ="\15";
846 if (jgetstr(t->cap,"cr"))
847 t->cr = jgetstr(t->cap,"cr");
848 if (getflag(t->cap,"nc") || getflag(t->cap,"xr"))
849 t->cr = NULL;
850 t->ccr = tcost(t->cap, t->cr, 1, 2, 2, 0, 0);
851
852 t->cRI = tcost(t->cap, t->RI = jgetstr(t->cap,"RI"), 1, 2, 2, 0, 0);
853 t->cLE = tcost(t->cap, t->LE = jgetstr(t->cap,"LE"), 1, 2, 2, 0, 0);
854 t->cUP = tcost(t->cap, t->UP = jgetstr(t->cap,"UP"), 1, 2, 2, 0, 0);
855 t->cDO = tcost(t->cap, t->DO = jgetstr(t->cap,"DO"), 1, 2, 2, 0, 0);
856 t->cch = tcost(t->cap, t->ch = jgetstr(t->cap,"ch"), 1, 2, 2, 0, 0);
857 t->ccv = tcost(t->cap, t->cv = jgetstr(t->cap,"cv"), 1, 2, 2, 0, 0);
858 t->ccV = tcost(t->cap, t->cV = jgetstr(t->cap,"cV"), 1, 2, 2, 0, 0);
859 t->ccm = tcost(t->cap, t->cm = jgetstr(t->cap,"cm"), 1, 2, 2, 0, 0);
860
861 t->cce = tcost(t->cap, t->ce = jgetstr(t->cap,"ce"), 1, 2, 2, 0, 0);
862
863 /* Make sure terminal can do absolute positioning */
864 if (t->cm)
865 goto ok;
866 if (t->ch && t->cv)
867 goto ok;
868 if (t->ho && (t->lf || t->DO || t->cv))
869 goto ok;
870 if (t->ll && (t->up || t->UP || t->cv))
871 goto ok;
872 if (t->cr && t->cv)
873 goto ok;
874 leave = 1;
875 ttclose();
876 signrm();
877 fprintf(stderr,"cm=%p ch=%p cv=%p ho=%p lf=%p DO=%p ll=%p up=%p UP=%p cr=%p\n",
878 t->cm, t->ch, t->cv, t->ho, t->lf, t->DO, t->ll, t->up, t->UP, t->cr);
879 fputs(joe_gettext(_("Sorry, your terminal can't do absolute cursor positioning.\nIt's broken\n")), stderr);
880 return NULL;
881 ok:
882
883 /* Determine if we can scroll */
884 if (((t->sr || t->SR) && (t->sf || t->SF) && t->cs) || ((t->al || t->AL) && (t->dl || t->DL)))
885 t->scroll = 1;
886 else {
887 t->scroll = 0;
888 if (tty_baud < 38400)
889 opt_mid = 1;
890 }
891
892 /* Determine if we can ins/del within lines */
893 if ((t->im || t->ic || t->IC) && (t->dc || t->DC))
894 t->insdel = 1;
895 else
896 t->insdel = 0;
897
898 /* Adjust for high baud rates */
899 if (tty_baud >= 38400) {
900 /* t->scroll = 0; */ /* With all the output from syntax highlighting, it's now better to scroll in terminal emulators */
901 t->insdel = 0;
902 }
903
904 /* Adjust for low baud rates */
905 if (tty_baud < 38400) {
906 opt_left = -2;
907 opt_right = -2;
908 }
909
910 /* Send out li linefeeds so that scroll-back history is not lost */
911 if (notite && !nolinefeeds) {
912 for (y = 1; y < li; ++y)
913 ttputc(10);
914 }
915
916 /* Send out terminal initialization string */
917 if (t->ti)
918 texec(t->cap, t->ti, 1, 0, 0, 0, 0);
919 if (!skiptop && t->cl)
920 texec(t->cap, t->cl, 1, 0, 0, 0, 0);
921 if (t->brp)
922 texec(t->cap, t->brp, 1, 0, 0, 0, 0);
923
924 /* Initialize variable screen size dependent vars */
925 t->scrn = NULL;
926 t->attr = NULL;
927 t->sary = NULL;
928 t->updtab = NULL;
929 t->compose = NULL;
930 t->ofst = NULL;
931 t->ary = NULL;
932 t->htab = (struct hentry *) joe_malloc(256 * SIZEOF(struct hentry));
933
934 nresize(t, co, li);
935
936 /* Initialize mouse */
937 mouseopen();
938
939 return t;
940 }
941
942 /* Change size of screen. Decrease width by 1 if printing in the last column is not supported */
943
nresize(SCRN * t,ptrdiff_t w,ptrdiff_t h)944 int nresize(SCRN *t, ptrdiff_t w, ptrdiff_t h)
945 {
946 if (h < skiptop + 1)
947 h = skiptop + 1;
948 if (w < 8)
949 w = 8;
950 if (!t->xn)
951 w--; /* Don't write to the last column if terminal lacks xn capability */
952 if (h == t->li && w == t->co)
953 return 0;
954 t->li = h;
955 t->co = w;
956 if (t->sary)
957 joe_free(t->sary);
958 if (t->updtab)
959 joe_free(t->updtab);
960 if (t->scrn)
961 joe_free(t->scrn);
962 if (t->attr)
963 joe_free(t->attr);
964 if (t->compose)
965 joe_free(t->compose);
966 if (t->ofst)
967 joe_free(t->ofst);
968 if (t->ary)
969 joe_free(t->ary);
970 t->scrn = (int (*)[COMPOSE])joe_malloc(t->li * t->co * SIZEOF(int [COMPOSE]));
971 t->attr = (int *)joe_malloc(t->li * t->co * SIZEOF(int));
972 t->sary = (ptrdiff_t *)joe_calloc(t->li, SIZEOF(ptrdiff_t));
973 t->updtab = (int *)joe_malloc(t->li * SIZEOF(int));
974 t->compose = (int *)joe_malloc(t->co * SIZEOF(int));
975 t->ofst = (ptrdiff_t *)joe_malloc(t->co * SIZEOF(ptrdiff_t));
976 t->ary = (struct hentry *)joe_malloc(t->co * SIZEOF(struct hentry));
977
978 nredraw(t);
979 return 1;
980 }
981
982 /* Calculate cost of positioning the cursor using only relative cursor
983 * positioning functions: t->(lf, DO, up, UP, bs, LE, RI, ta, bt) and rewriting
984 * characters (to move right)
985 *
986 * This doesn't use the am and bw capabilities although it probably could.
987 */
988
relcost(register SCRN * t,register ptrdiff_t x,register ptrdiff_t y,register ptrdiff_t ox,register ptrdiff_t oy)989 static ptrdiff_t relcost(register SCRN *t, register ptrdiff_t x, register ptrdiff_t y, register ptrdiff_t ox, register ptrdiff_t oy)
990 {
991 ptrdiff_t cost = 0;
992
993 /* If we don't know the cursor position, force use of absolute positioning */
994 if (oy == -1 || ox == -1)
995 return 10000;
996
997 /* First adjust row */
998 if (y > oy) {
999 ptrdiff_t dist = y - oy;
1000
1001 /* Have to go down */
1002 if (t->lf) {
1003 ptrdiff_t mult = dist * t->clf;
1004
1005 if (dist < 10 && t->cDO < mult)
1006 cost += t->cDO;
1007 else if (dist >= 10 && t->cDO + 1 < mult)
1008 cost += t->cDO + 1;
1009 else
1010 cost += mult;
1011 } else if (t->DO)
1012 if (dist < 10)
1013 cost += t->cDO;
1014 else
1015 cost += t->cDO + 1;
1016 else
1017 return 10000;
1018 } else if (y < oy) {
1019 ptrdiff_t dist = oy - y;
1020
1021 /* Have to go up */
1022 if (t->up) {
1023 ptrdiff_t mult = dist * t->cup;
1024
1025 if (dist < 10 && t->cUP < mult)
1026 cost += t->cUP;
1027 else if (dist >= 10 && t->cUP < mult)
1028 cost += t->cUP + 1;
1029 else
1030 cost += mult;
1031 } else if (t->UP)
1032 if (dist < 10)
1033 cost += t->cUP;
1034 else
1035 cost += t->cUP + 1;
1036 else
1037 return 10000;
1038 }
1039
1040 /* Now adjust column */
1041
1042 /* Use tabs */
1043 if (x > ox && t->ta) {
1044 ptrdiff_t dist = x - ox;
1045 ptrdiff_t ntabs = (dist + ox % t->tw) / t->tw;
1046 ptrdiff_t cstunder = x % t->tw + t->cta * ntabs;
1047 ptrdiff_t cstover;
1048
1049 if (x + t->tw < t->co && t->bs)
1050 cstover = t->cbs * (t->tw - x % t->tw) + t->cta * (ntabs + 1);
1051 else
1052 cstover = 10000;
1053 if (dist < 10 && cstunder < t->cRI && cstunder < x - ox && cstover > cstunder)
1054 return cost + cstunder;
1055 else if (cstunder < t->cRI + 1 && cstunder < x - ox && cstover > cstunder)
1056 return cost + cstunder;
1057 else if (dist < 10 && cstover < t->cRI && cstover < x - ox)
1058 return cost + cstover;
1059 else if (cstover < t->cRI + 1 && cstover < x - ox)
1060 return cost + cstover;
1061 } else if (x < ox && t->bt) {
1062 ptrdiff_t dist = ox - x;
1063 ptrdiff_t ntabs = (dist + t->tw - ox % t->tw) / t->tw;
1064 ptrdiff_t cstunder, cstover;
1065
1066 if (t->bs)
1067 cstunder = t->cbt * ntabs + t->cbs * (t->tw - x % t->tw);
1068 else
1069 cstunder = 10000;
1070 if (x - t->tw >= 0)
1071 cstover = t->cbt * (ntabs + 1) + x % t->tw;
1072 else
1073 cstover = 10000;
1074 if (dist < 10 && cstunder < t->cLE && (t->bs ? cstunder < (ox - x) * t->cbs : 1)
1075 && cstover > cstunder)
1076 return cost + cstunder;
1077 if (cstunder < t->cLE + 1 && (t->bs ? cstunder < (ox - x) * t->cbs : 1)
1078 && cstover > cstunder)
1079 return cost + cstunder;
1080 else if (dist < 10 && cstover < t->cRI && (t->bs ? cstover < (ox - x) * t->cbs : 1))
1081 return cost + cstover;
1082 else if (cstover < t->cRI + 1 && (t->bs ? cstover < (ox - x) * t->cbs : 1))
1083 return cost + cstover;
1084 }
1085
1086 /* Use simple motions */
1087 if (x < ox) {
1088 ptrdiff_t dist = ox - x;
1089
1090 /* Have to go left */
1091 if (t->bs) {
1092 ptrdiff_t mult = dist * t->cbs;
1093
1094 if (t->cLE < mult && dist < 10)
1095 cost += t->cLE;
1096 else if (t->cLE + 1 < mult)
1097 cost += t->cLE + 1;
1098 else
1099 cost += mult;
1100 } else if (t->LE)
1101 cost += t->cLE;
1102 else
1103 return 10000;
1104 } else if (x > ox) {
1105 ptrdiff_t dist = x - ox;
1106
1107 /* Have to go right */
1108 /* Hmm.. this should take into account possible attribute changes */
1109 if (t->cRI < dist && dist < 10)
1110 cost += t->cRI;
1111 else if (t->cRI + 1 < dist)
1112 cost += t->cRI + 1;
1113 else
1114 cost += dist;
1115 }
1116
1117 return cost;
1118 }
1119
1120 /* Find optimal set of cursor positioning commands to move from the current
1121 * cursor row and column (either or both of which might be unknown) to the
1122 * given new row and column and execute them.
1123 */
1124
cposs(register SCRN * t,register ptrdiff_t x,register ptrdiff_t y)1125 static void cposs(register SCRN *t, register ptrdiff_t x, register ptrdiff_t y)
1126 {
1127 register ptrdiff_t bestcost, cost;
1128 int bestway;
1129 ptrdiff_t hy;
1130 ptrdiff_t hl;
1131
1132 fixupcursor(t);
1133
1134 /* Home y position is usually 0, but it is 'top' if we have scrolling region
1135 * relative addressing
1136 */
1137 if (t->rr) {
1138 hy = t->top;
1139 hl = t->bot - 1;
1140 } else {
1141 hy = 0;
1142 hl = t->li - 1;
1143 }
1144
1145 /* Assume best way is with only using relative cursor positioning */
1146
1147 bestcost = relcost(t, x, y, t->x, t->y);
1148 bestway = 0;
1149
1150 /* Now check if combinations of absolute cursor positioning functions are
1151 * better (or necessary in case one or both cursor positions are unknown)
1152 */
1153
1154 if (t->ccm < bestcost) {
1155 cost = tcost(t->cap, t->cm, 1, y, x, 0, 0);
1156 if (cost < bestcost) {
1157 bestcost = cost;
1158 bestway = 6;
1159 }
1160 }
1161 if (t->ccr < bestcost) {
1162 cost = relcost(t, x, y, 0, t->y) + t->ccr;
1163 if (cost < bestcost) {
1164 bestcost = cost;
1165 bestway = 1;
1166 }
1167 }
1168 if (t->cho < bestcost) {
1169 cost = relcost(t, x, y, 0, hy) + t->cho;
1170 if (cost < bestcost) {
1171 bestcost = cost;
1172 bestway = 2;
1173 }
1174 }
1175 if (t->cll < bestcost) {
1176 cost = relcost(t, x, y, 0, hl) + t->cll;
1177 if (cost < bestcost) {
1178 bestcost = cost;
1179 bestway = 3;
1180 }
1181 }
1182 if (t->cch < bestcost && x != t->x) {
1183 cost = relcost(t, x, y, x, t->y) + tcost(t->cap, t->ch, 1, x, 0, 0, 0);
1184 if (cost < bestcost) {
1185 bestcost = cost;
1186 bestway = 4;
1187 }
1188 }
1189 if (t->ccv < bestcost && y != t->y) {
1190 cost = relcost(t, x, y, t->x, y) + tcost(t->cap, t->cv, 1, y, 0, 0, 0);
1191 if (cost < bestcost) {
1192 bestcost = cost;
1193 bestway = 5;
1194 }
1195 }
1196 if (t->ccV < bestcost) {
1197 cost = relcost(t, x, y, 0, y) + tcost(t->cap, t->cV, 1, y, 0, 0, 0);
1198 if (cost < bestcost) {
1199 bestcost = cost;
1200 bestway = 13;
1201 }
1202 }
1203 if (t->cch + t->ccv < bestcost && x != t->x && y != t->y) {
1204 cost = tcost(t->cap, t->cv, 1, y - hy, 0, 0, 0) + tcost(t->cap, t->ch, 1, x, 0, 0, 0);
1205 if (cost < bestcost) {
1206 bestcost = cost;
1207 bestway = 7;
1208 }
1209 }
1210 if (t->ccv + t->ccr < bestcost && y != t->y) {
1211 cost = tcost(t->cap, t->cv, 1, y, 0, 0, 0) + tcost(t->cap, t->cr, 1, 0, 0, 0, 0) + relcost(t, x, y, 0, y);
1212 if (cost < bestcost) {
1213 bestcost = cost;
1214 bestway = 8;
1215 }
1216 }
1217 if (t->cll + t->cch < bestcost) {
1218 cost = tcost(t->cap, t->ll, 1, 0, 0, 0, 0) + tcost(t->cap, t->ch, 1, x, 0, 0, 0) + relcost(t, x, y, x, hl);
1219 if (cost < bestcost) {
1220 bestcost = cost;
1221 bestway = 9;
1222 }
1223 }
1224 if (t->cll + t->ccv < bestcost) {
1225 cost = tcost(t->cap, t->ll, 1, 0, 0, 0, 0) + tcost(t->cap, t->cv, 1, y, 0, 0, 0) + relcost(t, x, y, 0, y);
1226 if (cost < bestcost) {
1227 bestcost = cost;
1228 bestway = 10;
1229 }
1230 }
1231 if (t->cho + t->cch < bestcost) {
1232 cost = tcost(t->cap, t->ho, 1, 0, 0, 0, 0) + tcost(t->cap, t->ch, 1, x, 0, 0, 0) + relcost(t, x, y, x, hy);
1233 if (cost < bestcost) {
1234 bestcost = cost;
1235 bestway = 11;
1236 }
1237 }
1238 if (t->cho + t->ccv < bestcost) {
1239 cost = tcost(t->cap, t->ho, 1, 0, 0, 0, 0) + tcost(t->cap, t->cv, 1, y, 0, 0, 0) + relcost(t, x, y, 0, y);
1240 if (cost < bestcost) {
1241 bestcost = cost;
1242 bestway = 12;
1243 }
1244 }
1245
1246 /* Do absolute cursor positioning if we don't know the cursor position or
1247 * if it is faster than doing only relative cursor positioning
1248 */
1249
1250 switch (bestway) {
1251 case 1:
1252 texec(t->cap, t->cr, 1, 0, 0, 0, 0);
1253 t->x = 0;
1254 break;
1255 case 2:
1256 texec(t->cap, t->ho, 1, 0, 0, 0, 0);
1257 t->x = 0;
1258 t->y = hy;
1259 break;
1260 case 3:
1261 texec(t->cap, t->ll, 1, 0, 0, 0, 0);
1262 t->x = 0;
1263 t->y = hl;
1264 break;
1265 case 9:
1266 texec(t->cap, t->ll, 1, 0, 0, 0, 0);
1267 t->x = 0;
1268 t->y = hl;
1269 goto doch;
1270 case 11:
1271 texec(t->cap, t->ho, 1, 0, 0, 0, 0);
1272 t->x = 0;
1273 t->y = hy;
1274 doch:
1275 case 4:
1276 texec(t->cap, t->ch, 1, x, 0, 0, 0);
1277 t->x = x;
1278 break;
1279 case 10:
1280 texec(t->cap, t->ll, 1, 0, 0, 0, 0);
1281 t->x = 0;
1282 t->y = hl;
1283 goto docv;
1284 case 12:
1285 texec(t->cap, t->ho, 1, 0, 0, 0, 0);
1286 t->x = 0;
1287 t->y = hy;
1288 goto docv;
1289 case 8:
1290 texec(t->cap, t->cr, 1, 0, 0, 0, 0);
1291 t->x = 0;
1292 docv:
1293 case 5:
1294 texec(t->cap, t->cv, 1, y, 0, 0, 0);
1295 t->y = y;
1296 break;
1297 case 6:
1298 texec(t->cap, t->cm, 1, y, x, 0, 0);
1299 t->y = y;
1300 t->x = x;
1301 break;
1302 case 7:
1303 texec(t->cap, t->cv, 1, y, 0, 0, 0);
1304 t->y = y;
1305 texec(t->cap, t->ch, 1, x, 0, 0, 0);
1306 t->x = x;
1307 break;
1308 case 13:
1309 texec(t->cap, t->cV, 1, y, 0, 0, 0);
1310 t->y = y;
1311 t->x = 0;
1312 break;
1313 }
1314
1315 /* Use relative cursor position functions if we're not there yet */
1316
1317 /* First adjust row */
1318 if (y > t->y) {
1319 /* Have to go down */
1320 if (!t->lf || t->cDO < (y - t->y) * t->clf) {
1321 texec(t->cap, t->DO, 1, y - t->y, 0, 0, 0);
1322 t->y = y;
1323 } else
1324 while (y > t->y) {
1325 texec(t->cap, t->lf, 1, 0, 0, 0, 0);
1326 ++t->y;
1327 }
1328 } else if (y < t->y) {
1329 /* Have to go up */
1330 if (!t->up || t->cUP < (t->y - y) * t->cup) {
1331 texec(t->cap, t->UP, 1, t->y - y, 0, 0, 0);
1332 t->y = y;
1333 } else
1334 while (y < t->y) {
1335 texec(t->cap, t->up, 1, 0, 0, 0, 0);
1336 --t->y;
1337 }
1338 }
1339
1340 /* Use tabs */
1341 if (x > t->x && t->ta) {
1342 ptrdiff_t ntabs = (x - t->x + t->x % t->tw) / t->tw;
1343 ptrdiff_t cstunder = x % t->tw + t->cta * ntabs;
1344 ptrdiff_t cstover;
1345
1346 if (x + t->tw < t->co && t->bs)
1347 cstover = t->cbs * (t->tw - x % t->tw) + t->cta * (ntabs + 1);
1348 else
1349 cstover = 10000;
1350 if (cstunder < t->cRI && cstunder < x - t->x && cstover > cstunder) {
1351 if (ntabs) {
1352 t->x = x - x % t->tw;
1353 do {
1354 texec(t->cap, t->ta, 1, 0, 0, 0, 0);
1355 } while (--ntabs);
1356 }
1357 } else if (cstover < t->cRI && cstover < x - t->x) {
1358 t->x = t->tw + x - x % t->tw;
1359 ++ntabs;
1360 do {
1361 texec(t->cap, t->ta, 1, 0, 0, 0, 0);
1362 } while (--ntabs);
1363 }
1364 } else if (x < t->x && t->bt) {
1365 ptrdiff_t ntabs = ((t->x + t->tw - 1) - (t->x + t->tw - 1) % t->tw - ((x + t->tw - 1) - (x + t->tw - 1) % t->tw)) / t->tw;
1366 ptrdiff_t cstunder, cstover;
1367
1368 if (t->bs)
1369 cstunder = t->cbt * ntabs + t->cbs * (t->tw - x % t->tw);
1370 else
1371 cstunder = 10000;
1372 if (x - t->tw >= 0)
1373 cstover = t->cbt * (ntabs + 1) + x % t->tw;
1374 else
1375 cstover = 10000;
1376 if (cstunder < t->cLE && (t->bs ? cstunder < (t->x - x) * t->cbs : 1)
1377 && cstover > cstunder) {
1378 if (ntabs) {
1379 do {
1380 texec(t->cap, t->bt, 1, 0, 0, 0, 0);
1381 } while (--ntabs);
1382 t->x = x + t->tw - x % t->tw;
1383 }
1384 } else if (cstover < t->cRI && (t->bs ? cstover < (t->x - x) * t->cbs : 1)) {
1385 t->x = x - x % t->tw;
1386 ++ntabs;
1387 do {
1388 texec(t->cap, t->bt, 1, 0, 0, 0, 0);
1389 } while (--ntabs);
1390 }
1391 }
1392
1393 /* Now adjust column */
1394 if (x < t->x) {
1395 /* Have to go left */
1396 if (!t->bs || t->cLE < (t->x - x) * t->cbs) {
1397 texec(t->cap, t->LE, 1, t->x - x, 0, 0, 0);
1398 t->x = x;
1399 } else
1400 while (x < t->x) {
1401 texec(t->cap, t->bs, 1, 0, 0, 0, 0);
1402 --t->x;
1403 }
1404 } else if (x > t->x) {
1405 /* Have to go right */
1406 /* Hmm.. this should take into account possible attribute changes */
1407 if (x-t->x>1 && t->RI) {
1408 texec(t->cap, t->RI, 1, x - t->x, 0, 0, 0);
1409 t->x = x;
1410 } else {
1411 while(x>t->x) {
1412 texec(t->cap, t->nd, 1, 0, 0, 0, 0);
1413 ++t->x;
1414 }
1415 }
1416
1417 /* if (t->cRI < x - t->x) { */
1418 /* } else {
1419 int *s = t->scrn + t->x + t->y * t->co;
1420 int *a = t->attr + t->x + t->y * t->co;
1421
1422 if (t->ins)
1423 clrins(t);
1424 while (x > t->x) {
1425 int atr, c;
1426 if(*s==-1) c=' ', atr=0;
1427 else c= *s, atr= *a;
1428
1429 if (atr != t->attrib)
1430 set_attr(t, atr);
1431 utf8_putc(c);
1432 ++s;
1433 ++a;
1434 ++t->x;
1435 }
1436 }
1437 */
1438 }
1439 }
1440
cpos(register SCRN * t,register ptrdiff_t x,register ptrdiff_t y)1441 int cpos(register SCRN *t, register ptrdiff_t x, register ptrdiff_t y)
1442 {
1443 /* Move cursor quickly if we can */
1444 if (y == t->y) {
1445 if (x > t->x && x - t->x < 4 && !t->ins) {
1446 int (*cs)[COMPOSE] = t->scrn + t->x + t->co * t->y;
1447 int *as = t->attr + t->x + t->co * t->y;
1448 do {
1449 /* We used to space over unknown chars, but they now could be
1450 the right half of a UTF-8 two column character, so we can't.
1451 Also do not try to emit utf-8 sequences here. */
1452 if((*cs)[0]<32 || (*cs)[0]>=127)
1453 break;
1454 /* Also ignore if we would have to emit combining characters */
1455 if ((*cs)[1])
1456 break;
1457
1458 if (*as != t->attrib)
1459 set_attr(t, *as);
1460
1461 ttputc(TO_CHAR_OK((*cs)[0]));
1462
1463 ++cs;
1464 ++as;
1465 ++t->x;
1466
1467 } while (x != t->x);
1468 }
1469 if (x == t->x)
1470 return 0;
1471 }
1472
1473 /* We used to reset background if t->ut was set before a cursor motion. This
1474 should not be necessary and makes screen update slower, especially with syntax
1475 highlighting and/or color scheme. */
1476 if ((!t->ms && t->attrib & (INVERSE | UNDERLINE | BG_NOT_DEFAULT)) /* ||
1477 (t->ut && (t->attrib & BG_NOT_DEFAULT)) */)
1478 set_attr(t, t->attrib & ~(INVERSE | UNDERLINE | BG_MASK));
1479
1480 /* Should be in cposs */
1481 if (y < t->top || y >= t->bot)
1482 setregn(t, 0, t->li);
1483
1484 cposs(t, x, y);
1485 return 0;
1486 }
1487
1488 /* Insert/Delete within line */
1489 /* FIXME: doesn't know about attr */
1490 #if 0
1491
1492 /* Enter insert mode */
1493
1494 static void setins(SCRN *t, ptrdiff_t x)
1495 {
1496 if (t->ins != 1 && t->im) {
1497 t->ins = 1;
1498 texec(t->cap, t->im, 1, x, 0, 0, 0);
1499 }
1500 }
1501
1502 /* As above but usable in insert mode */
1503 /* The cursor position must already be correct */
1504
1505 static void outatri(SCRN *t, ptrdiff_t x, ptrdiff_t y, int c, int a)
1506 {
1507 /*
1508 if (c == -1)
1509 c = ' ';
1510 if (a != t->attrib)
1511 set_attr(t, a);
1512 if (t->haz && c == '~')
1513 c = '\\';
1514 utf8_putc(c);
1515 t->x+=joe_wcwidth(1,c);
1516 */
1517 /* ++t->x; */
1518 }
1519
1520
1521 static void doinschr(SCRN *t, ptrdiff_t x, ptrdiff_t y, int (*s)[COMPOSE], int *as, ptrdiff_t n)
1522 {
1523 ptrdiff_t a;
1524
1525 if (x < 0) {
1526 s -= x;
1527 as -= x;
1528 x = 0;
1529 }
1530 if (x >= t->co || n <= 0)
1531 return;
1532 if (t->im || t->ic || t->IC) {
1533 cpos(t, x, y);
1534 if ((n == 1 && t->ic) || !t->IC) {
1535 if (!t->ic)
1536 setins(t, x);
1537 for (a = 0; a != n; ++a) {
1538 texec(t->cap, t->ic, 1, x, 0, 0, 0);
1539 outatri(t, x + a, y, s[a][0], as[a]);
1540 texec(t->cap, t->ip, 1, x, 0, 0, 0);
1541 }
1542 if (!t->mi)
1543 clrins(t);
1544 } else {
1545 texec(t->cap, t->IC, 1, n, 0, 0, 0);
1546 for (a = 0; a != n; ++a)
1547 outatri(t, x + a, y, s[a][0], as[a]);
1548 }
1549 }
1550 mmove(t->scrn + x + t->co * y + n, t->scrn + x + t->co * y, (t->co - (x + n)) * SIZEOF(int [COMPOSE]));
1551 mmove(t->attr + x + t->co * y + n, t->attr + x + t->co * y, (t->co - (x + n)) * SIZEOF(int));
1552 mmove(t->scrn + x + t->co * y, s, n * SIZEOF(int [COMPOSE]));
1553 mmove(t->attr + x + t->co * y, s, n * SIZEOF(int));
1554 }
1555
1556 static void dodelchr(SCRN *t, ptrdiff_t x, ptrdiff_t y, ptrdiff_t n)
1557 {
1558 ptrdiff_t a;
1559
1560 if (x < 0)
1561 x = 0;
1562 if (!n || x >= t->co)
1563 return;
1564 if (t->dc || t->DC) {
1565 cpos(t, x, y);
1566 texec(t->cap, t->dm, 1, x, 0, 0, 0); /* Enter delete mode */
1567 if ((n == 1 && t->dc) || !t->DC)
1568 for (a = n; a; --a)
1569 texec(t->cap, t->dc, 1, x, 0, 0, 0);
1570 else
1571 texec(t->cap, t->DC, 1, n, 0, 0, 0);
1572 texec(t->cap, t->ed, 1, x, 0, 0, 0); /* Exit delete mode */
1573 }
1574 mmove(t->scrn + t->co * y + x, t->scrn + t->co * y + x + n, (t->co - (x + n)) * SIZEOF(int [COMPOSE]));
1575 mmove(t->attr + t->co * y + x, t->attr + t->co * y + x + n, (t->co - (x + n)) * SIZEOF(int));
1576 mfill(t->scrn + t->co * y + t->co - n, ' ', n);
1577 msetI(t->attr + t->co * y + t->co - n, (t->attrib & FG_MASK), n);
1578 }
1579
1580 void magic(SCRN *t, ptrdiff_t y, int (*cs)[COMPOSE], int *ca,int *s, int *a, ptrdiff_t placex)
1581 {
1582 struct hentry *htab = t->htab;
1583 ptrdiff_t *ofst = t->ofst;
1584 int aryx = 1;
1585 ptrdiff_t x;
1586
1587 if (!(t->im || t->ic || t->IC) || !(t->dc || t->DC))
1588 return;
1589 mset((char *)htab, 0, 256 * SIZEOF(struct hentry));
1590
1591 msetD(ofst, 0, t->co);
1592
1593 /* Build hash table */
1594 for (x = 0; x != t->co; ++x) {
1595 t->ary[aryx].next = htab[cs[x] & 255].next;
1596 t->ary[aryx].loc = x;
1597 ++htab[cs[x] & 255].loc;
1598 htab[cs[x] & 255].next = aryx++;
1599 }
1600
1601 /* Build offset table */
1602 for (x = 0; x < t->co;)
1603 if (htab[s[x] & 255].loc >= 15)
1604 ofst[x++] = t->co;
1605 else {
1606 ptrdiff_t aryy;
1607 ptrdiff_t maxaryy = 0;
1608 ptrdiff_t maxlen = 0;
1609 ptrdiff_t best = 0;
1610 ptrdiff_t bestback = 0;
1611 ptrdiff_t z;
1612
1613 for (aryy = htab[s[x] & 255].next; aryy; aryy = t->ary[aryy].next) {
1614 ptrdiff_t amnt, back;
1615 ptrdiff_t tsfo = t->ary[aryy].loc - x;
1616 ptrdiff_t cst = tsfo < 0 ? -tsfo : tsfo;
1617 ptrdiff_t pre = 32;
1618
1619 for (amnt = 0; x + amnt < t->co && x + tsfo + amnt < t->co; ++amnt) {
1620 if (cs[x + tsfo + amnt] != s[x + amnt])
1621 break;
1622 else if ((s[x + amnt] & 255) != 32 || pre != 32)
1623 ++cst;
1624 pre = s[x + amnt] & 255;
1625 }
1626 pre = 32;
1627 for (back = 0; back + x > 0 && back + tsfo + x > 0; --back) {
1628 if (cs[x + tsfo + back - 1] != s[x + back - 1])
1629 break;
1630 else if ((s[x + back - 1] & 255) != 32 || pre != 32)
1631 ++cst;
1632 pre = s[x + back - 1] & 255;
1633 }
1634 if (cst > best) {
1635 maxaryy = aryy;
1636 maxlen = amnt;
1637 best = cst;
1638 bestback = back;
1639 }
1640 }
1641 if (!maxlen) {
1642 ofst[x] = t->co;
1643 maxlen = 1;
1644 } else if (best < 2)
1645 for (z = 0; z != maxlen; ++z)
1646 ofst[x + z] = t->co;
1647 else
1648 for (z = 0; z != maxlen - bestback; ++z)
1649 ofst[x + z + bestback] = t->ary[maxaryy].loc - x;
1650 x += maxlen;
1651 }
1652
1653 /* Apply scrolling commands */
1654
1655 for (x = 0; x != t->co; ++x) {
1656 ptrdiff_t q = ofst[x];
1657
1658 if (q && q != t->co) {
1659 if (q > 0) {
1660 ptrdiff_t z, fu;
1661
1662 for (z = x; z != t->co && ofst[z] == q; ++z) ;
1663 while (s[x] == cs[x] && x < placex)
1664 ++x;
1665 dodelchr(t, x, y, q);
1666 for (fu = x; fu != t->co; ++fu)
1667 if (ofst[fu] != t->co)
1668 ofst[fu] -= q;
1669 x = z - 1;
1670 } else {
1671 ptrdiff_t z, fu;
1672
1673 for (z = x; z != t->co && ofst[z] == q; ++z) ;
1674 while (s[x + q] == cs[x + q] && x - q < placex)
1675 ++x;
1676 doinschr(t, x + q, y, s + x + q, a + x + q, -q);
1677 for (fu = x; fu != t->co; ++fu)
1678 if (ofst[fu] != t->co)
1679 ofst[fu] -= q;
1680 x = z - 1;
1681 }
1682 }
1683 }
1684 }
1685 #endif
1686
doupscrl(SCRN * t,ptrdiff_t top,ptrdiff_t bot,ptrdiff_t amnt,int atr)1687 static void doupscrl(SCRN *t, ptrdiff_t top, ptrdiff_t bot, ptrdiff_t amnt, int atr)
1688 {
1689 ptrdiff_t a = amnt;
1690
1691 if (!amnt)
1692 return;
1693 set_attr(t, atr);
1694 if (top == 0 && bot == t->li && (t->sf || t->SF)) {
1695 setregn(t, 0, t->li);
1696 cpos(t, 0, t->li - 1);
1697 if ((amnt == 1 && t->sf) || !t->SF)
1698 while (a--)
1699 texec(t->cap, t->sf, 1, t->li - 1, 0, 0, 0);
1700 else
1701 texec(t->cap, t->SF, a, a, 0, 0, 0);
1702 goto done;
1703 }
1704 if (bot == t->li && (t->dl || t->DL)) {
1705 setregn(t, 0, t->li);
1706 cpos(t, 0, top);
1707 if ((amnt == 1 && t->dl) || !t->DL)
1708 while (a--)
1709 texec(t->cap, t->dl, 1, top, 0, 0, 0);
1710 else
1711 texec(t->cap, t->DL, a, a, 0, 0, 0);
1712 goto done;
1713 }
1714 if (t->cs && (t->sf || t->SF)) {
1715 setregn(t, top, bot);
1716 cpos(t, 0, bot - 1);
1717 if ((amnt == 1 && t->sf) || !t->SF)
1718 while (a--)
1719 texec(t->cap, t->sf, 1, bot - 1, 0, 0, 0);
1720 else
1721 texec(t->cap, t->SF, a, a, 0, 0, 0);
1722 goto done;
1723 }
1724 if ((t->dl || t->DL) && (t->al || t->AL)) {
1725 cpos(t, 0, top);
1726 if ((amnt == 1 && t->dl) || !t->DL)
1727 while (a--)
1728 texec(t->cap, t->dl, 1, top, 0, 0, 0);
1729 else
1730 texec(t->cap, t->DL, a, a, 0, 0, 0);
1731 a = amnt;
1732 cpos(t, 0, bot - amnt);
1733 if ((amnt == 1 && t->al) || !t->AL)
1734 while (a--)
1735 texec(t->cap, t->al, 1, bot - amnt, 0, 0, 0);
1736 else
1737 texec(t->cap, t->AL, a, a, 0, 0, 0);
1738 goto done;
1739 }
1740 msetI(t->updtab + top, 1, bot - top);
1741 return;
1742
1743 done:
1744 mmove(t->scrn + top * t->co, t->scrn + (top + amnt) * t->co, (bot - top - amnt) * t->co * SIZEOF(int [COMPOSE]));
1745 mmove(t->attr + top * t->co, t->attr + (top + amnt) * t->co, (bot - top - amnt) * t->co * SIZEOF(int));
1746
1747 if (bot == t->li && t->db) {
1748 mfill(t->scrn + (t->li - amnt) * t->co, -1, amnt * t->co);
1749 msetI(t->attr + (t->li - amnt) * t->co, 0, amnt * t->co);
1750 msetI(t->updtab + t->li - amnt, 1, amnt);
1751 } else {
1752 mfill(t->scrn + (bot - amnt) * t->co, ' ', amnt * t->co);
1753 msetI(t->attr + (bot - amnt) * t->co, 0, amnt * t->co);
1754 }
1755 }
1756
dodnscrl(SCRN * t,ptrdiff_t top,ptrdiff_t bot,ptrdiff_t amnt,int atr)1757 static void dodnscrl(SCRN *t, ptrdiff_t top, ptrdiff_t bot, ptrdiff_t amnt, int atr)
1758 {
1759 ptrdiff_t a = amnt;
1760
1761 if (!amnt)
1762 return;
1763 set_attr(t, atr);
1764 if (top == 0 && bot == t->li && (t->sr || t->SR)) {
1765 setregn(t, 0, t->li);
1766 cpos(t, 0, 0);
1767 if ((amnt == 1 && t->sr) || !t->SR)
1768 while (a--)
1769 texec(t->cap, t->sr, 1, 0, 0, 0, 0);
1770 else
1771 texec(t->cap, t->SR, a, a, 0, 0, 0);
1772 goto done;
1773 }
1774 if (bot == t->li && (t->al || t->AL)) {
1775 setregn(t, 0, t->li);
1776 cpos(t, 0, top);
1777 if ((amnt == 1 && t->al) || !t->AL)
1778 while (a--)
1779 texec(t->cap, t->al, 1, top, 0, 0, 0);
1780 else
1781 texec(t->cap, t->AL, a, a, 0, 0, 0);
1782 goto done;
1783 }
1784 if (t->cs && (t->sr || t->SR)) {
1785 setregn(t, top, bot);
1786 cpos(t, 0, top);
1787 if ((amnt == 1 && t->sr) || !t->SR)
1788 while (a--)
1789 texec(t->cap, t->sr, 1, top, 0, 0, 0);
1790 else
1791 texec(t->cap, t->SR, a, a, 0, 0, 0);
1792 goto done;
1793 }
1794 if ((t->dl || t->DL) && (t->al || t->AL)) {
1795 cpos(t, 0, bot - amnt);
1796 if ((amnt == 1 && t->dl) || !t->DL)
1797 while (a--)
1798 texec(t->cap, t->dl, 1, bot - amnt, 0, 0, 0);
1799 else
1800 texec(t->cap, t->DL, a, a, 0, 0, 0);
1801 a = amnt;
1802 cpos(t, 0, top);
1803 if ((amnt == 1 && t->al) || !t->AL)
1804 while (a--)
1805 texec(t->cap, t->al, 1, top, 0, 0, 0);
1806 else
1807 texec(t->cap, t->AL, a, a, 0, 0, 0);
1808 goto done;
1809 }
1810 msetI(t->updtab + top, 1, bot - top);
1811 return;
1812 done:
1813 mmove(t->scrn + (top + amnt) * t->co, t->scrn + top * t->co, (bot - top - amnt) * t->co * SIZEOF(int [COMPOSE]));
1814 mmove(t->attr + (top + amnt) * t->co, t->attr + top * t->co, (bot - top - amnt) * t->co * SIZEOF(int));
1815
1816 if (!top && t->da) {
1817 mfill(t->scrn, -1, amnt * t->co);
1818 msetI(t->attr, 0, amnt * t->co);
1819 msetI(t->updtab, 1, amnt);
1820 } else {
1821 mfill(t->scrn + t->co * top, ' ', amnt * t->co);
1822 msetI(t->attr + t->co * top, 0, amnt * t->co);
1823 }
1824 }
1825
nscroll(SCRN * t,int atr)1826 void nscroll(SCRN *t,int atr)
1827 {
1828 ptrdiff_t y, z, q, r, p;
1829
1830 for (y = 0; y != t->li; ++y) {
1831 q = t->sary[y];
1832 if (ifhave)
1833 return;
1834 if (q && q != t->li) {
1835 if (q > 0) {
1836 for (z = y; z != t->li && t->sary[z] == q; ++z)
1837 t->sary[z] = 0;
1838 doupscrl(t, y, z + q, q, atr);
1839 y = z - 1;
1840 } else {
1841 for (r = y; r != t->li && (t->sary[r] < 0 || t->sary[r] == t->li); ++r) ;
1842 p = r - 1;
1843 do {
1844 q = t->sary[p];
1845 if (q && q != t->li) {
1846 for (z = p; t->sary[z] = 0, (z && t->sary[z - 1] == q); --z) ;
1847 dodnscrl(t, z + q, p + 1, -q, atr);
1848 p = z + 1;
1849 }
1850 } while (p-- != y);
1851 y = r - 1;
1852 }
1853 }
1854 }
1855 msetD(t->sary, 0, t->li);
1856 }
1857
npartial(SCRN * t)1858 void npartial(SCRN *t)
1859 {
1860 set_attr(t, BG_COLOR(bg_text));
1861 clrins(t);
1862 setregn(t, 0, t->li);
1863 }
1864
nescape(SCRN * t)1865 void nescape(SCRN *t)
1866 {
1867 mouseclose();
1868 npartial(t);
1869 cpos(t, 0, t->li - 1);
1870 eraeol(t, 0, t->li - 1, 0);
1871 if (t->bre)
1872 texec(t->cap, t->bre, 1, 0, 0, 0, 0);
1873 if (t->te)
1874 texec(t->cap, t->te, 1, 0, 0, 0, 0);
1875 }
1876
nreturn(SCRN * t)1877 void nreturn(SCRN *t)
1878 {
1879 mouseopen();
1880 if (t->ti)
1881 texec(t->cap, t->ti, 1, 0, 0, 0, 0);
1882 if (!skiptop && t->cl)
1883 texec(t->cap, t->cl, 1, 0, 0, 0, 0);
1884 if (t->brp)
1885 texec(t->cap, t->brp, 1, 0, 0, 0, 0);
1886 nredraw(t);
1887 }
1888
nclose(SCRN * t)1889 void nclose(SCRN *t)
1890 {
1891 mouseclose();
1892 leave = 1;
1893 set_attr(t, 0);
1894 clrins(t);
1895 setregn(t, 0, t->li);
1896 cpos(t, 0, t->li - 1);
1897 if (t->bre)
1898 texec(t->cap, t->bre, 1, 0, 0, 0, 0);
1899 if (t->te)
1900 texec(t->cap, t->te, 1, 0, 0, 0, 0);
1901 ttclose();
1902 rmcap(t->cap);
1903 joe_free(t->scrn);
1904 joe_free(t->attr);
1905 joe_free(t->sary);
1906 joe_free(t->ofst);
1907 joe_free(t->htab);
1908 joe_free(t->ary);
1909 joe_free(t);
1910 }
1911
nscrldn(SCRN * t,ptrdiff_t top,ptrdiff_t bot,ptrdiff_t amnt)1912 void nscrldn(SCRN *t, ptrdiff_t top, ptrdiff_t bot, ptrdiff_t amnt)
1913 {
1914 ptrdiff_t x;
1915
1916 if (!amnt || top >= bot || bot > t->li)
1917 return;
1918 if ((amnt < bot - top && bot - top - amnt < amnt / 2) || !t->scroll)
1919 amnt = bot - top;
1920 if (amnt < bot - top) {
1921 for (x = bot; x != top + amnt; --x) {
1922 t->sary[x - 1] = (t->sary[x - amnt - 1] == t->li ? t->li : t->sary[x - amnt - 1] - amnt);
1923 t->updtab[x - 1] = t->updtab[x - amnt - 1];
1924 }
1925 for (x = top; x != top + amnt; ++x) {
1926 t->updtab[x] = 1;
1927 }
1928 }
1929 if (amnt > bot - top)
1930 amnt = bot - top;
1931 msetD(t->sary + top, t->li, amnt);
1932 if (amnt == bot - top) {
1933 msetI(t->updtab + top, 1, amnt);
1934 }
1935 }
1936
nscrlup(SCRN * t,ptrdiff_t top,ptrdiff_t bot,ptrdiff_t amnt)1937 void nscrlup(SCRN *t, ptrdiff_t top, ptrdiff_t bot, ptrdiff_t amnt)
1938 {
1939 ptrdiff_t x;
1940
1941 if (!amnt || top >= bot || bot > t->li)
1942 return;
1943 if ((amnt < bot - top && bot - top - amnt < amnt / 2) || !t->scroll)
1944 amnt = bot - top;
1945 if (amnt < bot - top) {
1946 for (x = top + amnt; x != bot; ++x) {
1947 t->sary[x - amnt] = (t->sary[x] == t->li ? t->li : t->sary[x] + amnt);
1948 t->updtab[x - amnt] = t->updtab[x];
1949 }
1950 for (x = bot - amnt; x != bot; ++x) {
1951 t->updtab[x] = 1;
1952 }
1953 }
1954 if (amnt > bot - top)
1955 amnt = bot - top;
1956 msetD(t->sary + bot - amnt, t->li, amnt);
1957 if (amnt == bot - top) {
1958 msetI(t->updtab + bot - amnt, 1, amnt);
1959 }
1960 }
1961
nredraw(SCRN * t)1962 void nredraw(SCRN *t)
1963 {
1964 dostaupd = 1;
1965 mfill(t->scrn, ' ', t->co * skiptop);
1966 msetI(t->attr, BG_COLOR(bg_text), t->co * skiptop);
1967 mfill(t->scrn + skiptop * t->co, -1, (t->li - skiptop) * t->co);
1968 msetI(t->attr + skiptop * t->co, BG_COLOR(bg_text), (t->li - skiptop) * t->co);
1969 msetD(t->sary, 0, t->li);
1970 msetI(t->updtab + skiptop, -1, t->li - skiptop);
1971 t->x = -1;
1972 t->y = -1;
1973 t->top = t->li;
1974 t->bot = 0;
1975 t->attrib = -1;
1976 t->ins = -1;
1977 set_attr(t, BG_COLOR(bg_text));
1978 clrins(t);
1979 setregn(t, 0, t->li);
1980
1981 if (!skiptop) {
1982 #if 0 // Leave screen contents invalid. This way line update emits explicit spaces.
1983 if (t->cl) {
1984 texec(t->cap, t->cl, 1, 0, 0, 0, 0);
1985 t->x = 0;
1986 t->y = 0;
1987 mfill(t->scrn, ' ', t->li * t->co);
1988 msetI(t->attr, BG_COLOR(bg_text), t->li * t->co);
1989 } else if (t->cd) {
1990 cpos(t, 0, 0);
1991 texec(t->cap, t->cd, 1, 0, 0, 0, 0);
1992 mfill(t->scrn, ' ', t->li * t->co);
1993 msetI(t->attr, BG_COLOR(bg_text), t->li * t->co);
1994 }
1995 #endif
1996 }
1997 }
1998
1999 /* Convert color/attribute name into internal code */
2000
meta_color_single(const char * s)2001 static int meta_color_single(const char *s)
2002 {
2003 if(!zcmp(s,"inverse"))
2004 return INVERSE;
2005 else if(!zcmp(s,"underline"))
2006 return UNDERLINE;
2007 else if(!zcmp(s,"bold"))
2008 return BOLD;
2009 else if(!zcmp(s,"blink"))
2010 return BLINK;
2011 else if(!zcmp(s,"dim"))
2012 return DIM;
2013 else if(!zcmp(s,"italic"))
2014 return ITALIC;
2015 else if(!zcmp(s,"dunderline"))
2016 return DOUBLE_UNDERLINE;
2017 else if(!zcmp(s,"stricken"))
2018 return CROSSED_OUT;
2019
2020 /* ISO colors */
2021 else if(!zcmp(s,"white"))
2022 return FG_WHITE;
2023 else if(!zcmp(s,"cyan"))
2024 return FG_CYAN;
2025 else if(!zcmp(s,"magenta"))
2026 return FG_MAGENTA;
2027 else if(!zcmp(s,"blue"))
2028 return FG_BLUE;
2029 else if(!zcmp(s,"yellow"))
2030 return FG_YELLOW;
2031 else if(!zcmp(s,"green"))
2032 return FG_GREEN;
2033 else if(!zcmp(s,"red"))
2034 return FG_RED;
2035 else if(!zcmp(s,"black"))
2036 return FG_BLACK;
2037 else if(!zcmp(s,"bg_white"))
2038 return BG_WHITE;
2039 else if(!zcmp(s,"bg_cyan"))
2040 return BG_CYAN;
2041 else if(!zcmp(s,"bg_magenta"))
2042 return BG_MAGENTA;
2043 else if(!zcmp(s,"bg_blue"))
2044 return BG_BLUE;
2045 else if(!zcmp(s,"bg_yellow"))
2046 return BG_YELLOW;
2047 else if(!zcmp(s,"bg_green"))
2048 return BG_GREEN;
2049 else if(!zcmp(s,"bg_red"))
2050 return BG_RED;
2051 else if(!zcmp(s,"bg_black"))
2052 return BG_BLACK;
2053
2054 /* 16 color xterm support: codes 8 - 15 are brighter versions of above */
2055 else if(!zcmp(s,"WHITE"))
2056 return FG_BWHITE;
2057 else if(!zcmp(s,"CYAN"))
2058 return FG_BCYAN;
2059 else if(!zcmp(s,"MAGENTA"))
2060 return FG_BMAGENTA;
2061 else if(!zcmp(s,"BLUE"))
2062 return FG_BBLUE;
2063 else if(!zcmp(s,"YELLOW"))
2064 return FG_BYELLOW;
2065 else if(!zcmp(s,"GREEN"))
2066 return FG_BGREEN;
2067 else if(!zcmp(s,"RED"))
2068 return FG_BRED;
2069 else if(!zcmp(s,"BLACK"))
2070 return FG_BBLACK;
2071 else if(!zcmp(s,"bg_WHITE"))
2072 return BG_BWHITE;
2073 else if(!zcmp(s,"bg_CYAN"))
2074 return BG_BCYAN;
2075 else if(!zcmp(s,"bg_MAGENTA"))
2076 return BG_BMAGENTA;
2077 else if(!zcmp(s,"bg_BLUE"))
2078 return BG_BBLUE;
2079 else if(!zcmp(s,"bg_YELLOW"))
2080 return BG_BYELLOW;
2081 else if(!zcmp(s,"bg_GREEN"))
2082 return BG_BGREEN;
2083 else if(!zcmp(s,"bg_RED"))
2084 return BG_BRED;
2085 else if(!zcmp(s,"bg_BLACK"))
2086 return BG_BBLACK;
2087
2088 /* Look at the "256colres.pl" PERL script in the xterm source
2089 distribution to see how these work. */
2090
2091 /* 256 color xterm support: bg_RGB and fg_RGB, where R, G, and B range from 0 - 5 */
2092 /* Codes 16 - 231 are a 6x6x6 color cube */
2093 else if(s[0]=='f' && s[1]=='g' && s[2]=='_' &&
2094 s[3]>='0' && s[3]<='5' &&
2095 s[4]>='0' && s[4]<='5' &&
2096 s[5]>='0' && s[5]<='5' && !s[6])
2097 return FG_NOT_DEFAULT | ((16 + (s[3]-'0')*6*6 + (s[4]-'0')*6 + (s[5]-'0')) << FG_SHIFT);
2098
2099 else if(s[0]=='b' && s[1]=='g' && s[2]=='_' &&
2100 s[3]>='0' && s[3]<='5' &&
2101 s[4]>='0' && s[4]<='5' &&
2102 s[5]>='0' && s[5]<='5' && !s[6])
2103 return BG_NOT_DEFAULT | ((16 + (s[3]-'0')*6*6 + (s[4]-'0')*6 + (s[5]-'0')) << BG_SHIFT);
2104
2105 /* 256 color xterm support: shades of grey */
2106 /* Codes 232 - 255 are shades of grey */
2107 else if(s[0]=='f' && s[1]=='g' && s[2]=='_' && ztoi(s+3) >= 0 && ztoi(s+3) <= 23)
2108 return FG_NOT_DEFAULT | (232 + (ztoi(s+3) << FG_SHIFT));
2109
2110 else if(s[0]=='b' && s[1]=='g' && s[2]=='_' && ztoi(s+3) >= 0 && ztoi(s+3) <= 23)
2111 return BG_NOT_DEFAULT | (232 + (ztoi(s+3) << BG_SHIFT));
2112
2113 else
2114 return 0;
2115 }
2116
meta_color(const char * s)2117 int meta_color(const char *s)
2118 {
2119 int code = 0;
2120 while (*s) {
2121 char buf[32];
2122 int x = 0;
2123 while (*s)
2124 if (*s && *s != '+') {
2125 if (x != SIZEOF(buf) - 1)
2126 buf[x++] = *s;
2127 ++s;
2128 } else
2129 break;
2130 if (*s == '+')
2131 ++s;
2132 buf[x] = 0;
2133 code |= meta_color_single(buf);
2134 }
2135 return code;
2136 }
2137
2138 /* Generate a field
2139 *
2140 * 't' is SCRN to write to.
2141 * 'scrn' is address of field in character buffer
2142 * 'attr' is address of field in attribute buffer
2143 * 'x', 'y' are starting column and line numbers of field
2144 * 'ofst' is first column within string to display
2145 * 's', 'len' is string to generate in field
2146 * 'atr' is screeen attributes (and color) which should be used
2147 * 'width' is column width of field
2148 * 'flg' if set, erases to end of line
2149 * 'fmt' is array of attributes, one for each byte. OK if NULL.
2150 */
2151
genfield(SCRN * t,int (* scrn)[COMPOSE],int * attr,ptrdiff_t x,ptrdiff_t y,ptrdiff_t ofst,const char * s,ptrdiff_t len,int atr,ptrdiff_t width,int flg,int * fmt)2152 void genfield(SCRN *t,int (*scrn)[COMPOSE],int *attr,ptrdiff_t x,ptrdiff_t y,ptrdiff_t ofst,const char *s,ptrdiff_t len,int atr,ptrdiff_t width,int flg,int *fmt)
2153 {
2154 ptrdiff_t col;
2155 struct utf8_sm sm;
2156 ptrdiff_t last_col = x + width;
2157
2158 utf8_init(&sm);
2159
2160 for (col = 0;len != 0 && x < last_col; len--) {
2161 int c = *(const unsigned char *)s++;
2162 ptrdiff_t wid = -1;
2163 int my_atr = atr;
2164 if (fmt) {
2165 int fmtatr = *fmt++;
2166 if (fmtatr & FG_MASK)
2167 my_atr = (my_atr & ~FG_MASK) | (fmtatr & FG_MASK);
2168 if (fmtatr & BG_MASK)
2169 my_atr = (my_atr & ~BG_MASK) | (fmtatr & BG_MASK);
2170 my_atr |= fmtatr & AT_MASK;
2171 }
2172 if (locale_map->type) {
2173 /* UTF-8 mode: decode character and determine its width */
2174 c = utf8_decode(&sm,TO_CHAR_OK(c));
2175 if (c >= 0)
2176 wid = joe_wcwidth(1,c);
2177 } else {
2178 /* Byte mode: character is one column wide */
2179 wid = 1 ;
2180 }
2181 if (wid>=0) {
2182 if (col >= ofst) {
2183 if (x + wid > last_col) {
2184 /* Character crosses end of field, so fill balance of field with '>' characters instead */
2185 while (x < last_col) {
2186 outatr(locale_map, t, scrn, attr, x, y, '>', my_atr);
2187 ++scrn;
2188 ++attr;
2189 ++x;
2190 }
2191 } else if(wid) {
2192 /* Emit character */
2193 outatr(locale_map, t, scrn, attr, x, y, c, my_atr);
2194 x += wid;
2195 scrn += wid;
2196 attr += wid;
2197 }
2198 } else if ((col + wid) > ofst) {
2199 /* Wide character crosses left side of field */
2200 wid -= ofst - col;
2201 col = ofst;
2202 while (wid) {
2203 outatr(locale_map, t, scrn, attr, x, y, '<', my_atr);
2204 ++scrn;
2205 ++attr;
2206 ++x;
2207 ++col;
2208 --wid;
2209 }
2210 } else
2211 col += wid;
2212 }
2213 }
2214 /* Fill balance of field with spaces */
2215 while (x < last_col) {
2216 outatr(locale_map, t, scrn, attr, x, y, ' ', atr);
2217 ++x;
2218 ++scrn;
2219 ++attr;
2220 }
2221 /* Complete any unfinished characters */
2222 outatr_complete(t);
2223 /* Erase to end of line */
2224 if (flg)
2225 eraeol(t, x, y, atr);
2226 }
2227
2228 /* Width function for above */
2229
txtwidth(const char * s,ptrdiff_t len)2230 ptrdiff_t txtwidth(const char *s,ptrdiff_t len)
2231 {
2232 if (locale_map->type) {
2233 ptrdiff_t col=0;
2234 struct utf8_sm sm;
2235 utf8_init(&sm);
2236
2237 while(len--) {
2238 int d = utf8_decode(&sm,*s++);
2239 if (d >= 0)
2240 col += joe_wcwidth(1,d);
2241 }
2242
2243 return col;
2244 } else
2245 return len;
2246 }
2247
txtwidth1(struct charmap * map,off_t tabwidth,const char * s,ptrdiff_t len)2248 off_t txtwidth1(struct charmap *map,off_t tabwidth,const char *s,ptrdiff_t len)
2249 {
2250 if (map->type) {
2251 off_t col=0;
2252 struct utf8_sm sm;
2253 utf8_init(&sm);
2254
2255 while(len--) {
2256 int d = utf8_decode(&sm,*s++);
2257 if (d == '\t') {
2258 ++col;
2259 col += tabwidth - (col % tabwidth);
2260 } else if (d >= 0)
2261 col += joe_wcwidth(1,d);
2262 }
2263
2264 return col;
2265 } else {
2266 off_t col = 0;
2267 while (len--) {
2268 if (*s++ == '\t') {
2269 ++col;
2270 col += tabwidth - (col % tabwidth);
2271 } else
2272 ++col;
2273 }
2274 return col;
2275 }
2276 }
2277
2278 /* Generate text with formatting escape sequences */
2279
genfmt(SCRN * t,ptrdiff_t x,ptrdiff_t y,ptrdiff_t ofst,const char * s,int atr,int iatr,int flg)2280 void genfmt(SCRN *t, ptrdiff_t x, ptrdiff_t y, ptrdiff_t ofst, const char *s, int atr, int iatr, int flg)
2281 {
2282 int (*scrn)[COMPOSE] = t->scrn + y * t->co + x;
2283 int *attr = t->attr + y * t->co + x;
2284 ptrdiff_t col = 0;
2285 int c;
2286 struct utf8_sm sm;
2287 int inverted = !!(atr & INVERSE);
2288 int origcolor = atr & ~(FG_MASK | BG_MASK);
2289
2290 if (iatr && inverted) {
2291 atr = iatr | (atr & ~(FG_MASK | BG_MASK | INVERSE));
2292 }
2293
2294 utf8_init(&sm);
2295
2296 while ((c = *(const unsigned char *)s++) != '\0')
2297 if (c == '\\') {
2298 switch (c = *s++) {
2299 case 'u':
2300 case 'U':
2301 atr ^= UNDERLINE;
2302 break;
2303 case 'i':
2304 case 'I':
2305 if (iatr) {
2306 inverted = !inverted;
2307 atr = (inverted ? iatr : origcolor) | (atr & ~(FG_MASK | BG_MASK));
2308 } else {
2309 atr ^= INVERSE;
2310 }
2311 break;
2312 case 'b':
2313 case 'B':
2314 atr ^= BOLD;
2315 break;
2316 case 'l':
2317 case 'L':
2318 atr ^= ITALIC;
2319 break;
2320 case 'd':
2321 case 'D':
2322 atr ^= DIM;
2323 break;
2324 case 'f':
2325 case 'F':
2326 atr ^= BLINK;
2327 break;
2328 case 's':
2329 case 'S':
2330 atr ^= CROSSED_OUT;
2331 break;
2332 case 'z':
2333 case 'Z':
2334 atr ^= DOUBLE_UNDERLINE;
2335 break;
2336 case 0:
2337 --s;
2338 break;
2339 case '@':
2340 c = 0;
2341 default: {
2342 if (col++ >= ofst) {
2343 outatr(locale_map, t, scrn, attr, x, y, (c&0x7F), atr);
2344 ++scrn;
2345 ++attr;
2346 ++x;
2347 }
2348 break;
2349 }
2350 }
2351 } else {
2352 ptrdiff_t wid = -1;
2353 if (locale_map->type) {
2354 /* UTF-8 mode: decode character and determine its width */
2355 c = utf8_decode(&sm,TO_CHAR_OK(c));
2356 if (c >= 0) {
2357 wid = joe_wcwidth(1,c);
2358 }
2359 } else {
2360 /* Byte mode: character is one column wide */
2361 wid = 1 ;
2362 }
2363
2364 if (wid>=0) {
2365 if (col >= ofst) {
2366 outatr(locale_map, t, scrn, attr, x, y, c, atr);
2367 scrn += wid;
2368 attr += wid;
2369 x += wid;
2370 col += wid;
2371 } else if (col+wid>ofst) {
2372 while (col<ofst) {
2373 ++col;
2374 --wid;
2375 }
2376 while (wid) {
2377 outatr(locale_map, t, scrn, attr, x, y, '<', atr);
2378 ++scrn;
2379 ++attr;
2380 ++x;
2381 ++col;
2382 --wid;
2383 }
2384 } else
2385 col += wid;
2386 }
2387 }
2388 outatr_complete(t);
2389 if (flg)
2390 eraeol(t, x, y, atr);
2391 }
2392
2393 /* Determine column width of string with format codes */
2394
fmtlen(const char * s)2395 ptrdiff_t fmtlen(const char *s)
2396 {
2397 ptrdiff_t col = 0;
2398 struct utf8_sm sm;
2399 int c;
2400
2401 utf8_init(&sm);
2402
2403 while ((c = *(const unsigned char *)s++)) {
2404 if (c == '\\') {
2405 switch (*s++) {
2406 case 'u':
2407 case 'i':
2408 case 'd':
2409 case 'f':
2410 case 'b':
2411 case 'U':
2412 case 'I':
2413 case 'D':
2414 case 'F':
2415 case 'B':
2416 continue;
2417 case 0:
2418 return col;
2419 default:
2420 ++col;
2421 continue;
2422 }
2423 } else {
2424 ptrdiff_t wid = 0;
2425 if(locale_map->type) {
2426 c = utf8_decode(&sm,TO_CHAR_OK(c));
2427 if (c>=0)
2428 wid = joe_wcwidth(1,c);
2429 } else {
2430 wid = 1;
2431 }
2432 col += wid;
2433 }
2434 }
2435 return col;
2436 }
2437
2438 /* Return offset within format string which corresponds to a particular
2439 column */
2440
2441 /* FIXME: this is not valid if we land in the middle of a double-wide character */
2442
fmtpos(const char * s,ptrdiff_t goal)2443 ptrdiff_t fmtpos(const char *s, ptrdiff_t goal)
2444 {
2445 const char *org = s;
2446 ptrdiff_t col = 0;
2447 int c;
2448 struct utf8_sm sm;
2449
2450 utf8_init(&sm);
2451
2452 while ((c = *(const unsigned char *)s) && col<goal) {
2453 s++;
2454 if (c == '\\') {
2455 switch (*s++) {
2456 case 'u':
2457 case 'i':
2458 case 'd':
2459 case 'f':
2460 case 'b':
2461 case 'U':
2462 case 'I':
2463 case 'D':
2464 case 'F':
2465 case 'B':
2466 continue;
2467 case 0:
2468 --s;
2469 break;
2470 default:
2471 ++col;
2472 continue;
2473 }
2474 } else {
2475 ptrdiff_t wid = 0;
2476 if(locale_map->type) {
2477 c = utf8_decode(&sm,TO_CHAR_OK(c));
2478 if (c>=0)
2479 wid = joe_wcwidth(1,c);
2480 } else {
2481 wid = 1;
2482 }
2483 col += wid;
2484 }
2485 }
2486
2487 return s - org + goal - col;
2488 }
2489
2490 /* Set extended colors palette */
setextpal(SCRN * t,int * palette)2491 void setextpal(SCRN *t, int *palette)
2492 {
2493 t->palette = palette;
2494 }
2495