OSDN Git Service

[fix] gccの警告に従い修正
[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 "io/exit-panic.h"
161 #include "files.h"
162 #include "gameterm.h"
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 # include <curses.h>
175
176 typedef struct term_data term_data;
177
178 struct term_data
179 {
180    term t;
181
182    WINDOW *win;
183 };
184
185 #define MAX_TERM_DATA 4
186
187 static term_data data[MAX_TERM_DATA];
188
189
190 /*
191  * Hack -- try to guess which systems use what commands
192  * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
193  * Mega-Hack -- try to guess when "POSIX" is available.
194  * If the user defines two of these, we will probably crash.
195  */
196 #if !defined(USE_TERMIO) && !defined(USE_TCHARS)
197 # if defined(_POSIX_VERSION)
198 #  define USE_TPOSIX
199 # else
200 #  if defined(linux)
201 #   define USE_TERMIO
202 #  else
203 #   define USE_TCHARS
204 #  endif
205 # endif
206 #endif
207
208 /*
209  * Try redefining the colors at startup.
210  */
211 #define REDEFINE_COLORS
212
213 /*
214  * POSIX stuff
215  */
216 #ifdef USE_TPOSIX
217 # include <sys/ioctl.h>
218 # include <termios.h>
219 #endif
220
221 /*
222  * One version needs this file
223  */
224 #ifdef USE_TERMIO
225 # include <sys/ioctl.h>
226 # include <termio.h>
227 #endif
228
229 /*
230  * The other needs this file
231  */
232 #ifdef USE_TCHARS
233 # include <sys/ioctl.h>
234 # include <sys/resource.h>
235 # include <sys/param.h>
236 # include <sys/file.h>
237 # include <sys/types.h>
238 #endif
239
240 #include <locale.h>
241
242 /*
243  * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
244  *
245  * They should both work due to the "(i != 1)" test below.
246  */
247 #ifndef O_NDELAY
248 # define O_NDELAY O_NONBLOCK
249 #endif
250
251 /*
252  * OPTION: some machines lack "cbreak()"
253  * On these machines, we use an older definition
254  */
255 /* #define cbreak() crmode() */
256
257 /*
258  * OPTION: some machines cannot handle "nonl()" and "nl()"
259  * On these machines, we can simply ignore those commands.
260  */
261 /* #define nonl() */
262 /* #define nl() */
263
264  /*
265   * Standard sound names
266   */
267 static const concptr angband_sound_name[SOUND_MAX] =
268 {
269         "dummy",
270         "hit",
271         "miss",
272         "flee",
273         "drop",
274         "kill",
275         "level",
276         "death",
277         "study",
278         "teleport",
279         "shoot",
280         "quaff",
281         "zap",
282         "walk",
283         "tpother",
284         "hitwall",
285         "eat",
286         "store1",
287         "store2",
288         "store3",
289         "store4",
290         "dig",
291         "opendoor",
292         "shutdoor",
293         "tplevel",
294         "scroll",
295         "buy",
296         "sell",
297         "warn",
298         "rocket",
299         "n_kill",
300         "u_kill",
301         "quest",
302         "heal",
303         "x_heal",
304         "bite",
305         "claw",
306         "m_spell",
307         "summon",
308         "breath",
309         "ball",
310         "m_heal",
311         "atkspell",
312         "evil",
313         "touch",
314         "sting",
315         "crush",
316         "slime",
317         "wail",
318         "winner",
319         "fire",
320         "acid",
321         "elec",
322         "cold",
323         "illegal",
324         "fail",
325         "wakeup",
326         "invuln",
327         "fall",
328         "pain",
329         "destitem",
330         "moan",
331         "show",
332         "unused",
333         "explode",
334         "glass",
335         "reflect",
336 };
337
338 static concptr ANGBAND_DIR_XTRA_SOUND;
339
340 /*
341  * todo 有効活用されていない疑惑
342  * Flag set once "sound" has been initialized
343  */
344 static bool can_use_sound = FALSE;
345
346 /*
347  * An array of sound file names
348  */
349 static concptr sound_file[SOUND_MAX];
350
351 /*
352  * Save the "normal" and "angband" terminal settings
353  */
354
355 #ifdef USE_TPOSIX
356
357 static struct termios  norm_termios;
358
359 static struct termios  game_termios;
360
361 #endif
362
363 #ifdef USE_TERMIO
364
365 static struct termio  norm_termio;
366
367 static struct termio  game_termio;
368
369 #endif
370
371 #ifdef USE_TCHARS
372 static struct ltchars norm_speciax_chars;
373 static struct sgttyb  norm_ttyb;
374 static struct tchars  norm_tchars;
375 static int            norm_locax_chars;
376
377 static struct ltchars game_speciax_chars;
378 static struct sgttyb  game_ttyb;
379 static struct tchars  game_tchars;
380 static int            game_locax_chars;
381 #endif
382
383 /*
384  * Hack -- Number of initialized "term" structures
385  */
386 static int active = 0;
387
388 #ifdef A_COLOR
389 /*
390  * Hack -- define "A_BRIGHT" to be "A_BOLD", because on many
391  * machines, "A_BRIGHT" produces ugly "inverse" video.
392  */
393 #ifndef A_BRIGHT
394 # define A_BRIGHT A_BOLD
395 #endif
396
397 /*
398  * Software flag -- we are allowed to use color
399  */
400 static int can_use_color = FALSE;
401
402 /*
403  * Software flag -- we are allowed to change the colors
404  */
405 static int can_fix_color = FALSE;
406
407 /*
408  * Simple Angband to Curses color conversion table
409  */
410 static int colortable[16];
411 #endif
412
413 /*
414  * Place the "keymap" into its "normal" state
415  */
416 static void keymap_norm(void)
417 {
418 #ifdef USE_TPOSIX
419    /* restore the saved values of the special chars */
420    (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
421 #endif
422
423 #ifdef USE_TERMIO
424    /* restore the saved values of the special chars */
425    (void)ioctl(0, TCSETA, (char *)&norm_termio);
426 #endif
427
428 #ifdef USE_TCHARS
429    /* restore the saved values of the special chars */
430    (void)ioctl(0, TIOCSLTC, (char *)&norm_speciax_chars);
431    (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
432    (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
433    (void)ioctl(0, TIOCLSET, (char *)&norm_locax_chars);
434 #endif
435 }
436
437
438 /*
439  * Place the "keymap" into the "game" state
440  */
441 static void keymap_game(void)
442 {
443 #ifdef USE_TPOSIX
444    /* restore the saved values of the special chars */
445    (void)tcsetattr(0, TCSAFLUSH, &game_termios);
446 #endif
447
448 #ifdef USE_TERMIO
449    /* restore the saved values of the special chars */
450    (void)ioctl(0, TCSETA, (char *)&game_termio);
451 #endif
452
453 #ifdef USE_TCHARS
454    /* restore the saved values of the special chars */
455    (void)ioctl(0, TIOCSLTC, (char *)&game_speciax_chars);
456    (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
457    (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
458    (void)ioctl(0, TIOCLSET, (char *)&game_locax_chars);
459 #endif
460 }
461
462
463 /*
464  * Save the normal keymap
465  */
466 static void keymap_norm_prepare(void)
467 {
468 #ifdef USE_TPOSIX
469    /* Get the normal keymap */
470    tcgetattr(0, &norm_termios);
471 #endif
472
473 #ifdef USE_TERMIO
474    /* Get the normal keymap */
475    (void)ioctl(0, TCGETA, (char *)&norm_termio);
476 #endif
477
478 #ifdef USE_TCHARS
479    /* Get the normal keymap */
480    (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
481    (void)ioctl(0, TIOCGLTC, (char *)&norm_speciax_chars);
482    (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
483    (void)ioctl(0, TIOCLGET, (char *)&norm_locax_chars);
484 #endif
485 }
486
487
488 /*
489  * Save the keymaps (normal and game)
490  */
491 static void keymap_game_prepare(void)
492 {
493 #ifdef USE_TPOSIX
494    /* Acquire the current mapping */
495    tcgetattr(0, &game_termios);
496
497    /* Force "Ctrl-C" to interupt */
498    game_termios.c_cc[VINTR] = (char)3;
499
500    /* Force "Ctrl-Z" to suspend */
501    game_termios.c_cc[VSUSP] = (char)26;
502
503    /* Hack -- Leave "VSTART/VSTOP" alone */
504
505    /* Disable the standard control characters */
506    game_termios.c_cc[VQUIT] = (char)-1;
507    game_termios.c_cc[VERASE] = (char)-1;
508    game_termios.c_cc[VKILL] = (char)-1;
509    game_termios.c_cc[VEOF] = (char)-1;
510    game_termios.c_cc[VEOL] = (char)-1;
511
512    /* Normally, block until a character is read */
513    game_termios.c_cc[VMIN] = 1;
514    game_termios.c_cc[VTIME] = 0;
515 #endif
516
517 #ifdef USE_TERMIO
518    /* Acquire the current mapping */
519    (void)ioctl(0, TCGETA, (char *)&game_termio);
520
521    /* Force "Ctrl-C" to interupt */
522    game_termio.c_cc[VINTR] = (char)3;
523
524    /* Force "Ctrl-Z" to suspend */
525    game_termio.c_cc[VSUSP] = (char)26;
526
527    /* Disable the standard control characters */
528    game_termio.c_cc[VQUIT] = (char)-1;
529    game_termio.c_cc[VERASE] = (char)-1;
530    game_termio.c_cc[VKILL] = (char)-1;
531    game_termio.c_cc[VEOF] = (char)-1;
532    game_termio.c_cc[VEOL] = (char)-1;
533
534    /* Normally, block until a character is read */
535    game_termio.c_cc[VMIN] = 1;
536    game_termio.c_cc[VTIME] = 0;
537 #endif
538
539 #ifdef USE_TCHARS
540    /* Get the default game characters */
541    (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
542    (void)ioctl(0, TIOCGLTC, (char *)&game_speciax_chars);
543    (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
544    (void)ioctl(0, TIOCLGET, (char *)&game_locax_chars);
545
546    /* Force suspend (^Z) */
547    game_speciax_chars.t_suspc = (char)26;
548
549    /* Cancel some things */
550    game_speciax_chars.t_dsuspc = (char)-1;
551    game_speciax_chars.t_rprntc = (char)-1;
552    game_speciax_chars.t_flushc = (char)-1;
553    game_speciax_chars.t_werasc = (char)-1;
554    game_speciax_chars.t_lnextc = (char)-1;
555
556    /* Force interupt (^C) */
557    game_tchars.t_intrc = (char)3;
558
559    /* Force start/stop (^Q, ^S) */
560    game_tchars.t_startc = (char)17;
561    game_tchars.t_stopc = (char)19;
562
563    /* Cancel some things */
564    game_tchars.t_quitc = (char)-1;
565    game_tchars.t_eofc = (char)-1;
566    game_tchars.t_brkc = (char)-1;
567 #endif
568 }
569
570
571
572 /*
573  * Suspend/Resume
574  */
575 static errr Term_xtra_gcu_alive(int v)
576 {
577    if (!v)
578    {
579       /* Go to normal keymap mode */
580       keymap_norm();
581
582       /* Restore modes */
583       nocbreak();
584       echo();
585       nl();
586
587       /* Hack -- make sure the cursor is visible */
588       Term_xtra(TERM_XTRA_SHAPE, 1);
589
590       /* Flush the curses buffer */
591       (void)refresh();
592
593       /* this moves curses to bottom right corner */
594       mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
595
596       /* Exit curses */
597       endwin();
598
599       /* Flush the output */
600       (void)fflush(stdout);
601    }
602    else
603    {
604       /* Restore the settings */
605       cbreak();
606       noecho();
607       nonl();
608
609       /* Go to angband keymap mode */
610       keymap_game();
611    }
612
613    return (0);
614 }
615
616
617 /*
618  * Check for existance of a file
619  */
620 static bool check_file(concptr s)
621 {
622    FILE *fff;
623    fff = fopen(s, "r");
624    if (!fff) return (FALSE);
625
626    fclose(fff);
627    return (TRUE);
628 }
629
630
631 /*
632  * Initialize sound
633  */
634 static bool init_sound(void)
635 {
636    /* Initialize once */
637         if (can_use_sound) return can_use_sound;
638
639         int i;
640         char wav[128];
641         char buf[1024];
642
643         /* Prepare the sounds */
644         for (i = 1; i < SOUND_MAX; i++)
645         {
646                 /* Extract name of sound file */
647                 sprintf(wav, "%s.wav", angband_sound_name[i]);
648
649                 /* Access the sound */
650                 path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_SOUND, wav);
651
652                 /* Save the sound filename, if it exists */
653                 if (check_file(buf)) sound_file[i] = string_make(buf);
654         }
655
656         /* Sound available */
657         can_use_sound = TRUE;
658         return (can_use_sound);
659 }
660
661
662 /*
663  * Init the "curses" system
664  */
665 static void Term_init_gcu(term *t)
666 {
667    term_data *td = (term_data *)(t->data);
668
669    /* Count init's, handle first */
670    if (active++ != 0) return;
671
672    /* Erase the screen */
673    (void)wclear(td->win);
674
675    /* Reset the cursor */
676    (void)wmove(td->win, 0, 0);
677
678    /* Flush changes */
679    (void)wrefresh(td->win);
680
681    /* Game keymap */
682    keymap_game();
683 }
684
685
686 /*
687  * Nuke the "curses" system
688  */
689 static void Term_nuke_gcu(term *t)
690 {
691    term_data *td = (term_data *)(t->data);
692
693    /* Delete this window */
694    delwin(td->win);
695
696    /* Count nuke's, handle last */
697    if (--active != 0) return;
698
699    /* Hack -- make sure the cursor is visible */
700    Term_xtra(TERM_XTRA_SHAPE, 1);
701
702 #ifdef A_COLOR
703   /* Reset colors to defaults */
704   start_color();
705 #endif
706
707    /* This moves curses to bottom right corner */
708    mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
709
710    /* Flush the curses buffer */
711    (void)refresh();
712
713    /* Exit curses */
714    endwin();
715
716    /* Flush the output */
717    (void)fflush(stdout);
718
719    /* Normal keymap */
720    keymap_norm();
721 }
722
723 #ifdef USE_GETCH
724
725 /*
726  * Process events, with optional wait
727  */
728 static errr Term_xtra_gcu_event(int v)
729 {
730    int i, k;
731
732    /* Wait */
733    if (v)
734    {
735       /* Paranoia -- Wait for it */
736       nodelay(stdscr, FALSE);
737
738       /* Get a keypress */
739       i = getch();
740
741       /* Mega-Hack -- allow graceful "suspend" */
742       for (k = 0; (k < 10) && (i == ERR); k++) i = getch();
743
744       /* Broken input is special */
745       if (i == ERR) exit_game_panic(p_ptr);
746       if (i == EOF) exit_game_panic(p_ptr);
747    }
748
749    /* Do not wait */
750    else
751    {
752       /* Do not wait for it */
753       nodelay(stdscr, TRUE);
754
755       /* Check for keypresses */
756       i = getch();
757
758       /* Wait for it next time */
759       nodelay(stdscr, FALSE);
760
761       /* None ready */
762       if (i == ERR) return (1);
763       if (i == EOF) return (1);
764    }
765
766    /* Enqueue the keypress */
767    Term_keypress(i);
768
769    /* Success */
770    return (0);
771 }
772
773 #else /* USE_GETCH */
774
775 /*
776  * Process events (with optional wait)
777  */
778 static errr Term_xtra_gcu_event(int v)
779 {
780    int i, k;
781
782    char buf[2];
783
784    /* Wait */
785    if (v)
786    {
787       /* Wait for one byte */
788       i = read(0, buf, 1);
789
790       /* Hack -- Handle bizarre "errors" */
791       if ((i <= 0) && (errno != EINTR)) exit_game_panic(p_ptr);
792    }
793
794    /* Do not wait */
795    else
796    {
797       /* Get the current flags for stdin */
798       k = fcntl(0, F_GETFL, 0);
799
800       /* Oops */
801       if (k < 0) return (1);
802
803       /* Tell stdin not to block */
804       if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) return (1);
805
806       /* Read one byte, if possible */
807       i = read(0, buf, 1);
808
809       /* Replace the flags for stdin */
810       if (fcntl(0, F_SETFL, k)) return (1);
811    }
812
813    /* Ignore "invalid" keys */
814    if ((i != 1) || (!buf[0])) return (1);
815
816    /* Enqueue the keypress */
817    Term_keypress(buf[0]);
818
819    /* Success */
820    return (0);
821 }
822
823 #endif   /* USE_GETCH */
824
825 /*
826  * Hack -- make a sound
827  */
828 static errr Term_xtra_gcu_sound(int v)
829 {
830    char buf[1024];
831
832    /* Sound disabled */
833    if (!use_sound) return (1);
834
835    /* Illegal sound */
836    if ((v < 0) || (v >= SOUND_MAX)) return (1);
837
838    /* Unknown sound */
839    if (!sound_file[v]) return (1);
840
841    sprintf(buf,"./gcusound.sh %s\n", sound_file[v]);
842    
843    return (system(buf) < 0);
844
845    return (0);
846
847 }
848
849
850 /*
851  * React to changes
852  */
853 static errr Term_xtra_gcu_react(void)
854 {
855
856 #ifdef A_COLOR
857
858         int i;
859
860         /* Cannot handle color redefinition */
861         if (!can_fix_color) return (0);
862
863         /* Set the colors */
864         for (i = 0; i < 16; i++)
865         {
866                 /* Set one color (note scaling) */
867                 init_color(i, angband_color_table[i][1] * 1000 / 255,
868                               angband_color_table[i][2] * 1000 / 255,
869                               angband_color_table[i][3] * 1000 / 255);
870         }
871
872 #endif
873
874         /* Success */
875         return (0);
876 }
877
878
879 /*
880  * Handle a "special request"
881  */
882 static errr Term_xtra_gcu(int n, int v)
883 {
884    term_data *td = (term_data *)(Term->data);
885
886    /* Analyze the request */
887    switch (n)
888    {
889       /* Clear screen */
890       case TERM_XTRA_CLEAR:
891       touchwin(td->win);
892       (void)wclear(td->win);
893       return (0);
894
895       /* Make a noise */
896       case TERM_XTRA_NOISE:
897       return write(1, "\007", 1) != 1;
898
899       /* Make a special sound */
900       case TERM_XTRA_SOUND:
901          return (Term_xtra_gcu_sound(v));
902
903       /* Flush the Curses buffer */
904       case TERM_XTRA_FRESH:
905       (void)wrefresh(td->win);
906       return (0);
907
908 #ifdef USE_CURS_SET
909
910       /* Change the cursor visibility */
911       case TERM_XTRA_SHAPE:
912       curs_set(v);
913       return (0);
914
915 #endif
916
917       /* Suspend/Resume curses */
918       case TERM_XTRA_ALIVE:
919       return (Term_xtra_gcu_alive(v));
920
921       /* Process events */
922       case TERM_XTRA_EVENT:
923       return (Term_xtra_gcu_event(v));
924
925       /* Flush events */
926       case TERM_XTRA_FLUSH:
927       while (!Term_xtra_gcu_event(FALSE));
928       return (0);
929
930       /* Delay */
931       case TERM_XTRA_DELAY:
932       usleep(1000 * v);
933       return (0);
934
935       /* React to events */
936       case TERM_XTRA_REACT:
937       Term_xtra_gcu_react();
938       return (0);
939
940    }
941
942
943    /* Unknown */
944    return (1);
945 }
946
947
948 /*
949  * Actually MOVE the hardware cursor
950  */
951 static errr Term_curs_gcu(int x, int y)
952 {
953    term_data *td = (term_data *)(Term->data);
954
955    /* Literally move the cursor */
956    wmove(td->win, y, x);
957
958    /* Success */
959    return (0);
960 }
961
962
963 /*
964  * Erase a grid of space
965  * Hack -- try to be "semi-efficient".
966  */
967 static errr Term_wipe_gcu(int x, int y, int n)
968 {
969    term_data *td = (term_data *)(Term->data);
970
971    /* Place cursor */
972    wmove(td->win, y, x);
973
974    /* Clear to end of line */
975    if (x + n >= 80)
976    {
977       wclrtoeol(td->win);
978    }
979
980    /* Clear some characters */
981    else
982    {
983       while (n-- > 0) waddch(td->win, ' ');
984    }
985
986    /* Success */
987    return (0);
988 }
989
990 #ifdef USE_NCURSES_ACS
991 /*
992  * this function draws some ACS characters on the screen
993  * for DOS-based users: these are the graphical chars (blocks, lines etc)
994  *
995  * unix-gurus: before you start adding other attributes like A_REVERSE
996  * think hard about how map_info() in cave.c should handle the color
997  * of something that we here draw in reverse. It's not so simple, alas.
998  */
999 static void Term_acs_text_gcu(int x, int y, int n, byte a, concptr s)
1000 {
1001    term_data *td = (term_data *)(Term->data);
1002    int i;
1003
1004    /* position the cursor */
1005    wmove(td->win, y, x);
1006
1007 #ifdef A_COLOR
1008    /* Set the color */
1009    wattrset(td->win, colortable[a & 0x0F]);
1010 #endif
1011
1012    for (i=0; i < n; i++)
1013    {
1014       /* add acs_map of a */
1015       waddch(td->win, acs_map[(int)s[i]]);
1016    }
1017    wattrset(td->win, WA_NORMAL);
1018 }
1019 #endif
1020
1021 /*
1022  * Place some text on the screen using an attribute
1023  */
1024 static errr Term_text_gcu(int x, int y, int n, byte a, concptr s)
1025 {
1026    term_data *td = (term_data *)(Term->data);
1027
1028    int i;
1029
1030    char text[81];
1031
1032 #ifdef USE_NCURSES_ACS
1033    /* do we have colors + 16 ? */
1034    /* then call special routine for drawing special characters */
1035    if (a & 0x10)
1036    {
1037       Term_acs_text_gcu(x, y, n, a, s);
1038       return(0);
1039    }
1040 #endif
1041
1042    /* Obtain a copy of the text */
1043    for (i = 0; i < n; i++) text[i] = s[i];
1044    text[n] = 0;
1045
1046    /* Move the cursor and dump the string */
1047    wmove(td->win, y, x);
1048
1049 #ifdef A_COLOR
1050    /* Set the color */
1051    if (can_use_color) wattrset(td->win, colortable[a & 0x0F]);
1052 #endif
1053
1054    /* Add the text */
1055    waddstr(td->win, text);
1056
1057    /* Success */
1058    return (0);
1059 }
1060
1061
1062
1063 static errr term_data_init(term_data *td, int rows, int cols, int y, int x)
1064 {
1065    term *t = &td->t;
1066
1067    /* Make sure the window has a positive size */
1068    if (rows <= 0 || cols <= 0) return (0);
1069
1070    /* Create a window */
1071    td->win = newwin(rows, cols, y, x);
1072
1073    /* Make sure we succeed */
1074    if (!td->win)
1075    {
1076       plog("Failed to setup curses window.");
1077       return (-1);
1078    }
1079
1080    /* Initialize the term */
1081    term_init(t, cols, rows, 256);
1082
1083    /* Avoid the bottom right corner */
1084    t->icky_corner = TRUE;
1085
1086    /* Erase with "white space" */
1087    t->attr_blank = TERM_WHITE;
1088    t->char_blank = ' ';
1089
1090    /* Set some hooks */
1091    t->init_hook = Term_init_gcu;
1092    t->nuke_hook = Term_nuke_gcu;
1093
1094    /* Set some more hooks */
1095    t->text_hook = Term_text_gcu;
1096    t->wipe_hook = Term_wipe_gcu;
1097    t->curs_hook = Term_curs_gcu;
1098    t->xtra_hook = Term_xtra_gcu;
1099
1100    /* Save the data */
1101    t->data = td;
1102
1103    /* Activate it */
1104    Term_activate(t);
1105
1106
1107    /* Success */
1108    return (0);
1109 }
1110
1111
1112 static void hook_quit(concptr str)
1113 {
1114         /* Unused */
1115         (void)str;
1116
1117        /* Exit curses */
1118        endwin();
1119 }
1120
1121
1122 /*
1123  * Prepare "curses" for use by the file "term.c"
1124  *
1125  * Installs the "hook" functions defined above, and then activates
1126  * the main screen "term", which clears the screen and such things.
1127  *
1128  * Someone should really check the semantics of "initscr()"
1129  */
1130 errr init_gcu(int argc, char *argv[])
1131 {
1132    int i;
1133
1134    int num_term = 4, next_win = 0;
1135    char path[1024];
1136
1137    /* Unused */
1138    (void)argc;
1139    (void)argv;
1140
1141
1142    setlocale(LC_ALL, "");
1143
1144    /* Build the "sound" path */
1145    path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
1146
1147    /* Allocate the path */
1148    ANGBAND_DIR_XTRA_SOUND = string_make(path);
1149
1150    /* Extract the normal keymap */
1151    keymap_norm_prepare();
1152
1153    /* Initialize for others systems */
1154    if (initscr() == (WINDOW*)ERR) return (-1);
1155
1156    /* Activate hooks */
1157    quit_aux = hook_quit;
1158    core_aux = hook_quit;
1159
1160    /* Hack -- Require large screen, or Quit with message */
1161    i = ((LINES < 24) || (COLS < 80));
1162    if (i) quit("Angband needs an 80x24 'curses' screen");
1163
1164
1165 #ifdef A_COLOR
1166
1167    /*** Init the Color-pairs and set up a translation table ***/
1168
1169    /* Do we have color, and enough color, available? */
1170    can_use_color = ((start_color() != ERR) && has_colors() &&
1171                     (COLORS >= 8) && (COLOR_PAIRS >= 8));
1172
1173 #ifdef REDEFINE_COLORS
1174         /* Can we change colors? */
1175         can_fix_color = (can_use_color && can_change_color() &&
1176                          (COLORS >= 16) && (COLOR_PAIRS > 8));
1177 #endif
1178
1179    /* Attempt to use customized colors */
1180    if (can_fix_color)
1181    {
1182       /* Prepare the color pairs */
1183            for (i = 1; i <= 15; i++)
1184            {
1185                    if (init_pair(i, i, 0) == ERR)
1186                    {
1187                            quit("Color pair init failed");
1188                    }
1189
1190                    colortable[i] = COLOR_PAIR(i);
1191                    Term_xtra_gcu_react();
1192            }
1193    }
1194    /* Attempt to use colors */
1195    else if (can_use_color)
1196    {
1197                 /* Color-pair 0 is *always* WHITE on BLACK */
1198
1199                 /* Prepare the color pairs */
1200                 init_pair(1, COLOR_RED,     COLOR_BLACK);
1201                 init_pair(2, COLOR_GREEN,   COLOR_BLACK);
1202                 init_pair(3, COLOR_YELLOW,  COLOR_BLACK);
1203                 init_pair(4, COLOR_BLUE,    COLOR_BLACK);
1204                 init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
1205                 init_pair(6, COLOR_CYAN,    COLOR_BLACK);
1206                 init_pair(7, COLOR_BLACK,   COLOR_BLACK);
1207
1208                 /* Prepare the "Angband Colors" -- Bright white is too bright */
1209                 /* Changed in Drangband. Cyan as grey sucks -- -TM- */
1210                 colortable[0] = (COLOR_PAIR(7) | A_NORMAL);     /* Black */
1211                 colortable[1] = (COLOR_PAIR(0) | A_BRIGHT);     /* White */
1212                 colortable[2] = (COLOR_PAIR(0) | A_NORMAL);     /* Grey XXX */
1213                 colortable[3] = (COLOR_PAIR(1) | A_BRIGHT);     /* Orange XXX */
1214                 colortable[4] = (COLOR_PAIR(1) | A_NORMAL);     /* Red */
1215                 colortable[5] = (COLOR_PAIR(2) | A_NORMAL);     /* Green */
1216                 colortable[6] = (COLOR_PAIR(4) | A_BRIGHT);     /* Blue */
1217                 colortable[7] = (COLOR_PAIR(3) | A_NORMAL);     /* Umber */
1218                 colortable[8] = (COLOR_PAIR(7) | A_BRIGHT);     /* Dark-grey XXX */
1219                 colortable[9] = (COLOR_PAIR(0) | A_NORMAL);     /* Light-grey XXX */
1220                 colortable[10] = (COLOR_PAIR(5) | A_BRIGHT);    /* Purple */
1221                 colortable[11] = (COLOR_PAIR(3) | A_BRIGHT);    /* Yellow */
1222                 colortable[12] = (COLOR_PAIR(5) | A_NORMAL);    /* Light Red XXX */
1223                 colortable[13] = (COLOR_PAIR(2) | A_BRIGHT);    /* Light Green */
1224                 colortable[14] = (COLOR_PAIR(6) | A_BRIGHT);    /* Light Blue */
1225                 colortable[15] = (COLOR_PAIR(3) | A_NORMAL);    /* Light Umber XXX */
1226
1227    }
1228
1229 #endif
1230
1231    /* Handle "arg_sound" */
1232    if (use_sound != arg_sound)
1233    {
1234       /* Initialize (if needed) */
1235       if (arg_sound && !init_sound())
1236       {
1237          /* Warning */
1238          plog("Cannot initialize sound!");
1239
1240          /* Cannot enable */
1241          arg_sound = FALSE;
1242       }
1243
1244       /* Change setting */
1245       use_sound = arg_sound;
1246    }
1247
1248    /* Try graphics */
1249    if (arg_graphics)
1250    {
1251       /* if USE_NCURSES_ACS is defined, we can do something with graphics in curses! */
1252 #ifdef USE_NCURSES_ACS
1253       use_graphics = TRUE;
1254 #endif
1255    }
1256
1257    /*** Low level preparation ***/
1258
1259 #ifdef USE_GETCH
1260
1261    /* Paranoia -- Assume no waiting */
1262    nodelay(stdscr, FALSE);
1263
1264 #endif
1265
1266    /* Prepare */
1267    cbreak();
1268    noecho();
1269    nonl();
1270    raw();
1271
1272    /* Extract the game keymap */
1273    keymap_game_prepare();
1274
1275
1276    /*** Now prepare the term(s) ***/
1277    for (i = 0; i < num_term; i++)
1278    {
1279       int rows, cols;
1280       int y, x;
1281
1282       switch (i)
1283       {
1284          /* Upper left */
1285          case 0: rows = 24;
1286             cols = 80;
1287             y = x = 0;
1288             break;
1289          /* Lower left */
1290          case 1: rows = LINES - 25;
1291             cols = 80;
1292             y = 24;
1293             x = 0;
1294             break;
1295          /* Upper right */
1296          case 2: rows = 24;
1297             cols = COLS - 81;
1298             y = 0;
1299             x = 81;
1300             break;
1301          /* Lower right */
1302          case 3: rows = LINES - 25;
1303             cols = COLS - 81;
1304             y = 24;
1305             x = 81;
1306             break;
1307          /* XXX */
1308          default: rows = cols = 0;
1309              y = x = 0;
1310              break;
1311       }
1312
1313       /* No non-windows */
1314       if (rows <= 0 || cols <= 0) continue;
1315
1316       /* Initialize */
1317       term_data_init(&data[next_win], rows, cols, y, x);
1318
1319       /* Store */
1320       angband_term[next_win] = Term;
1321
1322       next_win++;
1323    }
1324
1325    /* Activate the "Angband" window screen */
1326    Term_activate(&data[0].t);
1327
1328    /* Store */
1329    term_screen = &data[0].t;
1330
1331    /* Success */
1332    return (0);
1333 }
1334
1335
1336 #endif /* USE_GCU */