OSDN Git Service

[Refacotr] #37353 使われていないプリプロSPECIAL_BSDを削除 / Removed unused preprocessors SPECIAL_BSD
[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    /* Normally, block until a character is read */
598    game_termio.c_cc[VMIN] = 1;
599    game_termio.c_cc[VTIME] = 0;
600
601 #endif
602
603 #ifdef USE_TCHARS
604
605    /* Get the default game characters */
606    (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
607    (void)ioctl(0, TIOCGLTC, (char *)&game_speciax_chars);
608    (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
609    (void)ioctl(0, TIOCLGET, (char *)&game_locax_chars);
610
611    /* Force suspend (^Z) */
612    game_speciax_chars.t_suspc = (char)26;
613
614    /* Cancel some things */
615    game_speciax_chars.t_dsuspc = (char)-1;
616    game_speciax_chars.t_rprntc = (char)-1;
617    game_speciax_chars.t_flushc = (char)-1;
618    game_speciax_chars.t_werasc = (char)-1;
619    game_speciax_chars.t_lnextc = (char)-1;
620
621    /* Force interupt (^C) */
622    game_tchars.t_intrc = (char)3;
623
624    /* Force start/stop (^Q, ^S) */
625    game_tchars.t_startc = (char)17;
626    game_tchars.t_stopc = (char)19;
627
628    /* Cancel some things */
629    game_tchars.t_quitc = (char)-1;
630    game_tchars.t_eofc = (char)-1;
631    game_tchars.t_brkc = (char)-1;
632
633 #endif
634
635 }
636
637
638
639
640 /*
641  * Suspend/Resume
642  */
643 static errr Term_xtra_gcu_alive(int v)
644 {
645    /* Suspend */
646    if (!v)
647    {
648       /* Go to normal keymap mode */
649       keymap_norm();
650
651       /* Restore modes */
652       nocbreak();
653       echo();
654       nl();
655
656       /* Hack -- make sure the cursor is visible */
657       Term_xtra(TERM_XTRA_SHAPE, 1);
658
659       /* Flush the curses buffer */
660       (void)refresh();
661
662       /* this moves curses to bottom right corner */
663       mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
664
665       /* Exit curses */
666       endwin();
667
668       /* Flush the output */
669       (void)fflush(stdout);
670    }
671
672    /* Resume */
673    else
674    {
675       /* Refresh */
676       /* (void)touchwin(curscr); */
677       /* (void)wrefresh(curscr); */
678
679       /* Restore the settings */
680       cbreak();
681       noecho();
682       nonl();
683
684       /* Go to angband keymap mode */
685       keymap_game();
686    }
687
688    /* Success */
689    return (0);
690 }
691
692 /*
693  * Check for existance of a file
694  */
695 static bool check_file(concptr s)
696 {
697    FILE *fff;
698
699    fff = fopen(s, "r");
700    if (!fff) return (FALSE);
701
702    fclose(fff);
703    return (TRUE);
704 }
705
706
707 /*
708  * Initialize sound
709  */
710 static bool init_sound(void)
711 {
712    /* Initialize once */
713    if (!can_use_sound)
714    {
715       int i;
716
717       char wav[128];
718       char buf[1024];
719
720       /* Prepare the sounds */
721       for (i = 1; i < SOUND_MAX; i++)
722       {
723          /* Extract name of sound file */
724          sprintf(wav, "%s.wav", angband_sound_name[i]);
725
726          /* Access the sound */
727          path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_SOUND, wav);
728
729          /* Save the sound filename, if it exists */
730          if (check_file(buf)) sound_file[i] = string_make(buf);
731       }
732
733       /* Sound available */
734       can_use_sound = TRUE;
735    }
736
737    /* Result */
738    return (can_use_sound);
739 }
740
741
742 /*
743  * Init the "curses" system
744  */
745 static void Term_init_gcu(term *t)
746 {
747    term_data *td = (term_data *)(t->data);
748
749    /* Count init's, handle first */
750    if (active++ != 0) return;
751
752    /* Erase the screen */
753    (void)wclear(td->win);
754
755    /* Reset the cursor */
756    (void)wmove(td->win, 0, 0);
757
758    /* Flush changes */
759    (void)wrefresh(td->win);
760
761    /* Game keymap */
762    keymap_game();
763 }
764
765
766 /*
767  * Nuke the "curses" system
768  */
769 static void Term_nuke_gcu(term *t)
770 {
771    term_data *td = (term_data *)(t->data);
772
773    /* Delete this window */
774    delwin(td->win);
775
776    /* Count nuke's, handle last */
777    if (--active != 0) return;
778
779    /* Hack -- make sure the cursor is visible */
780    Term_xtra(TERM_XTRA_SHAPE, 1);
781
782 #ifdef A_COLOR
783   /* Reset colors to defaults */
784   start_color();
785 #endif
786
787    /* This moves curses to bottom right corner */
788    mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
789
790    /* Flush the curses buffer */
791    (void)refresh();
792
793    /* Exit curses */
794    endwin();
795
796    /* Flush the output */
797    (void)fflush(stdout);
798
799    /* Normal keymap */
800    keymap_norm();
801 }
802
803 #ifdef USE_GETCH
804
805 /*
806  * Process events, with optional wait
807  */
808 static errr Term_xtra_gcu_event(int v)
809 {
810    int i, k;
811
812    /* Wait */
813    if (v)
814    {
815       /* Paranoia -- Wait for it */
816       nodelay(stdscr, FALSE);
817
818       /* Get a keypress */
819       i = getch();
820
821       /* Mega-Hack -- allow graceful "suspend" */
822       for (k = 0; (k < 10) && (i == ERR); k++) i = getch();
823
824       /* Broken input is special */
825       if (i == ERR) exit_game_panic(p_ptr);
826       if (i == EOF) exit_game_panic(p_ptr);
827    }
828
829    /* Do not wait */
830    else
831    {
832       /* Do not wait for it */
833       nodelay(stdscr, TRUE);
834
835       /* Check for keypresses */
836       i = getch();
837
838       /* Wait for it next time */
839       nodelay(stdscr, FALSE);
840
841       /* None ready */
842       if (i == ERR) return (1);
843       if (i == EOF) return (1);
844    }
845
846    /* Enqueue the keypress */
847    Term_keypress(i);
848
849    /* Success */
850    return (0);
851 }
852
853 #else /* USE_GETCH */
854
855 /*
856  * Process events (with optional wait)
857  */
858 static errr Term_xtra_gcu_event(int v)
859 {
860    int i, k;
861
862    char buf[2];
863
864    /* Wait */
865    if (v)
866    {
867       /* Wait for one byte */
868       i = read(0, buf, 1);
869
870       /* Hack -- Handle bizarre "errors" */
871       if ((i <= 0) && (errno != EINTR)) exit_game_panic(p_ptr);
872    }
873
874    /* Do not wait */
875    else
876    {
877       /* Get the current flags for stdin */
878       k = fcntl(0, F_GETFL, 0);
879
880       /* Oops */
881       if (k < 0) return (1);
882
883       /* Tell stdin not to block */
884       if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) return (1);
885
886       /* Read one byte, if possible */
887       i = read(0, buf, 1);
888
889       /* Replace the flags for stdin */
890       if (fcntl(0, F_SETFL, k)) return (1);
891    }
892
893    /* Ignore "invalid" keys */
894    if ((i != 1) || (!buf[0])) return (1);
895
896    /* Enqueue the keypress */
897    Term_keypress(buf[0]);
898
899    /* Success */
900    return (0);
901 }
902
903 #endif   /* USE_GETCH */
904
905 /*
906  * Hack -- make a sound
907  */
908 static errr Term_xtra_gcu_sound(int v)
909 {
910    char buf[1024];
911
912    /* Sound disabled */
913    if (!use_sound) return (1);
914
915    /* Illegal sound */
916    if ((v < 0) || (v >= SOUND_MAX)) return (1);
917
918    /* Unknown sound */
919    if (!sound_file[v]) return (1);
920
921    sprintf(buf,"./gcusound.sh %s\n", sound_file[v]);
922    
923    return (system(buf) < 0);
924
925    return (0);
926
927 }
928
929
930 /*
931  * React to changes
932  */
933 static errr Term_xtra_gcu_react(void)
934 {
935
936 #ifdef A_COLOR
937
938         int i;
939
940         /* Cannot handle color redefinition */
941         if (!can_fix_color) return (0);
942
943         /* Set the colors */
944         for (i = 0; i < 16; i++)
945         {
946                 /* Set one color (note scaling) */
947                 init_color(i, angband_color_table[i][1] * 1000 / 255,
948                               angband_color_table[i][2] * 1000 / 255,
949                               angband_color_table[i][3] * 1000 / 255);
950         }
951
952 #endif
953
954         /* Success */
955         return (0);
956 }
957
958
959 /*
960  * Handle a "special request"
961  */
962 static errr Term_xtra_gcu(int n, int v)
963 {
964    term_data *td = (term_data *)(Term->data);
965
966    /* Analyze the request */
967    switch (n)
968    {
969       /* Clear screen */
970       case TERM_XTRA_CLEAR:
971       touchwin(td->win);
972       (void)wclear(td->win);
973       return (0);
974
975       /* Make a noise */
976       case TERM_XTRA_NOISE:
977       return write(1, "\007", 1) != 1;
978
979       /* Make a special sound */
980       case TERM_XTRA_SOUND:
981          return (Term_xtra_gcu_sound(v));
982
983       /* Flush the Curses buffer */
984       case TERM_XTRA_FRESH:
985       (void)wrefresh(td->win);
986       return (0);
987
988 #ifdef USE_CURS_SET
989
990       /* Change the cursor visibility */
991       case TERM_XTRA_SHAPE:
992       curs_set(v);
993       return (0);
994
995 #endif
996
997       /* Suspend/Resume curses */
998       case TERM_XTRA_ALIVE:
999       return (Term_xtra_gcu_alive(v));
1000
1001       /* Process events */
1002       case TERM_XTRA_EVENT:
1003       return (Term_xtra_gcu_event(v));
1004
1005       /* Flush events */
1006       case TERM_XTRA_FLUSH:
1007       while (!Term_xtra_gcu_event(FALSE));
1008       return (0);
1009
1010       /* Delay */
1011       case TERM_XTRA_DELAY:
1012       usleep(1000 * v);
1013       return (0);
1014
1015       /* React to events */
1016       case TERM_XTRA_REACT:
1017       Term_xtra_gcu_react();
1018       return (0);
1019
1020    }
1021
1022
1023    /* Unknown */
1024    return (1);
1025 }
1026
1027
1028 /*
1029  * Actually MOVE the hardware cursor
1030  */
1031 static errr Term_curs_gcu(int x, int y)
1032 {
1033    term_data *td = (term_data *)(Term->data);
1034
1035    /* Literally move the cursor */
1036    wmove(td->win, y, x);
1037
1038    /* Success */
1039    return (0);
1040 }
1041
1042
1043 /*
1044  * Erase a grid of space
1045  * Hack -- try to be "semi-efficient".
1046  */
1047 static errr Term_wipe_gcu(int x, int y, int n)
1048 {
1049    term_data *td = (term_data *)(Term->data);
1050
1051    /* Place cursor */
1052    wmove(td->win, y, x);
1053
1054    /* Clear to end of line */
1055    if (x + n >= 80)
1056    {
1057       wclrtoeol(td->win);
1058    }
1059
1060    /* Clear some characters */
1061    else
1062    {
1063       while (n-- > 0) waddch(td->win, ' ');
1064    }
1065
1066    /* Success */
1067    return (0);
1068 }
1069
1070 #ifdef USE_NCURSES_ACS
1071 /*
1072  * this function draws some ACS characters on the screen
1073  * for DOS-based users: these are the graphical chars (blocks, lines etc)
1074  *
1075  * unix-gurus: before you start adding other attributes like A_REVERSE
1076  * think hard about how map_info() in cave.c should handle the color
1077  * of something that we here draw in reverse. It's not so simple, alas.
1078  */
1079 static void Term_acs_text_gcu(int x, int y, int n, byte a, concptr s)
1080 {
1081    term_data *td = (term_data *)(Term->data);
1082    int i;
1083
1084    /* position the cursor */
1085    wmove(td->win, y, x);
1086
1087 #ifdef A_COLOR
1088    /* Set the color */
1089    wattrset(td->win, colortable[a & 0x0F]);
1090 #endif
1091
1092    for (i=0; i < n; i++)
1093    {
1094       /* add acs_map of a */
1095       waddch(td->win, acs_map[(int)s[i]]);
1096    }
1097    wattrset(td->win, WA_NORMAL);
1098 }
1099 #endif
1100
1101 /*
1102  * Place some text on the screen using an attribute
1103  */
1104 static errr Term_text_gcu(int x, int y, int n, byte a, concptr s)
1105 {
1106    term_data *td = (term_data *)(Term->data);
1107
1108    int i;
1109
1110    char text[81];
1111
1112 #ifdef USE_NCURSES_ACS
1113    /* do we have colors + 16 ? */
1114    /* then call special routine for drawing special characters */
1115    if (a & 0x10)
1116    {
1117       Term_acs_text_gcu(x, y, n, a, s);
1118       return(0);
1119    }
1120 #endif
1121
1122    /* Obtain a copy of the text */
1123    for (i = 0; i < n; i++) text[i] = s[i];    text[n] = 0;
1124
1125    /* Move the cursor and dump the string */
1126    wmove(td->win, y, x);
1127
1128 #ifdef A_COLOR
1129    /* Set the color */
1130    if (can_use_color) wattrset(td->win, colortable[a & 0x0F]);
1131 #endif
1132
1133    /* Add the text */
1134    waddstr(td->win, text);
1135
1136    /* Success */
1137    return (0);
1138 }
1139
1140
1141
1142 static errr term_data_init(term_data *td, int rows, int cols, int y, int x)
1143 {
1144    term *t = &td->t;
1145
1146    /* Make sure the window has a positive size */
1147    if (rows <= 0 || cols <= 0) return (0);
1148
1149    /* Create a window */
1150    td->win = newwin(rows, cols, y, x);
1151
1152    /* Make sure we succeed */
1153    if (!td->win)
1154    {
1155       plog("Failed to setup curses window.");
1156       return (-1);
1157    }
1158
1159    /* Initialize the term */
1160    term_init(t, cols, rows, 256);
1161
1162    /* Avoid the bottom right corner */
1163    t->icky_corner = TRUE;
1164
1165    /* Erase with "white space" */
1166    t->attr_blank = TERM_WHITE;
1167    t->char_blank = ' ';
1168
1169    /* Set some hooks */
1170    t->init_hook = Term_init_gcu;
1171    t->nuke_hook = Term_nuke_gcu;
1172
1173    /* Set some more hooks */
1174    t->text_hook = Term_text_gcu;
1175    t->wipe_hook = Term_wipe_gcu;
1176    t->curs_hook = Term_curs_gcu;
1177    t->xtra_hook = Term_xtra_gcu;
1178
1179    /* Save the data */
1180    t->data = td;
1181
1182    /* Activate it */
1183    Term_activate(t);
1184
1185
1186    /* Success */
1187    return (0);
1188 }
1189
1190
1191 static void hook_quit(concptr str)
1192 {
1193         /* Unused */
1194         (void)str;
1195
1196        /* Exit curses */
1197        endwin();
1198 }
1199
1200
1201 /*
1202  * Prepare "curses" for use by the file "term.c"
1203  *
1204  * Installs the "hook" functions defined above, and then activates
1205  * the main screen "term", which clears the screen and such things.
1206  *
1207  * Someone should really check the semantics of "initscr()"
1208  */
1209 errr init_gcu(int argc, char *argv[])
1210 {
1211    int i;
1212
1213    int num_term = 4, next_win = 0;
1214    char path[1024];
1215
1216    /* Unused */
1217    (void)argc;
1218    (void)argv;
1219
1220
1221    setlocale(LC_ALL, "");
1222
1223    /* Build the "sound" path */
1224    path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
1225
1226    /* Allocate the path */
1227    ANGBAND_DIR_XTRA_SOUND = string_make(path);
1228
1229    /* Extract the normal keymap */
1230    keymap_norm_prepare();
1231
1232    /* Initialize for others systems */
1233    if (initscr() == (WINDOW*)ERR) return (-1);
1234
1235    /* Activate hooks */
1236    quit_aux = hook_quit;
1237    core_aux = hook_quit;
1238
1239    /* Hack -- Require large screen, or Quit with message */
1240    i = ((LINES < 24) || (COLS < 80));
1241    if (i) quit("Angband needs an 80x24 'curses' screen");
1242
1243
1244 #ifdef A_COLOR
1245
1246    /*** Init the Color-pairs and set up a translation table ***/
1247
1248    /* Do we have color, and enough color, available? */
1249    can_use_color = ((start_color() != ERR) && has_colors() &&
1250                     (COLORS >= 8) && (COLOR_PAIRS >= 8));
1251
1252 #ifdef REDEFINE_COLORS
1253         /* Can we change colors? */
1254         can_fix_color = (can_use_color && can_change_color() &&
1255                          (COLORS >= 16) && (COLOR_PAIRS > 8));
1256 #endif
1257
1258    /* Attempt to use customized colors */
1259    if (can_fix_color)
1260    {
1261       /* Prepare the color pairs */
1262       for (i = 1; i <= 63; i++)
1263       {
1264          /* Reset the color */
1265          if (init_pair(i, (i - 1) % 8, (i - 1) / 8) == ERR)
1266          {
1267             quit("Color pair init failed");
1268          }
1269
1270         /* Set up the colormap */
1271         colortable[i - 1] = (COLOR_PAIR(i) | A_NORMAL);
1272         colortable[i + 7] = (COLOR_PAIR(i) | A_BRIGHT);
1273
1274         /* XXX XXX XXX Take account of "gamma correction" */
1275
1276         /* Prepare the "Angband Colors" */
1277         Term_xtra_gcu_react();
1278       }
1279    }
1280    /* Attempt to use colors */
1281    else if (can_use_color)
1282    {
1283                 /* Color-pair 0 is *always* WHITE on BLACK */
1284
1285                 /* Prepare the color pairs */
1286                 init_pair(1, COLOR_RED,     COLOR_BLACK);
1287                 init_pair(2, COLOR_GREEN,   COLOR_BLACK);
1288                 init_pair(3, COLOR_YELLOW,  COLOR_BLACK);
1289                 init_pair(4, COLOR_BLUE,    COLOR_BLACK);
1290                 init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
1291                 init_pair(6, COLOR_CYAN,    COLOR_BLACK);
1292                 init_pair(7, COLOR_BLACK,   COLOR_BLACK);
1293
1294                 /* Prepare the "Angband Colors" -- Bright white is too bright */
1295                 /* Changed in Drangband. Cyan as grey sucks -- -TM- */
1296                 colortable[0] = (COLOR_PAIR(7) | A_NORMAL);     /* Black */
1297                 colortable[1] = (COLOR_PAIR(0) | A_BRIGHT);     /* White */
1298                 colortable[2] = (COLOR_PAIR(0) | A_NORMAL);     /* Grey XXX */
1299                 colortable[3] = (COLOR_PAIR(1) | A_BRIGHT);     /* Orange XXX */
1300                 colortable[4] = (COLOR_PAIR(1) | A_NORMAL);     /* Red */
1301                 colortable[5] = (COLOR_PAIR(2) | A_NORMAL);     /* Green */
1302                 colortable[6] = (COLOR_PAIR(4) | A_BRIGHT);     /* Blue */
1303                 colortable[7] = (COLOR_PAIR(3) | A_NORMAL);     /* Umber */
1304                 colortable[8] = (COLOR_PAIR(7) | A_BRIGHT);     /* Dark-grey XXX */
1305                 colortable[9] = (COLOR_PAIR(0) | A_NORMAL);     /* Light-grey XXX */
1306                 colortable[10] = (COLOR_PAIR(5) | A_BRIGHT);    /* Purple */
1307                 colortable[11] = (COLOR_PAIR(3) | A_BRIGHT);    /* Yellow */
1308                 colortable[12] = (COLOR_PAIR(5) | A_NORMAL);    /* Light Red XXX */
1309                 colortable[13] = (COLOR_PAIR(2) | A_BRIGHT);    /* Light Green */
1310                 colortable[14] = (COLOR_PAIR(6) | A_BRIGHT);    /* Light Blue */
1311                 colortable[15] = (COLOR_PAIR(3) | A_NORMAL);    /* Light Umber XXX */
1312
1313    }
1314
1315 #endif
1316
1317    /* Handle "arg_sound" */
1318    if (use_sound != arg_sound)
1319    {
1320       /* Initialize (if needed) */
1321       if (arg_sound && !init_sound())
1322       {
1323          /* Warning */
1324          plog("Cannot initialize sound!");
1325
1326          /* Cannot enable */
1327          arg_sound = FALSE;
1328       }
1329
1330       /* Change setting */
1331       use_sound = arg_sound;
1332    }
1333
1334    /* Try graphics */
1335    if (arg_graphics)
1336    {
1337       /* if USE_NCURSES_ACS is defined, we can do something with graphics in curses! */
1338 #ifdef USE_NCURSES_ACS
1339       use_graphics = TRUE;
1340 #endif
1341    }
1342
1343    /*** Low level preparation ***/
1344
1345 #ifdef USE_GETCH
1346
1347    /* Paranoia -- Assume no waiting */
1348    nodelay(stdscr, FALSE);
1349
1350 #endif
1351
1352    /* Prepare */
1353    cbreak();
1354    noecho();
1355    nonl();
1356    raw();
1357
1358    /* Extract the game keymap */
1359    keymap_game_prepare();
1360
1361
1362    /*** Now prepare the term(s) ***/
1363    for (i = 0; i < num_term; i++)
1364    {
1365       int rows, cols;
1366       int y, x;
1367
1368       switch (i)
1369       {
1370          /* Upper left */
1371          case 0: rows = 24;
1372             cols = 80;
1373             y = x = 0;
1374             break;
1375          /* Lower left */
1376          case 1: rows = LINES - 25;
1377             cols = 80;
1378             y = 24;
1379             x = 0;
1380             break;
1381          /* Upper right */
1382          case 2: rows = 24;
1383             cols = COLS - 81;
1384             y = 0;
1385             x = 81;
1386             break;
1387          /* Lower right */
1388          case 3: rows = LINES - 25;
1389             cols = COLS - 81;
1390             y = 24;
1391             x = 81;
1392             break;
1393          /* XXX */
1394          default: rows = cols = 0;
1395              y = x = 0;
1396              break;
1397       }
1398
1399       /* No non-windows */
1400       if (rows <= 0 || cols <= 0) continue;
1401
1402       /* Initialize */
1403       term_data_init(&data[next_win], rows, cols, y, x);
1404
1405       /* Store */
1406       angband_term[next_win] = Term;
1407
1408       next_win++;
1409    }
1410
1411    /* Activate the "Angband" window screen */
1412    Term_activate(&data[0].t);
1413
1414    /* Store */
1415    term_screen = &data[0].t;
1416
1417    /* Success */
1418    return (0);
1419 }
1420
1421
1422 #endif /* USE_GCU */
1423
1424