OSDN Git Service

[Fix] 属性への免疫を唱えた時のメッセージ
[hengbandforosx/hengbandosx.git] / src / main-gcu.cpp
1 /* File: main-gcu.c */
2
3 /*
4  * Copyright (c) 1997 Ben Harrison, and others
5  *
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.
9  */
10
11 /* Purpose: Allow use of Unix "curses" with Angband -BEN- */
12
13 /*
14  * This file has been modified to use multiple text windows if your screen
15  * is larger than 80x25.  By Keldon Jones (keldon@umr.edu).
16  *
17  * Also included is Keldon Jones patch to get better colors. To switch to
18  * a term that supports this, see this posting:
19  *
20  * From keldon@umr.edu Thu Apr 01 05:40:14 1999
21  * Sender: KELDON JONES <keldon@saucer.cc.umr.edu>
22  * From: Keldon Jones <keldon@umr.edu>
23  * Subject: Re: Linux colour prob (Or: question for Greg)
24  * Newsgroups: rec.games.roguelike.angband
25  * References: <slrn7g1jlp.gj9.scarblac-spamtrap@flits104-37.flits.rug.nl> <3700f96b.1593384@news.polsl.gliwice.pl> <slrn7g36er.fm4.wooledge@jekyll.local>
26  * X-Newsreader: TIN [UNIX 1.3 unoff BETA 970625; 9000/780 HP-UX B.10.20]
27  * NNTP-Posting-Host: saucer.cc.umr.edu
28  * X-NNTP-Posting-Host: saucer.cc.umr.edu
29  * Message-ID: <370306be.0@news.cc.umr.edu>
30  * Date: 1 Apr 99 05:40:14 GMT
31  * Organization: University of Missouri - Rolla
32  * Lines: 199
33  * Path:
34  * xs4all!xs4all!newsfeed.wirehub.nl!news-peer.gip.net!news.gsl.net!gip.net!news.he.net!mercury.cts.com!alpha.sky.net!news.missouri.edu!news.cc.umr.edu!not-for-mail
35  * Xref: xs4all rec.games.roguelike.angband:86332
36  *
37  * Greg Wooledge <wooledge@kellnet.com> wrote:
38  * > Gwidon S. Naskrent (naskrent@artemida.amu.edu.pl) wrote:
39  *
40  * > >On 30 Mar 1999 13:17:18 GMT, scarblac-spamtrap@pino.selwerd.cx (Remco
41  * > >Gerlich) wrote:
42  *
43  * > >>I recently switched to Linux, and *bands work fine. I like
44  * > >>to play them in consoles, not in X. However, colour is wrong.
45  * > >>"Slate" and "light slate" are always light blue, instead
46  * > >>of some shade of grey. Colours are fine in X.
47  *
48  * > I actually noticed the Linux console color issue a very long time ago,
49  * > but since I always play under X, I never really investigated it.
50  *
51  * > You're absolutely right, though -- the Linux console colors are not
52  * > "right" for Angband.
53  *
54  *    I've noticed this myself, so I spent the evening fixing it.
55  * Well, sorta fixing it.  It's not perfect yet, and it may not be
56  * possible to get it perfect with VGA hardware and/or the current
57  * Linux kernel.
58  *
59  * > OK, reading on in terminfo(5):
60  *
61  * >    Color Handling
62  * >        Most color terminals are either `Tektronix-like'  or  `HP-
63  * >        like'.   Tektronix-like terminals have a predefined set of
64  * >        N colors (where N usually 8), and can  set  character-cell
65  * >        foreground and background characters independently, mixing
66  * >        them into N * N color-pairs.  On  HP-like  terminals,  the
67  * >        use must set each color pair up separately (foreground and
68  * >        background are  not  independently  settable).   Up  to  M
69  * >        color-pairs  may  be  set  up  from  2*M different colors.
70  * >        ANSI-compatible terminals are Tektronix-like.
71  *
72  * > The "linux" terminfo entry is definitely in the "Tektronix-like" family.
73  * > It has the "setaf" and "setab" capabilities for setting the foreground
74  * > and background colors to one of 8 basically hard-coded values:
75  *
76  * >              Color       #define       Value       RGB
77  * >              black     COLOR_BLACK       0     0, 0, 0
78  * >              red       COLOR_RED         1     max,0,0
79  * >              green     COLOR_GREEN       2     0,max,0
80  * >              yellow    COLOR_YELLOW      3     max,max,0
81  * >              blue      COLOR_BLUE        4     0,0,max
82  * >              magenta   COLOR_MAGENTA     5     max,0,max
83  * >              cyan      COLOR_CYAN        6     0,max,max
84  * >              white     COLOR_WHITE       7     max,max,max
85  *
86  *    Well, not quite.  Using certain escape sequences, an
87  * application (or better yet, curses) can redefine the colors (at
88  * least some of them) and then those are used.  Read the
89  * curs_color manpage, and the part about "ccc" and "initc" in the
90  * terminfo manpage.  This is what the part of main-gcu inside the
91  * "if (can_fix_color)" code does.
92  *
93  * > So, what does this mean to the Angband player?  Well, it means that
94  * > either there's nothing you can do about the console colors as long as
95  * > straight curses/ncurses is used, or if there is something to be done,
96  * > I'm not clever enough to figure out how to do it.
97  *
98  *    Well, it is possible, though you have to patch main-gcu
99  * and edit a terminfo entry.  Apparently the relevant code in
100  * main-gcu was never tested (it's broken in at least one major
101  * way).  Apply the patch at the end of this message (notice that
102  * we need to define REDEFINE_COLORS at some point near the
103  * beginning of the file).
104  *    Next, write this termcap entry to a file:
105  *
106  * linux-c|linux console 1.3.6+ with private palette for each virtual console,
107  *         ccc,
108  *         colors#16, pairs#64,
109  *         initc=\E]P%x%p1%{16}%/%02x%p1%{16}%/%02x%p1%{16}%/%02x,
110  *         oc=\E]R,
111  *         use=linux,
112  *
113  * and run "tic" on it to produce a new terminfo entry called
114  * "linux-c".  Especially note the "ccc" flag which says that we
115  * can redefine colors.  The ugly "initc" string is what tells
116  * the console how to redefine a color.  Now, just set your TERM
117  * variable to "linux-c" and try Angband again.  If I've
118  * remembered to tell you everything that I've done, you should
119  * get the weird light-blue slate changed to a gray.
120  *    Now, there are still lots of problems with this.
121  * Something (I don't think it's curses, either the kernel or
122  * the hardware itself) seems to be ignoring my color changes to
123  * colors 6 and 7, which is annoying.  Also, the normal "white"
124  * color is now way too bright, but it's now necessary to
125  * distinguish it from the other grays.
126  *    The kernel seems to support 16 colors, but you can
127  * only switch to 8 of those, due to VT102 compatibility, it
128  * seems.  I think it would be possible to patch the kernel and
129  * allow all 16 colors to be used, but I haven't built up the
130  * nerve to try that yet.
131  *    Let me know if you can improve on this any.  Some of
132  * this may actually work differently on other hardware (ugh).
133  *
134  *    Keldon
135  *
136  */
137
138 /*
139  * To use this file, you must define "USE_GCU" in the Makefile.
140  *
141  * Hack -- note that "angband.h" is included AFTER the #ifdef test.
142  * This was necessary because of annoying "curses.h" silliness.
143  *
144  * Note that this file is not "intended" to support non-Unix machines,
145  * nor is it intended to support VMS or other bizarre setups.
146  *
147  * Also, this package assumes that the underlying "curses" handles both
148  * the "nonl()" and "cbreak()" commands correctly, see the "OPTION" below.
149  *
150  * This code should work with most versions of "curses" or "ncurses",
151  * and the "main-ncu.c" file (and USE_NCU define) are no longer used.
152  *
153  * See also "USE_CAP" and "main-cap.c" for code that bypasses "curses"
154  * and uses the "termcap" information directly, or even bypasses the
155  * "termcap" information and sends direct vt100 escape sequences.
156  *
157  * XXX XXX XXX Consider the use of "savetty()" and "resetty()".
158  */
159
160 #include "game-option/runtime-arguments.h"
161 #include "game-option/special-options.h"
162 #include "io/exit-panic.h"
163 #include "io/files-util.h"
164 #include "locale/japanese.h"
165 #include "main/sound-definitions-table.h"
166 #include "main/sound-of-music.h"
167 #include "system/angband-version.h"
168 #include "system/angband.h"
169 #include "system/player-type-definition.h"
170 #include "term/gameterm.h"
171 #include "term/term-color-types.h"
172 #include "term/z-form.h"
173 #include "util/angband-files.h"
174 #include "view/display-map.h"
175
176 #ifdef USE_GCU
177
178 /*
179  * Hack -- play games with "bool"
180  */
181 #if __STDC_VERSION__ < 199901L
182 #undef bool
183 #endif
184
185 /*
186  * Include the proper "header" file
187  */
188 #include <curses.h>
189
190 /**
191  * Simple rectangle type
192  */
193 struct rect_t {
194     int x, y;
195     int cx, cy;
196 };
197
198 /* Trivial rectangle utility to make code a bit more readable */
199 static rect_t rect(int x, int y, int cx, int cy)
200 {
201     rect_t r;
202     r.x = x;
203     r.y = y;
204     r.cx = cx;
205     r.cy = cy;
206     return r;
207 }
208
209 /**
210  * Information about a term
211  */
212 struct term_data {
213     term_type t;
214     rect_t r;
215     WINDOW *win;
216 };
217
218 /* Max number of windows on screen */
219 #define MAX_TERM_DATA 8
220
221 /* Information about our windows */
222 static term_data data[MAX_TERM_DATA];
223
224 /*
225  * Hack -- try to guess which systems use what commands
226  * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
227  * Mega-Hack -- try to guess when "POSIX" is available.
228  * If the user defines two of these, we will probably crash.
229  */
230 #if !defined(USE_TCHARS)
231 #if defined(_POSIX_VERSION)
232 #define USE_TPOSIX
233 #else
234 #define USE_TCHARS
235 #endif
236 #endif
237
238 /*
239  * Try redefining the colors at startup.
240  */
241 #define REDEFINE_COLORS
242
243 /*
244  * POSIX stuff
245  */
246 #ifdef USE_TPOSIX
247 #include <sys/ioctl.h>
248 #include <termios.h>
249 #endif
250
251 /*
252  * One version needs this file
253  */
254 #ifdef USE_TERMIO
255 #include <sys/ioctl.h>
256 #include <termio.h>
257 #endif
258
259 /*
260  * The other needs this file
261  */
262 #ifdef USE_TCHARS
263 #include <sys/file.h>
264 #include <sys/ioctl.h>
265 #include <sys/param.h>
266 #include <sys/resource.h>
267 #include <sys/types.h>
268 #endif
269
270 #include <locale.h>
271
272 /*
273  * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
274  *
275  * They should both work due to the "(i != 1)" test below.
276  */
277 #ifndef O_NDELAY
278 #define O_NDELAY O_NONBLOCK
279 #endif
280
281 /*
282  * OPTION: some machines lack "cbreak()"
283  * On these machines, we use an older definition
284  */
285 /* #define cbreak() crmode() */
286
287 /*
288  * OPTION: some machines cannot handle "nonl()" and "nl()"
289  * On these machines, we can simply ignore those commands.
290  */
291 /* #define nonl() */
292 /* #define nl() */
293
294 static std::filesystem::path ANGBAND_DIR_XTRA_SOUND;
295
296 /*
297  * todo 有効活用されていない疑惑
298  * Flag set once "sound" has been initialized
299  */
300 static bool can_use_sound = false;
301
302 /*
303  * An array of sound file names
304  */
305 static concptr sound_file[SOUND_MAX];
306
307 /*
308  * Save the "normal" and "angband" terminal settings
309  */
310
311 #ifdef USE_TPOSIX
312
313 static struct termios norm_termios;
314
315 static struct termios game_termios;
316
317 #endif
318
319 #ifdef USE_TERMIO
320
321 static struct termio norm_termio;
322
323 static struct termio game_termio;
324
325 #endif
326
327 #ifdef USE_TCHARS
328 static struct ltchars norm_speciax_chars;
329 static struct sgttyb norm_ttyb;
330 static struct tchars norm_tchars;
331 static int norm_locax_chars;
332
333 static struct ltchars game_speciax_chars;
334 static struct sgttyb game_ttyb;
335 static struct tchars game_tchars;
336 static int game_locax_chars;
337 #endif
338
339 /*
340  * Hack -- Number of initialized "term" structures
341  */
342 static int active = 0;
343
344 #ifdef A_COLOR
345 /*
346  * Hack -- define "A_BRIGHT" to be "A_BOLD", because on many
347  * machines, "A_BRIGHT" produces ugly "inverse" video.
348  */
349 #ifndef A_BRIGHT
350 #define A_BRIGHT A_BOLD
351 #endif
352
353 /*
354  * Software flag -- we are allowed to use color
355  */
356 static int can_use_color = false;
357
358 /*
359  * Software flag -- we are allowed to change the colors
360  */
361 static int can_fix_color = false;
362
363 /*
364  * Simple Angband to Curses color conversion table
365  */
366 static int colortable[16];
367
368 /**
369  * Background color we should draw with; either BLACK or DEFAULT
370  */
371 static int bg_color = COLOR_BLACK;
372
373 #endif
374
375 /*
376  * Place the "keymap" into its "normal" state
377  */
378 static void keymap_norm(void)
379 {
380 #ifdef USE_TPOSIX
381     /* restore the saved values of the special chars */
382     (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
383 #endif
384
385 #ifdef USE_TERMIO
386     /* restore the saved values of the special chars */
387     (void)ioctl(0, TCSETA, (char *)&norm_termio);
388 #endif
389
390 #ifdef USE_TCHARS
391     /* restore the saved values of the special chars */
392     (void)ioctl(0, TIOCSLTC, (char *)&norm_speciax_chars);
393     (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
394     (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
395     (void)ioctl(0, TIOCLSET, (char *)&norm_locax_chars);
396 #endif
397 }
398
399 /*
400  * Place the "keymap" into the "game" state
401  */
402 static void keymap_game(void)
403 {
404 #ifdef USE_TPOSIX
405     /* restore the saved values of the special chars */
406     (void)tcsetattr(0, TCSAFLUSH, &game_termios);
407 #endif
408
409 #ifdef USE_TERMIO
410     /* restore the saved values of the special chars */
411     (void)ioctl(0, TCSETA, (char *)&game_termio);
412 #endif
413
414 #ifdef USE_TCHARS
415     /* restore the saved values of the special chars */
416     (void)ioctl(0, TIOCSLTC, (char *)&game_speciax_chars);
417     (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
418     (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
419     (void)ioctl(0, TIOCLSET, (char *)&game_locax_chars);
420 #endif
421 }
422
423 /*
424  * Save the normal keymap
425  */
426 static void keymap_norm_prepare(void)
427 {
428 #ifdef USE_TPOSIX
429     /* Get the normal keymap */
430     tcgetattr(0, &norm_termios);
431 #endif
432
433 #ifdef USE_TERMIO
434     /* Get the normal keymap */
435     (void)ioctl(0, TCGETA, (char *)&norm_termio);
436 #endif
437
438 #ifdef USE_TCHARS
439     /* Get the normal keymap */
440     (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
441     (void)ioctl(0, TIOCGLTC, (char *)&norm_speciax_chars);
442     (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
443     (void)ioctl(0, TIOCLGET, (char *)&norm_locax_chars);
444 #endif
445 }
446
447 /*
448  * Save the keymaps (normal and game)
449  */
450 static void keymap_game_prepare(void)
451 {
452 #ifdef USE_TPOSIX
453     /* Acquire the current mapping */
454     tcgetattr(0, &game_termios);
455
456     /* Force "Ctrl-C" to interupt */
457     game_termios.c_cc[VINTR] = (char)3;
458
459     /* Force "Ctrl-Z" to suspend */
460     game_termios.c_cc[VSUSP] = (char)26;
461
462     /* Hack -- Leave "VSTART/VSTOP" alone */
463
464     /* Disable the standard control characters */
465     game_termios.c_cc[VQUIT] = (char)-1;
466     game_termios.c_cc[VERASE] = (char)-1;
467     game_termios.c_cc[VKILL] = (char)-1;
468     game_termios.c_cc[VEOF] = (char)-1;
469     game_termios.c_cc[VEOL] = (char)-1;
470
471     /* Normally, block until a character is read */
472     game_termios.c_cc[VMIN] = 1;
473     game_termios.c_cc[VTIME] = 0;
474 #endif
475
476 #ifdef USE_TERMIO
477     /* Acquire the current mapping */
478     (void)ioctl(0, TCGETA, (char *)&game_termio);
479
480     /* Force "Ctrl-C" to interupt */
481     game_termio.c_cc[VINTR] = (char)3;
482
483     /* Force "Ctrl-Z" to suspend */
484     game_termio.c_cc[VSUSP] = (char)26;
485
486     /* Disable the standard control characters */
487     game_termio.c_cc[VQUIT] = (char)-1;
488     game_termio.c_cc[VERASE] = (char)-1;
489     game_termio.c_cc[VKILL] = (char)-1;
490     game_termio.c_cc[VEOF] = (char)-1;
491     game_termio.c_cc[VEOL] = (char)-1;
492
493     /* Normally, block until a character is read */
494     game_termio.c_cc[VMIN] = 1;
495     game_termio.c_cc[VTIME] = 0;
496 #endif
497
498 #ifdef USE_TCHARS
499     /* Get the default game characters */
500     (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
501     (void)ioctl(0, TIOCGLTC, (char *)&game_speciax_chars);
502     (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
503     (void)ioctl(0, TIOCLGET, (char *)&game_locax_chars);
504
505     /* Force suspend (^Z) */
506     game_speciax_chars.t_suspc = (char)26;
507
508     /* Cancel some things */
509     game_speciax_chars.t_dsuspc = (char)-1;
510     game_speciax_chars.t_rprntc = (char)-1;
511     game_speciax_chars.t_flushc = (char)-1;
512     game_speciax_chars.t_werasc = (char)-1;
513     game_speciax_chars.t_lnextc = (char)-1;
514
515     /* Force interupt (^C) */
516     game_tchars.t_intrc = (char)3;
517
518     /* Force start/stop (^Q, ^S) */
519     game_tchars.t_startc = (char)17;
520     game_tchars.t_stopc = (char)19;
521
522     /* Cancel some things */
523     game_tchars.t_quitc = (char)-1;
524     game_tchars.t_eofc = (char)-1;
525     game_tchars.t_brkc = (char)-1;
526 #endif
527 }
528
529 /*
530  * Suspend/Resume
531  */
532 static errr game_term_xtra_gcu_alive(int v)
533 {
534     if (!v) {
535         /* Go to normal keymap mode */
536         keymap_norm();
537
538         /* Restore modes */
539         nocbreak();
540         echo();
541         nl();
542
543         /* Hack -- make sure the cursor is visible */
544         term_xtra(TERM_XTRA_SHAPE, 1);
545
546         /* Flush the curses buffer */
547         (void)refresh();
548
549         /* this moves curses to bottom right corner */
550         mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
551
552         /* Exit curses */
553         endwin();
554
555         /* Flush the output */
556         (void)fflush(stdout);
557     } else {
558         /* Restore the settings */
559         cbreak();
560         noecho();
561         nonl();
562
563         /* Go to angband keymap mode */
564         keymap_game();
565     }
566
567     return 0;
568 }
569
570 /*
571  * Check for existance of a file
572  */
573 static bool check_file(concptr s)
574 {
575     FILE *fff;
576     fff = fopen(s, "r");
577     if (!fff) {
578         return false;
579     }
580
581     fclose(fff);
582     return true;
583 }
584
585 /*
586  * Initialize sound
587  */
588 static bool init_sound(void)
589 {
590     if (can_use_sound) {
591         return can_use_sound;
592     }
593
594     for (auto i = 1; i < SOUND_MAX; i++) {
595         std::string wav = angband_sound_name[i];
596         wav.append(".wav");
597         const auto &path = path_build(ANGBAND_DIR_XTRA_SOUND, wav);
598         const auto &filename = path.string();
599         if (check_file(filename.data())) {
600             sound_file[i] = string_make(filename.data());
601         }
602     }
603
604     /* Sound available */
605     can_use_sound = true;
606     return can_use_sound;
607 }
608
609 /*
610  * Init the "curses" system
611  */
612 static void game_term_init_gcu(term_type *t)
613 {
614     term_data *td = (term_data *)(t->data);
615
616     /* Count init's, handle first */
617     if (active++ != 0) {
618         return;
619     }
620
621     /* Erase the screen */
622     (void)wclear(td->win);
623
624     /* Reset the cursor */
625     (void)wmove(td->win, 0, 0);
626
627     /* Flush changes */
628     (void)wrefresh(td->win);
629
630     /* Game keymap */
631     keymap_game();
632 }
633
634 /*
635  * Nuke the "curses" system
636  */
637 static void game_term_nuke_gcu(term_type *t)
638 {
639     term_data *td = (term_data *)(t->data);
640
641     /* Delete this window */
642     delwin(td->win);
643
644     /* Count nuke's, handle last */
645     if (--active != 0) {
646         return;
647     }
648
649     /* Hack -- make sure the cursor is visible */
650     term_xtra(TERM_XTRA_SHAPE, 1);
651
652 #ifdef A_COLOR
653     /* Reset colors to defaults */
654     start_color();
655 #endif
656
657     /* This moves curses to bottom right corner */
658     mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
659
660     /* Flush the curses buffer */
661     (void)refresh();
662
663     /* Exit curses */
664     endwin();
665
666     /* Flush the output */
667     (void)fflush(stdout);
668
669     /* Normal keymap */
670     keymap_norm();
671 }
672
673 /*
674  * Push multiple keys reversal
675  */
676 static void term_string_push(char *buf)
677 {
678     int i, l = strlen(buf);
679     for (i = l; i >= 0; i--) {
680         term_key_push(buf[i]);
681     }
682 }
683
684 #ifdef USE_GETCH
685
686 /*
687  * Process events, with optional wait
688  */
689 static errr game_term_xtra_gcu_event(int v)
690 {
691     int i, k;
692
693     /* Wait */
694     if (v) {
695         char buf[256];
696         char *bp = buf;
697
698         /* Paranoia -- Wait for it */
699         nodelay(stdscr, false);
700
701         /* Get a keypress */
702         i = getch();
703
704         /* Broken input is special */
705         if (i == ERR) {
706             exit_game_panic(p_ptr);
707         }
708         if (i == EOF) {
709             exit_game_panic(p_ptr);
710         }
711
712         *bp++ = (char)i;
713
714         /* Do not wait for it */
715         nodelay(stdscr, true);
716
717         while ((i = getch()) != EOF) {
718             if (i == ERR) {
719                 exit_game_panic(p_ptr);
720             }
721             *bp++ = (char)i;
722             if (bp == &buf[255]) {
723                 break;
724             }
725         }
726
727         /* Wait for it next time */
728         nodelay(stdscr, false);
729
730         *bp = '\0';
731 #ifdef JP
732         char eucbuf[sizeof(buf)];
733         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
734         if (utf8_to_euc(buf, strlen(buf) + 1, eucbuf, sizeof(eucbuf)) < 0) {
735             return -1;
736         }
737 #endif
738         term_string_push(_(eucbuf, buf));
739     }
740
741     /* Do not wait */
742     else {
743         /* Do not wait for it */
744         nodelay(stdscr, true);
745
746         /* Check for keypresses */
747         i = getch();
748
749         /* Wait for it next time */
750         nodelay(stdscr, false);
751
752         /* None ready */
753         if (i == ERR) {
754             return 1;
755         }
756         if (i == EOF) {
757             return 1;
758         }
759
760         /* Enqueue the keypress */
761         term_key_push(i);
762     }
763
764     /* Success */
765     return 0;
766 }
767
768 #else /* USE_GETCH */
769
770 /*
771  * Process events (with optional wait)
772  */
773 static errr game_term_xtra_gcu_event(int v)
774 {
775     int i, k;
776
777     char buf[256];
778
779     /* Wait */
780     if (v) {
781         char *bp = buf;
782
783         /* Wait for one byte */
784         i = read(0, bp++, 1);
785
786         /* Hack -- Handle bizarre "errors" */
787         if ((i <= 0) && (errno != EINTR)) {
788             exit_game_panic(p_ptr);
789         }
790
791         /* Get the current flags for stdin */
792         k = fcntl(0, F_GETFL, 0);
793
794         /* Oops */
795         if (k < 0) {
796             return 1;
797         }
798
799         /* Tell stdin not to block */
800         if (fcntl(0, F_SETFL, k | O_NDELAY) >= 0) {
801             if ((i = read(0, bp, 254)) > 0) {
802                 bp += i;
803             }
804
805             /* Replace the flags for stdin */
806             if (fcntl(0, F_SETFL, k)) {
807                 return 1;
808             }
809         }
810
811         bp[0] = '\0';
812 #ifdef JP
813         char eucbuf[sizeof(buf)];
814         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
815         if (utf8_to_euc(buf, strlen(buf) + 1, eucbuf, sizeof(eucbuf)) < 0) {
816             return -1;
817         }
818 #endif
819         term_string_push(_(eucbuf, buf));
820     }
821
822     /* Do not wait */
823     else {
824         /* Get the current flags for stdin */
825         k = fcntl(0, F_GETFL, 0);
826
827         /* Oops */
828         if (k < 0) {
829             return 1;
830         }
831
832         /* Tell stdin not to block */
833         if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) {
834             return 1;
835         }
836
837         /* Read one byte, if possible */
838         i = read(0, buf, 1);
839
840         /* Replace the flags for stdin */
841         if (fcntl(0, F_SETFL, k)) {
842             return 1;
843         }
844
845         /* Ignore "invalid" keys */
846         if ((i != 1) || (!buf[0])) {
847             return 1;
848         }
849
850         /* Enqueue the keypress */
851         term_key_push(buf[0]);
852     }
853
854     /* Success */
855     return 0;
856 }
857
858 #endif /* USE_GETCH */
859
860 /*
861  * Hack -- make a sound
862  */
863 static errr game_term_xtra_gcu_sound(int v)
864 {
865     /* Sound disabled */
866     if (!use_sound) {
867         return 1;
868     }
869
870     /* Illegal sound */
871     if ((v < 0) || (v >= SOUND_MAX)) {
872         return 1;
873     }
874
875     /* Unknown sound */
876     if (!sound_file[v]) {
877         return 1;
878     }
879
880     std::string buf = "./gcusound.sh ";
881     buf.append(sound_file[v]).append("\n");
882     return system(buf.data()) < 0;
883 }
884
885 static int scale_color(int i, int j, int scale)
886 {
887     return (angband_color_table[i][j] * (scale - 1) + 127) / 255;
888 }
889
890 static int create_color(int i, int scale)
891 {
892     int r = scale_color(i, 1, scale);
893     int g = scale_color(i, 2, scale);
894     int b = scale_color(i, 3, scale);
895     int rgb = 16 + scale * scale * r + scale * g + b;
896     /* In the case of white and black we need to use the ANSI colors */
897     if (r == g && g == b) {
898         if (b == 0) {
899             rgb = 0;
900         }
901         if (b == scale) {
902             rgb = 15;
903         }
904     }
905     return rgb;
906 }
907
908 /*
909  * React to changes
910  */
911 static errr game_term_xtra_gcu_react(void)
912 {
913
914 #ifdef A_COLOR
915
916     if (!can_change_color()) {
917         if (COLORS == 256 || COLORS == 88) {
918             /* If we have more than 16 colors, find the best matches. These numbers
919              * correspond to xterm/rxvt's builtin color numbers--they do not
920              * correspond to curses' constants OR with curses' color pairs.
921              *
922              * XTerm has 216 (6*6*6) RGB colors, with each RGB setting 0-5.
923              * RXVT has 64 (4*4*4) RGB colors, with each RGB setting 0-3.
924              *
925              * Both also have the basic 16 ANSI colors, plus some extra grayscale
926              * colors which we do not use.
927              */
928             int scale = COLORS == 256 ? 6 : 4;
929             for (int i = 0; i < 16; i++) {
930                 int fg = create_color(i, scale);
931                 init_pair(i + 1, fg, bg_color);
932                 colortable[i] = COLOR_PAIR(i + 1) | A_NORMAL;
933             }
934         }
935     } else {
936         for (int i = 0; i < 16; ++i) {
937             init_color(i,
938                 (angband_color_table[i][1] * 1000) / 255,
939                 (angband_color_table[i][2] * 1000) / 255,
940                 (angband_color_table[i][3] * 1000) / 255);
941         }
942     }
943
944 #endif
945
946     /* Success */
947     return 0;
948 }
949
950 /*
951  * Handle a "special request"
952  */
953 static errr game_term_xtra_gcu(int n, int v)
954 {
955     term_data *td = (term_data *)(game_term->data);
956
957     /* Analyze the request */
958     switch (n) {
959     /* Clear screen */
960     case TERM_XTRA_CLEAR:
961         touchwin(td->win);
962         (void)werase(td->win);
963         return 0;
964
965     /* Make a noise */
966     case TERM_XTRA_NOISE:
967         return write(1, "\007", 1) != 1;
968
969     /* Make a special sound */
970     case TERM_XTRA_SOUND:
971         return game_term_xtra_gcu_sound(v);
972
973     /* Flush the Curses buffer */
974     case TERM_XTRA_FRESH:
975         (void)wrefresh(td->win);
976         return 0;
977
978     /* Change the cursor visibility */
979     case TERM_XTRA_SHAPE:
980         curs_set(v);
981         return 0;
982
983     /* Suspend/Resume curses */
984     case TERM_XTRA_ALIVE:
985         return game_term_xtra_gcu_alive(v);
986
987     /* Process events */
988     case TERM_XTRA_EVENT:
989         return game_term_xtra_gcu_event(v);
990
991     /* Flush events */
992     case TERM_XTRA_FLUSH:
993         while (!game_term_xtra_gcu_event(false)) {
994             ;
995         }
996         return 0;
997
998     /* Delay */
999     case TERM_XTRA_DELAY:
1000         usleep(1000 * v);
1001         return 0;
1002
1003     /* React to events */
1004     case TERM_XTRA_REACT:
1005         game_term_xtra_gcu_react();
1006         return 0;
1007     }
1008
1009     /* Unknown */
1010     return 1;
1011 }
1012
1013 /*
1014  * Actually MOVE the hardware cursor
1015  */
1016 static errr game_term_curs_gcu(int x, int y)
1017 {
1018     term_data *td = (term_data *)(game_term->data);
1019
1020     /* Literally move the cursor */
1021     wmove(td->win, y, x);
1022
1023     /* Success */
1024     return 0;
1025 }
1026
1027 /*
1028  * Erase a grid of space
1029  * Hack -- try to be "semi-efficient".
1030  */
1031 static errr game_term_wipe_gcu(int x, int y, int n)
1032 {
1033     term_data *td = (term_data *)(game_term->data);
1034
1035     /* Place cursor */
1036     wmove(td->win, y, x);
1037
1038     /* Clear to end of line */
1039     if (x + n >= td->t.wid) {
1040         wclrtoeol(td->win);
1041     }
1042
1043     /* Clear some characters */
1044     else {
1045         while (n-- > 0) {
1046             waddch(td->win, ' ');
1047         }
1048     }
1049
1050     /* Success */
1051     return 0;
1052 }
1053
1054 #ifdef USE_NCURSES_ACS
1055 /*
1056  * this function draws some ACS characters on the screen
1057  * for DOS-based users: these are the graphical chars (blocks, lines etc)
1058  *
1059  * unix-gurus: before you start adding other attributes like A_REVERSE
1060  * think hard about how map_info() in cave.c should handle the color
1061  * of something that we here draw in reverse. It's not so simple, alas.
1062  */
1063 static void game_term_acs_text_gcu(int x, int y, int n, byte a, concptr s)
1064 {
1065     term_data *td = (term_data *)(game_term->data);
1066     int i;
1067
1068     /* position the cursor */
1069     wmove(td->win, y, x);
1070
1071 #ifdef A_COLOR
1072     /* Set the color */
1073     wattrset(td->win, colortable[a & 0x0F]);
1074 #endif
1075
1076     for (i = 0; i < n; i++) {
1077         /* add acs_map of a */
1078         waddch(td->win, acs_map[(int)s[i]]);
1079     }
1080     wattrset(td->win, WA_NORMAL);
1081 }
1082 #endif
1083
1084 /*
1085  * Place some text on the screen using an attribute
1086  */
1087 static errr game_term_text_gcu(int x, int y, int n, byte a, concptr s)
1088 {
1089     term_data *td = (term_data *)(game_term->data);
1090
1091 #ifdef USE_NCURSES_ACS
1092     /* do we have colors + 16 ? */
1093     /* then call special routine for drawing special characters */
1094     if (a & 0x10) {
1095         game_term_acs_text_gcu(x, y, n, a, s);
1096         return 0;
1097     }
1098 #endif
1099
1100     /* Move the cursor and dump the string */
1101     wmove(td->win, y, x);
1102
1103 #ifdef A_COLOR
1104     /* Set the color */
1105     if (can_use_color) {
1106         wattrset(td->win, colortable[a & 0x0F]);
1107     }
1108 #endif
1109
1110 #ifdef JP
1111     char text[1024];
1112     int text_len = euc_to_utf8(s, n, text, sizeof(text));
1113     if (text_len < 0) {
1114         return -1;
1115     }
1116 #endif
1117     /* Add the text */
1118     waddnstr(td->win, _(text, s), _(text_len, n));
1119
1120     /* Success */
1121     return 0;
1122 }
1123
1124 /**
1125  * Create a window for the given "term_data" argument.
1126  *
1127  * Assumes legal arguments.
1128  */
1129 static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x)
1130 {
1131     term_type *t = &td->t;
1132
1133     /* Make sure the window has a positive size */
1134     if (rows <= 0 || cols <= 0) {
1135         return 0;
1136     }
1137
1138     /* Create a window */
1139     td->win = newwin(rows, cols, y, x);
1140
1141     /* Make sure we succeed */
1142     if (!td->win) {
1143         plog("Failed to setup curses window.");
1144         return -1;
1145     }
1146
1147     /* Initialize the term */
1148     term_init(t, cols, rows, 256);
1149
1150     /* Avoid the bottom right corner */
1151     t->icky_corner = true;
1152
1153     /* Erase with "white space" */
1154     t->attr_blank = TERM_WHITE;
1155     t->char_blank = ' ';
1156
1157     /* Set some hooks */
1158     t->init_hook = game_term_init_gcu;
1159     t->nuke_hook = game_term_nuke_gcu;
1160
1161     /* Set some more hooks */
1162     t->text_hook = game_term_text_gcu;
1163     t->wipe_hook = game_term_wipe_gcu;
1164     t->curs_hook = game_term_curs_gcu;
1165     t->xtra_hook = game_term_xtra_gcu;
1166
1167     /* Save the data */
1168     t->data = td;
1169
1170     /* Activate it */
1171     term_activate(t);
1172
1173     /* Success */
1174     return 0;
1175 }
1176
1177 /**
1178  * Simple helper
1179  */
1180 static errr term_data_init(term_data *td)
1181 {
1182     return term_data_init_gcu(td, td->r.cy, td->r.cx, td->r.y, td->r.x);
1183 }
1184
1185 /* Parse 27,15,*x30 up to the 'x'. * gets converted to a big number
1186    Parse 32,* until the end. Return count of numbers parsed */
1187 static int _parse_size_list(const char *arg, int sizes[], int max)
1188 {
1189     int i = 0;
1190     const char *start = arg;
1191     const char *stop = arg;
1192
1193     for (;;) {
1194         if (!*stop || !isdigit(*stop)) {
1195             if (i >= max) {
1196                 break;
1197             }
1198             if (*start == '*') {
1199                 sizes[i] = 255;
1200             } else {
1201                 /* rely on atoi("23,34,*") -> 23
1202                    otherwise, copy [start, stop) into a new buffer first.*/
1203                 sizes[i] = atoi(start);
1204             }
1205             i++;
1206             if (!*stop || *stop != ',') {
1207                 break;
1208             }
1209
1210             stop++;
1211             start = stop;
1212         } else {
1213             stop++;
1214         }
1215     }
1216     return i;
1217 }
1218
1219 static void hook_quit(concptr str)
1220 {
1221     /* Unused */
1222     (void)str;
1223
1224     /* Exit curses */
1225     endwin();
1226 }
1227
1228 /*
1229  * Prepare "curses" for use by the file "term.c"
1230  *
1231  * Installs the "hook" functions defined above, and then activates
1232  * the main screen "term", which clears the screen and such things.
1233  *
1234  * Someone should really check the semantics of "initscr()"
1235  */
1236 errr init_gcu(int argc, char *argv[])
1237 {
1238     int num_term = 4, next_win = 0;
1239
1240     /* Unused */
1241     (void)argc;
1242     (void)argv;
1243
1244     setlocale(LC_ALL, "");
1245
1246     ANGBAND_DIR_XTRA_SOUND = path_build(ANGBAND_DIR_XTRA, "sound");
1247     keymap_norm_prepare();
1248     auto nobigscreen = false;
1249     for (auto i = 1; i < argc; i++) {
1250         if (prefix(argv[i], "-o")) {
1251             nobigscreen = true;
1252         }
1253     }
1254
1255     if (initscr() == (WINDOW *)ERR) {
1256         return -1;
1257     }
1258
1259     quit_aux = hook_quit;
1260     core_aux = hook_quit;
1261     if ((LINES < MAIN_TERM_MIN_ROWS) || (COLS < MAIN_TERM_MIN_COLS)) {
1262         quit_fmt("%s needs an %dx%d 'curses' screen", std::string(VARIANT_NAME).data(), MAIN_TERM_MIN_COLS, MAIN_TERM_MIN_ROWS);
1263     }
1264
1265 #ifdef A_COLOR
1266
1267     /*** Init the Color-pairs and set up a translation table ***/
1268
1269     /* Do we have color, and enough color, available? */
1270     can_use_color = ((start_color() != ERR) && has_colors() && (COLORS >= 8) && (COLOR_PAIRS >= 8));
1271
1272 #ifdef REDEFINE_COLORS
1273     /* Can we change colors? */
1274     can_fix_color = (can_use_color && can_change_color() && (COLORS >= 16) && (COLOR_PAIRS > 8));
1275 #endif
1276
1277     /* Attempt to use customized colors */
1278     if (can_fix_color) {
1279         /* Prepare the color pairs */
1280         for (auto i = 1; i <= 15; i++) {
1281             if (init_pair(i, i, 0) == ERR) {
1282                 quit("Color pair init failed");
1283             }
1284
1285             colortable[i] = COLOR_PAIR(i);
1286             game_term_xtra_gcu_react();
1287         }
1288     }
1289     /* Attempt to use colors */
1290     else if (can_use_color) {
1291         /* Color-pair 0 is *always* WHITE on BLACK */
1292
1293         /* Prepare the color pairs */
1294         init_pair(1, COLOR_RED, COLOR_BLACK);
1295         init_pair(2, COLOR_GREEN, COLOR_BLACK);
1296         init_pair(3, COLOR_YELLOW, COLOR_BLACK);
1297         init_pair(4, COLOR_BLUE, COLOR_BLACK);
1298         init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
1299         init_pair(6, COLOR_CYAN, COLOR_BLACK);
1300         init_pair(7, COLOR_BLACK, COLOR_BLACK);
1301
1302         /* Prepare the "Angband Colors" -- Bright white is too bright */
1303         /* Changed in Drangband. Cyan as grey sucks -- -TM- */
1304         colortable[0] = (COLOR_PAIR(7) | A_NORMAL); /* Black */
1305         colortable[1] = (COLOR_PAIR(0) | A_BRIGHT); /* White */
1306         colortable[2] = (COLOR_PAIR(0) | A_NORMAL); /* Grey XXX */
1307         colortable[3] = (COLOR_PAIR(1) | A_BRIGHT); /* Orange XXX */
1308         colortable[4] = (COLOR_PAIR(1) | A_NORMAL); /* Red */
1309         colortable[5] = (COLOR_PAIR(2) | A_NORMAL); /* Green */
1310         colortable[6] = (COLOR_PAIR(4) | A_BRIGHT); /* Blue */
1311         colortable[7] = (COLOR_PAIR(3) | A_NORMAL); /* Umber */
1312         colortable[8] = (COLOR_PAIR(7) | A_BRIGHT); /* Dark-grey XXX */
1313         colortable[9] = (COLOR_PAIR(0) | A_NORMAL); /* Light-grey XXX */
1314         colortable[10] = (COLOR_PAIR(5) | A_BRIGHT); /* Purple */
1315         colortable[11] = (COLOR_PAIR(3) | A_BRIGHT); /* Yellow */
1316         colortable[12] = (COLOR_PAIR(5) | A_NORMAL); /* Light Red XXX */
1317         colortable[13] = (COLOR_PAIR(2) | A_BRIGHT); /* Light Green */
1318         colortable[14] = (COLOR_PAIR(6) | A_BRIGHT); /* Light Blue */
1319         colortable[15] = (COLOR_PAIR(3) | A_NORMAL); /* Light Umber XXX */
1320     }
1321
1322 #endif
1323
1324     /* Handle "arg_sound" */
1325     if (use_sound != arg_sound) {
1326         /* Initialize (if needed) */
1327         if (arg_sound && !init_sound()) {
1328             /* Warning */
1329             plog("Cannot initialize sound!");
1330
1331             /* Cannot enable */
1332             arg_sound = false;
1333         }
1334
1335         /* Change setting */
1336         use_sound = arg_sound;
1337     }
1338
1339     /* Try graphics */
1340     if (arg_graphics) {
1341         /* if USE_NCURSES_ACS is defined, we can do something with graphics in curses! */
1342 #ifdef USE_NCURSES_ACS
1343         use_graphics = true;
1344 #endif
1345     }
1346
1347     /*** Low level preparation ***/
1348
1349 #ifdef USE_GETCH
1350
1351     /* Paranoia -- Assume no waiting */
1352     nodelay(stdscr, false);
1353
1354 #endif
1355
1356     /* Prepare */
1357     cbreak();
1358     noecho();
1359     nonl();
1360     raw();
1361
1362     /* Extract the game keymap */
1363     keymap_game_prepare();
1364
1365     /*** Now prepare the term(s) ***/
1366     if (nobigscreen) {
1367         /* Create several terms */
1368         for (auto i = 0; i < num_term; i++) {
1369             int rows, cols, y, x;
1370
1371             /* Decide on size and position */
1372             switch (i) {
1373             /* Upper left */
1374             case 0: {
1375                 rows = TERM_DEFAULT_ROWS;
1376                 cols = TERM_DEFAULT_COLS;
1377                 y = x = 0;
1378                 break;
1379             }
1380
1381             /* Lower left */
1382             case 1: {
1383                 rows = LINES - TERM_DEFAULT_ROWS - 1;
1384                 cols = TERM_DEFAULT_COLS;
1385                 y = TERM_DEFAULT_ROWS + 1;
1386                 x = 0;
1387                 break;
1388             }
1389
1390             /* Upper right */
1391             case 2: {
1392                 rows = TERM_DEFAULT_ROWS;
1393                 cols = COLS - TERM_DEFAULT_COLS - 1;
1394                 y = 0;
1395                 x = TERM_DEFAULT_COLS + 1;
1396                 break;
1397             }
1398
1399             /* Lower right */
1400             case 3: {
1401                 rows = LINES - TERM_DEFAULT_ROWS - 1;
1402                 cols = COLS - TERM_DEFAULT_COLS - 1;
1403                 y = TERM_DEFAULT_ROWS + 1;
1404                 x = TERM_DEFAULT_COLS + 1;
1405                 break;
1406             }
1407
1408             /* XXX */
1409             default: {
1410                 rows = cols = y = x = 0;
1411                 break;
1412             }
1413             }
1414
1415             /* Skip non-existant windows */
1416             if (rows <= 0 || cols <= 0) {
1417                 continue;
1418             }
1419
1420             /* Create a term */
1421             term_data_init_gcu(&data[next_win], rows, cols, y, x);
1422
1423             /* Remember the term */
1424             angband_terms[next_win] = &data[next_win].t;
1425
1426             /* One more window */
1427             next_win++;
1428         }
1429     } else
1430     /* Parse Args and Prepare the Terminals. Rectangles are specified
1431       as Width x Height, right? The game will allow you to have two
1432       strips of extra terminals, one on the right and one on the bottom.
1433       The map terminal will than fit in as big as possible in the remaining
1434       space.
1435       Examples:
1436         angband -mgcu -- -right 30x27,* -bottom *x7 will layout as
1437         Term-0: Map (COLS-30)x(LINES-7) | Term-1: 30x27
1438         --------------------------------|----------------------
1439         <----Term-3: (COLS-30)x7------->| Term-2: 30x(LINES-27)
1440         composband -mgcu -- -bottom *x7 -right 30x27,* will layout as
1441         Term-0: Map (COLS-30)x(LINES-7) | Term-2: 30x27
1442                                         |------------------------------
1443                                         | Term-3: 30x(LINES-27)
1444         ---------------------------------------------------------------
1445         <----------Term-1: (COLS)x7----------------------------------->
1446         Notice the effect on the bottom terminal by specifying its argument
1447         second or first. Notice the sequence numbers for the various terminals
1448         as you will have to blindly configure them in the window setup screen.
1449         EDIT: Added support for -left and -top.
1450     */
1451     {
1452         rect_t remaining = rect(0, 0, COLS, LINES);
1453         int spacer_cx = 1;
1454         int spacer_cy = 1;
1455         int next_term = 1;
1456         int term_ct = 1;
1457
1458         for (auto i = 1; i < argc; i++) {
1459             if (streq(argv[i], "-spacer")) {
1460                 i++;
1461                 if (i >= argc) {
1462                     quit("Missing size specifier for -spacer");
1463                 }
1464                 sscanf(argv[i], "%dx%d", &spacer_cx, &spacer_cy);
1465             } else if (streq(argv[i], "-right") || streq(argv[i], "-left")) {
1466                 const char *arg, *tmp;
1467                 bool left = streq(argv[i], "-left");
1468                 int cx, cys[MAX_TERM_DATA] = { 0 }, ct, j, x, y;
1469
1470                 i++;
1471                 if (i >= argc) {
1472                     quit_fmt("Missing size specifier for -%s", left ? "left" : "right");
1473                 }
1474
1475                 arg = argv[i];
1476                 tmp = strchr(arg, 'x');
1477                 if (!tmp) {
1478                     quit_fmt("Expected something like -%s 60x27,* for two %s hand terminals of 60 columns, the first 27 lines and the second whatever is left.", left ? "left" : "right", left ? "left" : "right");
1479                 }
1480                 cx = atoi(arg);
1481                 remaining.cx -= cx;
1482                 if (left) {
1483                     x = remaining.x;
1484                     y = remaining.y;
1485                     remaining.x += cx;
1486                 } else {
1487                     x = remaining.x + remaining.cx;
1488                     y = remaining.y;
1489                 }
1490                 remaining.cx -= spacer_cx;
1491                 if (left) {
1492                     remaining.x += spacer_cx;
1493                 }
1494
1495                 tmp++;
1496                 ct = _parse_size_list(tmp, cys, MAX_TERM_DATA);
1497                 for (j = 0; j < ct; j++) {
1498                     int cy = cys[j];
1499                     if (y + cy > remaining.y + remaining.cy) {
1500                         cy = remaining.y + remaining.cy - y;
1501                     }
1502                     if (next_term >= MAX_TERM_DATA) {
1503                         quit_fmt("Too many terminals. Only %d are allowed.", MAX_TERM_DATA);
1504                     }
1505                     if (cy <= 0) {
1506                         quit_fmt("Out of bounds in -%s: %d is too large (%d rows max for this strip)",
1507                             left ? "left" : "right", cys[j], remaining.cy);
1508                     }
1509                     data[next_term++].r = rect(x, y, cx, cy);
1510                     y += cy + spacer_cy;
1511                     term_ct++;
1512                 }
1513             } else if (streq(argv[i], "-top") || streq(argv[i], "-bottom")) {
1514                 const char *arg, *tmp;
1515                 bool top = streq(argv[i], "-top");
1516                 int cy, cxs[MAX_TERM_DATA] = { 0 }, ct, j, x, y;
1517
1518                 i++;
1519                 if (i >= argc) {
1520                     quit_fmt("Missing size specifier for -%s", top ? "top" : "bottom");
1521                 }
1522
1523                 arg = argv[i];
1524                 tmp = strchr(arg, 'x');
1525                 if (!tmp) {
1526                     quit_fmt("Expected something like -%s *x7 for a single %s terminal of 7 lines using as many columns as are available.", top ? "top" : "bottom", top ? "top" : "bottom");
1527                 }
1528                 tmp++;
1529                 cy = atoi(tmp);
1530                 ct = _parse_size_list(arg, cxs, MAX_TERM_DATA);
1531
1532                 remaining.cy -= cy;
1533                 if (top) {
1534                     x = remaining.x;
1535                     y = remaining.y;
1536                     remaining.y += cy;
1537                 } else {
1538                     x = remaining.x;
1539                     y = remaining.y + remaining.cy;
1540                 }
1541                 remaining.cy -= spacer_cy;
1542                 if (top) {
1543                     remaining.y += spacer_cy;
1544                 }
1545
1546                 tmp++;
1547                 for (j = 0; j < ct; j++) {
1548                     int cx = cxs[j];
1549                     if (x + cx > remaining.x + remaining.cx) {
1550                         cx = remaining.x + remaining.cx - x;
1551                     }
1552                     if (next_term >= MAX_TERM_DATA) {
1553                         quit_fmt("Too many terminals. Only %d are allowed.", MAX_TERM_DATA);
1554                     }
1555                     if (cx <= 0) {
1556                         quit_fmt("Out of bounds in -%s: %d is too large (%d cols max for this strip)",
1557                             top ? "top" : "bottom", cxs[j], remaining.cx);
1558                     }
1559                     data[next_term++].r = rect(x, y, cx, cy);
1560                     x += cx + spacer_cx;
1561                     term_ct++;
1562                 }
1563             }
1564         }
1565
1566         /* Map Terminal */
1567         if (remaining.cx < MAIN_TERM_MIN_COLS || remaining.cy < MAIN_TERM_MIN_ROWS) {
1568             quit_fmt("Failed: %s needs an %dx%d map screen, not %dx%d", std::string(VARIANT_NAME).data(), MAIN_TERM_MIN_COLS, MAIN_TERM_MIN_ROWS, remaining.cx, remaining.cy);
1569         }
1570         data[0].r = remaining;
1571         term_data_init(&data[0]);
1572         angband_terms[0] = game_term;
1573
1574         /* Child Terminals */
1575         for (next_term = 1; next_term < term_ct; next_term++) {
1576             term_data_init(&data[next_term]);
1577             angband_terms[next_term] = game_term;
1578         }
1579     }
1580
1581     /* Activate the "Angband" window screen */
1582     term_activate(&data[0].t);
1583
1584     /* Store */
1585     term_screen = &data[0].t;
1586
1587     /* Success */
1588     return 0;
1589 }
1590
1591 #endif /* USE_GCU */