OSDN Git Service

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