OSDN Git Service

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