OSDN Git Service

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