OSDN Git Service

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