OSDN Git Service

Merge pull request #2214 from Hourier/Rename-Pascal-Case-To-Snake-Case
[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.h"
168 #include "system/angband-version.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     fclose(fff);
585     return true;
586 }
587
588 /*
589  * Initialize sound
590  */
591 static bool init_sound(void)
592 {
593     /* Initialize once */
594     if (can_use_sound)
595         return can_use_sound;
596
597     int i;
598     char wav[128];
599     char buf[1024];
600
601     /* Prepare the sounds */
602     for (i = 1; i < SOUND_MAX; i++) {
603         /* Extract name of sound file */
604         sprintf(wav, "%s.wav", angband_sound_name[i]);
605
606         /* Access the sound */
607         path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_SOUND, wav);
608
609         /* Save the sound filename, if it exists */
610         if (check_file(buf))
611             sound_file[i] = string_make(buf);
612     }
613
614     /* Sound available */
615     can_use_sound = true;
616     return can_use_sound;
617 }
618
619 /*
620  * Init the "curses" system
621  */
622 static void game_term_init_gcu(term_type *t)
623 {
624     term_data *td = (term_data *)(t->data);
625
626     /* Count init's, handle first */
627     if (active++ != 0)
628         return;
629
630     /* Erase the screen */
631     (void)wclear(td->win);
632
633     /* Reset the cursor */
634     (void)wmove(td->win, 0, 0);
635
636     /* Flush changes */
637     (void)wrefresh(td->win);
638
639     /* Game keymap */
640     keymap_game();
641 }
642
643 /*
644  * Nuke the "curses" system
645  */
646 static void game_term_nuke_gcu(term_type *t)
647 {
648     term_data *td = (term_data *)(t->data);
649
650     /* Delete this window */
651     delwin(td->win);
652
653     /* Count nuke's, handle last */
654     if (--active != 0)
655         return;
656
657     /* Hack -- make sure the cursor is visible */
658     term_xtra(TERM_XTRA_SHAPE, 1);
659
660 #ifdef A_COLOR
661     /* Reset colors to defaults */
662     start_color();
663 #endif
664
665     /* This moves curses to bottom right corner */
666     mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
667
668     /* Flush the curses buffer */
669     (void)refresh();
670
671     /* Exit curses */
672     endwin();
673
674     /* Flush the output */
675     (void)fflush(stdout);
676
677     /* Normal keymap */
678     keymap_norm();
679 }
680
681 /*
682  * Push multiple keys reversal
683  */
684 static void term_string_push(char *buf)
685 {
686     int i, l = strlen(buf);
687     for (i = l; i >= 0; i--)
688         term_key_push(buf[i]);
689 }
690
691 #ifdef USE_GETCH
692
693 /*
694  * Process events, with optional wait
695  */
696 static errr game_term_xtra_gcu_event(int v)
697 {
698     int i, k;
699
700     /* Wait */
701     if (v) {
702         char buf[256];
703         char *bp = buf;
704
705         /* Paranoia -- Wait for it */
706         nodelay(stdscr, false);
707
708         /* Get a keypress */
709         i = getch();
710
711         /* Broken input is special */
712         if (i == ERR)
713             exit_game_panic(p_ptr);
714         if (i == EOF)
715             exit_game_panic(p_ptr);
716
717         *bp++ = (char)i;
718
719         /* Do not wait for it */
720         nodelay(stdscr, true);
721
722         while ((i = getch()) != EOF) {
723             if (i == ERR)
724                 exit_game_panic(p_ptr);
725             *bp++ = (char)i;
726             if (bp == &buf[255])
727                 break;
728         }
729
730         /* Wait for it next time */
731         nodelay(stdscr, false);
732
733         *bp = '\0';
734 #ifdef JP
735         char eucbuf[sizeof(buf)];
736         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
737         if (utf8_to_euc(buf, strlen(buf) + 1, eucbuf, sizeof(eucbuf)) < 0) {
738             return -1;
739         }
740 #endif
741         term_string_push(_(eucbuf, buf));
742     }
743
744     /* Do not wait */
745     else {
746         /* Do not wait for it */
747         nodelay(stdscr, true);
748
749         /* Check for keypresses */
750         i = getch();
751
752         /* Wait for it next time */
753         nodelay(stdscr, false);
754
755         /* None ready */
756         if (i == ERR)
757             return 1;
758         if (i == EOF)
759             return 1;
760
761         /* Enqueue the keypress */
762         term_key_push(i);
763     }
764
765     /* Success */
766     return 0;
767 }
768
769 #else /* USE_GETCH */
770
771 /*
772  * Process events (with optional wait)
773  */
774 static errr game_term_xtra_gcu_event(int v)
775 {
776     int i, k;
777
778     char buf[256];
779
780     /* Wait */
781     if (v) {
782         char *bp = buf;
783
784         /* Wait for one byte */
785         i = read(0, bp++, 1);
786
787         /* Hack -- Handle bizarre "errors" */
788         if ((i <= 0) && (errno != EINTR))
789             exit_game_panic(p_ptr);
790
791         /* Get the current flags for stdin */
792         k = fcntl(0, F_GETFL, 0);
793
794         /* Oops */
795         if (k < 0)
796             return 1;
797
798         /* Tell stdin not to block */
799         if (fcntl(0, F_SETFL, k | O_NDELAY) >= 0) {
800             if ((i = read(0, bp, 254)) > 0) {
801                 bp += i;
802             }
803
804             /* Replace the flags for stdin */
805             if (fcntl(0, F_SETFL, k))
806                 return 1;
807         }
808
809         bp[0] = '\0';
810 #ifdef JP
811         char eucbuf[sizeof(buf)];
812         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
813         if (utf8_to_euc(buf, strlen(buf) + 1, eucbuf, sizeof(eucbuf)) < 0) {
814             return -1;
815         }
816 #endif
817         term_string_push(_(eucbuf, buf));
818     }
819
820     /* Do not wait */
821     else {
822         /* Get the current flags for stdin */
823         k = fcntl(0, F_GETFL, 0);
824
825         /* Oops */
826         if (k < 0)
827             return 1;
828
829         /* Tell stdin not to block */
830         if (fcntl(0, F_SETFL, k | O_NDELAY) < 0)
831             return 1;
832
833         /* Read one byte, if possible */
834         i = read(0, buf, 1);
835
836         /* Replace the flags for stdin */
837         if (fcntl(0, F_SETFL, k))
838             return 1;
839
840         /* Ignore "invalid" keys */
841         if ((i != 1) || (!buf[0]))
842             return 1;
843
844         /* Enqueue the keypress */
845         term_key_push(buf[0]);
846     }
847
848     /* Success */
849     return 0;
850 }
851
852 #endif /* USE_GETCH */
853
854 /*
855  * Hack -- make a sound
856  */
857 static errr game_term_xtra_gcu_sound(int v)
858 {
859     char buf[1024];
860
861     /* Sound disabled */
862     if (!use_sound)
863         return 1;
864
865     /* Illegal sound */
866     if ((v < 0) || (v >= SOUND_MAX))
867         return 1;
868
869     /* Unknown sound */
870     if (!sound_file[v])
871         return 1;
872
873     sprintf(buf, "./gcusound.sh %s\n", sound_file[v]);
874
875     return system(buf) < 0;
876
877     return 0;
878 }
879
880 static int scale_color(int i, int j, int scale)
881 {
882     return (angband_color_table[i][j] * (scale - 1) + 127) / 255;
883 }
884
885 static int create_color(int i, int scale)
886 {
887     int r = scale_color(i, 1, scale);
888     int g = scale_color(i, 2, scale);
889     int b = scale_color(i, 3, scale);
890     int rgb = 16 + scale * scale * r + scale * g + b;
891     /* In the case of white and black we need to use the ANSI colors */
892     if (r == g && g == b) {
893         if (b == 0)
894             rgb = 0;
895         if (b == scale)
896             rgb = 15;
897     }
898     return rgb;
899 }
900
901 /*
902  * React to changes
903  */
904 static errr game_term_xtra_gcu_react(void)
905 {
906
907 #ifdef A_COLOR
908
909     if (!can_change_color()) {
910         if (COLORS == 256 || COLORS == 88) {
911             /* If we have more than 16 colors, find the best matches. These numbers
912              * correspond to xterm/rxvt's builtin color numbers--they do not
913              * correspond to curses' constants OR with curses' color pairs.
914              *
915              * XTerm has 216 (6*6*6) RGB colors, with each RGB setting 0-5.
916              * RXVT has 64 (4*4*4) RGB colors, with each RGB setting 0-3.
917              *
918              * Both also have the basic 16 ANSI colors, plus some extra grayscale
919              * colors which we do not use.
920              */
921             int scale = COLORS == 256 ? 6 : 4;
922             for (int i = 0; i < 16; i++) {
923                 int fg = create_color(i, scale);
924                 init_pair(i + 1, fg, bg_color);
925                 colortable[i] = COLOR_PAIR(i + 1) | A_NORMAL;
926             }
927         }
928     } else {
929         for (int i = 0; i < 16; ++i) {
930             init_color(i,
931                 (angband_color_table[i][1] * 1000) / 255,
932                 (angband_color_table[i][2] * 1000) / 255,
933                 (angband_color_table[i][3] * 1000) / 255);
934         }
935     }
936
937 #endif
938
939     /* Success */
940     return 0;
941 }
942
943 /*
944  * Handle a "special request"
945  */
946 static errr game_term_xtra_gcu(int n, int v)
947 {
948     term_data *td = (term_data *)(game_term->data);
949
950     /* Analyze the request */
951     switch (n) {
952     /* Clear screen */
953     case TERM_XTRA_CLEAR:
954         touchwin(td->win);
955         (void)wclear(td->win);
956         return 0;
957
958     /* Make a noise */
959     case TERM_XTRA_NOISE:
960         return write(1, "\007", 1) != 1;
961
962     /* Make a special sound */
963     case TERM_XTRA_SOUND:
964         return game_term_xtra_gcu_sound(v);
965
966     /* Flush the Curses buffer */
967     case TERM_XTRA_FRESH:
968         (void)wrefresh(td->win);
969         return 0;
970
971     /* Change the cursor visibility */
972     case TERM_XTRA_SHAPE:
973         curs_set(v);
974         return 0;
975
976     /* Suspend/Resume curses */
977     case TERM_XTRA_ALIVE:
978         return game_term_xtra_gcu_alive(v);
979
980     /* Process events */
981     case TERM_XTRA_EVENT:
982         return game_term_xtra_gcu_event(v);
983
984     /* Flush events */
985     case TERM_XTRA_FLUSH:
986         while (!game_term_xtra_gcu_event(false))
987             ;
988         return 0;
989
990     /* Delay */
991     case TERM_XTRA_DELAY:
992         usleep(1000 * v);
993         return 0;
994
995     /* React to events */
996     case TERM_XTRA_REACT:
997         game_term_xtra_gcu_react();
998         return 0;
999     }
1000
1001     /* Unknown */
1002     return 1;
1003 }
1004
1005 /*
1006  * Actually MOVE the hardware cursor
1007  */
1008 static errr game_term_curs_gcu(int x, int y)
1009 {
1010     term_data *td = (term_data *)(game_term->data);
1011
1012     /* Literally move the cursor */
1013     wmove(td->win, y, x);
1014
1015     /* Success */
1016     return 0;
1017 }
1018
1019 /*
1020  * Erase a grid of space
1021  * Hack -- try to be "semi-efficient".
1022  */
1023 static errr game_term_wipe_gcu(int x, int y, int n)
1024 {
1025     term_data *td = (term_data *)(game_term->data);
1026
1027     /* Place cursor */
1028     wmove(td->win, y, x);
1029
1030     /* Clear to end of line */
1031     if (x + n >= 80) {
1032         wclrtoeol(td->win);
1033     }
1034
1035     /* Clear some characters */
1036     else {
1037         while (n-- > 0)
1038             waddch(td->win, ' ');
1039     }
1040
1041     /* Success */
1042     return 0;
1043 }
1044
1045 #ifdef USE_NCURSES_ACS
1046 /*
1047  * this function draws some ACS characters on the screen
1048  * for DOS-based users: these are the graphical chars (blocks, lines etc)
1049  *
1050  * unix-gurus: before you start adding other attributes like A_REVERSE
1051  * think hard about how map_info() in cave.c should handle the color
1052  * of something that we here draw in reverse. It's not so simple, alas.
1053  */
1054 static void game_term_acs_text_gcu(int x, int y, int n, byte a, concptr s)
1055 {
1056     term_data *td = (term_data *)(game_term->data);
1057     int i;
1058
1059     /* position the cursor */
1060     wmove(td->win, y, x);
1061
1062 #ifdef A_COLOR
1063     /* Set the color */
1064     wattrset(td->win, colortable[a & 0x0F]);
1065 #endif
1066
1067     for (i = 0; i < n; i++) {
1068         /* add acs_map of a */
1069         waddch(td->win, acs_map[(int)s[i]]);
1070     }
1071     wattrset(td->win, WA_NORMAL);
1072 }
1073 #endif
1074
1075 /*
1076  * Place some text on the screen using an attribute
1077  */
1078 static errr game_term_text_gcu(int x, int y, int n, byte a, concptr s)
1079 {
1080     term_data *td = (term_data *)(game_term->data);
1081
1082 #ifdef USE_NCURSES_ACS
1083     /* do we have colors + 16 ? */
1084     /* then call special routine for drawing special characters */
1085     if (a & 0x10) {
1086         game_term_acs_text_gcu(x, y, n, a, s);
1087         return 0;
1088     }
1089 #endif
1090
1091     /* Move the cursor and dump the string */
1092     wmove(td->win, y, x);
1093
1094 #ifdef A_COLOR
1095     /* Set the color */
1096     if (can_use_color)
1097         wattrset(td->win, colortable[a & 0x0F]);
1098 #endif
1099
1100 #ifdef JP
1101     char text[1024];
1102     int text_len = euc_to_utf8(s, n, text, sizeof(text));
1103     if (text_len < 0) {
1104         return -1;
1105     }
1106 #endif
1107     /* Add the text */
1108     waddnstr(td->win, _(text, s), _(text_len, n));
1109
1110     /* Success */
1111     return 0;
1112 }
1113
1114 /**
1115  * Create a window for the given "term_data" argument.
1116  *
1117  * Assumes legal arguments.
1118  */
1119 static errr term_data_init_gcu(term_data *td, int rows, int cols, int y, int x)
1120 {
1121     term_type *t = &td->t;
1122
1123     /* Make sure the window has a positive size */
1124     if (rows <= 0 || cols <= 0)
1125         return 0;
1126
1127     /* Create a window */
1128     td->win = newwin(rows, cols, y, x);
1129
1130     /* Make sure we succeed */
1131     if (!td->win) {
1132         plog("Failed to setup curses window.");
1133         return -1;
1134     }
1135
1136     /* Initialize the term */
1137     term_init(t, cols, rows, 256);
1138
1139     /* Avoid the bottom right corner */
1140     t->icky_corner = true;
1141
1142     /* Erase with "white space" */
1143     t->attr_blank = TERM_WHITE;
1144     t->char_blank = ' ';
1145
1146     /* Set some hooks */
1147     t->init_hook = game_term_init_gcu;
1148     t->nuke_hook = game_term_nuke_gcu;
1149
1150     /* Set some more hooks */
1151     t->text_hook = game_term_text_gcu;
1152     t->wipe_hook = game_term_wipe_gcu;
1153     t->curs_hook = game_term_curs_gcu;
1154     t->xtra_hook = game_term_xtra_gcu;
1155
1156     /* Save the data */
1157     t->data = td;
1158
1159     /* Activate it */
1160     term_activate(t);
1161
1162     /* Success */
1163     return 0;
1164 }
1165
1166 /**
1167  * Simple helper
1168  */
1169 static errr term_data_init(term_data *td)
1170 {
1171     return term_data_init_gcu(td, td->r.cy, td->r.cx, td->r.y, td->r.x);
1172 }
1173
1174 /* Parse 27,15,*x30 up to the 'x'. * gets converted to a big number
1175    Parse 32,* until the end. Return count of numbers parsed */
1176 static int _parse_size_list(const char *arg, int sizes[], int max)
1177 {
1178     int i = 0;
1179     const char *start = arg;
1180     const char *stop = arg;
1181
1182     for (;;) {
1183         if (!*stop || !isdigit(*stop)) {
1184             if (i >= max)
1185                 break;
1186             if (*start == '*')
1187                 sizes[i] = 255;
1188             else {
1189                 /* rely on atoi("23,34,*") -> 23
1190                    otherwise, copy [start, stop) into a new buffer first.*/
1191                 sizes[i] = atoi(start);
1192             }
1193             i++;
1194             if (!*stop || *stop != ',')
1195                 break;
1196
1197             stop++;
1198             start = stop;
1199         } else
1200             stop++;
1201     }
1202     return i;
1203 }
1204
1205 static void hook_quit(concptr str)
1206 {
1207     /* Unused */
1208     (void)str;
1209
1210     /* Exit curses */
1211     endwin();
1212 }
1213
1214 /*
1215  * Prepare "curses" for use by the file "term.c"
1216  *
1217  * Installs the "hook" functions defined above, and then activates
1218  * the main screen "term", which clears the screen and such things.
1219  *
1220  * Someone should really check the semantics of "initscr()"
1221  */
1222 errr init_gcu(int argc, char *argv[])
1223 {
1224     int i;
1225
1226     int num_term = 4, next_win = 0;
1227     char path[1024];
1228
1229     /* Unused */
1230     (void)argc;
1231     (void)argv;
1232
1233     setlocale(LC_ALL, "");
1234
1235     /* Build the "sound" path */
1236     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
1237
1238     /* Allocate the path */
1239     ANGBAND_DIR_XTRA_SOUND = string_make(path);
1240
1241     /* Extract the normal keymap */
1242     keymap_norm_prepare();
1243
1244     bool nobigscreen = false;
1245
1246     /* Parse args */
1247     for (i = 1; i < argc; i++) {
1248         if (prefix(argv[i], "-o")) {
1249             nobigscreen = true;
1250         }
1251     }
1252
1253     /* Initialize for others systems */
1254     if (initscr() == (WINDOW *)ERR)
1255         return -1;
1256
1257     /* Activate hooks */
1258     quit_aux = hook_quit;
1259     core_aux = hook_quit;
1260
1261     /* Hack -- Require large screen, or Quit with message */
1262     i = ((LINES < 24) || (COLS < 80));
1263     if (i)
1264         quit_fmt("%s needs an 80x24 'curses' screen", std::string(VARIANT_NAME).c_str());
1265
1266 #ifdef A_COLOR
1267
1268     /*** Init the Color-pairs and set up a translation table ***/
1269
1270     /* Do we have color, and enough color, available? */
1271     can_use_color = ((start_color() != ERR) && has_colors() && (COLORS >= 8) && (COLOR_PAIRS >= 8));
1272
1273 #ifdef REDEFINE_COLORS
1274     /* Can we change colors? */
1275     can_fix_color = (can_use_color && can_change_color() && (COLORS >= 16) && (COLOR_PAIRS > 8));
1276 #endif
1277
1278     /* Attempt to use customized colors */
1279     if (can_fix_color) {
1280         /* Prepare the color pairs */
1281         for (i = 1; i <= 15; i++) {
1282             if (init_pair(i, i, 0) == ERR) {
1283                 quit("Color pair init failed");
1284             }
1285
1286             colortable[i] = COLOR_PAIR(i);
1287             game_term_xtra_gcu_react();
1288         }
1289     }
1290     /* Attempt to use colors */
1291     else if (can_use_color) {
1292         /* Color-pair 0 is *always* WHITE on BLACK */
1293
1294         /* Prepare the color pairs */
1295         init_pair(1, COLOR_RED, COLOR_BLACK);
1296         init_pair(2, COLOR_GREEN, COLOR_BLACK);
1297         init_pair(3, COLOR_YELLOW, COLOR_BLACK);
1298         init_pair(4, COLOR_BLUE, COLOR_BLACK);
1299         init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
1300         init_pair(6, COLOR_CYAN, COLOR_BLACK);
1301         init_pair(7, COLOR_BLACK, COLOR_BLACK);
1302
1303         /* Prepare the "Angband Colors" -- Bright white is too bright */
1304         /* Changed in Drangband. Cyan as grey sucks -- -TM- */
1305         colortable[0] = (COLOR_PAIR(7) | A_NORMAL); /* Black */
1306         colortable[1] = (COLOR_PAIR(0) | A_BRIGHT); /* White */
1307         colortable[2] = (COLOR_PAIR(0) | A_NORMAL); /* Grey XXX */
1308         colortable[3] = (COLOR_PAIR(1) | A_BRIGHT); /* Orange XXX */
1309         colortable[4] = (COLOR_PAIR(1) | A_NORMAL); /* Red */
1310         colortable[5] = (COLOR_PAIR(2) | A_NORMAL); /* Green */
1311         colortable[6] = (COLOR_PAIR(4) | A_BRIGHT); /* Blue */
1312         colortable[7] = (COLOR_PAIR(3) | A_NORMAL); /* Umber */
1313         colortable[8] = (COLOR_PAIR(7) | A_BRIGHT); /* Dark-grey XXX */
1314         colortable[9] = (COLOR_PAIR(0) | A_NORMAL); /* Light-grey XXX */
1315         colortable[10] = (COLOR_PAIR(5) | A_BRIGHT); /* Purple */
1316         colortable[11] = (COLOR_PAIR(3) | A_BRIGHT); /* Yellow */
1317         colortable[12] = (COLOR_PAIR(5) | A_NORMAL); /* Light Red XXX */
1318         colortable[13] = (COLOR_PAIR(2) | A_BRIGHT); /* Light Green */
1319         colortable[14] = (COLOR_PAIR(6) | A_BRIGHT); /* Light Blue */
1320         colortable[15] = (COLOR_PAIR(3) | A_NORMAL); /* Light Umber XXX */
1321     }
1322
1323 #endif
1324
1325     /* Handle "arg_sound" */
1326     if (use_sound != arg_sound) {
1327         /* Initialize (if needed) */
1328         if (arg_sound && !init_sound()) {
1329             /* Warning */
1330             plog("Cannot initialize sound!");
1331
1332             /* Cannot enable */
1333             arg_sound = false;
1334         }
1335
1336         /* Change setting */
1337         use_sound = arg_sound;
1338     }
1339
1340     /* Try graphics */
1341     if (arg_graphics) {
1342         /* if USE_NCURSES_ACS is defined, we can do something with graphics in curses! */
1343 #ifdef USE_NCURSES_ACS
1344         use_graphics = true;
1345 #endif
1346     }
1347
1348     /*** Low level preparation ***/
1349
1350 #ifdef USE_GETCH
1351
1352     /* Paranoia -- Assume no waiting */
1353     nodelay(stdscr, false);
1354
1355 #endif
1356
1357     /* Prepare */
1358     cbreak();
1359     noecho();
1360     nonl();
1361     raw();
1362
1363     /* Extract the game keymap */
1364     keymap_game_prepare();
1365
1366     /*** Now prepare the term(s) ***/
1367     if (nobigscreen) {
1368         /* Create several terms */
1369         for (i = 0; i < num_term; i++) {
1370             int rows, cols, y, x;
1371
1372             /* Decide on size and position */
1373             switch (i) {
1374             /* Upper left */
1375             case 0: {
1376                 rows = 24;
1377                 cols = 80;
1378                 y = x = 0;
1379                 break;
1380             }
1381
1382             /* Lower left */
1383             case 1: {
1384                 rows = LINES - 25;
1385                 cols = 80;
1386                 y = 25;
1387                 x = 0;
1388                 break;
1389             }
1390
1391             /* Upper right */
1392             case 2: {
1393                 rows = 24;
1394                 cols = COLS - 81;
1395                 y = 0;
1396                 x = 81;
1397                 break;
1398             }
1399
1400             /* Lower right */
1401             case 3: {
1402                 rows = LINES - 25;
1403                 cols = COLS - 81;
1404                 y = 25;
1405                 x = 81;
1406                 break;
1407             }
1408
1409             /* XXX */
1410             default: {
1411                 rows = cols = y = x = 0;
1412                 break;
1413             }
1414             }
1415
1416             /* Skip non-existant windows */
1417             if (rows <= 0 || cols <= 0)
1418                 continue;
1419
1420             /* Create a term */
1421             term_data_init_gcu(&data[next_win], rows, cols, y, x);
1422
1423             /* Remember the term */
1424             angband_term[next_win] = &data[next_win].t;
1425
1426             /* One more window */
1427             next_win++;
1428         }
1429     } else
1430     /* Parse Args and Prepare the Terminals. Rectangles are specified
1431       as Width x Height, right? The game will allow you to have two
1432       strips of extra terminals, one on the right and one on the bottom.
1433       The map terminal will than fit in as big as possible in the remaining
1434       space.
1435       Examples:
1436         angband -mgcu -- -right 30x27,* -bottom *x7 will layout as
1437         Term-0: Map (COLS-30)x(LINES-7) | Term-1: 30x27
1438         --------------------------------|----------------------
1439         <----Term-3: (COLS-30)x7------->| Term-2: 30x(LINES-27)
1440         composband -mgcu -- -bottom *x7 -right 30x27,* will layout as
1441         Term-0: Map (COLS-30)x(LINES-7) | Term-2: 30x27
1442                                         |------------------------------
1443                                         | Term-3: 30x(LINES-27)
1444         ---------------------------------------------------------------
1445         <----------Term-1: (COLS)x7----------------------------------->
1446         Notice the effect on the bottom terminal by specifying its argument
1447         second or first. Notice the sequence numbers for the various terminals
1448         as you will have to blindly configure them in the window setup screen.
1449         EDIT: Added support for -left and -top.
1450     */
1451     {
1452         rect_t remaining = rect(0, 0, COLS, LINES);
1453         int spacer_cx = 1;
1454         int spacer_cy = 1;
1455         int next_term = 1;
1456         int term_ct = 1;
1457
1458         for (i = 1; i < argc; i++) {
1459             if (streq(argv[i], "-spacer")) {
1460                 i++;
1461                 if (i >= argc)
1462                     quit("Missing size specifier for -spacer");
1463                 sscanf(argv[i], "%dx%d", &spacer_cx, &spacer_cy);
1464             } else if (streq(argv[i], "-right") || streq(argv[i], "-left")) {
1465                 const char *arg, *tmp;
1466                 bool left = streq(argv[i], "-left");
1467                 int cx, cys[MAX_TERM_DATA] = { 0 }, ct, j, x, y;
1468
1469                 i++;
1470                 if (i >= argc)
1471                     quit(format("Missing size specifier for -%s", left ? "left" : "right"));
1472
1473                 arg = argv[i];
1474                 tmp = strchr(arg, 'x');
1475                 if (!tmp)
1476                     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"));
1477                 cx = atoi(arg);
1478                 remaining.cx -= cx;
1479                 if (left) {
1480                     x = remaining.x;
1481                     y = remaining.y;
1482                     remaining.x += cx;
1483                 } else {
1484                     x = remaining.x + remaining.cx;
1485                     y = remaining.y;
1486                 }
1487                 remaining.cx -= spacer_cx;
1488                 if (left)
1489                     remaining.x += spacer_cx;
1490
1491                 tmp++;
1492                 ct = _parse_size_list(tmp, cys, MAX_TERM_DATA);
1493                 for (j = 0; j < ct; j++) {
1494                     int cy = cys[j];
1495                     if (y + cy > remaining.y + remaining.cy)
1496                         cy = remaining.y + remaining.cy - y;
1497                     if (next_term >= MAX_TERM_DATA)
1498                         quit(format("Too many terminals. Only %d are allowed.", MAX_TERM_DATA));
1499                     if (cy <= 0) {
1500                         quit(format("Out of bounds in -%s: %d is too large (%d rows max for this strip)",
1501                             left ? "left" : "right", cys[j], remaining.cy));
1502                     }
1503                     data[next_term++].r = rect(x, y, cx, cy);
1504                     y += cy + spacer_cy;
1505                     term_ct++;
1506                 }
1507             } else if (streq(argv[i], "-top") || streq(argv[i], "-bottom")) {
1508                 const char *arg, *tmp;
1509                 bool top = streq(argv[i], "-top");
1510                 int cy, cxs[MAX_TERM_DATA] = { 0 }, ct, j, x, y;
1511
1512                 i++;
1513                 if (i >= argc)
1514                     quit(format("Missing size specifier for -%s", top ? "top" : "bottom"));
1515
1516                 arg = argv[i];
1517                 tmp = strchr(arg, 'x');
1518                 if (!tmp)
1519                     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"));
1520                 tmp++;
1521                 cy = atoi(tmp);
1522                 ct = _parse_size_list(arg, cxs, MAX_TERM_DATA);
1523
1524                 remaining.cy -= cy;
1525                 if (top) {
1526                     x = remaining.x;
1527                     y = remaining.y;
1528                     remaining.y += cy;
1529                 } else {
1530                     x = remaining.x;
1531                     y = remaining.y + remaining.cy;
1532                 }
1533                 remaining.cy -= spacer_cy;
1534                 if (top)
1535                     remaining.y += spacer_cy;
1536
1537                 tmp++;
1538                 for (j = 0; j < ct; j++) {
1539                     int cx = cxs[j];
1540                     if (x + cx > remaining.x + remaining.cx)
1541                         cx = remaining.x + remaining.cx - x;
1542                     if (next_term >= MAX_TERM_DATA)
1543                         quit(format("Too many terminals. Only %d are allowed.", MAX_TERM_DATA));
1544                     if (cx <= 0) {
1545                         quit(format("Out of bounds in -%s: %d is too large (%d cols max for this strip)",
1546                             top ? "top" : "bottom", cxs[j], remaining.cx));
1547                     }
1548                     data[next_term++].r = rect(x, y, cx, cy);
1549                     x += cx + spacer_cx;
1550                     term_ct++;
1551                 }
1552             }
1553         }
1554
1555         /* Map Terminal */
1556         if (remaining.cx < MIN_TERM0_COLS || remaining.cy < MIN_TERM0_LINES)
1557             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);
1558         data[0].r = remaining;
1559         term_data_init(&data[0]);
1560         angband_term[0] = game_term;
1561
1562         /* Child Terminals */
1563         for (next_term = 1; next_term < term_ct; next_term++) {
1564             term_data_init(&data[next_term]);
1565             angband_term[next_term] = game_term;
1566         }
1567     }
1568
1569     /* Activate the "Angband" window screen */
1570     term_activate(&data[0].t);
1571
1572     /* Store */
1573     term_screen = &data[0].t;
1574
1575     /* Success */
1576     return 0;
1577 }
1578
1579 #endif /* USE_GCU */