OSDN Git Service

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