OSDN Git Service

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