OSDN Git Service

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