2 * @brief Purpose: a generic, efficient, terminal window package -BEN-
3 * Copyright (c) 1997 Ben Harrison
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.
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 #include "term/gameterm.h"
16 #include "game-option/map-screen-options.h"
18 /* Special flags in the attr data */
19 #define AF_BIGTILE2 0xf0
25 * 属性に全角文字の1バイト目、2バイト目も記憶。
28 #define AF_KANJI1 0x10
29 #define AF_KANJI2 0x20
30 #define AF_KANJIC 0x0f
33 /* The current "term" */
34 term_type *Term = NULL;
36 /*** Local routines ***/
39 * Nuke a term_win (see below)
41 errr term_win_nuke(term_win *s, TERM_LEN w, TERM_LEN h)
43 /* Free the window access arrays */
44 C_KILL(s->a, h, TERM_COLOR *);
45 C_KILL(s->c, h, char *);
47 /* Free the window content arrays */
48 C_KILL(s->va, h * w, TERM_COLOR);
49 C_KILL(s->vc, h * w, char);
51 /* Free the terrain access arrays */
52 C_KILL(s->ta, h, TERM_COLOR *);
53 C_KILL(s->tc, h, char *);
55 /* Free the terrain content arrays */
56 C_KILL(s->vta, h * w, TERM_COLOR);
57 C_KILL(s->vtc, h * w, char);
63 * Initialize a "term_win" (using the given window size)
65 static errr term_win_init(term_win *s, TERM_LEN w, TERM_LEN h)
67 /* Make the window access arrays */
68 C_MAKE(s->a, h, TERM_COLOR *);
69 C_MAKE(s->c, h, char *);
71 /* Make the window content arrays */
72 C_MAKE(s->va, h * w, TERM_COLOR);
73 C_MAKE(s->vc, h * w, char);
75 /* Make the terrain access arrays */
76 C_MAKE(s->ta, h, TERM_COLOR *);
77 C_MAKE(s->tc, h, char *);
79 /* Make the terrain content arrays */
80 C_MAKE(s->vta, h * w, TERM_COLOR);
81 C_MAKE(s->vtc, h * w, char);
83 /* Prepare the window access arrays */
84 for (TERM_LEN y = 0; y < h; y++) {
85 s->a[y] = s->va + w * y;
86 s->c[y] = s->vc + w * y;
88 s->ta[y] = s->vta + w * y;
89 s->tc[y] = s->vtc + w * y;
96 * Copy a "term_win" from another
98 static errr term_win_copy(term_win *s, term_win *f, TERM_LEN w, TERM_LEN h)
101 for (TERM_LEN y = 0; y < h; y++) {
102 TERM_COLOR *f_aa = f->a[y];
103 char *f_cc = f->c[y];
105 TERM_COLOR *s_aa = s->a[y];
106 char *s_cc = s->c[y];
108 TERM_COLOR *f_taa = f->ta[y];
109 char *f_tcc = f->tc[y];
111 TERM_COLOR *s_taa = s->ta[y];
112 char *s_tcc = s->tc[y];
114 for (TERM_LEN x = 0; x < w; x++) {
131 /*** External hooks ***/
134 * Execute the "Term->user_hook" hook, if available (see above).
136 errr term_user(int n)
138 /* Verify the hook */
139 if (!Term->user_hook)
143 return ((*Term->user_hook)(n));
147 * Execute the "Term->xtra_hook" hook, if available (see above).
149 errr term_xtra(int n, int v)
151 /* Verify the hook */
152 if (!Term->xtra_hook)
156 return ((*Term->xtra_hook)(n, v));
162 * Fake hook for "term_curs()" (see above)
164 static errr term_curs_hack(TERM_LEN x, TERM_LEN y)
174 * Fake hook for "term_bigcurs()" (see above)
176 static errr term_bigcurs_hack(TERM_LEN x, TERM_LEN y) { return (*Term->curs_hook)(x, y); }
179 * Fake hook for "term_wipe()" (see above)
181 static errr term_wipe_hack(TERM_LEN x, TERM_LEN y, int n)
192 * Fake hook for "term_text()" (see above)
194 static errr term_text_hack(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr cp)
207 * Fake hook for "term_pict()" (see above)
209 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)
223 /*** Efficient routines ***/
226 * Mentally draw an attr/char at a given location
227 * Assumes given location and values are valid.
229 void term_queue_char(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
231 term_win *scrn = Term->scr;
233 TERM_COLOR *scr_aa = &scrn->a[y][x];
234 char *scr_cc = &scrn->c[y][x];
236 TERM_COLOR *scr_taa = &scrn->ta[y][x];
237 char *scr_tcc = &scrn->tc[y][x];
239 /* Ignore non-changes */
240 if ((*scr_aa == a) && (*scr_cc == c) && (*scr_taa == ta) && (*scr_tcc == tc))
243 /* Save the "literal" information */
250 /* Check for new min/max row info */
256 /* Check for new min/max col info for this row */
258 Term->x1[y] = (byte)x;
260 Term->x2[y] = (byte)x;
263 if (((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2) || (scrn->a[y][x] & AF_KANJI2))
265 if ((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2)
267 if ((x - 1) < Term->x1[y])
272 * Bigtile version of term_queue_char().
273 * If use_bigtile is FALSE, simply call term_queue_char().
274 * Otherwise, mentally draw a pair of attr/char at a given location.
275 * Assumes given location and values are valid.
277 void term_queue_bigchar(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
282 * A table which relates each ascii character to a multibyte
285 * 「■」は二倍幅豆腐の内部コードに使用。
287 static char ascii_to_zenkaku[] = " !”#$%&’()*+,-./"
298 /* If non bigtile mode, call orginal function */
300 term_queue_char(x, y, a, c, ta, tc);
304 /* A tile becomes a Bigtile */
305 if ((a & AF_TILE1) && (c & 0x80)) {
306 /* Mark it as a Bigtile */
311 /* Ignore non-tile background */
312 if (!((ta & AF_TILE1) && (tc & 0x80))) {
320 * Use a multibyte character instead of a dirty pair of ASCII
323 else if (' ' <= c) /* isprint(c) */
325 c2 = ascii_to_zenkaku[2 * (c - ' ') + 1];
326 c = ascii_to_zenkaku[2 * (c - ' ')];
328 /* Mark it as a Kanji */
335 /* Dirty pair of ASCII characters */
340 /* Display pair of attr/char */
341 term_queue_char(x, y, a, c, ta, tc);
342 term_queue_char(x + 1, y, a2, c2, 0, 0);
346 * Mentally draw a string of attr/chars at a given location
347 * Assumes given location and values are valid.
348 * This function is designed to be fast, with no consistancy checking.
349 * It is used to update the map in the game.
351 void term_queue_line(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR *a, char *c, TERM_COLOR *ta, char *tc)
353 term_win *scrn = Term->scr;
358 TERM_COLOR *scr_aa = &scrn->a[y][x];
359 char *scr_cc = &scrn->c[y][x];
361 TERM_COLOR *scr_taa = &scrn->ta[y][x];
362 char *scr_tcc = &scrn->tc[y][x];
365 /* Ignore non-changes */
366 if ((*scr_aa == *a) && (*scr_cc == *c) && (*scr_taa == *ta) && (*scr_tcc == *tc)) {
379 /* Save the "literal" information */
383 /* Save the "literal" information */
387 /* Track minimum changed column */
391 /* Track maximum changed column */
397 /* Expand the "change area" as needed */
399 /* Check for new min/max row info */
405 /* Check for new min/max col info in this row */
406 if (x1 < Term->x1[y])
407 Term->x1[y] = (byte)x1;
408 if (x2 > Term->x2[y])
409 Term->x2[y] = (byte)x2;
414 * Mentally draw some attr/chars at a given location
416 * Assumes that (x,y) is a valid location, that the first "n" characters
417 * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
418 * a valid location, so the first "n" characters of "s" can all be added
419 * starting at (x,y) without causing any illegal operations.
421 static void term_queue_chars(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
423 TERM_LEN x1 = -1, x2 = -1;
425 TERM_COLOR *scr_aa = Term->scr->a[y];
427 char *scr_cc = Term->scr->c[y];
429 TERM_COLOR *scr_taa = Term->scr->ta[y];
430 char *scr_tcc = Term->scr->tc[y];
432 char *scr_cc = Term->scr->c[y];
434 TERM_COLOR *scr_taa = Term->scr->ta[y];
435 char *scr_tcc = Term->scr->tc[y];
440 if (n == 0 || *s == 0)
443 * 全角文字の右半分から文字を表示する場合、
447 if ((scr_aa[x] & AF_KANJI2) && (scr_aa[x] & AF_BIGTILE2) != AF_BIGTILE2) {
449 scr_aa[x - 1] &= AF_KANJIC;
453 /* Queue the attr/chars */
454 for (; n; x++, s++, n--) {
456 /* 特殊文字としてMSBが立っている可能性がある */
457 /* その場合attrのMSBも立っているのでこれで識別する */
459 if (!(a & AF_TILE1) && iskanji(*s)) {
463 byte na1 = (a | AF_KANJI1);
464 byte na2 = (a | AF_KANJI2);
466 if ((--n == 0) || !nc2)
469 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)
470 && (scr_tcc[x - 1] == 0) && (scr_tcc[x] == 0))
483 TERM_COLOR oa = scr_aa[x];
486 TERM_COLOR ota = scr_taa[x];
487 char otc = scr_tcc[x];
489 /* Ignore non-changes */
490 if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0))
493 /* Save the "literal" information */
500 /* Note the "range" of window updates */
511 * 全角文字の左半分で表示を終了する場合、
513 * (条件追加:タイルの1文字目でない事を確かめるように。)
518 term_get_size(&w, &h);
519 if (x != w && !(scr_aa[x] & AF_TILE1) && (scr_aa[x] & AF_KANJI2)) {
521 scr_aa[x] &= AF_KANJIC;
528 /* Expand the "change area" as needed */
530 /* Check for new min/max row info */
536 /* Check for new min/max col info in this row */
537 if (x1 < Term->x1[y])
538 Term->x1[y] = (byte)x1;
539 if (x2 > Term->x2[y])
540 Term->x2[y] = (byte)x2;
544 /*** Refresh routines ***/
547 * Flush a row of the current window (see "term_fresh")
548 * Display text using "term_pict()"
550 static void term_fresh_row_pict(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
552 TERM_COLOR *old_aa = Term->old->a[y];
553 char *old_cc = Term->old->c[y];
555 TERM_COLOR *scr_aa = Term->scr->a[y];
556 char *scr_cc = Term->scr->c[y];
558 TERM_COLOR *old_taa = Term->old->ta[y];
559 char *old_tcc = Term->old->tc[y];
561 TERM_COLOR *scr_taa = Term->scr->ta[y];
562 char *scr_tcc = Term->scr->tc[y];
586 /* Scan "modified" columns */
587 for (TERM_LEN x = x1; x <= x2; x++) {
588 /* See what is currently here */
592 /* See what is desired there */
605 /* 特殊文字としてMSBが立っている可能性がある */
606 /* その場合attrのMSBも立っているのでこれで識別する */
608 kanji = (iskanji(nc) && !(na & AF_TILE1));
617 /* Handle unchanged grids */
619 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
621 || (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])))
623 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
628 /* Draw pending attr/char pairs */
629 (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
646 /* Save new contents */
653 /* Restart and Advance */
660 /* Draw pending attr/char pairs */
661 (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
666 * Flush a row of the current window (see "term_fresh")
668 * Display text using "term_text()" and "term_wipe()",
669 * but use "term_pict()" for high-bit attr/char pairs
671 static void term_fresh_row_both(TERM_LEN y, int x1, int x2)
673 TERM_COLOR *old_aa = Term->old->a[y];
674 char *old_cc = Term->old->c[y];
676 TERM_COLOR *scr_aa = Term->scr->a[y];
677 char *scr_cc = Term->scr->c[y];
679 TERM_COLOR *old_taa = Term->old->ta[y];
680 char *old_tcc = Term->old->tc[y];
681 TERM_COLOR *scr_taa = Term->scr->ta[y];
682 char *scr_tcc = Term->scr->tc[y];
689 /* The "always_text" flag */
690 int always_text = Term->always_text;
699 byte fa = Term->attr_blank;
711 /* Scan "modified" columns */
712 for (TERM_LEN x = x1; x <= x2; x++) {
713 /* See what is currently here */
717 /* See what is desired there */
730 /* 特殊文字としてMSBが立っている可能性がある */
731 /* その場合attrのMSBも立っているのでこれで識別する */
733 /* kanji = (iskanji(nc)); */
734 kanji = (iskanji(nc) && !(na & AF_TILE1));
743 /* Handle unchanged grids */
745 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
747 || (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])))
749 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
754 /* Draw pending chars (normal) */
755 if (fa || always_text) {
756 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
759 /* Draw pending chars (black) */
761 (void)((*Term->wipe_hook)(fx, y, fn));
780 /* Save new contents */
787 /* 2nd byte of bigtile */
788 if ((na & AF_BIGTILE2) == AF_BIGTILE2)
791 /* Handle high-bit attr/chars */
792 if ((na & AF_TILE1) && (nc & 0x80)) {
795 /* Draw pending chars (normal) */
796 if (fa || always_text) {
797 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
800 /* Draw pending chars (black) */
802 (void)((*Term->wipe_hook)(fx, y, fn));
809 /* Draw the special attr/char pair */
810 (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc));
816 /* Notice new color */
818 if (fa != (na & AF_KANJIC))
826 /* Draw the pending chars */
827 if (fa || always_text) {
828 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
831 /* Erase "leading" spaces */
833 (void)((*Term->wipe_hook)(fx, y, fn));
840 /* Save the new color */
842 fa = (na & AF_KANJIC);
848 /* Restart and Advance */
855 /* Draw pending chars (normal) */
856 if (fa || always_text) {
857 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
860 /* Draw pending chars (black) */
862 (void)((*Term->wipe_hook)(fx, y, fn));
868 * Flush a row of the current window (see "term_fresh")
870 * Display text using "term_text()" and "term_wipe()"
872 static void term_fresh_row_text(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
874 TERM_COLOR *old_aa = Term->old->a[y];
875 char *old_cc = Term->old->c[y];
877 TERM_COLOR *scr_aa = Term->scr->a[y];
878 char *scr_cc = Term->scr->c[y];
880 /* The "always_text" flag */
881 int always_text = Term->always_text;
890 byte fa = Term->attr_blank;
902 for (TERM_LEN x = 0; x < x1; x++)
903 if (!(old_aa[x] & AF_TILE1) && iskanji(old_cc[x])) {
911 /* Scan "modified" columns */
912 for (TERM_LEN x = x1; x <= x2; x++) {
913 /* See what is currently here */
917 /* See what is desired there */
930 /* 特殊文字としてMSBが立っている可能性がある */
931 /* その場合attrのMSBも立っているのでこれで識別する */
933 kanji = (iskanji(nc) && !(na & AF_TILE1));
935 /* Handle unchanged grids */
937 if ((na == oa) && (nc == oc) && (!kanji || (scr_aa[x + 1] == old_aa[x + 1] && scr_cc[x + 1] == old_cc[x + 1])))
939 if ((na == oa) && (nc == oc))
945 /* Draw pending chars (normal) */
946 if (fa || always_text) {
947 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
950 /* Draw pending chars (black) */
952 (void)((*Term->wipe_hook)(fx, y, fn));
971 /* Save new contents */
975 /* Notice new color */
977 if (fa != (na & AF_KANJIC))
985 /* Draw the pending chars */
986 if (fa || always_text) {
987 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
990 /* Erase "leading" spaces */
992 (void)((*Term->wipe_hook)(fx, y, fn));
999 /* Save the new color */
1001 fa = (na & AF_KANJIC);
1007 /* Restart and Advance */
1014 /* Draw pending chars (normal) */
1015 if (fa || always_text) {
1016 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1019 /* Draw pending chars (black) */
1021 (void)((*Term->wipe_hook)(fx, y, fn));
1026 bool macro_running(void) {
1027 int diff = angband_term[0]->key_head - angband_term[0]->key_tail;
1028 return diff < -1 || 1 < diff;
1031 bool need_term_fresh(void) { return !macro_running() || fresh_after; }
1033 * @brief Actually perform all requested changes to the window
1035 errr term_fresh(void)
1043 term_win *old = Term->old;
1044 term_win *scr = Term->scr;
1046 /* Before initialize (Advice from Mr.shimitei)*/
1050 /* Do nothing unless "mapped" */
1051 if (!Term->mapped_flag)
1054 /* Trivial Refresh */
1055 if ((y1 > y2) && (scr->cu == old->cu) && (scr->cv == old->cv) && (scr->cx == old->cx) && (scr->cy == old->cy) && !(Term->total_erase)) {
1060 /* Handle "total erase" */
1061 if (Term->total_erase) {
1062 byte na = Term->attr_blank;
1063 char nc = Term->char_blank;
1065 /* Physically erase the entire window */
1066 term_xtra(TERM_XTRA_CLEAR, 0);
1068 /* clear all "cursor" data */
1069 old->cv = old->cu = old->cx = old->cy = 0;
1072 for (TERM_LEN y = 0; y < h; y++) {
1073 TERM_COLOR *aa = old->a[y];
1074 char *cc = old->c[y];
1076 TERM_COLOR *taa = old->ta[y];
1077 char *tcc = old->tc[y];
1079 /* Wipe each column */
1080 for (TERM_LEN x = 0; x < w; x++) {
1081 /* Wipe each grid */
1090 /* Redraw every row */
1092 Term->y2 = y2 = h - 1;
1094 /* Redraw every column */
1095 for (TERM_LEN y = 0; y < h; y++) {
1097 Term->x2[y] = w - 1;
1100 /* Forget "total erase" */
1101 Term->total_erase = FALSE;
1104 /* Cursor update -- Erase old Cursor */
1105 if (Term->soft_cursor) {
1106 /* Cursor was visible */
1107 if (!old->cu && old->cv) {
1109 TERM_LEN tx = old->cx;
1110 TERM_LEN ty = old->cy;
1112 TERM_COLOR *old_aa = old->a[ty];
1113 char *old_cc = old->c[ty];
1115 TERM_COLOR *old_taa = old->ta[ty];
1116 char *old_tcc = old->tc[ty];
1118 TERM_COLOR ota = old_taa[tx];
1119 char otc = old_tcc[tx];
1122 if (tx + 1 < Term->wid && !(old_aa[tx] & AF_TILE1) && iskanji(old_cc[tx]))
1125 /* Use "term_pict()" always */
1126 if (Term->always_pict)
1127 (void)((*Term->pict_hook)(tx, ty, csize, &old_aa[tx], &old_cc[tx], &ota, &otc));
1129 /* Use "term_pict()" sometimes */
1130 else if (Term->higher_pict && (old_aa[tx] & AF_TILE1) && (old_cc[tx] & 0x80))
1131 (void)((*Term->pict_hook)(tx, ty, 1, &old_aa[tx], &old_cc[tx], &ota, &otc));
1134 * Restore the actual character
1135 * 元の文字の描画範囲がカーソルより小さいと、
1136 * 上書きされなかった部分がゴミとして残る。
1137 * wipe_hook でカーソルを消去して text_hook で書き直す。
1139 else if (old_aa[tx] || Term->always_text) {
1140 (void)((*Term->wipe_hook)(tx, ty, 1));
1141 (void)((*Term->text_hook)(tx, ty, csize, (unsigned char)(old_aa[tx] & 0xf), &old_cc[tx]));
1144 /* Erase the grid */
1146 (void)((*Term->wipe_hook)(tx, ty, 1));
1150 /* Cursor Update -- Erase old Cursor */
1152 /* Cursor will be invisible */
1153 if (scr->cu || !scr->cv) {
1154 /* Make the cursor invisible */
1155 term_xtra(TERM_XTRA_SHAPE, 0);
1159 /* Something to update */
1161 /* Handle "icky corner" */
1162 if (Term->icky_corner) {
1163 /* Avoid the corner */
1165 /* Avoid the corner */
1166 if (Term->x2[h - 1] > w - 2) {
1167 /* Avoid the corner */
1168 Term->x2[h - 1] = w - 2;
1173 /* Scan the "modified" rows */
1174 for (TERM_LEN y = y1; y <= y2; ++y) {
1175 TERM_LEN x1 = Term->x1[y];
1176 TERM_LEN x2 = Term->x2[y];
1178 /* Flush each "modified" row */
1180 /* Always use "term_pict()" */
1181 if (Term->always_pict) {
1183 term_fresh_row_pict(y, x1, x2);
1186 /* Sometimes use "term_pict()" */
1187 else if (Term->higher_pict) {
1189 term_fresh_row_both(y, x1, x2);
1192 /* Never use "term_pict()" */
1195 term_fresh_row_text(y, x1, x2);
1198 /* This row is all done */
1199 Term->x1[y] = (byte)w;
1202 /* Flush that row (if allowed) */
1203 if (!Term->never_frosh)
1204 term_xtra(TERM_XTRA_FROSH, y);
1208 /* No rows are invalid */
1213 /* Cursor update -- Show new Cursor */
1214 if (Term->soft_cursor) {
1215 /* Draw the cursor */
1216 if (!scr->cu && scr->cv) {
1218 if ((scr->cx + 1 < w)
1219 && ((old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2
1220 || (!(old->a[scr->cy][scr->cx] & AF_TILE1) && iskanji(old->c[scr->cy][scr->cx]))))
1222 if ((scr->cx + 1 < w) && (old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2)
1225 /* Double width cursor for the Bigtile mode */
1226 (void)((*Term->bigcurs_hook)(scr->cx, scr->cy));
1228 /* Call the cursor display routine */
1229 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1234 /* Cursor Update -- Show new Cursor */
1236 /* The cursor is useless, hide it */
1238 /* Paranoia -- Put the cursor NEAR where it belongs */
1239 (void)((*Term->curs_hook)(w - 1, scr->cy));
1241 /* Make the cursor invisible */
1242 /* term_xtra(TERM_XTRA_SHAPE, 0); */
1245 /* The cursor is invisible, hide it */
1246 else if (!scr->cv) {
1247 /* Paranoia -- Put the cursor where it belongs */
1248 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1250 /* Make the cursor invisible */
1251 /* term_xtra(TERM_XTRA_SHAPE, 0); */
1254 /* The cursor is visible, display it correctly */
1256 /* Put the cursor where it belongs */
1257 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1259 /* Make the cursor visible */
1260 term_xtra(TERM_XTRA_SHAPE, 1);
1264 /* Save the "cursor state" */
1270 /* Actually flush the output */
1271 term_xtra(TERM_XTRA_FRESH, 0);
1275 /*** Output routines ***/
1278 * Set the cursor visibility
1280 errr term_set_cursor(int v)
1283 if (Term->scr->cv == v)
1287 Term->scr->cv = (bool)v;
1292 * Place the cursor at a given location
1294 * Note -- "illegal" requests do not move the cursor.
1296 errr term_gotoxy(TERM_LEN x, TERM_LEN y)
1302 if ((x < 0) || (x >= w))
1304 if ((y < 0) || (y >= h))
1307 /* Remember the cursor */
1308 Term->scr->cx = (byte)x;
1309 Term->scr->cy = (byte)y;
1311 /* The cursor is not useless */
1317 * At a given location, place an attr/char
1318 * Do not change the cursor position
1319 * No visual changes until "term_fresh()".
1321 errr term_draw(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1326 if ((x < 0) || (x >= w))
1328 if ((y < 0) || (y >= h))
1331 /* Paranoia -- illegal char */
1335 /* Queue it for later */
1336 term_queue_char(x, y, a, c, 0, 0);
1341 * Using the given attr, add the given char at the cursor.
1343 * We return "-2" if the character is "illegal". XXX XXX
1345 * We return "-1" if the cursor is currently unusable.
1347 * We queue the given attr/char for display at the current
1348 * cursor location, and advance the cursor to the right,
1349 * marking it as unuable and returning "1" if it leaves
1350 * the screen, and otherwise returning "0".
1352 * So when this function, or the following one, return a
1353 * positive value, future calls to either function will
1354 * return negative ones.
1356 errr term_addch(TERM_COLOR a, char c)
1358 TERM_LEN w = Term->wid;
1360 /* Handle "unusable" cursor */
1364 /* Paranoia -- no illegal chars */
1368 /* Queue the given character for display */
1369 term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1371 /* Advance the cursor */
1375 if (Term->scr->cx < w)
1378 /* Note "Useless" cursor */
1381 /* Note "Useless" cursor */
1386 * Bigtile version of term_addch().
1388 * If use_bigtile is FALSE, simply call term_addch() .
1390 * Otherwise, queue a pair of attr/char for display at the current
1391 * cursor location, and advance the cursor to the right by two.
1393 errr term_add_bigch(TERM_COLOR a, char c)
1396 return term_addch(a, c);
1398 /* Handle "unusable" cursor */
1402 /* Paranoia -- no illegal chars */
1406 /* Queue the given character for display */
1407 term_queue_bigchar(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1409 /* Advance the cursor */
1413 if (Term->scr->cx < Term->wid)
1416 /* Note "Useless" cursor */
1419 /* Note "Useless" cursor */
1424 * At the current location, using an attr, add a string
1426 * We also take a length "n", using negative values to imply
1427 * the largest possible value, and then we use the minimum of
1428 * this length and the "actual" length of the string as the
1429 * actual number of characters to attempt to display, never
1430 * displaying more characters than will actually fit, since
1431 * we do NOT attempt to "wrap" the cursor at the screen edge.
1433 * We return "-1" if the cursor is currently unusable.
1434 * We return "N" if we were "only" able to write "N" chars,
1435 * even if all of the given characters fit on the screen,
1436 * and mark the cursor as unusable for future attempts.
1438 * So when this function, or the preceding one, return a
1439 * positive value, future calls to either function will
1440 * return negative ones.
1442 errr term_addstr(int n, TERM_COLOR a, concptr s)
1445 TERM_LEN w = Term->wid;
1448 /* Handle "unusable" cursor */
1452 /* Obtain maximal length */
1453 k = (n < 0) ? (w + 1) : n;
1455 /* Obtain the usable string length */
1456 for (n = 0; (n < k) && s[n]; n++) /* loop */
1459 /* React to reaching the edge of the screen */
1460 if (Term->scr->cx + n >= w)
1461 res = n = w - Term->scr->cx;
1463 /* Queue the first "n" characters for display */
1464 term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
1466 /* Advance the cursor */
1467 Term->scr->cx += (byte)n;
1469 /* Notice "Useless" cursor */
1477 * Move to a location and, using an attr, add a char
1479 errr term_putch(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1484 if ((res = term_gotoxy(x, y)) != 0)
1487 /* Then add the char */
1488 if ((res = term_addch(a, c)) != 0)
1495 * Move to a location and, using an attr, add a string
1497 errr term_putstr(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
1502 if ((res = term_gotoxy(x, y)) != 0)
1505 /* Then add the string */
1506 if ((res = term_addstr(n, a, s)) != 0)
1513 * Place cursor at (x,y), and clear the next "n" chars
1515 errr term_erase(TERM_LEN x, TERM_LEN y, int n)
1517 TERM_LEN w = Term->wid;
1518 /* int h = Term->hgt; */
1523 int na = Term->attr_blank;
1524 int nc = Term->char_blank;
1529 TERM_COLOR *scr_taa;
1533 if (term_gotoxy(x, y))
1536 /* Force legal size */
1541 scr_aa = Term->scr->a[y];
1542 scr_cc = Term->scr->c[y];
1544 scr_taa = Term->scr->ta[y];
1545 scr_tcc = Term->scr->tc[y];
1549 * 全角文字の右半分から文字を表示する場合、
1552 if (n > 0 && (((scr_aa[x] & AF_KANJI2) && !(scr_aa[x] & AF_TILE1)) || (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2))
1554 if (n > 0 && (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2)
1561 /* Scan every column */
1562 for (int i = 0; i < n; i++, x++) {
1566 /* Ignore "non-changes" */
1567 if ((oa == na) && (oc == nc))
1572 * 全角文字の左半分で表示を終了する場合、
1575 * 2001/04/29 -- Habu
1576 * 行の右端の場合はこの処理をしないように修正。
1578 if ((oa & AF_KANJI1) && (i + 1) == n && x != w - 1)
1581 /* Save the "literal" information */
1582 scr_aa[x] = (byte)na;
1583 scr_cc[x] = (char)nc;
1588 /* Track minimum changed column */
1592 /* Track maximum changed column */
1596 /* Expand the "change area" as needed */
1598 /* Check for new min/max row info */
1604 /* Check for new min/max col info in this row */
1605 if (x1 < Term->x1[y])
1606 Term->x1[y] = (byte)x1;
1607 if (x2 > Term->x2[y])
1608 Term->x2[y] = (byte)x2;
1615 * Clear the entire window, and move to the top left corner
1617 * Note the use of the special "total_erase" code
1619 errr term_clear(void)
1621 TERM_LEN w = Term->wid;
1622 TERM_LEN h = Term->hgt;
1624 TERM_COLOR na = Term->attr_blank;
1625 char nc = Term->char_blank;
1630 /* Cursor to the top left */
1631 Term->scr->cx = Term->scr->cy = 0;
1634 for (TERM_LEN y = 0; y < h; y++) {
1635 TERM_COLOR *scr_aa = Term->scr->a[y];
1636 char *scr_cc = Term->scr->c[y];
1638 TERM_COLOR *scr_taa = Term->scr->ta[y];
1639 char *scr_tcc = Term->scr->tc[y];
1641 /* Wipe each column */
1642 for (TERM_LEN x = 0; x < w; x++) {
1650 /* This row has changed */
1652 Term->x2[y] = w - 1;
1655 /* Every row has changed */
1659 /* Force "total erase" */
1660 Term->total_erase = TRUE;
1665 * Redraw (and refresh) the whole window.
1667 errr term_redraw(void)
1669 /* Force "total erase" */
1670 Term->total_erase = TRUE;
1676 * Redraw part of a window.
1678 errr term_redraw_section(TERM_LEN x1, TERM_LEN y1, TERM_LEN x2, TERM_LEN y2)
1682 /* Bounds checking */
1683 if (y2 >= Term->hgt)
1685 if (x2 >= Term->wid)
1693 Term->y1 = (byte)y1;
1694 Term->y2 = (byte)y2;
1696 /* Set the x limits */
1697 for (int i = Term->y1; i <= Term->y2; i++) {
1703 if (Term->scr->a[i][x1j] & AF_KANJI2)
1707 if (x2j < Term->wid - 1) {
1708 if (Term->scr->a[i][x2j] & AF_KANJI1)
1712 Term->x1[i] = (byte)x1j;
1713 Term->x2[i] = (byte)x2j;
1715 g_ptr = Term->old->c[i];
1717 /* Clear the section so it is redrawn */
1718 for (int j = x1j; j <= x2j; j++) {
1719 /* Hack - set the old character to "none" */
1726 g_ptr = Term->old->c[i];
1728 /* Clear the section so it is redrawn */
1729 for (int j = x1; j <= x2; j++) {
1730 /* Hack - set the old character to "none" */
1740 /*** Access routines ***/
1743 * Extract the cursor visibility
1745 errr term_get_cursor(int *v)
1747 /* Extract visibility */
1748 (*v) = Term->scr->cv;
1753 * Extract the current window size
1755 errr term_get_size(TERM_LEN *w, TERM_LEN *h)
1757 /* Access the cursor */
1764 * Extract the current cursor location
1766 errr term_locate(TERM_LEN *x, TERM_LEN *y)
1768 /* Access the cursor */
1769 (*x) = Term->scr->cx;
1770 (*y) = Term->scr->cy;
1772 /* Warn about "useless" cursor */
1780 * At a given location, determine the "current" attr and char
1781 * Note that this refers to what will be on the window after the
1782 * next call to "term_fresh()". It may or may not already be there.
1784 errr term_what(TERM_LEN x, TERM_LEN y, TERM_COLOR *a, char *c)
1786 TERM_LEN w = Term->wid;
1787 TERM_LEN h = Term->hgt;
1789 if ((x < 0) || (x >= w))
1791 if ((y < 0) || (y >= h))
1795 (*a) = Term->scr->a[y][x];
1796 (*c) = Term->scr->c[y][x];
1800 /*** Input routines ***/
1803 * Flush and forget the input
1805 errr term_flush(void)
1807 /* Flush all events */
1808 term_xtra(TERM_XTRA_FLUSH, 0);
1810 /* Forget all keypresses */
1811 Term->key_head = Term->key_tail = 0;
1816 * Add a keypress to the FRONT of the "queue"
1818 errr term_key_push(int k)
1820 /* Refuse to enqueue non-keys */
1824 /* Overflow may induce circular queue */
1825 if (Term->key_tail == 0)
1826 Term->key_tail = Term->key_size;
1828 /* Back up, Store the char */
1829 Term->key_queue[--Term->key_tail] = (char)k;
1831 if (Term->key_head != Term->key_tail)
1838 * Check for a pending keypress on the key queue.
1840 * Store the keypress, if any, in "ch", and return "0".
1841 * Otherwise store "zero" in "ch", and return "1".
1843 * Wait for a keypress if "wait" is true.
1845 * Remove the keypress if "take" is true.
1847 errr term_inkey(char *ch, bool wait, bool take)
1853 if (!Term->never_bored) {
1854 /* Process random events */
1855 term_xtra(TERM_XTRA_BORED, 0);
1860 /* Process pending events while necessary */
1861 while (Term->key_head == Term->key_tail) {
1862 /* Process events (wait for one) */
1863 term_xtra(TERM_XTRA_EVENT, TRUE);
1869 /* Process pending events if necessary */
1870 if (Term->key_head == Term->key_tail) {
1871 /* Process events (do not wait) */
1872 term_xtra(TERM_XTRA_EVENT, FALSE);
1876 /* No keys are ready */
1877 if (Term->key_head == Term->key_tail)
1880 /* Extract the next keypress */
1881 (*ch) = Term->key_queue[Term->key_tail];
1883 /* If requested, advance the queue, wrap around if necessary */
1884 if (take && (++Term->key_tail == Term->key_size))
1890 /*** Extra routines ***/
1893 * Save the "requested" screen into the "memorized" screen
1895 * Every "term_save()" should match exactly one "term_load()"
1897 errr term_save(void)
1899 TERM_LEN w = Term->wid;
1900 TERM_LEN h = Term->hgt;
1904 /* Allocate window */
1905 MAKE(Term->mem, term_win);
1907 /* Initialize window */
1908 term_win_init(Term->mem, w, h);
1912 term_win_copy(Term->mem, Term->scr, w, h);
1917 * Restore the "requested" contents (see above).
1919 * Every "term_save()" should match exactly one "term_load()"
1921 errr term_load(void)
1923 TERM_LEN w = Term->wid;
1924 TERM_LEN h = Term->hgt;
1928 /* Allocate window */
1929 MAKE(Term->mem, term_win);
1931 /* Initialize window */
1932 term_win_init(Term->mem, w, h);
1936 term_win_copy(Term->scr, Term->mem, w, h);
1939 for (TERM_LEN y = 0; y < h; y++) {
1942 Term->x2[y] = w - 1;
1952 * Exchange the "requested" screen with the "tmp" screen
1954 errr term_exchange(void)
1956 TERM_LEN w = Term->wid;
1957 TERM_LEN h = Term->hgt;
1959 term_win *exchanger;
1963 /* Allocate window */
1964 MAKE(Term->tmp, term_win);
1966 /* Initialize window */
1967 term_win_init(Term->tmp, w, h);
1971 exchanger = Term->scr;
1972 Term->scr = Term->tmp;
1973 Term->tmp = exchanger;
1976 for (TERM_LEN y = 0; y < h; y++) {
1979 Term->x2[y] = w - 1;
1989 * React to a new physical window size.
1991 errr term_resize(TERM_LEN w, TERM_LEN h)
2003 /* Resizing is forbidden */
2004 if (Term->fixed_shape)
2007 /* Ignore illegal changes */
2008 if ((w < 1) || (h < 1))
2011 /* Ignore non-changes */
2012 if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile))
2015 use_bigtile = arg_bigtile;
2017 /* Minimum dimensions */
2018 wid = MIN(Term->wid, w);
2019 hgt = MIN(Term->hgt, h);
2025 /* Save old window */
2026 hold_old = Term->old;
2028 /* Save old window */
2029 hold_scr = Term->scr;
2031 /* Save old window */
2032 hold_mem = Term->mem;
2034 /* Save old window */
2035 hold_tmp = Term->tmp;
2037 /* Create new scanners */
2038 C_MAKE(Term->x1, h, TERM_LEN);
2039 C_MAKE(Term->x2, h, TERM_LEN);
2041 /* Create new window */
2042 MAKE(Term->old, term_win);
2044 /* Initialize new window */
2045 term_win_init(Term->old, w, h);
2047 /* Save the contents */
2048 term_win_copy(Term->old, hold_old, wid, hgt);
2050 /* Create new window */
2051 MAKE(Term->scr, term_win);
2053 /* Initialize new window */
2054 term_win_init(Term->scr, w, h);
2056 /* Save the contents */
2057 term_win_copy(Term->scr, hold_scr, wid, hgt);
2061 /* Create new window */
2062 MAKE(Term->mem, term_win);
2064 /* Initialize new window */
2065 term_win_init(Term->mem, w, h);
2067 /* Save the contents */
2068 term_win_copy(Term->mem, hold_mem, wid, hgt);
2073 /* Create new window */
2074 MAKE(Term->tmp, term_win);
2076 /* Initialize new window */
2077 term_win_init(Term->tmp, w, h);
2079 /* Save the contents */
2080 term_win_copy(Term->tmp, hold_tmp, wid, hgt);
2083 /* Free some arrays */
2084 C_KILL(hold_x1, Term->hgt, TERM_LEN);
2085 C_KILL(hold_x2, Term->hgt, TERM_LEN);
2088 term_win_nuke(hold_old, Term->wid, Term->hgt);
2091 KILL(hold_old, term_win);
2093 /* Illegal cursor */
2094 if (Term->old->cx >= w)
2096 if (Term->old->cy >= h)
2100 term_win_nuke(hold_scr, Term->wid, Term->hgt);
2103 KILL(hold_scr, term_win);
2105 /* Illegal cursor */
2106 if (Term->scr->cx >= w)
2108 if (Term->scr->cy >= h)
2114 term_win_nuke(hold_mem, Term->wid, Term->hgt);
2117 KILL(hold_mem, term_win);
2119 /* Illegal cursor */
2120 if (Term->mem->cx >= w)
2122 if (Term->mem->cy >= h)
2129 term_win_nuke(hold_tmp, Term->wid, Term->hgt);
2132 KILL(hold_tmp, term_win);
2134 /* Illegal cursor */
2135 if (Term->tmp->cx >= w)
2137 if (Term->tmp->cy >= h)
2145 /* Force "total erase" */
2146 Term->total_erase = TRUE;
2149 for (int i = 0; i < h; i++) {
2152 Term->x2[i] = w - 1;
2159 /* Execute the "resize_hook" hook, if available */
2160 if (Term->resize_hook)
2161 Term->resize_hook();
2167 * Activate a new Term (and deactivate the current Term)
2169 * This function is extremely important, and also somewhat bizarre.
2170 * It is the only function that should "modify" the value of "Term".
2172 * To "create" a valid "term", one should do "term_init(t)", then
2173 * set the various flags and hooks, and then do "term_activate(t)".
2175 errr term_activate(term_type *t)
2181 /* Deactivate the old Term */
2183 term_xtra(TERM_XTRA_LEVEL, 0);
2185 /* Call the special "init" hook */
2186 if (t && !t->active_flag) {
2187 /* Call the "init" hook */
2192 t->active_flag = TRUE;
2195 t->mapped_flag = TRUE;
2198 /* Remember the Term */
2201 /* Activate the new Term */
2203 term_xtra(TERM_XTRA_LEVEL, 1);
2209 * Initialize a term, using a window of the given size.
2210 * Also prepare the "input queue" for "k" keypresses
2211 * By default, the cursor starts out "invisible"
2212 * By default, we "erase" using "black spaces"
2214 errr term_init(term_type *t, TERM_LEN w, TERM_LEN h, int k)
2217 (void)WIPE(t, term_type);
2219 /* Prepare the input queue */
2220 t->key_head = t->key_tail = 0;
2222 /* Determine the input queue size */
2223 t->key_size = (u16b)k;
2225 /* Allocate the input queue */
2226 C_MAKE(t->key_queue, t->key_size, char);
2232 /* Allocate change arrays */
2233 C_MAKE(t->x1, h, TERM_LEN);
2234 C_MAKE(t->x2, h, TERM_LEN);
2236 /* Allocate "displayed" */
2237 MAKE(t->old, term_win);
2239 /* Initialize "displayed" */
2240 term_win_init(t->old, w, h);
2242 /* Allocate "requested" */
2243 MAKE(t->scr, term_win);
2245 /* Initialize "requested" */
2246 term_win_init(t->scr, w, h);
2249 for (TERM_LEN y = 0; y < h; y++) {
2259 /* Force "total erase" */
2260 t->total_erase = TRUE;
2262 /* Default "blank" */
2264 t->char_blank = ' ';
2266 /* Prepare "fake" hooks to prevent core dumps */
2267 t->curs_hook = term_curs_hack;
2268 t->bigcurs_hook = term_bigcurs_hack;
2269 t->wipe_hook = term_wipe_hack;
2270 t->text_hook = term_text_hack;
2271 t->pict_hook = term_pict_hack;
2277 * Move to a location and, using an attr, add a string vertically
2279 errr term_putstr_v(TERM_LEN x, TERM_LEN y, int n, byte a, concptr s)
2284 for (int i = 0; i < n && s[i] != 0; i++) {
2286 if ((res = term_gotoxy(x, y0)) != 0)
2289 if (iskanji(s[i])) {
2290 if ((res = term_addstr(2, a, &s[i])) != 0)
2297 if ((res = term_addstr(1, a, &s[i])) != 0)
2308 errr term_nuke(term_type *t)
2310 TERM_LEN w = t->wid;
2311 TERM_LEN h = t->hgt;
2312 if (t->active_flag) {
2316 t->active_flag = FALSE;
2317 t->mapped_flag = FALSE;
2320 term_win_nuke(t->old, w, h);
2321 KILL(t->old, term_win);
2322 term_win_nuke(t->scr, w, h);
2323 KILL(t->scr, term_win);
2325 term_win_nuke(t->mem, w, h);
2326 KILL(t->mem, term_win);
2330 term_win_nuke(t->tmp, w, h);
2331 KILL(t->tmp, term_win);
2334 C_KILL(t->x1, h, TERM_LEN);
2335 C_KILL(t->x2, h, TERM_LEN);
2336 C_KILL(t->key_queue, t->key_size, char);