OSDN Git Service

Merge pull request #2290 from habu1010/feature/clang-format-insert-braces
[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 4
220
221 /* Minimum main term size */
222 #define MIN_TERM0_LINES 24
223 #define MIN_TERM0_COLS 80
224
225 /* Information about our windows */
226 static term_data data[MAX_TERM_DATA];
227
228 /*
229  * Hack -- try to guess which systems use what commands
230  * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
231  * Mega-Hack -- try to guess when "POSIX" is available.
232  * If the user defines two of these, we will probably crash.
233  */
234 #if !defined(USE_TCHARS)
235 #if defined(_POSIX_VERSION)
236 #define USE_TPOSIX
237 #else
238 #define USE_TCHARS
239 #endif
240 #endif
241
242 /*
243  * Try redefining the colors at startup.
244  */
245 #define REDEFINE_COLORS
246
247 /*
248  * POSIX stuff
249  */
250 #ifdef USE_TPOSIX
251 #include <sys/ioctl.h>
252 #include <termios.h>
253 #endif
254
255 /*
256  * One version needs this file
257  */
258 #ifdef USE_TERMIO
259 #include <sys/ioctl.h>
260 #include <termio.h>
261 #endif
262
263 /*
264  * The other needs this file
265  */
266 #ifdef USE_TCHARS
267 #include <sys/file.h>
268 #include <sys/ioctl.h>
269 #include <sys/param.h>
270 #include <sys/resource.h>
271 #include <sys/types.h>
272 #endif
273
274 #include <locale.h>
275
276 /*
277  * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
278  *
279  * They should both work due to the "(i != 1)" test below.
280  */
281 #ifndef O_NDELAY
282 #define O_NDELAY O_NONBLOCK
283 #endif
284
285 /*
286  * OPTION: some machines lack "cbreak()"
287  * On these machines, we use an older definition
288  */
289 /* #define cbreak() crmode() */
290
291 /*
292  * OPTION: some machines cannot handle "nonl()" and "nl()"
293  * On these machines, we can simply ignore those commands.
294  */
295 /* #define nonl() */
296 /* #define nl() */
297
298 static concptr ANGBAND_DIR_XTRA_SOUND;
299
300 /*
301  * todo 有効活用されていない疑惑
302  * Flag set once "sound" has been initialized
303  */
304 static bool can_use_sound = false;
305
306 /*
307  * An array of sound file names
308  */
309 static concptr sound_file[SOUND_MAX];
310
311 /*
312  * Save the "normal" and "angband" terminal settings
313  */
314
315 #ifdef USE_TPOSIX
316
317 static struct termios norm_termios;
318
319 static struct termios game_termios;
320
321 #endif
322
323 #ifdef USE_TERMIO
324
325 static struct termio norm_termio;
326
327 static struct termio game_termio;
328
329 #endif
330
331 #ifdef USE_TCHARS
332 static struct ltchars norm_speciax_chars;
333 static struct sgttyb norm_ttyb;
334 static struct tchars norm_tchars;
335 static int norm_locax_chars;
336
337 static struct ltchars game_speciax_chars;
338 static struct sgttyb game_ttyb;
339 static struct tchars game_tchars;
340 static int game_locax_chars;
341 #endif
342
343 /*
344  * Hack -- Number of initialized "term" structures
345  */
346 static int active = 0;
347
348 #ifdef A_COLOR
349 /*
350  * Hack -- define "A_BRIGHT" to be "A_BOLD", because on many
351  * machines, "A_BRIGHT" produces ugly "inverse" video.
352  */
353 #ifndef A_BRIGHT
354 #define A_BRIGHT A_BOLD
355 #endif
356
357 /*
358  * Software flag -- we are allowed to use color
359  */
360 static int can_use_color = false;
361
362 /*
363  * Software flag -- we are allowed to change the colors
364  */
365 static int can_fix_color = false;
366
367 /*
368  * Simple Angband to Curses color conversion table
369  */
370 static int colortable[16];
371
372 /**
373  * Background color we should draw with; either BLACK or DEFAULT
374  */
375 static int bg_color = COLOR_BLACK;
376
377 #endif
378
379 /*
380  * Place the "keymap" into its "normal" state
381  */
382 static void keymap_norm(void)
383 {
384 #ifdef USE_TPOSIX
385     /* restore the saved values of the special chars */
386     (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
387 #endif
388
389 #ifdef USE_TERMIO
390     /* restore the saved values of the special chars */
391     (void)ioctl(0, TCSETA, (char *)&norm_termio);
392 #endif
393
394 #ifdef USE_TCHARS
395     /* restore the saved values of the special chars */
396     (void)ioctl(0, TIOCSLTC, (char *)&norm_speciax_chars);
397     (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
398     (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
399     (void)ioctl(0, TIOCLSET, (char *)&norm_locax_chars);
400 #endif
401 }
402
403 /*
404  * Place the "keymap" into the "game" state
405  */
406 static void keymap_game(void)
407 {
408 #ifdef USE_TPOSIX
409     /* restore the saved values of the special chars */
410     (void)tcsetattr(0, TCSAFLUSH, &game_termios);
411 #endif
412
413 #ifdef USE_TERMIO
414     /* restore the saved values of the special chars */
415     (void)ioctl(0, TCSETA, (char *)&game_termio);
416 #endif
417
418 #ifdef USE_TCHARS
419     /* restore the saved values of the special chars */
420     (void)ioctl(0, TIOCSLTC, (char *)&game_speciax_chars);
421     (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
422     (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
423     (void)ioctl(0, TIOCLSET, (char *)&game_locax_chars);
424 #endif
425 }
426
427 /*
428  * Save the normal keymap
429  */
430 static void keymap_norm_prepare(void)
431 {
432 #ifdef USE_TPOSIX
433     /* Get the normal keymap */
434     tcgetattr(0, &norm_termios);
435 #endif
436
437 #ifdef USE_TERMIO
438     /* Get the normal keymap */
439     (void)ioctl(0, TCGETA, (char *)&norm_termio);
440 #endif
441
442 #ifdef USE_TCHARS
443     /* Get the normal keymap */
444     (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
445     (void)ioctl(0, TIOCGLTC, (char *)&norm_speciax_chars);
446     (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
447     (void)ioctl(0, TIOCLGET, (char *)&norm_locax_chars);
448 #endif
449 }
450
451 /*
452  * Save the keymaps (normal and game)
453  */
454 static void keymap_game_prepare(void)
455 {
456 #ifdef USE_TPOSIX
457     /* Acquire the current mapping */
458     tcgetattr(0, &game_termios);
459
460     /* Force "Ctrl-C" to interupt */
461     game_termios.c_cc[VINTR] = (char)3;
462
463     /* Force "Ctrl-Z" to suspend */
464     game_termios.c_cc[VSUSP] = (char)26;
465
466     /* Hack -- Leave "VSTART/VSTOP" alone */
467
468     /* Disable the standard control characters */
469     game_termios.c_cc[VQUIT] = (char)-1;
470     game_termios.c_cc[VERASE] = (char)-1;
471     game_termios.c_cc[VKILL] = (char)-1;
472     game_termios.c_cc[VEOF] = (char)-1;
473     game_termios.c_cc[VEOL] = (char)-1;
474
475     /* Normally, block until a character is read */
476     game_termios.c_cc[VMIN] = 1;
477     game_termios.c_cc[VTIME] = 0;
478 #endif
479
480 #ifdef USE_TERMIO
481     /* Acquire the current mapping */
482     (void)ioctl(0, TCGETA, (char *)&game_termio);
483
484     /* Force "Ctrl-C" to interupt */
485     game_termio.c_cc[VINTR] = (char)3;
486
487     /* Force "Ctrl-Z" to suspend */
488     game_termio.c_cc[VSUSP] = (char)26;
489
490     /* Disable the standard control characters */
491     game_termio.c_cc[VQUIT] = (char)-1;
492     game_termio.c_cc[VERASE] = (char)-1;
493     game_termio.c_cc[VKILL] = (char)-1;
494     game_termio.c_cc[VEOF] = (char)-1;
495     game_termio.c_cc[VEOL] = (char)-1;
496
497     /* Normally, block until a character is read */
498     game_termio.c_cc[VMIN] = 1;
499     game_termio.c_cc[VTIME] = 0;
500 #endif
501
502 #ifdef USE_TCHARS
503     /* Get the default game characters */
504     (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
505     (void)ioctl(0, TIOCGLTC, (char *)&game_speciax_chars);
506     (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
507     (void)ioctl(0, TIOCLGET, (char *)&game_locax_chars);
508
509     /* Force suspend (^Z) */
510     game_speciax_chars.t_suspc = (char)26;
511
512     /* Cancel some things */
513     game_speciax_chars.t_dsuspc = (char)-1;
514     game_speciax_chars.t_rprntc = (char)-1;
515     game_speciax_chars.t_flushc = (char)-1;
516     game_speciax_chars.t_werasc = (char)-1;
517     game_speciax_chars.t_lnextc = (char)-1;
518
519     /* Force interupt (^C) */
520     game_tchars.t_intrc = (char)3;
521
522     /* Force start/stop (^Q, ^S) */
523     game_tchars.t_startc = (char)17;
524     game_tchars.t_stopc = (char)19;
525
526     /* Cancel some things */
527     game_tchars.t_quitc = (char)-1;
528     game_tchars.t_eofc = (char)-1;
529     game_tchars.t_brkc = (char)-1;
530 #endif
531 }
532
533 /*
534  * Suspend/Resume
535  */
536 static errr game_term_xtra_gcu_alive(int v)
537 {
538     if (!v) {
539         /* Go to normal keymap mode */
540         keymap_norm();
541
542         /* Restore modes */
543         nocbreak();
544         echo();
545         nl();
546
547         /* Hack -- make sure the cursor is visible */
548         term_xtra(TERM_XTRA_SHAPE, 1);
549
550         /* Flush the curses buffer */
551         (void)refresh();
552
553         /* this moves curses to bottom right corner */
554         mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
555
556         /* Exit curses */
557         endwin();
558
559         /* Flush the output */
560         (void)fflush(stdout);
561     } else {
562         /* Restore the settings */
563         cbreak();
564         noecho();
565         nonl();
566
567         /* Go to angband keymap mode */
568         keymap_game();
569     }
570
571     return 0;
572 }
573
574 /*
575  * Check for existance of a file
576  */
577 static bool check_file(concptr s)
578 {
579     FILE *fff;
580     fff = fopen(s, "r");
581     if (!fff) {
582         return false;
583     }
584
585     fclose(fff);
586     return true;
587 }
588
589 /*
590  * Initialize sound
591  */
592 static bool init_sound(void)
593 {
594     /* Initialize once */
595     if (can_use_sound) {
596         return can_use_sound;
597     }
598
599     int i;
600     char wav[128];
601     char buf[1024];
602
603     /* Prepare the sounds */
604     for (i = 1; i < SOUND_MAX; i++) {
605         /* Extract name of sound file */
606         sprintf(wav, "%s.wav", angband_sound_name[i]);
607
608         /* Access the sound */
609         path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_SOUND, wav);
610
611         /* Save the sound filename, if it exists */
612         if (check_file(buf)) {
613             sound_file[i] = string_make(buf);
614         }
615     }
616
617     /* Sound available */
618     can_use_sound = true;
619     return can_use_sound;
620 }
621
622 /*
623  * Init the "curses" system
624  */
625 static void game_term_init_gcu(term_type *t)
626 {
627     term_data *td = (term_data *)(t->data);
628
629     /* Count init's, handle first */
630     if (active++ != 0) {
631         return;
632     }
633
634     /* Erase the screen */
635     (void)wclear(td->win);
636
637     /* Reset the cursor */
638     (void)wmove(td->win, 0, 0);
639
640     /* Flush changes */
641     (void)wrefresh(td->win);
642
643     /* Game keymap */
644     keymap_game();
645 }
646
647 /*
648  * Nuke the "curses" system
649  */
650 static void game_term_nuke_gcu(term_type *t)
651 {
652     term_data *td = (term_data *)(t->data);
653
654     /* Delete this window */
655     delwin(td->win);
656
657     /* Count nuke's, handle last */
658     if (--active != 0) {
659         return;
660     }
661
662     /* Hack -- make sure the cursor is visible */
663     term_xtra(TERM_XTRA_SHAPE, 1);
664
665 #ifdef A_COLOR
666     /* Reset colors to defaults */
667     start_color();
668 #endif
669
670     /* This moves curses to bottom right corner */
671     mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
672
673     /* Flush the curses buffer */
674     (void)refresh();
675
676     /* Exit curses */
677     endwin();
678
679     /* Flush the output */
680     (void)fflush(stdout);
681
682     /* Normal keymap */
683     keymap_norm();
684 }
685
686 /*
687  * Push multiple keys reversal
688  */
689 static void term_string_push(char *buf)
690 {
691     int i, l = strlen(buf);
692     for (i = l; i >= 0; i--) {
693         term_key_push(buf[i]);
694     }
695 }
696
697 #ifdef USE_GETCH
698
699 /*
700  * Process events, with optional wait
701  */
702 static errr game_term_xtra_gcu_event(int v)
703 {
704     int i, k;
705
706     /* Wait */
707     if (v) {
708         char buf[256];
709         char *bp = buf;
710
711         /* Paranoia -- Wait for it */
712         nodelay(stdscr, false);
713
714         /* Get a keypress */
715         i = getch();
716
717         /* Broken input is special */
718         if (i == ERR) {
719             exit_game_panic(p_ptr);
720         }
721         if (i == EOF) {
722             exit_game_panic(p_ptr);
723         }
724
725         *bp++ = (char)i;
726
727         /* Do not wait for it */
728         nodelay(stdscr, true);
729
730         while ((i = getch()) != EOF) {
731             if (i == ERR) {
732                 exit_game_panic(p_ptr);
733             }
734             *bp++ = (char)i;
735             if (bp == &buf[255]) {
736                 break;
737             }
738         }
739
740         /* Wait for it next time */
741         nodelay(stdscr, false);
742
743         *bp = '\0';
744 #ifdef JP
745         char eucbuf[sizeof(buf)];
746         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
747         if (utf8_to_euc(buf, strlen(buf) + 1, eucbuf, sizeof(eucbuf)) < 0) {
748             return -1;
749         }
750 #endif
751         term_string_push(_(eucbuf, buf));
752     }
753
754     /* Do not wait */
755     else {
756         /* Do not wait for it */
757         nodelay(stdscr, true);
758
759         /* Check for keypresses */
760         i = getch();
761
762         /* Wait for it next time */
763         nodelay(stdscr, false);
764
765         /* None ready */
766         if (i == ERR) {
767             return 1;
768         }
769         if (i == EOF) {
770             return 1;
771         }
772
773         /* Enqueue the keypress */
774         term_key_push(i);
775     }
776
777     /* Success */
778     return 0;
779 }
780
781 #else /* USE_GETCH */
782
783 /*
784  * Process events (with optional wait)
785  */
786 static errr game_term_xtra_gcu_event(int v)
787 {
788     int i, k;
789
790     char buf[256];
791
792     /* Wait */
793     if (v) {
794         char *bp = buf;
795
796         /* Wait for one byte */
797         i = read(0, bp++, 1);
798
799         /* Hack -- Handle bizarre "errors" */
800         if ((i <= 0) && (errno != EINTR)) {
801             exit_game_panic(p_ptr);
802         }
803
804         /* Get the current flags for stdin */
805         k = fcntl(0, F_GETFL, 0);
806
807         /* Oops */
808         if (k < 0) {
809             return 1;
810         }
811
812         /* Tell stdin not to block */
813         if (fcntl(0, F_SETFL, k | O_NDELAY) >= 0) {
814             if ((i = read(0, bp, 254)) > 0) {
815                 bp += i;
816             }
817
818             /* Replace the flags for stdin */
819             if (fcntl(0, F_SETFL, k)) {
820                 return 1;
821             }
822         }
823
824         bp[0] = '\0';
825 #ifdef JP
826         char eucbuf[sizeof(buf)];
827         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
828         if (utf8_to_euc(buf, strlen(buf) + 1, eucbuf, sizeof(eucbuf)) < 0) {
829             return -1;
830         }
831 #endif
832         term_string_push(_(eucbuf, buf));
833     }
834
835     /* Do not wait */
836     else {
837         /* Get the current flags for stdin */
838         k = fcntl(0, F_GETFL, 0);
839
840         /* Oops */
841         if (k < 0) {
842             return 1;
843         }
844
845         /* Tell stdin not to block */
846         if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) {
847             return 1;
848         }
849
850         /* Read one byte, if possible */
851         i = read(0, buf, 1);
852
853         /* Replace the flags for stdin */
854         if (fcntl(0, F_SETFL, k)) {
855             return 1;
856         }
857
858         /* Ignore "invalid" keys */
859         if ((i != 1) || (!buf[0])) {
860             return 1;
861         }
862
863         /* Enqueue the keypress */
864         term_key_push(buf[0]);
865     }
866
867     /* Success */
868     return 0;
869 }
870
871 #endif /* USE_GETCH */
872
873 /*
874  * Hack -- make a sound
875  */
876 static errr game_term_xtra_gcu_sound(int v)
877 {
878     char buf[1024];
879
880     /* Sound disabled */
881     if (!use_sound) {
882         return 1;
883     }
884
885     /* Illegal sound */
886     if ((v < 0) || (v >= SOUND_MAX)) {
887         return 1;
888     }
889
890     /* Unknown sound */
891     if (!sound_file[v]) {
892         return 1;
893     }
894
895     sprintf(buf, "./gcusound.sh %s\n", sound_file[v]);
896
897     return system(buf) < 0;
898
899     return 0;
900 }
901
902 static int scale_color(int i, int j, int scale)
903 {
904     return (angband_color_table[i][j] * (scale - 1) + 127) / 255;
905 }
906
907 static int create_color(int i, int scale)
908 {
909     int r = scale_color(i, 1, scale);
910     int g = scale_color(i, 2, scale);
911     int b = scale_color(i, 3, scale);
912     int rgb = 16 + scale * scale * r + scale * g + b;
913     /* In the case of white and black we need to use the ANSI colors */
914     if (r == g && g == b) {
915         if (b == 0) {
916             rgb = 0;
917         }
918         if (b == scale) {
919             rgb = 15;
920         }
921     }
922     return rgb;
923 }
924
925 /*
926  * React to changes
927  */
928 static errr game_term_xtra_gcu_react(void)
929 {
930
931 #ifdef A_COLOR
932
933     if (!can_change_color()) {
934         if (COLORS == 256 || COLORS == 88) {
935             /* If we have more than 16 colors, find the best matches. These numbers
936              * correspond to xterm/rxvt's builtin color numbers--they do not
937              * correspond to curses' constants OR with curses' color pairs.
938              *
939              * XTerm has 216 (6*6*6) RGB colors, with each RGB setting 0-5.
940              * RXVT has 64 (4*4*4) RGB colors, with each RGB setting 0-3.
941              *
942              * Both also have the basic 16 ANSI colors, plus some extra grayscale
943              * colors which we do not use.
944              */
945             int scale = COLORS == 256 ? 6 : 4;
946             for (int i = 0; i < 16; i++) {
947                 int fg = create_color(i, scale);
948                 init_pair(i + 1, fg, bg_color);
949                 colortable[i] = COLOR_PAIR(i + 1) | A_NORMAL;
950             }
951         }
952     } else {
953         for (int i = 0; i < 16; ++i) {
954             init_color(i,
955                 (angband_color_table[i][1] * 1000) / 255,
956                 (angband_color_table[i][2] * 1000) / 255,
957                 (angband_color_table[i][3] * 1000) / 255);
958         }
959     }
960
961 #endif
962
963     /* Success */
964     return 0;
965 }
966
967 /*
968  * Handle a "special request"
969  */
970 static errr game_term_xtra_gcu(int n, int v)
971 {
972     term_data *td = (term_data *)(game_term->data);
973
974     /* Analyze the request */
975     switch (n) {
976     /* Clear screen */
977     case TERM_XTRA_CLEAR:
978         touchwin(td->win);
979         (void)wclear(td->win);
980         return 0;
981
982     /* Make a noise */
983     case TERM_XTRA_NOISE:
984         return write(1, "\007", 1) != 1;
985
986     /* Make a special sound */
987     case TERM_XTRA_SOUND:
988         return game_term_xtra_gcu_sound(v);
989
990     /* Flush the Curses buffer */
991     case TERM_XTRA_FRESH:
992         (void)wrefresh(td->win);
993         return 0;
994
995     /* Change the cursor visibility */
996     case TERM_XTRA_SHAPE:
997         curs_set(v);
998         return 0;
999
1000     /* Suspend/Resume curses */
1001     case TERM_XTRA_ALIVE:
1002         return game_term_xtra_gcu_alive(v);
1003
1004     /* Process events */
1005     case TERM_XTRA_EVENT:
1006         return game_term_xtra_gcu_event(v);
1007
1008     /* Flush events */
1009     case TERM_XTRA_FLUSH:
1010         while (!game_term_xtra_gcu_event(false)) {
1011             ;
1012         }
1013         return 0;
1014
1015     /* Delay */
1016     case TERM_XTRA_DELAY:
1017         usleep(1000 * v);
1018         return 0;
1019
1020     /* React to events */
1021     case TERM_XTRA_REACT:
1022         game_term_xtra_gcu_react();
1023         return 0;
1024     }
1025
1026     /* Unknown */
1027     return 1;
1028 }
1029
1030 /*
1031  * Actually MOVE the hardware cursor
1032  */
1033 static errr game_term_curs_gcu(int x, int y)
1034 {
1035     term_data *td = (term_data *)(game_term->data);
1036
1037     /* Literally move the cursor */
1038     wmove(td->win, y, x);
1039
1040     /* Success */
1041     return 0;
1042 }
1043
1044 /*
1045  * Erase a grid of space
1046  * Hack -- try to be "semi-efficient".
1047  */
1048 static errr game_term_wipe_gcu(int x, int y, int n)
1049 {
1050     term_data *td = (term_data *)(game_term->data);
1051
1052     /* Place cursor */
1053     wmove(td->win, y, x);
1054
1055     /* Clear to end of line */
1056     if (x + n >= 80) {
1057         wclrtoeol(td->win);
1058     }
1059
1060     /* Clear some characters */
1061     else {
1062         while (n-- > 0) {
1063             waddch(td->win, ' ');
1064         }
1065     }
1066
1067     /* Success */
1068     return 0;
1069 }
1070
1071 #ifdef USE_NCURSES_ACS
1072 /*
1073  * this function draws some ACS characters on the screen
1074  * for DOS-based users: these are the graphical chars (blocks, lines etc)
1075  *
1076  * unix-gurus: before you start adding other attributes like A_REVERSE
1077  * think hard about how map_info() in cave.c should handle the color
1078  * of something that we here draw in reverse. It's not so simple, alas.
1079  */
1080 static void game_term_acs_text_gcu(int x, int y, int n, byte a, concptr s)
1081 {
1082     term_data *td = (term_data *)(game_term->data);
1083     int i;
1084
1085     /* position the cursor */
1086     wmove(td->win, y, x);
1087
1088 #ifdef A_COLOR
1089     /* Set the color */
1090     wattrset(td->win, colortable[a & 0x0F]);
1091 #endif
1092
1093     for (i = 0; i < n; i++) {
1094         /* add acs_map of a */
1095         waddch(td->win, acs_map[(int)s[i]]);
1096     }
1097     wattrset(td->win, WA_NORMAL);
1098 }
1099 #endif
1100
1101 /*
1102  * Place some text on the screen using an attribute
1103  */
1104 static errr game_term_text_gcu(int x, int y, int n, byte a, concptr s)
1105 {
1106     term_data *td = (term_data *)(game_term->data);
1107
1108 #ifdef USE_NCURSES_ACS
1109     /* do we have colors + 16 ? */
1110     /* then call special routine for drawing special characters */
1111     if (a & 0x10) {
1112         game_term_acs_text_gcu(x, y, n, a, s);
1113         return 0;
1114     }
1115 #endif
1116
1117     /* Move the cursor and dump the string */
1118     wmove(td->win, y, x);
1119
1120 #ifdef A_COLOR
1121     /* Set the color */
1122     if (can_use_color) {
1123         wattrset(td->win, colortable[a & 0x0F]);
1124     }
1125 #endif
1126
1127 #ifdef JP
1128     char text[1024];
1129     int text_len = euc_to_utf8(s, n, text, sizeof(text));
1130     if (text_len < 0) {
1131         return -1;
1132     }
1133 #endif
1134     /* Add the text */
1135     waddnstr(td->win, _(text, s), _(text_len, n));
1136
1137     /* Success */
1138     return 0;
1139 }
1140
1141 /**
1142  * Create a window for the given "term_data" argument.
1143  *
1144  * Assumes legal arguments.
1145  */
1146 static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x)
1147 {
1148     term_type *t = &td->t;
1149
1150     /* Make sure the window has a positive size */
1151     if (rows <= 0 || cols <= 0) {
1152         return 0;
1153     }
1154
1155     /* Create a window */
1156     td->win = newwin(rows, cols, y, x);
1157
1158     /* Make sure we succeed */
1159     if (!td->win) {
1160         plog("Failed to setup curses window.");
1161         return -1;
1162     }
1163
1164     /* Initialize the term */
1165     term_init(t, cols, rows, 256);
1166
1167     /* Avoid the bottom right corner */
1168     t->icky_corner = true;
1169
1170     /* Erase with "white space" */
1171     t->attr_blank = TERM_WHITE;
1172     t->char_blank = ' ';
1173
1174     /* Set some hooks */
1175     t->init_hook = game_term_init_gcu;
1176     t->nuke_hook = game_term_nuke_gcu;
1177
1178     /* Set some more hooks */
1179     t->text_hook = game_term_text_gcu;
1180     t->wipe_hook = game_term_wipe_gcu;
1181     t->curs_hook = game_term_curs_gcu;
1182     t->xtra_hook = game_term_xtra_gcu;
1183
1184     /* Save the data */
1185     t->data = td;
1186
1187     /* Activate it */
1188     term_activate(t);
1189
1190     /* Success */
1191     return 0;
1192 }
1193
1194 /**
1195  * Simple helper
1196  */
1197 static errr term_data_init(term_data *td)
1198 {
1199     return term_data_init_gcu(td, td->r.cy, td->r.cx, td->r.y, td->r.x);
1200 }
1201
1202 /* Parse 27,15,*x30 up to the 'x'. * gets converted to a big number
1203    Parse 32,* until the end. Return count of numbers parsed */
1204 static int _parse_size_list(const char *arg, int sizes[], int max)
1205 {
1206     int i = 0;
1207     const char *start = arg;
1208     const char *stop = arg;
1209
1210     for (;;) {
1211         if (!*stop || !isdigit(*stop)) {
1212             if (i >= max) {
1213                 break;
1214             }
1215             if (*start == '*') {
1216                 sizes[i] = 255;
1217             } else {
1218                 /* rely on atoi("23,34,*") -> 23
1219                    otherwise, copy [start, stop) into a new buffer first.*/
1220                 sizes[i] = atoi(start);
1221             }
1222             i++;
1223             if (!*stop || *stop != ',') {
1224                 break;
1225             }
1226
1227             stop++;
1228             start = stop;
1229         } else {
1230             stop++;
1231         }
1232     }
1233     return i;
1234 }
1235
1236 static void hook_quit(concptr str)
1237 {
1238     /* Unused */
1239     (void)str;
1240
1241     /* Exit curses */
1242     endwin();
1243 }
1244
1245 /*
1246  * Prepare "curses" for use by the file "term.c"
1247  *
1248  * Installs the "hook" functions defined above, and then activates
1249  * the main screen "term", which clears the screen and such things.
1250  *
1251  * Someone should really check the semantics of "initscr()"
1252  */
1253 errr init_gcu(int argc, char *argv[])
1254 {
1255     int i;
1256
1257     int num_term = 4, next_win = 0;
1258     char path[1024];
1259
1260     /* Unused */
1261     (void)argc;
1262     (void)argv;
1263
1264     setlocale(LC_ALL, "");
1265
1266     /* Build the "sound" path */
1267     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
1268
1269     /* Allocate the path */
1270     ANGBAND_DIR_XTRA_SOUND = string_make(path);
1271
1272     /* Extract the normal keymap */
1273     keymap_norm_prepare();
1274
1275     bool nobigscreen = false;
1276
1277     /* Parse args */
1278     for (i = 1; i < argc; i++) {
1279         if (prefix(argv[i], "-o")) {
1280             nobigscreen = true;
1281         }
1282     }
1283
1284     /* Initialize for others systems */
1285     if (initscr() == (WINDOW *)ERR) {
1286         return -1;
1287     }
1288
1289     /* Activate hooks */
1290     quit_aux = hook_quit;
1291     core_aux = hook_quit;
1292
1293     /* Hack -- Require large screen, or Quit with message */
1294     i = ((LINES < 24) || (COLS < 80));
1295     if (i) {
1296         quit_fmt("%s needs an 80x24 'curses' screen", std::string(VARIANT_NAME).c_str());
1297     }
1298
1299 #ifdef A_COLOR
1300
1301     /*** Init the Color-pairs and set up a translation table ***/
1302
1303     /* Do we have color, and enough color, available? */
1304     can_use_color = ((start_color() != ERR) && has_colors() && (COLORS >= 8) && (COLOR_PAIRS >= 8));
1305
1306 #ifdef REDEFINE_COLORS
1307     /* Can we change colors? */
1308     can_fix_color = (can_use_color && can_change_color() && (COLORS >= 16) && (COLOR_PAIRS > 8));
1309 #endif
1310
1311     /* Attempt to use customized colors */
1312     if (can_fix_color) {
1313         /* Prepare the color pairs */
1314         for (i = 1; i <= 15; i++) {
1315             if (init_pair(i, i, 0) == ERR) {
1316                 quit("Color pair init failed");
1317             }
1318
1319             colortable[i] = COLOR_PAIR(i);
1320             game_term_xtra_gcu_react();
1321         }
1322     }
1323     /* Attempt to use colors */
1324     else if (can_use_color) {
1325         /* Color-pair 0 is *always* WHITE on BLACK */
1326
1327         /* Prepare the color pairs */
1328         init_pair(1, COLOR_RED, COLOR_BLACK);
1329         init_pair(2, COLOR_GREEN, COLOR_BLACK);
1330         init_pair(3, COLOR_YELLOW, COLOR_BLACK);
1331         init_pair(4, COLOR_BLUE, COLOR_BLACK);
1332         init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
1333         init_pair(6, COLOR_CYAN, COLOR_BLACK);
1334         init_pair(7, COLOR_BLACK, COLOR_BLACK);
1335
1336         /* Prepare the "Angband Colors" -- Bright white is too bright */
1337         /* Changed in Drangband. Cyan as grey sucks -- -TM- */
1338         colortable[0] = (COLOR_PAIR(7) | A_NORMAL); /* Black */
1339         colortable[1] = (COLOR_PAIR(0) | A_BRIGHT); /* White */
1340         colortable[2] = (COLOR_PAIR(0) | A_NORMAL); /* Grey XXX */
1341         colortable[3] = (COLOR_PAIR(1) | A_BRIGHT); /* Orange XXX */
1342         colortable[4] = (COLOR_PAIR(1) | A_NORMAL); /* Red */
1343         colortable[5] = (COLOR_PAIR(2) | A_NORMAL); /* Green */
1344         colortable[6] = (COLOR_PAIR(4) | A_BRIGHT); /* Blue */
1345         colortable[7] = (COLOR_PAIR(3) | A_NORMAL); /* Umber */
1346         colortable[8] = (COLOR_PAIR(7) | A_BRIGHT); /* Dark-grey XXX */
1347         colortable[9] = (COLOR_PAIR(0) | A_NORMAL); /* Light-grey XXX */
1348         colortable[10] = (COLOR_PAIR(5) | A_BRIGHT); /* Purple */
1349         colortable[11] = (COLOR_PAIR(3) | A_BRIGHT); /* Yellow */
1350         colortable[12] = (COLOR_PAIR(5) | A_NORMAL); /* Light Red XXX */
1351         colortable[13] = (COLOR_PAIR(2) | A_BRIGHT); /* Light Green */
1352         colortable[14] = (COLOR_PAIR(6) | A_BRIGHT); /* Light Blue */
1353         colortable[15] = (COLOR_PAIR(3) | A_NORMAL); /* Light Umber XXX */
1354     }
1355
1356 #endif
1357
1358     /* Handle "arg_sound" */
1359     if (use_sound != arg_sound) {
1360         /* Initialize (if needed) */
1361         if (arg_sound && !init_sound()) {
1362             /* Warning */
1363             plog("Cannot initialize sound!");
1364
1365             /* Cannot enable */
1366             arg_sound = false;
1367         }
1368
1369         /* Change setting */
1370         use_sound = arg_sound;
1371     }
1372
1373     /* Try graphics */
1374     if (arg_graphics) {
1375         /* if USE_NCURSES_ACS is defined, we can do something with graphics in curses! */
1376 #ifdef USE_NCURSES_ACS
1377         use_graphics = true;
1378 #endif
1379     }
1380
1381     /*** Low level preparation ***/
1382
1383 #ifdef USE_GETCH
1384
1385     /* Paranoia -- Assume no waiting */
1386     nodelay(stdscr, false);
1387
1388 #endif
1389
1390     /* Prepare */
1391     cbreak();
1392     noecho();
1393     nonl();
1394     raw();
1395
1396     /* Extract the game keymap */
1397     keymap_game_prepare();
1398
1399     /*** Now prepare the term(s) ***/
1400     if (nobigscreen) {
1401         /* Create several terms */
1402         for (i = 0; i < num_term; i++) {
1403             int rows, cols, y, x;
1404
1405             /* Decide on size and position */
1406             switch (i) {
1407             /* Upper left */
1408             case 0: {
1409                 rows = 24;
1410                 cols = 80;
1411                 y = x = 0;
1412                 break;
1413             }
1414
1415             /* Lower left */
1416             case 1: {
1417                 rows = LINES - 25;
1418                 cols = 80;
1419                 y = 25;
1420                 x = 0;
1421                 break;
1422             }
1423
1424             /* Upper right */
1425             case 2: {
1426                 rows = 24;
1427                 cols = COLS - 81;
1428                 y = 0;
1429                 x = 81;
1430                 break;
1431             }
1432
1433             /* Lower right */
1434             case 3: {
1435                 rows = LINES - 25;
1436                 cols = COLS - 81;
1437                 y = 25;
1438                 x = 81;
1439                 break;
1440             }
1441
1442             /* XXX */
1443             default: {
1444                 rows = cols = y = x = 0;
1445                 break;
1446             }
1447             }
1448
1449             /* Skip non-existant windows */
1450             if (rows <= 0 || cols <= 0) {
1451                 continue;
1452             }
1453
1454             /* Create a term */
1455             term_data_init_gcu(&data[next_win], rows, cols, y, x);
1456
1457             /* Remember the term */
1458             angband_term[next_win] = &data[next_win].t;
1459
1460             /* One more window */
1461             next_win++;
1462         }
1463     } else
1464     /* Parse Args and Prepare the Terminals. Rectangles are specified
1465       as Width x Height, right? The game will allow you to have two
1466       strips of extra terminals, one on the right and one on the bottom.
1467       The map terminal will than fit in as big as possible in the remaining
1468       space.
1469       Examples:
1470         angband -mgcu -- -right 30x27,* -bottom *x7 will layout as
1471         Term-0: Map (COLS-30)x(LINES-7) | Term-1: 30x27
1472         --------------------------------|----------------------
1473         <----Term-3: (COLS-30)x7------->| Term-2: 30x(LINES-27)
1474         composband -mgcu -- -bottom *x7 -right 30x27,* will layout as
1475         Term-0: Map (COLS-30)x(LINES-7) | Term-2: 30x27
1476                                         |------------------------------
1477                                         | Term-3: 30x(LINES-27)
1478         ---------------------------------------------------------------
1479         <----------Term-1: (COLS)x7----------------------------------->
1480         Notice the effect on the bottom terminal by specifying its argument
1481         second or first. Notice the sequence numbers for the various terminals
1482         as you will have to blindly configure them in the window setup screen.
1483         EDIT: Added support for -left and -top.
1484     */
1485     {
1486         rect_t remaining = rect(0, 0, COLS, LINES);
1487         int spacer_cx = 1;
1488         int spacer_cy = 1;
1489         int next_term = 1;
1490         int term_ct = 1;
1491
1492         for (i = 1; i < argc; i++) {
1493             if (streq(argv[i], "-spacer")) {
1494                 i++;
1495                 if (i >= argc) {
1496                     quit("Missing size specifier for -spacer");
1497                 }
1498                 sscanf(argv[i], "%dx%d", &spacer_cx, &spacer_cy);
1499             } else if (streq(argv[i], "-right") || streq(argv[i], "-left")) {
1500                 const char *arg, *tmp;
1501                 bool left = streq(argv[i], "-left");
1502                 int cx, cys[MAX_TERM_DATA] = { 0 }, ct, j, x, y;
1503
1504                 i++;
1505                 if (i >= argc) {
1506                     quit(format("Missing size specifier for -%s", left ? "left" : "right"));
1507                 }
1508
1509                 arg = argv[i];
1510                 tmp = strchr(arg, 'x');
1511                 if (!tmp) {
1512                     quit(format("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"));
1513                 }
1514                 cx = atoi(arg);
1515                 remaining.cx -= cx;
1516                 if (left) {
1517                     x = remaining.x;
1518                     y = remaining.y;
1519                     remaining.x += cx;
1520                 } else {
1521                     x = remaining.x + remaining.cx;
1522                     y = remaining.y;
1523                 }
1524                 remaining.cx -= spacer_cx;
1525                 if (left) {
1526                     remaining.x += spacer_cx;
1527                 }
1528
1529                 tmp++;
1530                 ct = _parse_size_list(tmp, cys, MAX_TERM_DATA);
1531                 for (j = 0; j < ct; j++) {
1532                     int cy = cys[j];
1533                     if (y + cy > remaining.y + remaining.cy) {
1534                         cy = remaining.y + remaining.cy - y;
1535                     }
1536                     if (next_term >= MAX_TERM_DATA) {
1537                         quit(format("Too many terminals. Only %d are allowed.", MAX_TERM_DATA));
1538                     }
1539                     if (cy <= 0) {
1540                         quit(format("Out of bounds in -%s: %d is too large (%d rows max for this strip)",
1541                             left ? "left" : "right", cys[j], remaining.cy));
1542                     }
1543                     data[next_term++].r = rect(x, y, cx, cy);
1544                     y += cy + spacer_cy;
1545                     term_ct++;
1546                 }
1547             } else if (streq(argv[i], "-top") || streq(argv[i], "-bottom")) {
1548                 const char *arg, *tmp;
1549                 bool top = streq(argv[i], "-top");
1550                 int cy, cxs[MAX_TERM_DATA] = { 0 }, ct, j, x, y;
1551
1552                 i++;
1553                 if (i >= argc) {
1554                     quit(format("Missing size specifier for -%s", top ? "top" : "bottom"));
1555                 }
1556
1557                 arg = argv[i];
1558                 tmp = strchr(arg, 'x');
1559                 if (!tmp) {
1560                     quit(format("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"));
1561                 }
1562                 tmp++;
1563                 cy = atoi(tmp);
1564                 ct = _parse_size_list(arg, cxs, MAX_TERM_DATA);
1565
1566                 remaining.cy -= cy;
1567                 if (top) {
1568                     x = remaining.x;
1569                     y = remaining.y;
1570                     remaining.y += cy;
1571                 } else {
1572                     x = remaining.x;
1573                     y = remaining.y + remaining.cy;
1574                 }
1575                 remaining.cy -= spacer_cy;
1576                 if (top) {
1577                     remaining.y += spacer_cy;
1578                 }
1579
1580                 tmp++;
1581                 for (j = 0; j < ct; j++) {
1582                     int cx = cxs[j];
1583                     if (x + cx > remaining.x + remaining.cx) {
1584                         cx = remaining.x + remaining.cx - x;
1585                     }
1586                     if (next_term >= MAX_TERM_DATA) {
1587                         quit(format("Too many terminals. Only %d are allowed.", MAX_TERM_DATA));
1588                     }
1589                     if (cx <= 0) {
1590                         quit(format("Out of bounds in -%s: %d is too large (%d cols max for this strip)",
1591                             top ? "top" : "bottom", cxs[j], remaining.cx));
1592                     }
1593                     data[next_term++].r = rect(x, y, cx, cy);
1594                     x += cx + spacer_cx;
1595                     term_ct++;
1596                 }
1597             }
1598         }
1599
1600         /* Map Terminal */
1601         if (remaining.cx < MIN_TERM0_COLS || remaining.cy < MIN_TERM0_LINES) {
1602             quit_fmt("Failed: %s needs an %dx%d map screen, not %dx%d", std::string(VARIANT_NAME).c_str(), MIN_TERM0_COLS, MIN_TERM0_LINES, remaining.cx, remaining.cy);
1603         }
1604         data[0].r = remaining;
1605         term_data_init(&data[0]);
1606         angband_term[0] = game_term;
1607
1608         /* Child Terminals */
1609         for (next_term = 1; next_term < term_ct; next_term++) {
1610             term_data_init(&data[next_term]);
1611             angband_term[next_term] = game_term;
1612         }
1613     }
1614
1615     /* Activate the "Angband" window screen */
1616     term_activate(&data[0].t);
1617
1618     /* Store */
1619     term_screen = &data[0].t;
1620
1621     /* Success */
1622     return 0;
1623 }
1624
1625 #endif /* USE_GCU */