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"
16 /* Special flags in the attr data */
17 #define AF_BIGTILE2 0xf0
23 * 属性に全角文字の1バイト目、2バイト目も記憶。
26 #define AF_KANJI1 0x10
27 #define AF_KANJI2 0x20
28 #define AF_KANJIC 0x0f
31 /* The current "term" */
32 term_type *Term = NULL;
34 /*** Local routines ***/
37 * Nuke a term_win (see below)
39 errr term_win_nuke(term_win *s, TERM_LEN w, TERM_LEN h)
41 /* Free the window access arrays */
42 C_KILL(s->a, h, TERM_COLOR *);
43 C_KILL(s->c, h, char *);
45 /* Free the window content arrays */
46 C_KILL(s->va, h * w, TERM_COLOR);
47 C_KILL(s->vc, h * w, char);
49 /* Free the terrain access arrays */
50 C_KILL(s->ta, h, TERM_COLOR *);
51 C_KILL(s->tc, h, char *);
53 /* Free the terrain content arrays */
54 C_KILL(s->vta, h * w, TERM_COLOR);
55 C_KILL(s->vtc, h * w, char);
61 * Initialize a "term_win" (using the given window size)
63 static errr term_win_init(term_win *s, TERM_LEN w, TERM_LEN h)
65 /* Make the window access arrays */
66 C_MAKE(s->a, h, TERM_COLOR *);
67 C_MAKE(s->c, h, char *);
69 /* Make the window content arrays */
70 C_MAKE(s->va, h * w, TERM_COLOR);
71 C_MAKE(s->vc, h * w, char);
73 /* Make the terrain access arrays */
74 C_MAKE(s->ta, h, TERM_COLOR *);
75 C_MAKE(s->tc, h, char *);
77 /* Make the terrain content arrays */
78 C_MAKE(s->vta, h * w, TERM_COLOR);
79 C_MAKE(s->vtc, h * w, char);
81 /* Prepare the window access arrays */
82 for (TERM_LEN y = 0; y < h; y++) {
83 s->a[y] = s->va + w * y;
84 s->c[y] = s->vc + w * y;
86 s->ta[y] = s->vta + w * y;
87 s->tc[y] = s->vtc + w * y;
94 * Copy a "term_win" from another
96 static errr term_win_copy(term_win *s, term_win *f, TERM_LEN w, TERM_LEN h)
99 for (TERM_LEN y = 0; y < h; y++) {
100 TERM_COLOR *f_aa = f->a[y];
101 char *f_cc = f->c[y];
103 TERM_COLOR *s_aa = s->a[y];
104 char *s_cc = s->c[y];
106 TERM_COLOR *f_taa = f->ta[y];
107 char *f_tcc = f->tc[y];
109 TERM_COLOR *s_taa = s->ta[y];
110 char *s_tcc = s->tc[y];
112 for (TERM_LEN x = 0; x < w; x++) {
129 /*** External hooks ***/
132 * Execute the "Term->user_hook" hook, if available (see above).
134 errr term_user(int n)
136 /* Verify the hook */
137 if (!Term->user_hook)
141 return ((*Term->user_hook)(n));
145 * Execute the "Term->xtra_hook" hook, if available (see above).
147 errr term_xtra(int n, int v)
149 /* Verify the hook */
150 if (!Term->xtra_hook)
154 return ((*Term->xtra_hook)(n, v));
160 * Fake hook for "term_curs()" (see above)
162 static errr term_curs_hack(TERM_LEN x, TERM_LEN y)
172 * Fake hook for "term_bigcurs()" (see above)
174 static errr term_bigcurs_hack(TERM_LEN x, TERM_LEN y) { return (*Term->curs_hook)(x, y); }
177 * Fake hook for "term_wipe()" (see above)
179 static errr term_wipe_hack(TERM_LEN x, TERM_LEN y, int n)
190 * Fake hook for "term_text()" (see above)
192 static errr term_text_hack(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr cp)
205 * Fake hook for "term_pict()" (see above)
207 static errr term_pict_hack(TERM_LEN x, TERM_LEN y, int n, const TERM_COLOR *ap, concptr cp, const TERM_COLOR *tap, concptr tcp)
221 /*** Efficient routines ***/
224 * Mentally draw an attr/char at a given location
225 * Assumes given location and values are valid.
227 void term_queue_char(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
229 term_win *scrn = Term->scr;
231 TERM_COLOR *scr_aa = &scrn->a[y][x];
232 char *scr_cc = &scrn->c[y][x];
234 TERM_COLOR *scr_taa = &scrn->ta[y][x];
235 char *scr_tcc = &scrn->tc[y][x];
237 /* Ignore non-changes */
238 if ((*scr_aa == a) && (*scr_cc == c) && (*scr_taa == ta) && (*scr_tcc == tc))
241 /* Save the "literal" information */
248 /* Check for new min/max row info */
254 /* Check for new min/max col info for this row */
256 Term->x1[y] = (byte)x;
258 Term->x2[y] = (byte)x;
261 if (((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2) || (scrn->a[y][x] & AF_KANJI2))
263 if ((scrn->a[y][x] & AF_BIGTILE2) == AF_BIGTILE2)
265 if ((x - 1) < Term->x1[y])
270 * Bigtile version of term_queue_char().
271 * If use_bigtile is FALSE, simply call term_queue_char().
272 * Otherwise, mentally draw a pair of attr/char at a given location.
273 * Assumes given location and values are valid.
275 void term_queue_bigchar(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c, TERM_COLOR ta, char tc)
280 * A table which relates each ascii character to a multibyte
283 * 「■」は二倍幅豆腐の内部コードに使用。
285 static char ascii_to_zenkaku[] = " !”#$%&’()*+,-./"
296 /* If non bigtile mode, call orginal function */
298 term_queue_char(x, y, a, c, ta, tc);
302 /* A tile becomes a Bigtile */
303 if ((a & AF_TILE1) && (c & 0x80)) {
304 /* Mark it as a Bigtile */
309 /* Ignore non-tile background */
310 if (!((ta & AF_TILE1) && (tc & 0x80))) {
318 * Use a multibyte character instead of a dirty pair of ASCII
321 else if (' ' <= c) /* isprint(c) */
323 c2 = ascii_to_zenkaku[2 * (c - ' ') + 1];
324 c = ascii_to_zenkaku[2 * (c - ' ')];
326 /* Mark it as a Kanji */
333 /* Dirty pair of ASCII characters */
338 /* Display pair of attr/char */
339 term_queue_char(x, y, a, c, ta, tc);
340 term_queue_char(x + 1, y, a2, c2, 0, 0);
344 * Mentally draw a string of attr/chars at a given location
345 * Assumes given location and values are valid.
346 * This function is designed to be fast, with no consistancy checking.
347 * It is used to update the map in the game.
349 void term_queue_line(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR *a, char *c, TERM_COLOR *ta, char *tc)
351 term_win *scrn = Term->scr;
356 TERM_COLOR *scr_aa = &scrn->a[y][x];
357 char *scr_cc = &scrn->c[y][x];
359 TERM_COLOR *scr_taa = &scrn->ta[y][x];
360 char *scr_tcc = &scrn->tc[y][x];
363 /* Ignore non-changes */
364 if ((*scr_aa == *a) && (*scr_cc == *c) && (*scr_taa == *ta) && (*scr_tcc == *tc)) {
377 /* Save the "literal" information */
381 /* Save the "literal" information */
385 /* Track minimum changed column */
389 /* Track maximum changed column */
395 /* Expand the "change area" as needed */
397 /* Check for new min/max row info */
403 /* Check for new min/max col info in this row */
404 if (x1 < Term->x1[y])
405 Term->x1[y] = (byte)x1;
406 if (x2 > Term->x2[y])
407 Term->x2[y] = (byte)x2;
412 * Mentally draw some attr/chars at a given location
414 * Assumes that (x,y) is a valid location, that the first "n" characters
415 * of the string "s" are all valid (non-zero), and that (x+n-1,y) is also
416 * a valid location, so the first "n" characters of "s" can all be added
417 * starting at (x,y) without causing any illegal operations.
419 static void term_queue_chars(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
421 TERM_LEN x1 = -1, x2 = -1;
423 TERM_COLOR *scr_aa = Term->scr->a[y];
425 char *scr_cc = Term->scr->c[y];
427 TERM_COLOR *scr_taa = Term->scr->ta[y];
428 char *scr_tcc = Term->scr->tc[y];
430 char *scr_cc = Term->scr->c[y];
432 TERM_COLOR *scr_taa = Term->scr->ta[y];
433 char *scr_tcc = Term->scr->tc[y];
438 if (n == 0 || *s == 0)
441 * 全角文字の右半分から文字を表示する場合、
445 if ((scr_aa[x] & AF_KANJI2) && (scr_aa[x] & AF_BIGTILE2) != AF_BIGTILE2) {
447 scr_aa[x - 1] &= AF_KANJIC;
451 /* Queue the attr/chars */
452 for (; n; x++, s++, n--) {
454 /* 特殊文字としてMSBが立っている可能性がある */
455 /* その場合attrのMSBも立っているのでこれで識別する */
457 if (!(a & AF_TILE1) && iskanji(*s)) {
461 byte na1 = (a | AF_KANJI1);
462 byte na2 = (a | AF_KANJI2);
464 if ((--n == 0) || !nc2)
467 if (scr_aa[x++] == na1 && scr_aa[x] == na2 && scr_cc[x - 1] == nc1 && scr_cc[x] == nc2 && (scr_taa[x - 1] == 0) && (scr_taa[x] == 0)
468 && (scr_tcc[x - 1] == 0) && (scr_tcc[x] == 0))
481 TERM_COLOR oa = scr_aa[x];
484 TERM_COLOR ota = scr_taa[x];
485 char otc = scr_tcc[x];
487 /* Ignore non-changes */
488 if ((oa == a) && (oc == *s) && (ota == 0) && (otc == 0))
491 /* Save the "literal" information */
498 /* Note the "range" of window updates */
509 * 全角文字の左半分で表示を終了する場合、
511 * (条件追加:タイルの1文字目でない事を確かめるように。)
516 term_get_size(&w, &h);
517 if (x != w && !(scr_aa[x] & AF_TILE1) && (scr_aa[x] & AF_KANJI2)) {
519 scr_aa[x] &= AF_KANJIC;
526 /* Expand the "change area" as needed */
528 /* Check for new min/max row info */
534 /* Check for new min/max col info in this row */
535 if (x1 < Term->x1[y])
536 Term->x1[y] = (byte)x1;
537 if (x2 > Term->x2[y])
538 Term->x2[y] = (byte)x2;
542 /*** Refresh routines ***/
545 * Flush a row of the current window (see "term_fresh")
546 * Display text using "term_pict()"
548 static void term_fresh_row_pict(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
550 TERM_COLOR *old_aa = Term->old->a[y];
551 char *old_cc = Term->old->c[y];
553 TERM_COLOR *scr_aa = Term->scr->a[y];
554 char *scr_cc = Term->scr->c[y];
556 TERM_COLOR *old_taa = Term->old->ta[y];
557 char *old_tcc = Term->old->tc[y];
559 TERM_COLOR *scr_taa = Term->scr->ta[y];
560 char *scr_tcc = Term->scr->tc[y];
584 /* Scan "modified" columns */
585 for (TERM_LEN x = x1; x <= x2; x++) {
586 /* See what is currently here */
590 /* See what is desired there */
603 /* 特殊文字としてMSBが立っている可能性がある */
604 /* その場合attrのMSBも立っているのでこれで識別する */
606 kanji = (iskanji(nc) && !(na & AF_TILE1));
615 /* Handle unchanged grids */
617 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
619 || (scr_aa[x + 1] == old_aa[x + 1] && scr_cc[x + 1] == old_cc[x + 1] && scr_taa[x + 1] == old_taa[x + 1] && scr_tcc[x + 1] == old_tcc[x + 1])))
621 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
626 /* Draw pending attr/char pairs */
627 (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
644 /* Save new contents */
651 /* Restart and Advance */
658 /* Draw pending attr/char pairs */
659 (void)((*Term->pict_hook)(fx, y, fn, &scr_aa[fx], &scr_cc[fx], &scr_taa[fx], &scr_tcc[fx]));
664 * Flush a row of the current window (see "term_fresh")
666 * Display text using "term_text()" and "term_wipe()",
667 * but use "term_pict()" for high-bit attr/char pairs
669 static void term_fresh_row_both(TERM_LEN y, int x1, int x2)
671 TERM_COLOR *old_aa = Term->old->a[y];
672 char *old_cc = Term->old->c[y];
674 TERM_COLOR *scr_aa = Term->scr->a[y];
675 char *scr_cc = Term->scr->c[y];
677 TERM_COLOR *old_taa = Term->old->ta[y];
678 char *old_tcc = Term->old->tc[y];
679 TERM_COLOR *scr_taa = Term->scr->ta[y];
680 char *scr_tcc = Term->scr->tc[y];
687 /* The "always_text" flag */
688 int always_text = Term->always_text;
697 byte fa = Term->attr_blank;
709 /* Scan "modified" columns */
710 for (TERM_LEN x = x1; x <= x2; x++) {
711 /* See what is currently here */
715 /* See what is desired there */
728 /* 特殊文字としてMSBが立っている可能性がある */
729 /* その場合attrのMSBも立っているのでこれで識別する */
731 /* kanji = (iskanji(nc)); */
732 kanji = (iskanji(nc) && !(na & AF_TILE1));
741 /* Handle unchanged grids */
743 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc)
745 || (scr_aa[x + 1] == old_aa[x + 1] && scr_cc[x + 1] == old_cc[x + 1] && scr_taa[x + 1] == old_taa[x + 1] && scr_tcc[x + 1] == old_tcc[x + 1])))
747 if ((na == oa) && (nc == oc) && (nta == ota) && (ntc == otc))
752 /* Draw pending chars (normal) */
753 if (fa || always_text) {
754 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
757 /* Draw pending chars (black) */
759 (void)((*Term->wipe_hook)(fx, y, fn));
778 /* Save new contents */
785 /* 2nd byte of bigtile */
786 if ((na & AF_BIGTILE2) == AF_BIGTILE2)
789 /* Handle high-bit attr/chars */
790 if ((na & AF_TILE1) && (nc & 0x80)) {
793 /* Draw pending chars (normal) */
794 if (fa || always_text) {
795 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
798 /* Draw pending chars (black) */
800 (void)((*Term->wipe_hook)(fx, y, fn));
807 /* Draw the special attr/char pair */
808 (void)((*Term->pict_hook)(x, y, 1, &na, &nc, &nta, &ntc));
814 /* Notice new color */
816 if (fa != (na & AF_KANJIC))
824 /* Draw the pending chars */
825 if (fa || always_text) {
826 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
829 /* Erase "leading" spaces */
831 (void)((*Term->wipe_hook)(fx, y, fn));
838 /* Save the new color */
840 fa = (na & AF_KANJIC);
846 /* Restart and Advance */
853 /* Draw pending chars (normal) */
854 if (fa || always_text) {
855 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
858 /* Draw pending chars (black) */
860 (void)((*Term->wipe_hook)(fx, y, fn));
866 * Flush a row of the current window (see "term_fresh")
868 * Display text using "term_text()" and "term_wipe()"
870 static void term_fresh_row_text(TERM_LEN y, TERM_LEN x1, TERM_LEN x2)
872 TERM_COLOR *old_aa = Term->old->a[y];
873 char *old_cc = Term->old->c[y];
875 TERM_COLOR *scr_aa = Term->scr->a[y];
876 char *scr_cc = Term->scr->c[y];
878 /* The "always_text" flag */
879 int always_text = Term->always_text;
888 byte fa = Term->attr_blank;
900 for (TERM_LEN x = 0; x < x1; x++)
901 if (!(old_aa[x] & AF_TILE1) && iskanji(old_cc[x])) {
909 /* Scan "modified" columns */
910 for (TERM_LEN x = x1; x <= x2; x++) {
911 /* See what is currently here */
915 /* See what is desired there */
928 /* 特殊文字としてMSBが立っている可能性がある */
929 /* その場合attrのMSBも立っているのでこれで識別する */
931 kanji = (iskanji(nc) && !(na & AF_TILE1));
933 /* Handle unchanged grids */
935 if ((na == oa) && (nc == oc) && (!kanji || (scr_aa[x + 1] == old_aa[x + 1] && scr_cc[x + 1] == old_cc[x + 1])))
937 if ((na == oa) && (nc == oc))
943 /* Draw pending chars (normal) */
944 if (fa || always_text) {
945 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
948 /* Draw pending chars (black) */
950 (void)((*Term->wipe_hook)(fx, y, fn));
969 /* Save new contents */
973 /* Notice new color */
975 if (fa != (na & AF_KANJIC))
983 /* Draw the pending chars */
984 if (fa || always_text) {
985 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
988 /* Erase "leading" spaces */
990 (void)((*Term->wipe_hook)(fx, y, fn));
997 /* Save the new color */
999 fa = (na & AF_KANJIC);
1005 /* Restart and Advance */
1012 /* Draw pending chars (normal) */
1013 if (fa || always_text) {
1014 (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
1017 /* Draw pending chars (black) */
1019 (void)((*Term->wipe_hook)(fx, y, fn));
1025 * @brief Actually perform all requested changes to the window
1027 errr term_fresh(void)
1035 term_win *old = Term->old;
1036 term_win *scr = Term->scr;
1038 /* Before initialize (Advice from Mr.shimitei)*/
1042 /* Do nothing unless "mapped" */
1043 if (!Term->mapped_flag)
1046 /* Trivial Refresh */
1047 if ((y1 > y2) && (scr->cu == old->cu) && (scr->cv == old->cv) && (scr->cx == old->cx) && (scr->cy == old->cy) && !(Term->total_erase)) {
1052 /* Handle "total erase" */
1053 if (Term->total_erase) {
1054 byte na = Term->attr_blank;
1055 char nc = Term->char_blank;
1057 /* Physically erase the entire window */
1058 term_xtra(TERM_XTRA_CLEAR, 0);
1060 /* clear all "cursor" data */
1061 old->cv = old->cu = old->cx = old->cy = 0;
1064 for (TERM_LEN y = 0; y < h; y++) {
1065 TERM_COLOR *aa = old->a[y];
1066 char *cc = old->c[y];
1068 TERM_COLOR *taa = old->ta[y];
1069 char *tcc = old->tc[y];
1071 /* Wipe each column */
1072 for (TERM_LEN x = 0; x < w; x++) {
1073 /* Wipe each grid */
1082 /* Redraw every row */
1084 Term->y2 = y2 = h - 1;
1086 /* Redraw every column */
1087 for (TERM_LEN y = 0; y < h; y++) {
1089 Term->x2[y] = w - 1;
1092 /* Forget "total erase" */
1093 Term->total_erase = FALSE;
1096 /* Cursor update -- Erase old Cursor */
1097 if (Term->soft_cursor) {
1098 /* Cursor was visible */
1099 if (!old->cu && old->cv) {
1101 TERM_LEN tx = old->cx;
1102 TERM_LEN ty = old->cy;
1104 TERM_COLOR *old_aa = old->a[ty];
1105 char *old_cc = old->c[ty];
1107 TERM_COLOR *old_taa = old->ta[ty];
1108 char *old_tcc = old->tc[ty];
1110 TERM_COLOR ota = old_taa[tx];
1111 char otc = old_tcc[tx];
1114 if (tx + 1 < Term->wid && !(old_aa[tx] & AF_TILE1) && iskanji(old_cc[tx]))
1117 /* Use "term_pict()" always */
1118 if (Term->always_pict)
1119 (void)((*Term->pict_hook)(tx, ty, csize, &old_aa[tx], &old_cc[tx], &ota, &otc));
1121 /* Use "term_pict()" sometimes */
1122 else if (Term->higher_pict && (old_aa[tx] & AF_TILE1) && (old_cc[tx] & 0x80))
1123 (void)((*Term->pict_hook)(tx, ty, 1, &old_aa[tx], &old_cc[tx], &ota, &otc));
1126 * Restore the actual character
1127 * 元の文字の描画範囲がカーソルより小さいと、
1128 * 上書きされなかった部分がゴミとして残る。
1129 * wipe_hook でカーソルを消去して text_hook で書き直す。
1131 else if (old_aa[tx] || Term->always_text) {
1132 (void)((*Term->wipe_hook)(tx, ty, 1));
1133 (void)((*Term->text_hook)(tx, ty, csize, (unsigned char)(old_aa[tx] & 0xf), &old_cc[tx]));
1136 /* Erase the grid */
1138 (void)((*Term->wipe_hook)(tx, ty, 1));
1142 /* Cursor Update -- Erase old Cursor */
1144 /* Cursor will be invisible */
1145 if (scr->cu || !scr->cv) {
1146 /* Make the cursor invisible */
1147 term_xtra(TERM_XTRA_SHAPE, 0);
1151 /* Something to update */
1153 /* Handle "icky corner" */
1154 if (Term->icky_corner) {
1155 /* Avoid the corner */
1157 /* Avoid the corner */
1158 if (Term->x2[h - 1] > w - 2) {
1159 /* Avoid the corner */
1160 Term->x2[h - 1] = w - 2;
1165 /* Scan the "modified" rows */
1166 for (TERM_LEN y = y1; y <= y2; ++y) {
1167 TERM_LEN x1 = Term->x1[y];
1168 TERM_LEN x2 = Term->x2[y];
1170 /* Flush each "modified" row */
1172 /* Always use "term_pict()" */
1173 if (Term->always_pict) {
1175 term_fresh_row_pict(y, x1, x2);
1178 /* Sometimes use "term_pict()" */
1179 else if (Term->higher_pict) {
1181 term_fresh_row_both(y, x1, x2);
1184 /* Never use "term_pict()" */
1187 term_fresh_row_text(y, x1, x2);
1190 /* This row is all done */
1191 Term->x1[y] = (byte)w;
1194 /* Flush that row (if allowed) */
1195 if (!Term->never_frosh)
1196 term_xtra(TERM_XTRA_FROSH, y);
1200 /* No rows are invalid */
1205 /* Cursor update -- Show new Cursor */
1206 if (Term->soft_cursor) {
1207 /* Draw the cursor */
1208 if (!scr->cu && scr->cv) {
1210 if ((scr->cx + 1 < w)
1211 && ((old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2
1212 || (!(old->a[scr->cy][scr->cx] & AF_TILE1) && iskanji(old->c[scr->cy][scr->cx]))))
1214 if ((scr->cx + 1 < w) && (old->a[scr->cy][scr->cx + 1] & AF_BIGTILE2) == AF_BIGTILE2)
1217 /* Double width cursor for the Bigtile mode */
1218 (void)((*Term->bigcurs_hook)(scr->cx, scr->cy));
1220 /* Call the cursor display routine */
1221 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1226 /* Cursor Update -- Show new Cursor */
1228 /* The cursor is useless, hide it */
1230 /* Paranoia -- Put the cursor NEAR where it belongs */
1231 (void)((*Term->curs_hook)(w - 1, scr->cy));
1233 /* Make the cursor invisible */
1234 /* term_xtra(TERM_XTRA_SHAPE, 0); */
1237 /* The cursor is invisible, hide it */
1238 else if (!scr->cv) {
1239 /* Paranoia -- Put the cursor where it belongs */
1240 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1242 /* Make the cursor invisible */
1243 /* term_xtra(TERM_XTRA_SHAPE, 0); */
1246 /* The cursor is visible, display it correctly */
1248 /* Put the cursor where it belongs */
1249 (void)((*Term->curs_hook)(scr->cx, scr->cy));
1251 /* Make the cursor visible */
1252 term_xtra(TERM_XTRA_SHAPE, 1);
1256 /* Save the "cursor state" */
1262 /* Actually flush the output */
1263 term_xtra(TERM_XTRA_FRESH, 0);
1267 /*** Output routines ***/
1270 * Set the cursor visibility
1272 errr term_set_cursor(int v)
1275 if (Term->scr->cv == v)
1279 Term->scr->cv = (bool)v;
1284 * Place the cursor at a given location
1286 * Note -- "illegal" requests do not move the cursor.
1288 errr term_gotoxy(TERM_LEN x, TERM_LEN y)
1294 if ((x < 0) || (x >= w))
1296 if ((y < 0) || (y >= h))
1299 /* Remember the cursor */
1300 Term->scr->cx = (byte)x;
1301 Term->scr->cy = (byte)y;
1303 /* The cursor is not useless */
1309 * At a given location, place an attr/char
1310 * Do not change the cursor position
1311 * No visual changes until "term_fresh()".
1313 errr term_draw(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1318 if ((x < 0) || (x >= w))
1320 if ((y < 0) || (y >= h))
1323 /* Paranoia -- illegal char */
1327 /* Queue it for later */
1328 term_queue_char(x, y, a, c, 0, 0);
1333 * Using the given attr, add the given char at the cursor.
1335 * We return "-2" if the character is "illegal". XXX XXX
1337 * We return "-1" if the cursor is currently unusable.
1339 * We queue the given attr/char for display at the current
1340 * cursor location, and advance the cursor to the right,
1341 * marking it as unuable and returning "1" if it leaves
1342 * the screen, and otherwise returning "0".
1344 * So when this function, or the following one, return a
1345 * positive value, future calls to either function will
1346 * return negative ones.
1348 errr term_addch(TERM_COLOR a, char c)
1350 TERM_LEN w = Term->wid;
1352 /* Handle "unusable" cursor */
1356 /* Paranoia -- no illegal chars */
1360 /* Queue the given character for display */
1361 term_queue_char(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1363 /* Advance the cursor */
1367 if (Term->scr->cx < w)
1370 /* Note "Useless" cursor */
1373 /* Note "Useless" cursor */
1378 * Bigtile version of term_addch().
1380 * If use_bigtile is FALSE, simply call term_addch() .
1382 * Otherwise, queue a pair of attr/char for display at the current
1383 * cursor location, and advance the cursor to the right by two.
1385 errr term_add_bigch(TERM_COLOR a, char c)
1388 return term_addch(a, c);
1390 /* Handle "unusable" cursor */
1394 /* Paranoia -- no illegal chars */
1398 /* Queue the given character for display */
1399 term_queue_bigchar(Term->scr->cx, Term->scr->cy, a, c, 0, 0);
1401 /* Advance the cursor */
1405 if (Term->scr->cx < Term->wid)
1408 /* Note "Useless" cursor */
1411 /* Note "Useless" cursor */
1416 * At the current location, using an attr, add a string
1418 * We also take a length "n", using negative values to imply
1419 * the largest possible value, and then we use the minimum of
1420 * this length and the "actual" length of the string as the
1421 * actual number of characters to attempt to display, never
1422 * displaying more characters than will actually fit, since
1423 * we do NOT attempt to "wrap" the cursor at the screen edge.
1425 * We return "-1" if the cursor is currently unusable.
1426 * We return "N" if we were "only" able to write "N" chars,
1427 * even if all of the given characters fit on the screen,
1428 * and mark the cursor as unusable for future attempts.
1430 * So when this function, or the preceding one, return a
1431 * positive value, future calls to either function will
1432 * return negative ones.
1434 errr term_addstr(int n, TERM_COLOR a, concptr s)
1437 TERM_LEN w = Term->wid;
1440 /* Handle "unusable" cursor */
1444 /* Obtain maximal length */
1445 k = (n < 0) ? (w + 1) : n;
1447 /* Obtain the usable string length */
1448 for (n = 0; (n < k) && s[n]; n++) /* loop */
1451 /* React to reaching the edge of the screen */
1452 if (Term->scr->cx + n >= w)
1453 res = n = w - Term->scr->cx;
1455 /* Queue the first "n" characters for display */
1456 term_queue_chars(Term->scr->cx, Term->scr->cy, n, a, s);
1458 /* Advance the cursor */
1459 Term->scr->cx += (byte)n;
1461 /* Notice "Useless" cursor */
1469 * Move to a location and, using an attr, add a char
1471 errr term_putch(TERM_LEN x, TERM_LEN y, TERM_COLOR a, char c)
1476 if ((res = term_gotoxy(x, y)) != 0)
1479 /* Then add the char */
1480 if ((res = term_addch(a, c)) != 0)
1487 * Move to a location and, using an attr, add a string
1489 errr term_putstr(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
1494 if ((res = term_gotoxy(x, y)) != 0)
1497 /* Then add the string */
1498 if ((res = term_addstr(n, a, s)) != 0)
1505 * Place cursor at (x,y), and clear the next "n" chars
1507 errr term_erase(TERM_LEN x, TERM_LEN y, int n)
1509 TERM_LEN w = Term->wid;
1510 /* int h = Term->hgt; */
1515 int na = Term->attr_blank;
1516 int nc = Term->char_blank;
1521 TERM_COLOR *scr_taa;
1525 if (term_gotoxy(x, y))
1528 /* Force legal size */
1533 scr_aa = Term->scr->a[y];
1534 scr_cc = Term->scr->c[y];
1536 scr_taa = Term->scr->ta[y];
1537 scr_tcc = Term->scr->tc[y];
1541 * 全角文字の右半分から文字を表示する場合、
1544 if (n > 0 && (((scr_aa[x] & AF_KANJI2) && !(scr_aa[x] & AF_TILE1)) || (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2))
1546 if (n > 0 && (scr_aa[x] & AF_BIGTILE2) == AF_BIGTILE2)
1553 /* Scan every column */
1554 for (int i = 0; i < n; i++, x++) {
1558 /* Ignore "non-changes" */
1559 if ((oa == na) && (oc == nc))
1564 * 全角文字の左半分で表示を終了する場合、
1567 * 2001/04/29 -- Habu
1568 * 行の右端の場合はこの処理をしないように修正。
1570 if ((oa & AF_KANJI1) && (i + 1) == n && x != w - 1)
1573 /* Save the "literal" information */
1574 scr_aa[x] = (byte)na;
1575 scr_cc[x] = (char)nc;
1580 /* Track minimum changed column */
1584 /* Track maximum changed column */
1588 /* Expand the "change area" as needed */
1590 /* Check for new min/max row info */
1596 /* Check for new min/max col info in this row */
1597 if (x1 < Term->x1[y])
1598 Term->x1[y] = (byte)x1;
1599 if (x2 > Term->x2[y])
1600 Term->x2[y] = (byte)x2;
1607 * Clear the entire window, and move to the top left corner
1609 * Note the use of the special "total_erase" code
1611 errr term_clear(void)
1613 TERM_LEN w = Term->wid;
1614 TERM_LEN h = Term->hgt;
1616 TERM_COLOR na = Term->attr_blank;
1617 char nc = Term->char_blank;
1622 /* Cursor to the top left */
1623 Term->scr->cx = Term->scr->cy = 0;
1626 for (TERM_LEN y = 0; y < h; y++) {
1627 TERM_COLOR *scr_aa = Term->scr->a[y];
1628 char *scr_cc = Term->scr->c[y];
1630 TERM_COLOR *scr_taa = Term->scr->ta[y];
1631 char *scr_tcc = Term->scr->tc[y];
1633 /* Wipe each column */
1634 for (TERM_LEN x = 0; x < w; x++) {
1642 /* This row has changed */
1644 Term->x2[y] = w - 1;
1647 /* Every row has changed */
1651 /* Force "total erase" */
1652 Term->total_erase = TRUE;
1657 * Redraw (and refresh) the whole window.
1659 errr term_redraw(void)
1661 /* Force "total erase" */
1662 Term->total_erase = TRUE;
1668 * Redraw part of a window.
1670 errr term_redraw_section(TERM_LEN x1, TERM_LEN y1, TERM_LEN x2, TERM_LEN y2)
1674 /* Bounds checking */
1675 if (y2 >= Term->hgt)
1677 if (x2 >= Term->wid)
1685 Term->y1 = (byte)y1;
1686 Term->y2 = (byte)y2;
1688 /* Set the x limits */
1689 for (int i = Term->y1; i <= Term->y2; i++) {
1695 if (Term->scr->a[i][x1j] & AF_KANJI2)
1699 if (x2j < Term->wid - 1) {
1700 if (Term->scr->a[i][x2j] & AF_KANJI1)
1704 Term->x1[i] = (byte)x1j;
1705 Term->x2[i] = (byte)x2j;
1707 g_ptr = Term->old->c[i];
1709 /* Clear the section so it is redrawn */
1710 for (int j = x1j; j <= x2j; j++) {
1711 /* Hack - set the old character to "none" */
1718 g_ptr = Term->old->c[i];
1720 /* Clear the section so it is redrawn */
1721 for (int j = x1; j <= x2; j++) {
1722 /* Hack - set the old character to "none" */
1732 /*** Access routines ***/
1735 * Extract the cursor visibility
1737 errr term_get_cursor(int *v)
1739 /* Extract visibility */
1740 (*v) = Term->scr->cv;
1745 * Extract the current window size
1747 errr term_get_size(TERM_LEN *w, TERM_LEN *h)
1749 /* Access the cursor */
1756 * Extract the current cursor location
1758 errr term_locate(TERM_LEN *x, TERM_LEN *y)
1760 /* Access the cursor */
1761 (*x) = Term->scr->cx;
1762 (*y) = Term->scr->cy;
1764 /* Warn about "useless" cursor */
1772 * At a given location, determine the "current" attr and char
1773 * Note that this refers to what will be on the window after the
1774 * next call to "term_fresh()". It may or may not already be there.
1776 errr term_what(TERM_LEN x, TERM_LEN y, TERM_COLOR *a, char *c)
1778 TERM_LEN w = Term->wid;
1779 TERM_LEN h = Term->hgt;
1781 if ((x < 0) || (x >= w))
1783 if ((y < 0) || (y >= h))
1787 (*a) = Term->scr->a[y][x];
1788 (*c) = Term->scr->c[y][x];
1792 /*** Input routines ***/
1795 * Flush and forget the input
1797 errr term_flush(void)
1799 /* Flush all events */
1800 term_xtra(TERM_XTRA_FLUSH, 0);
1802 /* Forget all keypresses */
1803 Term->key_head = Term->key_tail = 0;
1808 * Add a keypress to the FRONT of the "queue"
1810 errr term_key_push(int k)
1812 /* Refuse to enqueue non-keys */
1816 /* Overflow may induce circular queue */
1817 if (Term->key_tail == 0)
1818 Term->key_tail = Term->key_size;
1820 /* Back up, Store the char */
1821 Term->key_queue[--Term->key_tail] = (char)k;
1823 if (Term->key_head != Term->key_tail)
1830 * Check for a pending keypress on the key queue.
1832 * Store the keypress, if any, in "ch", and return "0".
1833 * Otherwise store "zero" in "ch", and return "1".
1835 * Wait for a keypress if "wait" is true.
1837 * Remove the keypress if "take" is true.
1839 errr term_inkey(char *ch, bool wait, bool take)
1845 if (!Term->never_bored) {
1846 /* Process random events */
1847 term_xtra(TERM_XTRA_BORED, 0);
1852 /* Process pending events while necessary */
1853 while (Term->key_head == Term->key_tail) {
1854 /* Process events (wait for one) */
1855 term_xtra(TERM_XTRA_EVENT, TRUE);
1861 /* Process pending events if necessary */
1862 if (Term->key_head == Term->key_tail) {
1863 /* Process events (do not wait) */
1864 term_xtra(TERM_XTRA_EVENT, FALSE);
1868 /* No keys are ready */
1869 if (Term->key_head == Term->key_tail)
1872 /* Extract the next keypress */
1873 (*ch) = Term->key_queue[Term->key_tail];
1875 /* If requested, advance the queue, wrap around if necessary */
1876 if (take && (++Term->key_tail == Term->key_size))
1882 /*** Extra routines ***/
1885 * Save the "requested" screen into the "memorized" screen
1887 * Every "term_save()" should match exactly one "term_load()"
1889 errr term_save(void)
1891 TERM_LEN w = Term->wid;
1892 TERM_LEN h = Term->hgt;
1896 /* Allocate window */
1897 MAKE(Term->mem, term_win);
1899 /* Initialize window */
1900 term_win_init(Term->mem, w, h);
1904 term_win_copy(Term->mem, Term->scr, w, h);
1909 * Restore the "requested" contents (see above).
1911 * Every "term_save()" should match exactly one "term_load()"
1913 errr term_load(void)
1915 TERM_LEN w = Term->wid;
1916 TERM_LEN h = Term->hgt;
1920 /* Allocate window */
1921 MAKE(Term->mem, term_win);
1923 /* Initialize window */
1924 term_win_init(Term->mem, w, h);
1928 term_win_copy(Term->scr, Term->mem, w, h);
1931 for (TERM_LEN y = 0; y < h; y++) {
1934 Term->x2[y] = w - 1;
1944 * Exchange the "requested" screen with the "tmp" screen
1946 static errr term_exchange(void)
1948 TERM_LEN w = Term->wid;
1949 TERM_LEN h = Term->hgt;
1951 term_win *exchanger;
1955 /* Allocate window */
1956 MAKE(Term->tmp, term_win);
1958 /* Initialize window */
1959 term_win_init(Term->tmp, w, h);
1963 exchanger = Term->scr;
1964 Term->scr = Term->tmp;
1965 Term->tmp = exchanger;
1968 for (TERM_LEN y = 0; y < h; y++) {
1971 Term->x2[y] = w - 1;
1981 * React to a new physical window size.
1983 errr term_resize(TERM_LEN w, TERM_LEN h)
1995 /* Resizing is forbidden */
1996 if (Term->fixed_shape)
1999 /* Ignore illegal changes */
2000 if ((w < 1) || (h < 1))
2003 /* Ignore non-changes */
2004 if ((Term->wid == w) && (Term->hgt == h) && (arg_bigtile == use_bigtile))
2007 use_bigtile = arg_bigtile;
2009 /* Minimum dimensions */
2010 wid = MIN(Term->wid, w);
2011 hgt = MIN(Term->hgt, h);
2017 /* Save old window */
2018 hold_old = Term->old;
2020 /* Save old window */
2021 hold_scr = Term->scr;
2023 /* Save old window */
2024 hold_mem = Term->mem;
2026 /* Save old window */
2027 hold_tmp = Term->tmp;
2029 /* Create new scanners */
2030 C_MAKE(Term->x1, h, TERM_LEN);
2031 C_MAKE(Term->x2, h, TERM_LEN);
2033 /* Create new window */
2034 MAKE(Term->old, term_win);
2036 /* Initialize new window */
2037 term_win_init(Term->old, w, h);
2039 /* Save the contents */
2040 term_win_copy(Term->old, hold_old, wid, hgt);
2042 /* Create new window */
2043 MAKE(Term->scr, term_win);
2045 /* Initialize new window */
2046 term_win_init(Term->scr, w, h);
2048 /* Save the contents */
2049 term_win_copy(Term->scr, hold_scr, wid, hgt);
2053 /* Create new window */
2054 MAKE(Term->mem, term_win);
2056 /* Initialize new window */
2057 term_win_init(Term->mem, w, h);
2059 /* Save the contents */
2060 term_win_copy(Term->mem, hold_mem, wid, hgt);
2065 /* Create new window */
2066 MAKE(Term->tmp, term_win);
2068 /* Initialize new window */
2069 term_win_init(Term->tmp, w, h);
2071 /* Save the contents */
2072 term_win_copy(Term->tmp, hold_tmp, wid, hgt);
2075 /* Free some arrays */
2076 C_KILL(hold_x1, Term->hgt, TERM_LEN);
2077 C_KILL(hold_x2, Term->hgt, TERM_LEN);
2080 term_win_nuke(hold_old, Term->wid, Term->hgt);
2083 KILL(hold_old, term_win);
2085 /* Illegal cursor */
2086 if (Term->old->cx >= w)
2088 if (Term->old->cy >= h)
2092 term_win_nuke(hold_scr, Term->wid, Term->hgt);
2095 KILL(hold_scr, term_win);
2097 /* Illegal cursor */
2098 if (Term->scr->cx >= w)
2100 if (Term->scr->cy >= h)
2106 term_win_nuke(hold_mem, Term->wid, Term->hgt);
2109 KILL(hold_mem, term_win);
2111 /* Illegal cursor */
2112 if (Term->mem->cx >= w)
2114 if (Term->mem->cy >= h)
2121 term_win_nuke(hold_tmp, Term->wid, Term->hgt);
2124 KILL(hold_tmp, term_win);
2126 /* Illegal cursor */
2127 if (Term->tmp->cx >= w)
2129 if (Term->tmp->cy >= h)
2137 /* Force "total erase" */
2138 Term->total_erase = TRUE;
2141 for (int i = 0; i < h; i++) {
2144 Term->x2[i] = w - 1;
2151 /* Execute the "resize_hook" hook, if available */
2152 if (Term->resize_hook)
2153 Term->resize_hook();
2159 * Activate a new Term (and deactivate the current Term)
2161 * This function is extremely important, and also somewhat bizarre.
2162 * It is the only function that should "modify" the value of "Term".
2164 * To "create" a valid "term", one should do "term_init(t)", then
2165 * set the various flags and hooks, and then do "term_activate(t)".
2167 errr term_activate(term_type *t)
2173 /* Deactivate the old Term */
2175 term_xtra(TERM_XTRA_LEVEL, 0);
2177 /* Call the special "init" hook */
2178 if (t && !t->active_flag) {
2179 /* Call the "init" hook */
2184 t->active_flag = TRUE;
2187 t->mapped_flag = TRUE;
2190 /* Remember the Term */
2193 /* Activate the new Term */
2195 term_xtra(TERM_XTRA_LEVEL, 1);
2201 * Initialize a term, using a window of the given size.
2202 * Also prepare the "input queue" for "k" keypresses
2203 * By default, the cursor starts out "invisible"
2204 * By default, we "erase" using "black spaces"
2206 errr term_init(term_type *t, TERM_LEN w, TERM_LEN h, int k)
2209 (void)WIPE(t, term_type);
2211 /* Prepare the input queue */
2212 t->key_head = t->key_tail = 0;
2214 /* Determine the input queue size */
2215 t->key_size = (u16b)k;
2217 /* Allocate the input queue */
2218 C_MAKE(t->key_queue, t->key_size, char);
2224 /* Allocate change arrays */
2225 C_MAKE(t->x1, h, TERM_LEN);
2226 C_MAKE(t->x2, h, TERM_LEN);
2228 /* Allocate "displayed" */
2229 MAKE(t->old, term_win);
2231 /* Initialize "displayed" */
2232 term_win_init(t->old, w, h);
2234 /* Allocate "requested" */
2235 MAKE(t->scr, term_win);
2237 /* Initialize "requested" */
2238 term_win_init(t->scr, w, h);
2241 for (TERM_LEN y = 0; y < h; y++) {
2251 /* Force "total erase" */
2252 t->total_erase = TRUE;
2254 /* Default "blank" */
2256 t->char_blank = ' ';
2258 /* Prepare "fake" hooks to prevent core dumps */
2259 t->curs_hook = term_curs_hack;
2260 t->bigcurs_hook = term_bigcurs_hack;
2261 t->wipe_hook = term_wipe_hack;
2262 t->text_hook = term_text_hack;
2263 t->pict_hook = term_pict_hack;
2269 * Move to a location and, using an attr, add a string vertically
2271 errr term_putstr_v(TERM_LEN x, TERM_LEN y, int n, byte a, concptr s)
2276 for (int i = 0; i < n && s[i] != 0; i++) {
2278 if ((res = term_gotoxy(x, y0)) != 0)
2281 if (iskanji(s[i])) {
2282 if ((res = term_addstr(2, a, &s[i])) != 0)
2289 if ((res = term_addstr(1, a, &s[i])) != 0)
2300 errr term_nuke(term_type *t)
2302 TERM_LEN w = t->wid;
2303 TERM_LEN h = t->hgt;
2304 if (t->active_flag) {
2308 t->active_flag = FALSE;
2309 t->mapped_flag = FALSE;
2312 term_win_nuke(t->old, w, h);
2313 KILL(t->old, term_win);
2314 term_win_nuke(t->scr, w, h);
2315 KILL(t->scr, term_win);
2317 term_win_nuke(t->mem, w, h);
2318 KILL(t->mem, term_win);
2322 term_win_nuke(t->tmp, w, h);
2323 KILL(t->tmp, term_win);
2326 C_KILL(t->x1, h, TERM_LEN);
2327 C_KILL(t->x2, h, TERM_LEN);
2328 C_KILL(t->key_queue, t->key_size, char);