OSDN Git Service

57e60ec5c3bcbca6c5b85c1b8b58cd805a3ffcc9
[hengband/hengband.git] / src / term / z-term.c
1 /*
2  * @brief Purpose: a generic, efficient, terminal window package -BEN-
3  * Copyright (c) 1997 Ben Harrison
4  *
5  * This software may be copied and distributed for educational, research,
6  * and not for profit purposes provided that this copyright and statement
7  * are included in all such copies.
8  */
9
10 #include "term/z-term.h"
11 #include "game-option/runtime-arguments.h"
12 #include "game-option/special-options.h"
13 #include "term/term-color-types.h"
14 #include "term/z-virt.h"
15
16 /* Special flags in the attr data */
17 #define AF_BIGTILE2 0xf0
18 #define AF_TILE1 0x80
19
20 #ifdef JP
21 /*
22  * 全角文字対応。
23  * 属性に全角文字の1バイト目、2バイト目も記憶。
24  * By FIRST
25  */
26 #define AF_KANJI1 0x10
27 #define AF_KANJI2 0x20
28 #define AF_KANJIC 0x0f
29 #endif
30
31 /* The current "term" */
32 term_type *Term = NULL;
33
34 /*** Local routines ***/
35
36 /*
37  * Nuke a term_win (see below)
38  */
39 errr term_win_nuke(term_win *s, TERM_LEN w, TERM_LEN h)
40 {
41     /* Free the window access arrays */
42     C_KILL(s->a, h, TERM_COLOR *);
43     C_KILL(s->c, h, char *);
44
45     /* Free the window content arrays */
46     C_KILL(s->va, h * w, TERM_COLOR);
47     C_KILL(s->vc, h * w, char);
48
49     /* Free the terrain access arrays */
50     C_KILL(s->ta, h, TERM_COLOR *);
51     C_KILL(s->tc, h, char *);
52
53     /* Free the terrain content arrays */
54     C_KILL(s->vta, h * w, TERM_COLOR);
55     C_KILL(s->vtc, h * w, char);
56
57     return 0;
58 }
59
60 /*
61  * Initialize a "term_win" (using the given window size)
62  */
63 static errr term_win_init(term_win *s, TERM_LEN w, TERM_LEN h)
64 {
65     /* Make the window access arrays */
66     C_MAKE(s->a, h, TERM_COLOR *);
67     C_MAKE(s->c, h, char *);
68
69     /* Make the window content arrays */
70     C_MAKE(s->va, h * w, TERM_COLOR);
71     C_MAKE(s->vc, h * w, char);
72
73     /* Make the terrain access arrays */
74     C_MAKE(s->ta, h, TERM_COLOR *);
75     C_MAKE(s->tc, h, char *);
76
77     /* Make the terrain content arrays */
78     C_MAKE(s->vta, h * w, TERM_COLOR);
79     C_MAKE(s->vtc, h * w, char);
80
81     /* Prepare the window access arrays */
82     for (TERM_LEN y = 0; y < h; y++) {
83         s->a[y] = s->va + w * y;
84         s->c[y] = s->vc + w * y;
85
86         s->ta[y] = s->vta + w * y;
87         s->tc[y] = s->vtc + w * y;
88     }
89
90     return 0;
91 }
92
93 /*
94  * Copy a "term_win" from another
95  */
96 static errr term_win_copy(term_win *s, term_win *f, TERM_LEN w, TERM_LEN h)
97 {
98     /* Copy contents */
99     for (TERM_LEN y = 0; y < h; y++) {
100         TERM_COLOR *f_aa = f->a[y];
101         char *f_cc = f->c[y];
102
103         TERM_COLOR *s_aa = s->a[y];
104         char *s_cc = s->c[y];
105
106         TERM_COLOR *f_taa = f->ta[y];
107         char *f_tcc = f->tc[y];
108
109         TERM_COLOR *s_taa = s->ta[y];
110         char *s_tcc = s->tc[y];
111
112         for (TERM_LEN x = 0; x < w; x++) {
113             *s_aa++ = *f_aa++;
114             *s_cc++ = *f_cc++;
115             *s_taa++ = *f_taa++;
116             *s_tcc++ = *f_tcc++;
117         }
118     }
119
120     /* Copy cursor */
121     s->cx = f->cx;
122     s->cy = f->cy;
123     s->cu = f->cu;
124     s->cv = f->cv;
125
126     return 0;
127 }
128
129 /*** External hooks ***/
130
131 /*
132  * Execute the "Term->user_hook" hook, if available (see above).
133  */
134 errr term_user(int n)
135 {
136     /* Verify the hook */
137     if (!Term->user_hook)
138         return -1;
139
140     /* Call the hook */
141     return ((*Term->user_hook)(n));
142 }
143
144 /*
145  * Execute the "Term->xtra_hook" hook, if available (see above).
146  */
147 errr term_xtra(int n, int v)
148 {
149     /* Verify the hook */
150     if (!Term->xtra_hook)
151         return -1;
152
153     /* Call the hook */
154     return ((*Term->xtra_hook)(n, v));
155 }
156
157 /*** Fake hooks ***/
158
159 /*
160  * Fake hook for "term_curs()" (see above)
161  */
162 static errr term_curs_hack(TERM_LEN x, TERM_LEN y)
163 {
164     /* Unused */
165     (void)x;
166     (void)y;
167
168     return -1;
169 }
170
171 /*
172  * Fake hook for "term_bigcurs()" (see above)
173  */
174 static errr term_bigcurs_hack(TERM_LEN x, TERM_LEN y) { return (*Term->curs_hook)(x, y); }
175
176 /*
177  * Fake hook for "term_wipe()" (see above)
178  */
179 static errr term_wipe_hack(TERM_LEN x, TERM_LEN y, int n)
180 {
181     /* Unused */
182     (void)x;
183     (void)y;
184     (void)n;
185
186     return -1;
187 }
188
189 /*
190  * Fake hook for "term_text()" (see above)
191  */
192 static errr term_text_hack(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr cp)
193 {
194     /* Unused */
195     (void)x;
196     (void)y;
197     (void)n;
198     (void)a;
199     (void)cp;
200
201     return -1;
202 }
203
204 /*
205  * Fake hook for "term_pict()" (see above)
206  */
207 static errr term_pict_hack(TERM_LEN x, TERM_LEN y, int n, const TERM_COLOR *ap, concptr cp, const TERM_COLOR *tap, concptr tcp)
208 {
209     /* Unused */
210     (void)x;
211     (void)y;
212     (void)n;
213     (void)ap;
214     (void)cp;
215     (void)tap;
216     (void)tcp;
217
218     return -1;
219 }
220
221 /*** Efficient routines ***/
222
223 /*
224  * Mentally draw an attr/char at a given location
225  * Assumes given location and values are valid.
226  */
227 void term_queue_char(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
228 {
229     term_win *scrn = Term->scr;
230
231     TERM_COLOR *scr_aa = &scrn->a[y][x];
232     char *scr_cc = &scrn->c[y][x];
233
234     TERM_COLOR *scr_taa = &scrn->ta[y][x];
235     char *scr_tcc = &scrn->tc[y][x];
236
237     /* Ignore non-changes */
238     if ((*scr_aa == a) && (*scr_cc == c) && (*scr_taa == ta) && (*scr_tcc == tc))
239         return;
240
241     /* Save the "literal" information */
242     *scr_aa = a;
243     *scr_cc = c;
244
245     *scr_taa = ta;
246     *scr_tcc = tc;
247
248     /* Check for new min/max row info */
249     if (y < Term->y1)
250         Term->y1 = (byte)y;
251     if (y > Term->y2)
252         Term->y2 = (byte)y;
253
254     /* Check for new min/max col info for this row */
255     if (x < Term->x1[y])
256         Term->x1[y] = (byte)x;
257     if (x > Term->x2[y])
258         Term->x2[y] = (byte)x;
259
260 #ifdef JP
261     if (((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2) || (scrn->a[y][x] & AF_KANJI2))
262 #else
263     if ((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2)
264 #endif
265         if ((x - 1) < Term->x1[y])
266             Term->x1[y]--;
267 }
268
269 /*
270  * Bigtile version of term_queue_char().
271  * If use_bigtile is FALSE, simply call term_queue_char().
272  * Otherwise, mentally draw a pair of attr/char at a given location.
273  * Assumes given location and values are valid.
274  */
275 void term_queue_bigchar(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
276 {
277
278 #ifdef JP
279     /*
280      * A table which relates each ascii character to a multibyte
281      * character.
282      *
283      * 「■」は二倍幅豆腐の内部コードに使用。
284      */
285     static char ascii_to_zenkaku[] = " !”#$%&’()*+,-./"
286                                      "0123456789:;<=>?"
287                                      "@ABCDEFGHIJKLMNO"
288                                      "PQRSTUVWXYZ[\]^_"
289                                      "‘abcdefghijklmno"
290                                      "pqrstuvwxyz{|}~■";
291 #endif
292
293     byte a2;
294     char c2;
295
296     /* If non bigtile mode, call orginal function */
297     if (!use_bigtile) {
298         term_queue_char(x, y, a, c, ta, tc);
299         return;
300     }
301
302     /* A tile becomes a Bigtile */
303     if ((a & AF_TILE1) && (c & 0x80)) {
304         /* Mark it as a Bigtile */
305         a2 = AF_BIGTILE2;
306
307         c2 = -1;
308
309         /* Ignore non-tile background */
310         if (!((ta & AF_TILE1) && (tc & 0x80))) {
311             ta = 0;
312             tc = 0;
313         }
314     }
315
316 #ifdef JP
317     /*
318      * Use a multibyte character instead of a dirty pair of ASCII
319      * characters.
320      */
321     else if (' ' <= c) /* isprint(c) */
322     {
323         c2 = ascii_to_zenkaku[2 * (c - ' ') + 1];
324         c = ascii_to_zenkaku[2 * (c - ' ')];
325
326         /* Mark it as a Kanji */
327         a2 = a | AF_KANJI2;
328         a |= AF_KANJI1;
329     }
330 #endif
331
332     else {
333         /* Dirty pair of ASCII characters */
334         a2 = TERM_WHITE;
335         c2 = ' ';
336     }
337
338     /* Display pair of attr/char */
339     term_queue_char(x, y, a, c, ta, tc);
340     term_queue_char(x + 1, y, a2, c2, 0, 0);
341 }
342
343 /*
344  * Mentally draw a string of attr/chars at a given location
345  * Assumes given location and values are valid.
346  * This function is designed to be fast, with no consistancy checking.
347  * It is used to update the map in the game.
348  */
349 void term_queue_line(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR *a, char *c, TERM_COLOR *ta, char *tc)
350 {
351     term_win *scrn = Term->scr;
352
353     TERM_LEN x1 = -1;
354     TERM_LEN x2 = -1;
355
356     TERM_COLOR *scr_aa = &scrn->a[y][x];
357     char *scr_cc = &scrn->c[y][x];
358
359     TERM_COLOR *scr_taa = &scrn->ta[y][x];
360     char *scr_tcc = &scrn->tc[y][x];
361
362     while (n--) {
363         /* Ignore non-changes */
364         if ((*scr_aa == *a) && (*scr_cc == *c) && (*scr_taa == *ta) && (*scr_tcc == *tc)) {
365             x++;
366             a++;
367             c++;
368             ta++;
369             tc++;
370             scr_aa++;
371             scr_cc++;
372             scr_taa++;
373             scr_tcc++;
374             continue;
375         }
376
377         /* Save the "literal" information */
378         *scr_taa++ = *ta++;
379         *scr_tcc++ = *tc++;
380
381         /* Save the "literal" information */
382         *scr_aa++ = *a++;
383         *scr_cc++ = *c++;
384
385         /* Track minimum changed column */
386         if (x1 < 0)
387             x1 = x;
388
389         /* Track maximum changed column */
390         x2 = x;
391
392         x++;
393     }
394
395     /* Expand the "change area" as needed */
396     if (x1 >= 0) {
397         /* Check for new min/max row info */
398         if (y < Term->y1)
399             Term->y1 = (byte)y;
400         if (y > Term->y2)
401             Term->y2 = (byte)y;
402
403         /* Check for new min/max col info in this row */
404         if (x1 < Term->x1[y])
405             Term->x1[y] = (byte)x1;
406         if (x2 > Term->x2[y])
407             Term->x2[y] = (byte)x2;
408     }
409 }
410
411 /*
412  * Mentally draw some attr/chars at a given location
413  *
414  * Assumes that (x,y) is a valid location, that the first "n" characters
415  * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
416  * a valid location, so the first "n" characters of "s" can all be added
417  * starting at (x,y) without causing any illegal operations.
418  */
419 static void term_queue_chars(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
420 {
421     TERM_LEN x1 = -1, x2 = -1;
422
423     TERM_COLOR *scr_aa = Term->scr->a[y];
424 #ifdef JP
425     char *scr_cc = Term->scr->c[y];
426
427     TERM_COLOR *scr_taa = Term->scr->ta[y];
428     char *scr_tcc = Term->scr->tc[y];
429 #else
430     char *scr_cc = Term->scr->c[y];
431
432     TERM_COLOR *scr_taa = Term->scr->ta[y];
433     char *scr_tcc = Term->scr->tc[y];
434 #endif
435
436 #ifdef JP
437     /* 表示文字なし */
438     if (n == 0 || *s == 0)
439         return;
440     /*
441      * 全角文字の右半分から文字を表示する場合、
442      * 重なった文字の左部分を消去。
443      * 表示開始位置が左端でないと仮定。
444      */
445     if ((scr_aa[x] & AF_KANJI2) && (scr_aa[x] & AF_BIGTILE2) != AF_BIGTILE2) {
446         scr_cc[x - 1] = ' ';
447         scr_aa[x - 1] &= AF_KANJIC;
448         x1 = x2 = x - 1;
449     }
450 #endif
451     /* Queue the attr/chars */
452     for (; n; x++, s++, n--) {
453 #ifdef JP
454         /* 特殊文字としてMSBが立っている可能性がある */
455         /* その場合attrのMSBも立っているのでこれで識別する */
456         /* check */
457         if (!(a & AF_TILE1) && iskanji(*s)) {
458             char nc1 = *s++;
459             char nc2 = *s;
460
461             byte na1 = (a | AF_KANJI1);
462             byte na2 = (a | AF_KANJI2);
463
464             if ((--n == 0) || !nc2)
465                 break;
466
467             if (scr_aa[x++] == na1 && scr_aa[x] == na2 && scr_cc[x - 1] == nc1 && scr_cc[x] == nc2 && (scr_taa[x - 1] == 0) && (scr_taa[x] == 0)
468                 && (scr_tcc[x - 1] == 0) && (scr_tcc[x] == 0))
469                 continue;
470
471             scr_aa[x - 1] = na1;
472             scr_aa[x] = na2;
473             scr_cc[x - 1] = nc1;
474             scr_cc[x] = nc2;
475
476             if (x1 < 0)
477                 x1 = x - 1;
478             x2 = x;
479         } else {
480 #endif
481             TERM_COLOR oa = scr_aa[x];
482             char oc = scr_cc[x];
483
484             TERM_COLOR ota = scr_taa[x];
485             char otc = scr_tcc[x];
486
487             /* Ignore non-changes */
488             if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0))
489                 continue;
490
491             /* Save the "literal" information */
492             scr_aa[x] = a;
493             scr_cc[x] = *s;
494
495             scr_taa[x] = 0;
496             scr_tcc[x] = 0;
497
498             /* Note the "range" of window updates */
499             if (x1 < 0)
500                 x1 = x;
501             x2 = x;
502 #ifdef JP
503         }
504 #endif
505     }
506
507 #ifdef JP
508     /*
509      * 全角文字の左半分で表示を終了する場合、
510      * 重なった文字の右部分を消去。
511      * (条件追加:タイルの1文字目でない事を確かめるように。)
512      */
513     {
514
515         int w, h;
516         term_get_size(&w, &h);
517         if (x != w && !(scr_aa[x] & AF_TILE1) && (scr_aa[x] & AF_KANJI2)) {
518             scr_cc[x] = ' ';
519             scr_aa[x] &= AF_KANJIC;
520             if (x1 < 0)
521                 x1 = x;
522             x2 = x;
523         }
524     }
525 #endif
526     /* Expand the "change area" as needed */
527     if (x1 >= 0) {
528         /* Check for new min/max row info */
529         if (y < Term->y1)
530             Term->y1 = (byte)y;
531         if (y > Term->y2)
532             Term->y2 = (byte)y;
533
534         /* Check for new min/max col info in this row */
535         if (x1 < Term->x1[y])
536             Term->x1[y] = (byte)x1;
537         if (x2 > Term->x2[y])
538             Term->x2[y] = (byte)x2;
539     }
540 }
541
542 /*** Refresh routines ***/
543
544 /*
545  * Flush a row of the current window (see "term_fresh")
546  * Display text using "term_pict()"
547  */
548 static void term_fresh_row_pict(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
549 {
550     TERM_COLOR *old_aa = Term->old->a[y];
551     char *old_cc = Term->old->c[y];
552
553     TERM_COLOR *scr_aa = Term->scr->a[y];
554     char *scr_cc = Term->scr->c[y];
555
556     TERM_COLOR *old_taa = Term->old->ta[y];
557     char *old_tcc = Term->old->tc[y];
558
559     TERM_COLOR *scr_taa = Term->scr->ta[y];
560     char *scr_tcc = Term->scr->tc[y];
561
562     TERM_COLOR ota;
563     char otc;
564
565     TERM_COLOR nta;
566     char ntc;
567
568     /* Pending length */
569     TERM_LEN fn = 0;
570
571     /* Pending start */
572     TERM_LEN fx = 0;
573
574     TERM_COLOR oa;
575     char oc;
576
577     TERM_COLOR na;
578     char nc;
579
580 #ifdef JP
581     /* 全角文字の2バイト目かどうか */
582     int kanji = 0;
583 #endif
584     /* Scan "modified" columns */
585     for (TERM_LEN x = x1; x <= x2; x++) {
586         /* See what is currently here */
587         oa = old_aa[x];
588         oc = old_cc[x];
589
590         /* See what is desired there */
591         na = scr_aa[x];
592         nc = scr_cc[x];
593
594 #ifdef JP
595         if (kanji) {
596             /* 全角文字2バイト目 */
597             kanji = 0;
598             old_aa[x] = na;
599             old_cc[x] = nc;
600             fn++;
601             continue;
602         }
603         /* 特殊文字としてMSBが立っている可能性がある */
604         /* その場合attrのMSBも立っているのでこれで識別する */
605         /* check */
606         kanji = (iskanji(nc) && !(na & AF_TILE1));
607 #endif
608
609         ota = old_taa[x];
610         otc = old_tcc[x];
611
612         nta = scr_taa[x];
613         ntc = scr_tcc[x];
614
615         /* Handle unchanged grids */
616 #ifdef JP
617         if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
618             && (!kanji
619                 || (scr_aa[x + 1] == old_aa[x + 1] && scr_cc[x + 1] == old_cc[x + 1] && scr_taa[x + 1] == old_taa[x + 1] && scr_tcc[x + 1] == old_tcc[x + 1])))
620 #else
621         if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
622 #endif
623         {
624             /* Flush */
625             if (fn) {
626                 /* Draw pending attr/char pairs */
627                 (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
628
629                 /* Forget */
630                 fn = 0;
631             }
632
633 #ifdef JP
634             /* 全角文字の時は再開位置は+1 */
635             if (kanji) {
636                 x++;
637                 fx++;
638                 kanji = 0;
639             }
640 #endif
641             /* Skip */
642             continue;
643         }
644         /* Save new contents */
645         old_aa[x] = na;
646         old_cc[x] = nc;
647
648         old_taa[x] = nta;
649         old_tcc[x] = ntc;
650
651         /* Restart and Advance */
652         if (fn++ == 0)
653             fx = x;
654     }
655
656     /* Flush */
657     if (fn) {
658         /* Draw pending attr/char pairs */
659         (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
660     }
661 }
662
663 /*
664  * Flush a row of the current window (see "term_fresh")
665  *
666  * Display text using "term_text()" and "term_wipe()",
667  * but use "term_pict()" for high-bit attr/char pairs
668  */
669 static void term_fresh_row_both(TERM_LEN y, int x1, int x2)
670 {
671     TERM_COLOR *old_aa = Term->old->a[y];
672     char *old_cc = Term->old->c[y];
673
674     TERM_COLOR *scr_aa = Term->scr->a[y];
675     char *scr_cc = Term->scr->c[y];
676
677     TERM_COLOR *old_taa = Term->old->ta[y];
678     char *old_tcc = Term->old->tc[y];
679     TERM_COLOR *scr_taa = Term->scr->ta[y];
680     char *scr_tcc = Term->scr->tc[y];
681
682     TERM_COLOR ota;
683     char otc;
684     TERM_COLOR nta;
685     char ntc;
686
687     /* The "always_text" flag */
688     int always_text = Term->always_text;
689
690     /* Pending length */
691     int fn = 0;
692
693     /* Pending start */
694     int fx = 0;
695
696     /* Pending attr */
697     byte fa = Term->attr_blank;
698
699     TERM_COLOR oa;
700     char oc;
701
702     TERM_COLOR na;
703     char nc;
704
705 #ifdef JP
706     /* 全角文字の2バイト目かどうか */
707     int kanji = 0;
708 #endif
709     /* Scan "modified" columns */
710     for (TERM_LEN x = x1; x <= x2; x++) {
711         /* See what is currently here */
712         oa = old_aa[x];
713         oc = old_cc[x];
714
715         /* See what is desired there */
716         na = scr_aa[x];
717         nc = scr_cc[x];
718
719 #ifdef JP
720         if (kanji) {
721             /* 全角文字2バイト目 */
722             kanji = 0;
723             old_aa[x] = na;
724             old_cc[x] = nc;
725             fn++;
726             continue;
727         }
728         /* 特殊文字としてMSBが立っている可能性がある */
729         /* その場合attrのMSBも立っているのでこれで識別する */
730         /* check */
731         /*              kanji = (iskanji(nc));  */
732         kanji = (iskanji(nc) && !(na & AF_TILE1));
733 #endif
734
735         ota = old_taa[x];
736         otc = old_tcc[x];
737
738         nta = scr_taa[x];
739         ntc = scr_tcc[x];
740
741         /* Handle unchanged grids */
742 #ifdef JP
743         if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
744             && (!kanji
745                 || (scr_aa[x + 1] == old_aa[x + 1] && scr_cc[x + 1] == old_cc[x + 1] && scr_taa[x + 1] == old_taa[x + 1] && scr_tcc[x + 1] == old_tcc[x + 1])))
746 #else
747         if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
748 #endif
749         {
750             /* Flush */
751             if (fn) {
752                 /* Draw pending chars (normal) */
753                 if (fa || always_text) {
754                     (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
755                 }
756
757                 /* Draw pending chars (black) */
758                 else {
759                     (void)((*Term->wipe_hook)(fx, y, fn));
760                 }
761
762                 /* Forget */
763                 fn = 0;
764             }
765
766 #ifdef JP
767             /* 全角文字の時は再開位置は+1 */
768             if (kanji) {
769                 x++;
770                 fx++;
771                 kanji = 0;
772             }
773 #endif
774             /* Skip */
775             continue;
776         }
777
778         /* Save new contents */
779         old_aa[x] = na;
780         old_cc[x] = nc;
781
782         old_taa[x] = nta;
783         old_tcc[x] = ntc;
784
785         /* 2nd byte of bigtile */
786         if ((na & AF_BIGTILE2) == AF_BIGTILE2)
787             continue;
788
789         /* Handle high-bit attr/chars */
790         if ((na & AF_TILE1) && (nc & 0x80)) {
791             /* Flush */
792             if (fn) {
793                 /* Draw pending chars (normal) */
794                 if (fa || always_text) {
795                     (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
796                 }
797
798                 /* Draw pending chars (black) */
799                 else {
800                     (void)((*Term->wipe_hook)(fx, y, fn));
801                 }
802
803                 /* Forget */
804                 fn = 0;
805             }
806
807             /* Draw the special attr/char pair */
808             (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc));
809
810             /* Skip */
811             continue;
812         }
813
814         /* Notice new color */
815 #ifdef JP
816         if (fa != (na & AF_KANJIC))
817 #else
818         if (fa != na)
819 #endif
820
821         {
822             /* Flush */
823             if (fn) {
824                 /* Draw the pending chars */
825                 if (fa || always_text) {
826                     (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
827                 }
828
829                 /* Erase "leading" spaces */
830                 else {
831                     (void)((*Term->wipe_hook)(fx, y, fn));
832                 }
833
834                 /* Forget */
835                 fn = 0;
836             }
837
838             /* Save the new color */
839 #ifdef JP
840             fa = (na & AF_KANJIC);
841 #else
842             fa = na;
843 #endif
844         }
845
846         /* Restart and Advance */
847         if (fn++ == 0)
848             fx = x;
849     }
850
851     /* Flush */
852     if (fn) {
853         /* Draw pending chars (normal) */
854         if (fa || always_text) {
855             (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
856         }
857
858         /* Draw pending chars (black) */
859         else {
860             (void)((*Term->wipe_hook)(fx, y, fn));
861         }
862     }
863 }
864
865 /*
866  * Flush a row of the current window (see "term_fresh")
867  *
868  * Display text using "term_text()" and "term_wipe()"
869  */
870 static void term_fresh_row_text(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
871 {
872     TERM_COLOR *old_aa = Term->old->a[y];
873     char *old_cc = Term->old->c[y];
874
875     TERM_COLOR *scr_aa = Term->scr->a[y];
876     char *scr_cc = Term->scr->c[y];
877
878     /* The "always_text" flag */
879     int always_text = Term->always_text;
880
881     /* Pending length */
882     int fn = 0;
883
884     /* Pending start */
885     int fx = 0;
886
887     /* Pending attr */
888     byte fa = Term->attr_blank;
889
890     TERM_COLOR oa;
891     char oc;
892
893     TERM_COLOR na;
894     char nc;
895
896 #ifdef JP
897     /* 全角文字の2バイト目かどうか */
898     int kanji = 0;
899
900     for (TERM_LEN x = 0; x < x1; x++)
901         if (!(old_aa[x] & AF_TILE1) && iskanji(old_cc[x])) {
902             if (x == x1 - 1) {
903                 x1--;
904                 break;
905             } else
906                 x++;
907         }
908 #endif
909     /* Scan "modified" columns */
910     for (TERM_LEN x = x1; x <= x2; x++) {
911         /* See what is currently here */
912         oa = old_aa[x];
913         oc = old_cc[x];
914
915         /* See what is desired there */
916         na = scr_aa[x];
917         nc = scr_cc[x];
918
919 #ifdef JP
920         if (kanji) {
921             /* 全角文字2バイト目 */
922             kanji = 0;
923             old_aa[x] = na;
924             old_cc[x] = nc;
925             fn++;
926             continue;
927         }
928         /* 特殊文字としてMSBが立っている可能性がある */
929         /* その場合attrのMSBも立っているのでこれで識別する */
930         /* check */
931         kanji = (iskanji(nc) && !(na & AF_TILE1));
932 #endif
933         /* Handle unchanged grids */
934 #ifdef JP
935         if ((na == oa) && (nc == oc) && (!kanji || (scr_aa[x + 1] == old_aa[x + 1] && scr_cc[x + 1] == old_cc[x + 1])))
936 #else
937         if ((na == oa) && (nc == oc))
938 #endif
939
940         {
941             /* Flush */
942             if (fn) {
943                 /* Draw pending chars (normal) */
944                 if (fa || always_text) {
945                     (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
946                 }
947
948                 /* Draw pending chars (black) */
949                 else {
950                     (void)((*Term->wipe_hook)(fx, y, fn));
951                 }
952
953                 /* Forget */
954                 fn = 0;
955             }
956
957 #ifdef JP
958             /* 全角文字の時は再開位置は+1 */
959             if (kanji) {
960                 x++;
961                 fx++;
962                 kanji = 0;
963             }
964 #endif
965             /* Skip */
966             continue;
967         }
968
969         /* Save new contents */
970         old_aa[x] = na;
971         old_cc[x] = nc;
972
973         /* Notice new color */
974 #ifdef JP
975         if (fa != (na & AF_KANJIC))
976 #else
977         if (fa != na)
978 #endif
979
980         {
981             /* Flush */
982             if (fn) {
983                 /* Draw the pending chars */
984                 if (fa || always_text) {
985                     (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
986                 }
987
988                 /* Erase "leading" spaces */
989                 else {
990                     (void)((*Term->wipe_hook)(fx, y, fn));
991                 }
992
993                 /* Forget */
994                 fn = 0;
995             }
996
997             /* Save the new color */
998 #ifdef JP
999             fa = (na & AF_KANJIC);
1000 #else
1001             fa = na;
1002 #endif
1003         }
1004
1005         /* Restart and Advance */
1006         if (fn++ == 0)
1007             fx = x;
1008     }
1009
1010     /* Flush */
1011     if (fn) {
1012         /* Draw pending chars (normal) */
1013         if (fa || always_text) {
1014             (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1015         }
1016
1017         /* Draw pending chars (black) */
1018         else {
1019             (void)((*Term->wipe_hook)(fx, y, fn));
1020         }
1021     }
1022 }
1023
1024 /*
1025  * @brief Actually perform all requested changes to the window
1026  */
1027 errr term_fresh(void)
1028 {
1029     int w = Term->wid;
1030     int h = Term->hgt;
1031
1032     int y1 = Term->y1;
1033     int y2 = Term->y2;
1034
1035     term_win *old = Term->old;
1036     term_win *scr = Term->scr;
1037
1038     /* Before initialize (Advice from Mr.shimitei)*/
1039     if (!old || !scr)
1040         return 1;
1041
1042     /* Do nothing unless "mapped" */
1043     if (!Term->mapped_flag)
1044         return 1;
1045
1046     /* Trivial Refresh */
1047     if ((y1 > y2) && (scr->cu == old->cu) && (scr->cv == old->cv) && (scr->cx == old->cx) && (scr->cy == old->cy) && !(Term->total_erase)) {
1048         /* Nothing */
1049         return 1;
1050     }
1051
1052     /* Handle "total erase" */
1053     if (Term->total_erase) {
1054         byte na = Term->attr_blank;
1055         char nc = Term->char_blank;
1056
1057         /* Physically erase the entire window */
1058         term_xtra(TERM_XTRA_CLEAR, 0);
1059
1060         /* clear all "cursor" data */
1061         old->cv = old->cu = old->cx = old->cy = 0;
1062
1063         /* Wipe each row */
1064         for (TERM_LEN y = 0; y < h; y++) {
1065             TERM_COLOR *aa = old->a[y];
1066             char *cc = old->c[y];
1067
1068             TERM_COLOR *taa = old->ta[y];
1069             char *tcc = old->tc[y];
1070
1071             /* Wipe each column */
1072             for (TERM_LEN x = 0; x < w; x++) {
1073                 /* Wipe each grid */
1074                 *aa++ = na;
1075                 *cc++ = nc;
1076
1077                 *taa++ = na;
1078                 *tcc++ = nc;
1079             }
1080         }
1081
1082         /* Redraw every row */
1083         Term->y1 = y1 = 0;
1084         Term->y2 = y2 = h - 1;
1085
1086         /* Redraw every column */
1087         for (TERM_LEN y = 0; y < h; y++) {
1088             Term->x1[y] = 0;
1089             Term->x2[y] = w - 1;
1090         }
1091
1092         /* Forget "total erase" */
1093         Term->total_erase = FALSE;
1094     }
1095
1096     /* Cursor update -- Erase old Cursor */
1097     if (Term->soft_cursor) {
1098         /* Cursor was visible */
1099         if (!old->cu && old->cv) {
1100             int csize = 1;
1101             TERM_LEN tx = old->cx;
1102             TERM_LEN ty = old->cy;
1103
1104             TERM_COLOR *old_aa = old->a[ty];
1105             char *old_cc = old->c[ty];
1106
1107             TERM_COLOR *old_taa = old->ta[ty];
1108             char *old_tcc = old->tc[ty];
1109
1110             TERM_COLOR ota = old_taa[tx];
1111             char otc = old_tcc[tx];
1112
1113 #ifdef JP
1114             if (tx + 1 < Term->wid && !(old_aa[tx] & AF_TILE1) && iskanji(old_cc[tx]))
1115                 csize = 2;
1116 #endif
1117             /* Use "term_pict()" always */
1118             if (Term->always_pict)
1119                 (void)((*Term->pict_hook)(tx, ty, csize, &old_aa[tx], &old_cc[tx], &ota, &otc));
1120
1121             /* Use "term_pict()" sometimes */
1122             else if (Term->higher_pict && (old_aa[tx] & AF_TILE1) && (old_cc[tx] & 0x80))
1123                 (void)((*Term->pict_hook)(tx, ty, 1, &old_aa[tx], &old_cc[tx], &ota, &otc));
1124
1125             /*
1126              * Restore the actual character
1127              * 元の文字の描画範囲がカーソルより小さいと、
1128              * 上書きされなかった部分がゴミとして残る。
1129              * wipe_hook でカーソルを消去して text_hook で書き直す。
1130              */
1131             else if (old_aa[tx] || Term->always_text) {
1132                 (void)((*Term->wipe_hook)(tx, ty, 1));
1133                 (void)((*Term->text_hook)(tx, ty, csize, (unsigned char)(old_aa[tx] & 0xf), &old_cc[tx]));
1134             }
1135
1136             /* Erase the grid */
1137             else
1138                 (void)((*Term->wipe_hook)(tx, ty, 1));
1139         }
1140     }
1141
1142     /* Cursor Update -- Erase old Cursor */
1143     else {
1144         /* Cursor will be invisible */
1145         if (scr->cu || !scr->cv) {
1146             /* Make the cursor invisible */
1147             term_xtra(TERM_XTRA_SHAPE, 0);
1148         }
1149     }
1150
1151     /* Something to update */
1152     if (y1 <= y2) {
1153         /* Handle "icky corner" */
1154         if (Term->icky_corner) {
1155             /* Avoid the corner */
1156             if (y2 >= h - 1) {
1157                 /* Avoid the corner */
1158                 if (Term->x2[h - 1] > w - 2) {
1159                     /* Avoid the corner */
1160                     Term->x2[h - 1] = w - 2;
1161                 }
1162             }
1163         }
1164
1165         /* Scan the "modified" rows */
1166         for (TERM_LEN y = y1; y <= y2; ++y) {
1167             TERM_LEN x1 = Term->x1[y];
1168             TERM_LEN x2 = Term->x2[y];
1169
1170             /* Flush each "modified" row */
1171             if (x1 <= x2) {
1172                 /* Always use "term_pict()" */
1173                 if (Term->always_pict) {
1174                     /* Flush the row */
1175                     term_fresh_row_pict(y, x1, x2);
1176                 }
1177
1178                 /* Sometimes use "term_pict()" */
1179                 else if (Term->higher_pict) {
1180                     /* Flush the row */
1181                     term_fresh_row_both(y, x1, x2);
1182                 }
1183
1184                 /* Never use "term_pict()" */
1185                 else {
1186                     /* Flush the row */
1187                     term_fresh_row_text(y, x1, x2);
1188                 }
1189
1190                 /* This row is all done */
1191                 Term->x1[y] = (byte)w;
1192                 Term->x2[y] = 0;
1193
1194                 /* Flush that row (if allowed) */
1195                 if (!Term->never_frosh)
1196                     term_xtra(TERM_XTRA_FROSH, y);
1197             }
1198         }
1199
1200         /* No rows are invalid */
1201         Term->y1 = (byte)h;
1202         Term->y2 = 0;
1203     }
1204
1205     /* Cursor update -- Show new Cursor */
1206     if (Term->soft_cursor) {
1207         /* Draw the cursor */
1208         if (!scr->cu && scr->cv) {
1209 #ifdef JP
1210             if ((scr->cx + 1 < w)
1211                 && ((old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2
1212                     || (!(old->a[scr->cy][scr->cx] & AF_TILE1) && iskanji(old->c[scr->cy][scr->cx]))))
1213 #else
1214             if ((scr->cx + 1 < w) && (old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2)
1215 #endif
1216             {
1217                 /* Double width cursor for the Bigtile mode */
1218                 (void)((*Term->bigcurs_hook)(scr->cx, scr->cy));
1219             } else {
1220                 /* Call the cursor display routine */
1221                 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1222             }
1223         }
1224     }
1225
1226     /* Cursor Update -- Show new Cursor */
1227     else {
1228         /* The cursor is useless, hide it */
1229         if (scr->cu) {
1230             /* Paranoia -- Put the cursor NEAR where it belongs */
1231             (void)((*Term->curs_hook)(w - 1, scr->cy));
1232
1233             /* Make the cursor invisible */
1234             /* term_xtra(TERM_XTRA_SHAPE, 0); */
1235         }
1236
1237         /* The cursor is invisible, hide it */
1238         else if (!scr->cv) {
1239             /* Paranoia -- Put the cursor where it belongs */
1240             (void)((*Term->curs_hook)(scr->cx, scr->cy));
1241
1242             /* Make the cursor invisible */
1243             /* term_xtra(TERM_XTRA_SHAPE, 0); */
1244         }
1245
1246         /* The cursor is visible, display it correctly */
1247         else {
1248             /* Put the cursor where it belongs */
1249             (void)((*Term->curs_hook)(scr->cx, scr->cy));
1250
1251             /* Make the cursor visible */
1252             term_xtra(TERM_XTRA_SHAPE, 1);
1253         }
1254     }
1255
1256     /* Save the "cursor state" */
1257     old->cu = scr->cu;
1258     old->cv = scr->cv;
1259     old->cx = scr->cx;
1260     old->cy = scr->cy;
1261
1262     /* Actually flush the output */
1263     term_xtra(TERM_XTRA_FRESH, 0);
1264     return 0;
1265 }
1266
1267 /*** Output routines ***/
1268
1269 /*
1270  * Set the cursor visibility
1271  */
1272 errr term_set_cursor(int v)
1273 {
1274     /* Already done */
1275     if (Term->scr->cv == v)
1276         return 1;
1277
1278     /* Change */
1279     Term->scr->cv = (bool)v;
1280     return 0;
1281 }
1282
1283 /*
1284  * Place the cursor at a given location
1285  *
1286  * Note -- "illegal" requests do not move the cursor.
1287  */
1288 errr term_gotoxy(TERM_LEN x, TERM_LEN y)
1289 {
1290     int w = Term->wid;
1291     int h = Term->hgt;
1292
1293     /* Verify */
1294     if ((x < 0) || (x >= w))
1295         return -1;
1296     if ((y < 0) || (y >= h))
1297         return -1;
1298
1299     /* Remember the cursor */
1300     Term->scr->cx = (byte)x;
1301     Term->scr->cy = (byte)y;
1302
1303     /* The cursor is not useless */
1304     Term->scr->cu = 0;
1305     return 0;
1306 }
1307
1308 /*
1309  * At a given location, place an attr/char
1310  * Do not change the cursor position
1311  * No visual changes until "term_fresh()".
1312  */
1313 errr term_draw(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1314 {
1315     int w = Term->wid;
1316     int h = Term->hgt;
1317
1318     if ((x < 0) || (x >= w))
1319         return -1;
1320     if ((y < 0) || (y >= h))
1321         return -1;
1322
1323     /* Paranoia -- illegal char */
1324     if (!c)
1325         return (-2);
1326
1327     /* Queue it for later */
1328     term_queue_char(x, y, a, c, 0, 0);
1329     return 0;
1330 }
1331
1332 /*
1333  * Using the given attr, add the given char at the cursor.
1334  *
1335  * We return "-2" if the character is "illegal". XXX XXX
1336  *
1337  * We return "-1" if the cursor is currently unusable.
1338  *
1339  * We queue the given attr/char for display at the current
1340  * cursor location, and advance the cursor to the right,
1341  * marking it as unuable and returning "1" if it leaves
1342  * the screen, and otherwise returning "0".
1343  *
1344  * So when this function, or the following one, return a
1345  * positive value, future calls to either function will
1346  * return negative ones.
1347  */
1348 errr term_addch(TERM_COLOR a, char c)
1349 {
1350     TERM_LEN w = Term->wid;
1351
1352     /* Handle "unusable" cursor */
1353     if (Term->scr->cu)
1354         return -1;
1355
1356     /* Paranoia -- no illegal chars */
1357     if (!c)
1358         return (-2);
1359
1360     /* Queue the given character for display */
1361     term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1362
1363     /* Advance the cursor */
1364     Term->scr->cx++;
1365
1366     /* Success */
1367     if (Term->scr->cx < w)
1368         return 0;
1369
1370     /* Note "Useless" cursor */
1371     Term->scr->cu = 1;
1372
1373     /* Note "Useless" cursor */
1374     return 1;
1375 }
1376
1377 /*
1378  * Bigtile version of term_addch().
1379  *
1380  * If use_bigtile is FALSE, simply call term_addch() .
1381  *
1382  * Otherwise, queue a pair of attr/char for display at the current
1383  * cursor location, and advance the cursor to the right by two.
1384  */
1385 errr term_add_bigch(TERM_COLOR a, char c)
1386 {
1387     if (!use_bigtile)
1388         return term_addch(a, c);
1389
1390     /* Handle "unusable" cursor */
1391     if (Term->scr->cu)
1392         return -1;
1393
1394     /* Paranoia -- no illegal chars */
1395     if (!c)
1396         return (-2);
1397
1398     /* Queue the given character for display */
1399     term_queue_bigchar(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1400
1401     /* Advance the cursor */
1402     Term->scr->cx += 2;
1403
1404     /* Success */
1405     if (Term->scr->cx < Term->wid)
1406         return 0;
1407
1408     /* Note "Useless" cursor */
1409     Term->scr->cu = 1;
1410
1411     /* Note "Useless" cursor */
1412     return 1;
1413 }
1414
1415 /*
1416  * At the current location, using an attr, add a string
1417  *
1418  * We also take a length "n", using negative values to imply
1419  * the largest possible value, and then we use the minimum of
1420  * this length and the "actual" length of the string as the
1421  * actual number of characters to attempt to display, never
1422  * displaying more characters than will actually fit, since
1423  * we do NOT attempt to "wrap" the cursor at the screen edge.
1424  *
1425  * We return "-1" if the cursor is currently unusable.
1426  * We return "N" if we were "only" able to write "N" chars,
1427  * even if all of the given characters fit on the screen,
1428  * and mark the cursor as unusable for future attempts.
1429  *
1430  * So when this function, or the preceding one, return a
1431  * positive value, future calls to either function will
1432  * return negative ones.
1433  */
1434 errr term_addstr(int n, TERM_COLOR a, concptr s)
1435 {
1436     int k;
1437     TERM_LEN w = Term->wid;
1438     errr res = 0;
1439
1440     /* Handle "unusable" cursor */
1441     if (Term->scr->cu)
1442         return -1;
1443
1444     /* Obtain maximal length */
1445     k = (n < 0) ? (w + 1) : n;
1446
1447     /* Obtain the usable string length */
1448     for (n = 0; (n < k) && s[n]; n++) /* loop */
1449         ;
1450
1451     /* React to reaching the edge of the screen */
1452     if (Term->scr->cx + n >= w)
1453         res = n = w - Term->scr->cx;
1454
1455     /* Queue the first "n" characters for display */
1456     term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
1457
1458     /* Advance the cursor */
1459     Term->scr->cx += (byte)n;
1460
1461     /* Notice "Useless" cursor */
1462     if (res)
1463         Term->scr->cu = 1;
1464
1465     return res;
1466 }
1467
1468 /*
1469  * Move to a location and, using an attr, add a char
1470  */
1471 errr term_putch(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1472 {
1473     errr res;
1474
1475     /* Move first */
1476     if ((res = term_gotoxy(x, y)) != 0)
1477         return (res);
1478
1479     /* Then add the char */
1480     if ((res = term_addch(a, c)) != 0)
1481         return (res);
1482
1483     return 0;
1484 }
1485
1486 /*
1487  * Move to a location and, using an attr, add a string
1488  */
1489 errr term_putstr(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
1490 {
1491     errr res;
1492
1493     /* Move first */
1494     if ((res = term_gotoxy(x, y)) != 0)
1495         return (res);
1496
1497     /* Then add the string */
1498     if ((res = term_addstr(n, a, s)) != 0)
1499         return (res);
1500
1501     return 0;
1502 }
1503
1504 /*
1505  * Place cursor at (x,y), and clear the next "n" chars
1506  */
1507 errr term_erase(TERM_LEN x, TERM_LEN y, int n)
1508 {
1509     TERM_LEN w = Term->wid;
1510     /* int h = Term->hgt; */
1511
1512     TERM_LEN x1 = -1;
1513     TERM_LEN x2 = -1;
1514
1515     int na = Term->attr_blank;
1516     int nc = Term->char_blank;
1517
1518     TERM_COLOR *scr_aa;
1519     char *scr_cc;
1520
1521     TERM_COLOR *scr_taa;
1522     char *scr_tcc;
1523
1524     /* Place cursor */
1525     if (term_gotoxy(x, y))
1526         return -1;
1527
1528     /* Force legal size */
1529     if (x + n > w)
1530         n = w - x;
1531
1532     /* Fast access */
1533     scr_aa = Term->scr->a[y];
1534     scr_cc = Term->scr->c[y];
1535
1536     scr_taa = Term->scr->ta[y];
1537     scr_tcc = Term->scr->tc[y];
1538
1539 #ifdef JP
1540     /*
1541      * 全角文字の右半分から文字を表示する場合、
1542      * 重なった文字の左部分を消去。
1543      */
1544     if (n > 0 && (((scr_aa[x] & AF_KANJI2) && !(scr_aa[x] & AF_TILE1)) || (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2))
1545 #else
1546     if (n > 0 && (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2)
1547 #endif
1548     {
1549         x--;
1550         n++;
1551     }
1552
1553     /* Scan every column */
1554     for (int i = 0; i < n; i++, x++) {
1555         int oa = scr_aa[x];
1556         int oc = scr_cc[x];
1557
1558         /* Ignore "non-changes" */
1559         if ((oa == na) && (oc == nc))
1560             continue;
1561
1562 #ifdef JP
1563         /*
1564          * 全角文字の左半分で表示を終了する場合、
1565          * 重なった文字の右部分を消去。
1566          *
1567          * 2001/04/29 -- Habu
1568          * 行の右端の場合はこの処理をしないように修正。
1569          */
1570         if ((oa & AF_KANJI1) && (i + 1) == n && x != w - 1)
1571             n++;
1572 #endif
1573         /* Save the "literal" information */
1574         scr_aa[x] = (byte)na;
1575         scr_cc[x] = (char)nc;
1576
1577         scr_taa[x] = 0;
1578         scr_tcc[x] = 0;
1579
1580         /* Track minimum changed column */
1581         if (x1 < 0)
1582             x1 = x;
1583
1584         /* Track maximum changed column */
1585         x2 = x;
1586     }
1587
1588     /* Expand the "change area" as needed */
1589     if (x1 >= 0) {
1590         /* Check for new min/max row info */
1591         if (y < Term->y1)
1592             Term->y1 = (byte)y;
1593         if (y > Term->y2)
1594             Term->y2 = (byte)y;
1595
1596         /* Check for new min/max col info in this row */
1597         if (x1 < Term->x1[y])
1598             Term->x1[y] = (byte)x1;
1599         if (x2 > Term->x2[y])
1600             Term->x2[y] = (byte)x2;
1601     }
1602
1603     return 0;
1604 }
1605
1606 /*
1607  * Clear the entire window, and move to the top left corner
1608  *
1609  * Note the use of the special "total_erase" code
1610  */
1611 errr term_clear(void)
1612 {
1613     TERM_LEN w = Term->wid;
1614     TERM_LEN h = Term->hgt;
1615
1616     TERM_COLOR na = Term->attr_blank;
1617     char nc = Term->char_blank;
1618
1619     /* Cursor usable */
1620     Term->scr->cu = 0;
1621
1622     /* Cursor to the top left */
1623     Term->scr->cx = Term->scr->cy = 0;
1624
1625     /* Wipe each row */
1626     for (TERM_LEN y = 0; y < h; y++) {
1627         TERM_COLOR *scr_aa = Term->scr->a[y];
1628         char *scr_cc = Term->scr->c[y];
1629
1630         TERM_COLOR *scr_taa = Term->scr->ta[y];
1631         char *scr_tcc = Term->scr->tc[y];
1632
1633         /* Wipe each column */
1634         for (TERM_LEN x = 0; x < w; x++) {
1635             scr_aa[x] = na;
1636             scr_cc[x] = nc;
1637
1638             scr_taa[x] = 0;
1639             scr_tcc[x] = 0;
1640         }
1641
1642         /* This row has changed */
1643         Term->x1[y] = 0;
1644         Term->x2[y] = w - 1;
1645     }
1646
1647     /* Every row has changed */
1648     Term->y1 = 0;
1649     Term->y2 = h - 1;
1650
1651     /* Force "total erase" */
1652     Term->total_erase = TRUE;
1653     return 0;
1654 }
1655
1656 /*
1657  * Redraw (and refresh) the whole window.
1658  */
1659 errr term_redraw(void)
1660 {
1661     /* Force "total erase" */
1662     Term->total_erase = TRUE;
1663     term_fresh();
1664     return 0;
1665 }
1666
1667 /*
1668  * Redraw part of a window.
1669  */
1670 errr term_redraw_section(TERM_LEN x1, TERM_LEN y1, TERM_LEN x2, TERM_LEN y2)
1671 {
1672     char *g_ptr;
1673
1674     /* Bounds checking */
1675     if (y2 >= Term->hgt)
1676         y2 = Term->hgt - 1;
1677     if (x2 >= Term->wid)
1678         x2 = Term->wid - 1;
1679     if (y1 < 0)
1680         y1 = 0;
1681     if (x1 < 0)
1682         x1 = 0;
1683
1684     /* Set y limits */
1685     Term->y1 = (byte)y1;
1686     Term->y2 = (byte)y2;
1687
1688     /* Set the x limits */
1689     for (int i = Term->y1; i <= Term->y2; i++) {
1690 #ifdef JP
1691         int x1j = x1;
1692         int x2j = x2;
1693
1694         if (x1j > 0) {
1695             if (Term->scr->a[i][x1j] & AF_KANJI2)
1696                 x1j--;
1697         }
1698
1699         if (x2j < Term->wid - 1) {
1700             if (Term->scr->a[i][x2j] & AF_KANJI1)
1701                 x2j++;
1702         }
1703
1704         Term->x1[i] = (byte)x1j;
1705         Term->x2[i] = (byte)x2j;
1706
1707         g_ptr = Term->old->c[i];
1708
1709         /* Clear the section so it is redrawn */
1710         for (int j = x1j; j <= x2j; j++) {
1711             /* Hack - set the old character to "none" */
1712             g_ptr[j] = 0;
1713         }
1714 #else
1715         Term->x1[i] = x1;
1716         Term->x2[i] = x2;
1717
1718         g_ptr = Term->old->c[i];
1719
1720         /* Clear the section so it is redrawn */
1721         for (int j = x1; j <= x2; j++) {
1722             /* Hack - set the old character to "none" */
1723             g_ptr[j] = 0;
1724         }
1725 #endif
1726     }
1727
1728     term_fresh();
1729     return 0;
1730 }
1731
1732 /*** Access routines ***/
1733
1734 /*
1735  * Extract the cursor visibility
1736  */
1737 errr term_get_cursor(int *v)
1738 {
1739     /* Extract visibility */
1740     (*v) = Term->scr->cv;
1741     return 0;
1742 }
1743
1744 /*
1745  * Extract the current window size
1746  */
1747 errr term_get_size(TERM_LEN *w, TERM_LEN *h)
1748 {
1749     /* Access the cursor */
1750     (*w) = Term->wid;
1751     (*h) = Term->hgt;
1752     return 0;
1753 }
1754
1755 /*
1756  * Extract the current cursor location
1757  */
1758 errr term_locate(TERM_LEN *x, TERM_LEN *y)
1759 {
1760     /* Access the cursor */
1761     (*x) = Term->scr->cx;
1762     (*y) = Term->scr->cy;
1763
1764     /* Warn about "useless" cursor */
1765     if (Term->scr->cu)
1766         return 1;
1767
1768     return 0;
1769 }
1770
1771 /*
1772  * At a given location, determine the "current" attr and char
1773  * Note that this refers to what will be on the window after the
1774  * next call to "term_fresh()".  It may or may not already be there.
1775  */
1776 errr term_what(TERM_LEN x, TERM_LEN y, TERM_COLOR *a, char *c)
1777 {
1778     TERM_LEN w = Term->wid;
1779     TERM_LEN h = Term->hgt;
1780
1781     if ((x < 0) || (x >= w))
1782         return -1;
1783     if ((y < 0) || (y >= h))
1784         return -1;
1785
1786     /* Direct access */
1787     (*a) = Term->scr->a[y][x];
1788     (*c) = Term->scr->c[y][x];
1789     return 0;
1790 }
1791
1792 /*** Input routines ***/
1793
1794 /*
1795  * Flush and forget the input
1796  */
1797 errr term_flush(void)
1798 {
1799     /* Flush all events */
1800     term_xtra(TERM_XTRA_FLUSH, 0);
1801
1802     /* Forget all keypresses */
1803     Term->key_head = Term->key_tail = 0;
1804     return 0;
1805 }
1806
1807 /*
1808  * Add a keypress to the FRONT of the "queue"
1809  */
1810 errr term_key_push(int k)
1811 {
1812     /* Refuse to enqueue non-keys */
1813     if (!k)
1814         return -1;
1815
1816     /* Overflow may induce circular queue */
1817     if (Term->key_tail == 0)
1818         Term->key_tail = Term->key_size;
1819
1820     /* Back up, Store the char */
1821     Term->key_queue[--Term->key_tail] = (char)k;
1822
1823     if (Term->key_head != Term->key_tail)
1824         return 0;
1825
1826     return 1;
1827 }
1828
1829 /*
1830  * Check for a pending keypress on the key queue.
1831  *
1832  * Store the keypress, if any, in "ch", and return "0".
1833  * Otherwise store "zero" in "ch", and return "1".
1834  *
1835  * Wait for a keypress if "wait" is true.
1836  *
1837  * Remove the keypress if "take" is true.
1838  */
1839 errr term_inkey(char *ch, bool wait, bool take)
1840 {
1841     /* Assume no key */
1842     (*ch) = '\0';
1843
1844     /* get bored */
1845     if (!Term->never_bored) {
1846         /* Process random events */
1847         term_xtra(TERM_XTRA_BORED, 0);
1848     }
1849
1850     /* Wait */
1851     if (wait) {
1852         /* Process pending events while necessary */
1853         while (Term->key_head == Term->key_tail) {
1854             /* Process events (wait for one) */
1855             term_xtra(TERM_XTRA_EVENT, TRUE);
1856         }
1857     }
1858
1859     /* Do not Wait */
1860     else {
1861         /* Process pending events if necessary */
1862         if (Term->key_head == Term->key_tail) {
1863             /* Process events (do not wait) */
1864             term_xtra(TERM_XTRA_EVENT, FALSE);
1865         }
1866     }
1867
1868     /* No keys are ready */
1869     if (Term->key_head == Term->key_tail)
1870         return 1;
1871
1872     /* Extract the next keypress */
1873     (*ch) = Term->key_queue[Term->key_tail];
1874
1875     /* If requested, advance the queue, wrap around if necessary */
1876     if (take && (++Term->key_tail == Term->key_size))
1877         Term->key_tail = 0;
1878
1879     return 0;
1880 }
1881
1882 /*** Extra routines ***/
1883
1884 /*
1885  * Save the "requested" screen into the "memorized" screen
1886  *
1887  * Every "term_save()" should match exactly one "term_load()"
1888  */
1889 errr term_save(void)
1890 {
1891     TERM_LEN w = Term->wid;
1892     TERM_LEN h = Term->hgt;
1893
1894     /* Create */
1895     if (!Term->mem) {
1896         /* Allocate window */
1897         MAKE(Term->mem, term_win);
1898
1899         /* Initialize window */
1900         term_win_init(Term->mem, w, h);
1901     }
1902
1903     /* Grab */
1904     term_win_copy(Term->mem, Term->scr, w, h);
1905     return 0;
1906 }
1907
1908 /*
1909  * Restore the "requested" contents (see above).
1910  *
1911  * Every "term_save()" should match exactly one "term_load()"
1912  */
1913 errr term_load(void)
1914 {
1915     TERM_LEN w = Term->wid;
1916     TERM_LEN h = Term->hgt;
1917
1918     /* Create */
1919     if (!Term->mem) {
1920         /* Allocate window */
1921         MAKE(Term->mem, term_win);
1922
1923         /* Initialize window */
1924         term_win_init(Term->mem, w, h);
1925     }
1926
1927     /* Load */
1928     term_win_copy(Term->scr, Term->mem, w, h);
1929
1930     /* Assume change */
1931     for (TERM_LEN y = 0; y < h; y++) {
1932         /* Assume change */
1933         Term->x1[y] = 0;
1934         Term->x2[y] = w - 1;
1935     }
1936
1937     /* Assume change */
1938     Term->y1 = 0;
1939     Term->y2 = h - 1;
1940     return 0;
1941 }
1942
1943 /*
1944  * Exchange the "requested" screen with the "tmp" screen
1945  */
1946 static errr term_exchange(void)
1947 {
1948     TERM_LEN w = Term->wid;
1949     TERM_LEN h = Term->hgt;
1950
1951     term_win *exchanger;
1952
1953     /* Create */
1954     if (!Term->tmp) {
1955         /* Allocate window */
1956         MAKE(Term->tmp, term_win);
1957
1958         /* Initialize window */
1959         term_win_init(Term->tmp, w, h);
1960     }
1961
1962     /* Swap */
1963     exchanger = Term->scr;
1964     Term->scr = Term->tmp;
1965     Term->tmp = exchanger;
1966
1967     /* Assume change */
1968     for (TERM_LEN y = 0; y < h; y++) {
1969         /* Assume change */
1970         Term->x1[y] = 0;
1971         Term->x2[y] = w - 1;
1972     }
1973
1974     /* Assume change */
1975     Term->y1 = 0;
1976     Term->y2 = h - 1;
1977     return 0;
1978 }
1979
1980 /*
1981  * React to a new physical window size.
1982  */
1983 errr term_resize(TERM_LEN w, TERM_LEN h)
1984 {
1985     TERM_LEN wid, hgt;
1986
1987     TERM_LEN *hold_x1;
1988     TERM_LEN *hold_x2;
1989
1990     term_win *hold_old;
1991     term_win *hold_scr;
1992     term_win *hold_mem;
1993     term_win *hold_tmp;
1994
1995     /* Resizing is forbidden */
1996     if (Term->fixed_shape)
1997         return -1;
1998
1999     /* Ignore illegal changes */
2000     if ((w < 1) || (h < 1))
2001         return -1;
2002
2003     /* Ignore non-changes */
2004     if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile))
2005         return 1;
2006
2007     use_bigtile = arg_bigtile;
2008
2009     /* Minimum dimensions */
2010     wid = MIN(Term->wid, w);
2011     hgt = MIN(Term->hgt, h);
2012
2013     /* Save scanners */
2014     hold_x1 = Term->x1;
2015     hold_x2 = Term->x2;
2016
2017     /* Save old window */
2018     hold_old = Term->old;
2019
2020     /* Save old window */
2021     hold_scr = Term->scr;
2022
2023     /* Save old window */
2024     hold_mem = Term->mem;
2025
2026     /* Save old window */
2027     hold_tmp = Term->tmp;
2028
2029     /* Create new scanners */
2030     C_MAKE(Term->x1, h, TERM_LEN);
2031     C_MAKE(Term->x2, h, TERM_LEN);
2032
2033     /* Create new window */
2034     MAKE(Term->old, term_win);
2035
2036     /* Initialize new window */
2037     term_win_init(Term->old, w, h);
2038
2039     /* Save the contents */
2040     term_win_copy(Term->old, hold_old, wid, hgt);
2041
2042     /* Create new window */
2043     MAKE(Term->scr, term_win);
2044
2045     /* Initialize new window */
2046     term_win_init(Term->scr, w, h);
2047
2048     /* Save the contents */
2049     term_win_copy(Term->scr, hold_scr, wid, hgt);
2050
2051     /* If needed */
2052     if (hold_mem) {
2053         /* Create new window */
2054         MAKE(Term->mem, term_win);
2055
2056         /* Initialize new window */
2057         term_win_init(Term->mem, w, h);
2058
2059         /* Save the contents */
2060         term_win_copy(Term->mem, hold_mem, wid, hgt);
2061     }
2062
2063     /* If needed */
2064     if (hold_tmp) {
2065         /* Create new window */
2066         MAKE(Term->tmp, term_win);
2067
2068         /* Initialize new window */
2069         term_win_init(Term->tmp, w, h);
2070
2071         /* Save the contents */
2072         term_win_copy(Term->tmp, hold_tmp, wid, hgt);
2073     }
2074
2075     /* Free some arrays */
2076     C_KILL(hold_x1, Term->hgt, TERM_LEN);
2077     C_KILL(hold_x2, Term->hgt, TERM_LEN);
2078
2079     /* Nuke */
2080     term_win_nuke(hold_old, Term->wid, Term->hgt);
2081
2082     /* Kill */
2083     KILL(hold_old, term_win);
2084
2085     /* Illegal cursor */
2086     if (Term->old->cx >= w)
2087         Term->old->cu = 1;
2088     if (Term->old->cy >= h)
2089         Term->old->cu = 1;
2090
2091     /* Nuke */
2092     term_win_nuke(hold_scr, Term->wid, Term->hgt);
2093
2094     /* Kill */
2095     KILL(hold_scr, term_win);
2096
2097     /* Illegal cursor */
2098     if (Term->scr->cx >= w)
2099         Term->scr->cu = 1;
2100     if (Term->scr->cy >= h)
2101         Term->scr->cu = 1;
2102
2103     /* If needed */
2104     if (hold_mem) {
2105         /* Nuke */
2106         term_win_nuke(hold_mem, Term->wid, Term->hgt);
2107
2108         /* Kill */
2109         KILL(hold_mem, term_win);
2110
2111         /* Illegal cursor */
2112         if (Term->mem->cx >= w)
2113             Term->mem->cu = 1;
2114         if (Term->mem->cy >= h)
2115             Term->mem->cu = 1;
2116     }
2117
2118     /* If needed */
2119     if (hold_tmp) {
2120         /* Nuke */
2121         term_win_nuke(hold_tmp, Term->wid, Term->hgt);
2122
2123         /* Kill */
2124         KILL(hold_tmp, term_win);
2125
2126         /* Illegal cursor */
2127         if (Term->tmp->cx >= w)
2128             Term->tmp->cu = 1;
2129         if (Term->tmp->cy >= h)
2130             Term->tmp->cu = 1;
2131     }
2132
2133     /* Save new size */
2134     Term->wid = w;
2135     Term->hgt = h;
2136
2137     /* Force "total erase" */
2138     Term->total_erase = TRUE;
2139
2140     /* Assume change */
2141     for (int i = 0; i < h; i++) {
2142         /* Assume change */
2143         Term->x1[i] = 0;
2144         Term->x2[i] = w - 1;
2145     }
2146
2147     /* Assume change */
2148     Term->y1 = 0;
2149     Term->y2 = h - 1;
2150
2151     /* Execute the "resize_hook" hook, if available */
2152     if (Term->resize_hook)
2153         Term->resize_hook();
2154
2155     return 0;
2156 }
2157
2158 /*
2159  * Activate a new Term (and deactivate the current Term)
2160  *
2161  * This function is extremely important, and also somewhat bizarre.
2162  * It is the only function that should "modify" the value of "Term".
2163  *
2164  * To "create" a valid "term", one should do "term_init(t)", then
2165  * set the various flags and hooks, and then do "term_activate(t)".
2166  */
2167 errr term_activate(term_type *t)
2168 {
2169     /* already done */
2170     if (Term == t)
2171         return 1;
2172
2173     /* Deactivate the old Term */
2174     if (Term)
2175         term_xtra(TERM_XTRA_LEVEL, 0);
2176
2177     /* Call the special "init" hook */
2178     if (t && !t->active_flag) {
2179         /* Call the "init" hook */
2180         if (t->init_hook)
2181             (*t->init_hook)(t);
2182
2183         /* Remember */
2184         t->active_flag = TRUE;
2185
2186         /* Assume mapped */
2187         t->mapped_flag = TRUE;
2188     }
2189
2190     /* Remember the Term */
2191     Term = t;
2192
2193     /* Activate the new Term */
2194     if (Term)
2195         term_xtra(TERM_XTRA_LEVEL, 1);
2196
2197     return 0;
2198 }
2199
2200 /*
2201  * Initialize a term, using a window of the given size.
2202  * Also prepare the "input queue" for "k" keypresses
2203  * By default, the cursor starts out "invisible"
2204  * By default, we "erase" using "black spaces"
2205  */
2206 errr term_init(term_type *t, TERM_LEN w, TERM_LEN h, int k)
2207 {
2208     /* Wipe it */
2209     (void)WIPE(t, term_type);
2210
2211     /* Prepare the input queue */
2212     t->key_head = t->key_tail = 0;
2213
2214     /* Determine the input queue size */
2215     t->key_size = (u16b)k;
2216
2217     /* Allocate the input queue */
2218     C_MAKE(t->key_queue, t->key_size, char);
2219
2220     /* Save the size */
2221     t->wid = w;
2222     t->hgt = h;
2223
2224     /* Allocate change arrays */
2225     C_MAKE(t->x1, h, TERM_LEN);
2226     C_MAKE(t->x2, h, TERM_LEN);
2227
2228     /* Allocate "displayed" */
2229     MAKE(t->old, term_win);
2230
2231     /* Initialize "displayed" */
2232     term_win_init(t->old, w, h);
2233
2234     /* Allocate "requested" */
2235     MAKE(t->scr, term_win);
2236
2237     /* Initialize "requested" */
2238     term_win_init(t->scr, w, h);
2239
2240     /* Assume change */
2241     for (TERM_LEN y = 0; y < h; y++) {
2242         /* Assume change */
2243         t->x1[y] = 0;
2244         t->x2[y] = w - 1;
2245     }
2246
2247     /* Assume change */
2248     t->y1 = 0;
2249     t->y2 = h - 1;
2250
2251     /* Force "total erase" */
2252     t->total_erase = TRUE;
2253
2254     /* Default "blank" */
2255     t->attr_blank = 0;
2256     t->char_blank = ' ';
2257
2258     /* Prepare "fake" hooks to prevent core dumps */
2259     t->curs_hook = term_curs_hack;
2260     t->bigcurs_hook = term_bigcurs_hack;
2261     t->wipe_hook = term_wipe_hack;
2262     t->text_hook = term_text_hack;
2263     t->pict_hook = term_pict_hack;
2264     return 0;
2265 }
2266
2267 #ifdef JP
2268 /*
2269  * Move to a location and, using an attr, add a string vertically
2270  */
2271 errr term_putstr_v(TERM_LEN x, TERM_LEN y, int n, byte a, concptr s)
2272 {
2273     errr res;
2274     int y0 = y;
2275
2276     for (int i = 0; i < n && s[i] != 0; i++) {
2277         /* Move first */
2278         if ((res = term_gotoxy(x, y0)) != 0)
2279             return (res);
2280
2281         if (iskanji(s[i])) {
2282             if ((res = term_addstr(2, a, &s[i])) != 0)
2283                 return (res);
2284             i++;
2285             y0++;
2286             if (s[i] == 0)
2287                 break;
2288         } else {
2289             if ((res = term_addstr(1, a, &s[i])) != 0)
2290                 return (res);
2291             y0++;
2292         }
2293     }
2294
2295     return 0;
2296 }
2297 #endif
2298
2299 #ifndef WINDOWS
2300 errr term_nuke(term_type *t)
2301 {
2302     TERM_LEN w = t->wid;
2303     TERM_LEN h = t->hgt;
2304     if (t->active_flag) {
2305         if (t->nuke_hook)
2306             (*t->nuke_hook)(t);
2307
2308         t->active_flag = FALSE;
2309         t->mapped_flag = FALSE;
2310     }
2311
2312     term_win_nuke(t->old, w, h);
2313     KILL(t->old, term_win);
2314     term_win_nuke(t->scr, w, h);
2315     KILL(t->scr, term_win);
2316     if (t->mem) {
2317         term_win_nuke(t->mem, w, h);
2318         KILL(t->mem, term_win);
2319     }
2320
2321     if (t->tmp) {
2322         term_win_nuke(t->tmp, w, h);
2323         KILL(t->tmp, term_win);
2324     }
2325
2326     C_KILL(t->x1, h, TERM_LEN);
2327     C_KILL(t->x2, h, TERM_LEN);
2328     C_KILL(t->key_queue, t->key_size, char);
2329     return 0;
2330 }
2331 #endif