OSDN Git Service

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