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 "core/player-processor.h"
12 #include "game-option/map-screen-options.h"
13 #include "game-option/runtime-arguments.h"
14 #include "game-option/special-options.h"
15 #include "io/input-key-requester.h"
16 #include "term/gameterm.h"
17 #include "term/term-color-types.h"
18 #include "term/z-virt.h"
20 /* Special flags in the attr data */
21 #define AF_BIGTILE2 0xf0
27 * 属性に全角文字の1バイト目、2バイト目も記憶。
30 #define AF_KANJI1 0x10
31 #define AF_KANJI2 0x20
32 #define AF_KANJIC 0x0f
35 /* The current "term" */
36 term_type *Term = NULL;
38 /*** Local routines ***/
41 * Nuke a term_win (see below)
43 errr term_win_nuke(term_win *s, TERM_LEN w, TERM_LEN h)
45 /* Free the window access arrays */
46 C_KILL(s->a, h, TERM_COLOR *);
47 C_KILL(s->c, h, char *);
49 /* Free the window content arrays */
50 C_KILL(s->va, h * w, TERM_COLOR);
51 C_KILL(s->vc, h * w, char);
53 /* Free the terrain access arrays */
54 C_KILL(s->ta, h, TERM_COLOR *);
55 C_KILL(s->tc, h, char *);
57 /* Free the terrain content arrays */
58 C_KILL(s->vta, h * w, TERM_COLOR);
59 C_KILL(s->vtc, h * w, char);
65 * Initialize a "term_win" (using the given window size)
67 static errr term_win_init(term_win *s, TERM_LEN w, TERM_LEN h)
69 /* Make the window access arrays */
70 C_MAKE(s->a, h, TERM_COLOR *);
71 C_MAKE(s->c, h, char *);
73 /* Make the window content arrays */
74 C_MAKE(s->va, h * w, TERM_COLOR);
75 C_MAKE(s->vc, h * w, char);
77 /* Make the terrain access arrays */
78 C_MAKE(s->ta, h, TERM_COLOR *);
79 C_MAKE(s->tc, h, char *);
81 /* Make the terrain content arrays */
82 C_MAKE(s->vta, h * w, TERM_COLOR);
83 C_MAKE(s->vtc, h * w, char);
85 /* Prepare the window access arrays */
86 for (TERM_LEN y = 0; y < h; y++) {
87 s->a[y] = s->va + w * y;
88 s->c[y] = s->vc + w * y;
90 s->ta[y] = s->vta + w * y;
91 s->tc[y] = s->vtc + w * y;
98 * Copy a "term_win" from another
100 static errr term_win_copy(term_win *s, term_win *f, TERM_LEN w, TERM_LEN h)
103 for (TERM_LEN y = 0; y < h; y++) {
104 TERM_COLOR *f_aa = f->a[y];
105 char *f_cc = f->c[y];
107 TERM_COLOR *s_aa = s->a[y];
108 char *s_cc = s->c[y];
110 TERM_COLOR *f_taa = f->ta[y];
111 char *f_tcc = f->tc[y];
113 TERM_COLOR *s_taa = s->ta[y];
114 char *s_tcc = s->tc[y];
116 for (TERM_LEN x = 0; x < w; x++) {
133 /*** External hooks ***/
136 * Execute the "Term->user_hook" hook, if available (see above).
138 errr term_user(int n)
140 /* Verify the hook */
141 if (!Term->user_hook)
145 return ((*Term->user_hook)(n));
149 * Execute the "Term->xtra_hook" hook, if available (see above).
151 errr term_xtra(int n, int v)
153 /* Verify the hook */
154 if (!Term->xtra_hook)
158 return ((*Term->xtra_hook)(n, v));
164 * Fake hook for "term_curs()" (see above)
166 static errr term_curs_hack(TERM_LEN x, TERM_LEN y)
176 * Fake hook for "term_bigcurs()" (see above)
178 static errr term_bigcurs_hack(TERM_LEN x, TERM_LEN y) { return (*Term->curs_hook)(x, y); }
181 * Fake hook for "term_wipe()" (see above)
183 static errr term_wipe_hack(TERM_LEN x, TERM_LEN y, int n)
194 * Fake hook for "term_text()" (see above)
196 static errr term_text_hack(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr cp)
209 * Fake hook for "term_pict()" (see above)
211 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)
225 /*** Efficient routines ***/
228 * Mentally draw an attr/char at a given location
229 * Assumes given location and values are valid.
231 void term_queue_char(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
233 term_win *scrn = Term->scr;
235 TERM_COLOR *scr_aa = &scrn->a[y][x];
236 char *scr_cc = &scrn->c[y][x];
238 TERM_COLOR *scr_taa = &scrn->ta[y][x];
239 char *scr_tcc = &scrn->tc[y][x];
241 /* Ignore non-changes */
242 if ((*scr_aa == a) && (*scr_cc == c) && (*scr_taa == ta) && (*scr_tcc == tc))
245 /* Save the "literal" information */
252 /* Check for new min/max row info */
258 /* Check for new min/max col info for this row */
260 Term->x1[y] = (byte)x;
262 Term->x2[y] = (byte)x;
265 if (((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2) || (scrn->a[y][x] & AF_KANJI2))
267 if ((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2)
269 if ((x - 1) < Term->x1[y])
274 * Bigtile version of term_queue_char().
275 * If use_bigtile is FALSE, simply call term_queue_char().
276 * Otherwise, mentally draw a pair of attr/char at a given location.
277 * Assumes given location and values are valid.
279 void term_queue_bigchar(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
283 * A table which relates each ascii character to a multibyte
286 * 「■」は二倍幅豆腐の内部コードに使用。
288 static char ascii_to_zenkaku[] = " !”#$%&’()*+,-./"
299 /* If non bigtile mode, call orginal function */
301 term_queue_char(x, y, a, c, ta, tc);
305 /* A tile becomes a Bigtile */
306 if ((a & AF_TILE1) && (c & 0x80)) {
307 /* Mark it as a Bigtile */
312 /* Ignore non-tile background */
313 if (!((ta & AF_TILE1) && (tc & 0x80))) {
321 * Use a multibyte character instead of a dirty pair of ASCII
324 else if (' ' <= c) /* isprint(c) */
326 c2 = ascii_to_zenkaku[2 * (c - ' ') + 1];
327 c = ascii_to_zenkaku[2 * (c - ' ')];
329 /* Mark it as a Kanji */
336 /* Dirty pair of ASCII characters */
341 /* Display pair of attr/char */
342 term_queue_char(x, y, a, c, ta, tc);
343 term_queue_char(x + 1, y, a2, c2, 0, 0);
347 * Mentally draw a string of attr/chars at a given location
348 * Assumes given location and values are valid.
349 * This function is designed to be fast, with no consistancy checking.
350 * It is used to update the map in the game.
352 void term_queue_line(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR *a, char *c, TERM_COLOR *ta, char *tc)
354 term_win *scrn = Term->scr;
359 TERM_COLOR *scr_aa = &scrn->a[y][x];
360 char *scr_cc = &scrn->c[y][x];
362 TERM_COLOR *scr_taa = &scrn->ta[y][x];
363 char *scr_tcc = &scrn->tc[y][x];
366 /* Ignore non-changes */
367 if ((*scr_aa == *a) && (*scr_cc == *c) && (*scr_taa == *ta) && (*scr_tcc == *tc)) {
380 /* Save the "literal" information */
384 /* Save the "literal" information */
388 /* Track minimum changed column */
392 /* Track maximum changed column */
398 /* Expand the "change area" as needed */
400 /* Check for new min/max row info */
406 /* Check for new min/max col info in this row */
407 if (x1 < Term->x1[y])
408 Term->x1[y] = (byte)x1;
409 if (x2 > Term->x2[y])
410 Term->x2[y] = (byte)x2;
415 * Mentally draw some attr/chars at a given location
417 * Assumes that (x,y) is a valid location, that the first "n" characters
418 * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
419 * a valid location, so the first "n" characters of "s" can all be added
420 * starting at (x,y) without causing any illegal operations.
422 static void term_queue_chars(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
424 TERM_LEN x1 = -1, x2 = -1;
426 TERM_COLOR *scr_aa = Term->scr->a[y];
428 char *scr_cc = Term->scr->c[y];
430 TERM_COLOR *scr_taa = Term->scr->ta[y];
431 char *scr_tcc = Term->scr->tc[y];
433 char *scr_cc = Term->scr->c[y];
435 TERM_COLOR *scr_taa = Term->scr->ta[y];
436 char *scr_tcc = Term->scr->tc[y];
441 if (n == 0 || *s == 0)
444 * 全角文字の右半分から文字を表示する場合、
448 if ((scr_aa[x] & AF_KANJI2) && (scr_aa[x] & AF_BIGTILE2) != AF_BIGTILE2) {
450 scr_aa[x - 1] &= AF_KANJIC;
454 /* Queue the attr/chars */
455 for (; n; x++, s++, n--) {
457 /* 特殊文字としてMSBが立っている可能性がある */
458 /* その場合attrのMSBも立っているのでこれで識別する */
460 if (!(a & AF_TILE1) && iskanji(*s)) {
464 byte na1 = (a | AF_KANJI1);
465 byte na2 = (a | AF_KANJI2);
467 if ((--n == 0) || !nc2)
470 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)
471 && (scr_tcc[x - 1] == 0) && (scr_tcc[x] == 0))
484 TERM_COLOR oa = scr_aa[x];
487 TERM_COLOR ota = scr_taa[x];
488 char otc = scr_tcc[x];
490 /* Ignore non-changes */
491 if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0))
494 /* Save the "literal" information */
501 /* Note the "range" of window updates */
512 * 全角文字の左半分で表示を終了する場合、
514 * (条件追加:タイルの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)
1028 int diff = angband_term[0]->key_head - angband_term[0]->key_tail;
1029 return diff < -1 || 1 < diff;
1032 bool need_term_fresh(player_type *player_ptr) { return (!macro_running() && !continuous_action_running(player_ptr)) || fresh_after; }
1035 * @brief Actually perform all requested changes to the window
1037 errr term_fresh(void)
1045 term_win *old = Term->old;
1046 term_win *scr = Term->scr;
1048 /* Before initialize (Advice from Mr.shimitei)*/
1052 /* Do nothing unless "mapped" */
1053 if (!Term->mapped_flag)
1056 /* Trivial Refresh */
1057 if ((y1 > y2) && (scr->cu == old->cu) && (scr->cv == old->cv) && (scr->cx == old->cx) && (scr->cy == old->cy) && !(Term->total_erase)) {
1062 /* Handle "total erase" */
1063 if (Term->total_erase) {
1064 byte na = Term->attr_blank;
1065 char nc = Term->char_blank;
1067 /* Physically erase the entire window */
1068 term_xtra(TERM_XTRA_CLEAR, 0);
1070 /* clear all "cursor" data */
1071 old->cv = old->cu = old->cx = old->cy = 0;
1074 for (TERM_LEN y = 0; y < h; y++) {
1075 TERM_COLOR *aa = old->a[y];
1076 char *cc = old->c[y];
1078 TERM_COLOR *taa = old->ta[y];
1079 char *tcc = old->tc[y];
1081 /* Wipe each column */
1082 for (TERM_LEN x = 0; x < w; x++) {
1083 /* Wipe each grid */
1092 /* Redraw every row */
1094 Term->y2 = y2 = h - 1;
1096 /* Redraw every column */
1097 for (TERM_LEN y = 0; y < h; y++) {
1099 Term->x2[y] = w - 1;
1102 /* Forget "total erase" */
1103 Term->total_erase = FALSE;
1106 /* Cursor update -- Erase old Cursor */
1107 if (Term->soft_cursor) {
1108 /* Cursor was visible */
1109 if (!old->cu && old->cv) {
1111 TERM_LEN tx = old->cx;
1112 TERM_LEN ty = old->cy;
1114 TERM_COLOR *old_aa = old->a[ty];
1115 char *old_cc = old->c[ty];
1117 TERM_COLOR *old_taa = old->ta[ty];
1118 char *old_tcc = old->tc[ty];
1120 TERM_COLOR ota = old_taa[tx];
1121 char otc = old_tcc[tx];
1124 if (tx + 1 < Term->wid && !(old_aa[tx] & AF_TILE1) && iskanji(old_cc[tx]))
1127 /* Use "term_pict()" always */
1128 if (Term->always_pict)
1129 (void)((*Term->pict_hook)(tx, ty, csize, &old_aa[tx], &old_cc[tx], &ota, &otc));
1131 /* Use "term_pict()" sometimes */
1132 else if (Term->higher_pict && (old_aa[tx] & AF_TILE1) && (old_cc[tx] & 0x80))
1133 (void)((*Term->pict_hook)(tx, ty, 1, &old_aa[tx], &old_cc[tx], &ota, &otc));
1136 * Restore the actual character
1137 * 元の文字の描画範囲がカーソルより小さいと、
1138 * 上書きされなかった部分がゴミとして残る。
1139 * wipe_hook でカーソルを消去して text_hook で書き直す。
1141 else if (old_aa[tx] || Term->always_text) {
1142 (void)((*Term->wipe_hook)(tx, ty, 1));
1143 (void)((*Term->text_hook)(tx, ty, csize, (unsigned char)(old_aa[tx] & 0xf), &old_cc[tx]));
1146 /* Erase the grid */
1148 (void)((*Term->wipe_hook)(tx, ty, 1));
1152 /* Cursor Update -- Erase old Cursor */
1154 /* Cursor will be invisible */
1155 if (scr->cu || !scr->cv) {
1156 /* Make the cursor invisible */
1157 term_xtra(TERM_XTRA_SHAPE, 0);
1161 /* Something to update */
1163 /* Handle "icky corner" */
1164 if (Term->icky_corner) {
1165 /* Avoid the corner */
1167 /* Avoid the corner */
1168 if (Term->x2[h - 1] > w - 2) {
1169 /* Avoid the corner */
1170 Term->x2[h - 1] = w - 2;
1175 /* Scan the "modified" rows */
1176 for (TERM_LEN y = y1; y <= y2; ++y) {
1177 TERM_LEN x1 = Term->x1[y];
1178 TERM_LEN x2 = Term->x2[y];
1180 /* Flush each "modified" row */
1182 /* Always use "term_pict()" */
1183 if (Term->always_pict) {
1185 term_fresh_row_pict(y, x1, x2);
1188 /* Sometimes use "term_pict()" */
1189 else if (Term->higher_pict) {
1191 term_fresh_row_both(y, x1, x2);
1194 /* Never use "term_pict()" */
1197 term_fresh_row_text(y, x1, x2);
1200 /* This row is all done */
1201 Term->x1[y] = (byte)w;
1204 /* Flush that row (if allowed) */
1205 if (!Term->never_frosh)
1206 term_xtra(TERM_XTRA_FROSH, y);
1210 /* No rows are invalid */
1215 /* Cursor update -- Show new Cursor */
1216 if (Term->soft_cursor) {
1217 /* Draw the cursor */
1218 if (!scr->cu && scr->cv) {
1220 if ((scr->cx + 1 < w)
1221 && ((old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2
1222 || (!(old->a[scr->cy][scr->cx] & AF_TILE1) && iskanji(old->c[scr->cy][scr->cx]))))
1224 if ((scr->cx + 1 < w) && (old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2)
1227 /* Double width cursor for the Bigtile mode */
1228 (void)((*Term->bigcurs_hook)(scr->cx, scr->cy));
1230 /* Call the cursor display routine */
1231 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1236 /* Cursor Update -- Show new Cursor */
1238 /* The cursor is useless, hide it */
1240 /* Paranoia -- Put the cursor NEAR where it belongs */
1241 (void)((*Term->curs_hook)(w - 1, scr->cy));
1243 /* Make the cursor invisible */
1244 /* term_xtra(TERM_XTRA_SHAPE, 0); */
1247 /* The cursor is invisible, hide it */
1248 else if (!scr->cv) {
1249 /* Paranoia -- Put the cursor where it belongs */
1250 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1252 /* Make the cursor invisible */
1253 /* term_xtra(TERM_XTRA_SHAPE, 0); */
1256 /* The cursor is visible, display it correctly */
1258 /* Put the cursor where it belongs */
1259 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1261 /* Make the cursor visible */
1262 term_xtra(TERM_XTRA_SHAPE, 1);
1266 /* Save the "cursor state" */
1272 /* Actually flush the output */
1273 term_xtra(TERM_XTRA_FRESH, 0);
1277 /*** Output routines ***/
1280 * Set the cursor visibility
1282 errr term_set_cursor(int v)
1285 if (Term->scr->cv == v)
1289 Term->scr->cv = (bool)v;
1294 * Place the cursor at a given location
1296 * Note -- "illegal" requests do not move the cursor.
1298 errr term_gotoxy(TERM_LEN x, TERM_LEN y)
1304 if ((x < 0) || (x >= w))
1306 if ((y < 0) || (y >= h))
1309 /* Remember the cursor */
1310 Term->scr->cx = (byte)x;
1311 Term->scr->cy = (byte)y;
1313 /* The cursor is not useless */
1319 * At a given location, place an attr/char
1320 * Do not change the cursor position
1321 * No visual changes until "term_fresh()".
1323 errr term_draw(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1328 if ((x < 0) || (x >= w))
1330 if ((y < 0) || (y >= h))
1333 /* Paranoia -- illegal char */
1337 /* Queue it for later */
1338 term_queue_char(x, y, a, c, 0, 0);
1343 * Using the given attr, add the given char at the cursor.
1345 * We return "-2" if the character is "illegal". XXX XXX
1347 * We return "-1" if the cursor is currently unusable.
1349 * We queue the given attr/char for display at the current
1350 * cursor location, and advance the cursor to the right,
1351 * marking it as unuable and returning "1" if it leaves
1352 * the screen, and otherwise returning "0".
1354 * So when this function, or the following one, return a
1355 * positive value, future calls to either function will
1356 * return negative ones.
1358 errr term_addch(TERM_COLOR a, char c)
1360 TERM_LEN w = Term->wid;
1362 /* Handle "unusable" cursor */
1366 /* Paranoia -- no illegal chars */
1370 /* Queue the given character for display */
1371 term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1373 /* Advance the cursor */
1377 if (Term->scr->cx < w)
1380 /* Note "Useless" cursor */
1383 /* Note "Useless" cursor */
1388 * Bigtile version of term_addch().
1390 * If use_bigtile is FALSE, simply call term_addch() .
1392 * Otherwise, queue a pair of attr/char for display at the current
1393 * cursor location, and advance the cursor to the right by two.
1395 errr term_add_bigch(TERM_COLOR a, char c)
1398 return term_addch(a, c);
1400 /* Handle "unusable" cursor */
1404 /* Paranoia -- no illegal chars */
1408 /* Queue the given character for display */
1409 term_queue_bigchar(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1411 /* Advance the cursor */
1415 if (Term->scr->cx < Term->wid)
1418 /* Note "Useless" cursor */
1421 /* Note "Useless" cursor */
1426 * At the current location, using an attr, add a string
1428 * We also take a length "n", using negative values to imply
1429 * the largest possible value, and then we use the minimum of
1430 * this length and the "actual" length of the string as the
1431 * actual number of characters to attempt to display, never
1432 * displaying more characters than will actually fit, since
1433 * we do NOT attempt to "wrap" the cursor at the screen edge.
1435 * We return "-1" if the cursor is currently unusable.
1436 * We return "N" if we were "only" able to write "N" chars,
1437 * even if all of the given characters fit on the screen,
1438 * and mark the cursor as unusable for future attempts.
1440 * So when this function, or the preceding one, return a
1441 * positive value, future calls to either function will
1442 * return negative ones.
1444 errr term_addstr(int n, TERM_COLOR a, concptr s)
1447 TERM_LEN w = Term->wid;
1450 /* Handle "unusable" cursor */
1454 /* Obtain maximal length */
1455 k = (n < 0) ? (w + 1) : n;
1457 /* Obtain the usable string length */
1458 for (n = 0; (n < k) && s[n]; n++) /* loop */
1461 /* React to reaching the edge of the screen */
1462 if (Term->scr->cx + n >= w)
1463 res = n = w - Term->scr->cx;
1465 /* Queue the first "n" characters for display */
1466 term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
1468 /* Advance the cursor */
1469 Term->scr->cx += (byte)n;
1471 /* Notice "Useless" cursor */
1479 * Move to a location and, using an attr, add a char
1481 errr term_putch(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1486 if ((res = term_gotoxy(x, y)) != 0)
1489 /* Then add the char */
1490 if ((res = term_addch(a, c)) != 0)
1497 * Move to a location and, using an attr, add a string
1499 errr term_putstr(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
1504 if ((res = term_gotoxy(x, y)) != 0)
1507 /* Then add the string */
1508 if ((res = term_addstr(n, a, s)) != 0)
1515 * Place cursor at (x,y), and clear the next "n" chars
1517 errr term_erase(TERM_LEN x, TERM_LEN y, int n)
1519 TERM_LEN w = Term->wid;
1520 /* int h = Term->hgt; */
1525 int na = Term->attr_blank;
1526 int nc = Term->char_blank;
1531 TERM_COLOR *scr_taa;
1535 if (term_gotoxy(x, y))
1538 /* Force legal size */
1543 scr_aa = Term->scr->a[y];
1544 scr_cc = Term->scr->c[y];
1546 scr_taa = Term->scr->ta[y];
1547 scr_tcc = Term->scr->tc[y];
1551 * 全角文字の右半分から文字を表示する場合、
1554 if (n > 0 && (((scr_aa[x] & AF_KANJI2) && !(scr_aa[x] & AF_TILE1)) || (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2))
1556 if (n > 0 && (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2)
1563 /* Scan every column */
1564 for (int i = 0; i < n; i++, x++) {
1568 /* Ignore "non-changes" */
1569 if ((oa == na) && (oc == nc))
1574 * 全角文字の左半分で表示を終了する場合、
1577 * 2001/04/29 -- Habu
1578 * 行の右端の場合はこの処理をしないように修正。
1580 if ((oa & AF_KANJI1) && (i + 1) == n && x != w - 1)
1583 /* Save the "literal" information */
1584 scr_aa[x] = (byte)na;
1585 scr_cc[x] = (char)nc;
1590 /* Track minimum changed column */
1594 /* Track maximum changed column */
1598 /* Expand the "change area" as needed */
1600 /* Check for new min/max row info */
1606 /* Check for new min/max col info in this row */
1607 if (x1 < Term->x1[y])
1608 Term->x1[y] = (byte)x1;
1609 if (x2 > Term->x2[y])
1610 Term->x2[y] = (byte)x2;
1617 * Clear the entire window, and move to the top left corner
1619 * Note the use of the special "total_erase" code
1621 errr term_clear(void)
1623 TERM_LEN w = Term->wid;
1624 TERM_LEN h = Term->hgt;
1626 TERM_COLOR na = Term->attr_blank;
1627 char nc = Term->char_blank;
1632 /* Cursor to the top left */
1633 Term->scr->cx = Term->scr->cy = 0;
1636 for (TERM_LEN y = 0; y < h; y++) {
1637 TERM_COLOR *scr_aa = Term->scr->a[y];
1638 char *scr_cc = Term->scr->c[y];
1640 TERM_COLOR *scr_taa = Term->scr->ta[y];
1641 char *scr_tcc = Term->scr->tc[y];
1643 /* Wipe each column */
1644 for (TERM_LEN x = 0; x < w; x++) {
1652 /* This row has changed */
1654 Term->x2[y] = w - 1;
1657 /* Every row has changed */
1661 /* Force "total erase" */
1662 Term->total_erase = TRUE;
1667 * Redraw (and refresh) the whole window.
1669 errr term_redraw(void)
1671 /* Force "total erase" */
1672 Term->total_erase = TRUE;
1678 * Redraw part of a window.
1680 errr term_redraw_section(TERM_LEN x1, TERM_LEN y1, TERM_LEN x2, TERM_LEN y2)
1684 /* Bounds checking */
1685 if (y2 >= Term->hgt)
1687 if (x2 >= Term->wid)
1695 Term->y1 = (byte)y1;
1696 Term->y2 = (byte)y2;
1698 /* Set the x limits */
1699 for (int i = Term->y1; i <= Term->y2; i++) {
1705 if (Term->scr->a[i][x1j] & AF_KANJI2)
1709 if (x2j < Term->wid - 1) {
1710 if (Term->scr->a[i][x2j] & AF_KANJI1)
1714 Term->x1[i] = (byte)x1j;
1715 Term->x2[i] = (byte)x2j;
1717 g_ptr = Term->old->c[i];
1719 /* Clear the section so it is redrawn */
1720 for (int j = x1j; j <= x2j; j++) {
1721 /* Hack - set the old character to "none" */
1728 g_ptr = Term->old->c[i];
1730 /* Clear the section so it is redrawn */
1731 for (int j = x1; j <= x2; j++) {
1732 /* Hack - set the old character to "none" */
1742 /*** Access routines ***/
1745 * Extract the cursor visibility
1747 errr term_get_cursor(int *v)
1749 /* Extract visibility */
1750 (*v) = Term->scr->cv;
1755 * Extract the current window size
1757 errr term_get_size(TERM_LEN *w, TERM_LEN *h)
1759 /* Access the cursor */
1766 * Extract the current cursor location
1768 errr term_locate(TERM_LEN *x, TERM_LEN *y)
1770 /* Access the cursor */
1771 (*x) = Term->scr->cx;
1772 (*y) = Term->scr->cy;
1774 /* Warn about "useless" cursor */
1782 * At a given location, determine the "current" attr and char
1783 * Note that this refers to what will be on the window after the
1784 * next call to "term_fresh()". It may or may not already be there.
1786 errr term_what(TERM_LEN x, TERM_LEN y, TERM_COLOR *a, char *c)
1788 TERM_LEN w = Term->wid;
1789 TERM_LEN h = Term->hgt;
1791 if ((x < 0) || (x >= w))
1793 if ((y < 0) || (y >= h))
1797 (*a) = Term->scr->a[y][x];
1798 (*c) = Term->scr->c[y][x];
1802 /*** Input routines ***/
1805 * Flush and forget the input
1807 errr term_flush(void)
1809 /* Flush all events */
1810 term_xtra(TERM_XTRA_FLUSH, 0);
1812 /* Forget all keypresses */
1813 Term->key_head = Term->key_tail = 0;
1818 * Add a keypress to the FRONT of the "queue"
1820 errr term_key_push(int k)
1822 /* Refuse to enqueue non-keys */
1826 /* Overflow may induce circular queue */
1827 if (Term->key_tail == 0)
1828 Term->key_tail = Term->key_size;
1830 /* Back up, Store the char */
1831 Term->key_queue[--Term->key_tail] = (char)k;
1833 if (Term->key_head != Term->key_tail)
1840 * Check for a pending keypress on the key queue.
1842 * Store the keypress, if any, in "ch", and return "0".
1843 * Otherwise store "zero" in "ch", and return "1".
1845 * Wait for a keypress if "wait" is true.
1847 * Remove the keypress if "take" is true.
1849 errr term_inkey(char *ch, bool wait, bool take)
1855 if (!Term->never_bored) {
1856 /* Process random events */
1857 term_xtra(TERM_XTRA_BORED, 0);
1862 /* Process pending events while necessary */
1863 while (Term->key_head == Term->key_tail) {
1864 /* Process events (wait for one) */
1865 term_xtra(TERM_XTRA_EVENT, TRUE);
1871 /* Process pending events if necessary */
1872 if (Term->key_head == Term->key_tail) {
1873 /* Process events (do not wait) */
1874 term_xtra(TERM_XTRA_EVENT, FALSE);
1878 /* No keys are ready */
1879 if (Term->key_head == Term->key_tail)
1882 /* Extract the next keypress */
1883 (*ch) = Term->key_queue[Term->key_tail];
1885 /* If requested, advance the queue, wrap around if necessary */
1886 if (take && (++Term->key_tail == Term->key_size))
1892 /*** Extra routines ***/
1895 * Save the "requested" screen into the "memorized" screen
1897 * Every "term_save()" should match exactly one "term_load()"
1899 errr term_save(void)
1901 TERM_LEN w = Term->wid;
1902 TERM_LEN h = Term->hgt;
1906 /* Allocate window */
1907 MAKE(Term->mem, term_win);
1909 /* Initialize window */
1910 term_win_init(Term->mem, w, h);
1914 term_win_copy(Term->mem, Term->scr, w, h);
1919 * Restore the "requested" contents (see above).
1921 * Every "term_save()" should match exactly one "term_load()"
1923 errr term_load(void)
1925 TERM_LEN w = Term->wid;
1926 TERM_LEN h = Term->hgt;
1930 /* Allocate window */
1931 MAKE(Term->mem, term_win);
1933 /* Initialize window */
1934 term_win_init(Term->mem, w, h);
1938 term_win_copy(Term->scr, Term->mem, w, h);
1941 for (TERM_LEN y = 0; y < h; y++) {
1944 Term->x2[y] = w - 1;
1954 * Exchange the "requested" screen with the "tmp" screen
1956 errr term_exchange(void)
1958 TERM_LEN w = Term->wid;
1959 TERM_LEN h = Term->hgt;
1961 term_win *exchanger;
1965 /* Allocate window */
1966 MAKE(Term->tmp, term_win);
1968 /* Initialize window */
1969 term_win_init(Term->tmp, w, h);
1973 exchanger = Term->scr;
1974 Term->scr = Term->tmp;
1975 Term->tmp = exchanger;
1978 for (TERM_LEN y = 0; y < h; y++) {
1981 Term->x2[y] = w - 1;
1991 * React to a new physical window size.
1993 errr term_resize(TERM_LEN w, TERM_LEN h)
2005 /* Resizing is forbidden */
2006 if (Term->fixed_shape)
2009 /* Ignore illegal changes */
2010 if ((w < 1) || (h < 1))
2013 /* Ignore non-changes */
2014 if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile))
2017 use_bigtile = arg_bigtile;
2019 /* Minimum dimensions */
2020 wid = MIN(Term->wid, w);
2021 hgt = MIN(Term->hgt, h);
2027 /* Save old window */
2028 hold_old = Term->old;
2030 /* Save old window */
2031 hold_scr = Term->scr;
2033 /* Save old window */
2034 hold_mem = Term->mem;
2036 /* Save old window */
2037 hold_tmp = Term->tmp;
2039 /* Create new scanners */
2040 C_MAKE(Term->x1, h, TERM_LEN);
2041 C_MAKE(Term->x2, h, TERM_LEN);
2043 /* Create new window */
2044 MAKE(Term->old, term_win);
2046 /* Initialize new window */
2047 term_win_init(Term->old, w, h);
2049 /* Save the contents */
2050 term_win_copy(Term->old, hold_old, wid, hgt);
2052 /* Create new window */
2053 MAKE(Term->scr, term_win);
2055 /* Initialize new window */
2056 term_win_init(Term->scr, w, h);
2058 /* Save the contents */
2059 term_win_copy(Term->scr, hold_scr, wid, hgt);
2063 /* Create new window */
2064 MAKE(Term->mem, term_win);
2066 /* Initialize new window */
2067 term_win_init(Term->mem, w, h);
2069 /* Save the contents */
2070 term_win_copy(Term->mem, hold_mem, wid, hgt);
2075 /* Create new window */
2076 MAKE(Term->tmp, term_win);
2078 /* Initialize new window */
2079 term_win_init(Term->tmp, w, h);
2081 /* Save the contents */
2082 term_win_copy(Term->tmp, hold_tmp, wid, hgt);
2085 /* Free some arrays */
2086 C_KILL(hold_x1, Term->hgt, TERM_LEN);
2087 C_KILL(hold_x2, Term->hgt, TERM_LEN);
2090 term_win_nuke(hold_old, Term->wid, Term->hgt);
2093 KILL(hold_old, term_win);
2095 /* Illegal cursor */
2096 if (Term->old->cx >= w)
2098 if (Term->old->cy >= h)
2102 term_win_nuke(hold_scr, Term->wid, Term->hgt);
2105 KILL(hold_scr, term_win);
2107 /* Illegal cursor */
2108 if (Term->scr->cx >= w)
2110 if (Term->scr->cy >= h)
2116 term_win_nuke(hold_mem, Term->wid, Term->hgt);
2119 KILL(hold_mem, term_win);
2121 /* Illegal cursor */
2122 if (Term->mem->cx >= w)
2124 if (Term->mem->cy >= h)
2131 term_win_nuke(hold_tmp, Term->wid, Term->hgt);
2134 KILL(hold_tmp, term_win);
2136 /* Illegal cursor */
2137 if (Term->tmp->cx >= w)
2139 if (Term->tmp->cy >= h)
2147 /* Force "total erase" */
2148 Term->total_erase = TRUE;
2151 for (int i = 0; i < h; i++) {
2154 Term->x2[i] = w - 1;
2161 /* Execute the "resize_hook" hook, if available */
2162 if (Term->resize_hook)
2163 Term->resize_hook();
2169 * Activate a new Term (and deactivate the current Term)
2171 * This function is extremely important, and also somewhat bizarre.
2172 * It is the only function that should "modify" the value of "Term".
2174 * To "create" a valid "term", one should do "term_init(t)", then
2175 * set the various flags and hooks, and then do "term_activate(t)".
2177 errr term_activate(term_type *t)
2183 /* Deactivate the old Term */
2185 term_xtra(TERM_XTRA_LEVEL, 0);
2187 /* Call the special "init" hook */
2188 if (t && !t->active_flag) {
2189 /* Call the "init" hook */
2194 t->active_flag = TRUE;
2197 t->mapped_flag = TRUE;
2200 /* Remember the Term */
2203 /* Activate the new Term */
2205 term_xtra(TERM_XTRA_LEVEL, 1);
2211 * Initialize a term, using a window of the given size.
2212 * Also prepare the "input queue" for "k" keypresses
2213 * By default, the cursor starts out "invisible"
2214 * By default, we "erase" using "black spaces"
2216 errr term_init(term_type *t, TERM_LEN w, TERM_LEN h, int k)
2219 (void)WIPE(t, term_type);
2221 /* Prepare the input queue */
2222 t->key_head = t->key_tail = 0;
2224 /* Determine the input queue size */
2225 t->key_size = (u16b)k;
2227 /* Allocate the input queue */
2228 C_MAKE(t->key_queue, t->key_size, char);
2234 /* Allocate change arrays */
2235 C_MAKE(t->x1, h, TERM_LEN);
2236 C_MAKE(t->x2, h, TERM_LEN);
2238 /* Allocate "displayed" */
2239 MAKE(t->old, term_win);
2241 /* Initialize "displayed" */
2242 term_win_init(t->old, w, h);
2244 /* Allocate "requested" */
2245 MAKE(t->scr, term_win);
2247 /* Initialize "requested" */
2248 term_win_init(t->scr, w, h);
2251 for (TERM_LEN y = 0; y < h; y++) {
2261 /* Force "total erase" */
2262 t->total_erase = TRUE;
2264 /* Default "blank" */
2266 t->char_blank = ' ';
2268 /* Prepare "fake" hooks to prevent core dumps */
2269 t->curs_hook = term_curs_hack;
2270 t->bigcurs_hook = term_bigcurs_hack;
2271 t->wipe_hook = term_wipe_hack;
2272 t->text_hook = term_text_hack;
2273 t->pict_hook = term_pict_hack;
2279 * Move to a location and, using an attr, add a string vertically
2281 errr term_putstr_v(TERM_LEN x, TERM_LEN y, int n, byte a, concptr s)
2286 for (int i = 0; i < n && s[i] != 0; i++) {
2288 if ((res = term_gotoxy(x, y0)) != 0)
2291 if (iskanji(s[i])) {
2292 if ((res = term_addstr(2, a, &s[i])) != 0)
2299 if ((res = term_addstr(1, a, &s[i])) != 0)
2310 errr term_nuke(term_type *t)
2312 TERM_LEN w = t->wid;
2313 TERM_LEN h = t->hgt;
2314 if (t->active_flag) {
2318 t->active_flag = FALSE;
2319 t->mapped_flag = FALSE;
2322 term_win_nuke(t->old, w, h);
2323 KILL(t->old, term_win);
2324 term_win_nuke(t->scr, w, h);
2325 KILL(t->scr, term_win);
2327 term_win_nuke(t->mem, w, h);
2328 KILL(t->mem, term_win);
2332 term_win_nuke(t->tmp, w, h);
2333 KILL(t->tmp, term_win);
2336 C_KILL(t->x1, h, TERM_LEN);
2337 C_KILL(t->x2, h, TERM_LEN);
2338 C_KILL(t->key_queue, t->key_size, char);