OSDN Git Service

Merge pull request #2962 from habu1010/feature/refactor-format-retval-to-string
[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 /* 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 buf[1024];
601
602     /* Prepare the sounds */
603     for (i = 1; i < SOUND_MAX; i++) {
604         /* Extract name of sound file */
605         std::string wav = angband_sound_name[i];
606         wav.append(".wav");
607
608         /* Access the sound */
609         path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_SOUND, wav.data());
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     /* Sound disabled */
879     if (!use_sound) {
880         return 1;
881     }
882
883     /* Illegal sound */
884     if ((v < 0) || (v >= SOUND_MAX)) {
885         return 1;
886     }
887
888     /* Unknown sound */
889     if (!sound_file[v]) {
890         return 1;
891     }
892
893     std::string buf = "./gcusound.sh ";
894     buf.append(sound_file[v]).append("\n");
895     return system(buf.data()) < 0;
896 }
897
898 static int scale_color(int i, int j, int scale)
899 {
900     return (angband_color_table[i][j] * (scale - 1) + 127) / 255;
901 }
902
903 static int create_color(int i, int scale)
904 {
905     int r = scale_color(i, 1, scale);
906     int g = scale_color(i, 2, scale);
907     int b = scale_color(i, 3, scale);
908     int rgb = 16 + scale * scale * r + scale * g + b;
909     /* In the case of white and black we need to use the ANSI colors */
910     if (r == g && g == b) {
911         if (b == 0) {
912             rgb = 0;
913         }
914         if (b == scale) {
915             rgb = 15;
916         }
917     }
918     return rgb;
919 }
920
921 /*
922  * React to changes
923  */
924 static errr game_term_xtra_gcu_react(void)
925 {
926
927 #ifdef A_COLOR
928
929     if (!can_change_color()) {
930         if (COLORS == 256 || COLORS == 88) {
931             /* If we have more than 16 colors, find the best matches. These numbers
932              * correspond to xterm/rxvt's builtin color numbers--they do not
933              * correspond to curses' constants OR with curses' color pairs.
934              *
935              * XTerm has 216 (6*6*6) RGB colors, with each RGB setting 0-5.
936              * RXVT has 64 (4*4*4) RGB colors, with each RGB setting 0-3.
937              *
938              * Both also have the basic 16 ANSI colors, plus some extra grayscale
939              * colors which we do not use.
940              */
941             int scale = COLORS == 256 ? 6 : 4;
942             for (int i = 0; i < 16; i++) {
943                 int fg = create_color(i, scale);
944                 init_pair(i + 1, fg, bg_color);
945                 colortable[i] = COLOR_PAIR(i + 1) | A_NORMAL;
946             }
947         }
948     } else {
949         for (int i = 0; i < 16; ++i) {
950             init_color(i,
951                 (angband_color_table[i][1] * 1000) / 255,
952                 (angband_color_table[i][2] * 1000) / 255,
953                 (angband_color_table[i][3] * 1000) / 255);
954         }
955     }
956
957 #endif
958
959     /* Success */
960     return 0;
961 }
962
963 /*
964  * Handle a "special request"
965  */
966 static errr game_term_xtra_gcu(int n, int v)
967 {
968     term_data *td = (term_data *)(game_term->data);
969
970     /* Analyze the request */
971     switch (n) {
972     /* Clear screen */
973     case TERM_XTRA_CLEAR:
974         touchwin(td->win);
975         (void)werase(td->win);
976         return 0;
977
978     /* Make a noise */
979     case TERM_XTRA_NOISE:
980         return write(1, "\007", 1) != 1;
981
982     /* Make a special sound */
983     case TERM_XTRA_SOUND:
984         return game_term_xtra_gcu_sound(v);
985
986     /* Flush the Curses buffer */
987     case TERM_XTRA_FRESH:
988         (void)wrefresh(td->win);
989         return 0;
990
991     /* Change the cursor visibility */
992     case TERM_XTRA_SHAPE:
993         curs_set(v);
994         return 0;
995
996     /* Suspend/Resume curses */
997     case TERM_XTRA_ALIVE:
998         return game_term_xtra_gcu_alive(v);
999
1000     /* Process events */
1001     case TERM_XTRA_EVENT:
1002         return game_term_xtra_gcu_event(v);
1003
1004     /* Flush events */
1005     case TERM_XTRA_FLUSH:
1006         while (!game_term_xtra_gcu_event(false)) {
1007             ;
1008         }
1009         return 0;
1010
1011     /* Delay */
1012     case TERM_XTRA_DELAY:
1013         usleep(1000 * v);
1014         return 0;
1015
1016     /* React to events */
1017     case TERM_XTRA_REACT:
1018         game_term_xtra_gcu_react();
1019         return 0;
1020     }
1021
1022     /* Unknown */
1023     return 1;
1024 }
1025
1026 /*
1027  * Actually MOVE the hardware cursor
1028  */
1029 static errr game_term_curs_gcu(int x, int y)
1030 {
1031     term_data *td = (term_data *)(game_term->data);
1032
1033     /* Literally move the cursor */
1034     wmove(td->win, y, x);
1035
1036     /* Success */
1037     return 0;
1038 }
1039
1040 /*
1041  * Erase a grid of space
1042  * Hack -- try to be "semi-efficient".
1043  */
1044 static errr game_term_wipe_gcu(int x, int y, int n)
1045 {
1046     term_data *td = (term_data *)(game_term->data);
1047
1048     /* Place cursor */
1049     wmove(td->win, y, x);
1050
1051     /* Clear to end of line */
1052     if (x + n >= td->t.wid) {
1053         wclrtoeol(td->win);
1054     }
1055
1056     /* Clear some characters */
1057     else {
1058         while (n-- > 0) {
1059             waddch(td->win, ' ');
1060         }
1061     }
1062
1063     /* Success */
1064     return 0;
1065 }
1066
1067 #ifdef USE_NCURSES_ACS
1068 /*
1069  * this function draws some ACS characters on the screen
1070  * for DOS-based users: these are the graphical chars (blocks, lines etc)
1071  *
1072  * unix-gurus: before you start adding other attributes like A_REVERSE
1073  * think hard about how map_info() in cave.c should handle the color
1074  * of something that we here draw in reverse. It's not so simple, alas.
1075  */
1076 static void game_term_acs_text_gcu(int x, int y, int n, byte a, concptr s)
1077 {
1078     term_data *td = (term_data *)(game_term->data);
1079     int i;
1080
1081     /* position the cursor */
1082     wmove(td->win, y, x);
1083
1084 #ifdef A_COLOR
1085     /* Set the color */
1086     wattrset(td->win, colortable[a & 0x0F]);
1087 #endif
1088
1089     for (i = 0; i < n; i++) {
1090         /* add acs_map of a */
1091         waddch(td->win, acs_map[(int)s[i]]);
1092     }
1093     wattrset(td->win, WA_NORMAL);
1094 }
1095 #endif
1096
1097 /*
1098  * Place some text on the screen using an attribute
1099  */
1100 static errr game_term_text_gcu(int x, int y, int n, byte a, concptr s)
1101 {
1102     term_data *td = (term_data *)(game_term->data);
1103
1104 #ifdef USE_NCURSES_ACS
1105     /* do we have colors + 16 ? */
1106     /* then call special routine for drawing special characters */
1107     if (a & 0x10) {
1108         game_term_acs_text_gcu(x, y, n, a, s);
1109         return 0;
1110     }
1111 #endif
1112
1113     /* Move the cursor and dump the string */
1114     wmove(td->win, y, x);
1115
1116 #ifdef A_COLOR
1117     /* Set the color */
1118     if (can_use_color) {
1119         wattrset(td->win, colortable[a & 0x0F]);
1120     }
1121 #endif
1122
1123 #ifdef JP
1124     char text[1024];
1125     int text_len = euc_to_utf8(s, n, text, sizeof(text));
1126     if (text_len < 0) {
1127         return -1;
1128     }
1129 #endif
1130     /* Add the text */
1131     waddnstr(td->win, _(text, s), _(text_len, n));
1132
1133     /* Success */
1134     return 0;
1135 }
1136
1137 /**
1138  * Create a window for the given "term_data" argument.
1139  *
1140  * Assumes legal arguments.
1141  */
1142 static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x)
1143 {
1144     term_type *t = &td->t;
1145
1146     /* Make sure the window has a positive size */
1147     if (rows <= 0 || cols <= 0) {
1148         return 0;
1149     }
1150
1151     /* Create a window */
1152     td->win = newwin(rows, cols, y, x);
1153
1154     /* Make sure we succeed */
1155     if (!td->win) {
1156         plog("Failed to setup curses window.");
1157         return -1;
1158     }
1159
1160     /* Initialize the term */
1161     term_init(t, cols, rows, 256);
1162
1163     /* Avoid the bottom right corner */
1164     t->icky_corner = true;
1165
1166     /* Erase with "white space" */
1167     t->attr_blank = TERM_WHITE;
1168     t->char_blank = ' ';
1169
1170     /* Set some hooks */
1171     t->init_hook = game_term_init_gcu;
1172     t->nuke_hook = game_term_nuke_gcu;
1173
1174     /* Set some more hooks */
1175     t->text_hook = game_term_text_gcu;
1176     t->wipe_hook = game_term_wipe_gcu;
1177     t->curs_hook = game_term_curs_gcu;
1178     t->xtra_hook = game_term_xtra_gcu;
1179
1180     /* Save the data */
1181     t->data = td;
1182
1183     /* Activate it */
1184     term_activate(t);
1185
1186     /* Success */
1187     return 0;
1188 }
1189
1190 /**
1191  * Simple helper
1192  */
1193 static errr term_data_init(term_data *td)
1194 {
1195     return term_data_init_gcu(td, td->r.cy, td->r.cx, td->r.y, td->r.x);
1196 }
1197
1198 /* Parse 27,15,*x30 up to the 'x'. * gets converted to a big number
1199    Parse 32,* until the end. Return count of numbers parsed */
1200 static int _parse_size_list(const char *arg, int sizes[], int max)
1201 {
1202     int i = 0;
1203     const char *start = arg;
1204     const char *stop = arg;
1205
1206     for (;;) {
1207         if (!*stop || !isdigit(*stop)) {
1208             if (i >= max) {
1209                 break;
1210             }
1211             if (*start == '*') {
1212                 sizes[i] = 255;
1213             } else {
1214                 /* rely on atoi("23,34,*") -> 23
1215                    otherwise, copy [start, stop) into a new buffer first.*/
1216                 sizes[i] = atoi(start);
1217             }
1218             i++;
1219             if (!*stop || *stop != ',') {
1220                 break;
1221             }
1222
1223             stop++;
1224             start = stop;
1225         } else {
1226             stop++;
1227         }
1228     }
1229     return i;
1230 }
1231
1232 static void hook_quit(concptr str)
1233 {
1234     /* Unused */
1235     (void)str;
1236
1237     /* Exit curses */
1238     endwin();
1239 }
1240
1241 /*
1242  * Prepare "curses" for use by the file "term.c"
1243  *
1244  * Installs the "hook" functions defined above, and then activates
1245  * the main screen "term", which clears the screen and such things.
1246  *
1247  * Someone should really check the semantics of "initscr()"
1248  */
1249 errr init_gcu(int argc, char *argv[])
1250 {
1251     int i;
1252
1253     int num_term = 4, next_win = 0;
1254     char path[1024];
1255
1256     /* Unused */
1257     (void)argc;
1258     (void)argv;
1259
1260     setlocale(LC_ALL, "");
1261
1262     /* Build the "sound" path */
1263     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
1264
1265     /* Allocate the path */
1266     ANGBAND_DIR_XTRA_SOUND = string_make(path);
1267
1268     /* Extract the normal keymap */
1269     keymap_norm_prepare();
1270
1271     bool nobigscreen = false;
1272
1273     /* Parse args */
1274     for (i = 1; i < argc; i++) {
1275         if (prefix(argv[i], "-o")) {
1276             nobigscreen = true;
1277         }
1278     }
1279
1280     /* Initialize for others systems */
1281     if (initscr() == (WINDOW *)ERR) {
1282         return -1;
1283     }
1284
1285     /* Activate hooks */
1286     quit_aux = hook_quit;
1287     core_aux = hook_quit;
1288
1289     /* Hack -- Require large screen, or Quit with message */
1290     i = ((LINES < 24) || (COLS < 80));
1291     if (i) {
1292         quit_fmt("%s needs an 80x24 'curses' screen", std::string(VARIANT_NAME).data());
1293     }
1294
1295 #ifdef A_COLOR
1296
1297     /*** Init the Color-pairs and set up a translation table ***/
1298
1299     /* Do we have color, and enough color, available? */
1300     can_use_color = ((start_color() != ERR) && has_colors() && (COLORS >= 8) && (COLOR_PAIRS >= 8));
1301
1302 #ifdef REDEFINE_COLORS
1303     /* Can we change colors? */
1304     can_fix_color = (can_use_color && can_change_color() && (COLORS >= 16) && (COLOR_PAIRS > 8));
1305 #endif
1306
1307     /* Attempt to use customized colors */
1308     if (can_fix_color) {
1309         /* Prepare the color pairs */
1310         for (i = 1; i <= 15; i++) {
1311             if (init_pair(i, i, 0) == ERR) {
1312                 quit("Color pair init failed");
1313             }
1314
1315             colortable[i] = COLOR_PAIR(i);
1316             game_term_xtra_gcu_react();
1317         }
1318     }
1319     /* Attempt to use colors */
1320     else if (can_use_color) {
1321         /* Color-pair 0 is *always* WHITE on BLACK */
1322
1323         /* Prepare the color pairs */
1324         init_pair(1, COLOR_RED, COLOR_BLACK);
1325         init_pair(2, COLOR_GREEN, COLOR_BLACK);
1326         init_pair(3, COLOR_YELLOW, COLOR_BLACK);
1327         init_pair(4, COLOR_BLUE, COLOR_BLACK);
1328         init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
1329         init_pair(6, COLOR_CYAN, COLOR_BLACK);
1330         init_pair(7, COLOR_BLACK, COLOR_BLACK);
1331
1332         /* Prepare the "Angband Colors" -- Bright white is too bright */
1333         /* Changed in Drangband. Cyan as grey sucks -- -TM- */
1334         colortable[0] = (COLOR_PAIR(7) | A_NORMAL); /* Black */
1335         colortable[1] = (COLOR_PAIR(0) | A_BRIGHT); /* White */
1336         colortable[2] = (COLOR_PAIR(0) | A_NORMAL); /* Grey XXX */
1337         colortable[3] = (COLOR_PAIR(1) | A_BRIGHT); /* Orange XXX */
1338         colortable[4] = (COLOR_PAIR(1) | A_NORMAL); /* Red */
1339         colortable[5] = (COLOR_PAIR(2) | A_NORMAL); /* Green */
1340         colortable[6] = (COLOR_PAIR(4) | A_BRIGHT); /* Blue */
1341         colortable[7] = (COLOR_PAIR(3) | A_NORMAL); /* Umber */
1342         colortable[8] = (COLOR_PAIR(7) | A_BRIGHT); /* Dark-grey XXX */
1343         colortable[9] = (COLOR_PAIR(0) | A_NORMAL); /* Light-grey XXX */
1344         colortable[10] = (COLOR_PAIR(5) | A_BRIGHT); /* Purple */
1345         colortable[11] = (COLOR_PAIR(3) | A_BRIGHT); /* Yellow */
1346         colortable[12] = (COLOR_PAIR(5) | A_NORMAL); /* Light Red XXX */
1347         colortable[13] = (COLOR_PAIR(2) | A_BRIGHT); /* Light Green */
1348         colortable[14] = (COLOR_PAIR(6) | A_BRIGHT); /* Light Blue */
1349         colortable[15] = (COLOR_PAIR(3) | A_NORMAL); /* Light Umber XXX */
1350     }
1351
1352 #endif
1353
1354     /* Handle "arg_sound" */
1355     if (use_sound != arg_sound) {
1356         /* Initialize (if needed) */
1357         if (arg_sound && !init_sound()) {
1358             /* Warning */
1359             plog("Cannot initialize sound!");
1360
1361             /* Cannot enable */
1362             arg_sound = false;
1363         }
1364
1365         /* Change setting */
1366         use_sound = arg_sound;
1367     }
1368
1369     /* Try graphics */
1370     if (arg_graphics) {
1371         /* if USE_NCURSES_ACS is defined, we can do something with graphics in curses! */
1372 #ifdef USE_NCURSES_ACS
1373         use_graphics = true;
1374 #endif
1375     }
1376
1377     /*** Low level preparation ***/
1378
1379 #ifdef USE_GETCH
1380
1381     /* Paranoia -- Assume no waiting */
1382     nodelay(stdscr, false);
1383
1384 #endif
1385
1386     /* Prepare */
1387     cbreak();
1388     noecho();
1389     nonl();
1390     raw();
1391
1392     /* Extract the game keymap */
1393     keymap_game_prepare();
1394
1395     /*** Now prepare the term(s) ***/
1396     if (nobigscreen) {
1397         /* Create several terms */
1398         for (i = 0; i < num_term; i++) {
1399             int rows, cols, y, x;
1400
1401             /* Decide on size and position */
1402             switch (i) {
1403             /* Upper left */
1404             case 0: {
1405                 rows = 24;
1406                 cols = 80;
1407                 y = x = 0;
1408                 break;
1409             }
1410
1411             /* Lower left */
1412             case 1: {
1413                 rows = LINES - 25;
1414                 cols = 80;
1415                 y = 25;
1416                 x = 0;
1417                 break;
1418             }
1419
1420             /* Upper right */
1421             case 2: {
1422                 rows = 24;
1423                 cols = COLS - 81;
1424                 y = 0;
1425                 x = 81;
1426                 break;
1427             }
1428
1429             /* Lower right */
1430             case 3: {
1431                 rows = LINES - 25;
1432                 cols = COLS - 81;
1433                 y = 25;
1434                 x = 81;
1435                 break;
1436             }
1437
1438             /* XXX */
1439             default: {
1440                 rows = cols = y = x = 0;
1441                 break;
1442             }
1443             }
1444
1445             /* Skip non-existant windows */
1446             if (rows <= 0 || cols <= 0) {
1447                 continue;
1448             }
1449
1450             /* Create a term */
1451             term_data_init_gcu(&data[next_win], rows, cols, y, x);
1452
1453             /* Remember the term */
1454             angband_terms[next_win] = &data[next_win].t;
1455
1456             /* One more window */
1457             next_win++;
1458         }
1459     } else
1460     /* Parse Args and Prepare the Terminals. Rectangles are specified
1461       as Width x Height, right? The game will allow you to have two
1462       strips of extra terminals, one on the right and one on the bottom.
1463       The map terminal will than fit in as big as possible in the remaining
1464       space.
1465       Examples:
1466         angband -mgcu -- -right 30x27,* -bottom *x7 will layout as
1467         Term-0: Map (COLS-30)x(LINES-7) | Term-1: 30x27
1468         --------------------------------|----------------------
1469         <----Term-3: (COLS-30)x7------->| Term-2: 30x(LINES-27)
1470         composband -mgcu -- -bottom *x7 -right 30x27,* will layout as
1471         Term-0: Map (COLS-30)x(LINES-7) | Term-2: 30x27
1472                                         |------------------------------
1473                                         | Term-3: 30x(LINES-27)
1474         ---------------------------------------------------------------
1475         <----------Term-1: (COLS)x7----------------------------------->
1476         Notice the effect on the bottom terminal by specifying its argument
1477         second or first. Notice the sequence numbers for the various terminals
1478         as you will have to blindly configure them in the window setup screen.
1479         EDIT: Added support for -left and -top.
1480     */
1481     {
1482         rect_t remaining = rect(0, 0, COLS, LINES);
1483         int spacer_cx = 1;
1484         int spacer_cy = 1;
1485         int next_term = 1;
1486         int term_ct = 1;
1487
1488         for (i = 1; i < argc; i++) {
1489             if (streq(argv[i], "-spacer")) {
1490                 i++;
1491                 if (i >= argc) {
1492                     quit("Missing size specifier for -spacer");
1493                 }
1494                 sscanf(argv[i], "%dx%d", &spacer_cx, &spacer_cy);
1495             } else if (streq(argv[i], "-right") || streq(argv[i], "-left")) {
1496                 const char *arg, *tmp;
1497                 bool left = streq(argv[i], "-left");
1498                 int cx, cys[MAX_TERM_DATA] = { 0 }, ct, j, x, y;
1499
1500                 i++;
1501                 if (i >= argc) {
1502                     quit_fmt("Missing size specifier for -%s", left ? "left" : "right");
1503                 }
1504
1505                 arg = argv[i];
1506                 tmp = strchr(arg, 'x');
1507                 if (!tmp) {
1508                     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");
1509                 }
1510                 cx = atoi(arg);
1511                 remaining.cx -= cx;
1512                 if (left) {
1513                     x = remaining.x;
1514                     y = remaining.y;
1515                     remaining.x += cx;
1516                 } else {
1517                     x = remaining.x + remaining.cx;
1518                     y = remaining.y;
1519                 }
1520                 remaining.cx -= spacer_cx;
1521                 if (left) {
1522                     remaining.x += spacer_cx;
1523                 }
1524
1525                 tmp++;
1526                 ct = _parse_size_list(tmp, cys, MAX_TERM_DATA);
1527                 for (j = 0; j < ct; j++) {
1528                     int cy = cys[j];
1529                     if (y + cy > remaining.y + remaining.cy) {
1530                         cy = remaining.y + remaining.cy - y;
1531                     }
1532                     if (next_term >= MAX_TERM_DATA) {
1533                         quit_fmt("Too many terminals. Only %d are allowed.", MAX_TERM_DATA);
1534                     }
1535                     if (cy <= 0) {
1536                         quit_fmt("Out of bounds in -%s: %d is too large (%d rows max for this strip)",
1537                             left ? "left" : "right", cys[j], remaining.cy);
1538                     }
1539                     data[next_term++].r = rect(x, y, cx, cy);
1540                     y += cy + spacer_cy;
1541                     term_ct++;
1542                 }
1543             } else if (streq(argv[i], "-top") || streq(argv[i], "-bottom")) {
1544                 const char *arg, *tmp;
1545                 bool top = streq(argv[i], "-top");
1546                 int cy, cxs[MAX_TERM_DATA] = { 0 }, ct, j, x, y;
1547
1548                 i++;
1549                 if (i >= argc) {
1550                     quit_fmt("Missing size specifier for -%s", top ? "top" : "bottom");
1551                 }
1552
1553                 arg = argv[i];
1554                 tmp = strchr(arg, 'x');
1555                 if (!tmp) {
1556                     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");
1557                 }
1558                 tmp++;
1559                 cy = atoi(tmp);
1560                 ct = _parse_size_list(arg, cxs, MAX_TERM_DATA);
1561
1562                 remaining.cy -= cy;
1563                 if (top) {
1564                     x = remaining.x;
1565                     y = remaining.y;
1566                     remaining.y += cy;
1567                 } else {
1568                     x = remaining.x;
1569                     y = remaining.y + remaining.cy;
1570                 }
1571                 remaining.cy -= spacer_cy;
1572                 if (top) {
1573                     remaining.y += spacer_cy;
1574                 }
1575
1576                 tmp++;
1577                 for (j = 0; j < ct; j++) {
1578                     int cx = cxs[j];
1579                     if (x + cx > remaining.x + remaining.cx) {
1580                         cx = remaining.x + remaining.cx - x;
1581                     }
1582                     if (next_term >= MAX_TERM_DATA) {
1583                         quit_fmt("Too many terminals. Only %d are allowed.", MAX_TERM_DATA);
1584                     }
1585                     if (cx <= 0) {
1586                         quit_fmt("Out of bounds in -%s: %d is too large (%d cols max for this strip)",
1587                             top ? "top" : "bottom", cxs[j], remaining.cx);
1588                     }
1589                     data[next_term++].r = rect(x, y, cx, cy);
1590                     x += cx + spacer_cx;
1591                     term_ct++;
1592                 }
1593             }
1594         }
1595
1596         /* Map Terminal */
1597         if (remaining.cx < MIN_TERM0_COLS || remaining.cy < MIN_TERM0_LINES) {
1598             quit_fmt("Failed: %s needs an %dx%d map screen, not %dx%d", std::string(VARIANT_NAME).data(), MIN_TERM0_COLS, MIN_TERM0_LINES, remaining.cx, remaining.cy);
1599         }
1600         data[0].r = remaining;
1601         term_data_init(&data[0]);
1602         angband_terms[0] = game_term;
1603
1604         /* Child Terminals */
1605         for (next_term = 1; next_term < term_ct; next_term++) {
1606             term_data_init(&data[next_term]);
1607             angband_terms[next_term] = game_term;
1608         }
1609     }
1610
1611     /* Activate the "Angband" window screen */
1612     term_activate(&data[0].t);
1613
1614     /* Store */
1615     term_screen = &data[0].t;
1616
1617     /* Success */
1618     return 0;
1619 }
1620
1621 #endif /* USE_GCU */