OSDN Git Service

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