OSDN Git Service

[Refactor] #37353 main-win.c 以外のmain*.cについて、'if 0'のプリプロを削除 / Removed preprocessor...
[hengband/hengband.git] / src / main-gcu.c
1 /* File: main-gcu.c */
2
3 /*
4  * Copyright (c) 1997 Ben Harrison, and others
5  *
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.
9  */
10
11 /* Purpose: Allow use of Unix "curses" with Angband -BEN- */
12
13 /*
14  * This file has been modified to use multiple text windows if your screen
15  * is larger than 80x25.  By Keldon Jones (keldon@umr.edu).
16  *
17  * Also included is Keldon Jones patch to get better colors. To switch to
18  * a term that supports this, see this posting:
19  *
20  * From keldon@umr.edu Thu Apr 01 05:40:14 1999
21  * Sender: KELDON JONES <keldon@saucer.cc.umr.edu>
22  * From: Keldon Jones <keldon@umr.edu>
23  * Subject: Re: Linux colour prob (Or: question for Greg)
24  * Newsgroups: rec.games.roguelike.angband
25  * References: <slrn7g1jlp.gj9.scarblac-spamtrap@flits104-37.flits.rug.nl> <3700f96b.1593384@news.polsl.gliwice.pl> <slrn7g36er.fm4.wooledge@jekyll.local>
26  * X-Newsreader: TIN [UNIX 1.3 unoff BETA 970625; 9000/780 HP-UX B.10.20]
27  * NNTP-Posting-Host: saucer.cc.umr.edu
28  * X-NNTP-Posting-Host: saucer.cc.umr.edu
29  * Message-ID: <370306be.0@news.cc.umr.edu>
30  * Date: 1 Apr 99 05:40:14 GMT
31  * Organization: University of Missouri - Rolla
32  * Lines: 199
33  * Path: xs4all!xs4all!newsfeed.wirehub.nl!news-peer.gip.net!news.gsl.net!gip.net!news.he.net!mercury.cts.com!alpha.sky.net!news.missouri.edu!news.cc.umr.edu!not-for-mail
34  * Xref: xs4all rec.games.roguelike.angband:86332
35  *
36  * Greg Wooledge <wooledge@kellnet.com> wrote:
37  * > Gwidon S. Naskrent (naskrent@artemida.amu.edu.pl) wrote:
38  *
39  * > >On 30 Mar 1999 13:17:18 GMT, scarblac-spamtrap@pino.selwerd.cx (Remco
40  * > >Gerlich) wrote:
41  *
42  * > >>I recently switched to Linux, and *bands work fine. I like
43  * > >>to play them in consoles, not in X. However, colour is wrong.
44  * > >>"Slate" and "light slate" are always light blue, instead
45  * > >>of some shade of grey. Colours are fine in X.
46  *
47  * > I actually noticed the Linux console color issue a very long time ago,
48  * > but since I always play under X, I never really investigated it.
49  *
50  * > You're absolutely right, though -- the Linux console colors are not
51  * > "right" for Angband.
52  *
53  *    I've noticed this myself, so I spent the evening fixing it.
54  * Well, sorta fixing it.  It's not perfect yet, and it may not be
55  * possible to get it perfect with VGA hardware and/or the current
56  * Linux kernel.
57  *
58  * > OK, reading on in terminfo(5):
59  *
60  * >    Color Handling
61  * >        Most color terminals are either `Tektronix-like'  or  `HP-
62  * >        like'.   Tektronix-like terminals have a predefined set of
63  * >        N colors (where N usually 8), and can  set  character-cell
64  * >        foreground and background characters independently, mixing
65  * >        them into N * N color-pairs.  On  HP-like  terminals,  the
66  * >        use must set each color pair up separately (foreground and
67  * >        background are  not  independently  settable).   Up  to  M
68  * >        color-pairs  may  be  set  up  from  2*M different colors.
69  * >        ANSI-compatible terminals are Tektronix-like.
70  *
71  * > The "linux" terminfo entry is definitely in the "Tektronix-like" family.
72  * > It has the "setaf" and "setab" capabilities for setting the foreground
73  * > and background colors to one of 8 basically hard-coded values:
74  *
75  * >              Color       #define       Value       RGB
76  * >              black     COLOR_BLACK       0     0, 0, 0
77  * >              red       COLOR_RED         1     max,0,0
78  * >              green     COLOR_GREEN       2     0,max,0
79  * >              yellow    COLOR_YELLOW      3     max,max,0
80  * >              blue      COLOR_BLUE        4     0,0,max
81  * >              magenta   COLOR_MAGENTA     5     max,0,max
82  * >              cyan      COLOR_CYAN        6     0,max,max
83  * >              white     COLOR_WHITE       7     max,max,max
84  *
85  *    Well, not quite.  Using certain escape sequences, an
86  * application (or better yet, curses) can redefine the colors (at
87  * least some of them) and then those are used.  Read the
88  * curs_color manpage, and the part about "ccc" and "initc" in the
89  * terminfo manpage.  This is what the part of main-gcu inside the
90  * "if (can_fix_color)" code does.
91  *
92  * > So, what does this mean to the Angband player?  Well, it means that
93  * > either there's nothing you can do about the console colors as long as
94  * > straight curses/ncurses is used, or if there is something to be done,
95  * > I'm not clever enough to figure out how to do it.
96  *
97  *    Well, it is possible, though you have to patch main-gcu
98  * and edit a terminfo entry.  Apparently the relevant code in
99  * main-gcu was never tested (it's broken in at least one major
100  * way).  Apply the patch at the end of this message (notice that
101  * we need to define REDEFINE_COLORS at some point near the
102  * beginning of the file).
103  *    Next, write this termcap entry to a file:
104  *
105  * linux-c|linux console 1.3.6+ with private palette for each virtual console,
106  *         ccc,
107  *         colors#16, pairs#64,
108  *         initc=\E]P%x%p1%{16}%/%02x%p1%{16}%/%02x%p1%{16}%/%02x,
109  *         oc=\E]R,
110  *         use=linux,
111  *
112  * and run "tic" on it to produce a new terminfo entry called
113  * "linux-c".  Especially note the "ccc" flag which says that we
114  * can redefine colors.  The ugly "initc" string is what tells
115  * the console how to redefine a color.  Now, just set your TERM
116  * variable to "linux-c" and try Angband again.  If I've
117  * remembered to tell you everything that I've done, you should
118  * get the weird light-blue slate changed to a gray.
119  *    Now, there are still lots of problems with this.
120  * Something (I don't think it's curses, either the kernel or
121  * the hardware itself) seems to be ignoring my color changes to
122  * colors 6 and 7, which is annoying.  Also, the normal "white"
123  * color is now way too bright, but it's now necessary to
124  * distinguish it from the other grays.
125  *    The kernel seems to support 16 colors, but you can
126  * only switch to 8 of those, due to VT102 compatibility, it
127  * seems.  I think it would be possible to patch the kernel and
128  * allow all 16 colors to be used, but I haven't built up the
129  * nerve to try that yet.
130  *    Let me know if you can improve on this any.  Some of
131  * this may actually work differently on other hardware (ugh).
132  *
133  *    Keldon
134  *
135  */
136
137 /*
138  * To use this file, you must define "USE_GCU" in the Makefile.
139  *
140  * Hack -- note that "angband.h" is included AFTER the #ifdef test.
141  * This was necessary because of annoying "curses.h" silliness.
142  *
143  * Note that this file is not "intended" to support non-Unix machines,
144  * nor is it intended to support VMS or other bizarre setups.
145  *
146  * Also, this package assumes that the underlying "curses" handles both
147  * the "nonl()" and "cbreak()" commands correctly, see the "OPTION" below.
148  *
149  * This code should work with most versions of "curses" or "ncurses",
150  * and the "main-ncu.c" file (and USE_NCU define) are no longer used.
151  *
152  * See also "USE_CAP" and "main-cap.c" for code that bypasses "curses"
153  * and uses the "termcap" information directly, or even bypasses the
154  * "termcap" information and sends direct vt100 escape sequences.
155  *
156  * XXX XXX XXX Consider the use of "savetty()" and "resetty()".
157  */
158
159 #include "angband.h"
160 #include "files.h"
161 #include "term.h"
162
163
164 #ifdef USE_GCU
165
166 /*
167  * Hack -- play games with "bool"
168  */
169 #undef bool
170
171 /*
172  * Include the proper "header" file
173  */
174 #ifdef USE_NCURSES
175 # undef bool
176 # include <ncurses.h>
177 #else
178 # include <curses.h>
179 #endif
180
181 typedef struct term_data term_data;
182
183 struct term_data
184 {
185    term t;
186
187    WINDOW *win;
188 };
189
190 #define MAX_TERM_DATA 4
191
192 static term_data data[MAX_TERM_DATA];
193
194
195 /*
196  * Hack -- try to guess which systems use what commands
197  * Hack -- allow one of the "USE_Txxxxx" flags to be pre-set.
198  * Mega-Hack -- try to guess when "POSIX" is available.
199  * If the user defines two of these, we will probably crash.
200  */
201 #if !defined(USE_TPOSIX)
202 # if !defined(USE_TERMIO) && !defined(USE_TCHARS)
203 #  if defined(_POSIX_VERSION)
204 #   define USE_TPOSIX
205 #  else
206 #   if defined(linux)
207 #    define USE_TERMIO
208 #   else
209 #    define USE_TCHARS
210 #   endif
211 #  endif
212 # endif
213 #endif
214
215 /*
216  * Hack -- Amiga uses "fake curses" and cannot do any of this stuff
217  */
218 #if defined(AMIGA)
219 # undef USE_TPOSIX
220 # undef USE_TERMIO
221 # undef USE_TCHARS
222 #endif
223
224 /*
225  * Try redefining the colors at startup.
226  */
227 #define REDEFINE_COLORS
228
229
230
231 /*
232  * POSIX stuff
233  */
234 #ifdef USE_TPOSIX
235 # include <sys/ioctl.h>
236 # include <termios.h>
237 #endif
238
239 /*
240  * One version needs this file
241  */
242 #ifdef USE_TERMIO
243 # include <sys/ioctl.h>
244 # include <termio.h>
245 #endif
246
247 /*
248  * The other needs this file
249  */
250 #ifdef USE_TCHARS
251 # include <sys/ioctl.h>
252 # include <sys/resource.h>
253 # include <sys/param.h>
254 # include <sys/file.h>
255 # include <sys/types.h>
256 #endif
257
258
259 #include <locale.h>
260
261
262 /*
263  * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
264  *
265  * They should both work due to the "(i != 1)" test below.
266  */
267 #ifndef O_NDELAY
268 # define O_NDELAY O_NONBLOCK
269 #endif
270
271
272 /*
273  * OPTION: some machines lack "cbreak()"
274  * On these machines, we use an older definition
275  */
276 /* #define cbreak() crmode() */
277
278
279 /*
280  * OPTION: some machines cannot handle "nonl()" and "nl()"
281  * On these machines, we can simply ignore those commands.
282  */
283 /* #define nonl() */
284 /* #define nl() */
285
286
287  /*
288   * Standard sound names
289   */
290 static const concptr angband_sound_name[SOUND_MAX] =
291 {
292         "dummy",
293         "hit",
294         "miss",
295         "flee",
296         "drop",
297         "kill",
298         "level",
299         "death",
300         "study",
301         "teleport",
302         "shoot",
303         "quaff",
304         "zap",
305         "walk",
306         "tpother",
307         "hitwall",
308         "eat",
309         "store1",
310         "store2",
311         "store3",
312         "store4",
313         "dig",
314         "opendoor",
315         "shutdoor",
316         "tplevel",
317         "scroll",
318         "buy",
319         "sell",
320         "warn",
321         "rocket",
322         "n_kill",
323         "u_kill",
324         "quest",
325         "heal",
326         "x_heal",
327         "bite",
328         "claw",
329         "m_spell",
330         "summon",
331         "breath",
332         "ball",
333         "m_heal",
334         "atkspell",
335         "evil",
336         "touch",
337         "sting",
338         "crush",
339         "slime",
340         "wail",
341         "winner",
342         "fire",
343         "acid",
344         "elec",
345         "cold",
346         "illegal",
347         "fail",
348         "wakeup",
349         "invuln",
350         "fall",
351         "pain",
352         "destitem",
353         "moan",
354         "show",
355         "unused",
356         "explode",
357         "glass",
358         "reflect",
359 };
360
361 static concptr ANGBAND_DIR_XTRA_SOUND;
362
363 /*
364  * Flag set once "sound" has been initialized
365  */
366 static bool can_use_sound = FALSE;
367
368 /*
369  * An array of sound file names
370  */
371 static concptr sound_file[SOUND_MAX];
372
373
374 /*
375  * Save the "normal" and "angband" terminal settings
376  */
377
378 #ifdef USE_TPOSIX
379
380 static struct termios  norm_termios;
381
382 static struct termios  game_termios;
383
384 #endif
385
386 #ifdef USE_TERMIO
387
388 static struct termio  norm_termio;
389
390 static struct termio  game_termio;
391
392 #endif
393
394 #ifdef USE_TCHARS
395
396 static struct ltchars norm_speciax_chars;
397 static struct sgttyb  norm_ttyb;
398 static struct tchars  norm_tchars;
399 static int            norm_locax_chars;
400
401 static struct ltchars game_speciax_chars;
402 static struct sgttyb  game_ttyb;
403 static struct tchars  game_tchars;
404 static int            game_locax_chars;
405
406 #endif
407
408
409
410 /*
411  * Hack -- Number of initialized "term" structures
412  */
413 static int active = 0;
414
415
416
417 #ifdef A_COLOR
418
419 /*
420  * Hack -- define "A_BRIGHT" to be "A_BOLD", because on many
421  * machines, "A_BRIGHT" produces ugly "inverse" video.
422  */
423 #ifndef A_BRIGHT
424 # define A_BRIGHT A_BOLD
425 #endif
426
427 /*
428  * Software flag -- we are allowed to use color
429  */
430 static int can_use_color = FALSE;
431
432 /*
433  * Software flag -- we are allowed to change the colors
434  */
435 static int can_fix_color = FALSE;
436
437 /*
438  * Simple Angband to Curses color conversion table
439  */
440 static int colortable[16];
441
442 #endif
443
444
445
446 /*
447  * Place the "keymap" into its "normal" state
448  */
449 static void keymap_norm(void)
450 {
451
452 #ifdef USE_TPOSIX
453
454    /* restore the saved values of the special chars */
455    (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
456
457 #endif
458
459 #ifdef USE_TERMIO
460
461    /* restore the saved values of the special chars */
462    (void)ioctl(0, TCSETA, (char *)&norm_termio);
463
464 #endif
465
466 #ifdef USE_TCHARS
467
468    /* restore the saved values of the special chars */
469    (void)ioctl(0, TIOCSLTC, (char *)&norm_speciax_chars);
470    (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
471    (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
472    (void)ioctl(0, TIOCLSET, (char *)&norm_locax_chars);
473
474 #endif
475
476 }
477
478
479 /*
480  * Place the "keymap" into the "game" state
481  */
482 static void keymap_game(void)
483 {
484
485 #ifdef USE_TPOSIX
486
487    /* restore the saved values of the special chars */
488    (void)tcsetattr(0, TCSAFLUSH, &game_termios);
489
490 #endif
491
492 #ifdef USE_TERMIO
493
494    /* restore the saved values of the special chars */
495    (void)ioctl(0, TCSETA, (char *)&game_termio);
496
497 #endif
498
499 #ifdef USE_TCHARS
500
501    /* restore the saved values of the special chars */
502    (void)ioctl(0, TIOCSLTC, (char *)&game_speciax_chars);
503    (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
504    (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
505    (void)ioctl(0, TIOCLSET, (char *)&game_locax_chars);
506
507 #endif
508
509 }
510
511
512 /*
513  * Save the normal keymap
514  */
515 static void keymap_norm_prepare(void)
516 {
517
518 #ifdef USE_TPOSIX
519
520    /* Get the normal keymap */
521    tcgetattr(0, &norm_termios);
522
523 #endif
524
525 #ifdef USE_TERMIO
526
527    /* Get the normal keymap */
528    (void)ioctl(0, TCGETA, (char *)&norm_termio);
529
530 #endif
531
532 #ifdef USE_TCHARS
533
534    /* Get the normal keymap */
535    (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
536    (void)ioctl(0, TIOCGLTC, (char *)&norm_speciax_chars);
537    (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
538    (void)ioctl(0, TIOCLGET, (char *)&norm_locax_chars);
539
540 #endif
541
542 }
543
544
545 /*
546  * Save the keymaps (normal and game)
547  */
548 static void keymap_game_prepare(void)
549 {
550
551 #ifdef USE_TPOSIX
552
553    /* Acquire the current mapping */
554    tcgetattr(0, &game_termios);
555
556    /* Force "Ctrl-C" to interupt */
557    game_termios.c_cc[VINTR] = (char)3;
558
559    /* Force "Ctrl-Z" to suspend */
560    game_termios.c_cc[VSUSP] = (char)26;
561
562    /* Hack -- Leave "VSTART/VSTOP" alone */
563
564    /* Disable the standard control characters */
565    game_termios.c_cc[VQUIT] = (char)-1;
566    game_termios.c_cc[VERASE] = (char)-1;
567    game_termios.c_cc[VKILL] = (char)-1;
568    game_termios.c_cc[VEOF] = (char)-1;
569    game_termios.c_cc[VEOL] = (char)-1;
570
571    /* Normally, block until a character is read */
572    game_termios.c_cc[VMIN] = 1;
573    game_termios.c_cc[VTIME] = 0;
574
575 #endif
576
577 #ifdef USE_TERMIO
578
579    /* Acquire the current mapping */
580    (void)ioctl(0, TCGETA, (char *)&game_termio);
581
582    /* Force "Ctrl-C" to interupt */
583    game_termio.c_cc[VINTR] = (char)3;
584
585    /* Force "Ctrl-Z" to suspend */
586    game_termio.c_cc[VSUSP] = (char)26;
587
588    /* Hack -- Leave "VSTART/VSTOP" alone */
589
590    /* Disable the standard control characters */
591    game_termio.c_cc[VQUIT] = (char)-1;
592    game_termio.c_cc[VERASE] = (char)-1;
593    game_termio.c_cc[VKILL] = (char)-1;
594    game_termio.c_cc[VEOF] = (char)-1;
595    game_termio.c_cc[VEOL] = (char)-1;
596
597    /* Normally, block until a character is read */
598    game_termio.c_cc[VMIN] = 1;
599    game_termio.c_cc[VTIME] = 0;
600
601 #endif
602
603 #ifdef USE_TCHARS
604
605    /* Get the default game characters */
606    (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
607    (void)ioctl(0, TIOCGLTC, (char *)&game_speciax_chars);
608    (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
609    (void)ioctl(0, TIOCLGET, (char *)&game_locax_chars);
610
611    /* Force suspend (^Z) */
612    game_speciax_chars.t_suspc = (char)26;
613
614    /* Cancel some things */
615    game_speciax_chars.t_dsuspc = (char)-1;
616    game_speciax_chars.t_rprntc = (char)-1;
617    game_speciax_chars.t_flushc = (char)-1;
618    game_speciax_chars.t_werasc = (char)-1;
619    game_speciax_chars.t_lnextc = (char)-1;
620
621    /* Force interupt (^C) */
622    game_tchars.t_intrc = (char)3;
623
624    /* Force start/stop (^Q, ^S) */
625    game_tchars.t_startc = (char)17;
626    game_tchars.t_stopc = (char)19;
627
628    /* Cancel some things */
629    game_tchars.t_quitc = (char)-1;
630    game_tchars.t_eofc = (char)-1;
631    game_tchars.t_brkc = (char)-1;
632
633 #endif
634
635 }
636
637
638
639
640 /*
641  * Suspend/Resume
642  */
643 static errr Term_xtra_gcu_alive(int v)
644 {
645    /* Suspend */
646    if (!v)
647    {
648       /* Go to normal keymap mode */
649       keymap_norm();
650
651       /* Restore modes */
652       nocbreak();
653       echo();
654       nl();
655
656       /* Hack -- make sure the cursor is visible */
657       Term_xtra(TERM_XTRA_SHAPE, 1);
658
659       /* Flush the curses buffer */
660       (void)refresh();
661
662 #ifdef SPECIAL_BSD
663       /* this moves curses to bottom right corner */
664       mvcur(curscr->cury, curscr->curx, LINES - 1, 0);
665 #else
666       /* this moves curses to bottom right corner */
667       mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
668 #endif
669
670       /* Exit curses */
671       endwin();
672
673       /* Flush the output */
674       (void)fflush(stdout);
675    }
676
677    /* Resume */
678    else
679    {
680       /* Refresh */
681       /* (void)touchwin(curscr); */
682       /* (void)wrefresh(curscr); */
683
684       /* Restore the settings */
685       cbreak();
686       noecho();
687       nonl();
688
689       /* Go to angband keymap mode */
690       keymap_game();
691    }
692
693    /* Success */
694    return (0);
695 }
696
697 /*
698  * Check for existance of a file
699  */
700 static bool check_file(concptr s)
701 {
702    FILE *fff;
703
704    fff = fopen(s, "r");
705    if (!fff) return (FALSE);
706
707    fclose(fff);
708    return (TRUE);
709 }
710
711
712 /*
713  * Initialize sound
714  */
715 static bool init_sound(void)
716 {
717    /* Initialize once */
718    if (!can_use_sound)
719    {
720       int i;
721
722       char wav[128];
723       char buf[1024];
724
725       /* Prepare the sounds */
726       for (i = 1; i < SOUND_MAX; i++)
727       {
728          /* Extract name of sound file */
729          sprintf(wav, "%s.wav", angband_sound_name[i]);
730
731          /* Access the sound */
732          path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_SOUND, wav);
733
734          /* Save the sound filename, if it exists */
735          if (check_file(buf)) sound_file[i] = string_make(buf);
736       }
737
738       /* Sound available */
739       can_use_sound = TRUE;
740    }
741
742    /* Result */
743    return (can_use_sound);
744 }
745
746
747 /*
748  * Init the "curses" system
749  */
750 static void Term_init_gcu(term *t)
751 {
752    term_data *td = (term_data *)(t->data);
753
754    /* Count init's, handle first */
755    if (active++ != 0) return;
756
757    /* Erase the screen */
758    (void)wclear(td->win);
759
760    /* Reset the cursor */
761    (void)wmove(td->win, 0, 0);
762
763    /* Flush changes */
764    (void)wrefresh(td->win);
765
766    /* Game keymap */
767    keymap_game();
768 }
769
770
771 /*
772  * Nuke the "curses" system
773  */
774 static void Term_nuke_gcu(term *t)
775 {
776    term_data *td = (term_data *)(t->data);
777
778    /* Delete this window */
779    delwin(td->win);
780
781    /* Count nuke's, handle last */
782    if (--active != 0) return;
783
784    /* Hack -- make sure the cursor is visible */
785    Term_xtra(TERM_XTRA_SHAPE, 1);
786
787 #ifdef A_COLOR
788   /* Reset colors to defaults */
789   start_color();
790 #endif
791
792 #ifdef SPECIAL_BSD
793    /* This moves curses to bottom right corner */
794    mvcur(curscr->cury, curscr->curx, LINES - 1, 0);
795 #else
796    /* This moves curses to bottom right corner */
797    mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
798 #endif
799
800    /* Flush the curses buffer */
801    (void)refresh();
802
803    /* Exit curses */
804    endwin();
805
806    /* Flush the output */
807    (void)fflush(stdout);
808
809    /* Normal keymap */
810    keymap_norm();
811 }
812
813 #ifdef USE_GETCH
814
815 /*
816  * Process events, with optional wait
817  */
818 static errr Term_xtra_gcu_event(int v)
819 {
820    int i, k;
821
822    /* Wait */
823    if (v)
824    {
825       /* Paranoia -- Wait for it */
826       nodelay(stdscr, FALSE);
827
828       /* Get a keypress */
829       i = getch();
830
831       /* Mega-Hack -- allow graceful "suspend" */
832       for (k = 0; (k < 10) && (i == ERR); k++) i = getch();
833
834       /* Broken input is special */
835       if (i == ERR) exit_game_panic(p_ptr);
836       if (i == EOF) exit_game_panic(p_ptr);
837    }
838
839    /* Do not wait */
840    else
841    {
842       /* Do not wait for it */
843       nodelay(stdscr, TRUE);
844
845       /* Check for keypresses */
846       i = getch();
847
848       /* Wait for it next time */
849       nodelay(stdscr, FALSE);
850
851       /* None ready */
852       if (i == ERR) return (1);
853       if (i == EOF) return (1);
854    }
855
856    /* Enqueue the keypress */
857    Term_keypress(i);
858
859    /* Success */
860    return (0);
861 }
862
863 #else /* USE_GETCH */
864
865 /*
866  * Process events (with optional wait)
867  */
868 static errr Term_xtra_gcu_event(int v)
869 {
870    int i, k;
871
872    char buf[2];
873
874    /* Wait */
875    if (v)
876    {
877       /* Wait for one byte */
878       i = read(0, buf, 1);
879
880       /* Hack -- Handle bizarre "errors" */
881       if ((i <= 0) && (errno != EINTR)) exit_game_panic(p_ptr);
882    }
883
884    /* Do not wait */
885    else
886    {
887       /* Get the current flags for stdin */
888       k = fcntl(0, F_GETFL, 0);
889
890       /* Oops */
891       if (k < 0) return (1);
892
893       /* Tell stdin not to block */
894       if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) return (1);
895
896       /* Read one byte, if possible */
897       i = read(0, buf, 1);
898
899       /* Replace the flags for stdin */
900       if (fcntl(0, F_SETFL, k)) return (1);
901    }
902
903    /* Ignore "invalid" keys */
904    if ((i != 1) || (!buf[0])) return (1);
905
906    /* Enqueue the keypress */
907    Term_keypress(buf[0]);
908
909    /* Success */
910    return (0);
911 }
912
913 #endif   /* USE_GETCH */
914
915 /*
916  * Hack -- make a sound
917  */
918 static errr Term_xtra_gcu_sound(int v)
919 {
920    char buf[1024];
921
922    /* Sound disabled */
923    if (!use_sound) return (1);
924
925    /* Illegal sound */
926    if ((v < 0) || (v >= SOUND_MAX)) return (1);
927
928    /* Unknown sound */
929    if (!sound_file[v]) return (1);
930
931    sprintf(buf,"./gcusound.sh %s\n", sound_file[v]);
932    
933    return (system(buf) < 0);
934
935    return (0);
936
937 }
938
939
940 /*
941  * React to changes
942  */
943 static errr Term_xtra_gcu_react(void)
944 {
945
946 #ifdef A_COLOR
947
948         int i;
949
950         /* Cannot handle color redefinition */
951         if (!can_fix_color) return (0);
952
953         /* Set the colors */
954         for (i = 0; i < 16; i++)
955         {
956                 /* Set one color (note scaling) */
957                 init_color(i, angband_color_table[i][1] * 1000 / 255,
958                               angband_color_table[i][2] * 1000 / 255,
959                               angband_color_table[i][3] * 1000 / 255);
960         }
961
962 #endif
963
964         /* Success */
965         return (0);
966 }
967
968
969 /*
970  * Handle a "special request"
971  */
972 static errr Term_xtra_gcu(int n, int v)
973 {
974    term_data *td = (term_data *)(Term->data);
975
976    /* Analyze the request */
977    switch (n)
978    {
979       /* Clear screen */
980       case TERM_XTRA_CLEAR:
981       touchwin(td->win);
982       (void)wclear(td->win);
983       return (0);
984
985       /* Make a noise */
986       case TERM_XTRA_NOISE:
987       return write(1, "\007", 1) != 1;
988
989       /* Make a special sound */
990       case TERM_XTRA_SOUND:
991          return (Term_xtra_gcu_sound(v));
992
993       /* Flush the Curses buffer */
994       case TERM_XTRA_FRESH:
995       (void)wrefresh(td->win);
996       return (0);
997
998 #ifdef USE_CURS_SET
999
1000       /* Change the cursor visibility */
1001       case TERM_XTRA_SHAPE:
1002       curs_set(v);
1003       return (0);
1004
1005 #endif
1006
1007       /* Suspend/Resume curses */
1008       case TERM_XTRA_ALIVE:
1009       return (Term_xtra_gcu_alive(v));
1010
1011       /* Process events */
1012       case TERM_XTRA_EVENT:
1013       return (Term_xtra_gcu_event(v));
1014
1015       /* Flush events */
1016       case TERM_XTRA_FLUSH:
1017       while (!Term_xtra_gcu_event(FALSE));
1018       return (0);
1019
1020       /* Delay */
1021       case TERM_XTRA_DELAY:
1022       usleep(1000 * v);
1023       return (0);
1024
1025       /* React to events */
1026       case TERM_XTRA_REACT:
1027       Term_xtra_gcu_react();
1028       return (0);
1029
1030    }
1031
1032
1033    /* Unknown */
1034    return (1);
1035 }
1036
1037
1038 /*
1039  * Actually MOVE the hardware cursor
1040  */
1041 static errr Term_curs_gcu(int x, int y)
1042 {
1043    term_data *td = (term_data *)(Term->data);
1044
1045    /* Literally move the cursor */
1046    wmove(td->win, y, x);
1047
1048    /* Success */
1049    return (0);
1050 }
1051
1052
1053 /*
1054  * Erase a grid of space
1055  * Hack -- try to be "semi-efficient".
1056  */
1057 static errr Term_wipe_gcu(int x, int y, int n)
1058 {
1059    term_data *td = (term_data *)(Term->data);
1060
1061    /* Place cursor */
1062    wmove(td->win, y, x);
1063
1064    /* Clear to end of line */
1065    if (x + n >= 80)
1066    {
1067       wclrtoeol(td->win);
1068    }
1069
1070    /* Clear some characters */
1071    else
1072    {
1073       while (n-- > 0) waddch(td->win, ' ');
1074    }
1075
1076    /* Success */
1077    return (0);
1078 }
1079
1080 #ifdef USE_NCURSES_ACS
1081 /*
1082  * this function draws some ACS characters on the screen
1083  * for DOS-based users: these are the graphical chars (blocks, lines etc)
1084  *
1085  * unix-gurus: before you start adding other attributes like A_REVERSE
1086  * think hard about how map_info() in cave.c should handle the color
1087  * of something that we here draw in reverse. It's not so simple, alas.
1088  */
1089 static void Term_acs_text_gcu(int x, int y, int n, byte a, concptr s)
1090 {
1091    term_data *td = (term_data *)(Term->data);
1092    int i;
1093
1094    /* position the cursor */
1095    wmove(td->win, y, x);
1096
1097 #ifdef A_COLOR
1098    /* Set the color */
1099    wattrset(td->win, colortable[a & 0x0F]);
1100 #endif
1101
1102    for (i=0; i < n; i++)
1103    {
1104       /* add acs_map of a */
1105       waddch(td->win, acs_map[(int)s[i]]);
1106    }
1107    wattrset(td->win, WA_NORMAL);
1108 }
1109 #endif
1110
1111 /*
1112  * Place some text on the screen using an attribute
1113  */
1114 static errr Term_text_gcu(int x, int y, int n, byte a, concptr s)
1115 {
1116    term_data *td = (term_data *)(Term->data);
1117
1118    int i;
1119
1120    char text[81];
1121
1122 #ifdef USE_NCURSES_ACS
1123    /* do we have colors + 16 ? */
1124    /* then call special routine for drawing special characters */
1125    if (a & 0x10)
1126    {
1127       Term_acs_text_gcu(x, y, n, a, s);
1128       return(0);
1129    }
1130 #endif
1131
1132    /* Obtain a copy of the text */
1133    for (i = 0; i < n; i++) text[i] = s[i];    text[n] = 0;
1134
1135    /* Move the cursor and dump the string */
1136    wmove(td->win, y, x);
1137
1138 #ifdef A_COLOR
1139    /* Set the color */
1140    if (can_use_color) wattrset(td->win, colortable[a & 0x0F]);
1141 #endif
1142
1143    /* Add the text */
1144    waddstr(td->win, text);
1145
1146    /* Success */
1147    return (0);
1148 }
1149
1150
1151
1152 static errr term_data_init(term_data *td, int rows, int cols, int y, int x)
1153 {
1154    term *t = &td->t;
1155
1156    /* Make sure the window has a positive size */
1157    if (rows <= 0 || cols <= 0) return (0);
1158
1159    /* Create a window */
1160    td->win = newwin(rows, cols, y, x);
1161
1162    /* Make sure we succeed */
1163    if (!td->win)
1164    {
1165       plog("Failed to setup curses window.");
1166       return (-1);
1167    }
1168
1169    /* Initialize the term */
1170    term_init(t, cols, rows, 256);
1171
1172    /* Avoid the bottom right corner */
1173    t->icky_corner = TRUE;
1174
1175    /* Erase with "white space" */
1176    t->attr_blank = TERM_WHITE;
1177    t->char_blank = ' ';
1178
1179    /* Set some hooks */
1180    t->init_hook = Term_init_gcu;
1181    t->nuke_hook = Term_nuke_gcu;
1182
1183    /* Set some more hooks */
1184    t->text_hook = Term_text_gcu;
1185    t->wipe_hook = Term_wipe_gcu;
1186    t->curs_hook = Term_curs_gcu;
1187    t->xtra_hook = Term_xtra_gcu;
1188
1189    /* Save the data */
1190    t->data = td;
1191
1192    /* Activate it */
1193    Term_activate(t);
1194
1195
1196    /* Success */
1197    return (0);
1198 }
1199
1200
1201 static void hook_quit(concptr str)
1202 {
1203         /* Unused */
1204         (void)str;
1205
1206        /* Exit curses */
1207        endwin();
1208 }
1209
1210
1211 /*
1212  * Prepare "curses" for use by the file "term.c"
1213  *
1214  * Installs the "hook" functions defined above, and then activates
1215  * the main screen "term", which clears the screen and such things.
1216  *
1217  * Someone should really check the semantics of "initscr()"
1218  */
1219 errr init_gcu(int argc, char *argv[])
1220 {
1221    int i;
1222
1223    int num_term = 4, next_win = 0;
1224    char path[1024];
1225
1226    /* Unused */
1227    (void)argc;
1228    (void)argv;
1229
1230
1231    setlocale(LC_ALL, "");
1232
1233    /* Build the "sound" path */
1234    path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
1235
1236    /* Allocate the path */
1237    ANGBAND_DIR_XTRA_SOUND = string_make(path);
1238
1239    /* Extract the normal keymap */
1240    keymap_norm_prepare();
1241
1242    /* Initialize for others systems */
1243    if (initscr() == (WINDOW*)ERR) return (-1);
1244
1245    /* Activate hooks */
1246    quit_aux = hook_quit;
1247    core_aux = hook_quit;
1248
1249    /* Hack -- Require large screen, or Quit with message */
1250    i = ((LINES < 24) || (COLS < 80));
1251    if (i) quit("Angband needs an 80x24 'curses' screen");
1252
1253
1254 #ifdef A_COLOR
1255
1256    /*** Init the Color-pairs and set up a translation table ***/
1257
1258    /* Do we have color, and enough color, available? */
1259    can_use_color = ((start_color() != ERR) && has_colors() &&
1260                     (COLORS >= 8) && (COLOR_PAIRS >= 8));
1261
1262 #ifdef REDEFINE_COLORS
1263         /* Can we change colors? */
1264         can_fix_color = (can_use_color && can_change_color() &&
1265                          (COLORS >= 16) && (COLOR_PAIRS > 8));
1266 #endif
1267
1268    /* Attempt to use customized colors */
1269    if (can_fix_color)
1270    {
1271       /* Prepare the color pairs */
1272       for (i = 1; i <= 63; i++)
1273       {
1274          /* Reset the color */
1275          if (init_pair(i, (i - 1) % 8, (i - 1) / 8) == ERR)
1276          {
1277             quit("Color pair init failed");
1278          }
1279
1280         /* Set up the colormap */
1281         colortable[i - 1] = (COLOR_PAIR(i) | A_NORMAL);
1282         colortable[i + 7] = (COLOR_PAIR(i) | A_BRIGHT);
1283
1284         /* XXX XXX XXX Take account of "gamma correction" */
1285
1286         /* Prepare the "Angband Colors" */
1287         Term_xtra_gcu_react();
1288       }
1289    }
1290    /* Attempt to use colors */
1291    else if (can_use_color)
1292    {
1293                 /* Color-pair 0 is *always* WHITE on BLACK */
1294
1295                 /* Prepare the color pairs */
1296                 init_pair(1, COLOR_RED,     COLOR_BLACK);
1297                 init_pair(2, COLOR_GREEN,   COLOR_BLACK);
1298                 init_pair(3, COLOR_YELLOW,  COLOR_BLACK);
1299                 init_pair(4, COLOR_BLUE,    COLOR_BLACK);
1300                 init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
1301                 init_pair(6, COLOR_CYAN,    COLOR_BLACK);
1302                 init_pair(7, COLOR_BLACK,   COLOR_BLACK);
1303
1304                 /* Prepare the "Angband Colors" -- Bright white is too bright */
1305                 /* Changed in Drangband. Cyan as grey sucks -- -TM- */
1306                 colortable[0] = (COLOR_PAIR(7) | A_NORMAL);     /* Black */
1307                 colortable[1] = (COLOR_PAIR(0) | A_BRIGHT);     /* White */
1308                 colortable[2] = (COLOR_PAIR(0) | A_NORMAL);     /* Grey XXX */
1309                 colortable[3] = (COLOR_PAIR(1) | A_BRIGHT);     /* Orange XXX */
1310                 colortable[4] = (COLOR_PAIR(1) | A_NORMAL);     /* Red */
1311                 colortable[5] = (COLOR_PAIR(2) | A_NORMAL);     /* Green */
1312                 colortable[6] = (COLOR_PAIR(4) | A_BRIGHT);     /* Blue */
1313                 colortable[7] = (COLOR_PAIR(3) | A_NORMAL);     /* Umber */
1314                 colortable[8] = (COLOR_PAIR(7) | A_BRIGHT);     /* Dark-grey XXX */
1315                 colortable[9] = (COLOR_PAIR(0) | A_NORMAL);     /* Light-grey XXX */
1316                 colortable[10] = (COLOR_PAIR(5) | A_BRIGHT);    /* Purple */
1317                 colortable[11] = (COLOR_PAIR(3) | A_BRIGHT);    /* Yellow */
1318                 colortable[12] = (COLOR_PAIR(5) | A_NORMAL);    /* Light Red XXX */
1319                 colortable[13] = (COLOR_PAIR(2) | A_BRIGHT);    /* Light Green */
1320                 colortable[14] = (COLOR_PAIR(6) | A_BRIGHT);    /* Light Blue */
1321                 colortable[15] = (COLOR_PAIR(3) | A_NORMAL);    /* Light Umber XXX */
1322
1323    }
1324
1325 #endif
1326
1327    /* Handle "arg_sound" */
1328    if (use_sound != arg_sound)
1329    {
1330       /* Initialize (if needed) */
1331       if (arg_sound && !init_sound())
1332       {
1333          /* Warning */
1334          plog("Cannot initialize sound!");
1335
1336          /* Cannot enable */
1337          arg_sound = FALSE;
1338       }
1339
1340       /* Change setting */
1341       use_sound = arg_sound;
1342    }
1343
1344    /* Try graphics */
1345    if (arg_graphics)
1346    {
1347       /* if USE_NCURSES_ACS is defined, we can do something with graphics in curses! */
1348 #ifdef USE_NCURSES_ACS
1349       use_graphics = TRUE;
1350 #endif
1351    }
1352
1353    /*** Low level preparation ***/
1354
1355 #ifdef USE_GETCH
1356
1357    /* Paranoia -- Assume no waiting */
1358    nodelay(stdscr, FALSE);
1359
1360 #endif
1361
1362    /* Prepare */
1363    cbreak();
1364    noecho();
1365    nonl();
1366    raw();
1367
1368    /* Extract the game keymap */
1369    keymap_game_prepare();
1370
1371
1372    /*** Now prepare the term(s) ***/
1373    for (i = 0; i < num_term; i++)
1374    {
1375       int rows, cols;
1376       int y, x;
1377
1378       switch (i)
1379       {
1380          /* Upper left */
1381          case 0: rows = 24;
1382             cols = 80;
1383             y = x = 0;
1384             break;
1385          /* Lower left */
1386          case 1: rows = LINES - 25;
1387             cols = 80;
1388             y = 24;
1389             x = 0;
1390             break;
1391          /* Upper right */
1392          case 2: rows = 24;
1393             cols = COLS - 81;
1394             y = 0;
1395             x = 81;
1396             break;
1397          /* Lower right */
1398          case 3: rows = LINES - 25;
1399             cols = COLS - 81;
1400             y = 24;
1401             x = 81;
1402             break;
1403          /* XXX */
1404          default: rows = cols = 0;
1405              y = x = 0;
1406              break;
1407       }
1408
1409       /* No non-windows */
1410       if (rows <= 0 || cols <= 0) continue;
1411
1412       /* Initialize */
1413       term_data_init(&data[next_win], rows, cols, y, x);
1414
1415       /* Store */
1416       angband_term[next_win] = Term;
1417
1418       next_win++;
1419    }
1420
1421    /* Activate the "Angband" window screen */
1422    Term_activate(&data[0].t);
1423
1424    /* Store */
1425    term_screen = &data[0].t;
1426
1427    /* Success */
1428    return (0);
1429 }
1430
1431
1432 #endif /* USE_GCU */
1433
1434