OSDN Git Service

Habu's cursor flickering fix
[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
342 /**
343  * Background color we should draw with; either BLACK or DEFAULT
344  */
345 static int bg_color = COLOR_BLACK;
346
347 #endif
348
349 /*
350  * Place the "keymap" into its "normal" state
351  */
352 static void keymap_norm(void)
353 {
354 #ifdef USE_TPOSIX
355     /* restore the saved values of the special chars */
356     (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
357 #endif
358
359 #ifdef USE_TERMIO
360     /* restore the saved values of the special chars */
361     (void)ioctl(0, TCSETA, (char *)&norm_termio);
362 #endif
363
364 #ifdef USE_TCHARS
365     /* restore the saved values of the special chars */
366     (void)ioctl(0, TIOCSLTC, (char *)&norm_speciax_chars);
367     (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
368     (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
369     (void)ioctl(0, TIOCLSET, (char *)&norm_locax_chars);
370 #endif
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  * 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  * Save the keymaps (normal and game)
423  */
424 static void keymap_game_prepare(void)
425 {
426 #ifdef USE_TPOSIX
427     /* Acquire the current mapping */
428     tcgetattr(0, &game_termios);
429
430     /* Force "Ctrl-C" to interupt */
431     game_termios.c_cc[VINTR] = (char)3;
432
433     /* Force "Ctrl-Z" to suspend */
434     game_termios.c_cc[VSUSP] = (char)26;
435
436     /* Hack -- Leave "VSTART/VSTOP" alone */
437
438     /* Disable the standard control characters */
439     game_termios.c_cc[VQUIT] = (char)-1;
440     game_termios.c_cc[VERASE] = (char)-1;
441     game_termios.c_cc[VKILL] = (char)-1;
442     game_termios.c_cc[VEOF] = (char)-1;
443     game_termios.c_cc[VEOL] = (char)-1;
444
445     /* Normally, block until a character is read */
446     game_termios.c_cc[VMIN] = 1;
447     game_termios.c_cc[VTIME] = 0;
448 #endif
449
450 #ifdef USE_TERMIO
451     /* Acquire the current mapping */
452     (void)ioctl(0, TCGETA, (char *)&game_termio);
453
454     /* Force "Ctrl-C" to interupt */
455     game_termio.c_cc[VINTR] = (char)3;
456
457     /* Force "Ctrl-Z" to suspend */
458     game_termio.c_cc[VSUSP] = (char)26;
459
460     /* Disable the standard control characters */
461     game_termio.c_cc[VQUIT] = (char)-1;
462     game_termio.c_cc[VERASE] = (char)-1;
463     game_termio.c_cc[VKILL] = (char)-1;
464     game_termio.c_cc[VEOF] = (char)-1;
465     game_termio.c_cc[VEOL] = (char)-1;
466
467     /* Normally, block until a character is read */
468     game_termio.c_cc[VMIN] = 1;
469     game_termio.c_cc[VTIME] = 0;
470 #endif
471
472 #ifdef USE_TCHARS
473     /* Get the default game characters */
474     (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
475     (void)ioctl(0, TIOCGLTC, (char *)&game_speciax_chars);
476     (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
477     (void)ioctl(0, TIOCLGET, (char *)&game_locax_chars);
478
479     /* Force suspend (^Z) */
480     game_speciax_chars.t_suspc = (char)26;
481
482     /* Cancel some things */
483     game_speciax_chars.t_dsuspc = (char)-1;
484     game_speciax_chars.t_rprntc = (char)-1;
485     game_speciax_chars.t_flushc = (char)-1;
486     game_speciax_chars.t_werasc = (char)-1;
487     game_speciax_chars.t_lnextc = (char)-1;
488
489     /* Force interupt (^C) */
490     game_tchars.t_intrc = (char)3;
491
492     /* Force start/stop (^Q, ^S) */
493     game_tchars.t_startc = (char)17;
494     game_tchars.t_stopc = (char)19;
495
496     /* Cancel some things */
497     game_tchars.t_quitc = (char)-1;
498     game_tchars.t_eofc = (char)-1;
499     game_tchars.t_brkc = (char)-1;
500 #endif
501 }
502
503 /*
504  * Suspend/Resume
505  */
506 static errr Term_xtra_gcu_alive(int v)
507 {
508     if (!v) {
509         /* Go to normal keymap mode */
510         keymap_norm();
511
512         /* Restore modes */
513         nocbreak();
514         echo();
515         nl();
516
517         /* Hack -- make sure the cursor is visible */
518         term_xtra(TERM_XTRA_SHAPE, 1);
519
520         /* Flush the curses buffer */
521         (void)refresh();
522
523         /* this moves curses to bottom right corner */
524         mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
525
526         /* Exit curses */
527         endwin();
528
529         /* Flush the output */
530         (void)fflush(stdout);
531     } else {
532         /* Restore the settings */
533         cbreak();
534         noecho();
535         nonl();
536
537         /* Go to angband keymap mode */
538         keymap_game();
539     }
540
541     return (0);
542 }
543
544 /*
545  * Check for existance of a file
546  */
547 static bool check_file(concptr s)
548 {
549     FILE *fff;
550     fff = fopen(s, "r");
551     if (!fff)
552         return false;
553
554     fclose(fff);
555     return true;
556 }
557
558 /*
559  * Initialize sound
560  */
561 static bool init_sound(void)
562 {
563     /* Initialize once */
564     if (can_use_sound)
565         return can_use_sound;
566
567     int i;
568     char wav[128];
569     char buf[1024];
570
571     /* Prepare the sounds */
572     for (i = 1; i < SOUND_MAX; i++) {
573         /* Extract name of sound file */
574         sprintf(wav, "%s.wav", angband_sound_name[i]);
575
576         /* Access the sound */
577         path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_SOUND, wav);
578
579         /* Save the sound filename, if it exists */
580         if (check_file(buf))
581             sound_file[i] = string_make(buf);
582     }
583
584     /* Sound available */
585     can_use_sound = true;
586     return (can_use_sound);
587 }
588
589 /*
590  * Init the "curses" system
591  */
592 static void Term_init_gcu(term_type *t)
593 {
594     term_data *td = (term_data *)(t->data);
595
596     /* Count init's, handle first */
597     if (active++ != 0)
598         return;
599
600     /* Erase the screen */
601     (void)wclear(td->win);
602
603     /* Reset the cursor */
604     (void)wmove(td->win, 0, 0);
605
606     /* Flush changes */
607     (void)wrefresh(td->win);
608
609     /* Game keymap */
610     keymap_game();
611 }
612
613 /*
614  * Nuke the "curses" system
615  */
616 static void Term_nuke_gcu(term_type *t)
617 {
618     term_data *td = (term_data *)(t->data);
619
620     /* Delete this window */
621     delwin(td->win);
622
623     /* Count nuke's, handle last */
624     if (--active != 0)
625         return;
626
627     /* Hack -- make sure the cursor is visible */
628     term_xtra(TERM_XTRA_SHAPE, 1);
629
630 #ifdef A_COLOR
631     /* Reset colors to defaults */
632     start_color();
633 #endif
634
635     /* This moves curses to bottom right corner */
636     mvcur(getcury(curscr), getcurx(curscr), LINES - 1, 0);
637
638     /* Flush the curses buffer */
639     (void)refresh();
640
641     /* Exit curses */
642     endwin();
643
644     /* Flush the output */
645     (void)fflush(stdout);
646
647     /* Normal keymap */
648     keymap_norm();
649 }
650
651 /*
652  * Push multiple keys reversal
653  */
654 static void term_string_push(char *buf)
655 {
656     int i, l = strlen(buf);
657     for (i = l; i >= 0; i--)
658         term_key_push(buf[i]);
659 }
660
661 #ifdef USE_GETCH
662
663 /*
664  * Process events, with optional wait
665  */
666 static errr Term_xtra_gcu_event(int v)
667 {
668     int i, k;
669
670     /* Wait */
671     if (v) {
672         char buf[256];
673         char *bp = buf;
674
675         /* Paranoia -- Wait for it */
676         nodelay(stdscr, false);
677
678         /* Get a keypress */
679         i = getch();
680
681         /* Broken input is special */
682         if (i == ERR)
683             exit_game_panic(p_ptr);
684         if (i == EOF)
685             exit_game_panic(p_ptr);
686
687         *bp++ = (char)i;
688
689         /* Do not wait for it */
690         nodelay(stdscr, true);
691
692         while ((i = getch()) != EOF) {
693             if (i == ERR)
694                 exit_game_panic(p_ptr);
695             *bp++ = (char)i;
696             if (bp == &buf[255])
697                 break;
698         }
699
700         /* Wait for it next time */
701         nodelay(stdscr, false);
702
703         *bp = '\0';
704 #ifdef JP
705         char eucbuf[sizeof(buf)];
706         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
707         if (utf8_to_euc(buf, strlen(buf) + 1, eucbuf, sizeof(eucbuf)) < 0) {
708             return (-1);
709         }
710 #endif
711         term_string_push(_(eucbuf, buf));
712     }
713
714     /* Do not wait */
715     else {
716         /* Do not wait for it */
717         nodelay(stdscr, true);
718
719         /* Check for keypresses */
720         i = getch();
721
722         /* Wait for it next time */
723         nodelay(stdscr, false);
724
725         /* None ready */
726         if (i == ERR)
727             return (1);
728         if (i == EOF)
729             return (1);
730
731         /* Enqueue the keypress */
732         term_key_push(i);
733     }
734
735     /* Success */
736     return (0);
737 }
738
739 #else /* USE_GETCH */
740
741 /*
742  * Process events (with optional wait)
743  */
744 static errr Term_xtra_gcu_event(int v)
745 {
746     int i, k;
747
748     char buf[256];
749
750     /* Wait */
751     if (v) {
752         char *bp = buf;
753
754         /* Wait for one byte */
755         i = read(0, bp++, 1);
756
757         /* Hack -- Handle bizarre "errors" */
758         if ((i <= 0) && (errno != EINTR))
759             exit_game_panic(p_ptr);
760
761         /* Get the current flags for stdin */
762         k = fcntl(0, F_GETFL, 0);
763
764         /* Oops */
765         if (k < 0)
766             return (1);
767
768         /* Tell stdin not to block */
769         if (fcntl(0, F_SETFL, k | O_NDELAY) >= 0) {
770             if ((i = read(0, bp, 254)) > 0) {
771                 bp += i;
772             }
773
774             /* Replace the flags for stdin */
775             if (fcntl(0, F_SETFL, k))
776                 return (1);
777         }
778
779         bp[0] = '\0';
780 #ifdef JP
781         char eucbuf[sizeof(buf)];
782         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
783         if (utf8_to_euc(buf, strlen(buf) + 1, eucbuf, sizeof(eucbuf)) < 0) {
784             return (-1);
785         }
786 #endif
787         term_string_push(_(eucbuf, buf));
788     }
789
790     /* Do not wait */
791     else {
792         /* Get the current flags for stdin */
793         k = fcntl(0, F_GETFL, 0);
794
795         /* Oops */
796         if (k < 0)
797             return (1);
798
799         /* Tell stdin not to block */
800         if (fcntl(0, F_SETFL, k | O_NDELAY) < 0)
801             return (1);
802
803         /* Read one byte, if possible */
804         i = read(0, buf, 1);
805
806         /* Replace the flags for stdin */
807         if (fcntl(0, F_SETFL, k))
808             return (1);
809
810         /* Ignore "invalid" keys */
811         if ((i != 1) || (!buf[0]))
812             return (1);
813
814         /* Enqueue the keypress */
815         term_key_push(buf[0]);
816     }
817
818     /* Success */
819     return (0);
820 }
821
822 #endif /* USE_GETCH */
823
824 /*
825  * Hack -- make a sound
826  */
827 static errr Term_xtra_gcu_sound(int v)
828 {
829     char buf[1024];
830
831     /* Sound disabled */
832     if (!use_sound)
833         return (1);
834
835     /* Illegal sound */
836     if ((v < 0) || (v >= SOUND_MAX))
837         return (1);
838
839     /* Unknown sound */
840     if (!sound_file[v])
841         return (1);
842
843     sprintf(buf, "./gcusound.sh %s\n", sound_file[v]);
844
845     return (system(buf) < 0);
846
847     return (0);
848 }
849
850 static int scale_color(int i, int j, int scale)
851 {
852     return (angband_color_table[i][j] * (scale - 1) + 127) / 255;
853 }
854
855 static int create_color(int i, int scale)
856 {
857     int r = scale_color(i, 1, scale);
858     int g = scale_color(i, 2, scale);
859     int b = scale_color(i, 3, scale);
860     int rgb = 16 + scale * scale * r + scale * g + b;
861     /* In the case of white and black we need to use the ANSI colors */
862     if (r == g && g == b)
863     {
864         if (b == 0) rgb = 0;
865         if (b == scale) rgb = 15;
866     }
867     return rgb;
868 }
869
870 /*
871  * React to changes
872  */
873 static errr Term_xtra_gcu_react(void)
874 {
875
876 #ifdef A_COLOR
877
878     if (COLORS == 256 || COLORS == 88)
879     {
880         /* If we have more than 16 colors, find the best matches. These numbers
881         * correspond to xterm/rxvt's builtin color numbers--they do not
882         * correspond to curses' constants OR with curses' color pairs.
883         *
884         * XTerm has 216 (6*6*6) RGB colors, with each RGB setting 0-5.
885         * RXVT has 64 (4*4*4) RGB colors, with each RGB setting 0-3.
886         *
887         * Both also have the basic 16 ANSI colors, plus some extra grayscale
888         * colors which we do not use.
889         */
890         int scale = COLORS == 256 ? 6 : 4;
891         for (int i = 0; i < 16; i++)
892         {
893             int fg = create_color(i, scale);
894             init_pair(i + 1, fg, bg_color);
895             colortable[i] = COLOR_PAIR(i + 1) | A_NORMAL;
896         }
897     }
898
899 #endif
900
901     /* Success */
902     return (0);
903 }
904
905 /*
906  * Handle a "special request"
907  */
908 static errr Term_xtra_gcu(int n, int v)
909 {
910     term_data *td = (term_data *)(Term->data);
911
912     /* Analyze the request */
913     switch (n) {
914     /* Clear screen */
915     case TERM_XTRA_CLEAR:
916         touchwin(td->win);
917         (void)wclear(td->win);
918         return (0);
919
920     /* Make a noise */
921     case TERM_XTRA_NOISE:
922         return write(1, "\007", 1) != 1;
923
924     /* Make a special sound */
925     case TERM_XTRA_SOUND:
926         return (Term_xtra_gcu_sound(v));
927
928     /* Flush the Curses buffer */
929     case TERM_XTRA_FRESH:
930         (void)wrefresh(td->win);
931         return (0);
932
933     /* Change the cursor visibility */
934     case TERM_XTRA_SHAPE:
935         curs_set(v);
936         return (0);
937
938     /* Suspend/Resume curses */
939     case TERM_XTRA_ALIVE:
940         return (Term_xtra_gcu_alive(v));
941
942     /* Process events */
943     case TERM_XTRA_EVENT:
944         return (Term_xtra_gcu_event(v));
945
946     /* Flush events */
947     case TERM_XTRA_FLUSH:
948         while (!Term_xtra_gcu_event(false))
949             ;
950         return (0);
951
952     /* Delay */
953     case TERM_XTRA_DELAY:
954         usleep(1000 * v);
955         return (0);
956
957     /* React to events */
958     case TERM_XTRA_REACT:
959         Term_xtra_gcu_react();
960         return (0);
961     }
962
963     /* Unknown */
964     return (1);
965 }
966
967 /*
968  * Actually MOVE the hardware cursor
969  */
970 static errr Term_curs_gcu(int x, int y)
971 {
972     term_data *td = (term_data *)(Term->data);
973
974     /* Literally move the cursor */
975     wmove(td->win, y, x);
976
977     /* Success */
978     return (0);
979 }
980
981 /*
982  * Erase a grid of space
983  * Hack -- try to be "semi-efficient".
984  */
985 static errr Term_wipe_gcu(int x, int y, int n)
986 {
987     term_data *td = (term_data *)(Term->data);
988
989     /* Place cursor */
990     wmove(td->win, y, x);
991
992     /* Clear to end of line */
993     if (x + n >= 80) {
994         wclrtoeol(td->win);
995     }
996
997     /* Clear some characters */
998     else {
999         while (n-- > 0)
1000             waddch(td->win, ' ');
1001     }
1002
1003     /* Success */
1004     return (0);
1005 }
1006
1007 #ifdef USE_NCURSES_ACS
1008 /*
1009  * this function draws some ACS characters on the screen
1010  * for DOS-based users: these are the graphical chars (blocks, lines etc)
1011  *
1012  * unix-gurus: before you start adding other attributes like A_REVERSE
1013  * think hard about how map_info() in cave.c should handle the color
1014  * of something that we here draw in reverse. It's not so simple, alas.
1015  */
1016 static void Term_acs_text_gcu(int x, int y, int n, byte a, concptr s)
1017 {
1018     term_data *td = (term_data *)(Term->data);
1019     int i;
1020
1021     /* position the cursor */
1022     wmove(td->win, y, x);
1023
1024 #ifdef A_COLOR
1025     /* Set the color */
1026     wattrset(td->win, colortable[a & 0x0F]);
1027 #endif
1028
1029     for (i = 0; i < n; i++) {
1030         /* add acs_map of a */
1031         waddch(td->win, acs_map[(int)s[i]]);
1032     }
1033     wattrset(td->win, WA_NORMAL);
1034 }
1035 #endif
1036
1037 /*
1038  * Place some text on the screen using an attribute
1039  */
1040 static errr Term_text_gcu(int x, int y, int n, byte a, concptr s)
1041 {
1042     term_data *td = (term_data *)(Term->data);
1043
1044 #ifdef USE_NCURSES_ACS
1045     /* do we have colors + 16 ? */
1046     /* then call special routine for drawing special characters */
1047     if (a & 0x10) {
1048         Term_acs_text_gcu(x, y, n, a, s);
1049         return (0);
1050     }
1051 #endif
1052
1053     /* Move the cursor and dump the string */
1054     wmove(td->win, y, x);
1055
1056 #ifdef A_COLOR
1057     /* Set the color */
1058     if (can_use_color)
1059         wattrset(td->win, colortable[a & 0x0F]);
1060 #endif
1061
1062 #ifdef JP
1063     char text[1024];
1064     int text_len = euc_to_utf8(s, n, text, sizeof(text));
1065     if (text_len < 0) {
1066         return (-1);
1067     }
1068 #endif
1069     /* Add the text */
1070     waddnstr(td->win, _(text, s), _(text_len, n));
1071
1072     /* Success */
1073     return (0);
1074 }
1075
1076 static errr term_data_init(term_data *td, int rows, int cols, int y, int x)
1077 {
1078     term_type *t = &td->t;
1079
1080     /* Make sure the window has a positive size */
1081     if (rows <= 0 || cols <= 0)
1082         return (0);
1083
1084     /* Create a window */
1085     td->win = newwin(rows, cols, y, x);
1086
1087     /* Make sure we succeed */
1088     if (!td->win) {
1089         plog("Failed to setup curses window.");
1090         return (-1);
1091     }
1092
1093     /* Initialize the term */
1094     term_init(t, cols, rows, 256);
1095
1096     /* Avoid the bottom right corner */
1097     t->icky_corner = true;
1098
1099     /* Erase with "white space" */
1100     t->attr_blank = TERM_WHITE;
1101     t->char_blank = ' ';
1102
1103     /* Set some hooks */
1104     t->init_hook = Term_init_gcu;
1105     t->nuke_hook = Term_nuke_gcu;
1106
1107     /* Set some more hooks */
1108     t->text_hook = Term_text_gcu;
1109     t->wipe_hook = Term_wipe_gcu;
1110     t->curs_hook = Term_curs_gcu;
1111     t->xtra_hook = Term_xtra_gcu;
1112
1113     /* Save the data */
1114     t->data = td;
1115
1116     /* Activate it */
1117     term_activate(t);
1118
1119     /* Success */
1120     return (0);
1121 }
1122
1123 static void hook_quit(concptr str)
1124 {
1125     /* Unused */
1126     (void)str;
1127
1128     /* Exit curses */
1129     endwin();
1130 }
1131
1132 /*
1133  * Prepare "curses" for use by the file "term.c"
1134  *
1135  * Installs the "hook" functions defined above, and then activates
1136  * the main screen "term", which clears the screen and such things.
1137  *
1138  * Someone should really check the semantics of "initscr()"
1139  */
1140 errr init_gcu(int argc, char *argv[])
1141 {
1142     int i;
1143
1144     int num_term = 4, next_win = 0;
1145     char path[1024];
1146
1147     /* Unused */
1148     (void)argc;
1149     (void)argv;
1150
1151     setlocale(LC_ALL, "");
1152
1153     /* Build the "sound" path */
1154     path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "sound");
1155
1156     /* Allocate the path */
1157     ANGBAND_DIR_XTRA_SOUND = string_make(path);
1158
1159     /* Extract the normal keymap */
1160     keymap_norm_prepare();
1161
1162     /* Initialize for others systems */
1163     if (initscr() == (WINDOW *)ERR)
1164         return (-1);
1165
1166     /* Activate hooks */
1167     quit_aux = hook_quit;
1168     core_aux = hook_quit;
1169
1170     /* Hack -- Require large screen, or Quit with message */
1171     i = ((LINES < 24) || (COLS < 80));
1172     if (i)
1173         quit("Angband needs an 80x24 'curses' screen");
1174
1175 #ifdef A_COLOR
1176
1177     /*** Init the Color-pairs and set up a translation table ***/
1178
1179     /* Do we have color, and enough color, available? */
1180     can_use_color = ((start_color() != ERR) && has_colors() && (COLORS >= 8) && (COLOR_PAIRS >= 8));
1181
1182 #ifdef REDEFINE_COLORS
1183     /* Can we change colors? */
1184     can_fix_color = (can_use_color && can_change_color() && (COLORS >= 16) && (COLOR_PAIRS > 8));
1185 #endif
1186
1187     /* Attempt to use customized colors */
1188     if (can_fix_color) {
1189         /* Prepare the color pairs */
1190         for (i = 1; i <= 15; i++) {
1191             if (init_pair(i, i, 0) == ERR) {
1192                 quit("Color pair init failed");
1193             }
1194
1195             colortable[i] = COLOR_PAIR(i);
1196             Term_xtra_gcu_react();
1197         }
1198     }
1199     /* Attempt to use colors */
1200     else if (can_use_color) {
1201         /* Color-pair 0 is *always* WHITE on BLACK */
1202
1203         /* Prepare the color pairs */
1204         init_pair(1, COLOR_RED, COLOR_BLACK);
1205         init_pair(2, COLOR_GREEN, COLOR_BLACK);
1206         init_pair(3, COLOR_YELLOW, COLOR_BLACK);
1207         init_pair(4, COLOR_BLUE, COLOR_BLACK);
1208         init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
1209         init_pair(6, COLOR_CYAN, COLOR_BLACK);
1210         init_pair(7, COLOR_BLACK, COLOR_BLACK);
1211
1212         /* Prepare the "Angband Colors" -- Bright white is too bright */
1213         /* Changed in Drangband. Cyan as grey sucks -- -TM- */
1214         colortable[0] = (COLOR_PAIR(7) | A_NORMAL); /* Black */
1215         colortable[1] = (COLOR_PAIR(0) | A_BRIGHT); /* White */
1216         colortable[2] = (COLOR_PAIR(0) | A_NORMAL); /* Grey XXX */
1217         colortable[3] = (COLOR_PAIR(1) | A_BRIGHT); /* Orange XXX */
1218         colortable[4] = (COLOR_PAIR(1) | A_NORMAL); /* Red */
1219         colortable[5] = (COLOR_PAIR(2) | A_NORMAL); /* Green */
1220         colortable[6] = (COLOR_PAIR(4) | A_BRIGHT); /* Blue */
1221         colortable[7] = (COLOR_PAIR(3) | A_NORMAL); /* Umber */
1222         colortable[8] = (COLOR_PAIR(7) | A_BRIGHT); /* Dark-grey XXX */
1223         colortable[9] = (COLOR_PAIR(0) | A_NORMAL); /* Light-grey XXX */
1224         colortable[10] = (COLOR_PAIR(5) | A_BRIGHT); /* Purple */
1225         colortable[11] = (COLOR_PAIR(3) | A_BRIGHT); /* Yellow */
1226         colortable[12] = (COLOR_PAIR(5) | A_NORMAL); /* Light Red XXX */
1227         colortable[13] = (COLOR_PAIR(2) | A_BRIGHT); /* Light Green */
1228         colortable[14] = (COLOR_PAIR(6) | A_BRIGHT); /* Light Blue */
1229         colortable[15] = (COLOR_PAIR(3) | A_NORMAL); /* Light Umber XXX */
1230     }
1231
1232 #endif
1233
1234     /* Handle "arg_sound" */
1235     if (use_sound != arg_sound) {
1236         /* Initialize (if needed) */
1237         if (arg_sound && !init_sound()) {
1238             /* Warning */
1239             plog("Cannot initialize sound!");
1240
1241             /* Cannot enable */
1242             arg_sound = false;
1243         }
1244
1245         /* Change setting */
1246         use_sound = arg_sound;
1247     }
1248
1249     /* Try graphics */
1250     if (arg_graphics) {
1251         /* if USE_NCURSES_ACS is defined, we can do something with graphics in curses! */
1252 #ifdef USE_NCURSES_ACS
1253         use_graphics = true;
1254 #endif
1255     }
1256
1257     /*** Low level preparation ***/
1258
1259 #ifdef USE_GETCH
1260
1261     /* Paranoia -- Assume no waiting */
1262     nodelay(stdscr, false);
1263
1264 #endif
1265
1266     /* Prepare */
1267     cbreak();
1268     noecho();
1269     nonl();
1270     raw();
1271
1272     /* Extract the game keymap */
1273     keymap_game_prepare();
1274
1275     /*** Now prepare the term(s) ***/
1276     for (i = 0; i < num_term; i++) {
1277         int rows, cols;
1278         int y, x;
1279
1280         switch (i) {
1281         /* Upper left */
1282         case 0:
1283             rows = 24;
1284             cols = 80;
1285             y = x = 0;
1286             break;
1287         /* Lower left */
1288         case 1:
1289             rows = LINES - 25;
1290             cols = 80;
1291             y = 24;
1292             x = 0;
1293             break;
1294         /* Upper right */
1295         case 2:
1296             rows = 24;
1297             cols = COLS - 81;
1298             y = 0;
1299             x = 81;
1300             break;
1301         /* Lower right */
1302         case 3:
1303             rows = LINES - 25;
1304             cols = COLS - 81;
1305             y = 24;
1306             x = 81;
1307             break;
1308         /* XXX */
1309         default:
1310             rows = cols = 0;
1311             y = x = 0;
1312             break;
1313         }
1314
1315         /* No non-windows */
1316         if (rows <= 0 || cols <= 0)
1317             continue;
1318
1319         /* Initialize */
1320         term_data_init(&data[next_win], rows, cols, y, x);
1321
1322         /* Store */
1323         angband_term[next_win] = Term;
1324
1325         next_win++;
1326     }
1327
1328     /* Activate the "Angband" window screen */
1329     term_activate(&data[0].t);
1330
1331     /* Store */
1332     term_screen = &data[0].t;
1333
1334     /* Success */
1335     return (0);
1336 }
1337
1338 #endif /* USE_GCU */