OSDN Git Service

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